1pub 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#[derive(Debug, Default, Clone, Copy)]
49#[non_exhaustive]
50pub struct EthereumNode;
51
52impl EthereumNode {
53 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 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#[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#[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#[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#[derive(Debug, Default, Clone, Copy)]
326#[non_exhaustive]
327pub struct EthereumPoolBuilder {
328 }
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 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 (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 {
376 let pool = transaction_pool.clone();
377 let chain_events = ctx.provider().canonical_state_stream();
378 let client = ctx.provider().clone();
379 if !ctx.config().txpool.disable_transactions_backup {
381 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 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#[derive(Debug, Default, Clone, Copy)]
431pub struct EthereumNetworkBuilder {
432 }
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#[derive(Debug, Default, Clone, Copy)]
459pub struct EthereumConsensusBuilder {
460 }
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#[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}