1use crate::{
4 args::RollupArgs,
5 engine::OpEngineValidator,
6 txpool::{OpTransactionPool, OpTransactionValidator},
7 OpEngineApiBuilder, OpEngineTypes,
8};
9use op_alloy_consensus::{interop::SafetyLevel, OpPooledTransaction};
10use op_alloy_rpc_types_engine::OpExecutionData;
11use reth_chainspec::{ChainSpecProvider, EthChainSpec, Hardforks};
12use reth_engine_local::LocalPayloadAttributesBuilder;
13use reth_evm::ConfigureEvm;
14use reth_network::{
15 types::BasicNetworkPrimitives, NetworkConfig, NetworkHandle, NetworkManager, NetworkPrimitives,
16 PeersInfo,
17};
18use reth_node_api::{
19 AddOnsContext, BuildNextEnv, EngineTypes, FullNodeComponents, HeaderTy, NodeAddOns,
20 NodePrimitives, PayloadAttributesBuilder, PayloadTypes, PrimitivesTy, TxTy,
21};
22use reth_node_builder::{
23 components::{
24 BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder,
25 NetworkBuilder, PayloadBuilderBuilder, PoolBuilder, PoolBuilderConfigOverrides,
26 TxPoolBuilder,
27 },
28 node::{FullNodeTypes, NodeTypes},
29 rpc::{
30 BasicEngineValidatorBuilder, EngineApiBuilder, EngineValidatorAddOn,
31 EngineValidatorBuilder, EthApiBuilder, Identity, PayloadValidatorBuilder, RethRpcAddOns,
32 RethRpcMiddleware, RethRpcServerHandles, RpcAddOns, RpcContext, RpcHandle,
33 },
34 BuilderContext, DebugNode, Node, NodeAdapter, NodeComponentsBuilder,
35};
36use reth_optimism_chainspec::{OpChainSpec, OpHardfork};
37use reth_optimism_consensus::OpBeaconConsensus;
38use reth_optimism_evm::{OpEvmConfig, OpRethReceiptBuilder};
39use reth_optimism_forks::OpHardforks;
40use reth_optimism_payload_builder::{
41 builder::OpPayloadTransactions,
42 config::{OpBuilderConfig, OpDAConfig, OpGasLimitConfig},
43 OpAttributes, OpBuiltPayload, OpPayloadPrimitives,
44};
45use reth_optimism_primitives::{DepositReceipt, OpPrimitives};
46use reth_optimism_rpc::{
47 eth::{ext::OpEthExtApi, OpEthApiBuilder},
48 historical::{HistoricalRpc, HistoricalRpcClient},
49 miner::{MinerApiExtServer, OpMinerExtApi},
50 witness::{DebugExecutionWitnessApiServer, OpDebugWitnessApi},
51 SequencerClient,
52};
53use reth_optimism_storage::OpStorage;
54use reth_optimism_txpool::{
55 supervisor::{SupervisorClient, DEFAULT_SUPERVISOR_URL},
56 OpPooledTx,
57};
58use reth_provider::{providers::ProviderFactoryBuilder, CanonStateSubscriptions};
59use reth_rpc_api::{eth::RpcTypes, DebugApiServer, L2EthApiExtServer};
60use reth_rpc_server_types::RethRpcModule;
61use reth_tracing::tracing::{debug, info};
62use reth_transaction_pool::{
63 blobstore::DiskFileBlobStore, EthPoolTransaction, PoolPooledTx, PoolTransaction,
64 TransactionPool, TransactionValidationTaskExecutor,
65};
66use reth_trie_common::KeccakKeyHasher;
67use serde::de::DeserializeOwned;
68use std::{marker::PhantomData, sync::Arc};
69use url::Url;
70
71pub trait OpNodeTypes:
73 NodeTypes<Payload = OpEngineTypes, ChainSpec: OpHardforks + Hardforks, Primitives = OpPrimitives>
74{
75}
76impl<N> OpNodeTypes for N where
78 N: NodeTypes<
79 Payload = OpEngineTypes,
80 ChainSpec: OpHardforks + Hardforks,
81 Primitives = OpPrimitives,
82 >
83{
84}
85
86pub 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#[derive(Debug, Default, Clone)]
110#[non_exhaustive]
111pub struct OpNode {
112 pub args: RollupArgs,
114 pub da_config: OpDAConfig,
121 pub gas_limit_config: OpGasLimitConfig,
125}
126
127pub type OpNodeComponentBuilder<Node, Payload = OpPayloadBuilder> = ComponentsBuilder<
129 Node,
130 OpPoolBuilder,
131 BasicPayloadServiceBuilder<Payload>,
132 OpNetworkBuilder,
133 OpExecutorBuilder,
134 OpConsensusBuilder,
135>;
136
137impl OpNode {
138 pub fn new(args: RollupArgs) -> Self {
140 Self {
141 args,
142 da_config: OpDAConfig::default(),
143 gas_limit_config: OpGasLimitConfig::default(),
144 }
145 }
146
147 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
149 self.da_config = da_config;
150 self
151 }
152
153 pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
155 self.gas_limit_config = gas_limit_config;
156 self
157 }
158
159 pub fn components<Node>(&self) -> OpNodeComponentBuilder<Node>
161 where
162 Node: FullNodeTypes<Types: OpNodeTypes>,
163 {
164 let RollupArgs { disable_txpool_gossip, compute_pending_block, discovery_v4, .. } =
165 self.args;
166 ComponentsBuilder::default()
167 .node_types::<Node>()
168 .pool(
169 OpPoolBuilder::default()
170 .with_enable_tx_conditional(self.args.enable_tx_conditional)
171 .with_supervisor(
172 self.args.supervisor_http.clone(),
173 self.args.supervisor_safety_level,
174 ),
175 )
176 .executor(OpExecutorBuilder::default())
177 .payload(BasicPayloadServiceBuilder::new(
178 OpPayloadBuilder::new(compute_pending_block)
179 .with_da_config(self.da_config.clone())
180 .with_gas_limit_config(self.gas_limit_config.clone()),
181 ))
182 .network(OpNetworkBuilder::new(disable_txpool_gossip, !discovery_v4))
183 .consensus(OpConsensusBuilder::default())
184 }
185
186 pub fn add_ons_builder<NetworkT: RpcTypes>(&self) -> OpAddOnsBuilder<NetworkT> {
188 OpAddOnsBuilder::default()
189 .with_sequencer(self.args.sequencer.clone())
190 .with_sequencer_headers(self.args.sequencer_headers.clone())
191 .with_da_config(self.da_config.clone())
192 .with_gas_limit_config(self.gas_limit_config.clone())
193 .with_enable_tx_conditional(self.args.enable_tx_conditional)
194 .with_min_suggested_priority_fee(self.args.min_suggested_priority_fee)
195 .with_historical_rpc(self.args.historical_rpc.clone())
196 .with_flashblocks(self.args.flashblocks_url.clone())
197 .with_flashblock_consensus(self.args.flashblock_consensus)
198 }
199
200 pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
232 ProviderFactoryBuilder::default()
233 }
234}
235
236impl<N> Node<N> for OpNode
237where
238 N: FullNodeTypes<Types: OpFullNodeTypes + OpNodeTypes>,
239{
240 type ComponentsBuilder = ComponentsBuilder<
241 N,
242 OpPoolBuilder,
243 BasicPayloadServiceBuilder<OpPayloadBuilder>,
244 OpNetworkBuilder,
245 OpExecutorBuilder,
246 OpConsensusBuilder,
247 >;
248
249 type AddOns = OpAddOns<
250 NodeAdapter<N, <Self::ComponentsBuilder as NodeComponentsBuilder<N>>::Components>,
251 OpEthApiBuilder,
252 OpEngineValidatorBuilder,
253 OpEngineApiBuilder<OpEngineValidatorBuilder>,
254 BasicEngineValidatorBuilder<OpEngineValidatorBuilder>,
255 >;
256
257 fn components_builder(&self) -> Self::ComponentsBuilder {
258 Self::components(self)
259 }
260
261 fn add_ons(&self) -> Self::AddOns {
262 self.add_ons_builder().build()
263 }
264}
265
266impl<N> DebugNode<N> for OpNode
267where
268 N: FullNodeComponents<Types = Self>,
269{
270 type RpcBlock = alloy_rpc_types_eth::Block<op_alloy_consensus::OpTxEnvelope>;
271
272 fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_node_api::BlockTy<Self> {
273 rpc_block.into_consensus()
274 }
275
276 fn local_payload_attributes_builder(
277 chain_spec: &Self::ChainSpec,
278 ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes> {
279 LocalPayloadAttributesBuilder::new(Arc::new(chain_spec.clone()))
280 }
281}
282
283impl NodeTypes for OpNode {
284 type Primitives = OpPrimitives;
285 type ChainSpec = OpChainSpec;
286 type Storage = OpStorage;
287 type Payload = OpEngineTypes;
288}
289
290#[derive(Debug)]
295pub struct OpAddOns<
296 N: FullNodeComponents,
297 EthB: EthApiBuilder<N>,
298 PVB,
299 EB = OpEngineApiBuilder<PVB>,
300 EVB = BasicEngineValidatorBuilder<PVB>,
301 RpcMiddleware = Identity,
302> {
303 pub rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
306 pub da_config: OpDAConfig,
308 pub gas_limit_config: OpGasLimitConfig,
310 pub sequencer_url: Option<String>,
313 pub sequencer_headers: Vec<String>,
315 pub historical_rpc: Option<String>,
319 enable_tx_conditional: bool,
321 min_suggested_priority_fee: u64,
322}
323
324impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
325where
326 N: FullNodeComponents,
327 EthB: EthApiBuilder<N>,
328{
329 #[allow(clippy::too_many_arguments)]
331 pub const fn new(
332 rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
333 da_config: OpDAConfig,
334 gas_limit_config: OpGasLimitConfig,
335 sequencer_url: Option<String>,
336 sequencer_headers: Vec<String>,
337 historical_rpc: Option<String>,
338 enable_tx_conditional: bool,
339 min_suggested_priority_fee: u64,
340 ) -> Self {
341 Self {
342 rpc_add_ons,
343 da_config,
344 gas_limit_config,
345 sequencer_url,
346 sequencer_headers,
347 historical_rpc,
348 enable_tx_conditional,
349 min_suggested_priority_fee,
350 }
351 }
352}
353
354impl<N> Default for OpAddOns<N, OpEthApiBuilder, OpEngineValidatorBuilder>
355where
356 N: FullNodeComponents<Types: OpNodeTypes>,
357 OpEthApiBuilder: EthApiBuilder<N>,
358{
359 fn default() -> Self {
360 Self::builder().build()
361 }
362}
363
364impl<N, NetworkT, RpcMiddleware>
365 OpAddOns<
366 N,
367 OpEthApiBuilder<NetworkT>,
368 OpEngineValidatorBuilder,
369 OpEngineApiBuilder<OpEngineValidatorBuilder>,
370 RpcMiddleware,
371 >
372where
373 N: FullNodeComponents<Types: OpNodeTypes>,
374 OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
375{
376 pub fn builder() -> OpAddOnsBuilder<NetworkT> {
378 OpAddOnsBuilder::default()
379 }
380}
381
382impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
383where
384 N: FullNodeComponents,
385 EthB: EthApiBuilder<N>,
386{
387 pub fn with_engine_api<T>(
389 self,
390 engine_api_builder: T,
391 ) -> OpAddOns<N, EthB, PVB, T, EVB, RpcMiddleware> {
392 let Self {
393 rpc_add_ons,
394 da_config,
395 gas_limit_config,
396 sequencer_url,
397 sequencer_headers,
398 historical_rpc,
399 enable_tx_conditional,
400 min_suggested_priority_fee,
401 ..
402 } = self;
403 OpAddOns::new(
404 rpc_add_ons.with_engine_api(engine_api_builder),
405 da_config,
406 gas_limit_config,
407 sequencer_url,
408 sequencer_headers,
409 historical_rpc,
410 enable_tx_conditional,
411 min_suggested_priority_fee,
412 )
413 }
414
415 pub fn with_payload_validator<T>(
417 self,
418 payload_validator_builder: T,
419 ) -> OpAddOns<N, EthB, T, EB, EVB, RpcMiddleware> {
420 let Self {
421 rpc_add_ons,
422 da_config,
423 gas_limit_config,
424 sequencer_url,
425 sequencer_headers,
426 enable_tx_conditional,
427 min_suggested_priority_fee,
428 historical_rpc,
429 ..
430 } = self;
431 OpAddOns::new(
432 rpc_add_ons.with_payload_validator(payload_validator_builder),
433 da_config,
434 gas_limit_config,
435 sequencer_url,
436 sequencer_headers,
437 historical_rpc,
438 enable_tx_conditional,
439 min_suggested_priority_fee,
440 )
441 }
442
443 pub fn with_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOns<N, EthB, PVB, EB, EVB, T> {
451 let Self {
452 rpc_add_ons,
453 da_config,
454 gas_limit_config,
455 sequencer_url,
456 sequencer_headers,
457 enable_tx_conditional,
458 min_suggested_priority_fee,
459 historical_rpc,
460 ..
461 } = self;
462 OpAddOns::new(
463 rpc_add_ons.with_rpc_middleware(rpc_middleware),
464 da_config,
465 gas_limit_config,
466 sequencer_url,
467 sequencer_headers,
468 historical_rpc,
469 enable_tx_conditional,
470 min_suggested_priority_fee,
471 )
472 }
473
474 pub fn on_rpc_started<F>(mut self, hook: F) -> Self
476 where
477 F: FnOnce(RpcContext<'_, N, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
478 + Send
479 + 'static,
480 {
481 self.rpc_add_ons = self.rpc_add_ons.on_rpc_started(hook);
482 self
483 }
484
485 pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
487 where
488 F: FnOnce(RpcContext<'_, N, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
489 {
490 self.rpc_add_ons = self.rpc_add_ons.extend_rpc_modules(hook);
491 self
492 }
493}
494
495impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> NodeAddOns<N>
496 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
497where
498 N: FullNodeComponents<
499 Types: NodeTypes<
500 ChainSpec: OpHardforks,
501 Primitives: OpPayloadPrimitives,
502 Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
503 >,
504 Evm: ConfigureEvm<
505 NextBlockEnvCtx: BuildNextEnv<
506 Attrs,
507 HeaderTy<N::Types>,
508 <N::Types as NodeTypes>::ChainSpec,
509 >,
510 >,
511 Pool: TransactionPool<Transaction: OpPooledTx>,
512 >,
513 EthB: EthApiBuilder<N>,
514 PVB: Send,
515 EB: EngineApiBuilder<N>,
516 EVB: EngineValidatorBuilder<N>,
517 RpcMiddleware: RethRpcMiddleware,
518 Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
519{
520 type Handle = RpcHandle<N, EthB::EthApi>;
521
522 async fn launch_add_ons(
523 self,
524 ctx: reth_node_api::AddOnsContext<'_, N>,
525 ) -> eyre::Result<Self::Handle> {
526 let Self {
527 rpc_add_ons,
528 da_config,
529 gas_limit_config,
530 sequencer_url,
531 sequencer_headers,
532 enable_tx_conditional,
533 historical_rpc,
534 ..
535 } = self;
536
537 let maybe_pre_bedrock_historical_rpc = historical_rpc
538 .and_then(|historical_rpc| {
539 ctx.node
540 .provider()
541 .chain_spec()
542 .op_fork_activation(OpHardfork::Bedrock)
543 .block_number()
544 .filter(|activation| *activation > 0)
545 .map(|bedrock_block| (historical_rpc, bedrock_block))
546 })
547 .map(|(historical_rpc, bedrock_block)| -> eyre::Result<_> {
548 info!(target: "reth::cli", %bedrock_block, ?historical_rpc, "Using historical RPC endpoint pre bedrock");
549 let provider = ctx.node.provider().clone();
550 let client = HistoricalRpcClient::new(&historical_rpc)?;
551 let layer = HistoricalRpc::new(provider, client, bedrock_block);
552 Ok(layer)
553 })
554 .transpose()?
555 ;
556
557 let rpc_add_ons = rpc_add_ons.option_layer_rpc_middleware(maybe_pre_bedrock_historical_rpc);
558
559 let builder = reth_optimism_payload_builder::OpPayloadBuilder::new(
560 ctx.node.pool().clone(),
561 ctx.node.provider().clone(),
562 ctx.node.evm_config().clone(),
563 );
564 let debug_ext = OpDebugWitnessApi::<_, _, _, Attrs>::new(
566 ctx.node.provider().clone(),
567 Box::new(ctx.node.task_executor().clone()),
568 builder,
569 );
570 let miner_ext = OpMinerExtApi::new(da_config, gas_limit_config);
571
572 let sequencer_client = if let Some(url) = sequencer_url {
573 Some(SequencerClient::new_with_headers(url, sequencer_headers).await?)
574 } else {
575 None
576 };
577
578 let tx_conditional_ext: OpEthExtApi<N::Pool, N::Provider> = OpEthExtApi::new(
579 sequencer_client,
580 ctx.node.pool().clone(),
581 ctx.node.provider().clone(),
582 );
583
584 rpc_add_ons
585 .launch_add_ons_with(ctx, move |container| {
586 let reth_node_builder::rpc::RpcModuleContainer { modules, auth_module, registry } =
587 container;
588
589 debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint");
590 modules.merge_if_module_configured(RethRpcModule::Debug, debug_ext.into_rpc())?;
591
592 modules.add_or_replace_if_module_configured(
594 RethRpcModule::Miner,
595 miner_ext.clone().into_rpc(),
596 )?;
597
598 if modules.module_config().contains_any(&RethRpcModule::Miner) {
600 debug!(target: "reth::cli", "Installing miner DA rpc endpoint");
601 auth_module.merge_auth_methods(miner_ext.into_rpc())?;
602 }
603
604 if modules.module_config().contains_any(&RethRpcModule::Debug) {
606 debug!(target: "reth::cli", "Installing debug rpc endpoint");
607 auth_module.merge_auth_methods(registry.debug_api().into_rpc())?;
608 }
609
610 if enable_tx_conditional {
611 modules.merge_if_module_configured(
613 RethRpcModule::Eth,
614 tx_conditional_ext.into_rpc(),
615 )?;
616 }
617
618 Ok(())
619 })
620 .await
621 }
622}
623
624impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> RethRpcAddOns<N>
625 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
626where
627 N: FullNodeComponents<
628 Types: NodeTypes<
629 ChainSpec: OpHardforks,
630 Primitives: OpPayloadPrimitives,
631 Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
632 >,
633 Evm: ConfigureEvm<
634 NextBlockEnvCtx: BuildNextEnv<
635 Attrs,
636 HeaderTy<N::Types>,
637 <N::Types as NodeTypes>::ChainSpec,
638 >,
639 >,
640 >,
641 <<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: OpPooledTx,
642 EthB: EthApiBuilder<N>,
643 PVB: PayloadValidatorBuilder<N>,
644 EB: EngineApiBuilder<N>,
645 EVB: EngineValidatorBuilder<N>,
646 RpcMiddleware: RethRpcMiddleware,
647 Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
648{
649 type EthApi = EthB::EthApi;
650
651 fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
652 self.rpc_add_ons.hooks_mut()
653 }
654}
655
656impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EngineValidatorAddOn<N>
657 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
658where
659 N: FullNodeComponents,
660 EthB: EthApiBuilder<N>,
661 PVB: Send,
662 EB: EngineApiBuilder<N>,
663 EVB: EngineValidatorBuilder<N>,
664 RpcMiddleware: Send,
665{
666 type ValidatorBuilder = EVB;
667
668 fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
669 EngineValidatorAddOn::engine_validator_builder(&self.rpc_add_ons)
670 }
671}
672
673#[derive(Debug, Clone)]
675#[non_exhaustive]
676pub struct OpAddOnsBuilder<NetworkT, RpcMiddleware = Identity> {
677 sequencer_url: Option<String>,
680 sequencer_headers: Vec<String>,
682 historical_rpc: Option<String>,
684 da_config: Option<OpDAConfig>,
686 gas_limit_config: Option<OpGasLimitConfig>,
688 enable_tx_conditional: bool,
690 _nt: PhantomData<NetworkT>,
692 min_suggested_priority_fee: u64,
694 rpc_middleware: RpcMiddleware,
696 tokio_runtime: Option<tokio::runtime::Handle>,
698 flashblocks_url: Option<Url>,
700 flashblock_consensus: bool,
702}
703
704impl<NetworkT> Default for OpAddOnsBuilder<NetworkT> {
705 fn default() -> Self {
706 Self {
707 sequencer_url: None,
708 sequencer_headers: Vec::new(),
709 historical_rpc: None,
710 da_config: None,
711 gas_limit_config: None,
712 enable_tx_conditional: false,
713 min_suggested_priority_fee: 1_000_000,
714 _nt: PhantomData,
715 rpc_middleware: Identity::new(),
716 tokio_runtime: None,
717 flashblocks_url: None,
718 flashblock_consensus: false,
719 }
720 }
721}
722
723impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
724 pub fn with_sequencer(mut self, sequencer_client: Option<String>) -> Self {
726 self.sequencer_url = sequencer_client;
727 self
728 }
729
730 pub fn with_sequencer_headers(mut self, sequencer_headers: Vec<String>) -> Self {
732 self.sequencer_headers = sequencer_headers;
733 self
734 }
735
736 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
738 self.da_config = Some(da_config);
739 self
740 }
741
742 pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
744 self.gas_limit_config = Some(gas_limit_config);
745 self
746 }
747
748 pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
750 self.enable_tx_conditional = enable_tx_conditional;
751 self
752 }
753
754 pub const fn with_min_suggested_priority_fee(mut self, min: u64) -> Self {
756 self.min_suggested_priority_fee = min;
757 self
758 }
759
760 pub fn with_historical_rpc(mut self, historical_rpc: Option<String>) -> Self {
762 self.historical_rpc = historical_rpc;
763 self
764 }
765
766 pub fn with_tokio_runtime(mut self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
770 self.tokio_runtime = tokio_runtime;
771 self
772 }
773
774 pub fn with_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOnsBuilder<NetworkT, T> {
776 let Self {
777 sequencer_url,
778 sequencer_headers,
779 historical_rpc,
780 da_config,
781 gas_limit_config,
782 enable_tx_conditional,
783 min_suggested_priority_fee,
784 tokio_runtime,
785 _nt,
786 flashblocks_url,
787 flashblock_consensus,
788 ..
789 } = self;
790 OpAddOnsBuilder {
791 sequencer_url,
792 sequencer_headers,
793 historical_rpc,
794 da_config,
795 gas_limit_config,
796 enable_tx_conditional,
797 min_suggested_priority_fee,
798 _nt,
799 rpc_middleware,
800 tokio_runtime,
801 flashblocks_url,
802 flashblock_consensus,
803 }
804 }
805
806 pub fn with_flashblocks(mut self, flashblocks_url: Option<Url>) -> Self {
808 self.flashblocks_url = flashblocks_url;
809 self
810 }
811
812 pub const fn with_flashblock_consensus(mut self, flashblock_consensus: bool) -> Self {
814 self.flashblock_consensus = flashblock_consensus;
815 self
816 }
817}
818
819impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
820 pub fn build<N, PVB, EB, EVB>(
822 self,
823 ) -> OpAddOns<N, OpEthApiBuilder<NetworkT>, PVB, EB, EVB, RpcMiddleware>
824 where
825 N: FullNodeComponents<Types: NodeTypes>,
826 OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
827 PVB: PayloadValidatorBuilder<N> + Default,
828 EB: Default,
829 EVB: Default,
830 {
831 let Self {
832 sequencer_url,
833 sequencer_headers,
834 da_config,
835 gas_limit_config,
836 enable_tx_conditional,
837 min_suggested_priority_fee,
838 historical_rpc,
839 rpc_middleware,
840 tokio_runtime,
841 flashblocks_url,
842 flashblock_consensus,
843 ..
844 } = self;
845
846 OpAddOns::new(
847 RpcAddOns::new(
848 OpEthApiBuilder::default()
849 .with_sequencer(sequencer_url.clone())
850 .with_sequencer_headers(sequencer_headers.clone())
851 .with_min_suggested_priority_fee(min_suggested_priority_fee)
852 .with_flashblocks(flashblocks_url)
853 .with_flashblock_consensus(flashblock_consensus),
854 PVB::default(),
855 EB::default(),
856 EVB::default(),
857 rpc_middleware,
858 )
859 .with_tokio_runtime(tokio_runtime),
860 da_config.unwrap_or_default(),
861 gas_limit_config.unwrap_or_default(),
862 sequencer_url,
863 sequencer_headers,
864 historical_rpc,
865 enable_tx_conditional,
866 min_suggested_priority_fee,
867 )
868 }
869}
870
871#[derive(Debug, Copy, Clone, Default)]
873#[non_exhaustive]
874pub struct OpExecutorBuilder;
875
876impl<Node> ExecutorBuilder<Node> for OpExecutorBuilder
877where
878 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks, Primitives = OpPrimitives>>,
879{
880 type EVM =
881 OpEvmConfig<<Node::Types as NodeTypes>::ChainSpec, <Node::Types as NodeTypes>::Primitives>;
882
883 async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
884 let evm_config = OpEvmConfig::new(ctx.chain_spec(), OpRethReceiptBuilder::default());
885
886 Ok(evm_config)
887 }
888}
889
890#[derive(Debug)]
895pub struct OpPoolBuilder<T = crate::txpool::OpPooledTransaction> {
896 pub pool_config_overrides: PoolBuilderConfigOverrides,
898 pub enable_tx_conditional: bool,
900 pub supervisor_http: String,
902 pub supervisor_safety_level: SafetyLevel,
904 _pd: core::marker::PhantomData<T>,
906}
907
908impl<T> Default for OpPoolBuilder<T> {
909 fn default() -> Self {
910 Self {
911 pool_config_overrides: Default::default(),
912 enable_tx_conditional: false,
913 supervisor_http: DEFAULT_SUPERVISOR_URL.to_string(),
914 supervisor_safety_level: SafetyLevel::CrossUnsafe,
915 _pd: Default::default(),
916 }
917 }
918}
919
920impl<T> Clone for OpPoolBuilder<T> {
921 fn clone(&self) -> Self {
922 Self {
923 pool_config_overrides: self.pool_config_overrides.clone(),
924 enable_tx_conditional: self.enable_tx_conditional,
925 supervisor_http: self.supervisor_http.clone(),
926 supervisor_safety_level: self.supervisor_safety_level,
927 _pd: core::marker::PhantomData,
928 }
929 }
930}
931
932impl<T> OpPoolBuilder<T> {
933 pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
935 self.enable_tx_conditional = enable_tx_conditional;
936 self
937 }
938
939 pub fn with_pool_config_overrides(
941 mut self,
942 pool_config_overrides: PoolBuilderConfigOverrides,
943 ) -> Self {
944 self.pool_config_overrides = pool_config_overrides;
945 self
946 }
947
948 pub fn with_supervisor(
950 mut self,
951 supervisor_client: String,
952 supervisor_safety_level: SafetyLevel,
953 ) -> Self {
954 self.supervisor_http = supervisor_client;
955 self.supervisor_safety_level = supervisor_safety_level;
956 self
957 }
958}
959
960impl<Node, T> PoolBuilder<Node> for OpPoolBuilder<T>
961where
962 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks>>,
963 T: EthPoolTransaction<Consensus = TxTy<Node::Types>> + OpPooledTx,
964{
965 type Pool = OpTransactionPool<Node::Provider, DiskFileBlobStore, T>;
966
967 async fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> {
968 let Self { pool_config_overrides, .. } = self;
969
970 if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) &&
972 self.supervisor_http == DEFAULT_SUPERVISOR_URL
973 {
974 info!(target: "reth::cli",
975 url=%DEFAULT_SUPERVISOR_URL,
976 "Default supervisor url is used, consider changing --rollup.supervisor-http."
977 );
978 }
979 let supervisor_client = SupervisorClient::builder(self.supervisor_http.clone())
980 .minimum_safety(self.supervisor_safety_level)
981 .build()
982 .await;
983
984 let blob_store = reth_node_builder::components::create_blob_store(ctx)?;
985 let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone())
986 .no_eip4844()
987 .with_head_timestamp(ctx.head().timestamp)
988 .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
989 .kzg_settings(ctx.kzg_settings()?)
990 .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
991 .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
992 .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
993 .with_additional_tasks(
994 pool_config_overrides
995 .additional_validation_tasks
996 .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks),
997 )
998 .build_with_tasks(ctx.task_executor().clone(), blob_store.clone())
999 .map(|validator| {
1000 OpTransactionValidator::new(validator)
1001 .require_l1_data_gas_fee(!ctx.config().dev.dev)
1004 .with_supervisor(supervisor_client.clone())
1005 });
1006
1007 let final_pool_config = pool_config_overrides.apply(ctx.pool_config());
1008
1009 let transaction_pool = TxPoolBuilder::new(ctx)
1010 .with_validator(validator)
1011 .build_and_spawn_maintenance_task(blob_store, final_pool_config)?;
1012
1013 info!(target: "reth::cli", "Transaction pool initialized");
1014 debug!(target: "reth::cli", "Spawned txpool maintenance task");
1015
1016 if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) {
1018 let chain_events = ctx.provider().canonical_state_stream();
1020 ctx.task_executor().spawn_critical(
1021 "Op txpool interop maintenance task",
1022 reth_optimism_txpool::maintain::maintain_transaction_pool_interop_future(
1023 transaction_pool.clone(),
1024 chain_events,
1025 supervisor_client,
1026 ),
1027 );
1028 debug!(target: "reth::cli", "Spawned Op interop txpool maintenance task");
1029 }
1030
1031 if self.enable_tx_conditional {
1032 let chain_events = ctx.provider().canonical_state_stream();
1034 ctx.task_executor().spawn_critical(
1035 "Op txpool conditional maintenance task",
1036 reth_optimism_txpool::maintain::maintain_transaction_pool_conditional_future(
1037 transaction_pool.clone(),
1038 chain_events,
1039 ),
1040 );
1041 debug!(target: "reth::cli", "Spawned Op conditional txpool maintenance task");
1042 }
1043
1044 Ok(transaction_pool)
1045 }
1046}
1047
1048#[derive(Debug, Default, Clone)]
1050pub struct OpPayloadBuilder<Txs = ()> {
1051 pub compute_pending_block: bool,
1060 pub best_transactions: Txs,
1063 pub da_config: OpDAConfig,
1066 pub gas_limit_config: OpGasLimitConfig,
1069}
1070
1071impl OpPayloadBuilder {
1072 pub fn new(compute_pending_block: bool) -> Self {
1075 Self {
1076 compute_pending_block,
1077 best_transactions: (),
1078 da_config: OpDAConfig::default(),
1079 gas_limit_config: OpGasLimitConfig::default(),
1080 }
1081 }
1082
1083 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
1085 self.da_config = da_config;
1086 self
1087 }
1088
1089 pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
1091 self.gas_limit_config = gas_limit_config;
1092 self
1093 }
1094}
1095
1096impl<Txs> OpPayloadBuilder<Txs> {
1097 pub fn with_transactions<T>(self, best_transactions: T) -> OpPayloadBuilder<T> {
1100 let Self { compute_pending_block, da_config, gas_limit_config, .. } = self;
1101 OpPayloadBuilder { compute_pending_block, best_transactions, da_config, gas_limit_config }
1102 }
1103}
1104
1105impl<Node, Pool, Txs, Evm, Attrs> PayloadBuilderBuilder<Node, Pool, Evm> for OpPayloadBuilder<Txs>
1106where
1107 Node: FullNodeTypes<
1108 Provider: ChainSpecProvider<ChainSpec: OpHardforks>,
1109 Types: NodeTypes<
1110 Primitives: OpPayloadPrimitives,
1111 Payload: PayloadTypes<
1112 BuiltPayload = OpBuiltPayload<PrimitivesTy<Node::Types>>,
1113 PayloadBuilderAttributes = Attrs,
1114 >,
1115 >,
1116 >,
1117 Evm: ConfigureEvm<
1118 Primitives = PrimitivesTy<Node::Types>,
1119 NextBlockEnvCtx: BuildNextEnv<
1120 Attrs,
1121 HeaderTy<Node::Types>,
1122 <Node::Types as NodeTypes>::ChainSpec,
1123 >,
1124 > + 'static,
1125 Pool: TransactionPool<Transaction: OpPooledTx<Consensus = TxTy<Node::Types>>> + Unpin + 'static,
1126 Txs: OpPayloadTransactions<Pool::Transaction>,
1127 Attrs: OpAttributes<Transaction = TxTy<Node::Types>>,
1128{
1129 type PayloadBuilder =
1130 reth_optimism_payload_builder::OpPayloadBuilder<Pool, Node::Provider, Evm, Txs, Attrs>;
1131
1132 async fn build_payload_builder(
1133 self,
1134 ctx: &BuilderContext<Node>,
1135 pool: Pool,
1136 evm_config: Evm,
1137 ) -> eyre::Result<Self::PayloadBuilder> {
1138 let payload_builder = reth_optimism_payload_builder::OpPayloadBuilder::with_builder_config(
1139 pool,
1140 ctx.provider().clone(),
1141 evm_config,
1142 OpBuilderConfig {
1143 da_config: self.da_config.clone(),
1144 gas_limit_config: self.gas_limit_config.clone(),
1145 },
1146 )
1147 .with_transactions(self.best_transactions.clone())
1148 .set_compute_pending_block(self.compute_pending_block);
1149 Ok(payload_builder)
1150 }
1151}
1152
1153#[derive(Debug, Default)]
1155pub struct OpNetworkBuilder {
1156 pub disable_txpool_gossip: bool,
1158 pub disable_discovery_v4: bool,
1160}
1161
1162impl Clone for OpNetworkBuilder {
1163 fn clone(&self) -> Self {
1164 Self::new(self.disable_txpool_gossip, self.disable_discovery_v4)
1165 }
1166}
1167
1168impl OpNetworkBuilder {
1169 pub const fn new(disable_txpool_gossip: bool, disable_discovery_v4: bool) -> Self {
1171 Self { disable_txpool_gossip, disable_discovery_v4 }
1172 }
1173}
1174
1175impl OpNetworkBuilder {
1176 pub fn network_config<Node, NetworkP>(
1180 &self,
1181 ctx: &BuilderContext<Node>,
1182 ) -> eyre::Result<NetworkConfig<Node::Provider, NetworkP>>
1183 where
1184 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1185 NetworkP: NetworkPrimitives,
1186 {
1187 let disable_txpool_gossip = self.disable_txpool_gossip;
1188 let disable_discovery_v4 = self.disable_discovery_v4;
1189 let args = &ctx.config().network;
1190 let network_builder = ctx
1191 .network_config_builder()?
1192 .apply(|mut builder| {
1194 let rlpx_socket = (args.addr, args.port).into();
1195 if disable_discovery_v4 || args.discovery.disable_discovery {
1196 builder = builder.disable_discv4_discovery();
1197 }
1198 if !args.discovery.disable_discovery {
1199 builder = builder.discovery_v5(
1200 args.discovery.discovery_v5_builder(
1201 rlpx_socket,
1202 ctx.config()
1203 .network
1204 .resolved_bootnodes()
1205 .or_else(|| ctx.chain_spec().bootnodes())
1206 .unwrap_or_default(),
1207 ),
1208 );
1209 }
1210
1211 builder
1212 });
1213
1214 let mut network_config = ctx.build_network_config(network_builder);
1215
1216 network_config.tx_gossip_disabled = disable_txpool_gossip;
1220
1221 Ok(network_config)
1222 }
1223}
1224
1225impl<Node, Pool> NetworkBuilder<Node, Pool> for OpNetworkBuilder
1226where
1227 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1228 Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
1229 + Unpin
1230 + 'static,
1231{
1232 type Network =
1233 NetworkHandle<BasicNetworkPrimitives<PrimitivesTy<Node::Types>, PoolPooledTx<Pool>>>;
1234
1235 async fn build_network(
1236 self,
1237 ctx: &BuilderContext<Node>,
1238 pool: Pool,
1239 ) -> eyre::Result<Self::Network> {
1240 let network_config = self.network_config(ctx)?;
1241 let network = NetworkManager::builder(network_config).await?;
1242 let handle = ctx.start_network(network, pool);
1243 info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized");
1244
1245 Ok(handle)
1246 }
1247}
1248
1249#[derive(Debug, Default, Clone)]
1251#[non_exhaustive]
1252pub struct OpConsensusBuilder;
1253
1254impl<Node> ConsensusBuilder<Node> for OpConsensusBuilder
1255where
1256 Node: FullNodeTypes<
1257 Types: NodeTypes<
1258 ChainSpec: OpHardforks,
1259 Primitives: NodePrimitives<Receipt: DepositReceipt>,
1260 >,
1261 >,
1262{
1263 type Consensus = Arc<OpBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
1264
1265 async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
1266 Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
1267 }
1268}
1269
1270#[derive(Debug, Default, Clone)]
1272#[non_exhaustive]
1273pub struct OpEngineValidatorBuilder;
1274
1275impl<Node> PayloadValidatorBuilder<Node> for OpEngineValidatorBuilder
1276where
1277 Node: FullNodeComponents<
1278 Types: NodeTypes<
1279 ChainSpec: OpHardforks,
1280 Payload: PayloadTypes<ExecutionData = OpExecutionData>,
1281 >,
1282 >,
1283{
1284 type Validator = OpEngineValidator<
1285 Node::Provider,
1286 <<Node::Types as NodeTypes>::Primitives as NodePrimitives>::SignedTx,
1287 <Node::Types as NodeTypes>::ChainSpec,
1288 >;
1289
1290 async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
1291 Ok(OpEngineValidator::new::<KeccakKeyHasher>(
1292 ctx.config.chain.clone(),
1293 ctx.node.provider().clone(),
1294 ))
1295 }
1296}
1297
1298pub type OpNetworkPrimitives = BasicNetworkPrimitives<OpPrimitives, OpPooledTransaction>;