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