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