From 7b5d8adb9f3a653fad908aeb183c50cfe43a3077 Mon Sep 17 00:00:00 2001 From: Morty Date: Thu, 7 Aug 2025 03:41:29 +0800 Subject: [PATCH 1/8] refactor: make tx forwarding to sequencer non-blocking if tx propagate enabled --- crates/node/builder/src/rpc.rs | 5 +++++ crates/scroll/node/src/addons.rs | 30 ++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 2d95247a3fc..6ac47422f42 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -493,6 +493,11 @@ where } } + /// Returns a mutable reference to the eth API builder. + pub fn eth_api_builder(&mut self) -> &mut EthB { + &mut self.eth_api_builder + } + /// Maps the [`EngineApiBuilder`] builder type. pub fn with_engine_api( self, diff --git a/crates/scroll/node/src/addons.rs b/crates/scroll/node/src/addons.rs index 8d48748d123..36d94c45c18 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; @@ -137,6 +140,8 @@ pub struct ScrollAddOnsBuilder { 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, /// RPC middleware to use @@ -147,9 +152,9 @@ impl Default for ScrollAddOnsBuilder { fn default() -> Self { Self { sequencer_url: None, + min_suggested_priority_fee: DEFAULT_MIN_SUGGESTED_PRIORITY_FEE, payload_size_limit: SCROLL_DEFAULT_PAYLOAD_SIZE_LIMIT, - // TODO (scroll): update with default values. - min_suggested_priority_fee: 1_000_000, + propagate_local_transactions: true, _nt: PhantomData, rpc_middleware: Identity::new(), } @@ -178,13 +183,30 @@ impl ScrollAddOnsBuilder { self } + /// With whether local transactions should be propagated. + pub const fn with_propagate_local_transactions( + mut self, + propagate_local_transactions: bool, + ) -> Self { + self.propagate_local_transactions = propagate_local_transactions; + self + } + /// Configure the RPC middleware to use pub fn with_rpc_middleware(self, rpc_middleware: T) -> ScrollAddOnsBuilder { - let Self { sequencer_url, min_suggested_priority_fee, payload_size_limit, _nt, .. } = self; + let Self { + sequencer_url, + min_suggested_priority_fee, + payload_size_limit, + propagate_local_transactions, + _nt, + .. + } = self; ScrollAddOnsBuilder { sequencer_url, payload_size_limit, min_suggested_priority_fee, + propagate_local_transactions, _nt, rpc_middleware, } From abe7a956dd4d599539026db752ed6273a2733449 Mon Sep 17 00:00:00 2001 From: Morty Date: Thu, 7 Aug 2025 03:54:57 +0800 Subject: [PATCH 2/8] refactor: make tx forwarding to sequencer non-blocking if tx propagate enabled --- crates/scroll/node/src/addons.rs | 29 +++-------------------------- crates/scroll/rpc/src/eth/mod.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/crates/scroll/node/src/addons.rs b/crates/scroll/node/src/addons.rs index 36d94c45c18..6d966e80995 100644 --- a/crates/scroll/node/src/addons.rs +++ b/crates/scroll/node/src/addons.rs @@ -17,10 +17,7 @@ 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, DEFAULT_MIN_SUGGESTED_PRIORITY_FEE}, - 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; @@ -140,8 +137,6 @@ pub struct ScrollAddOnsBuilder { 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, /// RPC middleware to use @@ -152,9 +147,8 @@ impl Default for ScrollAddOnsBuilder { fn default() -> Self { Self { sequencer_url: None, - min_suggested_priority_fee: DEFAULT_MIN_SUGGESTED_PRIORITY_FEE, payload_size_limit: SCROLL_DEFAULT_PAYLOAD_SIZE_LIMIT, - propagate_local_transactions: true, + min_suggested_priority_fee: DEFAULT_MIN_SUGGESTED_PRIORITY_FEE, _nt: PhantomData, rpc_middleware: Identity::new(), } @@ -183,30 +177,13 @@ impl ScrollAddOnsBuilder { self } - /// With whether local transactions should be propagated. - pub const fn with_propagate_local_transactions( - mut self, - propagate_local_transactions: bool, - ) -> Self { - self.propagate_local_transactions = propagate_local_transactions; - self - } - /// Configure the RPC middleware to use pub fn with_rpc_middleware(self, rpc_middleware: T) -> ScrollAddOnsBuilder { - let Self { - sequencer_url, - min_suggested_priority_fee, - payload_size_limit, - propagate_local_transactions, - _nt, - .. - } = self; + let Self { sequencer_url, min_suggested_priority_fee, payload_size_limit, _nt, .. } = self; ScrollAddOnsBuilder { sequencer_url, payload_size_limit, min_suggested_priority_fee, - propagate_local_transactions, _nt, rpc_middleware, } diff --git a/crates/scroll/rpc/src/eth/mod.rs b/crates/scroll/rpc/src/eth/mod.rs index 6deb09f7dc3..7ea0d6833f1 100644 --- a/crates/scroll/rpc/src/eth/mod.rs +++ b/crates/scroll/rpc/src/eth/mod.rs @@ -316,6 +316,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 +328,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 +357,12 @@ 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) -> Self { + self.propagate_local_transactions = propagate_local_transactions; + self + } } impl EthApiBuilder for ScrollEthApiBuilder From 9af5092079e601b211a28dede1a089830b8bb510 Mon Sep 17 00:00:00 2001 From: Morty Date: Thu, 7 Aug 2025 04:16:49 +0800 Subject: [PATCH 3/8] change eth_api_builder to pub --- crates/node/builder/src/rpc.rs | 7 +------ crates/scroll/node/src/addons.rs | 5 ++++- crates/scroll/rpc/src/eth/mod.rs | 7 +++++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 6ac47422f42..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` @@ -493,11 +493,6 @@ where } } - /// Returns a mutable reference to the eth API builder. - pub fn eth_api_builder(&mut self) -> &mut EthB { - &mut self.eth_api_builder - } - /// Maps the [`EngineApiBuilder`] builder type. pub fn with_engine_api( self, diff --git a/crates/scroll/node/src/addons.rs b/crates/scroll/node/src/addons.rs index 6d966e80995..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, DEFAULT_MIN_SUGGESTED_PRIORITY_FEE}, 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; diff --git a/crates/scroll/rpc/src/eth/mod.rs b/crates/scroll/rpc/src/eth/mod.rs index 7ea0d6833f1..21473b32428 100644 --- a/crates/scroll/rpc/src/eth/mod.rs +++ b/crates/scroll/rpc/src/eth/mod.rs @@ -307,7 +307,7 @@ pub const DEFAULT_MIN_SUGGESTED_PRIORITY_FEE: u64 = 100; pub const DEFAULT_PAYLOAD_SIZE_LIMIT: u64 = 122_880; /// A type that knows how to build a [`ScrollEthApi`]. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ScrollEthApiBuilder { /// Sequencer client, configured to forward submitted transactions to sequencer of given Scroll /// network. @@ -359,7 +359,10 @@ impl ScrollEthApiBuilder { } /// With whether local transactions should be propagated. - pub const fn with_propagate_local_transactions(mut self, propagate_local_transactions: bool) -> Self { + pub const fn with_propagate_local_transactions( + mut self, + propagate_local_transactions: bool, + ) -> Self { self.propagate_local_transactions = propagate_local_transactions; self } From 71b5fb4740b5ff86ea0626e316d010032e5d7b15 Mon Sep 17 00:00:00 2001 From: Morty Date: Thu, 7 Aug 2025 05:07:04 +0800 Subject: [PATCH 4/8] fix: with_propagate_local_transactions --- crates/scroll/rpc/src/eth/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/scroll/rpc/src/eth/mod.rs b/crates/scroll/rpc/src/eth/mod.rs index 21473b32428..2b44ce3e153 100644 --- a/crates/scroll/rpc/src/eth/mod.rs +++ b/crates/scroll/rpc/src/eth/mod.rs @@ -360,9 +360,9 @@ impl ScrollEthApiBuilder { /// With whether local transactions should be propagated. pub const fn with_propagate_local_transactions( - mut self, + &mut self, propagate_local_transactions: bool, - ) -> Self { + ) -> &mut Self { self.propagate_local_transactions = propagate_local_transactions; self } From fdf1379f7ec43282d6760bfcc3a22743f4386e01 Mon Sep 17 00:00:00 2001 From: Morty Date: Thu, 7 Aug 2025 06:06:07 +0800 Subject: [PATCH 5/8] update send_raw_transaction --- crates/scroll/rpc/src/eth/mod.rs | 13 +++++- crates/scroll/rpc/src/eth/transaction.rs | 59 +++++++++++++----------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/crates/scroll/rpc/src/eth/mod.rs b/crates/scroll/rpc/src/eth/mod.rs index 2b44ce3e153..4210895b01e 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 { @@ -379,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())); @@ -400,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..8e35b89d903 100644 --- a/crates/scroll/rpc/src/eth/transaction.rs +++ b/crates/scroll/rpc/src/eth/transaction.rs @@ -34,40 +34,47 @@ 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(); + let tx_clone = tx.clone(); + let hash_clone = hash; + tokio::spawn(async move { + match client.forward_raw_transaction(&tx_clone).await { + Ok(sequencer_hash) => { + tracing::debug!(target: "scroll::rpc::eth", local_hash=%hash_clone, sequencer_hash=%sequencer_hash, "successfully forwarded transaction to sequencer"); + } + Err(err) => { + tracing::warn!(target: "scroll::rpc::eth", %err, %hash_clone, "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=%sequencer_hash, "successfully forwarded transaction to sequencer"); + } + Err(err) => { + tracing::warn!(target: "scroll::rpc::eth", %err, %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) } } From 72647dffbe31cb3fcbb03a1e5a91fc6c6eac8d42 Mon Sep 17 00:00:00 2001 From: Morty Date: Thu, 7 Aug 2025 15:26:57 +0800 Subject: [PATCH 6/8] rm ScrollEthApiBuilder clone trait --- crates/scroll/rpc/src/eth/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/scroll/rpc/src/eth/mod.rs b/crates/scroll/rpc/src/eth/mod.rs index 4210895b01e..8084177efa2 100644 --- a/crates/scroll/rpc/src/eth/mod.rs +++ b/crates/scroll/rpc/src/eth/mod.rs @@ -311,7 +311,7 @@ pub const DEFAULT_MIN_SUGGESTED_PRIORITY_FEE: u64 = 100; pub const DEFAULT_PAYLOAD_SIZE_LIMIT: u64 = 122_880; /// A type that knows how to build a [`ScrollEthApi`]. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ScrollEthApiBuilder { /// Sequencer client, configured to forward submitted transactions to sequencer of given Scroll /// network. From fde29244de4cc4b824e9f66c7a39be2d0c9280d3 Mon Sep 17 00:00:00 2001 From: Morty Date: Sat, 9 Aug 2025 00:25:18 +0800 Subject: [PATCH 7/8] address comment --- crates/scroll/rpc/src/eth/transaction.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/scroll/rpc/src/eth/transaction.rs b/crates/scroll/rpc/src/eth/transaction.rs index 8e35b89d903..a08eddfa84b 100644 --- a/crates/scroll/rpc/src/eth/transaction.rs +++ b/crates/scroll/rpc/src/eth/transaction.rs @@ -49,15 +49,14 @@ where if self.inner.propagate_local_transactions { // Forward to remote sequencer RPC asynchronously (fire and forget) let client = client.clone(); - let tx_clone = tx.clone(); - let hash_clone = hash; + let tx = tx.clone(); tokio::spawn(async move { - match client.forward_raw_transaction(&tx_clone).await { + match client.forward_raw_transaction(&tx).await { Ok(sequencer_hash) => { - tracing::debug!(target: "scroll::rpc::eth", local_hash=%hash_clone, sequencer_hash=%sequencer_hash, "successfully forwarded transaction to sequencer"); + 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, %hash_clone, "failed to forward transaction to sequencer, but transaction is in local pool and will be propagated"); + 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"); } } }); @@ -65,10 +64,10 @@ where // 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=%sequencer_hash, "successfully forwarded transaction to sequencer"); + 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, %hash, "failed to forward transaction to sequencer"); + tracing::warn!(target: "scroll::rpc::eth", %err, local_hash=%hash, "failed to forward transaction to sequencer"); return Err(ScrollEthApiError::Sequencer(err)); } } From 886f8eab8e1b82fef982b62e06283d5b76450cae Mon Sep 17 00:00:00 2001 From: Morty Date: Sat, 9 Aug 2025 00:35:49 +0800 Subject: [PATCH 8/8] perfect logic --- crates/scroll/rpc/src/eth/transaction.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/scroll/rpc/src/eth/transaction.rs b/crates/scroll/rpc/src/eth/transaction.rs index a08eddfa84b..d10983e798f 100644 --- a/crates/scroll/rpc/src/eth/transaction.rs +++ b/crates/scroll/rpc/src/eth/transaction.rs @@ -49,7 +49,6 @@ where if self.inner.propagate_local_transactions { // Forward to remote sequencer RPC asynchronously (fire and forget) let client = client.clone(); - let tx = tx.clone(); tokio::spawn(async move { match client.forward_raw_transaction(&tx).await { Ok(sequencer_hash) => {