diff --git a/Cargo.lock b/Cargo.lock index 8d944d04da..ea860db7ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9130,6 +9130,7 @@ dependencies = [ "reth-eth-wire-types", "reth-ethereum-forks", "reth-ethereum-primitives", + "reth-evm-ethereum", "reth-fs-util", "reth-metrics", "reth-net-banlist", @@ -9997,6 +9998,7 @@ dependencies = [ "parking_lot", "reth-chain-state", "reth-chainspec", + "reth-evm", "reth-metrics", "reth-optimism-chainspec", "reth-optimism-evm", @@ -11051,6 +11053,8 @@ dependencies = [ "reth-chainspec", "reth-eth-wire-types", "reth-ethereum-primitives", + "reth-evm", + "reth-evm-ethereum", "reth-execution-types", "reth-fs-util", "reth-metrics", @@ -11059,6 +11063,7 @@ dependencies = [ "reth-storage-api", "reth-tasks", "reth-tracing", + "revm", "revm-interpreter", "revm-primitives", "rustc-hash", diff --git a/crates/chainspec/src/lib.rs b/crates/chainspec/src/lib.rs index 2ba17ebf2a..8a30084eaa 100644 --- a/crates/chainspec/src/lib.rs +++ b/crates/chainspec/src/lib.rs @@ -25,6 +25,7 @@ pub use alloy_chains::{Chain, ChainKind, NamedChain}; /// Re-export for convenience pub use reth_ethereum_forks::*; +pub use alloy_evm::EvmLimitParams; pub use api::EthChainSpec; pub use info::ChainInfo; #[cfg(any(test, feature = "test-utils"))] diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 6db265befd..6ba01122c4 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -1,14 +1,11 @@ //! Collection of methods for block validation. -use alloy_consensus::{BlockHeader as _, Transaction, EMPTY_OMMER_ROOT_HASH}; +use alloy_consensus::{BlockHeader as _, EMPTY_OMMER_ROOT_HASH}; use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, eip7840::BlobParams}; use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks}; -use reth_consensus::{ConsensusError, TxGasLimitTooHighErr}; +use reth_consensus::ConsensusError; use reth_primitives_traits::{ - constants::{ - GAS_LIMIT_BOUND_DIVISOR, MAXIMUM_GAS_LIMIT_BLOCK, MAX_TX_GAS_LIMIT_OSAKA, MINIMUM_GAS_LIMIT, - }, - transaction::TxHashRef, + constants::{GAS_LIMIT_BOUND_DIVISOR, MAXIMUM_GAS_LIMIT_BLOCK, MINIMUM_GAS_LIMIT}, Block, BlockBody, BlockHeader, GotExpected, SealedBlock, SealedHeader, }; @@ -146,7 +143,7 @@ pub fn validate_block_pre_execution( ) -> Result<(), ConsensusError> where B: Block, - ChainSpec: EthereumHardforks, + ChainSpec: EthChainSpec + EthereumHardforks, { post_merge_hardfork_fields(block, chain_spec)?; @@ -154,19 +151,6 @@ where if let Err(error) = block.ensure_transaction_root_valid() { return Err(ConsensusError::BodyTransactionRootDiff(error.into())) } - // EIP-7825 validation - if chain_spec.is_osaka_active_at_timestamp(block.timestamp()) { - for tx in block.body().transactions() { - if tx.gas_limit() > MAX_TX_GAS_LIMIT_OSAKA { - return Err(TxGasLimitTooHighErr { - tx_hash: *tx.tx_hash(), - gas_limit: tx.gas_limit(), - max_allowed: MAX_TX_GAS_LIMIT_OSAKA, - } - .into()); - } - } - } Ok(()) } diff --git a/crates/ethereum/evm/Cargo.toml b/crates/ethereum/evm/Cargo.toml index fbbbeeed83..643e048324 100644 --- a/crates/ethereum/evm/Cargo.toml +++ b/crates/ethereum/evm/Cargo.toml @@ -59,6 +59,7 @@ std = [ "reth-storage-errors/std", ] test-utils = [ + "std", "dep:parking_lot", "dep:derive_more", "reth-chainspec/test-utils", diff --git a/crates/ethereum/node/src/node.rs b/crates/ethereum/node/src/node.rs index da93390154..2a645ad274 100644 --- a/crates/ethereum/node/src/node.rs +++ b/crates/ethereum/node/src/node.rs @@ -18,7 +18,7 @@ use reth_evm::{ }; use reth_network::{primitives::BasicNetworkPrimitives, NetworkHandle, PeersInfo}; use reth_node_api::{ - AddOnsContext, BlockTy, FullNodeComponents, HeaderTy, NodeAddOns, NodePrimitives, + AddOnsContext, FullNodeComponents, HeaderTy, NodeAddOns, NodePrimitives, PayloadAttributesBuilder, PrimitivesTy, TxTy, }; use reth_node_builder::{ @@ -53,8 +53,8 @@ use reth_rpc_eth_types::{error::FromEvmError, EthApiError}; use reth_rpc_server_types::RethRpcModule; use reth_tracing::tracing::{debug, info}; use reth_transaction_pool::{ - blobstore::DiskFileBlobStore, EthPooledTransaction, EthTransactionPool, PoolPooledTx, - PoolTransaction, TransactionPool, TransactionValidationTaskExecutor, + blobstore::DiskFileBlobStore, EthTransactionPool, PoolPooledTx, PoolTransaction, + TransactionPool, TransactionValidationTaskExecutor, }; use revm::context::TxEnv; use std::{marker::PhantomData, sync::Arc, time::SystemTime}; @@ -456,18 +456,22 @@ pub struct EthereumPoolBuilder { // TODO add options for txpool args } -impl PoolBuilder for EthereumPoolBuilder +impl PoolBuilder for EthereumPoolBuilder where Types: NodeTypes< ChainSpec: EthereumHardforks, Primitives: NodePrimitives, >, Node: FullNodeTypes, + Evm: ConfigureEvm> + Clone + 'static, { - type Pool = - EthTransactionPool>; + type Pool = EthTransactionPool; - async fn build_pool(self, ctx: &BuilderContext) -> eyre::Result { + async fn build_pool( + self, + ctx: &BuilderContext, + evm_config: Evm, + ) -> eyre::Result { let pool_config = ctx.pool_config(); let blobs_disabled = ctx.config().txpool.disable_blobs_support || @@ -493,17 +497,17 @@ where let blob_store = reth_node_builder::components::create_blob_store_with_cache(ctx, blob_cache_size)?; - let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone()) - .with_head_timestamp(ctx.head().timestamp) - .set_eip4844(!blobs_disabled) - .kzg_settings(ctx.kzg_settings()?) - .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes) - .with_local_transactions_config(pool_config.local_transactions_config.clone()) - .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap) - .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit) - .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee) - .with_additional_tasks(ctx.config().txpool.additional_validation_tasks) - .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()); + let validator = + TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config) + .set_eip4844(!blobs_disabled) + .kzg_settings(ctx.kzg_settings()?) + .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes) + .with_local_transactions_config(pool_config.local_transactions_config.clone()) + .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap) + .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit) + .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee) + .with_additional_tasks(ctx.config().txpool.additional_validation_tasks) + .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()); if validator.validator().eip4844() { // initializing the KZG settings can be expensive, this should be done upfront so that diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index e5bd089255..bf5ca7ae1d 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -35,7 +35,7 @@ use reth_execution_errors::BlockExecutionError; use reth_primitives_traits::{ BlockTy, HeaderTy, NodePrimitives, ReceiptTy, SealedBlock, SealedHeader, TxTy, }; -use revm::{context::TxEnv, database::State}; +use revm::{context::TxEnv, database::State, primitives::hardfork::SpecId}; pub mod either; /// EVM environment configuration. @@ -203,6 +203,7 @@ pub trait ConfigureEvm: Clone + Debug + Send + Sync + Unpin { + FromRecoveredTx> + FromTxWithEncoded>, Precompiles = PrecompilesMap, + Spec: Into, >, >; diff --git a/crates/exex/test-utils/src/lib.rs b/crates/exex/test-utils/src/lib.rs index 8430ea5d91..3fc75488e1 100644 --- a/crates/exex/test-utils/src/lib.rs +++ b/crates/exex/test-utils/src/lib.rs @@ -66,13 +66,17 @@ use tokio::sync::mpsc::{Sender, UnboundedReceiver}; #[non_exhaustive] pub struct TestPoolBuilder; -impl PoolBuilder for TestPoolBuilder +impl PoolBuilder for TestPoolBuilder where Node: FullNodeTypes>>, { type Pool = TestPool; - async fn build_pool(self, _ctx: &BuilderContext) -> eyre::Result { + async fn build_pool( + self, + _ctx: &BuilderContext, + _evm_config: Evm, + ) -> eyre::Result { Ok(testing_pool()) } } diff --git a/crates/net/network/Cargo.toml b/crates/net/network/Cargo.toml index cbe93a2386..62252155e3 100644 --- a/crates/net/network/Cargo.toml +++ b/crates/net/network/Cargo.toml @@ -14,6 +14,7 @@ workspace = true [dependencies] # reth reth-chainspec.workspace = true +reth-evm-ethereum = { workspace = true, optional = true } reth-fs-util.workspace = true reth-primitives-traits.workspace = true reth-net-banlist.workspace = true @@ -136,6 +137,8 @@ test-utils = [ "reth-primitives-traits/test-utils", "reth-provider/test-utils", "reth-ethereum-primitives/test-utils", + "dep:reth-evm-ethereum", + "reth-evm-ethereum?/test-utils", ] [[bench]] diff --git a/crates/net/network/src/test_utils/testnet.rs b/crates/net/network/src/test_utils/testnet.rs index aae1f7708e..2212963c91 100644 --- a/crates/net/network/src/test_utils/testnet.rs +++ b/crates/net/network/src/test_utils/testnet.rs @@ -19,6 +19,7 @@ use reth_eth_wire::{ protocol::Protocol, DisconnectReason, EthNetworkPrimitives, HelloMessageWithProtocols, }; use reth_ethereum_primitives::{PooledTransactionVariant, TransactionSigned}; +use reth_evm_ethereum::EthEvmConfig; use reth_network_api::{ events::{PeerEvent, SessionInfo}, test_utils::{PeersHandle, PeersHandleProvider}, @@ -182,17 +183,20 @@ where C: ChainSpecProvider + StateProviderFactory + BlockReaderIdExt - + HeaderProvider + + HeaderProvider
+ Clone + 'static, Pool: TransactionPool, { /// Installs an eth pool on each peer - pub fn with_eth_pool(self) -> Testnet> { + pub fn with_eth_pool( + self, + ) -> Testnet> { self.map_pool(|peer| { let blob_store = InMemoryBlobStore::default(); let pool = TransactionValidationTaskExecutor::eth( peer.client.clone(), + EthEvmConfig::mainnet(), blob_store.clone(), TokioTaskExecutor::default(), ); @@ -208,7 +212,7 @@ where pub fn with_eth_pool_config( self, tx_manager_config: TransactionsManagerConfig, - ) -> Testnet> { + ) -> Testnet> { self.with_eth_pool_config_and_policy(tx_manager_config, Default::default()) } @@ -217,11 +221,12 @@ where self, tx_manager_config: TransactionsManagerConfig, policy: TransactionPropagationKind, - ) -> Testnet> { + ) -> Testnet> { self.map_pool(|peer| { let blob_store = InMemoryBlobStore::default(); let pool = TransactionValidationTaskExecutor::eth( peer.client.clone(), + EthEvmConfig::mainnet(), blob_store.clone(), TokioTaskExecutor::default(), ); diff --git a/crates/net/network/tests/it/connect.rs b/crates/net/network/tests/it/connect.rs index d11c6b9541..a8d409ad69 100644 --- a/crates/net/network/tests/it/connect.rs +++ b/crates/net/network/tests/it/connect.rs @@ -20,6 +20,7 @@ use reth_network_p2p::{ }; use reth_network_peers::{mainnet_nodes, NodeRecord, TrustedPeer}; use reth_network_types::peers::config::PeerBackoffDurations; +use reth_provider::test_utils::MockEthProvider; use reth_storage_api::noop::NoopProvider; use reth_tracing::init_test_tracing; use reth_transaction_pool::test_utils::testing_pool; @@ -655,7 +656,8 @@ async fn new_random_peer( async fn test_connect_many() { reth_tracing::init_test_tracing(); - let net = Testnet::create_with(5, NoopProvider::default()).await; + let provider = MockEthProvider::default().with_genesis_block(); + let net = Testnet::create_with(5, provider).await; // install request handlers let net = net.with_eth_pool(); diff --git a/crates/net/network/tests/it/txgossip.rs b/crates/net/network/tests/it/txgossip.rs index d0f192cff5..1b518c50e9 100644 --- a/crates/net/network/tests/it/txgossip.rs +++ b/crates/net/network/tests/it/txgossip.rs @@ -22,7 +22,7 @@ use tokio::join; async fn test_tx_gossip() { reth_tracing::init_test_tracing(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); let net = Testnet::create_with(2, provider.clone()).await; // install request handlers @@ -61,7 +61,7 @@ async fn test_tx_gossip() { async fn test_tx_propagation_policy_trusted_only() { reth_tracing::init_test_tracing(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); let policy = TransactionPropagationKind::Trusted; let net = Testnet::create_with(2, provider.clone()).await; @@ -129,7 +129,7 @@ async fn test_tx_propagation_policy_trusted_only() { async fn test_tx_ingress_policy_trusted_only() { reth_tracing::init_test_tracing(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); let tx_manager_config = TransactionsManagerConfig { ingress_policy: TransactionIngressPolicy::Trusted, @@ -195,7 +195,7 @@ async fn test_tx_ingress_policy_trusted_only() { #[tokio::test(flavor = "multi_thread")] async fn test_4844_tx_gossip_penalization() { reth_tracing::init_test_tracing(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); let net = Testnet::create_with(2, provider.clone()).await; // install request handlers @@ -246,7 +246,7 @@ async fn test_4844_tx_gossip_penalization() { #[tokio::test(flavor = "multi_thread")] async fn test_sending_invalid_transactions() { reth_tracing::init_test_tracing(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); let net = Testnet::create_with(2, provider.clone()).await; // install request handlers let net = net.with_eth_pool(); diff --git a/crates/node/builder/src/components/builder.rs b/crates/node/builder/src/components/builder.rs index 216025d8e5..1d1600f6a4 100644 --- a/crates/node/builder/src/components/builder.rs +++ b/crates/node/builder/src/components/builder.rs @@ -62,12 +62,12 @@ impl pool_builder, payload_builder, network_builder, - executor_builder: evm_builder, + executor_builder, consensus_builder, _marker, } = self; ComponentsBuilder { - executor_builder: evm_builder, + executor_builder, pool_builder, payload_builder, network_builder, @@ -149,15 +149,12 @@ where pub fn pool( self, pool_builder: PB, - ) -> ComponentsBuilder - where - PB: PoolBuilder, - { + ) -> ComponentsBuilder { let Self { pool_builder: _, payload_builder, network_builder, - executor_builder: evm_builder, + executor_builder, consensus_builder, _marker, } = self; @@ -165,7 +162,7 @@ where pool_builder, payload_builder, network_builder, - executor_builder: evm_builder, + executor_builder, consensus_builder, _marker, } @@ -185,72 +182,6 @@ where _marker: self._marker, } } -} - -impl - ComponentsBuilder -where - Node: FullNodeTypes, - PoolB: PoolBuilder, -{ - /// Configures the network builder. - /// - /// This accepts a [`NetworkBuilder`] instance that will be used to create the node's network - /// stack. - pub fn network( - self, - network_builder: NB, - ) -> ComponentsBuilder - where - NB: NetworkBuilder, - { - let Self { - pool_builder, - payload_builder, - network_builder: _, - executor_builder: evm_builder, - consensus_builder, - _marker, - } = self; - ComponentsBuilder { - pool_builder, - payload_builder, - network_builder, - executor_builder: evm_builder, - consensus_builder, - _marker, - } - } - - /// Configures the payload builder. - /// - /// This accepts a [`PayloadServiceBuilder`] instance that will be used to create the node's - /// payload builder service. - pub fn payload( - self, - payload_builder: PB, - ) -> ComponentsBuilder - where - ExecB: ExecutorBuilder, - PB: PayloadServiceBuilder, - { - let Self { - pool_builder, - payload_builder: _, - network_builder, - executor_builder: evm_builder, - consensus_builder, - _marker, - } = self; - ComponentsBuilder { - pool_builder, - payload_builder, - network_builder, - executor_builder: evm_builder, - consensus_builder, - _marker, - } - } /// Configures the executor builder. /// @@ -298,7 +229,72 @@ where network_builder, executor_builder, consensus_builder: _, + _marker, + } = self; + ComponentsBuilder { + pool_builder, + payload_builder, + network_builder, + executor_builder, + consensus_builder, + _marker, + } + } +} +impl + ComponentsBuilder +where + Node: FullNodeTypes, + ExecB: ExecutorBuilder, + PoolB: PoolBuilder, +{ + /// Configures the network builder. + /// + /// This accepts a [`NetworkBuilder`] instance that will be used to create the node's network + /// stack. + pub fn network( + self, + network_builder: NB, + ) -> ComponentsBuilder + where + NB: NetworkBuilder, + { + let Self { + pool_builder, + payload_builder, + network_builder: _, + executor_builder, + consensus_builder, + _marker, + } = self; + ComponentsBuilder { + pool_builder, + payload_builder, + network_builder, + executor_builder, + consensus_builder, + _marker, + } + } + + /// Configures the payload builder. + /// + /// This accepts a [`PayloadServiceBuilder`] instance that will be used to create the node's + /// payload builder service. + pub fn payload( + self, + payload_builder: PB, + ) -> ComponentsBuilder + where + PB: PayloadServiceBuilder, + { + let Self { + pool_builder, + payload_builder: _, + network_builder, + executor_builder, + consensus_builder, _marker, } = self; ComponentsBuilder { @@ -358,7 +354,7 @@ impl NodeComponentsBuilder for ComponentsBuilder where Node: FullNodeTypes, - PoolB: PoolBuilder, + PoolB: PoolBuilder, NetworkB: NetworkBuilder< Node, PoolB::Pool, @@ -384,13 +380,13 @@ where pool_builder, payload_builder, network_builder, - executor_builder: evm_builder, + executor_builder, consensus_builder, _marker, } = self; - let evm_config = evm_builder.build_evm(context).await?; - let pool = pool_builder.build_pool(context).await?; + let evm_config = executor_builder.build_evm(context).await?; + let pool = pool_builder.build_pool(context, evm_config.clone()).await?; let network = network_builder.build_network(context, pool.clone()).await?; let payload_builder_handle = payload_builder .spawn_payload_builder_service(context, pool.clone(), evm_config.clone()) @@ -471,14 +467,19 @@ where #[derive(Debug, Clone)] pub struct NoopTransactionPoolBuilder(PhantomData); -impl PoolBuilder for NoopTransactionPoolBuilder +impl PoolBuilder for NoopTransactionPoolBuilder where N: FullNodeTypes, Tx: EthPoolTransaction> + Unpin, + Evm: Send, { type Pool = NoopTransactionPool; - async fn build_pool(self, _ctx: &BuilderContext) -> eyre::Result { + async fn build_pool( + self, + _ctx: &BuilderContext, + _evm_config: Evm, + ) -> eyre::Result { Ok(NoopTransactionPool::::new()) } } diff --git a/crates/node/builder/src/components/pool.rs b/crates/node/builder/src/components/pool.rs index 9f32b27915..3ca42282f4 100644 --- a/crates/node/builder/src/components/pool.rs +++ b/crates/node/builder/src/components/pool.rs @@ -12,7 +12,7 @@ use reth_transaction_pool::{ use std::{collections::HashSet, future::Future}; /// A type that knows how to build the transaction pool. -pub trait PoolBuilder: Send { +pub trait PoolBuilder: Send { /// The transaction pool to build. type Pool: TransactionPool>> + Unpin @@ -22,16 +22,17 @@ pub trait PoolBuilder: Send { fn build_pool( self, ctx: &BuilderContext, + evm_config: Evm, ) -> impl Future> + Send; } -impl PoolBuilder for F +impl PoolBuilder for F where Node: FullNodeTypes, Pool: TransactionPool>> + Unpin + 'static, - F: FnOnce(&BuilderContext) -> Fut + Send, + F: FnOnce(&BuilderContext, Evm) -> Fut + Send, Fut: Future> + Send, { type Pool = Pool; @@ -39,8 +40,9 @@ where fn build_pool( self, ctx: &BuilderContext, + evm_config: Evm, ) -> impl Future> { - self(ctx) + self(ctx, evm_config) } } diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index 6ee024ace9..51d0ff3022 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -16,7 +16,7 @@ use reth_network::{ PeersInfo, }; use reth_node_api::{ - AddOnsContext, BlockTy, BuildNextEnv, EngineTypes, FullNodeComponents, HeaderTy, NodeAddOns, + AddOnsContext, BuildNextEnv, EngineTypes, FullNodeComponents, HeaderTy, NodeAddOns, NodePrimitives, PayloadAttributesBuilder, PayloadTypes, PrimitivesTy, TxTy, }; use reth_node_builder::{ @@ -165,6 +165,7 @@ impl OpNode { self.args; ComponentsBuilder::default() .node_types::() + .executor(OpExecutorBuilder::default()) .pool( OpPoolBuilder::default() .with_enable_tx_conditional(self.args.enable_tx_conditional) @@ -173,7 +174,6 @@ impl OpNode { self.args.supervisor_safety_level, ), ) - .executor(OpExecutorBuilder::default()) .payload(BasicPayloadServiceBuilder::new( OpPayloadBuilder::new(compute_pending_block) .with_da_config(self.da_config.clone()) @@ -957,14 +957,19 @@ impl OpPoolBuilder { } } -impl PoolBuilder for OpPoolBuilder +impl PoolBuilder for OpPoolBuilder where Node: FullNodeTypes>, T: EthPoolTransaction> + OpPooledTx, + Evm: ConfigureEvm> + Clone + 'static, { - type Pool = OpTransactionPool>; + type Pool = OpTransactionPool; - async fn build_pool(self, ctx: &BuilderContext) -> eyre::Result { + async fn build_pool( + self, + ctx: &BuilderContext, + evm_config: Evm, + ) -> eyre::Result { let Self { pool_config_overrides, .. } = self; // supervisor used for interop @@ -982,27 +987,27 @@ where .await; let blob_store = reth_node_builder::components::create_blob_store(ctx)?; - let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone()) - .no_eip4844() - .with_head_timestamp(ctx.head().timestamp) - .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes) - .kzg_settings(ctx.kzg_settings()?) - .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap) - .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit) - .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee) - .with_additional_tasks( - pool_config_overrides - .additional_validation_tasks - .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks), - ) - .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()) - .map(|validator| { - OpTransactionValidator::new(validator) - // In --dev mode we can't require gas fees because we're unable to decode - // the L1 block info - .require_l1_data_gas_fee(!ctx.config().dev.dev) - .with_supervisor(supervisor_client.clone()) - }); + let validator = + TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config) + .no_eip4844() + .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes) + .kzg_settings(ctx.kzg_settings()?) + .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap) + .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit) + .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee) + .with_additional_tasks( + pool_config_overrides + .additional_validation_tasks + .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks), + ) + .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()) + .map(|validator| { + OpTransactionValidator::new(validator) + // In --dev mode we can't require gas fees because we're unable to decode + // the L1 block info + .require_l1_data_gas_fee(!ctx.config().dev.dev) + .with_supervisor(supervisor_client.clone()) + }); let final_pool_config = pool_config_overrides.apply(ctx.pool_config()); diff --git a/crates/optimism/node/src/rpc.rs b/crates/optimism/node/src/rpc.rs index 9030935d64..33de471753 100644 --- a/crates/optimism/node/src/rpc.rs +++ b/crates/optimism/node/src/rpc.rs @@ -52,9 +52,9 @@ //! ComponentsBuilder::default() //! .node_types::>() //! .noop_pool::() -//! .noop_network::() -//! .noop_consensus() //! .executor(OpExecutorBuilder::default()) +//! .noop_consensus() +//! .noop_network::() //! .noop_payload(), //! Box::new(()) as Box>, //! ) diff --git a/crates/optimism/txpool/Cargo.toml b/crates/optimism/txpool/Cargo.toml index a524f3f4df..3737d23cf0 100644 --- a/crates/optimism/txpool/Cargo.toml +++ b/crates/optimism/txpool/Cargo.toml @@ -23,6 +23,7 @@ alloy-serde.workspace = true # reth reth-chainspec.workspace = true +reth-evm.workspace = true reth-primitives-traits.workspace = true reth-chain-state.workspace = true reth-storage-api.workspace = true diff --git a/crates/optimism/txpool/src/lib.rs b/crates/optimism/txpool/src/lib.rs index b2c240abe1..5cee963064 100644 --- a/crates/optimism/txpool/src/lib.rs +++ b/crates/optimism/txpool/src/lib.rs @@ -9,7 +9,6 @@ #![cfg_attr(docsrs, feature(doc_cfg))] mod validator; -use op_alloy_consensus::OpBlock; pub use validator::{OpL1BlockInfo, OpTransactionValidator}; pub mod conditional; @@ -25,8 +24,8 @@ pub mod estimated_da_size; use reth_transaction_pool::{CoinbaseTipOrdering, Pool, TransactionValidationTaskExecutor}; /// Type alias for default optimism transaction pool -pub type OpTransactionPool = Pool< - TransactionValidationTaskExecutor>, +pub type OpTransactionPool = Pool< + TransactionValidationTaskExecutor>, CoinbaseTipOrdering, S, >; diff --git a/crates/optimism/txpool/src/transaction.rs b/crates/optimism/txpool/src/transaction.rs index fa2ec80e4d..d13ba555ba 100644 --- a/crates/optimism/txpool/src/transaction.rs +++ b/crates/optimism/txpool/src/transaction.rs @@ -316,7 +316,8 @@ mod tests { use alloy_primitives::{TxKind, U256}; use op_alloy_consensus::TxDeposit; use reth_optimism_chainspec::OP_MAINNET; - use reth_optimism_primitives::OpTransactionSigned; + use reth_optimism_evm::OpEvmConfig; + use reth_optimism_primitives::{OpPrimitives, OpTransactionSigned}; use reth_provider::test_utils::MockEthProvider; use reth_transaction_pool::{ blobstore::InMemoryBlobStore, validate::EthTransactionValidatorBuilder, TransactionOrigin, @@ -324,12 +325,14 @@ mod tests { }; #[tokio::test] async fn validate_optimism_transaction() { - let client = MockEthProvider::default().with_chain_spec(OP_MAINNET.clone()); - let validator = - EthTransactionValidatorBuilder::new(client) - .no_shanghai() - .no_cancun() - .build::<_, _, reth_optimism_primitives::OpBlock>(InMemoryBlobStore::default()); + let client = MockEthProvider::::new() + .with_chain_spec(OP_MAINNET.clone()) + .with_genesis_block(); + let evm_config = OpEvmConfig::optimism(OP_MAINNET.clone()); + let validator = EthTransactionValidatorBuilder::new(client, evm_config) + .no_shanghai() + .no_cancun() + .build(InMemoryBlobStore::default()); let validator = OpTransactionValidator::new(validator); let origin = TransactionOrigin::External; diff --git a/crates/optimism/txpool/src/validator.rs b/crates/optimism/txpool/src/validator.rs index 900a005d85..99f03e1a1e 100644 --- a/crates/optimism/txpool/src/validator.rs +++ b/crates/optimism/txpool/src/validator.rs @@ -1,13 +1,14 @@ use crate::{supervisor::SupervisorClient, InvalidCrossTx, OpPooledTx}; use alloy_consensus::{BlockHeader, Transaction}; -use op_alloy_consensus::OpBlock; use op_revm::L1BlockInfo; use parking_lot::RwLock; use reth_chainspec::ChainSpecProvider; +use reth_evm::ConfigureEvm; use reth_optimism_evm::RethL1BlockInfo; use reth_optimism_forks::OpHardforks; use reth_primitives_traits::{ - transaction::error::InvalidTransactionError, Block, BlockBody, GotExpected, SealedBlock, + transaction::error::InvalidTransactionError, Block, BlockBody, BlockTy, GotExpected, + SealedBlock, }; use reth_storage_api::{AccountInfoReader, BlockReaderIdExt, StateProviderFactory}; use reth_transaction_pool::{ @@ -40,9 +41,9 @@ impl OpL1BlockInfo { /// Validator for Optimism transactions. #[derive(Debug, Clone)] -pub struct OpTransactionValidator { +pub struct OpTransactionValidator { /// The type that performs the actual validation. - inner: Arc>, + inner: Arc>, /// Additional block info required for validation. block_info: Arc, /// If true, ensure that the transaction's sender has enough balance to cover the L1 gas fee @@ -55,7 +56,7 @@ pub struct OpTransactionValidator { fork_tracker: Arc, } -impl OpTransactionValidator { +impl OpTransactionValidator { /// Returns the configured chain spec pub fn chain_spec(&self) -> Arc where @@ -87,15 +88,15 @@ impl OpTransactionValidator { } } -impl OpTransactionValidator +impl OpTransactionValidator where Client: ChainSpecProvider + StateProviderFactory + BlockReaderIdExt + Sync, Tx: EthPoolTransaction + OpPooledTx, - B: Block, + Evm: ConfigureEvm, { /// Create a new [`OpTransactionValidator`]. - pub fn new(inner: EthTransactionValidator) -> Self { + pub fn new(inner: EthTransactionValidator) -> Self { let this = Self::with_block_info(inner, OpL1BlockInfo::default()); if let Ok(Some(block)) = this.inner.client().block_by_number_or_tag(alloy_eips::BlockNumberOrTag::Latest) @@ -114,7 +115,7 @@ where /// Create a new [`OpTransactionValidator`] with the given [`OpL1BlockInfo`]. pub fn with_block_info( - inner: EthTransactionValidator, + inner: EthTransactionValidator, block_info: OpL1BlockInfo, ) -> Self { Self { @@ -290,15 +291,15 @@ where } } -impl TransactionValidator for OpTransactionValidator +impl TransactionValidator for OpTransactionValidator where Client: ChainSpecProvider + StateProviderFactory + BlockReaderIdExt + Sync, Tx: EthPoolTransaction + OpPooledTx, - B: Block, + Evm: ConfigureEvm, { type Transaction = Tx; - type Block = B; + type Block = BlockTy; async fn validate_transaction( &self, diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index f9a2f980ef..d54324c54c 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -188,6 +188,21 @@ impl MockEthProvider { prune_modes: self.prune_modes, } } + + /// Adds the genesis block from the chain spec to the provider. + /// + /// This is useful for tests that require a valid latest block (e.g., transaction validation). + pub fn with_genesis_block(self) -> Self + where + ChainSpec: EthChainSpec
::Header>, + ::Body: Default, + { + let genesis_hash = self.chain_spec.genesis_hash(); + let genesis_header = self.chain_spec.genesis_header().clone(); + let genesis_block = T::Block::new(genesis_header, Default::default()); + self.add_block(genesis_hash, genesis_block); + self + } } impl Default for MockEthProvider { diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 0203071984..2fe1b88a6b 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -17,11 +17,14 @@ reth-chain-state.workspace = true reth-ethereum-primitives.workspace = true reth-chainspec.workspace = true reth-eth-wire-types.workspace = true +reth-evm.workspace = true +reth-evm-ethereum.workspace = true reth-primitives-traits.workspace = true reth-execution-types.workspace = true reth-fs-util.workspace = true reth-storage-api.workspace = true reth-tasks.workspace = true +revm.workspace = true revm-interpreter.workspace = true revm-primitives.workspace = true @@ -92,6 +95,7 @@ serde = [ "reth-ethereum-primitives/serde", "reth-chain-state/serde", "reth-storage-api/serde", + "revm/serde", ] test-utils = [ "rand", @@ -103,6 +107,8 @@ test-utils = [ "reth-primitives-traits/test-utils", "reth-ethereum-primitives/test-utils", "alloy-primitives/rand", + "reth-evm/test-utils", + "reth-evm-ethereum/test-utils", ] arbitrary = [ "proptest", @@ -118,6 +124,7 @@ arbitrary = [ "revm-interpreter/arbitrary", "reth-ethereum-primitives/arbitrary", "revm-primitives/arbitrary", + "revm/arbitrary", ] [[bench]] diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 7fbdda5f29..37904e9d65 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -197,16 +197,22 @@ //! //! ``` //! use reth_chainspec::MAINNET; -//! use reth_storage_api::StateProviderFactory; +//! use reth_storage_api::{BlockReaderIdExt, StateProviderFactory}; //! use reth_tasks::TokioTaskExecutor; //! use reth_chainspec::ChainSpecProvider; //! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool, TransactionPool}; //! use reth_transaction_pool::blobstore::InMemoryBlobStore; //! use reth_chainspec::EthereumHardforks; -//! async fn t(client: C) where C: ChainSpecProvider + StateProviderFactory + Clone + 'static{ +//! use reth_evm::ConfigureEvm; +//! use alloy_consensus::Header; +//! async fn t(client: C, evm_config: Evm) +//! where +//! C: ChainSpecProvider + StateProviderFactory + BlockReaderIdExt
+ Clone + 'static, +//! Evm: ConfigureEvm> + 'static, +//! { //! let blob_store = InMemoryBlobStore::default(); //! let pool = Pool::eth_pool( -//! TransactionValidationTaskExecutor::eth(client, blob_store.clone(), TokioTaskExecutor::default()), +//! TransactionValidationTaskExecutor::eth(client, evm_config, blob_store.clone(), TokioTaskExecutor::default()), //! blob_store, //! Default::default(), //! ); @@ -235,18 +241,21 @@ //! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool}; //! use reth_transaction_pool::blobstore::InMemoryBlobStore; //! use reth_transaction_pool::maintain::{maintain_transaction_pool_future}; +//! use reth_evm::ConfigureEvm; +//! use reth_ethereum_primitives::EthPrimitives; //! use alloy_consensus::Header; //! -//! async fn t(client: C, stream: St) +//! async fn t(client: C, stream: St, evm_config: Evm) //! where C: StateProviderFactory + BlockReaderIdExt
+ ChainSpecProvider + Clone + 'static, -//! St: Stream + Send + Unpin + 'static, +//! St: Stream> + Send + Unpin + 'static, +//! Evm: ConfigureEvm + 'static, //! { //! let blob_store = InMemoryBlobStore::default(); //! let rt = tokio::runtime::Runtime::new().unwrap(); //! let manager = TaskManager::new(rt.handle().clone()); //! let executor = manager.executor(); //! let pool = Pool::eth_pool( -//! TransactionValidationTaskExecutor::eth(client.clone(), blob_store.clone(), executor.clone()), +//! TransactionValidationTaskExecutor::eth(client.clone(), evm_config, blob_store.clone(), executor.clone()), //! blob_store, //! Default::default(), //! ); @@ -302,9 +311,11 @@ use alloy_primitives::{Address, TxHash, B256, U256}; use aquamarine as _; use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; use reth_eth_wire_types::HandleMempoolData; +use reth_evm::ConfigureEvm; +use reth_evm_ethereum::EthEvmConfig; use reth_execution_types::ChangedAccount; -use reth_primitives_traits::Recovered; -use reth_storage_api::StateProviderFactory; +use reth_primitives_traits::{HeaderTy, Recovered}; +use reth_storage_api::{BlockReaderIdExt, StateProviderFactory}; use std::{collections::HashSet, sync::Arc}; use tokio::sync::mpsc::Receiver; use tracing::{instrument, trace}; @@ -328,13 +339,8 @@ mod traits; pub mod test_utils; /// Type alias for default ethereum transaction pool -pub type EthTransactionPool< - Client, - S, - T = EthPooledTransaction, - B = reth_ethereum_primitives::Block, -> = Pool< - TransactionValidationTaskExecutor>, +pub type EthTransactionPool = Pool< + TransactionValidationTaskExecutor>, CoinbaseTipOrdering, S, >; @@ -415,11 +421,15 @@ where } } -impl EthTransactionPool +impl EthTransactionPool where - Client: - ChainSpecProvider + StateProviderFactory + Clone + 'static, + Client: ChainSpecProvider + + StateProviderFactory + + Clone + + BlockReaderIdExt
> + + 'static, S: BlobStore, + Evm: ConfigureEvm + 'static, { /// Returns a new [`Pool`] that uses the default [`TransactionValidationTaskExecutor`] when /// validating [`EthPooledTransaction`]s and ords via [`CoinbaseTipOrdering`] @@ -428,18 +438,25 @@ where /// /// ``` /// use reth_chainspec::MAINNET; - /// use reth_storage_api::StateProviderFactory; + /// use reth_storage_api::{BlockReaderIdExt, StateProviderFactory}; /// use reth_tasks::TokioTaskExecutor; /// use reth_chainspec::ChainSpecProvider; /// use reth_transaction_pool::{ /// blobstore::InMemoryBlobStore, Pool, TransactionValidationTaskExecutor, /// }; /// use reth_chainspec::EthereumHardforks; - /// # fn t(client: C) where C: ChainSpecProvider + StateProviderFactory + Clone + 'static { + /// use reth_evm::ConfigureEvm; + /// use alloy_consensus::Header; + /// # fn t(client: C, evm_config: Evm) + /// # where + /// # C: ChainSpecProvider + StateProviderFactory + BlockReaderIdExt
+ Clone + 'static, + /// # Evm: ConfigureEvm> + 'static, + /// # { /// let blob_store = InMemoryBlobStore::default(); /// let pool = Pool::eth_pool( /// TransactionValidationTaskExecutor::eth( /// client, + /// evm_config, /// blob_store.clone(), /// TokioTaskExecutor::default(), /// ), @@ -450,7 +467,7 @@ where /// ``` pub fn eth_pool( validator: TransactionValidationTaskExecutor< - EthTransactionValidator, + EthTransactionValidator, >, blob_store: S, config: PoolConfig, diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index ca4546e789..efe7a95887 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -857,12 +857,12 @@ mod tests { use super::*; use crate::{ blobstore::InMemoryBlobStore, validate::EthTransactionValidatorBuilder, - CoinbaseTipOrdering, EthPooledTransaction, EthTransactionValidator, Pool, - TransactionOrigin, + CoinbaseTipOrdering, EthPooledTransaction, Pool, TransactionOrigin, }; use alloy_eips::eip2718::Decodable2718; use alloy_primitives::{hex, U256}; use reth_ethereum_primitives::PooledTransactionVariant; + use reth_evm_ethereum::EthEvmConfig; use reth_fs_util as fs; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider}; use reth_tasks::TaskManager; @@ -886,14 +886,14 @@ mod tests { "02f87201830655c2808505ef61f08482565f94388c818ca8b9251b393131c08a736a67ccb192978801049e39c4b5b1f580c001a01764ace353514e8abdfb92446de356b260e3c1225b73fc4c8876a6258d12a129a04f02294aa61ca7676061cd99f29275491218b4754b46a0248e5e42bc5091f507" ); let tx = PooledTransactionVariant::decode_2718(&mut &tx_bytes[..]).unwrap(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); let transaction = EthPooledTransaction::from_pooled(tx.try_into_recovered().unwrap()); let tx_to_cmp = transaction.clone(); let sender = hex!("1f9090aaE28b8a3dCeaDf281B0F12828e676c326").into(); provider.add_account(sender, ExtendedAccount::new(42, U256::MAX)); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _, reth_ethereum_primitives::Block> = - EthTransactionValidatorBuilder::new(provider).build(blob_store.clone()); + let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet()) + .build(blob_store.clone()); let txpool = Pool::new( validator, diff --git a/crates/transaction-pool/src/validate/constants.rs b/crates/transaction-pool/src/validate/constants.rs index d4fca5a2ae..cb20793917 100644 --- a/crates/transaction-pool/src/validate/constants.rs +++ b/crates/transaction-pool/src/validate/constants.rs @@ -10,9 +10,3 @@ pub const TX_SLOT_BYTE_SIZE: usize = 32 * 1024; /// to validate whether they fit into the pool or not. Default is 4 times [`TX_SLOT_BYTE_SIZE`], /// which defaults to 32 KiB, so 128 KiB. pub const DEFAULT_MAX_TX_INPUT_BYTES: usize = 4 * TX_SLOT_BYTE_SIZE; // 128KB - -/// Maximum bytecode to permit for a contract. -pub const MAX_CODE_BYTE_SIZE: usize = revm_primitives::eip170::MAX_CODE_SIZE; - -/// Maximum initcode to permit in a creation transaction and create instructions. -pub const MAX_INIT_CODE_BYTE_SIZE: usize = revm_primitives::eip3860::MAX_INITCODE_SIZE; diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 1775387c68..c4c7d7ebda 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -8,7 +8,7 @@ use crate::{ }, metrics::TxPoolValidationMetrics, traits::TransactionOrigin, - validate::{ValidTransaction, ValidationTask, MAX_INIT_CODE_BYTE_SIZE}, + validate::{ValidTransaction, ValidationTask}, Address, BlobTransactionSidecarVariant, EthBlobTransactionSidecar, EthPoolTransaction, LocalTransactionConfig, TransactionValidationOutcome, TransactionValidationTaskExecutor, TransactionValidator, @@ -23,20 +23,22 @@ use alloy_consensus::{ }; use alloy_eips::{ eip1559::ETHEREUM_BLOCK_GAS_LIMIT_30M, eip4844::env_settings::EnvKzgSettings, - eip7840::BlobParams, + eip7840::BlobParams, BlockId, }; use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks}; +use reth_evm::ConfigureEvm; use reth_primitives_traits::{ - constants::MAX_TX_GAS_LIMIT_OSAKA, transaction::error::InvalidTransactionError, Account, Block, - GotExpected, + transaction::error::InvalidTransactionError, Account, BlockTy, GotExpected, HeaderTy, + SealedBlock, }; -use reth_storage_api::{AccountInfoReader, BytecodeReader, StateProviderFactory}; +use reth_storage_api::{AccountInfoReader, BlockReaderIdExt, BytecodeReader, StateProviderFactory}; use reth_tasks::TaskSpawner; +use revm::context_interface::Cfg; use revm_primitives::U256; use std::{ marker::PhantomData, sync::{ - atomic::{AtomicBool, AtomicU64}, + atomic::{AtomicBool, AtomicU64, AtomicUsize}, Arc, }, time::{Instant, SystemTime}, @@ -58,7 +60,7 @@ use tokio::sync::Mutex; /// /// And adheres to the configured [`LocalTransactionConfig`]. #[derive(Debug)] -pub struct EthTransactionValidator { +pub struct EthTransactionValidator { /// This type fetches account info from the db client: Client, /// Blobstore used for fetching re-injected blob transactions. @@ -89,15 +91,17 @@ pub struct EthTransactionValidator, /// Disable balance checks during transaction validation disable_balance_check: bool, + /// EVM configuration for fetching execution limits + evm_config: Evm, /// Marker for the transaction type - _marker: PhantomData<(T, B)>, + _marker: PhantomData, /// Metrics for tsx pool validation validation_metrics: TxPoolValidationMetrics, /// Bitmap of custom transaction types that are allowed. other_tx_types: U256, } -impl EthTransactionValidator { +impl EthTransactionValidator { /// Returns the configured chain spec pub fn chain_spec(&self) -> Arc where @@ -176,10 +180,11 @@ impl EthTransactionValidator { } } -impl EthTransactionValidator +impl EthTransactionValidator where - Client: ChainSpecProvider + StateProviderFactory, + Client: ChainSpecProvider + StateProviderFactory, Tx: EthPoolTransaction, + Evm: ConfigureEvm, { /// Returns the current max gas limit pub fn block_gas_limit(&self) -> u64 { @@ -361,10 +366,12 @@ where } // Check whether the init code size has been exceeded. - if self.fork_tracker.is_shanghai_activated() && - let Err(err) = transaction.ensure_max_init_code_size(MAX_INIT_CODE_BYTE_SIZE) - { - return Err(TransactionValidationOutcome::Invalid(transaction, err)) + if self.fork_tracker.is_shanghai_activated() { + let max_initcode_size = + self.fork_tracker.max_initcode_size.load(std::sync::atomic::Ordering::Relaxed); + if let Err(err) = transaction.ensure_max_init_code_size(max_initcode_size) { + return Err(TransactionValidationOutcome::Invalid(transaction, err)) + } } // Checks for gas limit @@ -506,10 +513,10 @@ where } } - // Osaka validation of max tx gas. - if self.fork_tracker.is_osaka_activated() && - transaction.gas_limit() > MAX_TX_GAS_LIMIT_OSAKA - { + // Transaction gas limit validation (EIP-7825 for Osaka+) + let tx_gas_limit_cap = + self.fork_tracker.tx_gas_limit_cap.load(std::sync::atomic::Ordering::Relaxed); + if tx_gas_limit_cap > 0 && transaction.gas_limit() > tx_gas_limit_cap { return Err(TransactionValidationOutcome::Invalid( transaction, InvalidTransactionError::GasLimitTooHigh.into(), @@ -743,7 +750,7 @@ where .collect() } - fn on_new_head_block(&self, new_tip_block: &T) { + fn on_new_head_block(&self, new_tip_block: &HeaderTy) { // update all forks if self.chain_spec().is_shanghai_active_at_timestamp(new_tip_block.timestamp()) { self.fork_tracker.shanghai.store(true, std::sync::atomic::Ordering::Relaxed); @@ -774,6 +781,19 @@ where } self.block_gas_limit.store(new_tip_block.gas_limit(), std::sync::atomic::Ordering::Relaxed); + + // Get EVM limits from evm_config.evm_env() + let evm_env = self + .evm_config + .evm_env(new_tip_block) + .expect("evm_env should not fail for executed block"); + + self.fork_tracker + .max_initcode_size + .store(evm_env.cfg_env.max_initcode_size(), std::sync::atomic::Ordering::Relaxed); + self.fork_tracker + .tx_gas_limit_cap + .store(evm_env.cfg_env.tx_gas_limit_cap(), std::sync::atomic::Ordering::Relaxed); } fn max_gas_limit(&self) -> u64 { @@ -799,14 +819,14 @@ where } } -impl TransactionValidator for EthTransactionValidator +impl TransactionValidator for EthTransactionValidator where - Client: ChainSpecProvider + StateProviderFactory, + Client: ChainSpecProvider + StateProviderFactory, Tx: EthPoolTransaction, - B: Block, + Evm: ConfigureEvm, { type Transaction = Tx; - type Block = B; + type Block = BlockTy; async fn validate_transaction( &self, @@ -831,15 +851,17 @@ where self.validate_batch_with_origin(origin, transactions) } - fn on_new_head_block(&self, new_tip_block: &reth_primitives_traits::SealedBlock) { + fn on_new_head_block(&self, new_tip_block: &SealedBlock) { Self::on_new_head_block(self, new_tip_block.header()) } } /// A builder for [`EthTransactionValidator`] and [`TransactionValidationTaskExecutor`] #[derive(Debug)] -pub struct EthTransactionValidatorBuilder { +pub struct EthTransactionValidatorBuilder { client: Client, + /// The EVM configuration to use for validation. + evm_config: Evm, /// Fork indicator whether we are in the Shanghai stage. shanghai: bool, /// Fork indicator whether we are in the Cancun hardfork. @@ -883,10 +905,14 @@ pub struct EthTransactionValidatorBuilder { disable_balance_check: bool, /// Bitmap of custom transaction types that are allowed. other_tx_types: U256, + /// Cached max initcode size from EVM config + max_initcode_size: usize, + /// Cached transaction gas limit cap from EVM config (0 = no cap) + tx_gas_limit_cap: u64, } -impl EthTransactionValidatorBuilder { - /// Creates a new builder for the given client +impl EthTransactionValidatorBuilder { + /// Creates a new builder for the given client and EVM config /// /// By default this assumes the network is on the `Prague` hardfork and the following /// transactions are allowed: @@ -895,10 +921,24 @@ impl EthTransactionValidatorBuilder { /// - EIP-1559 /// - EIP-4844 /// - EIP-7702 - pub fn new(client: Client) -> Self { + pub fn new(client: Client, evm_config: Evm) -> Self + where + Client: ChainSpecProvider + + BlockReaderIdExt
>, + Evm: ConfigureEvm, + { + let chain_spec = client.chain_spec(); + let tip = client + .header_by_id(BlockId::latest()) + .expect("failed to fetch latest header") + .expect("latest header is not found"); + let evm_env = + evm_config.evm_env(&tip).expect("evm_env should not fail for existing blocks"); + Self { block_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M.into(), client, + evm_config, minimum_priority_fee: None, additional_tasks: 1, kzg_settings: EnvKzgSettings::Default, @@ -912,28 +952,26 @@ impl EthTransactionValidatorBuilder { eip4844: true, eip7702: true, - // shanghai is activated by default - shanghai: true, + shanghai: chain_spec.is_shanghai_active_at_timestamp(tip.timestamp()), + cancun: chain_spec.is_cancun_active_at_timestamp(tip.timestamp()), + prague: chain_spec.is_prague_active_at_timestamp(tip.timestamp()), + osaka: chain_spec.is_osaka_active_at_timestamp(tip.timestamp()), - // cancun is activated by default - cancun: true, + tip_timestamp: tip.timestamp(), - // prague is activated by default - prague: true, - - // osaka not yet activated - osaka: false, - - tip_timestamp: 0, - - // max blob count is prague by default - max_blob_count: BlobParams::prague().max_blobs_per_tx, + max_blob_count: chain_spec + .blob_params_at_timestamp(tip.timestamp()) + .unwrap_or_else(BlobParams::prague) + .max_blobs_per_tx, // balance checks are enabled by default disable_balance_check: false, // no custom transaction types by default other_tx_types: U256::ZERO, + + tx_gas_limit_cap: evm_env.cfg_env.tx_gas_limit_cap(), + max_initcode_size: evm_env.cfg_env.max_initcode_size(), } } @@ -1041,28 +1079,6 @@ impl EthTransactionValidatorBuilder { self } - /// Configures validation rules based on the head block's timestamp. - /// - /// For example, whether the Shanghai and Cancun hardfork is activated at launch, or max blob - /// counts. - pub fn with_head_timestamp(mut self, timestamp: u64) -> Self - where - Client: ChainSpecProvider, - { - self.shanghai = self.client.chain_spec().is_shanghai_active_at_timestamp(timestamp); - self.cancun = self.client.chain_spec().is_cancun_active_at_timestamp(timestamp); - self.prague = self.client.chain_spec().is_prague_active_at_timestamp(timestamp); - self.osaka = self.client.chain_spec().is_osaka_active_at_timestamp(timestamp); - self.tip_timestamp = timestamp; - self.max_blob_count = self - .client - .chain_spec() - .blob_params_at_timestamp(timestamp) - .unwrap_or_else(BlobParams::cancun) - .max_blobs_per_tx; - self - } - /// Sets a max size in bytes of a single transaction allowed into the pool pub const fn with_max_tx_input_bytes(mut self, max_tx_input_bytes: usize) -> Self { self.max_tx_input_bytes = max_tx_input_bytes; @@ -1104,13 +1120,13 @@ impl EthTransactionValidatorBuilder { } /// Builds a the [`EthTransactionValidator`] without spawning validator tasks. - pub fn build(self, blob_store: S) -> EthTransactionValidator + pub fn build(self, blob_store: S) -> EthTransactionValidator where S: BlobStore, - B: Block, { let Self { client, + evm_config, shanghai, cancun, prague, @@ -1131,6 +1147,8 @@ impl EthTransactionValidatorBuilder { max_blob_count, additional_tasks: _, other_tx_types, + max_initcode_size, + tx_gas_limit_cap, } = self; let fork_tracker = ForkTracker { @@ -1140,6 +1158,8 @@ impl EthTransactionValidatorBuilder { osaka: AtomicBool::new(osaka), tip_timestamp: AtomicU64::new(tip_timestamp), max_blob_count: AtomicU64::new(max_blob_count), + max_initcode_size: AtomicUsize::new(max_initcode_size), + tx_gas_limit_cap: AtomicU64::new(tx_gas_limit_cap), }; EthTransactionValidator { @@ -1158,6 +1178,7 @@ impl EthTransactionValidatorBuilder { max_tx_input_bytes, max_tx_gas_limit, disable_balance_check, + evm_config, _marker: Default::default(), validation_metrics: TxPoolValidationMetrics::default(), other_tx_types, @@ -1170,18 +1191,17 @@ impl EthTransactionValidatorBuilder { /// The validator will spawn `additional_tasks` additional tasks for validation. /// /// By default this will spawn 1 additional task. - pub fn build_with_tasks( + pub fn build_with_tasks( self, tasks: T, blob_store: S, - ) -> TransactionValidationTaskExecutor> + ) -> TransactionValidationTaskExecutor> where T: TaskSpawner, S: BlobStore, - B: Block, { let additional_tasks = self.additional_tasks; - let validator = self.build::(blob_store); + let validator = self.build::(blob_store); let (tx, task) = ValidationTask::new(); @@ -1223,6 +1243,10 @@ pub struct ForkTracker { pub max_blob_count: AtomicU64, /// Tracks the timestamp of the tip block. pub tip_timestamp: AtomicU64, + /// Cached max initcode size from EVM config + pub max_initcode_size: AtomicUsize, + /// Cached transaction gas limit cap from EVM config (0 = no cap) + pub tx_gas_limit_cap: AtomicU64, } impl ForkTracker { @@ -1304,8 +1328,14 @@ mod tests { use alloy_eips::eip2718::Decodable2718; use alloy_primitives::{hex, U256}; use reth_ethereum_primitives::PooledTransactionVariant; + use reth_evm_ethereum::EthEvmConfig; use reth_primitives_traits::SignedTransaction; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider}; + use revm_primitives::eip3860::MAX_INITCODE_SIZE; + + fn test_evm_config() -> EthEvmConfig { + EthEvmConfig::mainnet() + } fn get_transaction() -> EthPooledTransaction { let raw = "0x02f914950181ad84b2d05e0085117553845b830f7df88080b9143a6040608081523462000414576200133a803803806200001e8162000419565b9283398101608082820312620004145781516001600160401b03908181116200041457826200004f9185016200043f565b92602092838201519083821162000414576200006d9183016200043f565b8186015190946001600160a01b03821692909183900362000414576060015190805193808511620003145760038054956001938488811c9816801562000409575b89891014620003f3578190601f988981116200039d575b50899089831160011462000336576000926200032a575b505060001982841b1c191690841b1781555b8751918211620003145760049788548481811c9116801562000309575b89821014620002f457878111620002a9575b5087908784116001146200023e5793839491849260009562000232575b50501b92600019911b1c19161785555b6005556007805460ff60a01b19169055600880546001600160a01b0319169190911790553015620001f3575060025469d3c21bcecceda100000092838201809211620001de57506000917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9160025530835282815284832084815401905584519384523093a351610e889081620004b28239f35b601190634e487b7160e01b6000525260246000fd5b90606493519262461bcd60e51b845283015260248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b0151935038806200013a565b9190601f198416928a600052848a6000209460005b8c8983831062000291575050501062000276575b50505050811b0185556200014a565b01519060f884600019921b161c191690553880808062000267565b86860151895590970196948501948893500162000253565b89600052886000208880860160051c8201928b8710620002ea575b0160051c019085905b828110620002dd5750506200011d565b60008155018590620002cd565b92508192620002c4565b60228a634e487b7160e01b6000525260246000fd5b90607f16906200010b565b634e487b7160e01b600052604160045260246000fd5b015190503880620000dc565b90869350601f19831691856000528b6000209260005b8d8282106200038657505084116200036d575b505050811b018155620000ee565b015160001983861b60f8161c191690553880806200035f565b8385015186558a979095019493840193016200034c565b90915083600052896000208980850160051c8201928c8610620003e9575b918891869594930160051c01915b828110620003d9575050620000c5565b60008155859450889101620003c9565b92508192620003bb565b634e487b7160e01b600052602260045260246000fd5b97607f1697620000ae565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176200031457604052565b919080601f84011215620004145782516001600160401b038111620003145760209062000475601f8201601f1916830162000419565b92818452828287010111620004145760005b8181106200049d57508260009394955001015290565b85810183015184820184015282016200048756fe608060408181526004918236101561001657600080fd5b600092833560e01c91826306fdde0314610a1c57508163095ea7b3146109f257816318160ddd146109d35781631b4c84d2146109ac57816323b872dd14610833578163313ce5671461081757816339509351146107c357816370a082311461078c578163715018a6146107685781638124f7ac146107495781638da5cb5b1461072057816395d89b411461061d578163a457c2d714610575578163a9059cbb146104e4578163c9567bf914610120575063dd62ed3e146100d557600080fd5b3461011c578060031936011261011c57806020926100f1610b5a565b6100f9610b75565b6001600160a01b0391821683526001865283832091168252845220549051908152f35b5080fd5b905082600319360112610338576008546001600160a01b039190821633036104975760079283549160ff8360a01c1661045557737a250d5630b4cf539739df2c5dacb4c659f2488d92836bffffffffffffffffffffffff60a01b8092161786553087526020938785528388205430156104065730895260018652848920828a52865280858a205584519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925863092a38554835163c45a015560e01b815290861685828581845afa9182156103dd57849187918b946103e7575b5086516315ab88c960e31b815292839182905afa9081156103dd576044879289928c916103c0575b508b83895196879586946364e329cb60e11b8652308c870152166024850152165af19081156103b6579086918991610389575b50169060065416176006558385541660604730895288865260c4858a20548860085416928751958694859363f305d71960e01b8552308a86015260248501528d60448501528d606485015260848401524260a48401525af1801561037f579084929161034c575b50604485600654169587541691888551978894859363095ea7b360e01b855284015260001960248401525af1908115610343575061030c575b5050805460ff60a01b1916600160a01b17905580f35b81813d831161033c575b6103208183610b8b565b8101031261033857518015150361011c5738806102f6565b8280fd5b503d610316565b513d86823e3d90fd5b6060809293503d8111610378575b6103648183610b8b565b81010312610374578290386102bd565b8580fd5b503d61035a565b83513d89823e3d90fd5b6103a99150863d88116103af575b6103a18183610b8b565b810190610e33565b38610256565b503d610397565b84513d8a823e3d90fd5b6103d79150843d86116103af576103a18183610b8b565b38610223565b85513d8b823e3d90fd5b6103ff919450823d84116103af576103a18183610b8b565b92386101fb565b845162461bcd60e51b81528085018790526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b6020606492519162461bcd60e51b8352820152601760248201527f74726164696e6720697320616c7265616479206f70656e0000000000000000006044820152fd5b608490602084519162461bcd60e51b8352820152602160248201527f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6044820152603760f91b6064820152fd5b9050346103385781600319360112610338576104fe610b5a565b9060243593303303610520575b602084610519878633610bc3565b5160018152f35b600594919454808302908382041483151715610562576127109004820391821161054f5750925080602061050b565b634e487b7160e01b815260118552602490fd5b634e487b7160e01b825260118652602482fd5b9050823461061a578260031936011261061a57610590610b5a565b918360243592338152600160205281812060018060a01b03861682526020522054908282106105c9576020856105198585038733610d31565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152fd5b80fd5b83833461011c578160031936011261011c57805191809380549160019083821c92828516948515610716575b6020958686108114610703578589529081156106df5750600114610687575b6106838787610679828c0383610b8b565b5191829182610b11565b0390f35b81529295507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b8284106106cc57505050826106839461067992820101948680610668565b80548685018801529286019281016106ae565b60ff19168887015250505050151560051b8301019250610679826106838680610668565b634e487b7160e01b845260228352602484fd5b93607f1693610649565b50503461011c578160031936011261011c5760085490516001600160a01b039091168152602090f35b50503461011c578160031936011261011c576020906005549051908152f35b833461061a578060031936011261061a57600880546001600160a01b031916905580f35b50503461011c57602036600319011261011c5760209181906001600160a01b036107b4610b5a565b16815280845220549051908152f35b82843461061a578160031936011261061a576107dd610b5a565b338252600160209081528383206001600160a01b038316845290528282205460243581019290831061054f57602084610519858533610d31565b50503461011c578160031936011261011c576020905160128152f35b83833461011c57606036600319011261011c5761084e610b5a565b610856610b75565b6044359160018060a01b0381169485815260209560018752858220338352875285822054976000198903610893575b505050906105199291610bc3565b85891061096957811561091a5733156108cc5750948481979861051997845260018a528284203385528a52039120558594938780610885565b865162461bcd60e51b8152908101889052602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b865162461bcd60e51b81529081018890526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b865162461bcd60e51b8152908101889052601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606490fd5b50503461011c578160031936011261011c5760209060ff60075460a01c1690519015158152f35b50503461011c578160031936011261011c576020906002549051908152f35b50503461011c578060031936011261011c57602090610519610a12610b5a565b6024359033610d31565b92915034610b0d5783600319360112610b0d57600354600181811c9186908281168015610b03575b6020958686108214610af05750848852908115610ace5750600114610a75575b6106838686610679828b0383610b8b565b929550600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b828410610abb575050508261068394610679928201019438610a64565b8054868501880152928601928101610a9e565b60ff191687860152505050151560051b83010192506106798261068338610a64565b634e487b7160e01b845260229052602483fd5b93607f1693610a44565b8380fd5b6020808252825181830181905290939260005b828110610b4657505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610b24565b600435906001600160a01b0382168203610b7057565b600080fd5b602435906001600160a01b0382168203610b7057565b90601f8019910116810190811067ffffffffffffffff821117610bad57604052565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03908116918215610cde5716918215610c8d57600082815280602052604081205491808310610c3957604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815220818154019055604051908152a3565b60405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608490fd5b60405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608490fd5b6001600160a01b03908116918215610de25716918215610d925760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b60405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608490fd5b60405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608490fd5b90816020910312610b7057516001600160a01b0381168103610b70579056fea2646970667358221220285c200b3978b10818ff576bb83f2dc4a2a7c98dfb6a36ea01170de792aa652764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000d3fd4f95820a9aa848ce716d6c200eaefb9a2e4900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000003543131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035431310000000000000000000000000000000000000000000000000000000000c001a04e551c75810ffdfe6caff57da9f5a8732449f42f0f4c57f935b05250a76db3b6a046cd47e6d01914270c1ec0d9ac7fae7dfb240ec9a8b6ec7898c4d6aa174388f2"; @@ -1327,6 +1357,8 @@ mod tests { osaka: false.into(), tip_timestamp: 0.into(), max_blob_count: 0.into(), + max_initcode_size: AtomicUsize::new(MAX_INITCODE_SIZE), + tx_gas_limit_cap: AtomicU64::new(0), }; let res = ensure_intrinsic_gas(&transaction, &fork_tracker); @@ -1336,14 +1368,14 @@ mod tests { let res = ensure_intrinsic_gas(&transaction, &fork_tracker); assert!(res.is_ok()); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider).build(blob_store.clone()); + let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config()) + .build(blob_store.clone()); let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone()); @@ -1363,17 +1395,16 @@ mod tests { async fn invalid_on_gas_limit_too_high() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .set_block_gas_limit(1_000_000) // tx gas limit is 1_015_288 - .build(blob_store.clone()); + let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config()) + .set_block_gas_limit(1_000_000) // tx gas limit is 1_015_288 + .build(blob_store.clone()); let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone()); @@ -1397,17 +1428,16 @@ mod tests { #[tokio::test] async fn invalid_on_fee_cap_exceeded() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .set_tx_fee_cap(100) // 100 wei cap - .build(blob_store.clone()); + let validator = EthTransactionValidatorBuilder::new(provider, test_evm_config()) + .set_tx_fee_cap(100) // 100 wei cap + .build(blob_store.clone()); let outcome = validator.validate_one(TransactionOrigin::Local, transaction.clone()); assert!(outcome.is_invalid()); @@ -1435,17 +1465,16 @@ mod tests { #[tokio::test] async fn valid_on_zero_fee_cap() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .set_tx_fee_cap(0) // no cap - .build(blob_store); + let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet()) + .set_tx_fee_cap(0) // no cap + .build(blob_store); let outcome = validator.validate_one(TransactionOrigin::Local, transaction); assert!(outcome.is_valid()); @@ -1454,17 +1483,16 @@ mod tests { #[tokio::test] async fn valid_on_normal_fee_cap() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .set_tx_fee_cap(2e18 as u128) // 2 ETH cap - .build(blob_store); + let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet()) + .set_tx_fee_cap(2e18 as u128) // 2 ETH cap + .build(blob_store); let outcome = validator.validate_one(TransactionOrigin::Local, transaction); assert!(outcome.is_valid()); @@ -1473,17 +1501,16 @@ mod tests { #[tokio::test] async fn invalid_on_max_tx_gas_limit_exceeded() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .with_max_tx_gas_limit(Some(500_000)) // Set limit lower than transaction gas limit (1_015_288) - .build(blob_store.clone()); + let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet()) + .with_max_tx_gas_limit(Some(500_000)) // Set limit lower than transaction gas limit (1_015_288) + .build(blob_store.clone()); let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone()); assert!(outcome.is_invalid()); @@ -1506,17 +1533,16 @@ mod tests { #[tokio::test] async fn valid_on_max_tx_gas_limit_disabled() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .with_max_tx_gas_limit(None) // disabled - .build(blob_store); + let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet()) + .with_max_tx_gas_limit(None) // disabled + .build(blob_store); let outcome = validator.validate_one(TransactionOrigin::External, transaction); assert!(outcome.is_valid()); @@ -1525,17 +1551,16 @@ mod tests { #[tokio::test] async fn valid_on_max_tx_gas_limit_within_limit() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), ); let blob_store = InMemoryBlobStore::default(); - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .with_max_tx_gas_limit(Some(2_000_000)) // Set limit higher than transaction gas limit (1_015_288) - .build(blob_store); + let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet()) + .with_max_tx_gas_limit(Some(2_000_000)) // Set limit higher than transaction gas limit (1_015_288) + .build(blob_store); let outcome = validator.validate_one(TransactionOrigin::External, transaction); assert!(outcome.is_valid()); @@ -1544,7 +1569,7 @@ mod tests { // Helper function to set up common test infrastructure for priority fee tests fn setup_priority_fee_test() -> (EthPooledTransaction, MockEthProvider) { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); provider.add_account( transaction.sender(), ExtendedAccount::new(transaction.nonce(), U256::MAX), @@ -1557,9 +1582,9 @@ mod tests { provider: MockEthProvider, minimum_priority_fee: Option, local_config: Option, - ) -> EthTransactionValidator { + ) -> EthTransactionValidator { let blob_store = InMemoryBlobStore::default(); - let mut builder = EthTransactionValidatorBuilder::new(provider) + let mut builder = EthTransactionValidatorBuilder::new(provider, test_evm_config()) .with_minimum_priority_fee(minimum_priority_fee); if let Some(config) = local_config { @@ -1714,7 +1739,7 @@ mod tests { fn reject_oversized_tx() { let mut transaction = get_transaction(); transaction.encoded_length = DEFAULT_MAX_TX_INPUT_BYTES + 1; - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); // No minimum priority fee set (default is None) let validator = create_validator_with_minimum_fee(provider, None, None); @@ -1727,7 +1752,7 @@ mod tests { #[tokio::test] async fn valid_with_disabled_balance_check() { let transaction = get_transaction(); - let provider = MockEthProvider::default(); + let provider = MockEthProvider::default().with_genesis_block(); // Set account with 0 balance provider.add_account( @@ -1736,8 +1761,8 @@ mod tests { ); // Validate with balance check enabled - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider.clone()) + let validator = + EthTransactionValidatorBuilder::new(provider.clone(), EthEvmConfig::mainnet()) .build(InMemoryBlobStore::default()); let outcome = validator.validate_one(TransactionOrigin::External, transaction.clone()); @@ -1753,10 +1778,9 @@ mod tests { } // Validate with balance check disabled - let validator: EthTransactionValidator<_, _> = - EthTransactionValidatorBuilder::new(provider) - .disable_balance_check() - .build(InMemoryBlobStore::default()); + let validator = EthTransactionValidatorBuilder::new(provider, EthEvmConfig::mainnet()) + .disable_balance_check() + .build(InMemoryBlobStore::default()); let outcome = validator.validate_one(TransactionOrigin::External, transaction); assert!(outcome.is_valid()); // Should be valid because balance check is disabled diff --git a/crates/transaction-pool/src/validate/mod.rs b/crates/transaction-pool/src/validate/mod.rs index d3ec6b3616..7344ac7609 100644 --- a/crates/transaction-pool/src/validate/mod.rs +++ b/crates/transaction-pool/src/validate/mod.rs @@ -21,9 +21,7 @@ pub use eth::*; pub use task::{TransactionValidationTaskExecutor, ValidationTask}; /// Validation constants. -pub use constants::{ - DEFAULT_MAX_TX_INPUT_BYTES, MAX_CODE_BYTE_SIZE, MAX_INIT_CODE_BYTE_SIZE, TX_SLOT_BYTE_SIZE, -}; +pub use constants::{DEFAULT_MAX_TX_INPUT_BYTES, TX_SLOT_BYTE_SIZE}; /// A Result type returned after checking a transaction's validity. #[derive(Debug)] diff --git a/crates/transaction-pool/src/validate/task.rs b/crates/transaction-pool/src/validate/task.rs index 39ae41bc3d..e25c47b248 100644 --- a/crates/transaction-pool/src/validate/task.rs +++ b/crates/transaction-pool/src/validate/task.rs @@ -8,7 +8,10 @@ use crate::{ TransactionValidator, }; use futures_util::{lock::Mutex, StreamExt}; -use reth_primitives_traits::SealedBlock; +use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; +use reth_evm::ConfigureEvm; +use reth_primitives_traits::{HeaderTy, SealedBlock}; +use reth_storage_api::BlockReaderIdExt; use reth_tasks::TaskSpawner; use std::{future::Future, pin::Pin, sync::Arc}; use tokio::{ @@ -116,8 +119,16 @@ impl Clone for TransactionValidationTaskExecutor { impl TransactionValidationTaskExecutor<()> { /// Convenience method to create a [`EthTransactionValidatorBuilder`] - pub fn eth_builder(client: Client) -> EthTransactionValidatorBuilder { - EthTransactionValidatorBuilder::new(client) + pub fn eth_builder( + client: Client, + evm_config: Evm, + ) -> EthTransactionValidatorBuilder + where + Client: ChainSpecProvider + + BlockReaderIdExt
>, + Evm: ConfigureEvm, + { + EthTransactionValidatorBuilder::new(client, evm_config) } } @@ -139,16 +150,19 @@ impl TransactionValidationTaskExecutor { } } -impl TransactionValidationTaskExecutor> { +impl TransactionValidationTaskExecutor> { /// Creates a new instance for the given client /// /// This will spawn a single validation tasks that performs the actual validation. /// See [`TransactionValidationTaskExecutor::eth_with_additional_tasks`] - pub fn eth(client: Client, blob_store: S, tasks: T) -> Self + pub fn eth(client: Client, evm_config: Evm, blob_store: S, tasks: T) -> Self where T: TaskSpawner, + Client: ChainSpecProvider + + BlockReaderIdExt
>, + Evm: ConfigureEvm, { - Self::eth_with_additional_tasks(client, blob_store, tasks, 0) + Self::eth_with_additional_tasks(client, evm_config, blob_store, tasks, 0) } /// Creates a new instance for the given client @@ -162,14 +176,18 @@ impl TransactionValidationTaskExecutor( client: Client, + evm_config: Evm, blob_store: S, tasks: T, num_additional_tasks: usize, ) -> Self where T: TaskSpawner, + Client: ChainSpecProvider + + BlockReaderIdExt
>, + Evm: ConfigureEvm, { - EthTransactionValidatorBuilder::new(client) + EthTransactionValidatorBuilder::new(client, evm_config) .with_additional_tasks(num_additional_tasks) .build_with_tasks(tasks, blob_store) } diff --git a/examples/custom-node-components/src/main.rs b/examples/custom-node-components/src/main.rs index 2150118173..ebb1246457 100644 --- a/examples/custom-node-components/src/main.rs +++ b/examples/custom-node-components/src/main.rs @@ -5,15 +5,16 @@ use reth_ethereum::{ chainspec::ChainSpec, cli::interface::Cli, + evm::EthEvmConfig, node::{ - api::{BlockTy, FullNodeTypes, NodeTypes}, + api::{FullNodeTypes, NodeTypes}, builder::{components::PoolBuilder, BuilderContext}, node::EthereumAddOns, EthereumNode, }, pool::{ - blobstore::InMemoryBlobStore, CoinbaseTipOrdering, EthPooledTransaction, - EthTransactionPool, Pool, PoolConfig, TransactionValidationTaskExecutor, + blobstore::InMemoryBlobStore, CoinbaseTipOrdering, EthTransactionPool, Pool, PoolConfig, + TransactionValidationTaskExecutor, }, provider::CanonStateSubscriptions, EthPrimitives, @@ -49,28 +50,24 @@ pub struct CustomPoolBuilder { /// Implement the [`PoolBuilder`] trait for the custom pool builder /// /// This will be used to build the transaction pool and its maintenance tasks during launch. -impl PoolBuilder for CustomPoolBuilder +impl PoolBuilder for CustomPoolBuilder where Node: FullNodeTypes>, { - type Pool = EthTransactionPool< - Node::Provider, - InMemoryBlobStore, - EthPooledTransaction, - BlockTy, - >; + type Pool = EthTransactionPool; - async fn build_pool(self, ctx: &BuilderContext) -> eyre::Result { + async fn build_pool( + self, + ctx: &BuilderContext, + evm_config: EthEvmConfig, + ) -> eyre::Result { let data_dir = ctx.config().datadir(); let blob_store = InMemoryBlobStore::default(); - let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone()) - .with_head_timestamp(ctx.head().timestamp) - .kzg_settings(ctx.kzg_settings()?) - .with_additional_tasks(ctx.config().txpool.additional_validation_tasks) - .build_with_tasks::<_, _, _, BlockTy>( - ctx.task_executor().clone(), - blob_store.clone(), - ); + let validator = + TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config) + .kzg_settings(ctx.kzg_settings()?) + .with_additional_tasks(ctx.config().txpool.additional_validation_tasks) + .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()); let transaction_pool = Pool::new(validator, CoinbaseTipOrdering::default(), blob_store, self.pool_config);