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