Skip to main content

reth_node_ethereum/
node.rs

1//! Ethereum Node types config.
2
3use crate::{
4    engine_ssz_proxy::{EngineSszApi, EngineSszProxyLayer},
5    engine_ssz_witness::EngineSszWitnessGenerator,
6    EthEngineTypes, EthEvmConfig,
7};
8use alloy_eips::{eip7840::BlobParams, merge::EPOCH_SLOTS};
9use alloy_network::Ethereum;
10use alloy_rpc_types_engine::ExecutionData;
11use reth_chainspec::{ChainSpec, EthChainSpec, EthereumHardforks, Hardforks};
12use reth_engine_local::LocalPayloadAttributesBuilder;
13use reth_engine_primitives::EngineTypes;
14use reth_ethereum_consensus::EthBeaconConsensus;
15use reth_ethereum_engine_primitives::{EthBuiltPayload, EthPayloadAttributes};
16use reth_ethereum_primitives::{EthPrimitives, TransactionSigned};
17use reth_evm::{ConfigureEvm, NextBlockEnvAttributes};
18#[cfg(not(feature = "jit"))]
19use reth_evm_ethereum::RethEvmFactory;
20#[cfg(feature = "jit")]
21use reth_evm_ethereum::{
22    JitBackend, JitMetrics, JitMode, RethEvmFactory, RuntimeConfig, RuntimeTuning,
23};
24use reth_network::{primitives::BasicNetworkPrimitives, NetworkHandle, PeersInfo};
25use reth_node_api::{
26    AddOnsContext, FullNodeComponents, HeaderTy, NodeAddOns, NodePrimitives,
27    PayloadAttributesBuilder, PrimitivesTy, TxTy,
28};
29use reth_node_builder::{
30    components::{
31        BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
32        NetworkBuilder, PoolBuilder, TxPoolBuilder,
33    },
34    node::{FullNodeTypes, NodeTypes},
35    rpc::{
36        BasicEngineApiBuilder, BasicEngineValidatorBuilder, Either, EngineApiBuilder,
37        EngineValidatorAddOn, EngineValidatorBuilder, EthApiBuilder, EthApiCtx, EthApiFor,
38        EthRpcConverterFor, Identity, PayloadValidatorBuilder, RethAuthHttpMiddleware,
39        RethRpcAddOns, RethRpcMiddleware, RpcAddOns, RpcHandle, Stack,
40    },
41    BuilderContext, DebugNode, EngineApiExt, Node, NodeAdapter, PayloadBuilderConfig,
42};
43use reth_node_core::args::JitArgs;
44use reth_payload_primitives::PayloadTypes;
45use reth_provider::{providers::ProviderFactoryBuilder, EthStorage};
46use reth_rpc::{TestingApi, ValidationApi};
47use reth_rpc_api::servers::{BlockSubmissionValidationApiServer, TestingApiServer};
48use reth_rpc_builder::config::RethRpcServerConfig;
49use reth_rpc_eth_api::{
50    helpers::{
51        config::{EthConfigApiServer, EthConfigHandler},
52        pending_block::BuildPendingEnv,
53    },
54    RpcConvert, RpcTypes, SignableTxRequest,
55};
56use reth_rpc_eth_types::{error::FromEvmError, EthApiError};
57use reth_rpc_server_types::RethRpcModule;
58use reth_transaction_pool::{
59    blobstore::DiskFileBlobStore, EthTransactionPool, PoolPooledTx, PoolTransaction,
60    TransactionPool, TransactionValidationTaskExecutor,
61};
62use std::{marker::PhantomData, sync::Arc, time::SystemTime};
63#[cfg(feature = "jit")]
64use tracing::warn;
65use tracing::{debug, info};
66
67pub use crate::{payload::EthereumPayloadBuilder, EthereumEngineValidator};
68
69/// Handles the legacy JIT helper subcommand hook.
70#[cfg(feature = "jit")]
71pub use reth_evm_ethereum::maybe_run_jit_helper;
72
73/// Handles the legacy JIT helper subcommand hook.
74#[cfg(not(feature = "jit"))]
75pub const fn maybe_run_jit_helper() -> eyre::Result<std::ops::ControlFlow<()>> {
76    Ok(std::ops::ControlFlow::Continue(()))
77}
78
79/// Type configuration for a regular Ethereum node.
80#[derive(Debug, Default, Clone, Copy)]
81#[non_exhaustive]
82pub struct EthereumNode;
83
84impl EthereumNode {
85    /// Returns a [`ComponentsBuilder`] configured for a regular Ethereum node.
86    pub fn components<Node>() -> ComponentsBuilder<
87        Node,
88        EthereumPoolBuilder,
89        BasicPayloadServiceBuilder<EthereumPayloadBuilder>,
90        EthereumNetworkBuilder,
91        EthereumExecutorBuilder,
92        EthereumConsensusBuilder,
93    >
94    where
95        Node: FullNodeTypes<
96            Types: NodeTypes<ChainSpec: Hardforks + EthereumHardforks, Primitives = EthPrimitives>,
97        >,
98        <Node::Types as NodeTypes>::Payload:
99            PayloadTypes<BuiltPayload = EthBuiltPayload, PayloadAttributes = EthPayloadAttributes>,
100    {
101        ComponentsBuilder::default()
102            .node_types::<Node>()
103            .pool(EthereumPoolBuilder::default())
104            .executor(EthereumExecutorBuilder::default())
105            .payload(BasicPayloadServiceBuilder::default())
106            .network(EthereumNetworkBuilder::default())
107            .consensus(EthereumConsensusBuilder::default())
108    }
109
110    /// Instantiates the [`ProviderFactoryBuilder`] for an ethereum node.
111    ///
112    /// # Open a Providerfactory in read-only mode from a datadir
113    ///
114    /// See also: [`ProviderFactoryBuilder`] and
115    /// [`ReadOnlyConfig`](reth_provider::providers::ReadOnlyConfig).
116    ///
117    /// ```no_run
118    /// use reth_chainspec::MAINNET;
119    /// use reth_node_ethereum::EthereumNode;
120    ///
121    /// fn demo(runtime: reth_tasks::Runtime) {
122    ///     let factory = EthereumNode::provider_factory_builder()
123    ///         .open_read_only(MAINNET.clone(), "datadir", runtime)
124    ///         .unwrap();
125    /// }
126    /// ```
127    ///
128    /// See also [`ProviderFactory::new`](reth_provider::ProviderFactory::new) for constructing
129    /// a [`ProviderFactory`](reth_provider::ProviderFactory) manually with all required
130    /// components.
131    pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
132        ProviderFactoryBuilder::default()
133    }
134}
135
136impl NodeTypes for EthereumNode {
137    type Primitives = EthPrimitives;
138    type ChainSpec = ChainSpec;
139    type Storage = EthStorage;
140    type Payload = EthEngineTypes;
141}
142
143/// Builds [`EthApi`](reth_rpc::EthApi) for Ethereum.
144#[derive(Debug)]
145pub struct EthereumEthApiBuilder<NetworkT = Ethereum>(PhantomData<NetworkT>);
146
147impl<NetworkT> Default for EthereumEthApiBuilder<NetworkT> {
148    fn default() -> Self {
149        Self(Default::default())
150    }
151}
152
153impl<N, NetworkT> EthApiBuilder<N> for EthereumEthApiBuilder<NetworkT>
154where
155    N: FullNodeComponents<
156        Types: NodeTypes<ChainSpec: Hardforks + EthereumHardforks>,
157        Evm: ConfigureEvm<NextBlockEnvCtx: BuildPendingEnv<HeaderTy<N::Types>>>,
158    >,
159    NetworkT: RpcTypes<TransactionRequest: SignableTxRequest<TxTy<N::Types>>>,
160    EthRpcConverterFor<N, NetworkT>: RpcConvert<
161        Primitives = PrimitivesTy<N::Types>,
162        Error = EthApiError,
163        Network = NetworkT,
164        Evm = N::Evm,
165    >,
166    EthApiError: FromEvmError<N::Evm>,
167{
168    type EthApi = EthApiFor<N, NetworkT>;
169
170    async fn build_eth_api(self, ctx: EthApiCtx<'_, N>) -> eyre::Result<Self::EthApi> {
171        Ok(ctx.eth_api_builder().map_converter(|r| r.with_network()).build())
172    }
173}
174
175/// Add-ons w.r.t. l1 ethereum.
176#[derive(Debug)]
177pub struct EthereumAddOns<
178    N: FullNodeComponents,
179    EthB: EthApiBuilder<N>,
180    PVB,
181    EB = BasicEngineApiBuilder<PVB>,
182    EVB = BasicEngineValidatorBuilder<PVB>,
183    RpcMiddleware = Identity,
184    AuthHttpMiddleware = Identity,
185> {
186    inner: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>,
187}
188
189impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
190    EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
191where
192    N: FullNodeComponents,
193    EthB: EthApiBuilder<N>,
194{
195    /// Creates a new instance from the inner `RpcAddOns`.
196    pub const fn new(
197        inner: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>,
198    ) -> Self {
199        Self { inner }
200    }
201}
202
203impl<N> Default for EthereumAddOns<N, EthereumEthApiBuilder, EthereumEngineValidatorBuilder>
204where
205    N: FullNodeComponents<
206        Types: NodeTypes<
207            ChainSpec: EthereumHardforks + Clone + 'static,
208            Payload: EngineTypes<ExecutionData = ExecutionData>
209                         + PayloadTypes<PayloadAttributes = EthPayloadAttributes>,
210            Primitives = EthPrimitives,
211        >,
212    >,
213    EthereumEthApiBuilder: EthApiBuilder<N>,
214{
215    fn default() -> Self {
216        Self::new(RpcAddOns::new(
217            EthereumEthApiBuilder::default(),
218            EthereumEngineValidatorBuilder::default(),
219            BasicEngineApiBuilder::default(),
220            BasicEngineValidatorBuilder::default(),
221            Default::default(),
222            Identity::new(),
223        ))
224    }
225}
226
227impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
228    EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
229where
230    N: FullNodeComponents,
231    EthB: EthApiBuilder<N>,
232{
233    /// Replace the engine API builder.
234    pub fn with_engine_api<T>(
235        self,
236        engine_api_builder: T,
237    ) -> EthereumAddOns<N, EthB, PVB, T, EVB, RpcMiddleware, AuthHttpMiddleware>
238    where
239        T: Send,
240    {
241        let Self { inner } = self;
242        EthereumAddOns::new(inner.with_engine_api(engine_api_builder))
243    }
244
245    /// Replace the payload validator builder.
246    pub fn with_payload_validator<V, T>(
247        self,
248        payload_validator_builder: T,
249    ) -> EthereumAddOns<N, EthB, T, EB, EVB, RpcMiddleware, AuthHttpMiddleware> {
250        let Self { inner } = self;
251        EthereumAddOns::new(inner.with_payload_validator(payload_validator_builder))
252    }
253
254    /// Sets rpc middleware
255    pub fn with_rpc_middleware<T>(
256        self,
257        rpc_middleware: T,
258    ) -> EthereumAddOns<N, EthB, PVB, EB, EVB, T, AuthHttpMiddleware>
259    where
260        T: Send,
261    {
262        let Self { inner } = self;
263        EthereumAddOns::new(inner.with_rpc_middleware(rpc_middleware))
264    }
265
266    /// Configures the HTTP transport middleware for the auth / Engine API server.
267    pub fn with_auth_http_middleware<T>(
268        self,
269        auth_http_middleware: T,
270    ) -> EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, T>
271    where
272        T: Send,
273    {
274        let Self { inner } = self;
275        EthereumAddOns::new(inner.with_auth_http_middleware(auth_http_middleware))
276    }
277
278    /// Stacks an additional HTTP transport middleware layer for the auth / Engine API server.
279    pub fn layer_auth_http_middleware<T>(
280        self,
281        layer: T,
282    ) -> EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, Stack<AuthHttpMiddleware, T>> {
283        let Self { inner } = self;
284        EthereumAddOns::new(inner.layer_auth_http_middleware(layer))
285    }
286
287    /// Conditionally stacks an HTTP transport middleware layer for the auth / Engine API server.
288    #[expect(clippy::type_complexity)]
289    pub fn option_layer_auth_http_middleware<T>(
290        self,
291        layer: Option<T>,
292    ) -> EthereumAddOns<
293        N,
294        EthB,
295        PVB,
296        EB,
297        EVB,
298        RpcMiddleware,
299        Stack<AuthHttpMiddleware, Either<T, Identity>>,
300    > {
301        let Self { inner } = self;
302        EthereumAddOns::new(inner.option_layer_auth_http_middleware(layer))
303    }
304
305    /// Sets the tokio runtime for the RPC servers.
306    ///
307    /// Caution: This runtime must not be created from within asynchronous context.
308    pub fn with_tokio_runtime(self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
309        let Self { inner } = self;
310        Self { inner: inner.with_tokio_runtime(tokio_runtime) }
311    }
312}
313
314impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware> NodeAddOns<N>
315    for EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
316where
317    N: FullNodeComponents<
318        Types: NodeTypes<
319            ChainSpec: EthChainSpec + Hardforks + EthereumHardforks,
320            Primitives = EthPrimitives,
321            Payload: EngineTypes<ExecutionData = ExecutionData>,
322        >,
323        Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes>,
324    >,
325    EthB: EthApiBuilder<N>,
326    PVB: Send,
327    EB: EngineApiBuilder<N>,
328    EB::EngineApi: EngineSszApi,
329    EVB: EngineValidatorBuilder<N>,
330    EthApiError: FromEvmError<N::Evm>,
331    RpcMiddleware: RethRpcMiddleware,
332    AuthHttpMiddleware: RethAuthHttpMiddleware<Identity>,
333    Stack<EngineSszProxyLayer<EB::EngineApi>, AuthHttpMiddleware>: RethAuthHttpMiddleware<Identity>,
334{
335    type Handle = RpcHandle<N, EthB::EthApi>;
336
337    async fn launch_add_ons(
338        self,
339        ctx: reth_node_api::AddOnsContext<'_, N>,
340    ) -> eyre::Result<Self::Handle> {
341        let validation_api = ValidationApi::<_, _, <N::Types as NodeTypes>::Payload>::new(
342            ctx.node.provider().clone(),
343            Arc::new(ctx.node.consensus().clone()),
344            ctx.node.evm_config().clone(),
345            ctx.config.rpc.flashbots_config(),
346            ctx.node.task_executor().clone(),
347            Arc::new(EthereumEngineValidator::new(ctx.config.chain.clone())),
348            ctx.sender_recovery_cache.clone(),
349        );
350
351        let eth_config =
352            EthConfigHandler::new(ctx.node.provider().clone(), ctx.node.evm_config().clone());
353
354        let testing_skip_invalid_transactions = ctx.config.rpc.testing_skip_invalid_transactions;
355        let testing_gas_limit_override = ctx.config.rpc.testing_gas_limit;
356        let testing_desired_gas_limit = ctx.config.builder.gas_limit_for(ctx.config.chain.chain());
357        let testing_engine_handle = ctx.beacon_engine_handle.clone();
358
359        let (ssz_proxy_layer, ssz_proxy_handle) = EngineSszProxyLayer::new();
360        ssz_proxy_handle.set_witness_handler_sync(Arc::new(EngineSszWitnessGenerator::new(
361            ctx.node.provider().clone(),
362            ctx.node.evm_config().clone(),
363            ctx.node.task_executor().clone(),
364        )));
365
366        self.inner
367            .map_engine_api(|engine_api_builder| {
368                EngineApiExt::new(engine_api_builder, move |engine_api| {
369                    ssz_proxy_handle.set_engine_api_sync(engine_api);
370                })
371            })
372            .map_auth_http_middleware(|middleware| Stack::new(ssz_proxy_layer, middleware))
373            .launch_add_ons_with(ctx, move |container| {
374                container.modules.merge_if_module_configured(
375                    RethRpcModule::Flashbots,
376                    validation_api.into_rpc(),
377                )?;
378
379                container
380                    .modules
381                    .merge_if_module_configured(RethRpcModule::Eth, eth_config.into_rpc())?;
382
383                // testing_buildBlockV1: only wire when the hidden testing module is explicitly
384                // requested on any transport. Default stays disabled to honor security guidance.
385                let mut testing_api = TestingApi::new(
386                    container.registry.eth_api().clone(),
387                    container.registry.evm_config().clone(),
388                    testing_desired_gas_limit,
389                    testing_engine_handle,
390                );
391                if testing_skip_invalid_transactions {
392                    testing_api = testing_api.with_skip_invalid_transactions();
393                }
394                if let Some(gas_limit) = testing_gas_limit_override {
395                    testing_api = testing_api.with_gas_limit_override(gas_limit);
396                }
397                container
398                    .modules
399                    .merge_if_module_configured(RethRpcModule::Testing, testing_api.into_rpc())?;
400
401                Ok(())
402            })
403            .await
404    }
405}
406
407impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware> RethRpcAddOns<N>
408    for EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
409where
410    N: FullNodeComponents<
411        Types: NodeTypes<
412            ChainSpec: Hardforks + EthereumHardforks,
413            Primitives = EthPrimitives,
414            Payload: EngineTypes<ExecutionData = ExecutionData>,
415        >,
416        Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes>,
417    >,
418    EthB: EthApiBuilder<N>,
419    PVB: PayloadValidatorBuilder<N>,
420    EB: EngineApiBuilder<N>,
421    EB::EngineApi: EngineSszApi,
422    EVB: EngineValidatorBuilder<N>,
423    EthApiError: FromEvmError<N::Evm>,
424    RpcMiddleware: RethRpcMiddleware,
425    AuthHttpMiddleware: RethAuthHttpMiddleware<Identity>,
426    Stack<EngineSszProxyLayer<EB::EngineApi>, AuthHttpMiddleware>: RethAuthHttpMiddleware<Identity>,
427{
428    type EthApi = EthB::EthApi;
429
430    fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
431        self.inner.hooks_mut()
432    }
433}
434
435impl<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware> EngineValidatorAddOn<N>
436    for EthereumAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware, AuthHttpMiddleware>
437where
438    N: FullNodeComponents<
439        Types: NodeTypes<
440            ChainSpec: EthChainSpec + EthereumHardforks,
441            Primitives = EthPrimitives,
442            Payload: EngineTypes<ExecutionData = ExecutionData>,
443        >,
444        Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes>,
445    >,
446    EthB: EthApiBuilder<N>,
447    PVB: Send,
448    EB: EngineApiBuilder<N>,
449    EVB: EngineValidatorBuilder<N>,
450    EthApiError: FromEvmError<N::Evm>,
451    RpcMiddleware: Send,
452    AuthHttpMiddleware: Send,
453{
454    type ValidatorBuilder = EVB;
455
456    fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
457        self.inner.engine_validator_builder()
458    }
459}
460
461impl<N> Node<N> for EthereumNode
462where
463    N: FullNodeTypes<Types = Self>,
464{
465    type ComponentsBuilder = ComponentsBuilder<
466        N,
467        EthereumPoolBuilder,
468        BasicPayloadServiceBuilder<EthereumPayloadBuilder>,
469        EthereumNetworkBuilder,
470        EthereumExecutorBuilder,
471        EthereumConsensusBuilder,
472    >;
473
474    type AddOns =
475        EthereumAddOns<NodeAdapter<N>, EthereumEthApiBuilder, EthereumEngineValidatorBuilder>;
476
477    fn components_builder(&self) -> Self::ComponentsBuilder {
478        Self::components()
479    }
480
481    fn add_ons(&self) -> Self::AddOns {
482        EthereumAddOns::default()
483    }
484}
485
486impl<N: FullNodeComponents<Types = Self>> DebugNode<N> for EthereumNode {
487    type RpcBlock = alloy_rpc_types_eth::Block;
488
489    fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_ethereum_primitives::Block {
490        rpc_block.into_consensus().convert_transactions()
491    }
492
493    fn local_payload_attributes_builder(
494        chain_spec: &Self::ChainSpec,
495    ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes> {
496        LocalPayloadAttributesBuilder::new(Arc::new(chain_spec.clone()))
497    }
498}
499
500/// Builds a [`RuntimeConfig`] from CLI [`JitArgs`].
501#[cfg(feature = "jit")]
502fn jit_runtime_config(jit: &JitArgs) -> RuntimeConfig {
503    let default_tuning = RuntimeTuning::default();
504    let tuning = RuntimeTuning {
505        channel_capacity: jit.channel_capacity,
506        jit_hot_threshold: jit.hot_threshold,
507        jit_max_bytecode_len: jit.max_bytecode_len,
508        jit_max_pending_jobs: jit.max_pending_jobs,
509        jit_worker_count: jit.worker_count.unwrap_or(default_tuning.jit_worker_count),
510        resident_code_cache_bytes: jit.code_cache_bytes,
511        idle_evict_duration: Some(jit.idle_evict_duration),
512        ..default_tuning
513    };
514
515    RuntimeConfig {
516        enabled: jit.enabled,
517        tuning,
518        debug_assertions: jit.debug,
519        blocking: jit.blocking,
520        jit_mode: JitMode::OutOfProcess,
521        ..RuntimeConfig::default()
522    }
523}
524
525/// Builds an [`EthEvmConfig`] with evm2 JIT from CLI [`JitArgs`].
526///
527/// This is the shared setup used by both [`EthereumExecutorBuilder`] and `reth re-execute`.
528///
529/// Returns the EVM config and metrics recorder if JIT starts enabled.
530#[cfg(feature = "jit")]
531#[allow(clippy::type_complexity)]
532pub fn build_evm_config<C: EthereumHardforks>(
533    chain_spec: Arc<C>,
534    jit: &JitArgs,
535    dump_dir: Option<std::path::PathBuf>,
536) -> eyre::Result<(EthEvmConfig<C, RethEvmFactory>, Option<Arc<JitMetrics>>)> {
537    if !jit.enabled {
538        // Keep the configured mode and tuning so `reth_jit enable` starts the backend with them.
539        // Blocking mode would start the backend immediately, so it only applies with `--jit`.
540        let config = RuntimeConfig { blocking: false, ..jit_runtime_config(jit) };
541        #[cfg(not(unix))]
542        let config = RuntimeConfig { jit_mode: JitMode::InProcess, ..config };
543        let factory = RethEvmFactory::new(JitBackend::new(config)?);
544        return Ok((EthEvmConfig::new_with_evm_factory(chain_spec, factory), None));
545    }
546
547    let mut config = jit_runtime_config(jit);
548    config.dump_dir = dump_dir;
549
550    let jit_metrics = Arc::new(JitMetrics::default());
551    let compilation_metrics = jit_metrics.clone();
552    config.on_compilation = Some(Arc::new(move |event| {
553        compilation_metrics.record_compilation(&event);
554    }));
555
556    let tuning = config.tuning;
557    let jit_mode = config.jit_mode;
558    let backend = JitBackend::new(config)?;
559
560    warn!(target: "reth::cli",
561        hot_threshold = tuning.jit_hot_threshold,
562        workers = tuning.jit_worker_count,
563        mode = ?jit_mode,
564        blocking = jit.blocking,
565        "Started experimental evm2 JIT backend; this may cause instability",
566    );
567
568    let factory = RethEvmFactory::new_with_metrics(backend, jit_metrics.as_ref().clone());
569    let evm_config = EthEvmConfig::new_with_evm_factory(chain_spec, factory);
570
571    Ok((evm_config, Some(jit_metrics)))
572}
573
574/// Builds an [`EthEvmConfig`] from CLI [`JitArgs`].
575///
576/// Compiled without the `jit` feature: errors if JIT was requested via [`JitArgs`] and otherwise
577/// returns a plain interpreter-backed config.
578#[cfg(not(feature = "jit"))]
579#[allow(clippy::type_complexity)]
580pub fn build_evm_config<C: EthereumHardforks>(
581    chain_spec: Arc<C>,
582    jit: &JitArgs,
583    _dump_dir: Option<std::path::PathBuf>,
584) -> eyre::Result<(EthEvmConfig<C, RethEvmFactory>, Option<()>)> {
585    if jit.enabled {
586        eyre::bail!(
587            "JIT compilation was requested but this binary was compiled without the `jit` feature"
588        );
589    }
590    let factory = RethEvmFactory::disabled();
591    Ok((EthEvmConfig::new_with_evm_factory(chain_spec, factory), None))
592}
593
594/// A regular ethereum evm and executor builder.
595#[derive(Debug, Default, Clone, Copy)]
596#[non_exhaustive]
597pub struct EthereumExecutorBuilder;
598
599impl<Types, Node> ExecutorBuilder<Node> for EthereumExecutorBuilder
600where
601    Types: NodeTypes<ChainSpec: Hardforks + EthereumHardforks, Primitives = EthPrimitives>,
602    Node: FullNodeTypes<Types = Types>,
603{
604    type EVM = EthEvmConfig<Types::ChainSpec, RethEvmFactory>;
605
606    async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
607        let jit = &ctx.config().jit;
608        let dump_dir = jit.debug.then(|| ctx.config().datadir().data_dir().join("jit"));
609
610        let (mut evm_config, jit_metrics) = build_evm_config(ctx.chain_spec(), jit, dump_dir)?;
611        if let Some(cache) = ctx.sender_recovery_cache() {
612            evm_config = evm_config.with_sender_recovery_cache(cache.clone());
613        }
614
615        #[cfg(not(feature = "jit"))]
616        let _ = jit_metrics;
617
618        #[cfg(feature = "jit")]
619        if let Some(jit_metrics) = jit_metrics {
620            let metrics_backend = evm_config.executor_factory.evm_factory().backend().clone();
621            ctx.task_executor().spawn_with_graceful_shutdown_signal(|shutdown| async move {
622                let mut shutdown = std::pin::pin!(shutdown);
623                loop {
624                    tokio::select! {
625                        _ = tokio::time::sleep(std::time::Duration::from_secs(5)) => {
626                            jit_metrics.record(&metrics_backend.stats());
627                        }
628                        _ = &mut shutdown => break,
629                    }
630                }
631            });
632        }
633
634        Ok(evm_config)
635    }
636}
637
638/// A basic ethereum transaction pool.
639///
640/// This contains various settings that can be configured and take precedence over the node's
641/// config.
642#[derive(Debug, Clone, Copy)]
643#[non_exhaustive]
644pub struct EthereumPoolBuilder {
645    init_kzg_settings: bool,
646}
647
648impl EthereumPoolBuilder {
649    /// Creates a new [`EthereumPoolBuilder`].
650    pub const fn new() -> Self {
651        Self { init_kzg_settings: false }
652    }
653
654    /// Sets whether to initialize KZG settings even if EIP-4844 support is disabled in the pool.
655    pub const fn with_init_kzg_settings(mut self, init_kzg_settings: bool) -> Self {
656        self.init_kzg_settings = init_kzg_settings;
657        self
658    }
659}
660
661impl Default for EthereumPoolBuilder {
662    fn default() -> Self {
663        Self::new()
664    }
665}
666
667impl<Types, Node, Evm> PoolBuilder<Node, Evm> for EthereumPoolBuilder
668where
669    Types: NodeTypes<
670        ChainSpec: EthereumHardforks,
671        Primitives: NodePrimitives<SignedTx = TransactionSigned>,
672    >,
673    Node: FullNodeTypes<Types = Types>,
674    Evm: ConfigureEvm<Primitives = PrimitivesTy<Types>> + 'static,
675{
676    type Pool = EthTransactionPool<Node::Provider, DiskFileBlobStore, Evm>;
677
678    async fn build_pool(
679        self,
680        ctx: &BuilderContext<Node>,
681        evm_config: Evm,
682    ) -> eyre::Result<Self::Pool> {
683        let pool_config = ctx.pool_config();
684
685        let blobs_disabled = ctx.config().txpool.disable_blobs_support ||
686            ctx.config().txpool.blobpool_max_count == 0;
687
688        let blob_cache_size = if let Some(blob_cache_size) = pool_config.blob_cache_size {
689            Some(blob_cache_size)
690        } else {
691            // get the current blob params for the current timestamp, fallback to default Cancun
692            // params
693            let current_timestamp =
694                SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_secs();
695            let blob_params = ctx
696                .chain_spec()
697                .blob_params_at_timestamp(current_timestamp)
698                .unwrap_or_else(BlobParams::cancun);
699
700            // Derive the blob cache size from the target blob count, to auto scale it by
701            // multiplying it with the slot count for 2 epochs: 384 for pectra
702            Some((blob_params.target_blob_count * EPOCH_SLOTS * 2) as u32)
703        };
704
705        let blob_store =
706            reth_node_builder::components::create_blob_store_with_cache(ctx, blob_cache_size)?;
707
708        let validator =
709            TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone(), evm_config)
710                .set_eip4844(!blobs_disabled)
711                .kzg_settings(ctx.kzg_settings()?)
712                .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
713                .with_local_transactions_config(pool_config.local_transactions_config.clone())
714                .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
715                .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
716                .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
717                .with_additional_tasks(ctx.config().txpool.additional_validation_tasks)
718                .build_with_tasks(ctx.task_executor().clone(), blob_store.clone());
719
720        if (validator.validator().eip4844() || self.init_kzg_settings) &&
721            !ctx.config().debug.lazy_kzg_settings
722        {
723            // initializing the KZG settings can be expensive, this should be done upfront so that
724            // it doesn't impact the first block or the first gossiped blob transaction, so we
725            // initialize this in the background
726            let kzg_settings = validator.validator().kzg_settings().clone();
727            ctx.task_executor().spawn_blocking_task(async move {
728                let _ = kzg_settings.get();
729                debug!(target: "reth::cli", "Initialized KZG settings");
730            });
731        }
732
733        let transaction_pool = TxPoolBuilder::new(ctx)
734            .with_validator(validator)
735            .build_and_spawn_maintenance_task(blob_store, pool_config)?;
736
737        info!(target: "reth::cli", "Transaction pool initialized");
738        debug!(target: "reth::cli", "Spawned txpool maintenance task");
739
740        Ok(transaction_pool)
741    }
742}
743
744/// A basic ethereum payload service.
745#[derive(Debug, Default, Clone, Copy)]
746pub struct EthereumNetworkBuilder {
747    // TODO add closure to modify network
748}
749
750impl<Node, Pool> NetworkBuilder<Node, Pool> for EthereumNetworkBuilder
751where
752    Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
753    Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
754        + Unpin
755        + 'static,
756{
757    type Network =
758        NetworkHandle<BasicNetworkPrimitives<PrimitivesTy<Node::Types>, PoolPooledTx<Pool>>>;
759
760    async fn build_network(
761        self,
762        ctx: &BuilderContext<Node>,
763        pool: Pool,
764    ) -> eyre::Result<Self::Network> {
765        let network = ctx.network_builder().await?;
766        let handle = ctx.start_network(network, pool);
767        info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized");
768        Ok(handle)
769    }
770}
771
772/// A basic ethereum consensus builder.
773#[derive(Debug, Default, Clone, Copy)]
774pub struct EthereumConsensusBuilder {
775    // TODO add closure to modify consensus
776}
777
778impl<Node> ConsensusBuilder<Node> for EthereumConsensusBuilder
779where
780    Node: FullNodeTypes<
781        Types: NodeTypes<ChainSpec: EthChainSpec + EthereumHardforks, Primitives = EthPrimitives>,
782    >,
783{
784    type Consensus = Arc<EthBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
785
786    async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
787        Ok(Arc::new(EthBeaconConsensus::new(ctx.chain_spec())))
788    }
789}
790
791/// Builder for [`EthereumEngineValidator`].
792#[derive(Debug, Default, Clone)]
793#[non_exhaustive]
794pub struct EthereumEngineValidatorBuilder;
795
796impl<Node, Types> PayloadValidatorBuilder<Node> for EthereumEngineValidatorBuilder
797where
798    Types: NodeTypes<
799        ChainSpec: Hardforks + EthereumHardforks + 'static,
800        Payload: EngineTypes<ExecutionData = ExecutionData>
801                     + PayloadTypes<PayloadAttributes = EthPayloadAttributes>,
802        Primitives = EthPrimitives,
803    >,
804    Node: FullNodeComponents<Types = Types>,
805{
806    type Validator = EthereumEngineValidator<Types::ChainSpec>;
807
808    async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
809        Ok(EthereumEngineValidator::new(ctx.config.chain.clone()))
810    }
811}