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> {
231 ProviderFactoryBuilder::default()
232 }
233}
234
235impl<N> Node<N> for OpNode
236where
237 N: FullNodeTypes<Types: OpFullNodeTypes + OpNodeTypes>,
238{
239 type ComponentsBuilder = ComponentsBuilder<
240 N,
241 OpPoolBuilder,
242 BasicPayloadServiceBuilder<OpPayloadBuilder>,
243 OpNetworkBuilder,
244 OpExecutorBuilder,
245 OpConsensusBuilder,
246 >;
247
248 type AddOns = OpAddOns<
249 NodeAdapter<N, <Self::ComponentsBuilder as NodeComponentsBuilder<N>>::Components>,
250 OpEthApiBuilder,
251 OpEngineValidatorBuilder,
252 OpEngineApiBuilder<OpEngineValidatorBuilder>,
253 BasicEngineValidatorBuilder<OpEngineValidatorBuilder>,
254 >;
255
256 fn components_builder(&self) -> Self::ComponentsBuilder {
257 Self::components(self)
258 }
259
260 fn add_ons(&self) -> Self::AddOns {
261 self.add_ons_builder().build()
262 }
263}
264
265impl<N> DebugNode<N> for OpNode
266where
267 N: FullNodeComponents<Types = Self>,
268{
269 type RpcBlock = alloy_rpc_types_eth::Block<op_alloy_consensus::OpTxEnvelope>;
270
271 fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_node_api::BlockTy<Self> {
272 rpc_block.into_consensus()
273 }
274
275 fn local_payload_attributes_builder(
276 chain_spec: &Self::ChainSpec,
277 ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes> {
278 LocalPayloadAttributesBuilder::new(Arc::new(chain_spec.clone()))
279 }
280}
281
282impl NodeTypes for OpNode {
283 type Primitives = OpPrimitives;
284 type ChainSpec = OpChainSpec;
285 type Storage = OpStorage;
286 type Payload = OpEngineTypes;
287}
288
289#[derive(Debug)]
294pub struct OpAddOns<
295 N: FullNodeComponents,
296 EthB: EthApiBuilder<N>,
297 PVB,
298 EB = OpEngineApiBuilder<PVB>,
299 EVB = BasicEngineValidatorBuilder<PVB>,
300 RpcMiddleware = Identity,
301> {
302 pub rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
305 pub da_config: OpDAConfig,
307 pub gas_limit_config: OpGasLimitConfig,
309 pub sequencer_url: Option<String>,
312 pub sequencer_headers: Vec<String>,
314 pub historical_rpc: Option<String>,
318 enable_tx_conditional: bool,
320 min_suggested_priority_fee: u64,
321}
322
323impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
324where
325 N: FullNodeComponents,
326 EthB: EthApiBuilder<N>,
327{
328 #[allow(clippy::too_many_arguments)]
330 pub const fn new(
331 rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
332 da_config: OpDAConfig,
333 gas_limit_config: OpGasLimitConfig,
334 sequencer_url: Option<String>,
335 sequencer_headers: Vec<String>,
336 historical_rpc: Option<String>,
337 enable_tx_conditional: bool,
338 min_suggested_priority_fee: u64,
339 ) -> Self {
340 Self {
341 rpc_add_ons,
342 da_config,
343 gas_limit_config,
344 sequencer_url,
345 sequencer_headers,
346 historical_rpc,
347 enable_tx_conditional,
348 min_suggested_priority_fee,
349 }
350 }
351}
352
353impl<N> Default for OpAddOns<N, OpEthApiBuilder, OpEngineValidatorBuilder>
354where
355 N: FullNodeComponents<Types: OpNodeTypes>,
356 OpEthApiBuilder: EthApiBuilder<N>,
357{
358 fn default() -> Self {
359 Self::builder().build()
360 }
361}
362
363impl<N, NetworkT, RpcMiddleware>
364 OpAddOns<
365 N,
366 OpEthApiBuilder<NetworkT>,
367 OpEngineValidatorBuilder,
368 OpEngineApiBuilder<OpEngineValidatorBuilder>,
369 RpcMiddleware,
370 >
371where
372 N: FullNodeComponents<Types: OpNodeTypes>,
373 OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
374{
375 pub fn builder() -> OpAddOnsBuilder<NetworkT> {
377 OpAddOnsBuilder::default()
378 }
379}
380
381impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
382where
383 N: FullNodeComponents,
384 EthB: EthApiBuilder<N>,
385{
386 pub fn with_engine_api<T>(
388 self,
389 engine_api_builder: T,
390 ) -> OpAddOns<N, EthB, PVB, T, EVB, RpcMiddleware> {
391 let Self {
392 rpc_add_ons,
393 da_config,
394 gas_limit_config,
395 sequencer_url,
396 sequencer_headers,
397 historical_rpc,
398 enable_tx_conditional,
399 min_suggested_priority_fee,
400 ..
401 } = self;
402 OpAddOns::new(
403 rpc_add_ons.with_engine_api(engine_api_builder),
404 da_config,
405 gas_limit_config,
406 sequencer_url,
407 sequencer_headers,
408 historical_rpc,
409 enable_tx_conditional,
410 min_suggested_priority_fee,
411 )
412 }
413
414 pub fn with_payload_validator<T>(
416 self,
417 payload_validator_builder: T,
418 ) -> OpAddOns<N, EthB, T, EB, EVB, RpcMiddleware> {
419 let Self {
420 rpc_add_ons,
421 da_config,
422 gas_limit_config,
423 sequencer_url,
424 sequencer_headers,
425 enable_tx_conditional,
426 min_suggested_priority_fee,
427 historical_rpc,
428 ..
429 } = self;
430 OpAddOns::new(
431 rpc_add_ons.with_payload_validator(payload_validator_builder),
432 da_config,
433 gas_limit_config,
434 sequencer_url,
435 sequencer_headers,
436 historical_rpc,
437 enable_tx_conditional,
438 min_suggested_priority_fee,
439 )
440 }
441
442 pub fn with_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOns<N, EthB, PVB, EB, EVB, T> {
450 let Self {
451 rpc_add_ons,
452 da_config,
453 gas_limit_config,
454 sequencer_url,
455 sequencer_headers,
456 enable_tx_conditional,
457 min_suggested_priority_fee,
458 historical_rpc,
459 ..
460 } = self;
461 OpAddOns::new(
462 rpc_add_ons.with_rpc_middleware(rpc_middleware),
463 da_config,
464 gas_limit_config,
465 sequencer_url,
466 sequencer_headers,
467 historical_rpc,
468 enable_tx_conditional,
469 min_suggested_priority_fee,
470 )
471 }
472
473 pub fn on_rpc_started<F>(mut self, hook: F) -> Self
475 where
476 F: FnOnce(RpcContext<'_, N, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
477 + Send
478 + 'static,
479 {
480 self.rpc_add_ons = self.rpc_add_ons.on_rpc_started(hook);
481 self
482 }
483
484 pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
486 where
487 F: FnOnce(RpcContext<'_, N, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
488 {
489 self.rpc_add_ons = self.rpc_add_ons.extend_rpc_modules(hook);
490 self
491 }
492}
493
494impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> NodeAddOns<N>
495 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
496where
497 N: FullNodeComponents<
498 Types: NodeTypes<
499 ChainSpec: OpHardforks,
500 Primitives: OpPayloadPrimitives,
501 Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
502 >,
503 Evm: ConfigureEvm<
504 NextBlockEnvCtx: BuildNextEnv<
505 Attrs,
506 HeaderTy<N::Types>,
507 <N::Types as NodeTypes>::ChainSpec,
508 >,
509 >,
510 Pool: TransactionPool<Transaction: OpPooledTx>,
511 >,
512 EthB: EthApiBuilder<N>,
513 PVB: Send,
514 EB: EngineApiBuilder<N>,
515 EVB: EngineValidatorBuilder<N>,
516 RpcMiddleware: RethRpcMiddleware,
517 Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
518{
519 type Handle = RpcHandle<N, EthB::EthApi>;
520
521 async fn launch_add_ons(
522 self,
523 ctx: reth_node_api::AddOnsContext<'_, N>,
524 ) -> eyre::Result<Self::Handle> {
525 let Self {
526 rpc_add_ons,
527 da_config,
528 gas_limit_config,
529 sequencer_url,
530 sequencer_headers,
531 enable_tx_conditional,
532 historical_rpc,
533 ..
534 } = self;
535
536 let maybe_pre_bedrock_historical_rpc = historical_rpc
537 .and_then(|historical_rpc| {
538 ctx.node
539 .provider()
540 .chain_spec()
541 .op_fork_activation(OpHardfork::Bedrock)
542 .block_number()
543 .filter(|activation| *activation > 0)
544 .map(|bedrock_block| (historical_rpc, bedrock_block))
545 })
546 .map(|(historical_rpc, bedrock_block)| -> eyre::Result<_> {
547 info!(target: "reth::cli", %bedrock_block, ?historical_rpc, "Using historical RPC endpoint pre bedrock");
548 let provider = ctx.node.provider().clone();
549 let client = HistoricalRpcClient::new(&historical_rpc)?;
550 let layer = HistoricalRpc::new(provider, client, bedrock_block);
551 Ok(layer)
552 })
553 .transpose()?
554 ;
555
556 let rpc_add_ons = rpc_add_ons.option_layer_rpc_middleware(maybe_pre_bedrock_historical_rpc);
557
558 let builder = reth_optimism_payload_builder::OpPayloadBuilder::new(
559 ctx.node.pool().clone(),
560 ctx.node.provider().clone(),
561 ctx.node.evm_config().clone(),
562 );
563 let debug_ext = OpDebugWitnessApi::<_, _, _, Attrs>::new(
565 ctx.node.provider().clone(),
566 Box::new(ctx.node.task_executor().clone()),
567 builder,
568 );
569 let miner_ext = OpMinerExtApi::new(da_config, gas_limit_config);
570
571 let sequencer_client = if let Some(url) = sequencer_url {
572 Some(SequencerClient::new_with_headers(url, sequencer_headers).await?)
573 } else {
574 None
575 };
576
577 let tx_conditional_ext: OpEthExtApi<N::Pool, N::Provider> = OpEthExtApi::new(
578 sequencer_client,
579 ctx.node.pool().clone(),
580 ctx.node.provider().clone(),
581 );
582
583 rpc_add_ons
584 .launch_add_ons_with(ctx, move |container| {
585 let reth_node_builder::rpc::RpcModuleContainer { modules, auth_module, registry } =
586 container;
587
588 debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint");
589 modules.merge_if_module_configured(RethRpcModule::Debug, debug_ext.into_rpc())?;
590
591 modules.add_or_replace_if_module_configured(
593 RethRpcModule::Miner,
594 miner_ext.clone().into_rpc(),
595 )?;
596
597 if modules.module_config().contains_any(&RethRpcModule::Miner) {
599 debug!(target: "reth::cli", "Installing miner DA rpc endpoint");
600 auth_module.merge_auth_methods(miner_ext.into_rpc())?;
601 }
602
603 if modules.module_config().contains_any(&RethRpcModule::Debug) {
605 debug!(target: "reth::cli", "Installing debug rpc endpoint");
606 auth_module.merge_auth_methods(registry.debug_api().into_rpc())?;
607 }
608
609 if enable_tx_conditional {
610 modules.merge_if_module_configured(
612 RethRpcModule::Eth,
613 tx_conditional_ext.into_rpc(),
614 )?;
615 }
616
617 Ok(())
618 })
619 .await
620 }
621}
622
623impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> RethRpcAddOns<N>
624 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
625where
626 N: FullNodeComponents<
627 Types: NodeTypes<
628 ChainSpec: OpHardforks,
629 Primitives: OpPayloadPrimitives,
630 Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
631 >,
632 Evm: ConfigureEvm<
633 NextBlockEnvCtx: BuildNextEnv<
634 Attrs,
635 HeaderTy<N::Types>,
636 <N::Types as NodeTypes>::ChainSpec,
637 >,
638 >,
639 >,
640 <<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: OpPooledTx,
641 EthB: EthApiBuilder<N>,
642 PVB: PayloadValidatorBuilder<N>,
643 EB: EngineApiBuilder<N>,
644 EVB: EngineValidatorBuilder<N>,
645 RpcMiddleware: RethRpcMiddleware,
646 Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
647{
648 type EthApi = EthB::EthApi;
649
650 fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
651 self.rpc_add_ons.hooks_mut()
652 }
653}
654
655impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EngineValidatorAddOn<N>
656 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
657where
658 N: FullNodeComponents,
659 EthB: EthApiBuilder<N>,
660 PVB: Send,
661 EB: EngineApiBuilder<N>,
662 EVB: EngineValidatorBuilder<N>,
663 RpcMiddleware: Send,
664{
665 type ValidatorBuilder = EVB;
666
667 fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
668 EngineValidatorAddOn::engine_validator_builder(&self.rpc_add_ons)
669 }
670}
671
672#[derive(Debug, Clone)]
674#[non_exhaustive]
675pub struct OpAddOnsBuilder<NetworkT, RpcMiddleware = Identity> {
676 sequencer_url: Option<String>,
679 sequencer_headers: Vec<String>,
681 historical_rpc: Option<String>,
683 da_config: Option<OpDAConfig>,
685 gas_limit_config: Option<OpGasLimitConfig>,
687 enable_tx_conditional: bool,
689 _nt: PhantomData<NetworkT>,
691 min_suggested_priority_fee: u64,
693 rpc_middleware: RpcMiddleware,
695 tokio_runtime: Option<tokio::runtime::Handle>,
697 flashblocks_url: Option<Url>,
699 flashblock_consensus: bool,
701}
702
703impl<NetworkT> Default for OpAddOnsBuilder<NetworkT> {
704 fn default() -> Self {
705 Self {
706 sequencer_url: None,
707 sequencer_headers: Vec::new(),
708 historical_rpc: None,
709 da_config: None,
710 gas_limit_config: None,
711 enable_tx_conditional: false,
712 min_suggested_priority_fee: 1_000_000,
713 _nt: PhantomData,
714 rpc_middleware: Identity::new(),
715 tokio_runtime: None,
716 flashblocks_url: None,
717 flashblock_consensus: false,
718 }
719 }
720}
721
722impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
723 pub fn with_sequencer(mut self, sequencer_client: Option<String>) -> Self {
725 self.sequencer_url = sequencer_client;
726 self
727 }
728
729 pub fn with_sequencer_headers(mut self, sequencer_headers: Vec<String>) -> Self {
731 self.sequencer_headers = sequencer_headers;
732 self
733 }
734
735 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
737 self.da_config = Some(da_config);
738 self
739 }
740
741 pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
743 self.gas_limit_config = Some(gas_limit_config);
744 self
745 }
746
747 pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
749 self.enable_tx_conditional = enable_tx_conditional;
750 self
751 }
752
753 pub const fn with_min_suggested_priority_fee(mut self, min: u64) -> Self {
755 self.min_suggested_priority_fee = min;
756 self
757 }
758
759 pub fn with_historical_rpc(mut self, historical_rpc: Option<String>) -> Self {
761 self.historical_rpc = historical_rpc;
762 self
763 }
764
765 pub fn with_tokio_runtime(mut self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
769 self.tokio_runtime = tokio_runtime;
770 self
771 }
772
773 pub fn with_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOnsBuilder<NetworkT, T> {
775 let Self {
776 sequencer_url,
777 sequencer_headers,
778 historical_rpc,
779 da_config,
780 gas_limit_config,
781 enable_tx_conditional,
782 min_suggested_priority_fee,
783 tokio_runtime,
784 _nt,
785 flashblocks_url,
786 flashblock_consensus,
787 ..
788 } = self;
789 OpAddOnsBuilder {
790 sequencer_url,
791 sequencer_headers,
792 historical_rpc,
793 da_config,
794 gas_limit_config,
795 enable_tx_conditional,
796 min_suggested_priority_fee,
797 _nt,
798 rpc_middleware,
799 tokio_runtime,
800 flashblocks_url,
801 flashblock_consensus,
802 }
803 }
804
805 pub fn with_flashblocks(mut self, flashblocks_url: Option<Url>) -> Self {
807 self.flashblocks_url = flashblocks_url;
808 self
809 }
810
811 pub const fn with_flashblock_consensus(mut self, flashblock_consensus: bool) -> Self {
813 self.flashblock_consensus = flashblock_consensus;
814 self
815 }
816}
817
818impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
819 pub fn build<N, PVB, EB, EVB>(
821 self,
822 ) -> OpAddOns<N, OpEthApiBuilder<NetworkT>, PVB, EB, EVB, RpcMiddleware>
823 where
824 N: FullNodeComponents<Types: NodeTypes>,
825 OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
826 PVB: PayloadValidatorBuilder<N> + Default,
827 EB: Default,
828 EVB: Default,
829 {
830 let Self {
831 sequencer_url,
832 sequencer_headers,
833 da_config,
834 gas_limit_config,
835 enable_tx_conditional,
836 min_suggested_priority_fee,
837 historical_rpc,
838 rpc_middleware,
839 tokio_runtime,
840 flashblocks_url,
841 flashblock_consensus,
842 ..
843 } = self;
844
845 OpAddOns::new(
846 RpcAddOns::new(
847 OpEthApiBuilder::default()
848 .with_sequencer(sequencer_url.clone())
849 .with_sequencer_headers(sequencer_headers.clone())
850 .with_min_suggested_priority_fee(min_suggested_priority_fee)
851 .with_flashblocks(flashblocks_url)
852 .with_flashblock_consensus(flashblock_consensus),
853 PVB::default(),
854 EB::default(),
855 EVB::default(),
856 rpc_middleware,
857 )
858 .with_tokio_runtime(tokio_runtime),
859 da_config.unwrap_or_default(),
860 gas_limit_config.unwrap_or_default(),
861 sequencer_url,
862 sequencer_headers,
863 historical_rpc,
864 enable_tx_conditional,
865 min_suggested_priority_fee,
866 )
867 }
868}
869
870#[derive(Debug, Copy, Clone, Default)]
872#[non_exhaustive]
873pub struct OpExecutorBuilder;
874
875impl<Node> ExecutorBuilder<Node> for OpExecutorBuilder
876where
877 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks, Primitives = OpPrimitives>>,
878{
879 type EVM =
880 OpEvmConfig<<Node::Types as NodeTypes>::ChainSpec, <Node::Types as NodeTypes>::Primitives>;
881
882 async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
883 let evm_config = OpEvmConfig::new(ctx.chain_spec(), OpRethReceiptBuilder::default());
884
885 Ok(evm_config)
886 }
887}
888
889#[derive(Debug)]
894pub struct OpPoolBuilder<T = crate::txpool::OpPooledTransaction> {
895 pub pool_config_overrides: PoolBuilderConfigOverrides,
897 pub enable_tx_conditional: bool,
899 pub supervisor_http: String,
901 pub supervisor_safety_level: SafetyLevel,
903 _pd: core::marker::PhantomData<T>,
905}
906
907impl<T> Default for OpPoolBuilder<T> {
908 fn default() -> Self {
909 Self {
910 pool_config_overrides: Default::default(),
911 enable_tx_conditional: false,
912 supervisor_http: DEFAULT_SUPERVISOR_URL.to_string(),
913 supervisor_safety_level: SafetyLevel::CrossUnsafe,
914 _pd: Default::default(),
915 }
916 }
917}
918
919impl<T> Clone for OpPoolBuilder<T> {
920 fn clone(&self) -> Self {
921 Self {
922 pool_config_overrides: self.pool_config_overrides.clone(),
923 enable_tx_conditional: self.enable_tx_conditional,
924 supervisor_http: self.supervisor_http.clone(),
925 supervisor_safety_level: self.supervisor_safety_level,
926 _pd: core::marker::PhantomData,
927 }
928 }
929}
930
931impl<T> OpPoolBuilder<T> {
932 pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
934 self.enable_tx_conditional = enable_tx_conditional;
935 self
936 }
937
938 pub fn with_pool_config_overrides(
940 mut self,
941 pool_config_overrides: PoolBuilderConfigOverrides,
942 ) -> Self {
943 self.pool_config_overrides = pool_config_overrides;
944 self
945 }
946
947 pub fn with_supervisor(
949 mut self,
950 supervisor_client: String,
951 supervisor_safety_level: SafetyLevel,
952 ) -> Self {
953 self.supervisor_http = supervisor_client;
954 self.supervisor_safety_level = supervisor_safety_level;
955 self
956 }
957}
958
959impl<Node, T> PoolBuilder<Node> for OpPoolBuilder<T>
960where
961 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks>>,
962 T: EthPoolTransaction<Consensus = TxTy<Node::Types>> + OpPooledTx,
963{
964 type Pool = OpTransactionPool<Node::Provider, DiskFileBlobStore, T>;
965
966 async fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> {
967 let Self { pool_config_overrides, .. } = self;
968
969 if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) &&
971 self.supervisor_http == DEFAULT_SUPERVISOR_URL
972 {
973 info!(target: "reth::cli",
974 url=%DEFAULT_SUPERVISOR_URL,
975 "Default supervisor url is used, consider changing --rollup.supervisor-http."
976 );
977 }
978 let supervisor_client = SupervisorClient::builder(self.supervisor_http.clone())
979 .minimum_safety(self.supervisor_safety_level)
980 .build()
981 .await;
982
983 let blob_store = reth_node_builder::components::create_blob_store(ctx)?;
984 let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone())
985 .no_eip4844()
986 .with_head_timestamp(ctx.head().timestamp)
987 .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
988 .kzg_settings(ctx.kzg_settings()?)
989 .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
990 .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
991 .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
992 .with_additional_tasks(
993 pool_config_overrides
994 .additional_validation_tasks
995 .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks),
996 )
997 .build_with_tasks(ctx.task_executor().clone(), blob_store.clone())
998 .map(|validator| {
999 OpTransactionValidator::new(validator)
1000 .require_l1_data_gas_fee(!ctx.config().dev.dev)
1003 .with_supervisor(supervisor_client.clone())
1004 });
1005
1006 let final_pool_config = pool_config_overrides.apply(ctx.pool_config());
1007
1008 let transaction_pool = TxPoolBuilder::new(ctx)
1009 .with_validator(validator)
1010 .build_and_spawn_maintenance_task(blob_store, final_pool_config)?;
1011
1012 info!(target: "reth::cli", "Transaction pool initialized");
1013 debug!(target: "reth::cli", "Spawned txpool maintenance task");
1014
1015 if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) {
1017 let chain_events = ctx.provider().canonical_state_stream();
1019 ctx.task_executor().spawn_critical(
1020 "Op txpool interop maintenance task",
1021 reth_optimism_txpool::maintain::maintain_transaction_pool_interop_future(
1022 transaction_pool.clone(),
1023 chain_events,
1024 supervisor_client,
1025 ),
1026 );
1027 debug!(target: "reth::cli", "Spawned Op interop txpool maintenance task");
1028 }
1029
1030 if self.enable_tx_conditional {
1031 let chain_events = ctx.provider().canonical_state_stream();
1033 ctx.task_executor().spawn_critical(
1034 "Op txpool conditional maintenance task",
1035 reth_optimism_txpool::maintain::maintain_transaction_pool_conditional_future(
1036 transaction_pool.clone(),
1037 chain_events,
1038 ),
1039 );
1040 debug!(target: "reth::cli", "Spawned Op conditional txpool maintenance task");
1041 }
1042
1043 Ok(transaction_pool)
1044 }
1045}
1046
1047#[derive(Debug, Default, Clone)]
1049pub struct OpPayloadBuilder<Txs = ()> {
1050 pub compute_pending_block: bool,
1059 pub best_transactions: Txs,
1062 pub da_config: OpDAConfig,
1065 pub gas_limit_config: OpGasLimitConfig,
1068}
1069
1070impl OpPayloadBuilder {
1071 pub fn new(compute_pending_block: bool) -> Self {
1074 Self {
1075 compute_pending_block,
1076 best_transactions: (),
1077 da_config: OpDAConfig::default(),
1078 gas_limit_config: OpGasLimitConfig::default(),
1079 }
1080 }
1081
1082 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
1084 self.da_config = da_config;
1085 self
1086 }
1087
1088 pub fn with_gas_limit_config(mut self, gas_limit_config: OpGasLimitConfig) -> Self {
1090 self.gas_limit_config = gas_limit_config;
1091 self
1092 }
1093}
1094
1095impl<Txs> OpPayloadBuilder<Txs> {
1096 pub fn with_transactions<T>(self, best_transactions: T) -> OpPayloadBuilder<T> {
1099 let Self { compute_pending_block, da_config, gas_limit_config, .. } = self;
1100 OpPayloadBuilder { compute_pending_block, best_transactions, da_config, gas_limit_config }
1101 }
1102}
1103
1104impl<Node, Pool, Txs, Evm, Attrs> PayloadBuilderBuilder<Node, Pool, Evm> for OpPayloadBuilder<Txs>
1105where
1106 Node: FullNodeTypes<
1107 Provider: ChainSpecProvider<ChainSpec: OpHardforks>,
1108 Types: NodeTypes<
1109 Primitives: OpPayloadPrimitives,
1110 Payload: PayloadTypes<
1111 BuiltPayload = OpBuiltPayload<PrimitivesTy<Node::Types>>,
1112 PayloadBuilderAttributes = Attrs,
1113 >,
1114 >,
1115 >,
1116 Evm: ConfigureEvm<
1117 Primitives = PrimitivesTy<Node::Types>,
1118 NextBlockEnvCtx: BuildNextEnv<
1119 Attrs,
1120 HeaderTy<Node::Types>,
1121 <Node::Types as NodeTypes>::ChainSpec,
1122 >,
1123 > + 'static,
1124 Pool: TransactionPool<Transaction: OpPooledTx<Consensus = TxTy<Node::Types>>> + Unpin + 'static,
1125 Txs: OpPayloadTransactions<Pool::Transaction>,
1126 Attrs: OpAttributes<Transaction = TxTy<Node::Types>>,
1127{
1128 type PayloadBuilder =
1129 reth_optimism_payload_builder::OpPayloadBuilder<Pool, Node::Provider, Evm, Txs, Attrs>;
1130
1131 async fn build_payload_builder(
1132 self,
1133 ctx: &BuilderContext<Node>,
1134 pool: Pool,
1135 evm_config: Evm,
1136 ) -> eyre::Result<Self::PayloadBuilder> {
1137 let payload_builder = reth_optimism_payload_builder::OpPayloadBuilder::with_builder_config(
1138 pool,
1139 ctx.provider().clone(),
1140 evm_config,
1141 OpBuilderConfig {
1142 da_config: self.da_config.clone(),
1143 gas_limit_config: self.gas_limit_config.clone(),
1144 },
1145 )
1146 .with_transactions(self.best_transactions.clone())
1147 .set_compute_pending_block(self.compute_pending_block);
1148 Ok(payload_builder)
1149 }
1150}
1151
1152#[derive(Debug, Default)]
1154pub struct OpNetworkBuilder {
1155 pub disable_txpool_gossip: bool,
1157 pub disable_discovery_v4: bool,
1159}
1160
1161impl Clone for OpNetworkBuilder {
1162 fn clone(&self) -> Self {
1163 Self::new(self.disable_txpool_gossip, self.disable_discovery_v4)
1164 }
1165}
1166
1167impl OpNetworkBuilder {
1168 pub const fn new(disable_txpool_gossip: bool, disable_discovery_v4: bool) -> Self {
1170 Self { disable_txpool_gossip, disable_discovery_v4 }
1171 }
1172}
1173
1174impl OpNetworkBuilder {
1175 pub fn network_config<Node, NetworkP>(
1179 &self,
1180 ctx: &BuilderContext<Node>,
1181 ) -> eyre::Result<NetworkConfig<Node::Provider, NetworkP>>
1182 where
1183 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1184 NetworkP: NetworkPrimitives,
1185 {
1186 let disable_txpool_gossip = self.disable_txpool_gossip;
1187 let disable_discovery_v4 = self.disable_discovery_v4;
1188 let args = &ctx.config().network;
1189 let network_builder = ctx
1190 .network_config_builder()?
1191 .apply(|mut builder| {
1193 let rlpx_socket = (args.addr, args.port).into();
1194 if disable_discovery_v4 || args.discovery.disable_discovery {
1195 builder = builder.disable_discv4_discovery();
1196 }
1197 if !args.discovery.disable_discovery {
1198 builder = builder.discovery_v5(
1199 args.discovery.discovery_v5_builder(
1200 rlpx_socket,
1201 ctx.config()
1202 .network
1203 .resolved_bootnodes()
1204 .or_else(|| ctx.chain_spec().bootnodes())
1205 .unwrap_or_default(),
1206 ),
1207 );
1208 }
1209
1210 builder
1211 });
1212
1213 let mut network_config = ctx.build_network_config(network_builder);
1214
1215 network_config.tx_gossip_disabled = disable_txpool_gossip;
1219
1220 Ok(network_config)
1221 }
1222}
1223
1224impl<Node, Pool> NetworkBuilder<Node, Pool> for OpNetworkBuilder
1225where
1226 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1227 Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
1228 + Unpin
1229 + 'static,
1230{
1231 type Network =
1232 NetworkHandle<BasicNetworkPrimitives<PrimitivesTy<Node::Types>, PoolPooledTx<Pool>>>;
1233
1234 async fn build_network(
1235 self,
1236 ctx: &BuilderContext<Node>,
1237 pool: Pool,
1238 ) -> eyre::Result<Self::Network> {
1239 let network_config = self.network_config(ctx)?;
1240 let network = NetworkManager::builder(network_config).await?;
1241 let handle = ctx.start_network(network, pool);
1242 info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized");
1243
1244 Ok(handle)
1245 }
1246}
1247
1248#[derive(Debug, Default, Clone)]
1250#[non_exhaustive]
1251pub struct OpConsensusBuilder;
1252
1253impl<Node> ConsensusBuilder<Node> for OpConsensusBuilder
1254where
1255 Node: FullNodeTypes<
1256 Types: NodeTypes<
1257 ChainSpec: OpHardforks,
1258 Primitives: NodePrimitives<Receipt: DepositReceipt>,
1259 >,
1260 >,
1261{
1262 type Consensus = Arc<OpBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
1263
1264 async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
1265 Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
1266 }
1267}
1268
1269#[derive(Debug, Default, Clone)]
1271#[non_exhaustive]
1272pub struct OpEngineValidatorBuilder;
1273
1274impl<Node> PayloadValidatorBuilder<Node> for OpEngineValidatorBuilder
1275where
1276 Node: FullNodeComponents<
1277 Types: NodeTypes<
1278 ChainSpec: OpHardforks,
1279 Payload: PayloadTypes<ExecutionData = OpExecutionData>,
1280 >,
1281 >,
1282{
1283 type Validator = OpEngineValidator<
1284 Node::Provider,
1285 <<Node::Types as NodeTypes>::Primitives as NodePrimitives>::SignedTx,
1286 <Node::Types as NodeTypes>::ChainSpec,
1287 >;
1288
1289 async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
1290 Ok(OpEngineValidator::new::<KeccakKeyHasher>(
1291 ctx.config.chain.clone(),
1292 ctx.node.provider().clone(),
1293 ))
1294 }
1295}
1296
1297pub type OpNetworkPrimitives = BasicNetworkPrimitives<OpPrimitives, OpPooledTransaction>;