Skip to main content

reth_node_ethereum/
node.rs

1//! Ethereum Node types config.
2
3pub use crate::{payload::EthereumPayloadBuilder, EthereumEngineValidator};
4use crate::{EthEngineTypes, EthEvmConfig};
5use alloy_eips::{eip7840::BlobParams, merge::EPOCH_SLOTS};
6use alloy_network::Ethereum;
7use alloy_rpc_types_engine::ExecutionData;
8use reth_chainspec::{ChainSpec, EthChainSpec, EthereumHardforks, Hardforks};
9use reth_engine_local::LocalPayloadAttributesBuilder;
10use reth_engine_primitives::EngineTypes;
11use reth_ethereum_consensus::EthBeaconConsensus;
12use reth_ethereum_engine_primitives::{EthBuiltPayload, EthPayloadAttributes};
13use reth_ethereum_primitives::{EthPrimitives, TransactionSigned};
14use reth_evm::{
15    eth::spec::EthExecutorSpec, ConfigureEvm, EvmFactory, EvmFactoryFor, NextBlockEnvAttributes,
16};
17use reth_network::{primitives::BasicNetworkPrimitives, NetworkHandle, PeersInfo};
18use reth_node_api::{
19    AddOnsContext, FullNodeComponents, HeaderTy, NodeAddOns, NodePrimitives,
20    PayloadAttributesBuilder, PrimitivesTy, TxTy,
21};
22use reth_node_builder::{
23    components::{
24        BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
25        NetworkBuilder, PoolBuilder, TxPoolBuilder,
26    },
27    node::{FullNodeTypes, NodeTypes},
28    rpc::{
29        BasicEngineApiBuilder, BasicEngineValidatorBuilder, EngineApiBuilder, EngineValidatorAddOn,
30        EngineValidatorBuilder, EthApiBuilder, EthApiCtx, Identity, PayloadValidatorBuilder,
31        RethRpcAddOns, RpcAddOns, RpcHandle,
32    },
33    BuilderContext, DebugNode, Node, NodeAdapter,
34};
35use reth_payload_primitives::PayloadTypes;
36use reth_provider::{providers::ProviderFactoryBuilder, EthStorage};
37use reth_rpc::{
38    eth::core::{EthApiFor, EthRpcConverterFor},
39    TestingApi, ValidationApi,
40};
41use reth_rpc_api::servers::{BlockSubmissionValidationApiServer, TestingApiServer};
42use reth_rpc_builder::{config::RethRpcServerConfig, middleware::RethRpcMiddleware};
43use reth_rpc_eth_api::{
44    helpers::{
45        config::{EthConfigApiServer, EthConfigHandler},
46        pending_block::BuildPendingEnv,
47    },
48    RpcConvert, RpcTypes, SignableTxRequest,
49};
50use reth_rpc_eth_types::{error::FromEvmError, EthApiError};
51use reth_rpc_server_types::RethRpcModule;
52use reth_tracing::tracing::{debug, info};
53use reth_transaction_pool::{
54    blobstore::DiskFileBlobStore, EthTransactionPool, PoolPooledTx, PoolTransaction,
55    TransactionPool, TransactionValidationTaskExecutor,
56};
57use revm::context::TxEnv;
58use std::{marker::PhantomData, sync::Arc, time::SystemTime};
59
60/// Type configuration for a regular Ethereum node.
61#[derive(Debug, Default, Clone, Copy)]
62#[non_exhaustive]
63pub struct EthereumNode;
64
65impl EthereumNode {
66    /// Returns a [`ComponentsBuilder`] configured for a regular Ethereum node.
67    pub fn components<Node>() -> ComponentsBuilder<
68        Node,
69        EthereumPoolBuilder,
70        BasicPayloadServiceBuilder<EthereumPayloadBuilder>,
71        EthereumNetworkBuilder,
72        EthereumExecutorBuilder,
73        EthereumConsensusBuilder,
74    >
75    where
76        Node: FullNodeTypes<
77            Types: NodeTypes<
78                ChainSpec: Hardforks + EthereumHardforks + EthExecutorSpec,
79                Primitives = EthPrimitives,
80            >,
81        >,
82        <Node::Types as NodeTypes>::Payload:
83            PayloadTypes<BuiltPayload = EthBuiltPayload, PayloadAttributes = EthPayloadAttributes>,
84    {
85        ComponentsBuilder::default()
86            .node_types::<Node>()
87            .pool(EthereumPoolBuilder::default())
88            .executor(EthereumExecutorBuilder::default())
89            .payload(BasicPayloadServiceBuilder::default())
90            .network(EthereumNetworkBuilder::default())
91            .consensus(EthereumConsensusBuilder::default())
92    }
93
94    /// Instantiates the [`ProviderFactoryBuilder`] for an ethereum node.
95    ///
96    /// # Open a Providerfactory in read-only mode from a datadir
97    ///
98    /// See also: [`ProviderFactoryBuilder`] and
99    /// [`ReadOnlyConfig`](reth_provider::providers::ReadOnlyConfig).
100    ///
101    /// ```no_run
102    /// use reth_chainspec::MAINNET;
103    /// use reth_node_ethereum::EthereumNode;
104    ///
105    /// fn demo(runtime: reth_tasks::Runtime) {
106    ///     let factory = EthereumNode::provider_factory_builder()
107    ///         .open_read_only(MAINNET.clone(), "datadir", runtime)
108    ///         .unwrap();
109    /// }
110    /// ```
111    ///
112    /// See also [`ProviderFactory::new`](reth_provider::ProviderFactory::new) for constructing
113    /// a [`ProviderFactory`](reth_provider::ProviderFactory) manually with all required
114    /// components.
115    pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
116        ProviderFactoryBuilder::default()
117    }
118}
119
120impl NodeTypes for EthereumNode {
121    type Primitives = EthPrimitives;
122    type ChainSpec = ChainSpec;
123    type Storage = EthStorage;
124    type Payload = EthEngineTypes;
125}
126
127/// Builds [`EthApi`](reth_rpc::EthApi) for Ethereum.
128#[derive(Debug)]
129pub struct EthereumEthApiBuilder<NetworkT = Ethereum>(PhantomData<NetworkT>);
130
131impl<NetworkT> Default for EthereumEthApiBuilder<NetworkT> {
132    fn default() -> Self {
133        Self(Default::default())
134    }
135}
136
137impl<N, NetworkT> EthApiBuilder<N> for EthereumEthApiBuilder<NetworkT>
138where
139    N: FullNodeComponents<
140        Types: NodeTypes<ChainSpec: Hardforks + EthereumHardforks>,
141        Evm: ConfigureEvm<NextBlockEnvCtx: BuildPendingEnv<HeaderTy<N::Types>>>,
142    >,
143    NetworkT: RpcTypes<TransactionRequest: SignableTxRequest<TxTy<N::Types>>>,
144    EthRpcConverterFor<N, NetworkT>: RpcConvert<
145        Primitives = PrimitivesTy<N::Types>,
146        Error = EthApiError,
147        Network = NetworkT,
148        Evm = N::Evm,
149    >,
150    EthApiError: FromEvmError<N::Evm>,
151{
152    type EthApi = EthApiFor<N, NetworkT>;
153
154    async fn build_eth_api(self, ctx: EthApiCtx<'_, N>) -> eyre::Result<Self::EthApi> {
155        Ok(ctx.eth_api_builder().map_converter(|r| r.with_network()).build())
156    }
157}
158
159/// Add-ons w.r.t. l1 ethereum.
160#[derive(Debug)]
161pub struct EthereumAddOns<
162    N: FullNodeComponents,
163    EthB: EthApiBuilder<N>,
164    PVB,
165    EB = BasicEngineApiBuilder<PVB>,
166    EVB = BasicEngineValidatorBuilder<PVB>,
167    RpcMiddleware = Identity,
168> {
169    inner: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
170}
171
172impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
173where
174    N: FullNodeComponents,
175    EthB: EthApiBuilder<N>,
176{
177    /// Creates a new instance from the inner `RpcAddOns`.
178    pub const fn new(inner: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>) -> Self {
179        Self { inner }
180    }
181}
182
183impl<N> Default for EthereumAddOns<N, EthereumEthApiBuilder, EthereumEngineValidatorBuilder>
184where
185    N: FullNodeComponents<
186        Types: NodeTypes<
187            ChainSpec: EthereumHardforks + Clone + 'static,
188            Payload: EngineTypes<ExecutionData = ExecutionData>
189                         + PayloadTypes<PayloadAttributes = EthPayloadAttributes>,
190            Primitives = EthPrimitives,
191        >,
192    >,
193    EthereumEthApiBuilder: EthApiBuilder<N>,
194{
195    fn default() -> Self {
196        Self::new(RpcAddOns::new(
197            EthereumEthApiBuilder::default(),
198            EthereumEngineValidatorBuilder::default(),
199            BasicEngineApiBuilder::default(),
200            BasicEngineValidatorBuilder::default(),
201            Default::default(),
202        ))
203    }
204}
205
206impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
207where
208    N: FullNodeComponents,
209    EthB: EthApiBuilder<N>,
210{
211    /// Replace the engine API builder.
212    pub fn with_engine_api<T>(
213        self,
214        engine_api_builder: T,
215    ) -> EthereumAddOns<N, EthB, PVB, T, EVB, RpcMiddleware>
216    where
217        T: Send,
218    {
219        let Self { inner } = self;
220        EthereumAddOns::new(inner.with_engine_api(engine_api_builder))
221    }
222
223    /// Replace the payload validator builder.
224    pub fn with_payload_validator<V, T>(
225        self,
226        payload_validator_builder: T,
227    ) -> EthereumAddOns<N, EthB, T, EB, EVB, RpcMiddleware> {
228        let Self { inner } = self;
229        EthereumAddOns::new(inner.with_payload_validator(payload_validator_builder))
230    }
231
232    /// Sets rpc middleware
233    pub fn with_rpc_middleware<T>(
234        self,
235        rpc_middleware: T,
236    ) -> EthereumAddOns<N, EthB, PVB, EB, EVB, T>
237    where
238        T: Send,
239    {
240        let Self { inner } = self;
241        EthereumAddOns::new(inner.with_rpc_middleware(rpc_middleware))
242    }
243
244    /// Sets the tokio runtime for the RPC servers.
245    ///
246    /// Caution: This runtime must not be created from within asynchronous context.
247    pub fn with_tokio_runtime(self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
248        let Self { inner } = self;
249        Self { inner: inner.with_tokio_runtime(tokio_runtime) }
250    }
251}
252
253impl<N, EthB, PVB, EB, EVB, RpcMiddleware> NodeAddOns<N>
254    for EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
255where
256    N: FullNodeComponents<
257        Types: NodeTypes<
258            ChainSpec: Hardforks + EthereumHardforks,
259            Primitives = EthPrimitives,
260            Payload: EngineTypes<ExecutionData = ExecutionData>,
261        >,
262        Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes>,
263    >,
264    EthB: EthApiBuilder<N>,
265    PVB: Send,
266    EB: EngineApiBuilder<N>,
267    EVB: EngineValidatorBuilder<N>,
268    EthApiError: FromEvmError<N::Evm>,
269    EvmFactoryFor<N::Evm>: EvmFactory<Tx = TxEnv>,
270    RpcMiddleware: RethRpcMiddleware,
271{
272    type Handle = RpcHandle<N, EthB::EthApi>;
273
274    async fn launch_add_ons(
275        self,
276        ctx: reth_node_api::AddOnsContext<'_, N>,
277    ) -> eyre::Result<Self::Handle> {
278        let validation_api = ValidationApi::<_, _, <N::Types as NodeTypes>::Payload>::new(
279            ctx.node.provider().clone(),
280            Arc::new(ctx.node.consensus().clone()),
281            ctx.node.evm_config().clone(),
282            ctx.config.rpc.flashbots_config(),
283            ctx.node.task_executor().clone(),
284            Arc::new(EthereumEngineValidator::new(ctx.config.chain.clone())),
285        );
286
287        let eth_config =
288            EthConfigHandler::new(ctx.node.provider().clone(), ctx.node.evm_config().clone());
289
290        let testing_skip_invalid_transactions = ctx.config.rpc.testing_skip_invalid_transactions;
291        let testing_gas_limit_override = ctx.config.rpc.testing_gas_limit;
292
293        self.inner
294            .launch_add_ons_with(ctx, move |container| {
295                container.modules.merge_if_module_configured(
296                    RethRpcModule::Flashbots,
297                    validation_api.into_rpc(),
298                )?;
299
300                container
301                    .modules
302                    .merge_if_module_configured(RethRpcModule::Eth, eth_config.into_rpc())?;
303
304                // testing_buildBlockV1: only wire when the hidden testing module is explicitly
305                // requested on any transport. Default stays disabled to honor security guidance.
306                let mut testing_api = TestingApi::new(
307                    container.registry.eth_api().clone(),
308                    container.registry.evm_config().clone(),
309                );
310                if testing_skip_invalid_transactions {
311                    testing_api = testing_api.with_skip_invalid_transactions();
312                }
313                if let Some(gas_limit) = testing_gas_limit_override {
314                    testing_api = testing_api.with_gas_limit_override(gas_limit);
315                }
316                container
317                    .modules
318                    .merge_if_module_configured(RethRpcModule::Testing, testing_api.into_rpc())?;
319
320                Ok(())
321            })
322            .await
323    }
324}
325
326impl<N, EthB, PVB, EB, EVB, RpcMiddleware> RethRpcAddOns<N>
327    for EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
328where
329    N: FullNodeComponents<
330        Types: NodeTypes<
331            ChainSpec: Hardforks + EthereumHardforks,
332            Primitives = EthPrimitives,
333            Payload: EngineTypes<ExecutionData = ExecutionData>,
334        >,
335        Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes>,
336    >,
337    EthB: EthApiBuilder<N>,
338    PVB: PayloadValidatorBuilder<N>,
339    EB: EngineApiBuilder<N>,
340    EVB: EngineValidatorBuilder<N>,
341    EthApiError: FromEvmError<N::Evm>,
342    EvmFactoryFor<N::Evm>: EvmFactory<Tx = TxEnv>,
343    RpcMiddleware: RethRpcMiddleware,
344{
345    type EthApi = EthB::EthApi;
346
347    fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
348        self.inner.hooks_mut()
349    }
350}
351
352impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EngineValidatorAddOn<N>
353    for EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
354where
355    N: FullNodeComponents<
356        Types: NodeTypes<
357            ChainSpec: EthChainSpec + EthereumHardforks,
358            Primitives = EthPrimitives,
359            Payload: EngineTypes<ExecutionData = ExecutionData>,
360        >,
361        Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes>,
362    >,
363    EthB: EthApiBuilder<N>,
364    PVB: Send,
365    EB: EngineApiBuilder<N>,
366    EVB: EngineValidatorBuilder<N>,
367    EthApiError: FromEvmError<N::Evm>,
368    EvmFactoryFor<N::Evm>: EvmFactory<Tx = TxEnv>,
369    RpcMiddleware: Send,
370{
371    type ValidatorBuilder = EVB;
372
373    fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
374        self.inner.engine_validator_builder()
375    }
376}
377
378impl<N> Node<N> for EthereumNode
379where
380    N: FullNodeTypes<Types = Self>,
381{
382    type ComponentsBuilder = ComponentsBuilder<
383        N,
384        EthereumPoolBuilder,
385        BasicPayloadServiceBuilder<EthereumPayloadBuilder>,
386        EthereumNetworkBuilder,
387        EthereumExecutorBuilder,
388        EthereumConsensusBuilder,
389    >;
390
391    type AddOns =
392        EthereumAddOns<NodeAdapter<N>, EthereumEthApiBuilder, EthereumEngineValidatorBuilder>;
393
394    fn components_builder(&self) -> Self::ComponentsBuilder {
395        Self::components()
396    }
397
398    fn add_ons(&self) -> Self::AddOns {
399        EthereumAddOns::default()
400    }
401}
402
403impl<N: FullNodeComponents<Types = Self>> DebugNode<N> for EthereumNode {
404    type RpcBlock = alloy_rpc_types_eth::Block;
405
406    fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_ethereum_primitives::Block {
407        rpc_block.into_consensus().convert_transactions()
408    }
409
410    fn local_payload_attributes_builder(
411        chain_spec: &Self::ChainSpec,
412    ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes> {
413        LocalPayloadAttributesBuilder::new(Arc::new(chain_spec.clone()))
414    }
415}
416
417/// A regular ethereum evm and executor builder.
418#[derive(Debug, Default, Clone, Copy)]
419#[non_exhaustive]
420pub struct EthereumExecutorBuilder;
421
422impl<Types, Node> ExecutorBuilder<Node> for EthereumExecutorBuilder
423where
424    Types: NodeTypes<
425        ChainSpec: Hardforks + EthExecutorSpec + EthereumHardforks,
426        Primitives = EthPrimitives,
427    >,
428    Node: FullNodeTypes<Types = Types>,
429{
430    type EVM = EthEvmConfig<Types::ChainSpec>;
431
432    async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
433        Ok(EthEvmConfig::new(ctx.chain_spec()))
434    }
435}
436
437/// A basic ethereum transaction pool.
438///
439/// This contains various settings that can be configured and take precedence over the node's
440/// config.
441#[derive(Debug, Default, Clone, Copy)]
442#[non_exhaustive]
443pub struct EthereumPoolBuilder {
444    // TODO add options for txpool args
445}
446
447impl<Types, Node, Evm> PoolBuilder<Node, Evm> for EthereumPoolBuilder
448where
449    Types: NodeTypes<
450        ChainSpec: EthereumHardforks,
451        Primitives: NodePrimitives<SignedTx = TransactionSigned>,
452    >,
453    Node: FullNodeTypes<Types = Types>,
454    Evm: ConfigureEvm<Primitives = PrimitivesTy<Types>> + Clone + 'static,
455{
456    type Pool = EthTransactionPool<Node::Provider, DiskFileBlobStore, Evm>;
457
458    async fn build_pool(
459        self,
460        ctx: &BuilderContext<Node>,
461        evm_config: Evm,
462    ) -> eyre::Result<Self::Pool> {
463        let pool_config = ctx.pool_config();
464
465        let blobs_disabled = ctx.config().txpool.disable_blobs_support ||
466            ctx.config().txpool.blobpool_max_count == 0;
467
468        let blob_cache_size = if let Some(blob_cache_size) = pool_config.blob_cache_size {
469            Some(blob_cache_size)
470        } else {
471            // get the current blob params for the current timestamp, fallback to default Cancun
472            // params
473            let current_timestamp =
474                SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_secs();
475            let blob_params = ctx
476                .chain_spec()
477                .blob_params_at_timestamp(current_timestamp)
478                .unwrap_or_else(BlobParams::cancun);
479
480            // Derive the blob cache size from the target blob count, to auto scale it by
481            // multiplying it with the slot count for 2 epochs: 384 for pectra
482            Some((blob_params.target_blob_count * EPOCH_SLOTS * 2) as u32)
483        };
484
485        let blob_store =
486            reth_node_builder::components::create_blob_store_with_cache(ctx, blob_cache_size)?;
487
488        let validator =
489            TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config)
490                .set_eip4844(!blobs_disabled)
491                .kzg_settings(ctx.kzg_settings()?)
492                .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
493                .with_local_transactions_config(pool_config.local_transactions_config.clone())
494                .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
495                .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
496                .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
497                .with_additional_tasks(ctx.config().txpool.additional_validation_tasks)
498                .build_with_tasks(ctx.task_executor().clone(), blob_store.clone());
499
500        if validator.validator().eip4844() {
501            // initializing the KZG settings can be expensive, this should be done upfront so that
502            // it doesn't impact the first block or the first gossiped blob transaction, so we
503            // initialize this in the background
504            let kzg_settings = validator.validator().kzg_settings().clone();
505            ctx.task_executor().spawn_blocking_task(async move {
506                let _ = kzg_settings.get();
507                debug!(target: "reth::cli", "Initialized KZG settings");
508            });
509        }
510
511        let transaction_pool = TxPoolBuilder::new(ctx)
512            .with_validator(validator)
513            .build_and_spawn_maintenance_task(blob_store, pool_config)?;
514
515        info!(target: "reth::cli", "Transaction pool initialized");
516        debug!(target: "reth::cli", "Spawned txpool maintenance task");
517
518        Ok(transaction_pool)
519    }
520}
521
522/// A basic ethereum payload service.
523#[derive(Debug, Default, Clone, Copy)]
524pub struct EthereumNetworkBuilder {
525    // TODO add closure to modify network
526}
527
528impl<Node, Pool> NetworkBuilder<Node, Pool> for EthereumNetworkBuilder
529where
530    Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
531    Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
532        + Unpin
533        + 'static,
534{
535    type Network =
536        NetworkHandle<BasicNetworkPrimitives<PrimitivesTy<Node::Types>, PoolPooledTx<Pool>>>;
537
538    async fn build_network(
539        self,
540        ctx: &BuilderContext<Node>,
541        pool: Pool,
542    ) -> eyre::Result<Self::Network> {
543        let network = ctx.network_builder().await?;
544        let handle = ctx.start_network(network, pool);
545        info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized");
546        Ok(handle)
547    }
548}
549
550/// A basic ethereum consensus builder.
551#[derive(Debug, Default, Clone, Copy)]
552pub struct EthereumConsensusBuilder {
553    // TODO add closure to modify consensus
554}
555
556impl<Node> ConsensusBuilder<Node> for EthereumConsensusBuilder
557where
558    Node: FullNodeTypes<
559        Types: NodeTypes<ChainSpec: EthChainSpec + EthereumHardforks, Primitives = EthPrimitives>,
560    >,
561{
562    type Consensus = Arc<EthBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
563
564    async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
565        Ok(Arc::new(
566            EthBeaconConsensus::new(ctx.chain_spec())
567                .with_skip_gas_limit_ramp_check(ctx.config().rpc.testing_skip_gas_limit_ramp_check),
568        ))
569    }
570}
571
572/// Builder for [`EthereumEngineValidator`].
573#[derive(Debug, Default, Clone)]
574#[non_exhaustive]
575pub struct EthereumEngineValidatorBuilder;
576
577impl<Node, Types> PayloadValidatorBuilder<Node> for EthereumEngineValidatorBuilder
578where
579    Types: NodeTypes<
580        ChainSpec: Hardforks + EthereumHardforks + Clone + 'static,
581        Payload: EngineTypes<ExecutionData = ExecutionData>
582                     + PayloadTypes<PayloadAttributes = EthPayloadAttributes>,
583        Primitives = EthPrimitives,
584    >,
585    Node: FullNodeComponents<Types = Types>,
586{
587    type Validator = EthereumEngineValidator<Types::ChainSpec>;
588
589    async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
590        Ok(EthereumEngineValidator::new(ctx.config.chain.clone()))
591    }
592}