reth_node_ethereum/
node.rs

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