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