reth_optimism_node/
node.rs

1//! Optimism Node types config.
2
3use crate::{
4    args::RollupArgs,
5    engine::OpEngineValidator,
6    txpool::{OpTransactionPool, OpTransactionValidator},
7    OpEngineApiBuilder, OpEngineTypes,
8};
9use op_alloy_consensus::{interop::SafetyLevel, OpPooledTransaction};
10use op_alloy_rpc_types_engine::OpExecutionData;
11use reth_chainspec::{ChainSpecProvider, EthChainSpec, Hardforks};
12use reth_engine_local::LocalPayloadAttributesBuilder;
13use reth_evm::ConfigureEvm;
14use reth_network::{
15    types::BasicNetworkPrimitives, NetworkConfig, NetworkHandle, NetworkManager, NetworkPrimitives,
16    PeersInfo,
17};
18use reth_node_api::{
19    AddOnsContext, BuildNextEnv, EngineTypes, FullNodeComponents, HeaderTy, NodeAddOns,
20    NodePrimitives, PayloadAttributesBuilder, PayloadTypes, PrimitivesTy, TxTy,
21};
22use reth_node_builder::{
23    components::{
24        BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
25        NetworkBuilder, PayloadBuilderBuilder, PoolBuilder, PoolBuilderConfigOverrides,
26        TxPoolBuilder,
27    },
28    node::{FullNodeTypes, NodeTypes},
29    rpc::{
30        BasicEngineValidatorBuilder, EngineApiBuilder, EngineValidatorAddOn,
31        EngineValidatorBuilder, EthApiBuilder, Identity, PayloadValidatorBuilder, RethRpcAddOns,
32        RethRpcMiddleware, RethRpcServerHandles, RpcAddOns, RpcContext, RpcHandle,
33    },
34    BuilderContext, DebugNode, Node, NodeAdapter, NodeComponentsBuilder,
35};
36use reth_optimism_chainspec::{OpChainSpec, OpHardfork};
37use reth_optimism_consensus::OpBeaconConsensus;
38use reth_optimism_evm::{OpEvmConfig, OpRethReceiptBuilder};
39use reth_optimism_forks::OpHardforks;
40use reth_optimism_payload_builder::{
41    builder::OpPayloadTransactions,
42    config::{OpBuilderConfig, OpDAConfig, OpGasLimitConfig},
43    OpAttributes, OpBuiltPayload, OpPayloadPrimitives,
44};
45use reth_optimism_primitives::{DepositReceipt, OpPrimitives};
46use reth_optimism_rpc::{
47    eth::{ext::OpEthExtApi, OpEthApiBuilder},
48    historical::{HistoricalRpc, HistoricalRpcClient},
49    miner::{MinerApiExtServer, OpMinerExtApi},
50    witness::{DebugExecutionWitnessApiServer, OpDebugWitnessApi},
51    SequencerClient,
52};
53use reth_optimism_storage::OpStorage;
54use reth_optimism_txpool::{
55    supervisor::{SupervisorClient, DEFAULT_SUPERVISOR_URL},
56    OpPooledTx,
57};
58use reth_provider::{providers::ProviderFactoryBuilder, CanonStateSubscriptions};
59use reth_rpc_api::{eth::RpcTypes, DebugApiServer, L2EthApiExtServer};
60use reth_rpc_server_types::RethRpcModule;
61use reth_tracing::tracing::{debug, info};
62use reth_transaction_pool::{
63    blobstore::DiskFileBlobStore, EthPoolTransaction, PoolPooledTx, PoolTransaction,
64    TransactionPool, TransactionValidationTaskExecutor,
65};
66use reth_trie_common::KeccakKeyHasher;
67use serde::de::DeserializeOwned;
68use std::{marker::PhantomData, sync::Arc};
69use url::Url;
70
71/// Marker trait for Optimism node types with standard engine, chain spec, and primitives.
72pub trait OpNodeTypes:
73    NodeTypes<Payload = OpEngineTypes, ChainSpec: OpHardforks + Hardforks, Primitives = OpPrimitives>
74{
75}
76/// Blanket impl for all node types that conform to the Optimism spec.
77impl<N> OpNodeTypes for N where
78    N: NodeTypes<
79        Payload = OpEngineTypes,
80        ChainSpec: OpHardforks + Hardforks,
81        Primitives = OpPrimitives,
82    >
83{
84}
85
86/// Helper trait for Optimism node types with full configuration including storage and execution
87/// data.
88pub trait OpFullNodeTypes:
89    NodeTypes<
90    ChainSpec: OpHardforks,
91    Primitives: OpPayloadPrimitives,
92    Storage = OpStorage,
93    Payload: EngineTypes<ExecutionData = OpExecutionData>,
94>
95{
96}
97
98impl<N> OpFullNodeTypes for N where
99    N: NodeTypes<
100        ChainSpec: OpHardforks,
101        Primitives: OpPayloadPrimitives,
102        Storage = OpStorage,
103        Payload: EngineTypes<ExecutionData = OpExecutionData>,
104    >
105{
106}
107
108/// Type configuration for a regular Optimism node.
109#[derive(Debug, Default, Clone)]
110#[non_exhaustive]
111pub struct OpNode {
112    /// Additional Optimism args
113    pub args: RollupArgs,
114    /// Data availability configuration for the OP builder.
115    ///
116    /// Used to throttle the size of the data availability payloads (configured by the batcher via
117    /// the `miner_` api).
118    ///
119    /// By default no throttling is applied.
120    pub da_config: OpDAConfig,
121    /// Gas limit configuration for the OP builder.
122    /// Used to control the gas limit of the blocks produced by the OP builder.(configured by the
123    /// batcher via the `miner_` api)
124    pub gas_limit_config: OpGasLimitConfig,
125}
126
127/// A [`ComponentsBuilder`] with its generic arguments set to a stack of Optimism specific builders.
128pub type OpNodeComponentBuilder<Node, Payload = OpPayloadBuilder> = ComponentsBuilder<
129    Node,
130    OpPoolBuilder,
131    BasicPayloadServiceBuilder<Payload>,
132    OpNetworkBuilder,
133    OpExecutorBuilder,
134    OpConsensusBuilder,
135>;
136
137impl OpNode {
138    /// Creates a new instance of the Optimism node type.
139    pub fn new(args: RollupArgs) -> Self {
140        Self {
141            args,
142            da_config: OpDAConfig::default(),
143            gas_limit_config: OpGasLimitConfig::default(),
144        }
145    }
146
147    /// Configure the data availability configuration for the OP builder.
148    pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
149        self.da_config = da_config;
150        self
151    }
152
153    /// Configure the gas limit configuration for the OP builder.
154    pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
155        self.gas_limit_config = gas_limit_config;
156        self
157    }
158
159    /// Returns the components for the given [`RollupArgs`].
160    pub fn components<Node>(&self) -> OpNodeComponentBuilder<Node>
161    where
162        Node: FullNodeTypes<Types: OpNodeTypes>,
163    {
164        let RollupArgs { disable_txpool_gossip, compute_pending_block, discovery_v4, .. } =
165            self.args;
166        ComponentsBuilder::default()
167            .node_types::<Node>()
168            .pool(
169                OpPoolBuilder::default()
170                    .with_enable_tx_conditional(self.args.enable_tx_conditional)
171                    .with_supervisor(
172                        self.args.supervisor_http.clone(),
173                        self.args.supervisor_safety_level,
174                    ),
175            )
176            .executor(OpExecutorBuilder::default())
177            .payload(BasicPayloadServiceBuilder::new(
178                OpPayloadBuilder::new(compute_pending_block)
179                    .with_da_config(self.da_config.clone())
180                    .with_gas_limit_config(self.gas_limit_config.clone()),
181            ))
182            .network(OpNetworkBuilder::new(disable_txpool_gossip, !discovery_v4))
183            .consensus(OpConsensusBuilder::default())
184    }
185
186    /// Returns [`OpAddOnsBuilder`] with configured arguments.
187    pub fn add_ons_builder<NetworkT: RpcTypes>(&self) -> OpAddOnsBuilder<NetworkT> {
188        OpAddOnsBuilder::default()
189            .with_sequencer(self.args.sequencer.clone())
190            .with_sequencer_headers(self.args.sequencer_headers.clone())
191            .with_da_config(self.da_config.clone())
192            .with_gas_limit_config(self.gas_limit_config.clone())
193            .with_enable_tx_conditional(self.args.enable_tx_conditional)
194            .with_min_suggested_priority_fee(self.args.min_suggested_priority_fee)
195            .with_historical_rpc(self.args.historical_rpc.clone())
196            .with_flashblocks(self.args.flashblocks_url.clone())
197            .with_flashblock_consensus(self.args.flashblock_consensus)
198    }
199
200    /// Instantiates the [`ProviderFactoryBuilder`] for an opstack node.
201    ///
202    /// # Open a Providerfactory in read-only mode from a datadir
203    ///
204    /// See also: [`ProviderFactoryBuilder`] and
205    /// [`ReadOnlyConfig`](reth_provider::providers::ReadOnlyConfig).
206    ///
207    /// ```no_run
208    /// use reth_optimism_chainspec::BASE_MAINNET;
209    /// use reth_optimism_node::OpNode;
210    ///
211    /// let factory =
212    ///     OpNode::provider_factory_builder().open_read_only(BASE_MAINNET.clone(), "datadir").unwrap();
213    /// ```
214    ///
215    /// # Open a Providerfactory manually with all required components
216    ///
217    /// ```no_run
218    /// use reth_db::open_db_read_only;
219    /// use reth_optimism_chainspec::OpChainSpecBuilder;
220    /// use reth_optimism_node::OpNode;
221    /// use reth_provider::providers::{RocksDBProvider, StaticFileProvider};
222    /// use std::sync::Arc;
223    ///
224    /// let factory = OpNode::provider_factory_builder()
225    ///     .db(Arc::new(open_db_read_only("db", Default::default()).unwrap()))
226    ///     .chainspec(OpChainSpecBuilder::base_mainnet().build().into())
227    ///     .static_file(StaticFileProvider::read_only("db/static_files", false).unwrap())
228    ///     .rocksdb_provider(RocksDBProvider::builder("db/rocksdb").build().unwrap())
229    ///     .build_provider_factory();
230    /// ```
231    pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
232        ProviderFactoryBuilder::default()
233    }
234}
235
236impl<N> Node<N> for OpNode
237where
238    N: FullNodeTypes<Types: OpFullNodeTypes + OpNodeTypes>,
239{
240    type ComponentsBuilder = ComponentsBuilder<
241        N,
242        OpPoolBuilder,
243        BasicPayloadServiceBuilder<OpPayloadBuilder>,
244        OpNetworkBuilder,
245        OpExecutorBuilder,
246        OpConsensusBuilder,
247    >;
248
249    type AddOns = OpAddOns<
250        NodeAdapter<N, <Self::ComponentsBuilder as NodeComponentsBuilder<N>>::Components>,
251        OpEthApiBuilder,
252        OpEngineValidatorBuilder,
253        OpEngineApiBuilder<OpEngineValidatorBuilder>,
254        BasicEngineValidatorBuilder<OpEngineValidatorBuilder>,
255    >;
256
257    fn components_builder(&self) -> Self::ComponentsBuilder {
258        Self::components(self)
259    }
260
261    fn add_ons(&self) -> Self::AddOns {
262        self.add_ons_builder().build()
263    }
264}
265
266impl<N> DebugNode<N> for OpNode
267where
268    N: FullNodeComponents<Types = Self>,
269{
270    type RpcBlock = alloy_rpc_types_eth::Block<op_alloy_consensus::OpTxEnvelope>;
271
272    fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_node_api::BlockTy<Self> {
273        rpc_block.into_consensus()
274    }
275
276    fn local_payload_attributes_builder(
277        chain_spec: &Self::ChainSpec,
278    ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes> {
279        LocalPayloadAttributesBuilder::new(Arc::new(chain_spec.clone()))
280    }
281}
282
283impl NodeTypes for OpNode {
284    type Primitives = OpPrimitives;
285    type ChainSpec = OpChainSpec;
286    type Storage = OpStorage;
287    type Payload = OpEngineTypes;
288}
289
290/// Add-ons w.r.t. optimism.
291///
292/// This type provides optimism-specific addons to the node and exposes the RPC server and engine
293/// API.
294#[derive(Debug)]
295pub struct OpAddOns<
296    N: FullNodeComponents,
297    EthB: EthApiBuilder<N>,
298    PVB,
299    EB = OpEngineApiBuilder<PVB>,
300    EVB = BasicEngineValidatorBuilder<PVB>,
301    RpcMiddleware = Identity,
302> {
303    /// Rpc add-ons responsible for launching the RPC servers and instantiating the RPC handlers
304    /// and eth-api.
305    pub rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
306    /// Data availability configuration for the OP builder.
307    pub da_config: OpDAConfig,
308    /// Gas limit configuration for the OP builder.
309    pub gas_limit_config: OpGasLimitConfig,
310    /// Sequencer client, configured to forward submitted transactions to sequencer of given OP
311    /// network.
312    pub sequencer_url: Option<String>,
313    /// Headers to use for the sequencer client requests.
314    pub sequencer_headers: Vec<String>,
315    /// RPC endpoint for historical data.
316    ///
317    /// This can be used to forward pre-bedrock rpc requests (op-mainnet).
318    pub historical_rpc: Option<String>,
319    /// Enable transaction conditionals.
320    enable_tx_conditional: bool,
321    min_suggested_priority_fee: u64,
322}
323
324impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
325where
326    N: FullNodeComponents,
327    EthB: EthApiBuilder<N>,
328{
329    /// Creates a new instance from components.
330    #[allow(clippy::too_many_arguments)]
331    pub const fn new(
332        rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
333        da_config: OpDAConfig,
334        gas_limit_config: OpGasLimitConfig,
335        sequencer_url: Option<String>,
336        sequencer_headers: Vec<String>,
337        historical_rpc: Option<String>,
338        enable_tx_conditional: bool,
339        min_suggested_priority_fee: u64,
340    ) -> Self {
341        Self {
342            rpc_add_ons,
343            da_config,
344            gas_limit_config,
345            sequencer_url,
346            sequencer_headers,
347            historical_rpc,
348            enable_tx_conditional,
349            min_suggested_priority_fee,
350        }
351    }
352}
353
354impl<N> Default for OpAddOns<N, OpEthApiBuilder, OpEngineValidatorBuilder>
355where
356    N: FullNodeComponents<Types: OpNodeTypes>,
357    OpEthApiBuilder: EthApiBuilder<N>,
358{
359    fn default() -> Self {
360        Self::builder().build()
361    }
362}
363
364impl<N, NetworkT, RpcMiddleware>
365    OpAddOns<
366        N,
367        OpEthApiBuilder<NetworkT>,
368        OpEngineValidatorBuilder,
369        OpEngineApiBuilder<OpEngineValidatorBuilder>,
370        RpcMiddleware,
371    >
372where
373    N: FullNodeComponents<Types: OpNodeTypes>,
374    OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
375{
376    /// Build a [`OpAddOns`] using [`OpAddOnsBuilder`].
377    pub fn builder() -> OpAddOnsBuilder<NetworkT> {
378        OpAddOnsBuilder::default()
379    }
380}
381
382impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
383where
384    N: FullNodeComponents,
385    EthB: EthApiBuilder<N>,
386{
387    /// Maps the [`reth_node_builder::rpc::EngineApiBuilder`] builder type.
388    pub fn with_engine_api<T>(
389        self,
390        engine_api_builder: T,
391    ) -> OpAddOns<N, EthB, PVB, T, EVB, RpcMiddleware> {
392        let Self {
393            rpc_add_ons,
394            da_config,
395            gas_limit_config,
396            sequencer_url,
397            sequencer_headers,
398            historical_rpc,
399            enable_tx_conditional,
400            min_suggested_priority_fee,
401            ..
402        } = self;
403        OpAddOns::new(
404            rpc_add_ons.with_engine_api(engine_api_builder),
405            da_config,
406            gas_limit_config,
407            sequencer_url,
408            sequencer_headers,
409            historical_rpc,
410            enable_tx_conditional,
411            min_suggested_priority_fee,
412        )
413    }
414
415    /// Maps the [`PayloadValidatorBuilder`] builder type.
416    pub fn with_payload_validator<T>(
417        self,
418        payload_validator_builder: T,
419    ) -> OpAddOns<N, EthB, T, EB, EVB, RpcMiddleware> {
420        let Self {
421            rpc_add_ons,
422            da_config,
423            gas_limit_config,
424            sequencer_url,
425            sequencer_headers,
426            enable_tx_conditional,
427            min_suggested_priority_fee,
428            historical_rpc,
429            ..
430        } = self;
431        OpAddOns::new(
432            rpc_add_ons.with_payload_validator(payload_validator_builder),
433            da_config,
434            gas_limit_config,
435            sequencer_url,
436            sequencer_headers,
437            historical_rpc,
438            enable_tx_conditional,
439            min_suggested_priority_fee,
440        )
441    }
442
443    /// Sets the RPC middleware stack for processing RPC requests.
444    ///
445    /// This method configures a custom middleware stack that will be applied to all RPC requests
446    /// across HTTP, `WebSocket`, and IPC transports. The middleware is applied to the RPC service
447    /// layer, allowing you to intercept, modify, or enhance RPC request processing.
448    ///
449    /// See also [`RpcAddOns::with_rpc_middleware`].
450    pub fn with_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOns<N, EthB, PVB, EB, EVB, T> {
451        let Self {
452            rpc_add_ons,
453            da_config,
454            gas_limit_config,
455            sequencer_url,
456            sequencer_headers,
457            enable_tx_conditional,
458            min_suggested_priority_fee,
459            historical_rpc,
460            ..
461        } = self;
462        OpAddOns::new(
463            rpc_add_ons.with_rpc_middleware(rpc_middleware),
464            da_config,
465            gas_limit_config,
466            sequencer_url,
467            sequencer_headers,
468            historical_rpc,
469            enable_tx_conditional,
470            min_suggested_priority_fee,
471        )
472    }
473
474    /// Sets the hook that is run once the rpc server is started.
475    pub fn on_rpc_started<F>(mut self, hook: F) -> Self
476    where
477        F: FnOnce(RpcContext<'_, N, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
478            + Send
479            + 'static,
480    {
481        self.rpc_add_ons = self.rpc_add_ons.on_rpc_started(hook);
482        self
483    }
484
485    /// Sets the hook that is run to configure the rpc modules.
486    pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
487    where
488        F: FnOnce(RpcContext<'_, N, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
489    {
490        self.rpc_add_ons = self.rpc_add_ons.extend_rpc_modules(hook);
491        self
492    }
493}
494
495impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> NodeAddOns<N>
496    for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
497where
498    N: FullNodeComponents<
499        Types: NodeTypes<
500            ChainSpec: OpHardforks,
501            Primitives: OpPayloadPrimitives,
502            Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
503        >,
504        Evm: ConfigureEvm<
505            NextBlockEnvCtx: BuildNextEnv<
506                Attrs,
507                HeaderTy<N::Types>,
508                <N::Types as NodeTypes>::ChainSpec,
509            >,
510        >,
511        Pool: TransactionPool<Transaction: OpPooledTx>,
512    >,
513    EthB: EthApiBuilder<N>,
514    PVB: Send,
515    EB: EngineApiBuilder<N>,
516    EVB: EngineValidatorBuilder<N>,
517    RpcMiddleware: RethRpcMiddleware,
518    Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
519{
520    type Handle = RpcHandle<N, EthB::EthApi>;
521
522    async fn launch_add_ons(
523        self,
524        ctx: reth_node_api::AddOnsContext<'_, N>,
525    ) -> eyre::Result<Self::Handle> {
526        let Self {
527            rpc_add_ons,
528            da_config,
529            gas_limit_config,
530            sequencer_url,
531            sequencer_headers,
532            enable_tx_conditional,
533            historical_rpc,
534            ..
535        } = self;
536
537        let maybe_pre_bedrock_historical_rpc = historical_rpc
538            .and_then(|historical_rpc| {
539                ctx.node
540                    .provider()
541                    .chain_spec()
542                    .op_fork_activation(OpHardfork::Bedrock)
543                    .block_number()
544                    .filter(|activation| *activation > 0)
545                    .map(|bedrock_block| (historical_rpc, bedrock_block))
546            })
547            .map(|(historical_rpc, bedrock_block)| -> eyre::Result<_> {
548                info!(target: "reth::cli", %bedrock_block, ?historical_rpc, "Using historical RPC endpoint pre bedrock");
549                let provider = ctx.node.provider().clone();
550                let client = HistoricalRpcClient::new(&historical_rpc)?;
551                let layer = HistoricalRpc::new(provider, client, bedrock_block);
552                Ok(layer)
553            })
554            .transpose()?
555            ;
556
557        let rpc_add_ons = rpc_add_ons.option_layer_rpc_middleware(maybe_pre_bedrock_historical_rpc);
558
559        let builder = reth_optimism_payload_builder::OpPayloadBuilder::new(
560            ctx.node.pool().clone(),
561            ctx.node.provider().clone(),
562            ctx.node.evm_config().clone(),
563        );
564        // install additional OP specific rpc methods
565        let debug_ext = OpDebugWitnessApi::<_, _, _, Attrs>::new(
566            ctx.node.provider().clone(),
567            Box::new(ctx.node.task_executor().clone()),
568            builder,
569        );
570        let miner_ext = OpMinerExtApi::new(da_config, gas_limit_config);
571
572        let sequencer_client = if let Some(url) = sequencer_url {
573            Some(SequencerClient::new_with_headers(url, sequencer_headers).await?)
574        } else {
575            None
576        };
577
578        let tx_conditional_ext: OpEthExtApi<N::Pool, N::Provider> = OpEthExtApi::new(
579            sequencer_client,
580            ctx.node.pool().clone(),
581            ctx.node.provider().clone(),
582        );
583
584        rpc_add_ons
585            .launch_add_ons_with(ctx, move |container| {
586                let reth_node_builder::rpc::RpcModuleContainer { modules, auth_module, registry } =
587                    container;
588
589                debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint");
590                modules.merge_if_module_configured(RethRpcModule::Debug, debug_ext.into_rpc())?;
591
592                // extend the miner namespace if configured in the regular http server
593                modules.add_or_replace_if_module_configured(
594                    RethRpcModule::Miner,
595                    miner_ext.clone().into_rpc(),
596                )?;
597
598                // install the miner extension in the authenticated if configured
599                if modules.module_config().contains_any(&RethRpcModule::Miner) {
600                    debug!(target: "reth::cli", "Installing miner DA rpc endpoint");
601                    auth_module.merge_auth_methods(miner_ext.into_rpc())?;
602                }
603
604                // install the debug namespace in the authenticated if configured
605                if modules.module_config().contains_any(&RethRpcModule::Debug) {
606                    debug!(target: "reth::cli", "Installing debug rpc endpoint");
607                    auth_module.merge_auth_methods(registry.debug_api().into_rpc())?;
608                }
609
610                if enable_tx_conditional {
611                    // extend the eth namespace if configured in the regular http server
612                    modules.merge_if_module_configured(
613                        RethRpcModule::Eth,
614                        tx_conditional_ext.into_rpc(),
615                    )?;
616                }
617
618                Ok(())
619            })
620            .await
621    }
622}
623
624impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> RethRpcAddOns<N>
625    for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
626where
627    N: FullNodeComponents<
628        Types: NodeTypes<
629            ChainSpec: OpHardforks,
630            Primitives: OpPayloadPrimitives,
631            Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
632        >,
633        Evm: ConfigureEvm<
634            NextBlockEnvCtx: BuildNextEnv<
635                Attrs,
636                HeaderTy<N::Types>,
637                <N::Types as NodeTypes>::ChainSpec,
638            >,
639        >,
640    >,
641    <<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: OpPooledTx,
642    EthB: EthApiBuilder<N>,
643    PVB: PayloadValidatorBuilder<N>,
644    EB: EngineApiBuilder<N>,
645    EVB: EngineValidatorBuilder<N>,
646    RpcMiddleware: RethRpcMiddleware,
647    Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
648{
649    type EthApi = EthB::EthApi;
650
651    fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
652        self.rpc_add_ons.hooks_mut()
653    }
654}
655
656impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EngineValidatorAddOn<N>
657    for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
658where
659    N: FullNodeComponents,
660    EthB: EthApiBuilder<N>,
661    PVB: Send,
662    EB: EngineApiBuilder<N>,
663    EVB: EngineValidatorBuilder<N>,
664    RpcMiddleware: Send,
665{
666    type ValidatorBuilder = EVB;
667
668    fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
669        EngineValidatorAddOn::engine_validator_builder(&self.rpc_add_ons)
670    }
671}
672
673/// A regular optimism evm and executor builder.
674#[derive(Debug, Clone)]
675#[non_exhaustive]
676pub struct OpAddOnsBuilder<NetworkT, RpcMiddleware = Identity> {
677    /// Sequencer client, configured to forward submitted transactions to sequencer of given OP
678    /// network.
679    sequencer_url: Option<String>,
680    /// Headers to use for the sequencer client requests.
681    sequencer_headers: Vec<String>,
682    /// RPC endpoint for historical data.
683    historical_rpc: Option<String>,
684    /// Data availability configuration for the OP builder.
685    da_config: Option<OpDAConfig>,
686    /// Gas limit configuration for the OP builder.
687    gas_limit_config: Option<OpGasLimitConfig>,
688    /// Enable transaction conditionals.
689    enable_tx_conditional: bool,
690    /// Marker for network types.
691    _nt: PhantomData<NetworkT>,
692    /// Minimum suggested priority fee (tip)
693    min_suggested_priority_fee: u64,
694    /// RPC middleware to use
695    rpc_middleware: RpcMiddleware,
696    /// Optional tokio runtime to use for the RPC server.
697    tokio_runtime: Option<tokio::runtime::Handle>,
698    /// A URL pointing to a secure websocket service that streams out flashblocks.
699    flashblocks_url: Option<Url>,
700    /// Enable flashblock consensus client to drive chain forward.
701    flashblock_consensus: bool,
702}
703
704impl<NetworkT> Default for OpAddOnsBuilder<NetworkT> {
705    fn default() -> Self {
706        Self {
707            sequencer_url: None,
708            sequencer_headers: Vec::new(),
709            historical_rpc: None,
710            da_config: None,
711            gas_limit_config: None,
712            enable_tx_conditional: false,
713            min_suggested_priority_fee: 1_000_000,
714            _nt: PhantomData,
715            rpc_middleware: Identity::new(),
716            tokio_runtime: None,
717            flashblocks_url: None,
718            flashblock_consensus: false,
719        }
720    }
721}
722
723impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
724    /// With a [`SequencerClient`].
725    pub fn with_sequencer(mut self, sequencer_client: Option<String>) -> Self {
726        self.sequencer_url = sequencer_client;
727        self
728    }
729
730    /// With headers to use for the sequencer client requests.
731    pub fn with_sequencer_headers(mut self, sequencer_headers: Vec<String>) -> Self {
732        self.sequencer_headers = sequencer_headers;
733        self
734    }
735
736    /// Configure the data availability configuration for the OP builder.
737    pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
738        self.da_config = Some(da_config);
739        self
740    }
741
742    /// Configure the gas limit configuration for the OP payload builder.
743    pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
744        self.gas_limit_config = Some(gas_limit_config);
745        self
746    }
747
748    /// Configure if transaction conditional should be enabled.
749    pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
750        self.enable_tx_conditional = enable_tx_conditional;
751        self
752    }
753
754    /// Configure the minimum priority fee (tip)
755    pub const fn with_min_suggested_priority_fee(mut self, min: u64) -> Self {
756        self.min_suggested_priority_fee = min;
757        self
758    }
759
760    /// Configures the endpoint for historical RPC forwarding.
761    pub fn with_historical_rpc(mut self, historical_rpc: Option<String>) -> Self {
762        self.historical_rpc = historical_rpc;
763        self
764    }
765
766    /// Configures a custom tokio runtime for the RPC server.
767    ///
768    /// Caution: This runtime must not be created from within asynchronous context.
769    pub fn with_tokio_runtime(mut self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
770        self.tokio_runtime = tokio_runtime;
771        self
772    }
773
774    /// Configure the RPC middleware to use
775    pub fn with_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOnsBuilder<NetworkT, T> {
776        let Self {
777            sequencer_url,
778            sequencer_headers,
779            historical_rpc,
780            da_config,
781            gas_limit_config,
782            enable_tx_conditional,
783            min_suggested_priority_fee,
784            tokio_runtime,
785            _nt,
786            flashblocks_url,
787            flashblock_consensus,
788            ..
789        } = self;
790        OpAddOnsBuilder {
791            sequencer_url,
792            sequencer_headers,
793            historical_rpc,
794            da_config,
795            gas_limit_config,
796            enable_tx_conditional,
797            min_suggested_priority_fee,
798            _nt,
799            rpc_middleware,
800            tokio_runtime,
801            flashblocks_url,
802            flashblock_consensus,
803        }
804    }
805
806    /// With a URL pointing to a flashblocks secure websocket subscription.
807    pub fn with_flashblocks(mut self, flashblocks_url: Option<Url>) -> Self {
808        self.flashblocks_url = flashblocks_url;
809        self
810    }
811
812    /// With a flashblock consensus client to drive chain forward.
813    pub const fn with_flashblock_consensus(mut self, flashblock_consensus: bool) -> Self {
814        self.flashblock_consensus = flashblock_consensus;
815        self
816    }
817}
818
819impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
820    /// Builds an instance of [`OpAddOns`].
821    pub fn build<N, PVB, EB, EVB>(
822        self,
823    ) -> OpAddOns<N, OpEthApiBuilder<NetworkT>, PVB, EB, EVB, RpcMiddleware>
824    where
825        N: FullNodeComponents<Types: NodeTypes>,
826        OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
827        PVB: PayloadValidatorBuilder<N> + Default,
828        EB: Default,
829        EVB: Default,
830    {
831        let Self {
832            sequencer_url,
833            sequencer_headers,
834            da_config,
835            gas_limit_config,
836            enable_tx_conditional,
837            min_suggested_priority_fee,
838            historical_rpc,
839            rpc_middleware,
840            tokio_runtime,
841            flashblocks_url,
842            flashblock_consensus,
843            ..
844        } = self;
845
846        OpAddOns::new(
847            RpcAddOns::new(
848                OpEthApiBuilder::default()
849                    .with_sequencer(sequencer_url.clone())
850                    .with_sequencer_headers(sequencer_headers.clone())
851                    .with_min_suggested_priority_fee(min_suggested_priority_fee)
852                    .with_flashblocks(flashblocks_url)
853                    .with_flashblock_consensus(flashblock_consensus),
854                PVB::default(),
855                EB::default(),
856                EVB::default(),
857                rpc_middleware,
858            )
859            .with_tokio_runtime(tokio_runtime),
860            da_config.unwrap_or_default(),
861            gas_limit_config.unwrap_or_default(),
862            sequencer_url,
863            sequencer_headers,
864            historical_rpc,
865            enable_tx_conditional,
866            min_suggested_priority_fee,
867        )
868    }
869}
870
871/// A regular optimism evm and executor builder.
872#[derive(Debug, Copy, Clone, Default)]
873#[non_exhaustive]
874pub struct OpExecutorBuilder;
875
876impl<Node> ExecutorBuilder<Node> for OpExecutorBuilder
877where
878    Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks, Primitives = OpPrimitives>>,
879{
880    type EVM =
881        OpEvmConfig<<Node::Types as NodeTypes>::ChainSpec, <Node::Types as NodeTypes>::Primitives>;
882
883    async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
884        let evm_config = OpEvmConfig::new(ctx.chain_spec(), OpRethReceiptBuilder::default());
885
886        Ok(evm_config)
887    }
888}
889
890/// A basic optimism transaction pool.
891///
892/// This contains various settings that can be configured and take precedence over the node's
893/// config.
894#[derive(Debug)]
895pub struct OpPoolBuilder<T = crate::txpool::OpPooledTransaction> {
896    /// Enforced overrides that are applied to the pool config.
897    pub pool_config_overrides: PoolBuilderConfigOverrides,
898    /// Enable transaction conditionals.
899    pub enable_tx_conditional: bool,
900    /// Supervisor client url
901    pub supervisor_http: String,
902    /// Supervisor safety level
903    pub supervisor_safety_level: SafetyLevel,
904    /// Marker for the pooled transaction type.
905    _pd: core::marker::PhantomData<T>,
906}
907
908impl<T> Default for OpPoolBuilder<T> {
909    fn default() -> Self {
910        Self {
911            pool_config_overrides: Default::default(),
912            enable_tx_conditional: false,
913            supervisor_http: DEFAULT_SUPERVISOR_URL.to_string(),
914            supervisor_safety_level: SafetyLevel::CrossUnsafe,
915            _pd: Default::default(),
916        }
917    }
918}
919
920impl<T> Clone for OpPoolBuilder<T> {
921    fn clone(&self) -> Self {
922        Self {
923            pool_config_overrides: self.pool_config_overrides.clone(),
924            enable_tx_conditional: self.enable_tx_conditional,
925            supervisor_http: self.supervisor_http.clone(),
926            supervisor_safety_level: self.supervisor_safety_level,
927            _pd: core::marker::PhantomData,
928        }
929    }
930}
931
932impl<T> OpPoolBuilder<T> {
933    /// Sets the `enable_tx_conditional` flag on the pool builder.
934    pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
935        self.enable_tx_conditional = enable_tx_conditional;
936        self
937    }
938
939    /// Sets the [`PoolBuilderConfigOverrides`] on the pool builder.
940    pub fn with_pool_config_overrides(
941        mut self,
942        pool_config_overrides: PoolBuilderConfigOverrides,
943    ) -> Self {
944        self.pool_config_overrides = pool_config_overrides;
945        self
946    }
947
948    /// Sets the supervisor client
949    pub fn with_supervisor(
950        mut self,
951        supervisor_client: String,
952        supervisor_safety_level: SafetyLevel,
953    ) -> Self {
954        self.supervisor_http = supervisor_client;
955        self.supervisor_safety_level = supervisor_safety_level;
956        self
957    }
958}
959
960impl<Node, T> PoolBuilder<Node> for OpPoolBuilder<T>
961where
962    Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks>>,
963    T: EthPoolTransaction<Consensus = TxTy<Node::Types>> + OpPooledTx,
964{
965    type Pool = OpTransactionPool<Node::Provider, DiskFileBlobStore, T>;
966
967    async fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> {
968        let Self { pool_config_overrides, .. } = self;
969
970        // supervisor used for interop
971        if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) &&
972            self.supervisor_http == DEFAULT_SUPERVISOR_URL
973        {
974            info!(target: "reth::cli",
975                url=%DEFAULT_SUPERVISOR_URL,
976                "Default supervisor url is used, consider changing --rollup.supervisor-http."
977            );
978        }
979        let supervisor_client = SupervisorClient::builder(self.supervisor_http.clone())
980            .minimum_safety(self.supervisor_safety_level)
981            .build()
982            .await;
983
984        let blob_store = reth_node_builder::components::create_blob_store(ctx)?;
985        let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone())
986            .no_eip4844()
987            .with_head_timestamp(ctx.head().timestamp)
988            .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
989            .kzg_settings(ctx.kzg_settings()?)
990            .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
991            .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
992            .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
993            .with_additional_tasks(
994                pool_config_overrides
995                    .additional_validation_tasks
996                    .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks),
997            )
998            .build_with_tasks(ctx.task_executor().clone(), blob_store.clone())
999            .map(|validator| {
1000                OpTransactionValidator::new(validator)
1001                    // In --dev mode we can't require gas fees because we're unable to decode
1002                    // the L1 block info
1003                    .require_l1_data_gas_fee(!ctx.config().dev.dev)
1004                    .with_supervisor(supervisor_client.clone())
1005            });
1006
1007        let final_pool_config = pool_config_overrides.apply(ctx.pool_config());
1008
1009        let transaction_pool = TxPoolBuilder::new(ctx)
1010            .with_validator(validator)
1011            .build_and_spawn_maintenance_task(blob_store, final_pool_config)?;
1012
1013        info!(target: "reth::cli", "Transaction pool initialized");
1014        debug!(target: "reth::cli", "Spawned txpool maintenance task");
1015
1016        // The Op txpool maintenance task is only spawned when interop is active
1017        if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) {
1018            // spawn the Op txpool maintenance task
1019            let chain_events = ctx.provider().canonical_state_stream();
1020            ctx.task_executor().spawn_critical(
1021                "Op txpool interop maintenance task",
1022                reth_optimism_txpool::maintain::maintain_transaction_pool_interop_future(
1023                    transaction_pool.clone(),
1024                    chain_events,
1025                    supervisor_client,
1026                ),
1027            );
1028            debug!(target: "reth::cli", "Spawned Op interop txpool maintenance task");
1029        }
1030
1031        if self.enable_tx_conditional {
1032            // spawn the Op txpool maintenance task
1033            let chain_events = ctx.provider().canonical_state_stream();
1034            ctx.task_executor().spawn_critical(
1035                "Op txpool conditional maintenance task",
1036                reth_optimism_txpool::maintain::maintain_transaction_pool_conditional_future(
1037                    transaction_pool.clone(),
1038                    chain_events,
1039                ),
1040            );
1041            debug!(target: "reth::cli", "Spawned Op conditional txpool maintenance task");
1042        }
1043
1044        Ok(transaction_pool)
1045    }
1046}
1047
1048/// A basic optimism payload service builder
1049#[derive(Debug, Default, Clone)]
1050pub struct OpPayloadBuilder<Txs = ()> {
1051    /// By default the pending block equals the latest block
1052    /// to save resources and not leak txs from the tx-pool,
1053    /// this flag enables computing of the pending block
1054    /// from the tx-pool instead.
1055    ///
1056    /// If `compute_pending_block` is not enabled, the payload builder
1057    /// will use the payload attributes from the latest block. Note
1058    /// that this flag is not yet functional.
1059    pub compute_pending_block: bool,
1060    /// The type responsible for yielding the best transactions for the payload if mempool
1061    /// transactions are allowed.
1062    pub best_transactions: Txs,
1063    /// This data availability configuration specifies constraints for the payload builder
1064    /// when assembling payloads
1065    pub da_config: OpDAConfig,
1066    /// Gas limit configuration for the OP builder.
1067    /// This is used to configure gas limit related constraints for the payload builder.
1068    pub gas_limit_config: OpGasLimitConfig,
1069}
1070
1071impl OpPayloadBuilder {
1072    /// Create a new instance with the given `compute_pending_block` flag and data availability
1073    /// config.
1074    pub fn new(compute_pending_block: bool) -> Self {
1075        Self {
1076            compute_pending_block,
1077            best_transactions: (),
1078            da_config: OpDAConfig::default(),
1079            gas_limit_config: OpGasLimitConfig::default(),
1080        }
1081    }
1082
1083    /// Configure the data availability configuration for the OP payload builder.
1084    pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
1085        self.da_config = da_config;
1086        self
1087    }
1088
1089    /// Configure the gas limit configuration for the OP payload builder.
1090    pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
1091        self.gas_limit_config = gas_limit_config;
1092        self
1093    }
1094}
1095
1096impl<Txs> OpPayloadBuilder<Txs> {
1097    /// Configures the type responsible for yielding the transactions that should be included in the
1098    /// payload.
1099    pub fn with_transactions<T>(self, best_transactions: T) -> OpPayloadBuilder<T> {
1100        let Self { compute_pending_block, da_config, gas_limit_config, .. } = self;
1101        OpPayloadBuilder { compute_pending_block, best_transactions, da_config, gas_limit_config }
1102    }
1103}
1104
1105impl<Node, Pool, Txs, Evm, Attrs> PayloadBuilderBuilder<Node, Pool, Evm> for OpPayloadBuilder<Txs>
1106where
1107    Node: FullNodeTypes<
1108        Provider: ChainSpecProvider<ChainSpec: OpHardforks>,
1109        Types: NodeTypes<
1110            Primitives: OpPayloadPrimitives,
1111            Payload: PayloadTypes<
1112                BuiltPayload = OpBuiltPayload<PrimitivesTy<Node::Types>>,
1113                PayloadBuilderAttributes = Attrs,
1114            >,
1115        >,
1116    >,
1117    Evm: ConfigureEvm<
1118            Primitives = PrimitivesTy<Node::Types>,
1119            NextBlockEnvCtx: BuildNextEnv<
1120                Attrs,
1121                HeaderTy<Node::Types>,
1122                <Node::Types as NodeTypes>::ChainSpec,
1123            >,
1124        > + 'static,
1125    Pool: TransactionPool<Transaction: OpPooledTx<Consensus = TxTy<Node::Types>>> + Unpin + 'static,
1126    Txs: OpPayloadTransactions<Pool::Transaction>,
1127    Attrs: OpAttributes<Transaction = TxTy<Node::Types>>,
1128{
1129    type PayloadBuilder =
1130        reth_optimism_payload_builder::OpPayloadBuilder<Pool, Node::Provider, Evm, Txs, Attrs>;
1131
1132    async fn build_payload_builder(
1133        self,
1134        ctx: &BuilderContext<Node>,
1135        pool: Pool,
1136        evm_config: Evm,
1137    ) -> eyre::Result<Self::PayloadBuilder> {
1138        let payload_builder = reth_optimism_payload_builder::OpPayloadBuilder::with_builder_config(
1139            pool,
1140            ctx.provider().clone(),
1141            evm_config,
1142            OpBuilderConfig {
1143                da_config: self.da_config.clone(),
1144                gas_limit_config: self.gas_limit_config.clone(),
1145            },
1146        )
1147        .with_transactions(self.best_transactions.clone())
1148        .set_compute_pending_block(self.compute_pending_block);
1149        Ok(payload_builder)
1150    }
1151}
1152
1153/// A basic optimism network builder.
1154#[derive(Debug, Default)]
1155pub struct OpNetworkBuilder {
1156    /// Disable transaction pool gossip
1157    pub disable_txpool_gossip: bool,
1158    /// Disable discovery v4
1159    pub disable_discovery_v4: bool,
1160}
1161
1162impl Clone for OpNetworkBuilder {
1163    fn clone(&self) -> Self {
1164        Self::new(self.disable_txpool_gossip, self.disable_discovery_v4)
1165    }
1166}
1167
1168impl OpNetworkBuilder {
1169    /// Creates a new `OpNetworkBuilder`.
1170    pub const fn new(disable_txpool_gossip: bool, disable_discovery_v4: bool) -> Self {
1171        Self { disable_txpool_gossip, disable_discovery_v4 }
1172    }
1173}
1174
1175impl OpNetworkBuilder {
1176    /// Returns the [`NetworkConfig`] that contains the settings to launch the p2p network.
1177    ///
1178    /// This applies the configured [`OpNetworkBuilder`] settings.
1179    pub fn network_config<Node, NetworkP>(
1180        &self,
1181        ctx: &BuilderContext<Node>,
1182    ) -> eyre::Result<NetworkConfig<Node::Provider, NetworkP>>
1183    where
1184        Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1185        NetworkP: NetworkPrimitives,
1186    {
1187        let disable_txpool_gossip = self.disable_txpool_gossip;
1188        let disable_discovery_v4 = self.disable_discovery_v4;
1189        let args = &ctx.config().network;
1190        let network_builder = ctx
1191            .network_config_builder()?
1192            // apply discovery settings
1193            .apply(|mut builder| {
1194                let rlpx_socket = (args.addr, args.port).into();
1195                if disable_discovery_v4 || args.discovery.disable_discovery {
1196                    builder = builder.disable_discv4_discovery();
1197                }
1198                if !args.discovery.disable_discovery {
1199                    builder = builder.discovery_v5(
1200                        args.discovery.discovery_v5_builder(
1201                            rlpx_socket,
1202                            ctx.config()
1203                                .network
1204                                .resolved_bootnodes()
1205                                .or_else(|| ctx.chain_spec().bootnodes())
1206                                .unwrap_or_default(),
1207                        ),
1208                    );
1209                }
1210
1211                builder
1212            });
1213
1214        let mut network_config = ctx.build_network_config(network_builder);
1215
1216        // When `sequencer_endpoint` is configured, the node will forward all transactions to a
1217        // Sequencer node for execution and inclusion on L1, and disable its own txpool
1218        // gossip to prevent other parties in the network from learning about them.
1219        network_config.tx_gossip_disabled = disable_txpool_gossip;
1220
1221        Ok(network_config)
1222    }
1223}
1224
1225impl<Node, Pool> NetworkBuilder<Node, Pool> for OpNetworkBuilder
1226where
1227    Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1228    Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
1229        + Unpin
1230        + 'static,
1231{
1232    type Network =
1233        NetworkHandle<BasicNetworkPrimitives<PrimitivesTy<Node::Types>, PoolPooledTx<Pool>>>;
1234
1235    async fn build_network(
1236        self,
1237        ctx: &BuilderContext<Node>,
1238        pool: Pool,
1239    ) -> eyre::Result<Self::Network> {
1240        let network_config = self.network_config(ctx)?;
1241        let network = NetworkManager::builder(network_config).await?;
1242        let handle = ctx.start_network(network, pool);
1243        info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized");
1244
1245        Ok(handle)
1246    }
1247}
1248
1249/// A basic optimism consensus builder.
1250#[derive(Debug, Default, Clone)]
1251#[non_exhaustive]
1252pub struct OpConsensusBuilder;
1253
1254impl<Node> ConsensusBuilder<Node> for OpConsensusBuilder
1255where
1256    Node: FullNodeTypes<
1257        Types: NodeTypes<
1258            ChainSpec: OpHardforks,
1259            Primitives: NodePrimitives<Receipt: DepositReceipt>,
1260        >,
1261    >,
1262{
1263    type Consensus = Arc<OpBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
1264
1265    async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
1266        Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
1267    }
1268}
1269
1270/// Builder for [`OpEngineValidator`].
1271#[derive(Debug, Default, Clone)]
1272#[non_exhaustive]
1273pub struct OpEngineValidatorBuilder;
1274
1275impl<Node> PayloadValidatorBuilder<Node> for OpEngineValidatorBuilder
1276where
1277    Node: FullNodeComponents<
1278        Types: NodeTypes<
1279            ChainSpec: OpHardforks,
1280            Payload: PayloadTypes<ExecutionData = OpExecutionData>,
1281        >,
1282    >,
1283{
1284    type Validator = OpEngineValidator<
1285        Node::Provider,
1286        <<Node::Types as NodeTypes>::Primitives as NodePrimitives>::SignedTx,
1287        <Node::Types as NodeTypes>::ChainSpec,
1288    >;
1289
1290    async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
1291        Ok(OpEngineValidator::new::<KeccakKeyHasher>(
1292            ctx.config.chain.clone(),
1293            ctx.node.provider().clone(),
1294        ))
1295    }
1296}
1297
1298/// Network primitive types used by Optimism networks.
1299pub type OpNetworkPrimitives = BasicNetworkPrimitives<OpPrimitives, OpPooledTransaction>;