From 71b9f1225af3ce0b1c8d3b2f768cbd67a5f85e8a Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Wed, 12 Feb 2025 14:15:27 +0100 Subject: [PATCH] feat(op, cli): add cli option to enable tx conditional (#14421) --- Cargo.lock | 1 + crates/optimism/node/Cargo.toml | 1 + crates/optimism/node/src/args.rs | 16 +++++++ crates/optimism/node/src/node.rs | 43 +++++++++++++++++-- crates/optimism/rpc/src/eth/ext.rs | 68 ++++++++++++++++++++++-------- crates/optimism/rpc/src/eth/mod.rs | 2 +- 6 files changed, 109 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec78a87a2..978225cdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8525,6 +8525,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 8f711a9df..c2dbd5f13 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 87c8c1be6..49fd830b7 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::::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 4721e9fdd..83438e40d 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 { >, /// 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, + /// Enable transaction conditionals. + enable_tx_conditional: bool, } impl>> Default for OpAddOns { @@ -237,6 +246,7 @@ where Evm: ConfigureEvmEnv, >, OpEthApiError: FromEvmError, + <::Pool as TransactionPool>::Transaction: MaybeConditionalTransaction, { type Handle = RpcHandle>; @@ -244,7 +254,7 @@ where self, ctx: reth_node_api::AddOnsContext<'_, N>, ) -> eyre::Result { - 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 = 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, >, OpEthApiError: FromEvmError, + <::Pool as TransactionPool>::Transaction: MaybeConditionalTransaction, { type EthApi = OpEthApi; @@ -329,6 +353,8 @@ pub struct OpAddOnsBuilder { sequencer_client: Option, /// Data availability configuration for the OP builder. da_config: Option, + /// 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>, { - 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::::builder().with_sequencer(sequencer_client).build(ctx), + move |ctx| { + OpEthApi::::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 0c497a66e..6c9484f8d 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 { + /// The transaction pool of the node. + pool: Pool, + /// The provider type used to interact with the node. + provider: Provider, +} + +impl OpEthExtApiInner { + 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 { - /// Gateway to node's core components. - inner: Arc>, +pub struct OpEthExtApi { + /// Sequencer client, configured to forward submitted transactions to sequencer of given OP + /// network. + sequencer_client: Option, + inner: Arc>, } -impl OpEthApiExt +impl OpEthExtApi where - N: OpNodeCore, + Provider: BlockReaderIdExt + Clone + 'static, { + /// Creates a new [`OpEthExtApi`]. + pub fn new(sequencer_client: Option, 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 L2EthApiExtServer for OpEthApiExt +impl L2EthApiExtServer for OpEthExtApi where - N: OpNodeCore + 'static, - N::Provider: BlockReaderIdExt + StateProviderFactory, - N::Pool: TransactionPool, + Provider: BlockReaderIdExt + StateProviderFactory + Clone + 'static, + Pool: TransactionPool + 'static, { async fn send_raw_transaction_conditional( &self, @@ -67,7 +99,7 @@ where OpEthApiError::Eth(reth_rpc_eth_types::EthApiError::FailedToDecodeSignedTransaction) })?; - let mut tx = ::Transaction::from_pooled(recovered_tx); + let mut tx = ::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 33de2800a..bd37f0994 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};