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