Skip to content

Commit 1687c96

Browse files
authored
apollo_l1_provider: add l1 msg hash to log (#13468)
1 parent 7c1654b commit 1687c96

9 files changed

Lines changed: 119 additions & 104 deletions

File tree

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/apollo_l1_provider/src/l1_provider.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,13 @@ impl L1Provider {
239239
);
240240
debug!(
241241
"Returned L1Handler txs: {:?}",
242-
txs.iter().map(|tx| tx.tx_hash).collect::<Vec<_>>()
242+
txs.iter()
243+
.map(|tx| format!(
244+
"L2 tx hash: {}, L1-L2 msg hash: {}",
245+
tx.tx_hash,
246+
tx.tx.calc_msg_hash()
247+
))
248+
.collect::<Vec<_>>()
243249
);
244250
Ok(txs)
245251
}

crates/apollo_l1_provider/src/l1_scraper.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,20 @@ impl<BaseLayerType: BaseLayerContract + Send + Sync + Debug> L1Scraper<BaseLayer
322322
Event::L1HandlerTransaction { l1_handler_tx, .. } => Some(l1_handler_tx.tx_hash),
323323
_ => None,
324324
});
325+
// Collect the L1-L2 message hashes (keccak) for L1 handler transactions.
326+
let l1_msg_hashes = events.iter().filter_map(|event| match event {
327+
Event::L1HandlerTransaction { l1_handler_tx, .. } => {
328+
Some(l1_handler_tx.tx.calc_msg_hash())
329+
}
330+
_ => None,
331+
});
325332

326-
let formatted_pairs = zip_eq(l1_messages_info, l2_hashes)
327-
.map(|((l1_hash, timestamp), l2_hash)| {
328-
format!("L1 tx hash: {l1_hash:?}, L1 timestamp: {timestamp}, L2 tx hash: {l2_hash}")
333+
let formatted_pairs = zip_eq(zip_eq(l1_messages_info, l2_hashes), l1_msg_hashes)
334+
.map(|(((l1_hash, timestamp), l2_hash), l1_msg_hash)| {
335+
format!(
336+
"L1 tx hash: {l1_hash:?}, L1 timestamp: {timestamp}, L2 tx hash: {l2_hash}, \
337+
L1-L2 msg hash: {l1_msg_hash}"
338+
)
329339
})
330340
.collect::<Vec<_>>();
331341
if formatted_pairs.is_empty() {

crates/apollo_rpc/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ license-file.workspace = true
77
description = "JSON-RPC server implementation for Starknet API."
88

99
[dependencies]
10-
alloy-primitives.workspace = true
1110
anyhow.workspace = true
1211
apollo_class_manager_types.workspace = true
1312
apollo_config.workspace = true

crates/apollo_rpc/src/v0_8/api/api_impl.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use starknet_api::core::{
5050
BLOCK_HASH_TABLE_ADDRESS,
5151
};
5252
use starknet_api::execution_utils::format_panic_data;
53+
use starknet_api::hash::{l1_handler_message_hash, L1L2MsgHash};
5354
use starknet_api::state::{StateNumber, StorageKey, ThinStateDiff as StarknetApiThinStateDiff};
5455
use starknet_api::transaction::fields::Fee;
5556
use starknet_api::transaction::{
@@ -99,8 +100,6 @@ use super::super::transaction::{
99100
get_block_txs_by_number,
100101
Event,
101102
GeneralTransactionReceipt,
102-
L1HandlerMsgHash,
103-
L1L2MsgHash,
104103
MessageFromL1,
105104
PendingTransactionFinalityStatus,
106105
PendingTransactionOutput,
@@ -1755,7 +1754,12 @@ fn client_receipt_to_rpc_pending_receipt(
17551754
let starknet_api_output =
17561755
client_transaction_receipt.into_starknet_api_transaction_output(client_transaction);
17571756
let msg_hash = match client_transaction {
1758-
ClientTransaction::L1Handler(tx) => Some(tx.calc_msg_hash()),
1757+
ClientTransaction::L1Handler(tx) => Some(l1_handler_message_hash(
1758+
&tx.contract_address,
1759+
tx.nonce,
1760+
&tx.entry_point_selector,
1761+
&tx.calldata,
1762+
)),
17591763
_ => None,
17601764
};
17611765
let output = PendingTransactionOutput::try_from(TransactionOutput::from((

crates/apollo_rpc/src/v0_8/api/test.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ use starknet_api::deprecated_contract_class::{
100100
FunctionAbiEntry,
101101
FunctionStateMutability,
102102
};
103+
use starknet_api::hash::{l1_handler_message_hash, L1L2MsgHash};
103104
use starknet_api::state::{SierraContractClass as StarknetApiContractClass, StateDiff};
104105
use starknet_api::transaction::{
105106
Event as StarknetApiEvent,
@@ -152,8 +153,6 @@ use super::super::transaction::{
152153
Event,
153154
GeneralTransactionReceipt,
154155
InvokeTransaction,
155-
L1HandlerMsgHash,
156-
L1L2MsgHash,
157156
PendingTransactionFinalityStatus,
158157
PendingTransactionOutput,
159158
PendingTransactionReceipt,
@@ -2219,7 +2218,12 @@ fn generate_client_transaction_client_receipt_rpc_transaction_and_rpc_receipt(
22192218
.into_starknet_api_transaction_output(&client_transaction);
22202219
let msg_hash = match &client_transaction {
22212220
apollo_starknet_client::reader::objects::transaction::Transaction::L1Handler(tx) => {
2222-
Some(tx.calc_msg_hash())
2221+
Some(l1_handler_message_hash(
2222+
&tx.contract_address,
2223+
tx.nonce,
2224+
&tx.entry_point_selector,
2225+
&tx.calldata,
2226+
))
22232227
}
22242228
_ => None,
22252229
};

crates/apollo_rpc/src/v0_8/transaction.rs

Lines changed: 2 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ use std::num::NonZeroU64;
77
use std::ops::Add;
88
use std::sync::Arc;
99

10-
use alloy_primitives::keccak256;
1110
use apollo_rpc_execution::objects::PriceUnit;
1211
use apollo_starknet_client::writer::objects::transaction as client_transaction;
1312
use apollo_storage::body::BodyStorageReader;
1413
use apollo_storage::db::TransactionKind;
1514
use apollo_storage::StorageTxn;
1615
use jsonrpsee::types::ErrorObjectOwned;
17-
use serde::{Deserialize, Deserializer, Serialize, Serializer};
16+
use serde::{Deserialize, Serialize};
1817
use starknet_api::block::{BlockHash, BlockNumber, BlockStatus};
1918
use starknet_api::core::{
2019
ClassHash,
@@ -26,7 +25,7 @@ use starknet_api::core::{
2625
};
2726
use starknet_api::data_availability::DataAvailabilityMode;
2827
use starknet_api::execution_resources::GasAmount;
29-
use starknet_api::serde_utils::bytes_from_hex_str;
28+
use starknet_api::hash::L1L2MsgHash;
3029
use starknet_api::transaction::fields::{
3130
AccountDeploymentData,
3231
AllResourceBounds,
@@ -1198,92 +1197,6 @@ pub fn get_block_tx_hashes_by_number<Mode: TransactionKind>(
11981197
Ok(transaction_hashes)
11991198
}
12001199

1201-
/// The hash of a L1 -> L2 message.
1202-
// The hash is Keccak256, so it doesn't necessarily fit in a Felt.
1203-
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, PartialOrd, Ord)]
1204-
pub struct L1L2MsgHash(pub [u8; 32]);
1205-
1206-
impl std::fmt::Display for L1L2MsgHash {
1207-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1208-
write!(f, "0x{}", hex::encode(self.0))
1209-
}
1210-
}
1211-
1212-
impl Serialize for L1L2MsgHash {
1213-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1214-
where
1215-
S: Serializer,
1216-
{
1217-
serializer.serialize_str(format!("{self}").as_str())
1218-
}
1219-
}
1220-
1221-
impl<'de> Deserialize<'de> for L1L2MsgHash {
1222-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1223-
where
1224-
D: Deserializer<'de>,
1225-
{
1226-
let s = String::deserialize(deserializer)?;
1227-
Ok(Self(bytes_from_hex_str::<32, true>(s.as_str()).map_err(serde::de::Error::custom)?))
1228-
}
1229-
}
1230-
1231-
pub trait L1HandlerMsgHash {
1232-
fn calc_msg_hash(&self) -> L1L2MsgHash;
1233-
}
1234-
1235-
impl L1HandlerMsgHash for L1HandlerTransaction {
1236-
fn calc_msg_hash(&self) -> L1L2MsgHash {
1237-
l1_handler_message_hash(
1238-
&self.contract_address,
1239-
self.nonce,
1240-
&self.entry_point_selector,
1241-
&self.calldata,
1242-
)
1243-
}
1244-
}
1245-
1246-
impl L1HandlerMsgHash
1247-
for apollo_starknet_client::reader::objects::transaction::L1HandlerTransaction
1248-
{
1249-
fn calc_msg_hash(&self) -> L1L2MsgHash {
1250-
l1_handler_message_hash(
1251-
&self.contract_address,
1252-
self.nonce,
1253-
&self.entry_point_selector,
1254-
&self.calldata,
1255-
)
1256-
}
1257-
}
1258-
1259-
/// Calculating the message hash of L1 -> L2 message.
1260-
/// `<For more info: https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/messaging-mechanism/#structure_and_hashing_l1-l2>`
1261-
fn l1_handler_message_hash(
1262-
contract_address: &ContractAddress,
1263-
nonce: Nonce,
1264-
entry_point_selector: &EntryPointSelector,
1265-
calldata: &Calldata,
1266-
) -> L1L2MsgHash {
1267-
let (from_address, payload) =
1268-
calldata.0.split_first().expect("Invalid calldata, expected at least from_address");
1269-
1270-
let mut encoded = Vec::new();
1271-
encoded.extend(from_address.to_bytes_be());
1272-
encoded.extend(contract_address.0.key().to_bytes_be());
1273-
encoded.extend(nonce.to_bytes_be());
1274-
encoded.extend(entry_point_selector.0.to_bytes_be());
1275-
1276-
let payload_length_as_felt =
1277-
Felt::from(u64::try_from(payload.len()).expect("usize should fit in u64"));
1278-
encoded.extend(payload_length_as_felt.to_bytes_be());
1279-
1280-
for felt in payload {
1281-
encoded.extend(felt.to_bytes_be());
1282-
}
1283-
1284-
L1L2MsgHash(keccak256(&encoded).0)
1285-
}
1286-
12871200
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
12881201
pub struct MessageFromL1 {
12891202
// TODO(Shahak): fix serialization of EthAddress in SN_API to fit the spec.

crates/apollo_rpc/src/v0_8/transaction_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use apollo_test_utils::{
88
use pretty_assertions::assert_eq;
99
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce};
1010
use starknet_api::data_availability::DataAvailabilityMode;
11+
use starknet_api::hash::L1L2MsgHash;
1112
use starknet_api::transaction::fields::{
1213
AccountDeploymentData,
1314
Calldata,
@@ -21,7 +22,6 @@ use starknet_api::transaction::fields::{
2122
use starknet_api::transaction::{L1HandlerTransaction, Transaction};
2223
use starknet_api::{calldata, contract_address, felt, nonce};
2324

24-
use super::super::transaction::{L1HandlerMsgHash, L1L2MsgHash};
2525
use super::{
2626
DeployAccountTransaction,
2727
DeployAccountTransactionV1,

crates/starknet_api/src/hash.rs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
use std::fmt::Debug;
1+
use std::fmt::{Debug, Display, Formatter};
22

33
use serde::{Deserialize, Serialize};
44
use sha3::{Digest, Keccak256};
55
use starknet_types_core::felt::{Felt, FromStrError};
66
use starknet_types_core::hash::{Poseidon, StarkHash as StarkHashTrait};
77

8-
use crate::core::{GlobalRoot, GLOBAL_STATE_VERSION};
8+
use crate::core::{ContractAddress, EntryPointSelector, GlobalRoot, Nonce, GLOBAL_STATE_VERSION};
9+
use crate::serde_utils::bytes_from_hex_str;
10+
use crate::transaction::fields::Calldata;
11+
use crate::transaction::L1HandlerTransaction;
912

1013
pub type StarkHash = Felt;
1114

@@ -108,3 +111,80 @@ impl StateRoots {
108111
]))
109112
}
110113
}
114+
115+
/// The hash of a L1 -> L2 message.
116+
// The hash is Keccak256, so it doesn't fit in a Felt.
117+
#[derive(Clone, Default, Eq, PartialEq, Hash, PartialOrd, Ord)]
118+
pub struct L1L2MsgHash(pub [u8; 32]);
119+
120+
impl Display for L1L2MsgHash {
121+
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
122+
write!(formatter, "0x{}", hex::encode(self.0))
123+
}
124+
}
125+
126+
impl Debug for L1L2MsgHash {
127+
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
128+
Display::fmt(self, formatter)
129+
}
130+
}
131+
132+
impl Serialize for L1L2MsgHash {
133+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
134+
where
135+
S: serde::Serializer,
136+
{
137+
serializer.serialize_str(format!("{self}").as_str())
138+
}
139+
}
140+
141+
impl<'de> Deserialize<'de> for L1L2MsgHash {
142+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
143+
where
144+
D: serde::Deserializer<'de>,
145+
{
146+
let s = String::deserialize(deserializer)?;
147+
Ok(Self(bytes_from_hex_str::<32, true>(s.as_str()).map_err(serde::de::Error::custom)?))
148+
}
149+
}
150+
151+
impl L1HandlerTransaction {
152+
pub fn calc_msg_hash(&self) -> L1L2MsgHash {
153+
l1_handler_message_hash(
154+
&self.contract_address,
155+
self.nonce,
156+
&self.entry_point_selector,
157+
&self.calldata,
158+
)
159+
}
160+
}
161+
162+
/// Calculating the message hash of L1 -> L2 message.
163+
/// For more info: <https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/messaging-mechanism/#structure_and_hashing_l1-l2>
164+
pub fn l1_handler_message_hash(
165+
contract_address: &ContractAddress,
166+
nonce: Nonce,
167+
entry_point_selector: &EntryPointSelector,
168+
calldata: &Calldata,
169+
) -> L1L2MsgHash {
170+
let (from_address, payload) =
171+
calldata.0.split_first().expect("Invalid calldata, expected at least from_address");
172+
173+
let mut encoded = Vec::new();
174+
encoded.extend(from_address.to_bytes_be());
175+
encoded.extend(contract_address.0.key().to_bytes_be());
176+
encoded.extend(nonce.to_bytes_be());
177+
encoded.extend(entry_point_selector.0.to_bytes_be());
178+
179+
let payload_length_as_felt =
180+
Felt::from(u64::try_from(payload.len()).expect("usize should fit in u64"));
181+
encoded.extend(payload_length_as_felt.to_bytes_be());
182+
183+
for felt in payload {
184+
encoded.extend(felt.to_bytes_be());
185+
}
186+
187+
let mut keccak = Keccak256::default();
188+
keccak.update(&encoded);
189+
L1L2MsgHash(keccak.finalize().into())
190+
}

0 commit comments

Comments
 (0)