diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 2d95247a3fc..c124129eece 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -442,7 +442,7 @@ pub struct RpcAddOns< /// Additional RPC add-ons. pub hooks: RpcHooks, /// Builder for `EthApi` - eth_api_builder: EthB, + pub eth_api_builder: EthB, /// Engine validator engine_validator_builder: EV, /// Builder for `EngineApi` diff --git a/crates/scroll/node/src/addons.rs b/crates/scroll/node/src/addons.rs index 8d48748d123..c5fc9d8ced8 100644 --- a/crates/scroll/node/src/addons.rs +++ b/crates/scroll/node/src/addons.rs @@ -17,7 +17,10 @@ use reth_scroll_chainspec::ScrollChainSpec; use reth_scroll_engine_primitives::ScrollEngineTypes; use reth_scroll_evm::ScrollNextBlockEnvAttributes; use reth_scroll_primitives::ScrollPrimitives; -use reth_scroll_rpc::{eth::ScrollEthApiBuilder, ScrollEthApiError}; +use reth_scroll_rpc::{ + eth::{ScrollEthApiBuilder, DEFAULT_MIN_SUGGESTED_PRIORITY_FEE}, + ScrollEthApiError, +}; use revm::context::TxEnv; use scroll_alloy_evm::ScrollTransactionIntoTxEnv; use scroll_alloy_network::Scroll; @@ -148,8 +151,7 @@ impl Default for ScrollAddOnsBuilder { Self { sequencer_url: None, payload_size_limit: SCROLL_DEFAULT_PAYLOAD_SIZE_LIMIT, - // TODO (scroll): update with default values. - min_suggested_priority_fee: 1_000_000, + min_suggested_priority_fee: DEFAULT_MIN_SUGGESTED_PRIORITY_FEE, _nt: PhantomData, rpc_middleware: Identity::new(), } diff --git a/crates/scroll/rpc/src/eth/mod.rs b/crates/scroll/rpc/src/eth/mod.rs index 6deb09f7dc3..8084177efa2 100644 --- a/crates/scroll/rpc/src/eth/mod.rs +++ b/crates/scroll/rpc/src/eth/mod.rs @@ -65,12 +65,14 @@ impl ScrollEthApi { sequencer_client: Option, min_suggested_priority_fee: U256, payload_size_limit: u64, + propagate_local_transactions: bool, ) -> Self { let inner = Arc::new(ScrollEthApiInner { eth_api, min_suggested_priority_fee, payload_size_limit, sequencer_client, + propagate_local_transactions, }); Self { inner } } @@ -277,6 +279,8 @@ pub struct ScrollEthApiInner { min_suggested_priority_fee: U256, /// Maximum payload size payload_size_limit: u64, + /// whether local transactions should be propagated. + propagate_local_transactions: bool, } impl ScrollEthApiInner { @@ -316,6 +320,8 @@ pub struct ScrollEthApiBuilder { min_suggested_priority_fee: u64, /// Maximum payload size payload_size_limit: u64, + /// whether local transactions should be propagated. + propagate_local_transactions: bool, /// Marker for network types. _nt: PhantomData, } @@ -326,6 +332,7 @@ impl Default for ScrollEthApiBuilder { sequencer_url: None, min_suggested_priority_fee: DEFAULT_MIN_SUGGESTED_PRIORITY_FEE, payload_size_limit: DEFAULT_PAYLOAD_SIZE_LIMIT, + propagate_local_transactions: true, _nt: PhantomData, } } @@ -354,6 +361,15 @@ impl ScrollEthApiBuilder { self.payload_size_limit = limit; self } + + /// With whether local transactions should be propagated. + pub const fn with_propagate_local_transactions( + &mut self, + propagate_local_transactions: bool, + ) -> &mut Self { + self.propagate_local_transactions = propagate_local_transactions; + self + } } impl EthApiBuilder for ScrollEthApiBuilder @@ -367,7 +383,13 @@ where type EthApi = ScrollEthApi>; async fn build_eth_api(self, ctx: EthApiCtx<'_, N>) -> eyre::Result { - let Self { min_suggested_priority_fee, payload_size_limit, sequencer_url, .. } = self; + let Self { + min_suggested_priority_fee, + payload_size_limit, + sequencer_url, + propagate_local_transactions, + .. + } = self; let rpc_converter = RpcConverter::new(ScrollReceiptConverter::default()) .with_mapper(ScrollTxInfoMapper::new(ctx.components.provider().clone())); @@ -388,6 +410,7 @@ where sequencer_client, U256::from(min_suggested_priority_fee), payload_size_limit, + propagate_local_transactions, )) } } diff --git a/crates/scroll/rpc/src/eth/transaction.rs b/crates/scroll/rpc/src/eth/transaction.rs index 1364b1ee642..d10983e798f 100644 --- a/crates/scroll/rpc/src/eth/transaction.rs +++ b/crates/scroll/rpc/src/eth/transaction.rs @@ -34,40 +34,45 @@ where let recovered = recover_raw_transaction(&tx)?; let pool_transaction = ::Transaction::from_pooled(recovered); + // submit the transaction to the pool with a `Local` origin + let AddedTransactionOutcome { hash, .. } = self + .pool() + .add_transaction(TransactionOrigin::Local, pool_transaction.clone()) + .await + .map_err(Self::Error::from_eth_err)?; + // On scroll, transactions are forwarded directly to the sequencer to be included in // blocks that it builds. - if let Some(client) = self.raw_tx_forwarder().as_ref() { + if let Some(client) = self.raw_tx_forwarder() { tracing::debug!(target: "scroll::rpc::eth", hash = %pool_transaction.hash(), "forwarding raw transaction to sequencer"); - // Retain tx in local tx pool before forwarding to sequencer rpc, for local RPC usage. - let AddedTransactionOutcome { hash, .. } = self - .pool() - .add_transaction(TransactionOrigin::Local, pool_transaction.clone()) - .await - .map_err(Self::Error::from_eth_err)?; - - tracing::debug!(target: "scroll::rpc::eth", %hash, "successfully added transaction to local tx pool"); - - // Forward to remote sequencer RPC. - match client.forward_raw_transaction(&tx).await { - Ok(sequencer_hash) => { - tracing::debug!(target: "scroll::rpc::eth", local_hash=%hash, sequencer_hash=%sequencer_hash, "successfully forwarded transaction to sequencer"); - } - Err(err) => { - tracing::warn!(target: "scroll::rpc::eth", %err, %hash, "failed to forward transaction to sequencer, but transaction is in local pool"); + if self.inner.propagate_local_transactions { + // Forward to remote sequencer RPC asynchronously (fire and forget) + let client = client.clone(); + tokio::spawn(async move { + match client.forward_raw_transaction(&tx).await { + Ok(sequencer_hash) => { + tracing::debug!(target: "scroll::rpc::eth", local_hash=%hash, %sequencer_hash, "successfully forwarded transaction to sequencer"); + } + Err(err) => { + tracing::warn!(target: "scroll::rpc::eth", %err, local_hash=%hash, "failed to forward transaction to sequencer, but transaction is in local pool and will be propagated"); + } + } + }); + } else { + // Forward to remote sequencer RPC synchronously + match client.forward_raw_transaction(&tx).await { + Ok(sequencer_hash) => { + tracing::debug!(target: "scroll::rpc::eth", local_hash=%hash, %sequencer_hash, "successfully forwarded transaction to sequencer"); + } + Err(err) => { + tracing::warn!(target: "scroll::rpc::eth", %err, local_hash=%hash, "failed to forward transaction to sequencer"); + return Err(ScrollEthApiError::Sequencer(err)); + } } } - - return Ok(hash); } - // submit the transaction to the pool with a `Local` origin - let AddedTransactionOutcome { hash, .. } = self - .pool() - .add_transaction(TransactionOrigin::Local, pool_transaction) - .await - .map_err(Self::Error::from_eth_err)?; - Ok(hash) } }