Repository: paradigmxyz/reth (language: rust)

Issue:
Add cli option for enabling sendrawtxconditionals
ref https://github.com/ethereum-optimism/op-geth/blob/928070c7fc097362ed2d40a4f72889ba91544931/eth/backend.go#L376-L376

most of this is already part of https://github.com/paradigmxyz/reth/pull/13926/files


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 63cd4dc712f6..546c6fc8582a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8518,6 +8518,7 @@ dependencies = [
  "reth-primitives-traits",
  "reth-provider",
  "reth-revm",
+ "reth-rpc-eth-api",
  "reth-rpc-eth-types",
  "reth-rpc-server-types",
  "reth-tasks",
diff --git a/crates/optimism/node/Cargo.toml b/crates/optimism/node/Cargo.toml
index 8f711a9df313..c2dbd5f13ba5 100644
--- a/crates/optimism/node/Cargo.toml
+++ b/crates/optimism/node/Cargo.toml
@@ -31,6 +31,7 @@ reth-evm.workspace = true
 reth-revm = { workspace = true, features = ["std"] }
 reth-trie-db.workspace = true
 reth-rpc-server-types.workspace = true
+reth-rpc-eth-api.workspace = true
 reth-rpc-eth-types.workspace = true
 reth-tasks = { workspace = true, optional = true }
 
diff --git a/crates/optimism/node/src/args.rs b/crates/optimism/node/src/args.rs
index 87c8c1be6450..49fd830b7d4e 100644
--- a/crates/optimism/node/src/args.rs
+++ b/crates/optimism/node/src/args.rs
@@ -33,6 +33,10 @@ pub struct RollupArgs {
     /// enables discovery v4 if provided
     #[arg(long = "rollup.discovery.v4", default_value = "false")]
     pub discovery_v4: bool,
+
+    /// Enable transaction conditional support on sequencer
+    #[arg(long = "rollup.enable-tx-conditional", default_value = "false")]
+    pub enable_tx_conditional: bool,
 }
 
 #[allow(clippy::derivable_impls)]
@@ -44,6 +48,7 @@ impl Default for RollupArgs {
             enable_genesis_walkback: false,
             compute_pending_block: false,
             discovery_v4: false,
+            enable_tx_conditional: false,
         }
     }
 }
@@ -114,12 +119,22 @@ mod tests {
         assert_eq!(args, expected_args);
     }
 
+    #[test]
+    fn test_parse_optimism_enable_tx_conditional() {
+        let expected_args = RollupArgs { enable_tx_conditional: true, ..Default::default() };
+        let args =
+            CommandParser::<RollupArgs>::parse_from(["reth", "--rollup.enable-tx-conditional"])
+                .args;
+        assert_eq!(args, expected_args);
+    }
+
     #[test]
     fn test_parse_optimism_many_args() {
         let expected_args = RollupArgs {
             disable_txpool_gossip: true,
             compute_pending_block: true,
             enable_genesis_walkback: true,
+            enable_tx_conditional: true,
             sequencer_http: Some("http://host:port".into()),
             ..Default::default()
         };
@@ -128,6 +143,7 @@ mod tests {
             "--rollup.disable-tx-pool-gossip",
             "--rollup.compute-pending-block",
             "--rollup.enable-genesis-walkback",
+            "--rollup.enable-tx-conditional",
             "--rollup.sequencer-http",
             "http://host:port",
         ])
diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs
index 4721e9fdd2d5..83438e40d657 100644
--- a/crates/optimism/node/src/node.rs
+++ b/crates/optimism/node/src/node.rs
@@ -34,11 +34,14 @@ use reth_optimism_payload_builder::{
 };
 use reth_optimism_primitives::{DepositReceipt, OpPrimitives, OpReceipt, OpTransactionSigned};
 use reth_optimism_rpc::{
+    eth::ext::OpEthExtApi,
     miner::{MinerApiExtServer, OpMinerExtApi},
     witness::{DebugExecutionWitnessApiServer, OpDebugWitnessApi},
     OpEthApi, OpEthApiError, SequencerClient,
 };
+use reth_optimism_txpool::conditional::MaybeConditionalTransaction;
 use reth_provider::{providers::ProviderFactoryBuilder, CanonStateSubscriptions, EthStorage};
+use reth_rpc_eth_api::ext::L2EthApiExtServer;
 use reth_rpc_eth_types::error::FromEvmError;
 use reth_rpc_server_types::RethRpcModule;
 use reth_tracing::tracing::{debug, info};
@@ -182,6 +185,7 @@ where
         Self::AddOns::builder()
             .with_sequencer(self.args.sequencer_http.clone())
             .with_da_config(self.da_config.clone())
