From 073909ad22b6eaea084d21991eb7852dfe496fc4 Mon Sep 17 00:00:00 2001 From: jbcaron Date: Tue, 26 Aug 2025 16:27:11 +0200 Subject: [PATCH] feat: add get_messages_status --- madara/Cargo.lock | 2 + .../client/gateway/server/src/handler.rs | 2 +- madara/crates/client/rpc/Cargo.toml | 1 + madara/crates/client/rpc/src/lib.rs | 17 ++- .../rpc/src/versions/user/v0_8_1/api.rs | 4 + .../methods/read/get_messages_status.rs | 34 ++++++ .../versions/user/v0_8_1/methods/read/mod.rs | 7 +- .../client/settlement_client/src/client.rs | 13 +++ .../client/settlement_client/src/error.rs | 3 + .../client/settlement_client/src/eth/mod.rs | 31 +++++- .../client/settlement_client/src/lib.rs | 2 +- .../settlement_client/src/starknet/mod.rs | 52 +++++++++ madara/crates/primitives/convert/Cargo.toml | 1 + madara/crates/primitives/convert/src/felt.rs | 6 + madara/crates/primitives/rpc/src/custom/l1.rs | 103 ++++++++++++++++++ .../crates/primitives/rpc/src/custom/mod.rs | 2 + .../crates/primitives/rpc/src/v0_8_1/mod.rs | 2 +- .../rpc/src/v0_8_1/starknet_api_openrpc.rs | 10 +- madara/node/src/main.rs | 8 +- madara/node/src/service/l1.rs | 5 + madara/node/src/service/rpc/mod.rs | 7 ++ 21 files changed, 302 insertions(+), 10 deletions(-) create mode 100644 madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/get_messages_status.rs create mode 100644 madara/crates/primitives/rpc/src/custom/l1.rs diff --git a/madara/Cargo.lock b/madara/Cargo.lock index f703c6a6c3..65be4c3f7e 100644 --- a/madara/Cargo.lock +++ b/madara/Cargo.lock @@ -6863,6 +6863,7 @@ dependencies = [ "mc-db", "mc-exec", "mc-mempool", + "mc-settlement-client", "mc-submit-tx", "mp-block", "mp-chain-config", @@ -7292,6 +7293,7 @@ version = "0.8.0" dependencies = [ "alloy", "assert_matches", + "mp-rpc", "primitive-types", "rstest 0.18.2", "serde", diff --git a/madara/crates/client/gateway/server/src/handler.rs b/madara/crates/client/gateway/server/src/handler.rs index 9df6226b22..a6f7ca5942 100644 --- a/madara/crates/client/gateway/server/src/handler.rs +++ b/madara/crates/client/gateway/server/src/handler.rs @@ -246,7 +246,7 @@ pub async fn handle_get_block_traces( } let traces = v0_7_1_trace_block_transactions( - &Starknet::new(backend, add_transaction_provider, Default::default(), None, ctx), + &Starknet::new(backend, add_transaction_provider, Default::default(), None, None, ctx), block_id, ) .await?; diff --git a/madara/crates/client/rpc/Cargo.toml b/madara/crates/client/rpc/Cargo.toml index cfd623cff7..8e810d0520 100644 --- a/madara/crates/client/rpc/Cargo.toml +++ b/madara/crates/client/rpc/Cargo.toml @@ -29,6 +29,7 @@ m-proc-macros = { workspace = true } mc-block-production = { workspace = true } mc-db = { workspace = true } mc-exec = { workspace = true } +mc-settlement-client = { workspace = true } mc-submit-tx = { workspace = true } mp-block = { workspace = true, default-features = true } mp-chain-config = { workspace = true } diff --git a/madara/crates/client/rpc/src/lib.rs b/madara/crates/client/rpc/src/lib.rs index f7dd6cc698..5dd38ee01e 100644 --- a/madara/crates/client/rpc/src/lib.rs +++ b/madara/crates/client/rpc/src/lib.rs @@ -13,6 +13,7 @@ pub mod versions; use jsonrpsee::RpcModule; use mc_db::db_block_id::DbBlockIdResolvable; use mc_db::MadaraBackend; +use mc_settlement_client::client::SettlementLayerProvider; use mc_submit_tx::SubmitTransaction; use mp_block::{BlockId, BlockTag, MadaraMaybePendingBlock, MadaraMaybePendingBlockInfo}; use mp_chain_config::ChainConfig; @@ -49,6 +50,7 @@ pub struct Starknet { pub(crate) add_transaction_provider: Arc, storage_proof_config: StorageProofConfig, pub(crate) block_prod_handle: Option, + pub(crate) settlement_client: Option>, pub ctx: ServiceContext, } @@ -58,10 +60,19 @@ impl Starknet { add_transaction_provider: Arc, storage_proof_config: StorageProofConfig, block_prod_handle: Option, + settlement_client: Option>, ctx: ServiceContext, ) -> Self { let ws_handles = Arc::new(WsSubscribeHandles::new()); - Self { backend, ws_handles, add_transaction_provider, storage_proof_config, block_prod_handle, ctx } + Self { + backend, + ws_handles, + add_transaction_provider, + storage_proof_config, + block_prod_handle, + settlement_client, + ctx, + } } pub fn clone_backend(&self) -> Arc { @@ -72,6 +83,10 @@ impl Starknet { Arc::clone(self.backend.chain_config()) } + pub fn settlement_client(&self) -> Option> { + self.settlement_client.clone() + } + pub fn get_block_info( &self, block_id: &impl DbBlockIdResolvable, diff --git a/madara/crates/client/rpc/src/versions/user/v0_8_1/api.rs b/madara/crates/client/rpc/src/versions/user/v0_8_1/api.rs index bdd93a60f2..2bdcb51355 100644 --- a/madara/crates/client/rpc/src/versions/user/v0_8_1/api.rs +++ b/madara/crates/client/rpc/src/versions/user/v0_8_1/api.rs @@ -1,6 +1,7 @@ use jsonrpsee::core::RpcResult; use m_proc_macros::versioned_rpc; use mp_block::BlockId; +use mp_rpc::v0_8_1::{L1TxnHash, TxnHashWithStatus}; use starknet_types_core::felt::Felt; type SubscriptionItemPendingTxs = super::methods::ws::SubscriptionItem; @@ -65,4 +66,7 @@ pub trait StarknetReadRpcApi { contract_addresses: Option>, contracts_storage_keys: Option>, ) -> RpcResult; + + #[method(name = "getMessagesStatus")] + async fn get_messages_status(&self, tx_hash_l1: L1TxnHash) -> RpcResult>; } diff --git a/madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/get_messages_status.rs b/madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/get_messages_status.rs new file mode 100644 index 0000000000..a2574e27e6 --- /dev/null +++ b/madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/get_messages_status.rs @@ -0,0 +1,34 @@ +use mc_settlement_client::error::SettlementClientError; +use mp_rpc::v0_8_1::{L1TxnHash, TxnHashWithStatus}; + +use crate::{ + utils::display_internal_server_error, + versions::user::v0_7_1::methods::read::get_transaction_status::get_transaction_status, Starknet, + StarknetRpcApiError, StarknetRpcResult, +}; + +pub async fn get_messages_status( + starknet: &Starknet, + transaction_hash: L1TxnHash, +) -> StarknetRpcResult> { + let transaction_hash = transaction_hash.into(); + let settlement_client = starknet.settlement_client().ok_or(StarknetRpcApiError::InternalServerError)?; + let l1_handlers = settlement_client.get_messages_to_l2(transaction_hash).await.map_err(|e| match e { + SettlementClientError::TxNotFound => StarknetRpcApiError::TxnHashNotFound, + e => { + display_internal_server_error(format!( + "Error getting messages to L2 for L1 transaction {transaction_hash:?}: {e}" + )); + StarknetRpcApiError::InternalServerError + } + })?; + + let mut results = Vec::new(); + for msg in l1_handlers { + let transaction_hash = msg.tx.compute_hash(starknet.chain_id(), false, false); + let finality_status = get_transaction_status(starknet, transaction_hash).await?.finality_status; + // TODO: Add failure_reason when we support it in get_transaction_status in v0.8.1. + results.push(TxnHashWithStatus { transaction_hash, finality_status, failure_reason: None }); + } + Ok(results) +} diff --git a/madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/mod.rs b/madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/mod.rs index 4eecbbe523..49dc3ce34e 100644 --- a/madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/mod.rs +++ b/madara/crates/client/rpc/src/versions/user/v0_8_1/methods/read/mod.rs @@ -3,10 +3,11 @@ use crate::Starknet; use jsonrpsee::core::{async_trait, RpcResult}; use mp_block::BlockId; use mp_chain_config::RpcVersion; -use mp_rpc::v0_8_1::{ContractStorageKeysItem, GetStorageProofResult}; +use mp_rpc::v0_8_1::{ContractStorageKeysItem, GetStorageProofResult, L1TxnHash, TxnHashWithStatus}; use starknet_types_core::felt::Felt; pub mod get_compiled_casm; +pub mod get_messages_status; pub mod get_storage_proof; #[async_trait] @@ -28,4 +29,8 @@ impl StarknetReadRpcApiV0_8_1Server for Starknet { ) -> RpcResult { get_storage_proof::get_storage_proof(self, block_id, class_hashes, contract_addresses, contracts_storage_keys) } + + async fn get_messages_status(&self, tx_hash_l1: L1TxnHash) -> RpcResult> { + Ok(get_messages_status::get_messages_status(self, tx_hash_l1).await?) + } } diff --git a/madara/crates/client/settlement_client/src/client.rs b/madara/crates/client/settlement_client/src/client.rs index 866fc93dda..81a61ed2a0 100644 --- a/madara/crates/client/settlement_client/src/client.rs +++ b/madara/crates/client/settlement_client/src/client.rs @@ -3,6 +3,7 @@ use crate::messaging::MessageToL2WithMetadata; use crate::state_update::{StateUpdate, StateUpdateWorker}; use async_trait::async_trait; use futures::stream::BoxStream; +use mp_convert::L1TransactionHash; use mp_transactions::L1HandlerTransactionWithFee; use mp_utils::service::ServiceContext; @@ -108,6 +109,18 @@ pub trait SettlementLayerProvider: Send + Sync { /// - An Error if the call fail async fn message_to_l2_is_pending(&self, msg_hash: &[u8]) -> Result; + /// Fetches L1 to L2 messages associated with a specific L1 transaction hash + /// + /// # Arguments + /// * `l1_tx_hash` - The hash of the L1 transaction + /// # Returns + /// - A vector of L1 to L2 messages associated with the given L1 transaction hash + /// - An error if the call fails + async fn get_messages_to_l2( + &self, + l1_tx_hash: L1TransactionHash, + ) -> Result, SettlementClientError>; + /// Return a block timestamp in second. /// /// # Arguments diff --git a/madara/crates/client/settlement_client/src/error.rs b/madara/crates/client/settlement_client/src/error.rs index 441812a8c7..32f5fc8b14 100644 --- a/madara/crates/client/settlement_client/src/error.rs +++ b/madara/crates/client/settlement_client/src/error.rs @@ -10,6 +10,9 @@ pub enum SettlementClientError { #[error("Starknet client error: {0}")] Starknet(StarknetClientError), + #[error("Transaction not found")] + TxNotFound, + #[error("Missing required field: {0}")] MissingField(&'static str), diff --git a/madara/crates/client/settlement_client/src/eth/mod.rs b/madara/crates/client/settlement_client/src/eth/mod.rs index b09a3739c8..54acf28cc2 100644 --- a/madara/crates/client/settlement_client/src/eth/mod.rs +++ b/madara/crates/client/settlement_client/src/eth/mod.rs @@ -8,7 +8,7 @@ use crate::utils::convert_log_state_update; use alloy::eips::{BlockId, BlockNumberOrTag}; use alloy::primitives::{keccak256, Address, B256, I256, U256}; use alloy::providers::{Provider, ProviderBuilder, ReqwestProvider, RootProvider}; -use alloy::rpc::types::Filter; +use alloy::rpc::types::{Filter, FilteredParams}; use alloy::sol; use alloy::sol_types::SolValue; use alloy::transports::http::{Client, Http}; @@ -17,7 +17,7 @@ use bitvec::macros::internal::funty::Fundamental; use error::EthereumClientError; use futures::stream::BoxStream; use futures::StreamExt; -use mp_convert::{FeltExt, ToFelt}; +use mp_convert::{FeltExt, L1TransactionHash, ToFelt}; use mp_transactions::L1HandlerTransactionWithFee; use mp_utils::service::ServiceContext; use std::str::FromStr; @@ -306,6 +306,33 @@ impl SettlementLayerProvider for EthereumClient { Ok(cancellation_timestamp._0 != U256::ZERO) } + async fn get_messages_to_l2( + &self, + l1_tx_hash: L1TransactionHash, + ) -> Result, SettlementClientError> { + let l1_tx_hash = B256::from(l1_tx_hash.into_eth().0); + let receipt = self + .provider + .get_transaction_receipt(l1_tx_hash) + .await + .map_err(|e| -> SettlementClientError { + EthereumClientError::Rpc(format!("Failed to get transaction receipt: {}", e)).into() + })? + .ok_or(SettlementClientError::TxNotFound)?; + + let filter = FilteredParams::new(Some(self.l1_core_contract.LogMessageToL2_filter().filter)); + + receipt + .inner + .logs() + .iter() + .filter(|log| filter.filter_address(&log.address()) && filter.filter_topics(log.topics())) + .filter_map(|log| log.log_decode::().ok()) + .map(|log| log.inner.data) + .map(TryInto::try_into) + .collect::, _>>() + } + async fn get_block_n_timestamp(&self, l1_block_n: u64) -> Result { let block = self .provider diff --git a/madara/crates/client/settlement_client/src/lib.rs b/madara/crates/client/settlement_client/src/lib.rs index f9b97e5649..baa6c0a8da 100644 --- a/madara/crates/client/settlement_client/src/lib.rs +++ b/madara/crates/client/settlement_client/src/lib.rs @@ -15,7 +15,7 @@ use std::sync::Arc; use tokio::sync::Notify; use url::Url; -mod client; +pub mod client; pub mod error; mod eth; pub mod gas_price; diff --git a/madara/crates/client/settlement_client/src/starknet/mod.rs b/madara/crates/client/settlement_client/src/starknet/mod.rs index dc207e353a..5566f3d251 100644 --- a/madara/crates/client/settlement_client/src/starknet/mod.rs +++ b/madara/crates/client/settlement_client/src/starknet/mod.rs @@ -8,6 +8,7 @@ use async_trait::async_trait; use bigdecimal::ToPrimitive; use futures::stream::BoxStream; use futures::{StreamExt, TryStreamExt}; +use mp_convert::L1TransactionHash; use mp_transactions::L1HandlerTransactionWithFee; use mp_utils::service::ServiceContext; use starknet_core::types::{BlockId, BlockTag, EmittedEvent, EventFilter, FunctionCall, MaybePendingBlockWithTxHashes}; @@ -343,6 +344,57 @@ impl SettlementLayerProvider for StarknetClient { Ok(matches!(status, MessageToAppchainStatus::Pending(_))) } + async fn get_messages_to_l2( + &self, + l1_tx_hash: L1TransactionHash, + ) -> Result, SettlementClientError> { + let l1_tx_hash = l1_tx_hash.into_starknet().map_err(|e| -> SettlementClientError { + StarknetClientError::Conversion(format!("Invalid L1 transaction hash: {}", e)).into() + })?; + let receipt = + self.provider.get_transaction_receipt(l1_tx_hash).await.map_err(|e| -> SettlementClientError { + StarknetClientError::L1ToL2Messaging { message: format!("Failed to fetch transaction receipt: {}", e) } + .into() + })?; + + let block_number = receipt.block.block_number(); + let block_hash = receipt.block.block_hash(); + + let events = match receipt.receipt { + starknet_core::types::TransactionReceipt::Invoke(receipt) => receipt.events, + starknet_core::types::TransactionReceipt::Declare(receipt) => receipt.events, + starknet_core::types::TransactionReceipt::Deploy(receipt) => receipt.events, + starknet_core::types::TransactionReceipt::DeployAccount(receipt) => receipt.events, + starknet_core::types::TransactionReceipt::L1Handler(receipt) => receipt.events, + }; + + let core_address = self.core_contract_address; + events + .into_iter() + .filter(|event| { + event.from_address == core_address + && event.keys + == vec![get_selector_from_name("MessageSent").expect("Failed to get MessageSent selector")] + }) + .map(|event| EmittedEvent { + from_address: event.from_address, + keys: event.keys, + data: event.data, + block_hash, + block_number, + transaction_hash: l1_tx_hash, + }) + .map(MessageToL2WithMetadata::try_from) + .map(|res| { + res.map(|m| m.message).map_err(|e| { + SettlementClientError::from(StarknetClientError::L1ToL2Messaging { + message: format!("Failed to parse MessageToL2 event: {e}"), + }) + }) + }) + .collect::, SettlementClientError>>() + } + async fn get_block_n_timestamp(&self, l1_block_n: u64) -> Result { tracing::debug!("get_block_n_timestamp l1_block_n={l1_block_n}"); let block = self.provider.get_block_with_tx_hashes(BlockId::Number(l1_block_n)).await.map_err( diff --git a/madara/crates/primitives/convert/Cargo.toml b/madara/crates/primitives/convert/Cargo.toml index 2dd51ae28c..b81e605ed7 100644 --- a/madara/crates/primitives/convert/Cargo.toml +++ b/madara/crates/primitives/convert/Cargo.toml @@ -15,6 +15,7 @@ workspace = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] +mp-rpc.workspace = true # Starknet alloy.workspace = true diff --git a/madara/crates/primitives/convert/src/felt.rs b/madara/crates/primitives/convert/src/felt.rs index 6b11259ab9..af012634bc 100644 --- a/madara/crates/primitives/convert/src/felt.rs +++ b/madara/crates/primitives/convert/src/felt.rs @@ -105,6 +105,12 @@ impl L1TransactionHash { } } +impl From for L1TransactionHash { + fn from(value: mp_rpc::v0_8_1::L1TxnHash) -> Self { + Self(value.0) + } +} + #[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)] #[serde(transparent)] pub struct L1CoreContractNonce(u64); diff --git a/madara/crates/primitives/rpc/src/custom/l1.rs b/madara/crates/primitives/rpc/src/custom/l1.rs new file mode 100644 index 0000000000..eafbc608cd --- /dev/null +++ b/madara/crates/primitives/rpc/src/custom/l1.rs @@ -0,0 +1,103 @@ +use serde::{Deserialize, Deserializer, Serialize}; + +#[derive(Debug, Clone, Eq, Hash, PartialEq)] +pub struct L1TxnHash(pub [u8; 32]); + +impl Serialize for L1TxnHash { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut s = String::with_capacity(66); + s.push_str("0x"); + for byte in &self.0 { + s.push_str(&format!("{:02x}", byte)); + } + serializer.serialize_str(&s) + } +} + +impl<'de> Deserialize<'de> for L1TxnHash { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: &str = Deserialize::deserialize(deserializer)?; + let s = s.strip_prefix("0x").unwrap_or(s); + if s.len() > 64 { + return Err(serde::de::Error::custom("Invalid length for L1TxnHash")); + } + let mut bytes = [0u8; 32]; + for (i, byte) in s.as_bytes().chunks(2).enumerate() { + let byte_str = std::str::from_utf8(byte).map_err(serde::de::Error::custom)?; + bytes[i] = u8::from_str_radix(byte_str, 16).map_err(serde::de::Error::custom)?; + } + Ok(L1TxnHash(bytes)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json; + + const TEST_HASH: L1TxnHash = L1TxnHash([ + 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, + 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, + ]); + + const TEST_HASH_STR: &str = "\"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\""; + + #[test] + fn test_l1_transaction_hash_serialization() { + let hash = L1TxnHash([0u8; 32]); + let serialized = serde_json::to_string(&hash).unwrap(); + assert_eq!(serialized, "\"0x0000000000000000000000000000000000000000000000000000000000000000\""); + let deserialized: L1TxnHash = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, hash); + } + + #[test] + fn test_l1_transaction_hash_deserialization() { + let deserialized: L1TxnHash = serde_json::from_str(TEST_HASH_STR).unwrap(); + assert_eq!(deserialized, TEST_HASH); + } + + #[test] + fn test_l1_transaction_hash_deserialization_no_prefix() { + let deserialized: L1TxnHash = + serde_json::from_str("\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"").unwrap(); + assert_eq!(deserialized, TEST_HASH); + } + + #[test] + fn test_l1_transaction_hash_deserialization_small() { + let deserialized: L1TxnHash = + serde_json::from_str("\"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"").unwrap(); + assert_eq!(deserialized, TEST_HASH); + } + + #[test] + fn test_l1_transaction_hash_deserialization_invalid_length() { + let result: Result = serde_json::from_str("\"0x123\""); + assert!(result.is_err()); + } + + #[test] + fn test_l1_transaction_hash_deserialization_invalid_hex() { + let result: Result = serde_json::from_str( + "\" +0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdeg\"", + ); + assert!(result.is_err()); + } + + #[test] + fn test_l1_transaction_hash_serialization_deserialization() { + let original = TEST_HASH; + let serialized = serde_json::to_string(&original).unwrap(); + assert_eq!(serialized, TEST_HASH_STR); + let deserialized: L1TxnHash = serde_json::from_str(&serialized).unwrap(); + assert_eq!(deserialized, original); + } +} diff --git a/madara/crates/primitives/rpc/src/custom/mod.rs b/madara/crates/primitives/rpc/src/custom/mod.rs index 6b8ecbf9a4..b3a330c30f 100644 --- a/madara/crates/primitives/rpc/src/custom/mod.rs +++ b/madara/crates/primitives/rpc/src/custom/mod.rs @@ -1,7 +1,9 @@ mod block_id; +mod l1; mod query; mod syncing_status; pub use self::block_id::*; +pub use self::l1::*; pub use self::query::*; pub use self::syncing_status::*; diff --git a/madara/crates/primitives/rpc/src/v0_8_1/mod.rs b/madara/crates/primitives/rpc/src/v0_8_1/mod.rs index 30b75ae8a9..1a00a6f281 100644 --- a/madara/crates/primitives/rpc/src/v0_8_1/mod.rs +++ b/madara/crates/primitives/rpc/src/v0_8_1/mod.rs @@ -1,6 +1,6 @@ //! v0.8.1 of the API. pub use crate::custom::{ - BlockId, BroadcastedDeclareTxn, BroadcastedDeployAccountTxn, BroadcastedInvokeTxn, SyncingStatus, + BlockId, BroadcastedDeclareTxn, BroadcastedDeployAccountTxn, BroadcastedInvokeTxn, L1TxnHash, SyncingStatus, }; mod starknet_api_openrpc; diff --git a/madara/crates/primitives/rpc/src/v0_8_1/starknet_api_openrpc.rs b/madara/crates/primitives/rpc/src/v0_8_1/starknet_api_openrpc.rs index 7d7bafac7d..0e016f676c 100644 --- a/madara/crates/primitives/rpc/src/v0_8_1/starknet_api_openrpc.rs +++ b/madara/crates/primitives/rpc/src/v0_8_1/starknet_api_openrpc.rs @@ -1,5 +1,5 @@ use super::{ - BlockHash, BlockNumber, BlockStatus, L1DaMode, PriceUnit, ResourcePrice, TransactionAndReceipt, TxnHash, + BlockHash, BlockNumber, BlockStatus, L1DaMode, PriceUnit, ResourcePrice, TransactionAndReceipt, TxnHash, TxnStatus, TxnWithHash, }; use serde::{Deserialize, Serialize}; @@ -128,6 +128,14 @@ pub enum MaybePendingBlockWithTxs { Pending(PendingBlockWithTxs), } +#[derive(Eq, Hash, PartialEq, Serialize, Deserialize, Clone, Debug)] +pub struct TxnHashWithStatus { + pub transaction_hash: TxnHash, + pub finality_status: TxnStatus, + #[serde(default)] + pub failure_reason: Option, +} + #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct FeeEstimate { /// The Ethereum gas consumption of the transaction, charged for L1->L2 messages and, depending on the block's DA_MODE, state diffs diff --git a/madara/node/src/main.rs b/madara/node/src/main.rs index 81d8f8aed2..52ca7cd490 100644 --- a/madara/node/src/main.rs +++ b/madara/node/src/main.rs @@ -265,8 +265,12 @@ async fn main() -> anyhow::Result<()> { // User-facing RPC - let service_rpc_user = - RpcService::user(run_cmd.rpc_params.clone(), Arc::clone(service_db.backend()), tx_submit.clone()); + let service_rpc_user = RpcService::user( + run_cmd.rpc_params.clone(), + Arc::clone(service_db.backend()), + tx_submit.clone(), + service_l1_sync.provider(), + ); // Admin-facing RPC (for node operators) diff --git a/madara/node/src/service/l1.rs b/madara/node/src/service/l1.rs index 4626b7e3a6..1837bef00c 100644 --- a/madara/node/src/service/l1.rs +++ b/madara/node/src/service/l1.rs @@ -1,6 +1,7 @@ use crate::cli::l1::{L1SyncParams, MadaraSettlementLayer}; use anyhow::{bail, Context}; use mc_db::MadaraBackend; +use mc_settlement_client::client::SettlementLayerProvider; use mc_settlement_client::gas_price::GasPriceProviderConfigBuilder; use mc_settlement_client::state_update::L1HeadSender; use mc_settlement_client::sync::SyncWorkerConfig; @@ -123,6 +124,10 @@ impl L1SyncService { Arc::new(L1SyncDisabledClient) } } + + pub fn provider(&self) -> Option> { + self.client.as_ref().map(|c| c.provider()) + } } #[async_trait::async_trait] diff --git a/madara/node/src/service/rpc/mod.rs b/madara/node/src/service/rpc/mod.rs index b62c1c8860..926213397a 100644 --- a/madara/node/src/service/rpc/mod.rs +++ b/madara/node/src/service/rpc/mod.rs @@ -4,6 +4,7 @@ use jsonrpsee::server::ServerHandle; use mc_block_production::BlockProductionHandle; use mc_db::MadaraBackend; use mc_rpc::{rpc_api_admin, rpc_api_user, Starknet}; +use mc_settlement_client::client::SettlementLayerProvider; use metrics::RpcMetrics; use mp_utils::service::{MadaraServiceId, PowerOfTwo, Service, ServiceId, ServiceRunner}; use server::{start_server, ServerConfig}; @@ -26,6 +27,7 @@ pub struct RpcService { server_handle: Option, rpc_type: RpcType, block_prod_handle: Option, + settlement_client: Option>, } impl RpcService { @@ -33,6 +35,7 @@ impl RpcService { config: RpcParams, backend: Arc, submit_tx_provider: MakeSubmitTransactionSwitch, + settlement_client: Option>, ) -> Self { Self { config, @@ -41,6 +44,7 @@ impl RpcService { server_handle: None, rpc_type: RpcType::User, block_prod_handle: None, + settlement_client, } } @@ -57,6 +61,7 @@ impl RpcService { server_handle: None, rpc_type: RpcType::Admin, block_prod_handle: Some(block_prod_handle), + settlement_client: None, } } } @@ -67,6 +72,7 @@ impl Service for RpcService { let config = self.config.clone(); let backend = Arc::clone(&self.backend); let submit_tx_provider = self.submit_tx_provider.clone(); + let settlement_client = self.settlement_client.clone(); let rpc_type = self.rpc_type.clone(); let (stop_handle, server_handle) = jsonrpsee::server::stop_channel(); @@ -82,6 +88,7 @@ impl Service for RpcService { submit_tx, config.storage_proof_config(), block_prod_handle, + settlement_client, ctx.clone(), ); let metrics = RpcMetrics::register()?;