1pub 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#[derive(Debug, Default, Clone, Copy)]
46#[non_exhaustive]
47pub struct EthereumNode;
48
49impl EthereumNode {
50 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 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#[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#[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#[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#[derive(Debug, Default, Clone, Copy)]
317#[non_exhaustive]
318pub struct EthereumPoolBuilder {
319 }
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 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 (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 {
371 let pool = transaction_pool.clone();
372 let chain_events = ctx.provider().canonical_state_stream();
373 let client = ctx.provider().clone();
374 if !ctx.config().txpool.disable_transactions_backup {
376 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 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#[derive(Debug, Default, Clone, Copy)]
426pub struct EthereumNetworkBuilder {
427 }
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#[derive(Debug, Default, Clone, Copy)]
457pub struct EthereumConsensusBuilder {
458 }
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#[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}