feat(op, cli): add cli option to enable tx conditional (#14421)

This commit is contained in:
Federico Gimenez
2025-02-12 14:15:27 +01:00
committed by GitHub
parent 82903d9fe4
commit 71b9f1225a
6 changed files with 109 additions and 22 deletions

1
Cargo.lock generated
View File

@ -8525,6 +8525,7 @@ dependencies = [
"reth-primitives-traits", "reth-primitives-traits",
"reth-provider", "reth-provider",
"reth-revm", "reth-revm",
"reth-rpc-eth-api",
"reth-rpc-eth-types", "reth-rpc-eth-types",
"reth-rpc-server-types", "reth-rpc-server-types",
"reth-tasks", "reth-tasks",

View File

@ -31,6 +31,7 @@ reth-evm.workspace = true
reth-revm = { workspace = true, features = ["std"] } reth-revm = { workspace = true, features = ["std"] }
reth-trie-db.workspace = true reth-trie-db.workspace = true
reth-rpc-server-types.workspace = true reth-rpc-server-types.workspace = true
reth-rpc-eth-api.workspace = true
reth-rpc-eth-types.workspace = true reth-rpc-eth-types.workspace = true
reth-tasks = { workspace = true, optional = true } reth-tasks = { workspace = true, optional = true }

View File

@ -33,6 +33,10 @@ pub struct RollupArgs {
/// enables discovery v4 if provided /// enables discovery v4 if provided
#[arg(long = "rollup.discovery.v4", default_value = "false")] #[arg(long = "rollup.discovery.v4", default_value = "false")]
pub discovery_v4: bool, 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)] #[allow(clippy::derivable_impls)]
@ -44,6 +48,7 @@ impl Default for RollupArgs {
enable_genesis_walkback: false, enable_genesis_walkback: false,
compute_pending_block: false, compute_pending_block: false,
discovery_v4: false, discovery_v4: false,
enable_tx_conditional: false,
} }
} }
} }
@ -114,12 +119,22 @@ mod tests {
assert_eq!(args, expected_args); 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] #[test]
fn test_parse_optimism_many_args() { fn test_parse_optimism_many_args() {
let expected_args = RollupArgs { let expected_args = RollupArgs {
disable_txpool_gossip: true, disable_txpool_gossip: true,
compute_pending_block: true, compute_pending_block: true,
enable_genesis_walkback: true, enable_genesis_walkback: true,
enable_tx_conditional: true,
sequencer_http: Some("http://host:port".into()), sequencer_http: Some("http://host:port".into()),
..Default::default() ..Default::default()
}; };
@ -128,6 +143,7 @@ mod tests {
"--rollup.disable-tx-pool-gossip", "--rollup.disable-tx-pool-gossip",
"--rollup.compute-pending-block", "--rollup.compute-pending-block",
"--rollup.enable-genesis-walkback", "--rollup.enable-genesis-walkback",
"--rollup.enable-tx-conditional",
"--rollup.sequencer-http", "--rollup.sequencer-http",
"http://host:port", "http://host:port",
]) ])

View File

