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},
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}
122
123pub type OpNodeComponentBuilder<Node, Payload = OpPayloadBuilder> = ComponentsBuilder<
125 Node,
126 OpPoolBuilder,
127 BasicPayloadServiceBuilder<Payload>,
128 OpNetworkBuilder,
129 OpExecutorBuilder,
130 OpConsensusBuilder,
131>;
132
133impl OpNode {
134 pub fn new(args: RollupArgs) -> Self {
136 Self { args, da_config: OpDAConfig::default() }
137 }
138
139 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
141 self.da_config = da_config;
142 self
143 }
144
145 pub fn components<Node>(&self) -> OpNodeComponentBuilder<Node>
147 where
148 Node: FullNodeTypes<Types: OpNodeTypes>,
149 {
150 let RollupArgs { disable_txpool_gossip, compute_pending_block, discovery_v4, .. } =
151 self.args;
152 ComponentsBuilder::default()
153 .node_types::<Node>()
154 .pool(
155 OpPoolBuilder::default()
156 .with_enable_tx_conditional(self.args.enable_tx_conditional)
157 .with_supervisor(
158 self.args.supervisor_http.clone(),
159 self.args.supervisor_safety_level,
160 ),
161 )
162 .executor(OpExecutorBuilder::default())
163 .payload(BasicPayloadServiceBuilder::new(
164 OpPayloadBuilder::new(compute_pending_block).with_da_config(self.da_config.clone()),
165 ))
166 .network(OpNetworkBuilder::new(disable_txpool_gossip, !discovery_v4))
167 .consensus(OpConsensusBuilder::default())
168 }
169
170 pub fn add_ons_builder<NetworkT: RpcTypes>(&self) -> OpAddOnsBuilder<NetworkT> {
172 OpAddOnsBuilder::default()
173 .with_sequencer(self.args.sequencer.clone())
174 .with_sequencer_headers(self.args.sequencer_headers.clone())
175 .with_da_config(self.da_config.clone())
176 .with_enable_tx_conditional(self.args.enable_tx_conditional)
177 .with_min_suggested_priority_fee(self.args.min_suggested_priority_fee)
178 .with_historical_rpc(self.args.historical_rpc.clone())
179 .with_flashblocks(self.args.flashblocks_url.clone())
180 }
181
182 pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
213 ProviderFactoryBuilder::default()
214 }
215}
216
217impl<N> Node<N> for OpNode
218where
219 N: FullNodeTypes<Types: OpFullNodeTypes + OpNodeTypes>,
220{
221 type ComponentsBuilder = ComponentsBuilder<
222 N,
223 OpPoolBuilder,
224 BasicPayloadServiceBuilder<OpPayloadBuilder>,
225 OpNetworkBuilder,
226 OpExecutorBuilder,
227 OpConsensusBuilder,
228 >;
229
230 type AddOns = OpAddOns<
231 NodeAdapter<N, <Self::ComponentsBuilder as NodeComponentsBuilder<N>>::Components>,
232 OpEthApiBuilder,
233 OpEngineValidatorBuilder,
234 OpEngineApiBuilder<OpEngineValidatorBuilder>,
235 BasicEngineValidatorBuilder<OpEngineValidatorBuilder>,
236 >;
237
238 fn components_builder(&self) -> Self::ComponentsBuilder {
239 Self::components(self)
240 }
241
242 fn add_ons(&self) -> Self::AddOns {
243 self.add_ons_builder().build()
244 }
245}
246
247impl<N> DebugNode<N> for OpNode
248where
249 N: FullNodeComponents<Types = Self>,
250{
251 type RpcBlock = alloy_rpc_types_eth::Block<op_alloy_consensus::OpTxEnvelope>;
252
253 fn rpc_to_primitive_block(rpc_block: Self::RpcBlock) -> reth_node_api::BlockTy<Self> {
254 rpc_block.into_consensus()
255 }
256
257 fn local_payload_attributes_builder(
258 chain_spec: &Self::ChainSpec,
259 ) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes> {
260 LocalPayloadAttributesBuilder::new(Arc::new(chain_spec.clone()))
261 }
262}
263
264impl NodeTypes for OpNode {
265 type Primitives = OpPrimitives;
266 type ChainSpec = OpChainSpec;
267 type Storage = OpStorage;
268 type Payload = OpEngineTypes;
269}
270
271#[derive(Debug)]
276pub struct OpAddOns<
277 N: FullNodeComponents,
278 EthB: EthApiBuilder<N>,
279 PVB,
280 EB = OpEngineApiBuilder<PVB>,
281 EVB = BasicEngineValidatorBuilder<PVB>,
282 RpcMiddleware = Identity,
283> {
284 pub rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
287 pub da_config: OpDAConfig,
289 pub sequencer_url: Option<String>,
292 pub sequencer_headers: Vec<String>,
294 pub historical_rpc: Option<String>,
298 enable_tx_conditional: bool,
300 min_suggested_priority_fee: u64,
301}
302
303impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
304where
305 N: FullNodeComponents,
306 EthB: EthApiBuilder<N>,
307{
308 pub const fn new(
310 rpc_add_ons: RpcAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>,
311 da_config: OpDAConfig,
312 sequencer_url: Option<String>,
313 sequencer_headers: Vec<String>,
314 historical_rpc: Option<String>,
315 enable_tx_conditional: bool,
316 min_suggested_priority_fee: u64,
317 ) -> Self {
318 Self {
319 rpc_add_ons,
320 da_config,
321 sequencer_url,
322 sequencer_headers,
323 historical_rpc,
324 enable_tx_conditional,
325 min_suggested_priority_fee,
326 }
327 }
328}
329
330impl<N> Default for OpAddOns<N, OpEthApiBuilder, OpEngineValidatorBuilder>
331where
332 N: FullNodeComponents<Types: OpNodeTypes>,
333 OpEthApiBuilder: EthApiBuilder<N>,
334{
335 fn default() -> Self {
336 Self::builder().build()
337 }
338}
339
340impl<N, NetworkT, RpcMiddleware>
341 OpAddOns<
342 N,
343 OpEthApiBuilder<NetworkT>,
344 OpEngineValidatorBuilder,
345 OpEngineApiBuilder<OpEngineValidatorBuilder>,
346 RpcMiddleware,
347 >
348where
349 N: FullNodeComponents<Types: OpNodeTypes>,
350 OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
351{
352 pub fn builder() -> OpAddOnsBuilder<NetworkT> {
354 OpAddOnsBuilder::default()
355 }
356}
357
358impl<N, EthB, PVB, EB, EVB, RpcMiddleware> OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
359where
360 N: FullNodeComponents,
361 EthB: EthApiBuilder<N>,
362{
363 pub fn with_engine_api<T>(
365 self,
366 engine_api_builder: T,
367 ) -> OpAddOns<N, EthB, PVB, T, EVB, RpcMiddleware> {
368 let Self {
369 rpc_add_ons,
370 da_config,
371 sequencer_url,
372 sequencer_headers,
373 historical_rpc,
374 enable_tx_conditional,
375 min_suggested_priority_fee,
376 ..
377 } = self;
378 OpAddOns::new(
379 rpc_add_ons.with_engine_api(engine_api_builder),
380 da_config,
381 sequencer_url,
382 sequencer_headers,
383 historical_rpc,
384 enable_tx_conditional,
385 min_suggested_priority_fee,
386 )
387 }
388
389 pub fn with_payload_validator<T>(
391 self,
392 payload_validator_builder: T,
393 ) -> OpAddOns<N, EthB, T, EB, EVB, RpcMiddleware> {
394 let Self {
395 rpc_add_ons,
396 da_config,
397 sequencer_url,
398 sequencer_headers,
399 enable_tx_conditional,
400 min_suggested_priority_fee,
401 historical_rpc,
402 ..
403 } = self;
404 OpAddOns::new(
405 rpc_add_ons.with_payload_validator(payload_validator_builder),
406 da_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_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOns<N, EthB, PVB, EB, EVB, T> {
423 let Self {
424 rpc_add_ons,
425 da_config,
426 sequencer_url,
427 sequencer_headers,
428 enable_tx_conditional,
429 min_suggested_priority_fee,
430 historical_rpc,
431 ..
432 } = self;
433 OpAddOns::new(
434 rpc_add_ons.with_rpc_middleware(rpc_middleware),
435 da_config,
436 sequencer_url,
437 sequencer_headers,
438 historical_rpc,
439 enable_tx_conditional,
440 min_suggested_priority_fee,
441 )
442 }
443
444 pub fn on_rpc_started<F>(mut self, hook: F) -> Self
446 where
447 F: FnOnce(RpcContext<'_, N, EthB::EthApi>, RethRpcServerHandles) -> eyre::Result<()>
448 + Send
449 + 'static,
450 {
451 self.rpc_add_ons = self.rpc_add_ons.on_rpc_started(hook);
452 self
453 }
454
455 pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
457 where
458 F: FnOnce(RpcContext<'_, N, EthB::EthApi>) -> eyre::Result<()> + Send + 'static,
459 {
460 self.rpc_add_ons = self.rpc_add_ons.extend_rpc_modules(hook);
461 self
462 }
463}
464
465impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> NodeAddOns<N>
466 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
467where
468 N: FullNodeComponents<
469 Types: NodeTypes<
470 ChainSpec: OpHardforks,
471 Primitives: OpPayloadPrimitives,
472 Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
473 >,
474 Evm: ConfigureEvm<
475 NextBlockEnvCtx: BuildNextEnv<
476 Attrs,
477 HeaderTy<N::Types>,
478 <N::Types as NodeTypes>::ChainSpec,
479 >,
480 >,
481 Pool: TransactionPool<Transaction: OpPooledTx>,
482 >,
483 EthB: EthApiBuilder<N>,
484 PVB: Send,
485 EB: EngineApiBuilder<N>,
486 EVB: EngineValidatorBuilder<N>,
487 RpcMiddleware: RethRpcMiddleware,
488 Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
489{
490 type Handle = RpcHandle<N, EthB::EthApi>;
491
492 async fn launch_add_ons(
493 self,
494 ctx: reth_node_api::AddOnsContext<'_, N>,
495 ) -> eyre::Result<Self::Handle> {
496 let Self {
497 rpc_add_ons,
498 da_config,
499 sequencer_url,
500 sequencer_headers,
501 enable_tx_conditional,
502 historical_rpc,
503 ..
504 } = self;
505
506 let maybe_pre_bedrock_historical_rpc = historical_rpc
507 .and_then(|historical_rpc| {
508 ctx.node
509 .provider()
510 .chain_spec()
511 .op_fork_activation(OpHardfork::Bedrock)
512 .block_number()
513 .filter(|activation| *activation > 0)
514 .map(|bedrock_block| (historical_rpc, bedrock_block))
515 })
516 .map(|(historical_rpc, bedrock_block)| -> eyre::Result<_> {
517 info!(target: "reth::cli", %bedrock_block, ?historical_rpc, "Using historical RPC endpoint pre bedrock");
518 let provider = ctx.node.provider().clone();
519 let client = HistoricalRpcClient::new(&historical_rpc)?;
520 let layer = HistoricalRpc::new(provider, client, bedrock_block);
521 Ok(layer)
522 })
523 .transpose()?
524 ;
525
526 let rpc_add_ons = rpc_add_ons.option_layer_rpc_middleware(maybe_pre_bedrock_historical_rpc);
527
528 let builder = reth_optimism_payload_builder::OpPayloadBuilder::new(
529 ctx.node.pool().clone(),
530 ctx.node.provider().clone(),
531 ctx.node.evm_config().clone(),
532 );
533 let debug_ext = OpDebugWitnessApi::<_, _, _, Attrs>::new(
535 ctx.node.provider().clone(),
536 Box::new(ctx.node.task_executor().clone()),
537 builder,
538 );
539 let miner_ext = OpMinerExtApi::new(da_config);
540
541 let sequencer_client = if let Some(url) = sequencer_url {
542 Some(SequencerClient::new_with_headers(url, sequencer_headers).await?)
543 } else {
544 None
545 };
546
547 let tx_conditional_ext: OpEthExtApi<N::Pool, N::Provider> = OpEthExtApi::new(
548 sequencer_client,
549 ctx.node.pool().clone(),
550 ctx.node.provider().clone(),
551 );
552
553 rpc_add_ons
554 .launch_add_ons_with(ctx, move |container| {
555 let reth_node_builder::rpc::RpcModuleContainer { modules, auth_module, registry } =
556 container;
557
558 debug!(target: "reth::cli", "Installing debug payload witness rpc endpoint");
559 modules.merge_if_module_configured(RethRpcModule::Debug, debug_ext.into_rpc())?;
560
561 modules.merge_if_module_configured(
563 RethRpcModule::Miner,
564 miner_ext.clone().into_rpc(),
565 )?;
566
567 if modules.module_config().contains_any(&RethRpcModule::Miner) {
569 debug!(target: "reth::cli", "Installing miner DA rpc endpoint");
570 auth_module.merge_auth_methods(miner_ext.into_rpc())?;
571 }
572
573 if modules.module_config().contains_any(&RethRpcModule::Debug) {
575 debug!(target: "reth::cli", "Installing debug rpc endpoint");
576 auth_module.merge_auth_methods(registry.debug_api().into_rpc())?;
577 }
578
579 if enable_tx_conditional {
580 modules.merge_if_module_configured(
582 RethRpcModule::Eth,
583 tx_conditional_ext.into_rpc(),
584 )?;
585 }
586
587 Ok(())
588 })
589 .await
590 }
591}
592
593impl<N, EthB, PVB, EB, EVB, Attrs, RpcMiddleware> RethRpcAddOns<N>
594 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
595where
596 N: FullNodeComponents<
597 Types: NodeTypes<
598 ChainSpec: OpHardforks,
599 Primitives: OpPayloadPrimitives,
600 Payload: PayloadTypes<PayloadBuilderAttributes = Attrs>,
601 >,
602 Evm: ConfigureEvm<
603 NextBlockEnvCtx: BuildNextEnv<
604 Attrs,
605 HeaderTy<N::Types>,
606 <N::Types as NodeTypes>::ChainSpec,
607 >,
608 >,
609 >,
610 <<N as FullNodeComponents>::Pool as TransactionPool>::Transaction: OpPooledTx,
611 EthB: EthApiBuilder<N>,
612 PVB: PayloadValidatorBuilder<N>,
613 EB: EngineApiBuilder<N>,
614 EVB: EngineValidatorBuilder<N>,
615 RpcMiddleware: RethRpcMiddleware,
616 Attrs: OpAttributes<Transaction = TxTy<N::Types>, RpcPayloadAttributes: DeserializeOwned>,
617{
618 type EthApi = EthB::EthApi;
619
620 fn hooks_mut(&mut self) -> &mut reth_node_builder::rpc::RpcHooks<N, Self::EthApi> {
621 self.rpc_add_ons.hooks_mut()
622 }
623}
624
625impl<N, EthB, PVB, EB, EVB, RpcMiddleware> EngineValidatorAddOn<N>
626 for OpAddOns<N, EthB, PVB, EB, EVB, RpcMiddleware>
627where
628 N: FullNodeComponents,
629 EthB: EthApiBuilder<N>,
630 PVB: Send,
631 EB: EngineApiBuilder<N>,
632 EVB: EngineValidatorBuilder<N>,
633 RpcMiddleware: Send,
634{
635 type ValidatorBuilder = EVB;
636
637 fn engine_validator_builder(&self) -> Self::ValidatorBuilder {
638 EngineValidatorAddOn::engine_validator_builder(&self.rpc_add_ons)
639 }
640}
641
642#[derive(Debug, Clone)]
644#[non_exhaustive]
645pub struct OpAddOnsBuilder<NetworkT, RpcMiddleware = Identity> {
646 sequencer_url: Option<String>,
649 sequencer_headers: Vec<String>,
651 historical_rpc: Option<String>,
653 da_config: Option<OpDAConfig>,
655 enable_tx_conditional: bool,
657 _nt: PhantomData<NetworkT>,
659 min_suggested_priority_fee: u64,
661 rpc_middleware: RpcMiddleware,
663 tokio_runtime: Option<tokio::runtime::Handle>,
665 flashblocks_url: Option<Url>,
667}
668
669impl<NetworkT> Default for OpAddOnsBuilder<NetworkT> {
670 fn default() -> Self {
671 Self {
672 sequencer_url: None,
673 sequencer_headers: Vec::new(),
674 historical_rpc: None,
675 da_config: None,
676 enable_tx_conditional: false,
677 min_suggested_priority_fee: 1_000_000,
678 _nt: PhantomData,
679 rpc_middleware: Identity::new(),
680 tokio_runtime: None,
681 flashblocks_url: None,
682 }
683 }
684}
685
686impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
687 pub fn with_sequencer(mut self, sequencer_client: Option<String>) -> Self {
689 self.sequencer_url = sequencer_client;
690 self
691 }
692
693 pub fn with_sequencer_headers(mut self, sequencer_headers: Vec<String>) -> Self {
695 self.sequencer_headers = sequencer_headers;
696 self
697 }
698
699 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
701 self.da_config = Some(da_config);
702 self
703 }
704
705 pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
707 self.enable_tx_conditional = enable_tx_conditional;
708 self
709 }
710
711 pub const fn with_min_suggested_priority_fee(mut self, min: u64) -> Self {
713 self.min_suggested_priority_fee = min;
714 self
715 }
716
717 pub fn with_historical_rpc(mut self, historical_rpc: Option<String>) -> Self {
719 self.historical_rpc = historical_rpc;
720 self
721 }
722
723 pub fn with_tokio_runtime(mut self, tokio_runtime: Option<tokio::runtime::Handle>) -> Self {
727 self.tokio_runtime = tokio_runtime;
728 self
729 }
730
731 pub fn with_rpc_middleware<T>(self, rpc_middleware: T) -> OpAddOnsBuilder<NetworkT, T> {
733 let Self {
734 sequencer_url,
735 sequencer_headers,
736 historical_rpc,
737 da_config,
738 enable_tx_conditional,
739 min_suggested_priority_fee,
740 tokio_runtime,
741 _nt,
742 flashblocks_url,
743 ..
744 } = self;
745 OpAddOnsBuilder {
746 sequencer_url,
747 sequencer_headers,
748 historical_rpc,
749 da_config,
750 enable_tx_conditional,
751 min_suggested_priority_fee,
752 _nt,
753 rpc_middleware,
754 tokio_runtime,
755 flashblocks_url,
756 }
757 }
758
759 pub fn with_flashblocks(mut self, flashblocks_url: Option<Url>) -> Self {
761 self.flashblocks_url = flashblocks_url;
762 self
763 }
764}
765
766impl<NetworkT, RpcMiddleware> OpAddOnsBuilder<NetworkT, RpcMiddleware> {
767 pub fn build<N, PVB, EB, EVB>(
769 self,
770 ) -> OpAddOns<N, OpEthApiBuilder<NetworkT>, PVB, EB, EVB, RpcMiddleware>
771 where
772 N: FullNodeComponents<Types: NodeTypes>,
773 OpEthApiBuilder<NetworkT>: EthApiBuilder<N>,
774 PVB: PayloadValidatorBuilder<N> + Default,
775 EB: Default,
776 EVB: Default,
777 {
778 let Self {
779 sequencer_url,
780 sequencer_headers,
781 da_config,
782 enable_tx_conditional,
783 min_suggested_priority_fee,
784 historical_rpc,
785 rpc_middleware,
786 tokio_runtime,
787 flashblocks_url,
788 ..
789 } = self;
790
791 OpAddOns::new(
792 RpcAddOns::new(
793 OpEthApiBuilder::default()
794 .with_sequencer(sequencer_url.clone())
795 .with_sequencer_headers(sequencer_headers.clone())
796 .with_min_suggested_priority_fee(min_suggested_priority_fee)
797 .with_flashblocks(flashblocks_url),
798 PVB::default(),
799 EB::default(),
800 EVB::default(),
801 rpc_middleware,
802 )
803 .with_tokio_runtime(tokio_runtime),
804 da_config.unwrap_or_default(),
805 sequencer_url,
806 sequencer_headers,
807 historical_rpc,
808 enable_tx_conditional,
809 min_suggested_priority_fee,
810 )
811 }
812}
813
814#[derive(Debug, Copy, Clone, Default)]
816#[non_exhaustive]
817pub struct OpExecutorBuilder;
818
819impl<Node> ExecutorBuilder<Node> for OpExecutorBuilder
820where
821 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks, Primitives = OpPrimitives>>,
822{
823 type EVM =
824 OpEvmConfig<<Node::Types as NodeTypes>::ChainSpec, <Node::Types as NodeTypes>::Primitives>;
825
826 async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
827 let evm_config = OpEvmConfig::new(ctx.chain_spec(), OpRethReceiptBuilder::default());
828
829 Ok(evm_config)
830 }
831}
832
833#[derive(Debug)]
838pub struct OpPoolBuilder<T = crate::txpool::OpPooledTransaction> {
839 pub pool_config_overrides: PoolBuilderConfigOverrides,
841 pub enable_tx_conditional: bool,
843 pub supervisor_http: String,
845 pub supervisor_safety_level: SafetyLevel,
847 _pd: core::marker::PhantomData<T>,
849}
850
851impl<T> Default for OpPoolBuilder<T> {
852 fn default() -> Self {
853 Self {
854 pool_config_overrides: Default::default(),
855 enable_tx_conditional: false,
856 supervisor_http: DEFAULT_SUPERVISOR_URL.to_string(),
857 supervisor_safety_level: SafetyLevel::CrossUnsafe,
858 _pd: Default::default(),
859 }
860 }
861}
862
863impl<T> Clone for OpPoolBuilder<T> {
864 fn clone(&self) -> Self {
865 Self {
866 pool_config_overrides: self.pool_config_overrides.clone(),
867 enable_tx_conditional: self.enable_tx_conditional,
868 supervisor_http: self.supervisor_http.clone(),
869 supervisor_safety_level: self.supervisor_safety_level,
870 _pd: core::marker::PhantomData,
871 }
872 }
873}
874
875impl<T> OpPoolBuilder<T> {
876 pub const fn with_enable_tx_conditional(mut self, enable_tx_conditional: bool) -> Self {
878 self.enable_tx_conditional = enable_tx_conditional;
879 self
880 }
881
882 pub fn with_pool_config_overrides(
884 mut self,
885 pool_config_overrides: PoolBuilderConfigOverrides,
886 ) -> Self {
887 self.pool_config_overrides = pool_config_overrides;
888 self
889 }
890
891 pub fn with_supervisor(
893 mut self,
894 supervisor_client: String,
895 supervisor_safety_level: SafetyLevel,
896 ) -> Self {
897 self.supervisor_http = supervisor_client;
898 self.supervisor_safety_level = supervisor_safety_level;
899 self
900 }
901}
902
903impl<Node, T> PoolBuilder<Node> for OpPoolBuilder<T>
904where
905 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: OpHardforks>>,
906 T: EthPoolTransaction<Consensus = TxTy<Node::Types>> + OpPooledTx,
907{
908 type Pool = OpTransactionPool<Node::Provider, DiskFileBlobStore, T>;
909
910 async fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> {
911 let Self { pool_config_overrides, .. } = self;
912
913 if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) &&
915 self.supervisor_http == DEFAULT_SUPERVISOR_URL
916 {
917 info!(target: "reth::cli",
918 url=%DEFAULT_SUPERVISOR_URL,
919 "Default supervisor url is used, consider changing --rollup.supervisor-http."
920 );
921 }
922 let supervisor_client = SupervisorClient::builder(self.supervisor_http.clone())
923 .minimum_safety(self.supervisor_safety_level)
924 .build()
925 .await;
926
927 let blob_store = reth_node_builder::components::create_blob_store(ctx)?;
928 let validator = TransactionValidationTaskExecutor::eth_builder(ctx.provider().clone())
929 .no_eip4844()
930 .with_head_timestamp(ctx.head().timestamp)
931 .with_max_tx_input_bytes(ctx.config().txpool.max_tx_input_bytes)
932 .kzg_settings(ctx.kzg_settings()?)
933 .set_tx_fee_cap(ctx.config().rpc.rpc_tx_fee_cap)
934 .with_max_tx_gas_limit(ctx.config().txpool.max_tx_gas_limit)
935 .with_minimum_priority_fee(ctx.config().txpool.minimum_priority_fee)
936 .with_additional_tasks(
937 pool_config_overrides
938 .additional_validation_tasks
939 .unwrap_or_else(|| ctx.config().txpool.additional_validation_tasks),
940 )
941 .build_with_tasks(ctx.task_executor().clone(), blob_store.clone())
942 .map(|validator| {
943 OpTransactionValidator::new(validator)
944 .require_l1_data_gas_fee(!ctx.config().dev.dev)
947 .with_supervisor(supervisor_client.clone())
948 });
949
950 let final_pool_config = pool_config_overrides.apply(ctx.pool_config());
951
952 let transaction_pool = TxPoolBuilder::new(ctx)
953 .with_validator(validator)
954 .build_and_spawn_maintenance_task(blob_store, final_pool_config)?;
955
956 info!(target: "reth::cli", "Transaction pool initialized");
957 debug!(target: "reth::cli", "Spawned txpool maintenance task");
958
959 if ctx.chain_spec().is_interop_active_at_timestamp(ctx.head().timestamp) &&
961 self.supervisor_http == DEFAULT_SUPERVISOR_URL
962 {
963 let chain_events = ctx.provider().canonical_state_stream();
965 ctx.task_executor().spawn_critical(
966 "Op txpool interop maintenance task",
967 reth_optimism_txpool::maintain::maintain_transaction_pool_interop_future(
968 transaction_pool.clone(),
969 chain_events,
970 supervisor_client,
971 ),
972 );
973 debug!(target: "reth::cli", "Spawned Op interop txpool maintenance task");
974 }
975
976 if self.enable_tx_conditional {
977 let chain_events = ctx.provider().canonical_state_stream();
979 ctx.task_executor().spawn_critical(
980 "Op txpool conditional maintenance task",
981 reth_optimism_txpool::maintain::maintain_transaction_pool_conditional_future(
982 transaction_pool.clone(),
983 chain_events,
984 ),
985 );
986 debug!(target: "reth::cli", "Spawned Op conditional txpool maintenance task");
987 }
988
989 Ok(transaction_pool)
990 }
991}
992
993#[derive(Debug, Default, Clone)]
995pub struct OpPayloadBuilder<Txs = ()> {
996 pub compute_pending_block: bool,
1005 pub best_transactions: Txs,
1008 pub da_config: OpDAConfig,
1011}
1012
1013impl OpPayloadBuilder {
1014 pub fn new(compute_pending_block: bool) -> Self {
1017 Self { compute_pending_block, best_transactions: (), da_config: OpDAConfig::default() }
1018 }
1019
1020 pub fn with_da_config(mut self, da_config: OpDAConfig) -> Self {
1022 self.da_config = da_config;
1023 self
1024 }
1025}
1026
1027impl<Txs> OpPayloadBuilder<Txs> {
1028 pub fn with_transactions<T>(self, best_transactions: T) -> OpPayloadBuilder<T> {
1031 let Self { compute_pending_block, da_config, .. } = self;
1032 OpPayloadBuilder { compute_pending_block, best_transactions, da_config }
1033 }
1034}
1035
1036impl<Node, Pool, Txs, Evm, Attrs> PayloadBuilderBuilder<Node, Pool, Evm> for OpPayloadBuilder<Txs>
1037where
1038 Node: FullNodeTypes<
1039 Provider: ChainSpecProvider<ChainSpec: OpHardforks>,
1040 Types: NodeTypes<
1041 Primitives: OpPayloadPrimitives,
1042 Payload: PayloadTypes<
1043 BuiltPayload = OpBuiltPayload<PrimitivesTy<Node::Types>>,
1044 PayloadBuilderAttributes = Attrs,
1045 >,
1046 >,
1047 >,
1048 Evm: ConfigureEvm<
1049 Primitives = PrimitivesTy<Node::Types>,
1050 NextBlockEnvCtx: BuildNextEnv<
1051 Attrs,
1052 HeaderTy<Node::Types>,
1053 <Node::Types as NodeTypes>::ChainSpec,
1054 >,
1055 > + 'static,
1056 Pool: TransactionPool<Transaction: OpPooledTx<Consensus = TxTy<Node::Types>>> + Unpin + 'static,
1057 Txs: OpPayloadTransactions<Pool::Transaction>,
1058 Attrs: OpAttributes<Transaction = TxTy<Node::Types>>,
1059{
1060 type PayloadBuilder =
1061 reth_optimism_payload_builder::OpPayloadBuilder<Pool, Node::Provider, Evm, Txs, Attrs>;
1062
1063 async fn build_payload_builder(
1064 self,
1065 ctx: &BuilderContext<Node>,
1066 pool: Pool,
1067 evm_config: Evm,
1068 ) -> eyre::Result<Self::PayloadBuilder> {
1069 let payload_builder = reth_optimism_payload_builder::OpPayloadBuilder::with_builder_config(
1070 pool,
1071 ctx.provider().clone(),
1072 evm_config,
1073 OpBuilderConfig { da_config: self.da_config.clone() },
1074 )
1075 .with_transactions(self.best_transactions.clone())
1076 .set_compute_pending_block(self.compute_pending_block);
1077 Ok(payload_builder)
1078 }
1079}
1080
1081#[derive(Debug, Default)]
1083pub struct OpNetworkBuilder {
1084 pub disable_txpool_gossip: bool,
1086 pub disable_discovery_v4: bool,
1088}
1089
1090impl Clone for OpNetworkBuilder {
1091 fn clone(&self) -> Self {
1092 Self::new(self.disable_txpool_gossip, self.disable_discovery_v4)
1093 }
1094}
1095
1096impl OpNetworkBuilder {
1097 pub const fn new(disable_txpool_gossip: bool, disable_discovery_v4: bool) -> Self {
1099 Self { disable_txpool_gossip, disable_discovery_v4 }
1100 }
1101}
1102
1103impl OpNetworkBuilder {
1104 pub fn network_config<Node, NetworkP>(
1108 &self,
1109 ctx: &BuilderContext<Node>,
1110 ) -> eyre::Result<NetworkConfig<Node::Provider, NetworkP>>
1111 where
1112 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1113 NetworkP: NetworkPrimitives,
1114 {
1115 let Self { disable_txpool_gossip, disable_discovery_v4, .. } = self.clone();
1116 let args = &ctx.config().network;
1117 let network_builder = ctx
1118 .network_config_builder()?
1119 .apply(|mut builder| {
1121 let rlpx_socket = (args.addr, args.port).into();
1122 if disable_discovery_v4 || args.discovery.disable_discovery {
1123 builder = builder.disable_discv4_discovery();
1124 }
1125 if !args.discovery.disable_discovery {
1126 builder = builder.discovery_v5(
1127 args.discovery.discovery_v5_builder(
1128 rlpx_socket,
1129 ctx.config()
1130 .network
1131 .resolved_bootnodes()
1132 .or_else(|| ctx.chain_spec().bootnodes())
1133 .unwrap_or_default(),
1134 ),
1135 );
1136 }
1137
1138 builder
1139 });
1140
1141 let mut network_config = ctx.build_network_config(network_builder);
1142
1143 network_config.tx_gossip_disabled = disable_txpool_gossip;
1147
1148 Ok(network_config)
1149 }
1150}
1151
1152impl<Node, Pool> NetworkBuilder<Node, Pool> for OpNetworkBuilder
1153where
1154 Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>,
1155 Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Node::Types>>>
1156 + Unpin
1157 + 'static,
1158{
1159 type Network =
1160 NetworkHandle<BasicNetworkPrimitives<PrimitivesTy<Node::Types>, PoolPooledTx<Pool>>>;
1161
1162 async fn build_network(
1163 self,
1164 ctx: &BuilderContext<Node>,
1165 pool: Pool,
1166 ) -> eyre::Result<Self::Network> {
1167 let network_config = self.network_config(ctx)?;
1168 let network = NetworkManager::builder(network_config).await?;
1169 let handle = ctx.start_network(network, pool);
1170 info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized");
1171
1172 Ok(handle)
1173 }
1174}
1175
1176#[derive(Debug, Default, Clone)]
1178#[non_exhaustive]
1179pub struct OpConsensusBuilder;
1180
1181impl<Node> ConsensusBuilder<Node> for OpConsensusBuilder
1182where
1183 Node: FullNodeTypes<
1184 Types: NodeTypes<
1185 ChainSpec: OpHardforks,
1186 Primitives: NodePrimitives<Receipt: DepositReceipt>,
1187 >,
1188 >,
1189{
1190 type Consensus = Arc<OpBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
1191
1192 async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
1193 Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
1194 }
1195}
1196
1197#[derive(Debug, Default, Clone)]
1199#[non_exhaustive]
1200pub struct OpEngineValidatorBuilder;
1201
1202impl<Node> PayloadValidatorBuilder<Node> for OpEngineValidatorBuilder
1203where
1204 Node: FullNodeComponents<Types: OpNodeTypes>,
1205{
1206 type Validator = OpEngineValidator<
1207 Node::Provider,
1208 <<Node::Types as NodeTypes>::Primitives as NodePrimitives>::SignedTx,
1209 <Node::Types as NodeTypes>::ChainSpec,
1210 >;
1211
1212 async fn build(self, ctx: &AddOnsContext<'_, Node>) -> eyre::Result<Self::Validator> {
1213 Ok(OpEngineValidator::new::<KeccakKeyHasher>(
1214 ctx.config.chain.clone(),
1215 ctx.node.provider().clone(),
1216 ))
1217 }
1218}
1219
1220pub type OpNetworkPrimitives = BasicNetworkPrimitives<OpPrimitives, OpPooledTransaction>;