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