@ -34,11 +34,14 @@ use reth_optimism_payload_builder::{
}; };
use reth_optimism_primitives::{DepositReceipt, OpPrimitives, OpReceipt, OpTransactionSigned}; use reth_optimism_primitives::{DepositReceipt, OpPrimitives, OpReceipt, OpTransactionSigned};
use reth_optimism_rpc::{ use reth_optimism_rpc::{
eth::ext::OpEthExtApi,
miner::{MinerApiExtServer, OpMinerExtApi}, miner::{MinerApiExtServer, OpMinerExtApi},
witness::{DebugExecutionWitnessApiServer, OpDebugWitnessApi}, witness::{DebugExecutionWitnessApiServer, OpDebugWitnessApi},
OpEthApi, OpEthApiError, SequencerClient, OpEthApi, OpEthApiError, SequencerClient,
}; };
use reth_optimism_txpool::conditional::MaybeConditionalTransaction;
use reth_provider::{providers::ProviderFactoryBuilder, CanonStateSubscriptions, EthStorage}; use reth_provider::{providers::ProviderFactoryBuilder, CanonStateSubscriptions, EthStorage};
use reth_rpc_eth_api::ext::L2EthApiExtServer;
use reth_rpc_eth_types::error::FromEvmError; use reth_rpc_eth_types::error::FromEvmError;
use reth_rpc_server_types::RethRpcModule; use reth_rpc_server_types::RethRpcModule;
use reth_tracing::tracing::{debug, info}; use reth_tracing::tracing::{debug, info};
@ -182,6 +185,7 @@ where
Self::AddOns::builder() Self::AddOns::builder()
.with_sequencer(self.args.sequencer_http.clone()) .with_sequencer(self.args.sequencer_http.clone())
.with_da_config(self.da_config.clone()) .with_da_config(self.da_config.clone())
.with_enable_tx_conditional(self.args.enable_tx_conditional)
.build() .build()
} }
} }
@ -210,6 +214,11 @@ pub struct OpAddOns<N: FullNodeComponents> {
>, >,
/// Data availability configuration for the OP builder. /// Data availability configuration for the OP builder.
pub da_config: OpDAConfig, 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> { impl<N: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>> Default for OpAddOns<N> {
@ -237,6 +246,7 @@ where
Evm: ConfigureEvmEnv<TxEnv = TxEnv>, Evm: ConfigureEvmEnv<TxEnv = TxEnv>,
>, >,
OpEthApiError: FromEvmError<N::Evm>, OpEthApiError: FromEvmError<N::Evm>,
<<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: MaybeConditionalTransaction,
{ {
type Handle = RpcHandle<N, OpEthApi<N>>; type Handle = RpcHandle<N, OpEthApi<N>>;
@ -244,7 +254,7 @@ where
self, self,
ctx: reth_node_api::AddOnsContext<'_, N>, ctx: reth_node_api::AddOnsContext<'_, N>,
) -> eyre::Result<Self::Handle> { ) -> 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( let builder = reth_optimism_payload_builder::OpPayloadBuilder::new(
ctx.node.pool().clone(), ctx.node.pool().clone(),
@ -260,6 +270,11 @@ where
); );
let miner_ext = OpMinerExtApi::new(da_config); 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 rpc_add_ons
.launch_add_ons_with(ctx, move |modules, auth_modules| { .launch_add_ons_with(ctx, move |modules, auth_modules| {
debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint"); debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint");
@ -277,6 +292,14 @@ where
auth_modules.merge_auth_methods(miner_ext.into_rpc())?; 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(()) Ok(())
}) })
.await .await
@ -295,6 +318,7 @@ where
Evm: ConfigureEvm<TxEnv = TxEnv>, Evm: ConfigureEvm<TxEnv = TxEnv>,
>, >,
OpEthApiError: FromEvmError<N::Evm>, OpEthApiError: FromEvmError<N::Evm>,
<<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: MaybeConditionalTransaction,
{ {
type EthApi = OpEthApi<N>; type EthApi = OpEthApi<N>;
@ -329,6 +353,8 @@ pub struct OpAddOnsBuilder {
sequencer_client: Option<SequencerClient>, sequencer_client: Option<SequencerClient>,
/// Data availability configuration for the OP builder. /// Data availability configuration for the OP builder.
da_config: Option<OpDAConfig>, da_config: Option<OpDAConfig>,
/// Enable transaction conditionals.
enable_tx_conditional: bool,
} }
impl OpAddOnsBuilder { impl OpAddOnsBuilder {
@ -343,6 +369,12 @@ impl OpAddOnsBuilder {
self.da_config = Some(da_config); self.da_config = Some(da_config);
self 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 { impl OpAddOnsBuilder {
@ -351,15 +383,20 @@ impl OpAddOnsBuilder {
where where
N: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>, 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 { OpAddOns {
rpc_add_ons: RpcAddOns::new( 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(),
Default::default(), Default::default(),
), ),
da_config: da_config.unwrap_or_default(), da_config: da_config.unwrap_or_default(),
sequencer_client,
enable_tx_conditional,
} }
} }
} }

View File

@ -1,4 +1,5 @@
use super::{OpEthApiInner, OpNodeCore}; //! Eth API extension.
use crate::{error::TxConditionalErr, OpEthApiError, SequencerClient}; use crate::{error::TxConditionalErr, OpEthApiError, SequencerClient};
use alloy_consensus::BlockHeader; use alloy_consensus::BlockHeader;
use alloy_eips::BlockNumberOrTag; use alloy_eips::BlockNumberOrTag;
@ -15,42 +16,73 @@ use std::sync::Arc;
/// Maximum execution const for conditional transactions. /// Maximum execution const for conditional transactions.
const MAX_CONDITIONAL_EXECUTION_COST: u64 = 5000; 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. /// OP-Reth `Eth` API extensions implementation.
/// ///
/// Separate from [`super::OpEthApi`] to allow to enable it conditionally, /// Separate from [`super::OpEthApi`] to allow to enable it conditionally,
#[derive(Clone)] #[derive(Clone, Debug)]
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) struct OpEthApiExt<N: OpNodeCore> { pub struct OpEthExtApi<Pool, Provider> {
/// Gateway to node's core components. /// Sequencer client, configured to forward submitted transactions to sequencer of given OP
inner: Arc<OpEthApiInner<N>>, /// network.
sequencer_client: Option<SequencerClient>,
inner: Arc<OpEthExtApiInner<Pool, Provider>>,
} }
impl<N> OpEthApiExt<N> impl<Pool, Provider> OpEthExtApi<Pool, Provider>
where 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. /// Returns the configured sequencer client, if any.
pub(crate) fn sequencer_client(&self) -> Option<&SequencerClient> { fn sequencer_client(&self) -> Option<&SequencerClient> {
self.inner.sequencer_client() self.sequencer_client.as_ref()
} }
#[inline] #[inline]
fn pool(&self) -> &N::Pool { fn pool(&self) -> &Pool {
self.inner.eth_api.pool() self.inner.pool()
} }
#[inline] #[inline]
fn provider(&self) -> &N::Provider { fn provider(&self) -> &Provider {
self.inner.eth_api.provider() self.inner.provider()
} }
} }
#[async_trait::async_trait] #[async_trait::async_trait]
impl<N> L2EthApiExtServer for OpEthApiExt<N> impl<Pool, Provider> L2EthApiExtServer for OpEthExtApi<Pool, Provider>
where where
N: OpNodeCore + 'static, Provider: BlockReaderIdExt + StateProviderFactory + Clone + 'static,
N::Provider: BlockReaderIdExt + StateProviderFactory, Pool: TransactionPool<Transaction: MaybeConditionalTransaction> + 'static,
N::Pool: TransactionPool<Transaction: MaybeConditionalTransaction>,
{ {
async fn send_raw_transaction_conditional( async fn send_raw_transaction_conditional(
&self, &self,
@ -67,7 +99,7 @@ where
OpEthApiError::Eth(reth_rpc_eth_types::EthApiError::FailedToDecodeSignedTransaction) 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 // get current header
let header_not_found = || { let header_not_found = || {

View File

@ -1,11 +1,11 @@
//! OP-Reth `eth_` endpoint implementation. //! OP-Reth `eth_` endpoint implementation.
pub mod ext;
pub mod receipt; pub mod receipt;
pub mod transaction; pub mod transaction;
mod block; mod block;
mod call; mod call;
mod ext;
mod pending_block; mod pending_block;
pub use receipt::{OpReceiptBuilder, OpReceiptFieldsBuilder}; pub use receipt::{OpReceiptBuilder, OpReceiptFieldsBuilder};