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::OpPooledTransaction;
10use reth_chainspec::{EthChainSpec, Hardforks};
11use reth_evm::{execute::BasicBlockExecutorProvider, ConfigureEvm, EvmFactory, EvmFactoryFor};
12use reth_network::{NetworkConfig, NetworkHandle, NetworkManager, NetworkPrimitives, PeersInfo};
13use reth_node_api::{
14    AddOnsContext, FullNodeComponents, KeyHasherTy, NodeAddOns, NodePrimitives, PrimitivesTy, TxTy,
15};
16use reth_node_builder::{
17    components::{
18        BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
19        NetworkBuilder, PayloadBuilderBuilder, PoolBuilder, PoolBuilderConfigOverrides,
20    },
21    node::{FullNodeTypes, NodeTypes, NodeTypesWithEngine},
22    rpc::{
23        EngineValidatorAddOn, EngineValidatorBuilder, EthApiBuilder, RethRpcAddOns, RpcAddOns,
24        RpcHandle,
25    },
26    BuilderContext, DebugNode, Node, NodeAdapter, NodeComponentsBuilder,
27};
28use reth_optimism_chainspec::OpChainSpec;
29use reth_optimism_consensus::OpBeaconConsensus;
30use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes};
31use reth_optimism_forks::OpHardforks;
32use reth_optimism_payload_builder::{
33    builder::OpPayloadTransactions,
34    config::{OpBuilderConfig, OpDAConfig},
35};
36use reth_optimism_primitives::{DepositReceipt, OpPrimitives, OpReceipt, OpTransactionSigned};
37use reth_optimism_rpc::{
38    eth::{ext::OpEthExtApi, OpEthApiBuilder},
39    miner::{MinerApiExtServer, OpMinerExtApi},
40    witness::{DebugExecutionWitnessApiServer, OpDebugWitnessApi},
41    OpEthApi, OpEthApiError, SequencerClient,
42};
43use reth_optimism_txpool::{conditional::MaybeConditionalTransaction, OpPooledTx};
44use reth_provider::{providers::ProviderFactoryBuilder, CanonStateSubscriptions, EthStorage};
45use reth_rpc_eth_api::ext::L2EthApiExtServer;
46use reth_rpc_eth_types::error::FromEvmError;
47use reth_rpc_server_types::RethRpcModule;
48use reth_tracing::tracing::{debug, info};
49use reth_transaction_pool::{
50    blobstore::DiskFileBlobStore, CoinbaseTipOrdering, EthPoolTransaction, PoolTransaction,
51    TransactionPool, TransactionValidationTaskExecutor,
52};
53use reth_trie_db::MerklePatriciaTrie;
54use revm::context::TxEnv;
55use std::sync::Arc;
56
57/// Storage implementation for Optimism.
58pub type OpStorage = EthStorage<OpTransactionSigned>;
59
60/// Type configuration for a regular Optimism node.
61#[derive(Debug, Default, Clone)]
62#[non_exhaustive]
63pub struct OpNode {
64    /// Additional Optimism args
65    pub args: RollupArgs,
66    /// Data availability configuration for the OP builder.
67    ///
68    /// Used to throttle the size of the data availability payloads (configured by the batcher via
69    /// the `miner_` api).
70    ///
71    /// By default no throttling is applied.
72    pub da_config: OpDAConfig,
73}
74
75impl OpNode {
76    /// Creates a new instance of the Optimism node type.
77    pub fn new(args: RollupArgs) -> Self {
78        Self { args, da_config: OpDAConfig::default() }
79    }
80
81    /// Configure the data availability configuration for the OP builder.
82    pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
83        self.da_config = da_config;
84        self
85    }
86
87    /// Returns the components for the given [`RollupArgs`].
88    pub fn components<Node>(
89        &self,
90    ) -> ComponentsBuilder<
91        Node,
92        OpPoolBuilder,
93        BasicPayloadServiceBuilder<OpPayloadBuilder>,
94        OpNetworkBuilder,
95        OpExecutorBuilder,
96        OpConsensusBuilder,
97    >
98    where
99        Node: FullNodeTypes<
100            Types: NodeTypesWithEngine<
101                Engine = OpEngineTypes,
102                ChainSpec = OpChainSpec,
103                Primitives = OpPrimitives,
104            >,
105        >,
106    {
107        let RollupArgs { disable_txpool_gossip, compute_pending_block, discovery_v4, .. } =
108            self.args;
109        ComponentsBuilder::default()
110            .node_types::<Node>()
111            .pool(
112                OpPoolBuilder::default()
113                    .with_enable_tx_conditional(self.args.enable_tx_conditional),
114            )
115            .payload(BasicPayloadServiceBuilder::new(
116                OpPayloadBuilder::new(compute_pending_block).with_da_config(self.da_config.clone()),
117            ))
118            .network(OpNetworkBuilder {
119                disable_txpool_gossip,
120                disable_discovery_v4: !discovery_v4,
121            })
122            .executor(OpExecutorBuilder::default())
123            .consensus(OpConsensusBuilder::default())
124    }
125
126    /// Instantiates the [`ProviderFactoryBuilder`] for an opstack node.
127    ///
128    /// # Open a Providerfactory in read-only mode from a datadir
129    ///
130    /// See also: [`ProviderFactoryBuilder`] and
131    /// [`ReadOnlyConfig`](reth_provider::providers::ReadOnlyConfig).
132    ///
133    /// ```no_run
134    /// use reth_optimism_chainspec::BASE_MAINNET;
135    /// use reth_optimism_node::OpNode;
136    ///
137    /// let factory =
138    ///     OpNode::provider_factory_builder().open_read_only(BASE_MAINNET.clone(), "datadir").unwrap();
139    /// ```
140    ///
141    /// # Open a Providerfactory manually with with all required components
142    ///
143    /// ```no_run
144    /// use reth_db::open_db_read_only;
145    /// use reth_optimism_chainspec::OpChainSpecBuilder;
146    /// use reth_optimism_node::OpNode;
147    /// use reth_provider::providers::StaticFileProvider;
148    /// use std::sync::Arc;
149    ///
150    /// let factory = OpNode::provider_factory_builder()
151    ///     .db(Arc::new(open_db_read_only("db", Default::default()).unwrap()))
152    ///     .chainspec(OpChainSpecBuilder::base_mainnet().build().into())
153    ///     .static_file(StaticFileProvider::read_only("db/static_files", false).unwrap())
154    ///     .build_provider_factory();
155    /// ```
156    pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
157        ProviderFactoryBuilder::default()
158    }
159}
160
161impl<N> Node<N> for OpNode
162where
163    N: FullNodeTypes<
164        Types: NodeTypesWithEngine<
165            Engine = OpEngineTypes,
166            ChainSpec = OpChainSpec,
167            Primitives = OpPrimitives,
168            Storage = OpStorage,
169        >,
170    >,
171{
172    type ComponentsBuilder = ComponentsBuilder<
173        N,
174        OpPoolBuilder,
175        BasicPayloadServiceBuilder<OpPayloadBuilder>,
176        OpNetworkBuilder,
177        OpExecutorBuilder,
178        OpConsensusBuilder,
179    >;
180
181    type AddOns =
182        OpAddOns<NodeAdapter<N, <Self::ComponentsBuilder as NodeComponentsBuilder<N>>::Components>>;
183
184    fn components_builder(&self) -> Self::ComponentsBuilder {
185        Self::components(self)
186    }
187
188    fn add_ons(&self) -> Self::AddOns {
189        Self::AddOns::builder()
190            .with_sequencer(self.args.sequencer_http.clone())
191            .with_da_config(self.da_config.clone())
192            .with_enable_tx_conditional(self.args.enable_tx_conditional)
193            .build()
194    }
195}
196
197impl<N> DebugNode<N> for OpNode
198where
199    N: FullNodeComponents<Types = Self>,
200{
201    type RpcBlock = alloy_rpc_types_eth::Block<op_alloy_consensus::OpTxEnvelope>;
202
203    fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_node_api::BlockTy<Self> {
204        let alloy_rpc_types_eth::Block { header, transactions, .. } = rpc_block;
205        reth_optimism_primitives::OpBlock {
206            header: header.inner,
207            body: reth_optimism_primitives::OpBlockBody {
208                transactions: transactions.into_transactions().map(Into::into).collect(),
209                ..Default::default()
210            },
211        }
212    }
213}
214
215impl NodeTypes for OpNode {
216    type Primitives = OpPrimitives;
217    type ChainSpec = OpChainSpec;
218    type StateCommitment = MerklePatriciaTrie;
219    type Storage = OpStorage;
220}
221
222impl NodeTypesWithEngine for OpNode {
223    type Engine = OpEngineTypes;
224}
225
226/// Add-ons w.r.t. optimism.
227#[derive(Debug)]
228pub struct OpAddOns<N>
229where
230    N: FullNodeComponents,
231    OpEthApiBuilder: EthApiBuilder<N>,
232{
233    /// Rpc add-ons responsible for launching the RPC servers and instantiating the RPC handlers
234    /// and eth-api.
235    pub rpc_add_ons: RpcAddOns<
236        N,
237        OpEthApiBuilder,
238        OpEngineValidatorBuilder,
239        OpEngineApiBuilder<OpEngineValidatorBuilder>,
240    >,
241    /// Data availability configuration for the OP builder.
242    pub da_config: OpDAConfig,
243    /// Sequencer client, configured to forward submitted transactions to sequencer of given OP
244    /// network.
245    pub sequencer_client: Option<SequencerClient>,
246    /// Enable transaction conditionals.
247    enable_tx_conditional: bool,
248}
249
250impl<N> Default for OpAddOns<N>
251where
252    N: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>,
253    OpEthApiBuilder: EthApiBuilder<N>,
254{
255    fn default() -> Self {
256        Self::builder().build()
257    }
258}
259
260impl<N> OpAddOns<N>
261where
262    N: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>,
263    OpEthApiBuilder: EthApiBuilder<N>,
264{
265    /// Build a [`OpAddOns`] using [`OpAddOnsBuilder`].
266    pub fn builder() -> OpAddOnsBuilder {
267        OpAddOnsBuilder::default()
268    }
269}
270
271impl<N> NodeAddOns<N> for OpAddOns<N>
272where
273    N: FullNodeComponents<
274        Types: NodeTypesWithEngine<
275            ChainSpec = OpChainSpec,
276            Primitives = OpPrimitives,
277            Storage = OpStorage,
278            Engine = OpEngineTypes,
279        >,
280        Evm: ConfigureEvm<NextBlockEnvCtx = OpNextBlockEnvAttributes>,
281    >,
282    OpEthApiError: FromEvmError<N::Evm>,
283    <N::Pool as TransactionPool>::Transaction: OpPooledTx,
284    EvmFactoryFor<N::Evm>: EvmFactory<Tx = op_revm::OpTransaction<TxEnv>>,
285{
286    type Handle = RpcHandle<N, OpEthApi<N>>;
287
288    async fn launch_add_ons(
289        self,
290        ctx: reth_node_api::AddOnsContext<'_, N>,
291    ) -> eyre::Result<Self::Handle> {
292        let Self { rpc_add_ons, da_config, sequencer_client, enable_tx_conditional } = self;
293
294        let builder = reth_optimism_payload_builder::OpPayloadBuilder::new(
295            ctx.node.pool().clone(),
296            ctx.node.provider().clone(),
297            ctx.node.evm_config().clone(),
298        );
299        // install additional OP specific rpc methods
300        let debug_ext = OpDebugWitnessApi::new(
301            ctx.node.provider().clone(),
302            Box::new(ctx.node.task_executor().clone()),
303            builder,
304        );
305        let miner_ext = OpMinerExtApi::new(da_config);
306
307        let tx_conditional_ext: OpEthExtApi<N::Pool, N::Provider> = OpEthExtApi::new(
308            sequencer_client,
309            ctx.node.pool().clone(),
310            ctx.node.provider().clone(),
311        );
312        rpc_add_ons
313            .launch_add_ons_with(ctx, move |modules, auth_modules| {
314                debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint");
315                modules.merge_if_module_configured(RethRpcModule::Debug, debug_ext.into_rpc())?;
316
317                // extend the miner namespace if configured in the regular http server
318                modules.merge_if_module_configured(
319                    RethRpcModule::Miner,
320                    miner_ext.clone().into_rpc(),
321                )?;
322
323                // install the miner extension in the authenticated if configured
324                if modules.module_config().contains_any(&RethRpcModule::Miner) {
325                    debug!(target: "reth::cli", "Installing miner DA rpc enddpoint");
326                    auth_modules.merge_auth_methods(miner_ext.into_rpc())?;
327                }
328
329                if enable_tx_conditional {
330                    // extend the eth namespace if configured in the regular http server
331                    modules.merge_if_module_configured(
332                        RethRpcModule::Eth,
333                        tx_conditional_ext.into_rpc(),
334                    )?;
335                }
336
337                Ok(())
338            })
339            .await
340    }
341}
342
343impl<N> RethRpcAddOns<N> for OpAddOns<N>
344where
345    N: FullNodeComponents<
346        Types: NodeTypesWithEngine<
347            ChainSpec = OpChainSpec,
348            Primitives = OpPrimitives,
349            Storage = OpStorage,
350            Engine = OpEngineTypes,
351        >,
352        Evm: ConfigureEvm<NextBlockEnvCtx = OpNextBlockEnvAttributes>,
353    >,
354    OpEthApiError: FromEvmError<N::Evm>,
355    <<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: OpPooledTx,
356    EvmFactoryFor<N::Evm>: EvmFactory<Tx = op_revm::OpTransaction<TxEnv>>,
357{
358    type EthApi = OpEthApi<N>;
359
360    fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
361        self.rpc_add_ons.hooks_mut()
362    }
363}
364
365impl<N> EngineValidatorAddOn<N> for OpAddOns<N>
366where
367    N: FullNodeComponents<
368        Types: NodeTypesWithEngine<
369            ChainSpec = OpChainSpec,
370            Primitives = OpPrimitives,
371            Engine = OpEngineTypes,
372        >,
373    >,
374    OpEthApiBuilder: EthApiBuilder<N>,
375{
376    type Validator = OpEngineValidator<N::Provider>;
377
378    async fn engine_validator(&self, ctx: &AddOnsContext<'_, N>) -> eyre::Result<Self::Validator> {
379        OpEngineValidatorBuilder::default().build(ctx).await
380    }
381}
382
383/// A regular optimism evm and executor builder.
384#[derive(Debug, Default, Clone)]
385#[non_exhaustive]
386pub struct OpAddOnsBuilder {
387    /// Sequencer client, configured to forward submitted transactions to sequencer of given OP
388    /// network.
389    sequencer_client: Option<SequencerClient>,
390    /// Data availability configuration for the OP builder.
391    da_config: Option<OpDAConfig>,
392    /// Enable transaction conditionals.
393    enable_tx_conditional: bool,
394}
395
396impl OpAddOnsBuilder {
397    /// With a [`SequencerClient`].
398    pub fn with_sequencer(mut self, sequencer_client: Option<String>) -> Self {
399        self.sequencer_client = sequencer_client.map(SequencerClient::new);
400        self
401    }
402
403    /// Configure the data availability configuration for the OP builder.
404    pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
405        self.da_config = Some(da_config);
406        self
407    }
408
409    /// Configure if transaction conditional should be enabled.
410    pub fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
411        self.enable_tx_conditional = enable_tx_conditional;
412        self
413    }
414}
415
416impl OpAddOnsBuilder {
417    /// Builds an instance of [`OpAddOns`].
418    pub fn build<N>(self) -> OpAddOns<N>
419    where
420        N: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>,
421        OpEthApiBuilder: EthApiBuilder<N>,
422    {
423        let Self { sequencer_client, da_config, enable_tx_conditional } = self;
424
425        let sequencer_client_clone = sequencer_client.clone();
426        OpAddOns {
427            rpc_add_ons: RpcAddOns::new(
428                OpEthApiBuilder::default().with_sequencer(sequencer_client_clone),
429                Default::default(),
430                Default::default(),
431            ),
432            da_config: da_config.unwrap_or_default(),
433            sequencer_client,
434            enable_tx_conditional,
435        }
436    }
437}
438
439/// A regular optimism evm and executor builder.
440#[derive(Debug, Default, Clone, Copy)]
441#[non_exhaustive]
442pub struct OpExecutorBuilder;
443
444impl<Node> ExecutorBuilder<Node> for OpExecutorBuilder
445where
446    Node: FullNodeTypes<Types: NodeTypes<ChainSpec = OpChainSpec, Primitives = OpPrimitives>>,
447{
448    type EVM = OpEvmConfig;
449    type Executor = BasicBlockExecutorProvider<Self::EVM>;
450
451    async fn build_evm(
452        self,
453        ctx: &BuilderContext<Node>,
454    ) -> eyre::Result<(Self::EVM, Self::Executor)> {
455        let evm_config = OpEvmConfig::optimism(ctx.chain_spec());
456        let executor = BasicBlockExecutorProvider::new(evm_config.clone());
457
458        Ok((evm_config, executor))
459    }
460}
461
462/// A basic optimism transaction pool.
463///
464/// This contains various settings that can be configured and take precedence over the node's
465/// config.
466#[derive(Debug, Clone)]
467pub struct OpPoolBuilder<T = crate::txpool::OpPooledTransaction> {
468    /// Enforced overrides that are applied to the pool config.
469    pub pool_config_overrides: PoolBuilderConfigOverrides,
470    /// Enable transaction conditionals.
471    pub enable_tx_conditional: bool,
472    /// Marker for the pooled transaction type.
473    _pd: core::marker::PhantomData<T>,
474}
475
476impl<T> Default for OpPoolBuilder<T> {
477    fn default() -> Self {
478        Self {
479            pool_config_overrides: Default::default(),
480            enable_tx_conditional: false,
481            _pd: Default::default(),
482        }
483    }
484}
485
486impl<T> OpPoolBuilder<T> {
487    /// Sets the `enable_tx_conditional` flag on the pool builder.
488    pub fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
489        self.enable_tx_conditional = enable_tx_conditional;
490        self
491    }
492
493    /// Sets the [`PoolBuilderConfigOverrides`] on the pool builder.
494    pub fn with_pool_config_overrides(
495        mut self,
496        pool_config_overrides: PoolBuilderConfigOverrides,
497    ) -> Self {
498        self.pool_config_overrides = pool_config_overrides;
499        self
500    }
501}
502
503impl<Node, T> PoolBuilder<Node> for OpPoolBuilder<T>
504where
505    Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks>>,
506    T: EthPoolTransaction<Consensus = TxTy<Node::Types>> + MaybeConditionalTransaction,
507{
508    type Pool = OpTransactionPool<Node::Provider, DiskFileBlobStore, T>;
509
510    async fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> {
511        let Self { pool_config_overrides, .. } = self;
512        let data_dir = ctx.config().datadir();
513        let blob_store = DiskFileBlobStore::open(data_dir.blobstore(), Default::default())?;
514
515        let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone())
516            .no_eip4844()
517            .with_head_timestamp(ctx.head().timestamp)
518            .kzg_settings(ctx.kzg_settings()?)
519            .with_additional_tasks(
520                pool_config_overrides
521                    .additional_validation_tasks
522                    .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks),
523            )
524            .build_with_tasks(ctx.task_executor().clone(), blob_store.clone())
525            .map(|validator| {
526                OpTransactionValidator::new(validator)
527                    // In --dev mode we can't require gas fees because we're unable to decode
528                    // the L1 block info
529                    .require_l1_data_gas_fee(!ctx.config().dev.dev)
530            });
531
532        let transaction_pool = reth_transaction_pool::Pool::new(
533            validator,
534            CoinbaseTipOrdering::default(),
535            blob_store,
536            pool_config_overrides.apply(ctx.pool_config()),
537        );
538        info!(target: "reth::cli", "Transaction pool initialized");
539        let transactions_path = data_dir.txpool_transactions();
540
541        // spawn txpool maintenance tasks
542        {
543            let pool = transaction_pool.clone();
544            let chain_events = ctx.provider().canonical_state_stream();
545            let client = ctx.provider().clone();
546            let transactions_backup_config =
547                reth_transaction_pool::maintain::LocalTransactionBackupConfig::with_local_txs_backup(transactions_path);
548
549            ctx.task_executor().spawn_critical_with_graceful_shutdown_signal(
550                "local transactions backup task",
551                |shutdown| {
552                    reth_transaction_pool::maintain::backup_local_transactions_task(
553                        shutdown,
554                        pool.clone(),
555                        transactions_backup_config,
556                    )
557                },
558            );
559
560            // spawn the main maintenance task
561            ctx.task_executor().spawn_critical(
562                "txpool maintenance task",
563                reth_transaction_pool::maintain::maintain_transaction_pool_future(
564                    client,
565                    pool.clone(),
566                    chain_events,
567                    ctx.task_executor().clone(),
568                    reth_transaction_pool::maintain::MaintainPoolConfig {
569                        max_tx_lifetime: pool.config().max_queued_lifetime,
570                        ..Default::default()
571                    },
572                ),
573            );
574            debug!(target: "reth::cli", "Spawned txpool maintenance task");
575
576            if self.enable_tx_conditional {
577                // spawn the Op txpool maintenance task
578                let chain_events = ctx.provider().canonical_state_stream();
579                ctx.task_executor().spawn_critical(
580                    "Op txpool maintenance task",
581                    reth_optimism_txpool::maintain::maintain_transaction_pool_future(
582                        pool,
583                        chain_events,
584                    ),
585                );
586                debug!(target: "reth::cli", "Spawned Op txpool maintenance task");
587            }
588        }
589
590        Ok(transaction_pool)
591    }
592}
593
594/// A basic optimism payload service builder
595#[derive(Debug, Default, Clone)]
596pub struct OpPayloadBuilder<Txs = ()> {
597    /// By default the pending block equals the latest block
598    /// to save resources and not leak txs from the tx-pool,
599    /// this flag enables computing of the pending block
600    /// from the tx-pool instead.
601    ///
602    /// If `compute_pending_block` is not enabled, the payload builder
603    /// will use the payload attributes from the latest block. Note
604    /// that this flag is not yet functional.
605    pub compute_pending_block: bool,
606    /// The type responsible for yielding the best transactions for the payload if mempool
607    /// transactions are allowed.
608    pub best_transactions: Txs,
609    /// This data availability configuration specifies constraints for the payload builder
610    /// when assembling payloads
611    pub da_config: OpDAConfig,
612}
613
614impl OpPayloadBuilder {
615    /// Create a new instance with the given `compute_pending_block` flag and data availability
616    /// config.
617    pub fn new(compute_pending_block: bool) -> Self {
618        Self { compute_pending_block, best_transactions: (), da_config: OpDAConfig::default() }
619    }
620
621    /// Configure the data availability configuration for the OP payload builder.
622    pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
623        self.da_config = da_config;
624        self
625    }
626}
627
628impl<Txs> OpPayloadBuilder<Txs> {
629    /// Configures the type responsible for yielding the transactions that should be included in the
630    /// payload.
631    pub fn with_transactions<T>(self, best_transactions: T) -> OpPayloadBuilder<T> {
632        let Self { compute_pending_block, da_config, .. } = self;
633        OpPayloadBuilder { compute_pending_block, best_transactions, da_config }
634    }
635
636    /// A helper method to initialize [`reth_optimism_payload_builder::OpPayloadBuilder`] with the
637    /// given EVM config.
638    pub fn build<Node, Evm, Pool>(
639        self,
640        evm_config: Evm,
641        ctx: &BuilderContext<Node>,
642        pool: Pool,
643    ) -> eyre::Result<reth_optimism_payload_builder::OpPayloadBuilder<Pool, Node::Provider, Evm, Txs>>
644    where
645        Node: FullNodeTypes<
646            Types: NodeTypesWithEngine<
647                Engine = OpEngineTypes,
648                ChainSpec = OpChainSpec,
649                Primitives = OpPrimitives,
650            >,
651        >,
652        Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
653            + Unpin
654            + 'static,
655        Evm: ConfigureEvm<Primitives = PrimitivesTy<Node::Types>>,
656        Txs: OpPayloadTransactions<Pool::Transaction>,
657    {
658        let payload_builder = reth_optimism_payload_builder::OpPayloadBuilder::with_builder_config(
659            pool,
660            ctx.provider().clone(),
661            evm_config,
662            OpBuilderConfig { da_config: self.da_config.clone() },
663        )
664        .with_transactions(self.best_transactions.clone())
665        .set_compute_pending_block(self.compute_pending_block);
666        Ok(payload_builder)
667    }
668}
669
670impl<Node, Pool, Txs> PayloadBuilderBuilder<Node, Pool> for OpPayloadBuilder<Txs>
671where
672    Node: FullNodeTypes<
673        Types: NodeTypesWithEngine<
674            Engine = OpEngineTypes,
675            ChainSpec = OpChainSpec,
676            Primitives = OpPrimitives,
677        >,
678    >,
679    Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
680        + Unpin
681        + 'static,
682    Txs: OpPayloadTransactions<Pool::Transaction>,
683    <Pool as TransactionPool>::Transaction: OpPooledTx,
684{
685    type PayloadBuilder =
686        reth_optimism_payload_builder::OpPayloadBuilder<Pool, Node::Provider, OpEvmConfig, Txs>;
687
688    async fn build_payload_builder(
689        self,
690        ctx: &BuilderContext<Node>,
691        pool: Pool,
692    ) -> eyre::Result<Self::PayloadBuilder> {
693        self.build(OpEvmConfig::optimism(ctx.chain_spec()), ctx, pool)
694    }
695}
696
697/// A basic optimism network builder.
698#[derive(Debug, Default, Clone)]
699pub struct OpNetworkBuilder {
700    /// Disable transaction pool gossip
701    pub disable_txpool_gossip: bool,
702    /// Disable discovery v4
703    pub disable_discovery_v4: bool,
704}
705
706impl OpNetworkBuilder {
707    /// Returns the [`NetworkConfig`] that contains the settings to launch the p2p network.
708    ///
709    /// This applies the configured [`OpNetworkBuilder`] settings.
710    pub fn network_config<Node>(
711        &self,
712        ctx: &BuilderContext<Node>,
713    ) -> eyre::Result<NetworkConfig<<Node as FullNodeTypes>::Provider, OpNetworkPrimitives>>
714    where
715        Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
716    {
717        let Self { disable_txpool_gossip, disable_discovery_v4 } = self.clone();
718        let args = &ctx.config().network;
719        let network_builder = ctx
720            .network_config_builder()?
721            // apply discovery settings
722            .apply(|mut builder| {
723                let rlpx_socket = (args.addr, args.port).into();
724                if disable_discovery_v4 || args.discovery.disable_discovery {
725                    builder = builder.disable_discv4_discovery();
726                }
727                if !args.discovery.disable_discovery {
728                    builder = builder.discovery_v5(
729                        args.discovery.discovery_v5_builder(
730                            rlpx_socket,
731                            ctx.config()
732                                .network
733                                .resolved_bootnodes()
734                                .or_else(|| ctx.chain_spec().bootnodes())
735                                .unwrap_or_default(),
736                        ),
737                    );
738                }
739
740                builder
741            });
742
743        let mut network_config = ctx.build_network_config(network_builder);
744
745        // When `sequencer_endpoint` is configured, the node will forward all transactions to a
746        // Sequencer node for execution and inclusion on L1, and disable its own txpool
747        // gossip to prevent other parties in the network from learning about them.
748        network_config.tx_gossip_disabled = disable_txpool_gossip;
749
750        Ok(network_config)
751    }
752}
753
754impl<Node, Pool> NetworkBuilder<Node, Pool> for OpNetworkBuilder
755where
756    Node: FullNodeTypes<Types: NodeTypes<ChainSpec = OpChainSpec, Primitives = OpPrimitives>>,
757    Pool: TransactionPool<
758            Transaction: PoolTransaction<
759                Consensus = TxTy<Node::Types>,
760                Pooled = OpPooledTransaction,
761            >,
762        > + Unpin
763        + 'static,
764{
765    type Primitives = OpNetworkPrimitives;
766
767    async fn build_network(
768        self,
769        ctx: &BuilderContext<Node>,
770        pool: Pool,
771    ) -> eyre::Result<NetworkHandle<Self::Primitives>> {
772        let network_config = self.network_config(ctx)?;
773        let network = NetworkManager::builder(network_config).await?;
774        let handle = ctx.start_network(network, pool);
775        info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized");
776
777        Ok(handle)
778    }
779}
780
781/// A basic optimism consensus builder.
782#[derive(Debug, Default, Clone)]
783#[non_exhaustive]
784pub struct OpConsensusBuilder;
785
786impl<Node> ConsensusBuilder<Node> for OpConsensusBuilder
787where
788    Node: FullNodeTypes<
789        Types: NodeTypes<
790            ChainSpec: OpHardforks,
791            Primitives: NodePrimitives<Receipt: DepositReceipt>,
792        >,
793    >,
794{
795    type Consensus = Arc<OpBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
796
797    async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
798        Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
799    }
800}
801
802/// Builder for [`OpEngineValidator`].
803#[derive(Debug, Default, Clone)]
804#[non_exhaustive]
805pub struct OpEngineValidatorBuilder;
806
807impl<Node, Types> EngineValidatorBuilder<Node> for OpEngineValidatorBuilder
808where
809    Types: NodeTypesWithEngine<
810        ChainSpec = OpChainSpec,
811        Primitives = OpPrimitives,
812        Engine = OpEngineTypes,
813    >,
814    Node: FullNodeComponents<Types = Types>,
815{
816    type Validator = OpEngineValidator<Node::Provider>;
817
818    async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
819        Ok(OpEngineValidator::new::<KeyHasherTy<Types>>(
820            ctx.config.chain.clone(),
821            ctx.node.provider().clone(),
822        ))
823    }
824}
825
826/// Network primitive types used by Optimism networks.
827#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
828#[non_exhaustive]
829pub struct OpNetworkPrimitives;
830
831impl NetworkPrimitives for OpNetworkPrimitives {
832    type BlockHeader = alloy_consensus::Header;
833    type BlockBody = reth_primitives::BlockBody<OpTransactionSigned>;
834    type Block = reth_primitives::Block<OpTransactionSigned>;
835    type BroadcastedTransaction = OpTransactionSigned;
836    type PooledTransaction = OpPooledTransaction;
837    type Receipt = OpReceipt;
838}