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