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