+            .with_enable_tx_conditional(self.args.enable_tx_conditional)
             .build()
     }
 }
@@ -210,6 +214,11 @@ pub struct OpAddOns<N: FullNodeComponents> {
     >,
     /// Data availability configuration for the OP builder.
     pub da_config: OpDAConfig,
+    /// Sequencer client, configured to forward submitted transactions to sequencer of given OP
+    /// network.
+    pub sequencer_client: Option<SequencerClient>,
+    /// Enable transaction conditionals.
+    enable_tx_conditional: bool,
 }
 
 impl<N: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>> Default for OpAddOns<N> {
@@ -237,6 +246,7 @@ where
         Evm: ConfigureEvmEnv<TxEnv = TxEnv>,
     >,
     OpEthApiError: FromEvmError<N::Evm>,
+    <<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: MaybeConditionalTransaction,
 {
     type Handle = RpcHandle<N, OpEthApi<N>>;
 
@@ -244,7 +254,7 @@ where
         self,
         ctx: reth_node_api::AddOnsContext<'_, N>,
     ) -> eyre::Result<Self::Handle> {
-        let Self { rpc_add_ons, da_config } = self;
+        let Self { rpc_add_ons, da_config, sequencer_client, enable_tx_conditional } = self;
 
         let builder = reth_optimism_payload_builder::OpPayloadBuilder::new(
             ctx.node.pool().clone(),
@@ -260,6 +270,11 @@ where
         );
         let miner_ext = OpMinerExtApi::new(da_config);
 
+        let tx_conditional_ext: OpEthExtApi<N::Pool, N::Provider> = OpEthExtApi::new(
+            sequencer_client,
+            ctx.node.pool().clone(),
+            ctx.node.provider().clone(),
+        );
         rpc_add_ons
             .launch_add_ons_with(ctx, move |modules, auth_modules| {
                 debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint");
@@ -277,6 +292,14 @@ where
                     auth_modules.merge_auth_methods(miner_ext.into_rpc())?;
                 }
 
+                if enable_tx_conditional {
+                    // extend the eth namespace if configured in the regular http server
+                    modules.merge_if_module_configured(
+                        RethRpcModule::Eth,
+                        tx_conditional_ext.into_rpc(),
+                    )?;
+                }
+
                 Ok(())
             })
             .await
@@ -295,6 +318,7 @@ where
         Evm: ConfigureEvm<TxEnv = TxEnv>,
     >,
     OpEthApiError: FromEvmError<N::Evm>,
+    <<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: MaybeConditionalTransaction,
 {
     type EthApi = OpEthApi<N>;
 
@@ -329,6 +353,8 @@ pub struct OpAddOnsBuilder {
     sequencer_client: Option<SequencerClient>,
     /// Data availability configuration for the OP builder.
     da_config: Option<OpDAConfig>,
+    /// Enable transaction conditionals.
+    enable_tx_conditional: bool,
 }
 
 impl OpAddOnsBuilder {
@@ -343,6 +369,12 @@ impl OpAddOnsBuilder {
         self.da_config = Some(da_config);
         self
     }
+
+    /// Configure if transaction conditional should be enabled.
+    pub fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
+        self.enable_tx_conditional = enable_tx_conditional;
+        self
+    }
 }
 
 impl OpAddOnsBuilder {
@@ -351,15 +383,20 @@ impl OpAddOnsBuilder {
     where
         N: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>,
     {
-        let Self { sequencer_client, da_config } = self;
+        let Self { sequencer_client, da_config, enable_tx_conditional } = self;
 
+        let sequencer_client_clone = sequencer_client.clone();
         OpAddOns {
             rpc_add_ons: RpcAddOns::new(
-                move |ctx| OpEthApi::<N>::builder().with_sequencer(sequencer_client).build(ctx),
+                move |ctx| {
+                    OpEthApi::<N>::builder().with_sequencer(sequencer_client_clone).build(ctx)
+                },
                 Default::default(),
                 Default::default(),
             ),
             da_config: da_config.unwrap_or_default(),
+            sequencer_client,
+            enable_tx_conditional,
         }
     }
 }
diff --git a/crates/optimism/rpc/src/eth/ext.rs b/crates/optimism/rpc/src/eth/ext.rs
index 0c497a66e5c3..6c9484f8d370 100644
--- a/crates/optimism/rpc/src/eth/ext.rs
+++ b/crates/optimism/rpc/src/eth/ext.rs
@@ -1,4 +1,5 @@
-use super::{OpEthApiInner, OpNodeCore};
+//! Eth API extension.
+
 use crate::{error::TxConditionalErr, OpEthApiError, SequencerClient};
 use alloy_consensus::BlockHeader;
 use alloy_eips::BlockNumberOrTag;
@@ -15,42 +16,73 @@ use std::sync::Arc;
 /// Maximum execution const for conditional transactions.
 const MAX_CONDITIONAL_EXECUTION_COST: u64 = 5000;
 
+#[derive(Debug)]
+struct OpEthExtApiInner<Pool, Provider> {
+    /// The transaction pool of the node.
+    pool: Pool,
+    /// The provider type used to interact with the node.
+    provider: Provider,
+}
+
+impl<Pool, Provider> OpEthExtApiInner<Pool, Provider> {
+    fn new(pool: Pool, provider: Provider) -> Self {
+        Self { pool, provider }
+    }
+
+    #[inline]
+    fn pool(&self) -> &Pool {
+        &self.pool
+    }
+
+    #[inline]
+    fn provider(&self) -> &Provider {
+        &self.provider
+    }
+}
+
 /// OP-Reth `Eth` API extensions implementation.
 ///
 /// Separate from [`super::OpEthApi`] to allow to enable it conditionally,
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 #[allow(dead_code)]
-pub(crate) struct OpEthApiExt<N: OpNodeCore> {
-    /// Gateway to node's core components.
-    inner: Arc<OpEthApiInner<N>>,
+pub struct OpEthExtApi<Pool, Provider> {
+    /// Sequencer client, configured to forward submitted transactions to sequencer of given OP
+    /// network.
+    sequencer_client: Option<SequencerClient>,
+    inner: Arc<OpEthExtApiInner<Pool, Provider>>,
 }
 
-impl<N> OpEthApiExt<N>
+impl<Pool, Provider> OpEthExtApi<Pool, Provider>
 where
-    N: OpNodeCore<Provider: BlockReaderIdExt + Clone + 'static>,
+    Provider: BlockReaderIdExt + Clone + 'static,
 {
+    /// Creates a new [`OpEthExtApi`].
+    pub fn new(sequencer_client: Option<SequencerClient>, pool: Pool, provider: Provider) -> Self {
+        let inner = Arc::new(OpEthExtApiInner::new(pool, provider));
+        Self { sequencer_client, inner }
+    }
+
     /// Returns the configured sequencer client, if any.
-    pub(crate) fn sequencer_client(&self) -> Option<&SequencerClient> {
-        self.inner.sequencer_client()
+    fn sequencer_client(&self) -> Option<&SequencerClient> {
+        self.sequencer_client.as_ref()
     }
 
     #[inline]
-    fn pool(&self) -> &N::Pool {
-        self.inner.eth_api.pool()
+    fn pool(&self) -> &Pool {
+        self.inner.pool()
     }
 
     #[inline]
-    fn provider(&self) -> &N::Provider {
-        self.inner.eth_api.provider()
+    fn provider(&self) -> &Provider {
+        self.inner.provider()
     }
 }
 
 #[async_trait::async_trait]
-impl<N> L2EthApiExtServer for OpEthApiExt<N>
+impl<Pool, Provider> L2EthApiExtServer for OpEthExtApi<Pool, Provider>
 where
-    N: OpNodeCore + 'static,
-    N::Provider: BlockReaderIdExt + StateProviderFactory,
-    N::Pool: TransactionPool<Transaction: MaybeConditionalTransaction>,
+    Provider: BlockReaderIdExt + StateProviderFactory + Clone + 'static,
+    Pool: TransactionPool<Transaction: MaybeConditionalTransaction> + 'static,
 {
     async fn send_raw_transaction_conditional(
         &self,
@@ -67,7 +99,7 @@ where
             OpEthApiError::Eth(reth_rpc_eth_types::EthApiError::FailedToDecodeSignedTransaction)
         })?;
 
-        let mut tx = <N::Pool as TransactionPool>::Transaction::from_pooled(recovered_tx);
+        let mut tx = <Pool as TransactionPool>::Transaction::from_pooled(recovered_tx);
 
         // get current header
         let header_not_found = || {
diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs
index 33de2800a867..bd37f0994b6f 100644
--- a/crates/optimism/rpc/src/eth/mod.rs
+++ b/crates/optimism/rpc/src/eth/mod.rs
@@ -1,11 +1,11 @@
 //! OP-Reth `eth_` endpoint implementation.
 
+pub mod ext;
 pub mod receipt;
 pub mod transaction;
 
 mod block;
 mod call;
-mod ext;
 mod pending_block;
 
 pub use receipt::{OpReceiptBuilder, OpReceiptFieldsBuilder};


Test patch: