diff --git a/Cargo.lock b/Cargo.lock index 58982032e..eabf3db01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8939,6 +8939,7 @@ dependencies = [ "reth-chainspec", "reth-consensus", "reth-consensus-common", + "reth-engine-primitives", "reth-errors", "reth-ethereum-consensus", "reth-evm", @@ -8946,7 +8947,6 @@ dependencies = [ "reth-network-api", "reth-network-peers", "reth-network-types", - "reth-payload-validator", "reth-primitives", "reth-primitives-traits", "reth-provider", @@ -9020,7 +9020,6 @@ dependencies = [ name = "reth-rpc-builder" version = "1.1.2" dependencies = [ - "alloy-consensus", "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", diff --git a/crates/engine/primitives/src/lib.rs b/crates/engine/primitives/src/lib.rs index 89fb7459b..2bd642cfa 100644 --- a/crates/engine/primitives/src/lib.rs +++ b/crates/engine/primitives/src/lib.rs @@ -10,6 +10,8 @@ mod error; +use core::fmt; + use alloy_consensus::BlockHeader; use alloy_rpc_types_engine::{ExecutionPayload, ExecutionPayloadSidecar, PayloadError}; pub use error::BeaconOnNewPayloadError; @@ -80,26 +82,11 @@ pub trait EngineTypes: + 'static; } -/// Type that validates the payloads processed by the engine. -pub trait EngineValidator: Clone + Send + Sync + Unpin + 'static { +/// Type that validates an [`ExecutionPayload`]. +pub trait PayloadValidator: fmt::Debug + Send + Sync + Unpin + 'static { /// The block type used by the engine. type Block: Block; - /// Validates the presence or exclusion of fork-specific fields based on the payload attributes - /// and the message version. - fn validate_version_specific_fields( - &self, - version: EngineApiMessageVersion, - payload_or_attrs: PayloadOrAttributes<'_, ::PayloadAttributes>, - ) -> Result<(), EngineObjectValidationError>; - - /// Ensures that the payload attributes are valid for the given [`EngineApiMessageVersion`]. - fn ensure_well_formed_attributes( - &self, - version: EngineApiMessageVersion, - attributes: &::PayloadAttributes, - ) -> Result<(), EngineObjectValidationError>; - /// Ensures that the given payload does not violate any consensus rules that concern the block's /// layout. /// @@ -113,6 +100,24 @@ pub trait EngineValidator: Clone + Send + Sync + Unpin + 'st payload: ExecutionPayload, sidecar: ExecutionPayloadSidecar, ) -> Result, PayloadError>; +} + +/// Type that validates the payloads processed by the engine. +pub trait EngineValidator: PayloadValidator { + /// Validates the presence or exclusion of fork-specific fields based on the payload attributes + /// and the message version. + fn validate_version_specific_fields( + &self, + version: EngineApiMessageVersion, + payload_or_attrs: PayloadOrAttributes<'_, ::PayloadAttributes>, + ) -> Result<(), EngineObjectValidationError>; + + /// Ensures that the payload attributes are valid for the given [`EngineApiMessageVersion`]. + fn ensure_well_formed_attributes( + &self, + version: EngineApiMessageVersion, + attributes: &::PayloadAttributes, + ) -> Result<(), EngineObjectValidationError>; /// Validates the payload attributes with respect to the header. /// diff --git a/crates/ethereum/engine-primitives/src/lib.rs b/crates/ethereum/engine-primitives/src/lib.rs index beefd54ca..59c870f4d 100644 --- a/crates/ethereum/engine-primitives/src/lib.rs +++ b/crates/ethereum/engine-primitives/src/lib.rs @@ -18,7 +18,7 @@ pub use alloy_rpc_types_engine::{ }; pub use payload::{EthBuiltPayload, EthPayloadBuilderAttributes}; use reth_chainspec::ChainSpec; -use reth_engine_primitives::{EngineTypes, EngineValidator}; +use reth_engine_primitives::{EngineTypes, EngineValidator, PayloadValidator}; use reth_payload_primitives::{ validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError, PayloadOrAttributes, PayloadTypes, @@ -82,12 +82,22 @@ impl EthereumEngineValidator { } } +impl PayloadValidator for EthereumEngineValidator { + type Block = Block; + + fn ensure_well_formed_payload( + &self, + payload: ExecutionPayload, + sidecar: ExecutionPayloadSidecar, + ) -> Result { + self.inner.ensure_well_formed_payload(payload, sidecar) + } +} + impl EngineValidator for EthereumEngineValidator where Types: EngineTypes, { - type Block = Block; - fn validate_version_specific_fields( &self, version: EngineApiMessageVersion, @@ -103,12 +113,4 @@ where ) -> Result<(), EngineObjectValidationError> { validate_version_specific_fields(self.chain_spec(), version, attributes.into()) } - - fn ensure_well_formed_payload( - &self, - payload: ExecutionPayload, - sidecar: ExecutionPayloadSidecar, - ) -> Result { - self.inner.ensure_well_formed_payload(payload, sidecar) - } } diff --git a/crates/ethereum/node/src/node.rs b/crates/ethereum/node/src/node.rs index b2fc7e677..54707e69b 100644 --- a/crates/ethereum/node/src/node.rs +++ b/crates/ethereum/node/src/node.rs @@ -6,14 +6,13 @@ use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGenera use reth_beacon_consensus::EthBeaconConsensus; use reth_chainspec::ChainSpec; use reth_ethereum_engine_primitives::{ - EthBuiltPayload, EthPayloadAttributes, EthPayloadBuilderAttributes, EthereumEngineValidator, + EthBuiltPayload, EthPayloadAttributes, EthPayloadBuilderAttributes, }; use reth_evm::execute::BasicBlockExecutorProvider; use reth_evm_ethereum::execute::EthExecutionStrategyFactory; use reth_network::{NetworkHandle, PeersInfo}; use reth_node_api::{ - AddOnsContext, ConfigureEvm, EngineValidator, FullNodeComponents, HeaderTy, NodeTypesWithDB, - TxTy, + AddOnsContext, ConfigureEvm, FullNodeComponents, HeaderTy, NodeTypesWithDB, TxTy, }; use reth_node_builder::{ components::{ @@ -37,6 +36,8 @@ use reth_trie_db::MerklePatriciaTrie; use crate::{EthEngineTypes, EthEvmConfig}; +pub use reth_ethereum_engine_primitives::EthereumEngineValidator; + /// Type configuration for a regular Ethereum node. #[derive(Debug, Default, Clone, Copy)] #[non_exhaustive] @@ -353,9 +354,12 @@ pub struct EthereumEngineValidatorBuilder; impl EngineValidatorBuilder for EthereumEngineValidatorBuilder where - Types: NodeTypesWithEngine, + Types: NodeTypesWithEngine< + ChainSpec = ChainSpec, + Engine = EthEngineTypes, + Primitives = EthPrimitives, + >, Node: FullNodeComponents, - EthereumEngineValidator: EngineValidator, { type Validator = EthereumEngineValidator; diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index e6c9ad233..c8e08078b 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -10,7 +10,7 @@ use std::{ use alloy_rpc_types::engine::ClientVersionV1; use futures::TryFutureExt; use reth_node_api::{ - AddOnsContext, EngineValidator, FullNodeComponents, NodeAddOns, NodePrimitives, NodeTypes, + AddOnsContext, BlockTy, EngineValidator, FullNodeComponents, NodeAddOns, NodeTypes, NodeTypesWithEngine, }; use reth_node_core::{ @@ -33,6 +33,7 @@ use reth_rpc_builder::{ use reth_rpc_engine_api::{capabilities::EngineCapabilities, EngineApi}; use reth_tasks::TaskExecutor; use reth_tracing::tracing::{debug, info}; +use std::sync::Arc; use crate::EthApiBuilderCtx; @@ -402,15 +403,7 @@ where impl RpcAddOns where - N: FullNodeComponents< - Types: ProviderNodeTypes< - Primitives: NodePrimitives< - Block = reth_primitives::Block, - BlockHeader = reth_primitives::Header, - BlockBody = reth_primitives::BlockBody, - >, - >, - >, + N: FullNodeComponents, EthApi: EthApiTypes + FullEthApiServer + AddDevSigners @@ -449,7 +442,7 @@ where Box::new(node.task_executor().clone()), client, EngineCapabilities::default(), - engine_validator, + engine_validator.clone(), ); info!(target: "reth::cli", "Engine API handler initialized"); @@ -466,7 +459,12 @@ where .with_evm_config(node.evm_config().clone()) .with_block_executor(node.block_executor().clone()) .with_consensus(node.consensus().clone()) - .build_with_auth_server(module_config, engine_api, eth_api_builder); + .build_with_auth_server( + module_config, + engine_api, + eth_api_builder, + Arc::new(engine_validator), + ); // in dev mode we generate 20 random dev-signer accounts if config.dev.dev { @@ -588,7 +586,8 @@ impl>> EthApi /// Helper trait that provides the validator for the engine API pub trait EngineValidatorAddOn: Send { /// The Validator type to use for the engine API. - type Validator: EngineValidator<::Engine>; + type Validator: EngineValidator<::Engine, Block = BlockTy> + + Clone; /// Creates the engine validator for an engine API based node. fn engine_validator( @@ -613,7 +612,8 @@ where /// A type that knows how to build the engine validator. pub trait EngineValidatorBuilder: Send + Sync + Clone { /// The consensus implementation to build. - type Validator: EngineValidator<::Engine>; + type Validator: EngineValidator<::Engine, Block = BlockTy> + + Clone; /// Creates the engine validator. fn build( @@ -625,8 +625,10 @@ pub trait EngineValidatorBuilder: Send + Sync + Clone impl EngineValidatorBuilder for F where Node: FullNodeComponents, - Validator: - EngineValidator<::Engine> + Clone + Unpin + 'static, + Validator: EngineValidator<::Engine, Block = BlockTy> + + Clone + + Unpin + + 'static, F: FnOnce(&AddOnsContext<'_, Node>) -> Fut + Send + Sync + Clone, Fut: Future> + Send, { diff --git a/crates/optimism/node/src/engine.rs b/crates/optimism/node/src/engine.rs index 063ac3617..1db50b72e 100644 --- a/crates/optimism/node/src/engine.rs +++ b/crates/optimism/node/src/engine.rs @@ -12,7 +12,7 @@ use reth_node_api::{ EngineObjectValidationError, MessageValidationKind, PayloadOrAttributes, PayloadTypes, VersionSpecificValidationError, }, - validate_version_specific_fields, EngineTypes, EngineValidator, + validate_version_specific_fields, EngineTypes, EngineValidator, PayloadValidator, }; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_forks::{OpHardfork, OpHardforks}; @@ -77,12 +77,22 @@ impl OpEngineValidator { } } +impl PayloadValidator for OpEngineValidator { + type Block = Block; + + fn ensure_well_formed_payload( + &self, + payload: ExecutionPayload, + sidecar: ExecutionPayloadSidecar, + ) -> Result, PayloadError> { + self.inner.ensure_well_formed_payload(payload, sidecar) + } +} + impl EngineValidator for OpEngineValidator where Types: EngineTypes, { - type Block = Block; - fn validate_version_specific_fields( &self, version: EngineApiMessageVersion, @@ -136,14 +146,6 @@ where Ok(()) } - - fn ensure_well_formed_payload( - &self, - payload: ExecutionPayload, - sidecar: ExecutionPayloadSidecar, - ) -> Result, PayloadError> { - self.inner.ensure_well_formed_payload(payload, sidecar) - } } /// Validates the presence of the `withdrawals` field according to the payload timestamp. diff --git a/crates/optimism/node/src/node.rs b/crates/optimism/node/src/node.rs index 35e33ccd7..54ff36dab 100644 --- a/crates/optimism/node/src/node.rs +++ b/crates/optimism/node/src/node.rs @@ -238,7 +238,12 @@ impl>> OpAddOn impl NodeAddOns for OpAddOns where N: FullNodeComponents< - Types: NodeTypes, + Types: NodeTypesWithEngine< + ChainSpec = OpChainSpec, + Primitives = OpPrimitives, + Storage = OpStorage, + Engine = OpEngineTypes, + >, >, OpEngineValidator: EngineValidator<::Engine>, { @@ -283,7 +288,12 @@ where impl RethRpcAddOns for OpAddOns where N: FullNodeComponents< - Types: NodeTypes, + Types: NodeTypesWithEngine< + ChainSpec = OpChainSpec, + Primitives = OpPrimitives, + Storage = OpStorage, + Engine = OpEngineTypes, + >, >, OpEngineValidator: EngineValidator<::Engine>, { @@ -296,8 +306,13 @@ where impl EngineValidatorAddOn for OpAddOns where - N: FullNodeComponents>, - OpEngineValidator: EngineValidator<::Engine>, + N: FullNodeComponents< + Types: NodeTypesWithEngine< + ChainSpec = OpChainSpec, + Primitives = OpPrimitives, + Engine = OpEngineTypes, + >, + >, { type Validator = OpEngineValidator; @@ -674,9 +689,12 @@ pub struct OpEngineValidatorBuilder; impl EngineValidatorBuilder for OpEngineValidatorBuilder where - Types: NodeTypesWithEngine, + Types: NodeTypesWithEngine< + ChainSpec = OpChainSpec, + Primitives = OpPrimitives, + Engine = OpEngineTypes, + >, Node: FullNodeComponents, - OpEngineValidator: EngineValidator, { type Validator = OpEngineValidator; diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index a0712d617..7dbbe7608 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -31,8 +31,6 @@ reth-transaction-pool.workspace = true reth-evm.workspace = true reth-engine-primitives.workspace = true -alloy-consensus.workspace = true - # rpc/net jsonrpsee = { workspace = true, features = ["server"] } tower-http = { workspace = true, features = ["full"] } diff --git a/crates/rpc/rpc-builder/src/eth.rs b/crates/rpc/rpc-builder/src/eth.rs index 2a6744e7b..7339c7089 100644 --- a/crates/rpc/rpc-builder/src/eth.rs +++ b/crates/rpc/rpc-builder/src/eth.rs @@ -1,4 +1,3 @@ -use alloy_consensus::Header; use reth_evm::ConfigureEvm; use reth_primitives::NodePrimitives; use reth_provider::{BlockReader, CanonStateSubscriptions, EvmEnvProvider, StateProviderFactory}; @@ -62,7 +61,7 @@ where >, ) -> Self where - EvmConfig: ConfigureEvm
, + EvmConfig: ConfigureEvm
, Tasks: TaskSpawner + Clone + 'static, { let cache = EthStateCache::spawn_with(provider.clone(), config.cache, executor.clone()); diff --git a/crates/rpc/rpc-builder/src/lib.rs b/crates/rpc/rpc-builder/src/lib.rs index 122002050..ce29b77f0 100644 --- a/crates/rpc/rpc-builder/src/lib.rs +++ b/crates/rpc/rpc-builder/src/lib.rs @@ -16,10 +16,10 @@ //! Configure only an http server with a selection of [`RethRpcModule`]s //! //! ``` -//! use alloy_consensus::Header; +//! use reth_engine_primitives::PayloadValidator; //! use reth_evm::{execute::BlockExecutorProvider, ConfigureEvm}; //! use reth_network_api::{NetworkInfo, Peers}; -//! use reth_primitives::TransactionSigned; +//! use reth_primitives::{Header, TransactionSigned}; //! use reth_provider::{AccountReader, CanonStateSubscriptions, ChangeSetReader, FullRpcProvider}; //! use reth_rpc::EthApi; //! use reth_rpc_builder::{ @@ -27,8 +27,18 @@ //! }; //! use reth_tasks::TokioTaskExecutor; //! use reth_transaction_pool::{PoolTransaction, TransactionPool}; +//! use std::sync::Arc; //! -//! pub async fn launch( +//! pub async fn launch< +//! Provider, +//! Pool, +//! Network, +//! Events, +//! EvmConfig, +//! BlockExecutor, +//! Consensus, +//! Validator, +//! >( //! provider: Provider, //! pool: Pool, //! network: Network, @@ -36,6 +46,7 @@ //! evm_config: EvmConfig, //! block_executor: BlockExecutor, //! consensus: Consensus, +//! validator: Validator, //! ) where //! Provider: FullRpcProvider< //! Transaction = TransactionSigned, @@ -53,6 +64,7 @@ //! EvmConfig: ConfigureEvm
, //! BlockExecutor: BlockExecutorProvider, //! Consensus: reth_consensus::FullConsensus + Clone + 'static, +//! Validator: PayloadValidator, //! { //! // configure the rpc module per transport //! let transports = TransportRpcModuleConfig::default().with_http(vec![ @@ -71,7 +83,7 @@ //! block_executor, //! consensus, //! ) -//! .build(transports, Box::new(EthApi::with_spawner)); +//! .build(transports, Box::new(EthApi::with_spawner), Arc::new(validator)); //! let handle = RpcServerConfig::default() //! .with_http(ServerBuilder::default()) //! .start(&transport_modules) @@ -83,11 +95,10 @@ //! //! //! ``` -//! use alloy_consensus::Header; -//! use reth_engine_primitives::EngineTypes; +//! use reth_engine_primitives::{EngineTypes, PayloadValidator}; //! use reth_evm::{execute::BlockExecutorProvider, ConfigureEvm}; //! use reth_network_api::{NetworkInfo, Peers}; -//! use reth_primitives::TransactionSigned; +//! use reth_primitives::{Header, TransactionSigned}; //! use reth_provider::{AccountReader, CanonStateSubscriptions, ChangeSetReader, FullRpcProvider}; //! use reth_rpc::EthApi; //! use reth_rpc_api::EngineApiServer; @@ -98,6 +109,7 @@ //! use reth_rpc_layer::JwtSecret; //! use reth_tasks::TokioTaskExecutor; //! use reth_transaction_pool::{PoolTransaction, TransactionPool}; +//! use std::sync::Arc; //! use tokio::try_join; //! //! pub async fn launch< @@ -110,6 +122,7 @@ //! EvmConfig, //! BlockExecutor, //! Consensus, +//! Validator, //! >( //! provider: Provider, //! pool: Pool, @@ -119,6 +132,7 @@ //! evm_config: EvmConfig, //! block_executor: BlockExecutor, //! consensus: Consensus, +//! validator: Validator, //! ) where //! Provider: FullRpcProvider< //! Transaction = TransactionSigned, @@ -138,6 +152,7 @@ //! EvmConfig: ConfigureEvm
, //! BlockExecutor: BlockExecutorProvider, //! Consensus: reth_consensus::FullConsensus + Clone + 'static, +//! Validator: PayloadValidator, //! { //! // configure the rpc module per transport //! let transports = TransportRpcModuleConfig::default().with_http(vec![ @@ -158,8 +173,12 @@ //! ); //! //! // configure the server modules -//! let (modules, auth_module, _registry) = -//! builder.build_with_auth_server(transports, engine_api, Box::new(EthApi::with_spawner)); +//! let (modules, auth_module, _registry) = builder.build_with_auth_server( +//! transports, +//! engine_api, +//! Box::new(EthApi::with_spawner), +//! Arc::new(validator), +//! ); //! //! // start the servers //! let auth_config = AuthServerConfig::builder(JwtSecret::random()).build(); @@ -187,7 +206,6 @@ use std::{ }; use crate::{auth::AuthRpcModule, error::WsHttpSamePortError, metrics::RpcRequestMetrics}; -use alloy_consensus::Header; use error::{ConflictingModules, RpcError, ServerKind}; use eth::DynEthApiBuilder; use http::{header::AUTHORIZATION, HeaderMap}; @@ -201,7 +219,7 @@ use jsonrpsee::{ }; use reth_chainspec::EthereumHardforks; use reth_consensus::FullConsensus; -use reth_engine_primitives::EngineTypes; +use reth_engine_primitives::{EngineTypes, PayloadValidator}; use reth_evm::{execute::BlockExecutorProvider, ConfigureEvm}; use reth_network_api::{noop::NoopNetwork, NetworkInfo, Peers}; use reth_primitives::NodePrimitives; @@ -274,6 +292,7 @@ pub async fn launch, block_executor: BlockExecutor, consensus: Arc>, + payload_validator: Arc>, ) -> Result where Provider: FullRpcProvider< @@ -297,12 +316,7 @@ where Header = ::BlockHeader, >, >, - BlockExecutor: BlockExecutorProvider< - Primitives: NodePrimitives< - BlockHeader = reth_primitives::Header, - BlockBody = reth_primitives::BlockBody, - >, - >, + BlockExecutor: BlockExecutorProvider, { let module_config = module_config.into(); server_config @@ -318,7 +332,7 @@ where block_executor, consensus, ) - .build(module_config, eth), + .build(module_config, eth, payload_validator), ) .await } @@ -651,6 +665,7 @@ where Provider: FullRpcProvider< Block = ::Block, Receipt = ::Receipt, + Header = ::BlockHeader, > + AccountReader + ChangeSetReader, Pool: TransactionPool + 'static, @@ -661,12 +676,7 @@ where Header = ::BlockHeader, Transaction = ::SignedTx, >, - BlockExecutor: BlockExecutorProvider< - Primitives: NodePrimitives< - BlockHeader = reth_primitives::Header, - BlockBody = reth_primitives::BlockBody, - >, - >, + BlockExecutor: BlockExecutorProvider, Consensus: reth_consensus::FullConsensus + Clone + 'static, { /// Configures all [`RpcModule`]s specific to the given [`TransportRpcModuleConfig`] which can @@ -681,6 +691,7 @@ where module_config: TransportRpcModuleConfig, engine: EngineApi, eth: DynEthApiBuilder, + payload_validator: Arc>, ) -> ( TransportRpcModules, AuthRpcModule, @@ -721,6 +732,7 @@ where evm_config, eth, block_executor, + payload_validator, ); let modules = registry.create_transport_rpc_modules(module_config); @@ -738,21 +750,24 @@ where /// # Example /// /// ```no_run - /// use alloy_consensus::Header; /// use reth_consensus::noop::NoopConsensus; + /// use reth_engine_primitives::PayloadValidator; /// use reth_evm::ConfigureEvm; /// use reth_evm_ethereum::execute::EthExecutorProvider; /// use reth_network_api::noop::NoopNetwork; - /// use reth_primitives::TransactionSigned; + /// use reth_primitives::{Header, TransactionSigned}; /// use reth_provider::test_utils::{NoopProvider, TestCanonStateSubscriptions}; /// use reth_rpc::EthApi; /// use reth_rpc_builder::RpcModuleBuilder; /// use reth_tasks::TokioTaskExecutor; /// use reth_transaction_pool::noop::NoopTransactionPool; + /// use std::sync::Arc; /// - /// fn init + 'static>( - /// evm: Evm, - /// ) { + /// fn init(evm: Evm, validator: Validator) + /// where + /// Evm: ConfigureEvm
+ 'static, + /// Validator: PayloadValidator + 'static, + /// { /// let mut registry = RpcModuleBuilder::default() /// .with_provider(NoopProvider::default()) /// .with_pool(NoopTransactionPool::default()) @@ -762,7 +777,7 @@ where /// .with_evm_config(evm) /// .with_block_executor(EthExecutorProvider::mainnet()) /// .with_consensus(NoopConsensus::default()) - /// .into_registry(Default::default(), Box::new(EthApi::with_spawner)); + /// .into_registry(Default::default(), Box::new(EthApi::with_spawner), Arc::new(validator)); /// /// let eth_api = registry.eth_api(); /// } @@ -771,6 +786,7 @@ where self, config: RpcModuleConfig, eth: DynEthApiBuilder, + payload_validator: Arc>, ) -> RpcRegistryInner where EthApi: EthApiTypes + 'static, @@ -796,6 +812,7 @@ where evm_config, eth, block_executor, + payload_validator, ) } @@ -805,6 +822,7 @@ where self, module_config: TransportRpcModuleConfig, eth: DynEthApiBuilder, + payload_validator: Arc>, ) -> TransportRpcModules<()> where EthApi: FullEthApiServer< @@ -843,6 +861,7 @@ where evm_config, eth, block_executor, + payload_validator, ); modules.config = module_config; @@ -957,6 +976,7 @@ pub struct RpcRegistryInner< events: Events, block_executor: BlockExecutor, consensus: Consensus, + payload_validator: Arc>, /// Holds the configuration for the RPC modules config: RpcModuleConfig, /// Holds a all `eth_` namespace handlers @@ -1008,9 +1028,10 @@ where EthApi, >, block_executor: BlockExecutor, + payload_validator: Arc>, ) -> Self where - EvmConfig: ConfigureEvm
, + EvmConfig: ConfigureEvm
, { let blocking_pool_guard = BlockingTaskGuard::new(config.eth.max_tracing_requests); @@ -1037,6 +1058,7 @@ where blocking_pool_guard, events, block_executor, + payload_validator, } } } @@ -1320,6 +1342,7 @@ where pub fn validation_api(&self) -> ValidationApi where Consensus: reth_consensus::FullConsensus + Clone + 'static, + Provider: BlockReader::Block>, { ValidationApi::new( self.provider.clone(), @@ -1327,6 +1350,7 @@ where self.block_executor.clone(), self.config.flashbots.clone(), Box::new(self.executor.clone()), + self.payload_validator.clone(), ) } } @@ -1334,7 +1358,9 @@ where impl RpcRegistryInner where - Provider: FullRpcProvider + AccountReader + ChangeSetReader, + Provider: FullRpcProvider::Block> + + AccountReader + + ChangeSetReader, Pool: TransactionPool + 'static, Network: NetworkInfo + Peers + Clone + 'static, Tasks: TaskSpawner + Clone + 'static, @@ -1346,12 +1372,7 @@ where Header = ::BlockHeader, >, >, - BlockExecutor: BlockExecutorProvider< - Primitives: NodePrimitives< - BlockHeader = reth_primitives::Header, - BlockBody = reth_primitives::BlockBody, - >, - >, + BlockExecutor: BlockExecutorProvider, Consensus: reth_consensus::FullConsensus + Clone + 'static, { /// Configures the auth module that includes the @@ -1500,6 +1521,7 @@ where self.block_executor.clone(), self.config.flashbots.clone(), Box::new(self.executor.clone()), + self.payload_validator.clone(), ) .into_rpc() .into(), diff --git a/crates/rpc/rpc-builder/tests/it/middleware.rs b/crates/rpc/rpc-builder/tests/it/middleware.rs index 96d818ed4..0e0bb80c0 100644 --- a/crates/rpc/rpc-builder/tests/it/middleware.rs +++ b/crates/rpc/rpc-builder/tests/it/middleware.rs @@ -5,6 +5,8 @@ use jsonrpsee::{ types::Request, MethodResponse, }; +use reth_chainspec::MAINNET; +use reth_ethereum_engine_primitives::EthereumEngineValidator; use reth_rpc::EthApi; use reth_rpc_builder::{RpcServerConfig, TransportRpcModuleConfig}; use reth_rpc_eth_api::EthApiClient; @@ -63,6 +65,7 @@ async fn test_rpc_middleware() { let modules = builder.build( TransportRpcModuleConfig::set_http(RpcModuleSelection::All), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); let mylayer = MyMiddlewareLayer::default(); diff --git a/crates/rpc/rpc-builder/tests/it/startup.rs b/crates/rpc/rpc-builder/tests/it/startup.rs index 9f6961fbb..ac53b0149 100644 --- a/crates/rpc/rpc-builder/tests/it/startup.rs +++ b/crates/rpc/rpc-builder/tests/it/startup.rs @@ -1,7 +1,9 @@ //! Startup tests -use std::io; +use std::{io, sync::Arc}; +use reth_chainspec::MAINNET; +use reth_ethereum_engine_primitives::EthereumEngineValidator; use reth_rpc::EthApi; use reth_rpc_builder::{ error::{RpcError, ServerKind, WsHttpSamePortError}, @@ -30,6 +32,7 @@ async fn test_http_addr_in_use() { let server = builder.build( TransportRpcModuleConfig::set_http(vec![RethRpcModule::Admin]), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); let result = RpcServerConfig::http(Default::default()).with_http_address(addr).start(&server).await; @@ -45,6 +48,7 @@ async fn test_ws_addr_in_use() { let server = builder.build( TransportRpcModuleConfig::set_ws(vec![RethRpcModule::Admin]), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); let result = RpcServerConfig::ws(Default::default()).with_ws_address(addr).start(&server).await; let err = result.unwrap_err(); @@ -66,6 +70,7 @@ async fn test_launch_same_port_different_modules() { TransportRpcModuleConfig::set_ws(vec![RethRpcModule::Admin]) .with_http(vec![RethRpcModule::Eth]), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); let addr = test_address(); let res = RpcServerConfig::ws(Default::default()) @@ -88,6 +93,7 @@ async fn test_launch_same_port_same_cors() { TransportRpcModuleConfig::set_ws(vec![RethRpcModule::Eth]) .with_http(vec![RethRpcModule::Eth]), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); let addr = test_address(); let res = RpcServerConfig::ws(Default::default()) @@ -108,6 +114,7 @@ async fn test_launch_same_port_different_cors() { TransportRpcModuleConfig::set_ws(vec![RethRpcModule::Eth]) .with_http(vec![RethRpcModule::Eth]), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); let addr = test_address(); let res = RpcServerConfig::ws(Default::default()) diff --git a/crates/rpc/rpc-builder/tests/it/utils.rs b/crates/rpc/rpc-builder/tests/it/utils.rs index 175992c0f..be708dac5 100644 --- a/crates/rpc/rpc-builder/tests/it/utils.rs +++ b/crates/rpc/rpc-builder/tests/it/utils.rs @@ -1,4 +1,7 @@ -use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use std::{ + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, + sync::Arc, +}; use alloy_rpc_types_engine::{ClientCode, ClientVersionV1}; use reth_beacon_consensus::BeaconConsensusEngineHandle; @@ -61,8 +64,11 @@ pub async fn launch_auth(secret: JwtSecret) -> AuthServerHandle { /// Launches a new server with http only with the given modules pub async fn launch_http(modules: impl Into) -> RpcServerHandle { let builder = test_rpc_builder(); - let server = - builder.build(TransportRpcModuleConfig::set_http(modules), Box::new(EthApi::with_spawner)); + let server = builder.build( + TransportRpcModuleConfig::set_http(modules), + Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), + ); RpcServerConfig::http(Default::default()) .with_http_address(test_address()) .start(&server) @@ -73,8 +79,11 @@ pub async fn launch_http(modules: impl Into) -> RpcServerHan /// Launches a new server with ws only with the given modules pub async fn launch_ws(modules: impl Into) -> RpcServerHandle { let builder = test_rpc_builder(); - let server = - builder.build(TransportRpcModuleConfig::set_ws(modules), Box::new(EthApi::with_spawner)); + let server = builder.build( + TransportRpcModuleConfig::set_ws(modules), + Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), + ); RpcServerConfig::ws(Default::default()) .with_ws_address(test_address()) .start(&server) @@ -89,6 +98,7 @@ pub async fn launch_http_ws(modules: impl Into) -> RpcServer let server = builder.build( TransportRpcModuleConfig::set_ws(modules.clone()).with_http(modules), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); RpcServerConfig::ws(Default::default()) .with_ws_address(test_address()) @@ -107,6 +117,7 @@ pub async fn launch_http_ws_same_port(modules: impl Into) -> let server = builder.build( TransportRpcModuleConfig::set_ws(modules.clone()).with_http(modules), Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(MAINNET.clone())), ); let addr = test_address(); RpcServerConfig::ws(Default::default()) diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index 8b57cb1f1..2e80c105e 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -25,7 +25,7 @@ use reth_payload_primitives::{ validate_payload_timestamp, EngineApiMessageVersion, PayloadBuilderAttributes, PayloadOrAttributes, }; -use reth_primitives::{Block, EthereumHardfork}; +use reth_primitives::EthereumHardfork; use reth_rpc_api::EngineApiServer; use reth_rpc_types_compat::engine::payload::{ convert_payload_input_v2_to_payload, convert_to_payload_body_v1, @@ -80,11 +80,7 @@ struct EngineApiInner EngineApi where - Provider: HeaderProvider - + BlockReader - + StateProviderFactory - + EvmEnvProvider - + 'static, + Provider: HeaderProvider + BlockReader + StateProviderFactory + EvmEnvProvider + 'static, EngineT: EngineTypes, Pool: TransactionPool + 'static, Validator: EngineValidator, @@ -573,7 +569,7 @@ where f: F, ) -> EngineApiResult>> where - F: Fn(Block) -> R + Send + 'static, + F: Fn(Provider::Block) -> R + Send + 'static, R: Send + 'static, { let len = hashes.len() as u64; @@ -748,11 +744,7 @@ where impl EngineApiServer for EngineApi where - Provider: HeaderProvider - + BlockReader - + StateProviderFactory - + EvmEnvProvider - + 'static, + Provider: HeaderProvider + BlockReader + StateProviderFactory + EvmEnvProvider + 'static, EngineT: EngineTypes, Pool: TransactionPool + 'static, Validator: EngineValidator, @@ -1045,7 +1037,7 @@ mod tests { use reth_engine_primitives::BeaconEngineMessage; use reth_ethereum_engine_primitives::{EthEngineTypes, EthereumEngineValidator}; use reth_payload_builder::test_utils::spawn_test_payload_service; - use reth_primitives::SealedBlock; + use reth_primitives::{Block, SealedBlock}; use reth_provider::test_utils::MockEthProvider; use reth_rpc_types_compat::engine::payload::execution_payload_from_sealed_block; use reth_tasks::TokioTaskExecutor; @@ -1171,7 +1163,7 @@ mod tests { let expected = blocks .iter() .cloned() - .map(|b| Some(convert_to_payload_body_v1(b.unseal()))) + .map(|b| Some(convert_to_payload_body_v1(b.unseal::()))) .collect::>(); let res = api.get_payload_bodies_by_range_v1(start, count).await.unwrap(); @@ -1213,7 +1205,7 @@ mod tests { if first_missing_range.contains(&b.number) { None } else { - Some(convert_to_payload_body_v1(b.unseal())) + Some(convert_to_payload_body_v1(b.unseal::())) } }) .collect::>(); @@ -1232,7 +1224,7 @@ mod tests { { None } else { - Some(convert_to_payload_body_v1(b.unseal())) + Some(convert_to_payload_body_v1(b.unseal::())) } }) .collect::>(); diff --git a/crates/rpc/rpc-engine-api/tests/it/payload.rs b/crates/rpc/rpc-engine-api/tests/it/payload.rs index 78b0351d4..363c816d2 100644 --- a/crates/rpc/rpc-engine-api/tests/it/payload.rs +++ b/crates/rpc/rpc-engine-api/tests/it/payload.rs @@ -38,7 +38,7 @@ fn payload_body_roundtrip() { 0..=99, BlockRangeParams { tx_count: 0..2, ..Default::default() }, ) { - let unsealed = block.clone().unseal(); + let unsealed = block.clone().unseal::(); let payload_body: ExecutionPayloadBodyV1 = convert_to_payload_body_v1(unsealed); assert_eq!( diff --git a/crates/rpc/rpc-types-compat/src/engine/payload.rs b/crates/rpc/rpc-types-compat/src/engine/payload.rs index f504d57ad..3be7835a3 100644 --- a/crates/rpc/rpc-types-compat/src/engine/payload.rs +++ b/crates/rpc/rpc-types-compat/src/engine/payload.rs @@ -16,6 +16,7 @@ use reth_primitives::{ proofs::{self}, Block, BlockBody, BlockExt, SealedBlock, TransactionSigned, }; +use reth_primitives_traits::BlockBody as _; /// Converts [`ExecutionPayloadV1`] to [`Block`] pub fn try_payload_v1_to_block(payload: ExecutionPayloadV1) -> Result { @@ -320,15 +321,13 @@ pub fn validate_block_hash( } /// Converts [`Block`] to [`ExecutionPayloadBodyV1`] -pub fn convert_to_payload_body_v1(value: Block) -> ExecutionPayloadBodyV1 { - let transactions = value.body.transactions.into_iter().map(|tx| { - let mut out = Vec::new(); - tx.encode_2718(&mut out); - out.into() - }); +pub fn convert_to_payload_body_v1( + value: impl reth_primitives_traits::Block, +) -> ExecutionPayloadBodyV1 { + let transactions = value.body().transactions().iter().map(|tx| tx.encoded_2718().into()); ExecutionPayloadBodyV1 { transactions: transactions.collect(), - withdrawals: value.body.withdrawals.map(Withdrawals::into_inner), + withdrawals: value.body().withdrawals().cloned().map(Withdrawals::into_inner), } } diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 5efae46f0..14519860e 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -18,6 +18,7 @@ reth-primitives = { workspace = true, features = ["secp256k1"] } reth-primitives-traits.workspace = true reth-rpc-api.workspace = true reth-rpc-eth-api.workspace = true +reth-engine-primitives.workspace = true reth-errors.workspace = true reth-ethereum-consensus.workspace = true reth-provider.workspace = true @@ -35,7 +36,6 @@ reth-rpc-eth-types.workspace = true reth-rpc-server-types.workspace = true reth-network-types.workspace = true reth-consensus.workspace = true -reth-payload-validator.workspace = true # ethereum alloy-consensus.workspace = true diff --git a/crates/rpc/rpc/src/validation.rs b/crates/rpc/rpc/src/validation.rs index b13e99eb2..a7042126c 100644 --- a/crates/rpc/rpc/src/validation.rs +++ b/crates/rpc/rpc/src/validation.rs @@ -14,10 +14,10 @@ use async_trait::async_trait; use jsonrpsee::core::RpcResult; use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; use reth_consensus::{Consensus, FullConsensus, PostExecutionInput}; +use reth_engine_primitives::PayloadValidator; use reth_errors::{BlockExecutionError, ConsensusError, ProviderError}; use reth_ethereum_consensus::GAS_LIMIT_BOUND_DIVISOR; use reth_evm::execute::{BlockExecutorProvider, Executor}; -use reth_payload_validator::ExecutionPayloadValidator; use reth_primitives::{GotExpected, NodePrimitives, SealedBlockWithSenders, SealedHeader}; use reth_primitives_traits::{Block as _, BlockBody}; use reth_provider::{ @@ -34,14 +34,13 @@ use tokio::sync::{oneshot, RwLock}; /// The type that implements the `validation` rpc namespace trait #[derive(Clone, Debug, derive_more::Deref)] -pub struct ValidationApi { +pub struct ValidationApi { #[deref] inner: Arc>, } impl ValidationApi where - Provider: ChainSpecProvider, E: BlockExecutorProvider, { /// Create a new instance of the [`ValidationApi`] @@ -51,10 +50,12 @@ where executor_provider: E, config: ValidationApiConfig, task_spawner: Box, + payload_validator: Arc< + dyn PayloadValidator::Block>, + >, ) -> Self { let ValidationApiConfig { disallow } = config; - let payload_validator = ExecutionPayloadValidator::new(provider.chain_spec()); let inner = Arc::new(ValidationApiInner { provider, consensus, @@ -91,16 +92,11 @@ where impl ValidationApi where - Provider: BlockReaderIdExt
+ Provider: BlockReaderIdExt
::BlockHeader> + ChainSpecProvider + StateProviderFactory + 'static, - E: BlockExecutorProvider< - Primitives: NodePrimitives< - BlockHeader = Provider::Header, - BlockBody = reth_primitives::BlockBody, - >, - >, + E: BlockExecutorProvider, { /// Validates the given block and a [`BidTrace`] against it. pub async fn validate_message_against_block( @@ -116,8 +112,8 @@ where self.consensus.validate_block_pre_execution(&block)?; if !self.disallow.is_empty() { - if self.disallow.contains(&block.beneficiary) { - return Err(ValidationApiError::Blacklist(block.beneficiary)) + if self.disallow.contains(&block.beneficiary()) { + return Err(ValidationApiError::Blacklist(block.beneficiary())) } if self.disallow.contains(&message.proposer_fee_recipient) { return Err(ValidationApiError::Blacklist(message.proposer_fee_recipient)) @@ -137,9 +133,9 @@ where let latest_header = self.provider.latest_header()?.ok_or_else(|| ValidationApiError::MissingLatestBlock)?; - if latest_header.hash() != block.header.parent_hash { + if latest_header.hash() != block.header.parent_hash() { return Err(ConsensusError::ParentHashMismatch( - GotExpected { got: block.header.parent_hash, expected: latest_header.hash() } + GotExpected { got: block.header.parent_hash(), expected: latest_header.hash() } .into(), ) .into()) @@ -200,7 +196,7 @@ where /// Ensures that fields of [`BidTrace`] match the fields of the [`SealedHeader`]. fn validate_message_against_header( &self, - header: &SealedHeader, + header: &SealedHeader<::BlockHeader>, message: &BidTrace, ) -> Result<(), ValidationApiError> { if header.hash() != message.block_hash { @@ -208,20 +204,20 @@ where got: message.block_hash, expected: header.hash(), })) - } else if header.parent_hash != message.parent_hash { + } else if header.parent_hash() != message.parent_hash { Err(ValidationApiError::ParentHashMismatch(GotExpected { got: message.parent_hash, - expected: header.parent_hash, + expected: header.parent_hash(), })) - } else if header.gas_limit != message.gas_limit { + } else if header.gas_limit() != message.gas_limit { Err(ValidationApiError::GasLimitMismatch(GotExpected { got: message.gas_limit, - expected: header.gas_limit, + expected: header.gas_limit(), })) - } else if header.gas_used != message.gas_used { + } else if header.gas_used() != message.gas_used { return Err(ValidationApiError::GasUsedMismatch(GotExpected { got: message.gas_used, - expected: header.gas_used, + expected: header.gas_used(), })) } else { Ok(()) @@ -235,20 +231,20 @@ where fn validate_gas_limit( &self, registered_gas_limit: u64, - parent_header: &SealedHeader, - header: &SealedHeader, + parent_header: &SealedHeader<::BlockHeader>, + header: &SealedHeader<::BlockHeader>, ) -> Result<(), ValidationApiError> { let max_gas_limit = - parent_header.gas_limit + parent_header.gas_limit / GAS_LIMIT_BOUND_DIVISOR - 1; + parent_header.gas_limit() + parent_header.gas_limit() / GAS_LIMIT_BOUND_DIVISOR - 1; let min_gas_limit = - parent_header.gas_limit - parent_header.gas_limit / GAS_LIMIT_BOUND_DIVISOR + 1; + parent_header.gas_limit() - parent_header.gas_limit() / GAS_LIMIT_BOUND_DIVISOR + 1; let best_gas_limit = std::cmp::max(min_gas_limit, std::cmp::min(max_gas_limit, registered_gas_limit)); - if best_gas_limit != header.gas_limit { + if best_gas_limit != header.gas_limit() { return Err(ValidationApiError::GasLimitMismatch(GotExpected { - got: header.gas_limit, + got: header.gas_limit(), expected: best_gas_limit, })) } @@ -409,17 +405,12 @@ where #[async_trait] impl BlockSubmissionValidationApiServer for ValidationApi where - Provider: BlockReaderIdExt
+ Provider: BlockReaderIdExt
::BlockHeader> + ChainSpecProvider + StateProviderFactory + Clone + 'static, - E: BlockExecutorProvider< - Primitives: NodePrimitives< - BlockHeader = Provider::Header, - BlockBody = reth_primitives::BlockBody, - >, - >, + E: BlockExecutorProvider, { async fn validate_builder_submission_v1( &self, @@ -473,13 +464,13 @@ where } #[derive(Debug)] -pub struct ValidationApiInner { +pub struct ValidationApiInner { /// The provider that can interact with the chain. provider: Provider, /// Consensus implementation. consensus: Arc>, /// Execution payload validator. - payload_validator: ExecutionPayloadValidator, + payload_validator: Arc::Block>>, /// Block executor factory. executor_provider: E, /// Set of disallowed addresses diff --git a/examples/custom-engine-types/src/main.rs b/examples/custom-engine-types/src/main.rs index 1034effeb..f30956d8f 100644 --- a/examples/custom-engine-types/src/main.rs +++ b/examples/custom-engine-types/src/main.rs @@ -55,7 +55,7 @@ use reth_chainspec::{Chain, ChainSpec, ChainSpecProvider}; use reth_node_api::{ payload::{EngineApiMessageVersion, EngineObjectValidationError, PayloadOrAttributes}, validate_version_specific_fields, AddOnsContext, EngineTypes, EngineValidator, - FullNodeComponents, PayloadAttributes, PayloadBuilderAttributes, + FullNodeComponents, PayloadAttributes, PayloadBuilderAttributes, PayloadValidator, }; use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; use reth_node_ethereum::{ @@ -189,12 +189,22 @@ impl CustomEngineValidator { } } +impl PayloadValidator for CustomEngineValidator { + type Block = Block; + + fn ensure_well_formed_payload( + &self, + payload: ExecutionPayload, + sidecar: ExecutionPayloadSidecar, + ) -> Result, PayloadError> { + self.inner.ensure_well_formed_payload(payload, sidecar) + } +} + impl EngineValidator for CustomEngineValidator where T: EngineTypes, { - type Block = Block; - fn validate_version_specific_fields( &self, version: EngineApiMessageVersion, @@ -220,14 +230,6 @@ where Ok(()) } - fn ensure_well_formed_payload( - &self, - payload: ExecutionPayload, - sidecar: ExecutionPayloadSidecar, - ) -> Result, PayloadError> { - self.inner.ensure_well_formed_payload(payload, sidecar) - } - fn validate_payload_attributes_against_header( &self, _attr: &::PayloadAttributes, @@ -246,7 +248,11 @@ pub struct CustomEngineValidatorBuilder; impl EngineValidatorBuilder for CustomEngineValidatorBuilder where N: FullNodeComponents< - Types: NodeTypesWithEngine, + Types: NodeTypesWithEngine< + Engine = CustomEngineTypes, + ChainSpec = ChainSpec, + Primitives = EthPrimitives, + >, >, { type Validator = CustomEngineValidator; diff --git a/examples/rpc-db/src/main.rs b/examples/rpc-db/src/main.rs index 92ae86f00..cde891036 100644 --- a/examples/rpc-db/src/main.rs +++ b/examples/rpc-db/src/main.rs @@ -34,7 +34,9 @@ use reth::rpc::builder::{ // Configuring the network parts, ideally also wouldn't need to think about this. use myrpc_ext::{MyRpcExt, MyRpcExtApiServer}; use reth::{blockchain_tree::noop::NoopBlockchainTree, tasks::TokioTaskExecutor}; -use reth_node_ethereum::{EthEvmConfig, EthExecutorProvider, EthereumNode}; +use reth_node_ethereum::{ + node::EthereumEngineValidator, EthEvmConfig, EthExecutorProvider, EthereumNode, +}; use reth_provider::{test_utils::TestCanonStateSubscriptions, ChainSpecProvider}; // Custom rpc extension @@ -70,11 +72,15 @@ async fn main() -> eyre::Result<()> { .with_evm_config(EthEvmConfig::new(spec.clone())) .with_events(TestCanonStateSubscriptions::default()) .with_block_executor(EthExecutorProvider::ethereum(provider.chain_spec())) - .with_consensus(EthBeaconConsensus::new(spec)); + .with_consensus(EthBeaconConsensus::new(spec.clone())); // Pick which namespaces to expose. let config = TransportRpcModuleConfig::default().with_http([RethRpcModule::Eth]); - let mut server = rpc_builder.build(config, Box::new(EthApi::with_spawner)); + let mut server = rpc_builder.build( + config, + Box::new(EthApi::with_spawner), + Arc::new(EthereumEngineValidator::new(spec)), + ); // Add a custom rpc namespace let custom_rpc = MyRpcExt { provider };