//! Ethereum Node types config. pub use crate::{payload::EthereumPayloadBuilder, EthereumEngineValidator}; use crate::{EthEngineTypes, EthEvmConfig}; use alloy_eips::{eip7840::BlobParams, merge::EPOCH_SLOTS}; use reth_chainspec::{ChainSpec, EthChainSpec, EthereumHardforks}; use reth_consensus::{ConsensusError, FullConsensus}; use reth_ethereum_consensus::EthBeaconConsensus; use reth_ethereum_engine_primitives::{ EthBuiltPayload, EthPayloadAttributes, EthPayloadBuilderAttributes, }; use reth_ethereum_primitives::{EthPrimitives, PooledTransactionVariant, TransactionSigned}; use reth_evm::{ConfigureEvm, EvmFactory, EvmFactoryFor, NextBlockEnvAttributes}; use reth_network::{EthNetworkPrimitives, NetworkHandle, PeersInfo}; use reth_node_api::{AddOnsContext, FullNodeComponents, NodeAddOns, NodePrimitives, TxTy}; use reth_node_builder::{ components::{ BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder, NetworkBuilder, PoolBuilder, }, node::{FullNodeTypes, NodeTypes}, rpc::{ EngineValidatorAddOn, EngineValidatorBuilder, EthApiBuilder, EthApiCtx, RethRpcAddOns, RpcAddOns, RpcHandle, }, BuilderContext, DebugNode, Node, NodeAdapter, NodeComponentsBuilder, PayloadBuilderConfig, PayloadTypes, }; use reth_provider::{providers::ProviderFactoryBuilder, CanonStateSubscriptions, EthStorage}; use reth_rpc::{eth::core::EthApiFor, ValidationApi}; use reth_rpc_api::{eth::FullEthApiServer, servers::BlockSubmissionValidationApiServer}; use reth_rpc_builder::config::RethRpcServerConfig; use reth_rpc_eth_types::{error::FromEvmError, EthApiError}; use reth_rpc_server_types::RethRpcModule; use reth_tracing::tracing::{debug, info}; use reth_transaction_pool::{ blobstore::{DiskFileBlobStore, DiskFileBlobStoreConfig}, EthTransactionPool, PoolTransaction, TransactionPool, TransactionValidationTaskExecutor, }; use reth_trie_db::MerklePatriciaTrie; use revm::context::TxEnv; use std::{default::Default, sync::Arc, time::SystemTime}; /// Type configuration for a regular Ethereum node. #[derive(Debug, Default, Clone, Copy)] #[non_exhaustive] pub struct EthereumNode; impl EthereumNode { /// Returns a [`ComponentsBuilder`] configured for a regular Ethereum node. pub fn components() -> ComponentsBuilder< Node, EthereumPoolBuilder, BasicPayloadServiceBuilder, EthereumNetworkBuilder, EthereumExecutorBuilder, EthereumConsensusBuilder, > where Node: FullNodeTypes>, ::Payload: PayloadTypes< BuiltPayload = EthBuiltPayload, PayloadAttributes = EthPayloadAttributes, PayloadBuilderAttributes = EthPayloadBuilderAttributes, >, { ComponentsBuilder::default() .node_types::() .pool(EthereumPoolBuilder::default()) .executor(EthereumExecutorBuilder::default()) .payload(BasicPayloadServiceBuilder::default()) .network(EthereumNetworkBuilder::default()) .consensus(EthereumConsensusBuilder::default()) } /// Instantiates the [`ProviderFactoryBuilder`] for an ethereum node. /// /// # Open a Providerfactory in read-only mode from a datadir /// /// See also: [`ProviderFactoryBuilder`] and /// [`ReadOnlyConfig`](reth_provider::providers::ReadOnlyConfig). /// /// ```no_run /// use reth_chainspec::MAINNET; /// use reth_node_ethereum::EthereumNode; /// /// let factory = EthereumNode::provider_factory_builder() /// .open_read_only(MAINNET.clone(), "datadir") /// .unwrap(); /// ``` /// /// # Open a Providerfactory manually with all required components /// /// ```no_run /// use reth_chainspec::ChainSpecBuilder; /// use reth_db::open_db_read_only; /// use reth_node_ethereum::EthereumNode; /// use reth_provider::providers::StaticFileProvider; /// use std::sync::Arc; /// /// let factory = EthereumNode::provider_factory_builder() /// .db(Arc::new(open_db_read_only("db", Default::default()).unwrap())) /// .chainspec(ChainSpecBuilder::mainnet().build().into()) /// .static_file(StaticFileProvider::read_only("db/static_files", false).unwrap()) /// .build_provider_factory(); /// ``` pub fn provider_factory_builder() -> ProviderFactoryBuilder { ProviderFactoryBuilder::default() } } impl NodeTypes for EthereumNode { type Primitives = EthPrimitives; type ChainSpec = ChainSpec; type StateCommitment = MerklePatriciaTrie; type Storage = EthStorage; type Payload = EthEngineTypes; } /// Builds [`EthApi`](reth_rpc::EthApi) for Ethereum. #[derive(Debug, Default)] pub struct EthereumEthApiBuilder; impl EthApiBuilder for EthereumEthApiBuilder where N: FullNodeComponents, EthApiFor: FullEthApiServer, { type EthApi = EthApiFor; async fn build_eth_api(self, ctx: EthApiCtx<'_, N>) -> eyre::Result { let api = reth_rpc::EthApiBuilder::new( ctx.components.provider().clone(), ctx.components.pool().clone(), ctx.components.network().clone(), ctx.components.evm_config().clone(), ) .eth_cache(ctx.cache) .task_spawner(ctx.components.task_executor().clone()) .gas_cap(ctx.config.rpc_gas_cap.into()) .max_simulate_blocks(ctx.config.rpc_max_simulate_blocks) .eth_proof_window(ctx.config.eth_proof_window) .fee_history_cache_config(ctx.config.fee_history_cache) .proof_permits(ctx.config.proof_permits) .gas_oracle_config(ctx.config.gas_oracle) .build(); Ok(api) } } /// Add-ons w.r.t. l1 ethereum. #[derive(Debug)] pub struct EthereumAddOns where EthApiFor: FullEthApiServer, { inner: RpcAddOns, } impl Default for EthereumAddOns where EthApiFor: FullEthApiServer, { fn default() -> Self { Self { inner: Default::default() } } } impl NodeAddOns for EthereumAddOns where N: FullNodeComponents< Types: NodeTypes< ChainSpec = ChainSpec, Primitives = EthPrimitives, Payload = EthEngineTypes, >, Evm: ConfigureEvm, >, EthApiError: FromEvmError, EvmFactoryFor: EvmFactory, { type Handle = RpcHandle>; async fn launch_add_ons( self, ctx: reth_node_api::AddOnsContext<'_, N>, ) -> eyre::Result { let validation_api = ValidationApi::new( ctx.node.provider().clone(), Arc::new(ctx.node.consensus().clone()), ctx.node.evm_config().clone(), ctx.config.rpc.flashbots_config(), Box::new(ctx.node.task_executor().clone()), Arc::new(EthereumEngineValidator::new(ctx.config.chain.clone())), ); self.inner .launch_add_ons_with(ctx, move |modules, _, _| { modules.merge_if_module_configured( RethRpcModule::Flashbots, validation_api.into_rpc(), )?; Ok(()) }) .await } } impl RethRpcAddOns for EthereumAddOns where N: FullNodeComponents< Types: NodeTypes< ChainSpec = ChainSpec, Primitives = EthPrimitives, Payload = EthEngineTypes, >, Evm: ConfigureEvm, >, EthApiError: FromEvmError, EvmFactoryFor: EvmFactory, { type EthApi = EthApiFor; fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks { self.inner.hooks_mut() } } impl EngineValidatorAddOn for EthereumAddOns where N: FullNodeComponents< Types: NodeTypes< ChainSpec = ChainSpec, Primitives = EthPrimitives, Payload = EthEngineTypes, >, >, EthApiFor: FullEthApiServer, { type Validator = EthereumEngineValidator; async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result { EthereumEngineValidatorBuilder::default().build(ctx).await } } impl Node for EthereumNode where N: FullNodeTypes, { type ComponentsBuilder = ComponentsBuilder< N, EthereumPoolBuilder, BasicPayloadServiceBuilder, EthereumNetworkBuilder, EthereumExecutorBuilder, EthereumConsensusBuilder, >; type AddOns = EthereumAddOns< NodeAdapter>::Components>, >; fn components_builder(&self) -> Self::ComponentsBuilder { Self::components() } fn add_ons(&self) -> Self::AddOns { EthereumAddOns::default() } } impl> DebugNode for EthereumNode { type RpcBlock = alloy_rpc_types_eth::Block; fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_ethereum_primitives::Block { rpc_block.into_consensus().convert_transactions() } } /// A regular ethereum evm and executor builder. #[derive(Debug, Default, Clone, Copy)] #[non_exhaustive] pub struct EthereumExecutorBuilder; impl ExecutorBuilder for EthereumExecutorBuilder where Types: NodeTypes, Node: FullNodeTypes, { type EVM = EthEvmConfig; async fn build_evm(self, ctx: &BuilderContext) -> eyre::Result { let evm_config = EthEvmConfig::new(ctx.chain_spec()) .with_extra_data(ctx.payload_builder_config().extra_data_bytes()); Ok(evm_config) } } /// A basic ethereum transaction pool. /// /// This contains various settings that can be configured and take precedence over the node's /// config. #[derive(Debug, Default, Clone, Copy)] #[non_exhaustive] pub struct EthereumPoolBuilder { // TODO add options for txpool args } impl PoolBuilder for EthereumPoolBuilder where Types: NodeTypes< ChainSpec: EthereumHardforks, Primitives: NodePrimitives, >, Node: FullNodeTypes, { type Pool = EthTransactionPool; async fn build_pool(self, ctx: &BuilderContext) -> eyre::Result { let data_dir = ctx.config().datadir(); let pool_config = ctx.pool_config(); let blob_cache_size = if let Some(blob_cache_size) = pool_config.blob_cache_size { blob_cache_size } else { // get the current blob params for the current timestamp, fallback to default Cancun // params let current_timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_secs(); let blob_params = ctx .chain_spec() .blob_params_at_timestamp(current_timestamp) .unwrap_or_else(BlobParams::cancun); // Derive the blob cache size from the target blob count, to auto scale it by // multiplying it with the slot count for 2 epochs: 384 for pectra (blob_params.target_blob_count * EPOCH_SLOTS * 2) as u32 }; let custom_config = DiskFileBlobStoreConfig::default().with_max_cached_entries(blob_cache_size); let blob_store = DiskFileBlobStore::open(data_dir.blobstore(), custom_config)?; let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone()) .with_head_timestamp(ctx.head().timestamp) .kzg_settings(ctx.kzg_settings()?) .with_local_transactions_config(pool_config.local_transactions_config.clone()) .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap) .with_additional_tasks(ctx.config().txpool.additional_validation_tasks) .build_with_tasks(ctx.task_executor().clone(), blob_store.clone()); let transaction_pool = reth_transaction_pool::Pool::eth_pool(validator, blob_store, pool_config); info!(target: "reth::cli", "Transaction pool initialized"); // spawn txpool maintenance task { let pool = transaction_pool.clone(); let chain_events = ctx.provider().canonical_state_stream(); let client = ctx.provider().clone(); // Only spawn backup task if not disabled if !ctx.config().txpool.disable_transactions_backup { // Use configured backup path or default to data dir let transactions_path = ctx .config() .txpool .transactions_backup_path .clone() .unwrap_or_else(|| data_dir.txpool_transactions()); let transactions_backup_config = reth_transaction_pool::maintain::LocalTransactionBackupConfig::with_local_txs_backup(transactions_path); ctx.task_executor().spawn_critical_with_graceful_shutdown_signal( "local transactions backup task", |shutdown| { reth_transaction_pool::maintain::backup_local_transactions_task( shutdown, pool.clone(), transactions_backup_config, ) }, ); } // spawn the maintenance task ctx.task_executor().spawn_critical( "txpool maintenance task", reth_transaction_pool::maintain::maintain_transaction_pool_future( client, pool, chain_events, ctx.task_executor().clone(), reth_transaction_pool::maintain::MaintainPoolConfig { max_tx_lifetime: transaction_pool.config().max_queued_lifetime, no_local_exemptions: transaction_pool .config() .local_transactions_config .no_exemptions, ..Default::default() }, ), ); debug!(target: "reth::cli", "Spawned txpool maintenance task"); } Ok(transaction_pool) } } /// A basic ethereum payload service. #[derive(Debug, Default, Clone, Copy)] pub struct EthereumNetworkBuilder { // TODO add closure to modify network } impl NetworkBuilder for EthereumNetworkBuilder where Node: FullNodeTypes>, Pool: TransactionPool< Transaction: PoolTransaction< Consensus = TxTy, Pooled = PooledTransactionVariant, >, > + Unpin + 'static, { type Network = NetworkHandle; async fn build_network( self, ctx: &BuilderContext, pool: Pool, ) -> eyre::Result { let network = ctx.network_builder().await?; let handle = ctx.start_network(network, pool); info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized"); Ok(handle) } } /// A basic ethereum consensus builder. #[derive(Debug, Default, Clone, Copy)] pub struct EthereumConsensusBuilder { // TODO add closure to modify consensus } impl ConsensusBuilder for EthereumConsensusBuilder where Node: FullNodeTypes>, { type Consensus = Arc>; async fn build_consensus(self, ctx: &BuilderContext) -> eyre::Result { Ok(Arc::new(EthBeaconConsensus::new(ctx.chain_spec()))) } } /// Builder for [`EthereumEngineValidator`]. #[derive(Debug, Default, Clone)] #[non_exhaustive] pub struct EthereumEngineValidatorBuilder; impl EngineValidatorBuilder for EthereumEngineValidatorBuilder where Types: NodeTypes, Node: FullNodeComponents, { type Validator = EthereumEngineValidator; async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result { Ok(EthereumEngineValidator::new(ctx.config.chain.clone())) } }