1#![allow(clippy::type_complexity, missing_debug_implementations)]
4
5use crate::{
6 common::WithConfigs,
7 components::NodeComponentsBuilder,
8 node::FullNode,
9 rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext},
10 BlockReaderFor, DebugNode, DebugNodeLauncher, EngineNodeLauncher, LaunchNode, Node,
11};
12use alloy_eips::eip4844::env_settings::EnvKzgSettings;
13use futures::Future;
14use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
15use reth_cli_util::get_secret_key;
16use reth_db_api::{database::Database, database_metrics::DatabaseMetrics};
17use reth_exex::ExExContext;
18use reth_network::{
19 transactions::{TransactionPropagationPolicy, TransactionsManagerConfig},
20 NetworkBuilder, NetworkConfig, NetworkConfigBuilder, NetworkHandle, NetworkManager,
21 NetworkPrimitives,
22};
23use reth_node_api::{
24 FullNodePrimitives, FullNodeTypes, FullNodeTypesAdapter, NodeAddOns, NodeTypes,
25 NodeTypesWithDBAdapter,
26};
27use reth_node_core::{
28 cli::config::{PayloadBuilderConfig, RethTransactionPoolConfig},
29 dirs::{ChainPath, DataDirPath},
30 node_config::NodeConfig,
31 primitives::Head,
32};
33use reth_provider::{
34 providers::{BlockchainProvider, NodeTypesForProvider},
35 ChainSpecProvider, FullProvider,
36};
37use reth_tasks::TaskExecutor;
38use reth_transaction_pool::{PoolConfig, PoolTransaction, TransactionPool};
39use secp256k1::SecretKey;
40use std::sync::Arc;
41use tracing::{info, trace, warn};
42
43pub mod add_ons;
44
45mod states;
46pub use states::*;
47
48pub type RethFullAdapter<DB, Types> =
51 FullNodeTypesAdapter<Types, DB, BlockchainProvider<NodeTypesWithDBAdapter<Types, DB>>>;
52
53#[expect(clippy::doc_markdown)]
54#[cfg_attr(doc, aquamarine::aquamarine)]
55pub struct NodeBuilder<DB, ChainSpec> {
152 config: NodeConfig<ChainSpec>,
154 database: DB,
156}
157
158impl<ChainSpec> NodeBuilder<(), ChainSpec> {
159 pub const fn new(config: NodeConfig<ChainSpec>) -> Self {
161 Self { config, database: () }
162 }
163
164 pub fn apply<F>(self, f: F) -> Self
166 where
167 F: FnOnce(Self) -> Self,
168 {
169 f(self)
170 }
171
172 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
174 where
175 F: FnOnce(Self) -> Self,
176 {
177 if cond {
178 f(self)
179 } else {
180 self
181 }
182 }
183}
184
185impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec> {
186 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
188 &self.config
189 }
190
191 pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
193 &mut self.config
194 }
195}
196
197impl<DB, ChainSpec: EthChainSpec> NodeBuilder<DB, ChainSpec> {
198 pub fn with_database<D>(self, database: D) -> NodeBuilder<D, ChainSpec> {
200 NodeBuilder { config: self.config, database }
201 }
202
203 pub const fn with_launch_context(self, task_executor: TaskExecutor) -> WithLaunchContext<Self> {
207 WithLaunchContext { builder: self, task_executor }
208 }
209
210 #[cfg(feature = "test-utils")]
212 pub fn testing_node(
213 mut self,
214 task_executor: TaskExecutor,
215 ) -> WithLaunchContext<
216 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
217 > {
218 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(
219 reth_db::test_utils::tempdir_path(),
220 );
221 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
222 datadir: path.clone(),
223 ..Default::default()
224 });
225
226 let data_dir =
227 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
228
229 let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
230
231 WithLaunchContext { builder: self.with_database(db), task_executor }
232 }
233}
234
235impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
236where
237 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
238 ChainSpec: EthChainSpec + EthereumHardforks,
239{
240 pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
242 where
243 T: NodeTypes<ChainSpec = ChainSpec> + NodeTypesForProvider,
244 {
245 self.with_types_and_provider()
246 }
247
248 pub fn with_types_and_provider<T, P>(
250 self,
251 ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
252 where
253 T: NodeTypes<ChainSpec = ChainSpec> + NodeTypesForProvider,
254 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
255 {
256 NodeBuilderWithTypes::new(self.config, self.database)
257 }
258
259 pub fn node<N>(
263 self,
264 node: N,
265 ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
266 where
267 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
268 {
269 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
270 }
271}
272
273pub struct WithLaunchContext<Builder> {
278 builder: Builder,
279 task_executor: TaskExecutor,
280}
281
282impl<Builder> WithLaunchContext<Builder> {
283 pub const fn task_executor(&self) -> &TaskExecutor {
285 &self.task_executor
286 }
287}
288
289impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
290 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
292 self.builder.config()
293 }
294}
295
296impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
297where
298 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
299 ChainSpec: EthChainSpec + EthereumHardforks,
300{
301 pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
303 where
304 T: NodeTypes<ChainSpec = ChainSpec> + NodeTypesForProvider,
305 {
306 WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
307 }
308
309 pub fn with_types_and_provider<T, P>(
311 self,
312 ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
313 where
314 T: NodeTypes<ChainSpec = ChainSpec> + NodeTypesForProvider,
315 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
316 {
317 WithLaunchContext {
318 builder: self.builder.with_types_and_provider(),
319 task_executor: self.task_executor,
320 }
321 }
322
323 pub fn node<N>(
327 self,
328 node: N,
329 ) -> WithLaunchContext<
330 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
331 >
332 where
333 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
334 {
335 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
336 }
337
338 pub async fn launch_node<N>(
344 self,
345 node: N,
346 ) -> eyre::Result<
347 <EngineNodeLauncher as LaunchNode<
348 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
349 >>::Node,
350 >
351 where
352 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
353 N::AddOns: RethRpcAddOns<
354 NodeAdapter<
355 RethFullAdapter<DB, N>,
356 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
357 >,
358 >,
359 N::Primitives: FullNodePrimitives,
360 EngineNodeLauncher: LaunchNode<
361 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
362 >,
363 {
364 self.node(node).launch().await
365 }
366}
367
368impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
369 pub fn with_components<CB>(
371 self,
372 components_builder: CB,
373 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
374 where
375 CB: NodeComponentsBuilder<T>,
376 {
377 WithLaunchContext {
378 builder: self.builder.with_components(components_builder),
379 task_executor: self.task_executor,
380 }
381 }
382}
383
384impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
385where
386 T: FullNodeTypes,
387 CB: NodeComponentsBuilder<T>,
388{
389 pub fn with_add_ons<AO>(
392 self,
393 add_ons: AO,
394 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
395 where
396 AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
397 {
398 WithLaunchContext {
399 builder: self.builder.with_add_ons(add_ons),
400 task_executor: self.task_executor,
401 }
402 }
403}
404
405impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
406where
407 T: FullNodeTypes,
408 CB: NodeComponentsBuilder<T>,
409 AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
410{
411 pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
413 &self.builder.config
414 }
415
416 pub fn apply<F>(self, f: F) -> Self
418 where
419 F: FnOnce(Self) -> Self,
420 {
421 f(self)
422 }
423
424 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
426 where
427 F: FnOnce(Self) -> Self,
428 {
429 if cond {
430 f(self)
431 } else {
432 self
433 }
434 }
435
436 pub fn on_component_initialized<F>(self, hook: F) -> Self
438 where
439 F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
440 {
441 Self {
442 builder: self.builder.on_component_initialized(hook),
443 task_executor: self.task_executor,
444 }
445 }
446
447 pub fn on_node_started<F>(self, hook: F) -> Self
449 where
450 F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
451 + Send
452 + 'static,
453 {
454 Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
455 }
456
457 pub fn map_add_ons<F>(self, f: F) -> Self
459 where
460 F: FnOnce(AO) -> AO,
461 {
462 Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
463 }
464
465 pub fn on_rpc_started<F>(self, hook: F) -> Self
467 where
468 F: FnOnce(
469 RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
470 RethRpcServerHandles,
471 ) -> eyre::Result<()>
472 + Send
473 + 'static,
474 {
475 Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
476 }
477
478 pub fn extend_rpc_modules<F>(self, hook: F) -> Self
480 where
481 F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
482 + Send
483 + 'static,
484 {
485 Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
486 }
487
488 pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
494 where
495 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
496 R: Future<Output = eyre::Result<E>> + Send,
497 E: Future<Output = eyre::Result<()>> + Send,
498 {
499 Self {
500 builder: self.builder.install_exex(exex_id, exex),
501 task_executor: self.task_executor,
502 }
503 }
504
505 pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
511 where
512 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
513 R: Future<Output = eyre::Result<E>> + Send,
514 E: Future<Output = eyre::Result<()>> + Send,
515 {
516 if cond {
517 self.install_exex(exex_id, exex)
518 } else {
519 self
520 }
521 }
522
523 pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
525 where
526 L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
527 {
528 launcher.launch_node(self.builder).await
529 }
530
531 pub fn launch_with_fn<L, R>(self, launcher: L) -> R
533 where
534 L: FnOnce(Self) -> R,
535 {
536 launcher(self)
537 }
538
539 pub const fn check_launch(self) -> Self {
543 self
544 }
545
546 pub async fn launch(
548 self,
549 ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
550 where
551 EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
552 {
553 let Self { builder, task_executor } = self;
554
555 let engine_tree_config = builder.config.engine.tree_config();
556
557 let launcher =
558 EngineNodeLauncher::new(task_executor, builder.config.datadir(), engine_tree_config);
559 builder.launch_with(launcher).await
560 }
561
562 pub async fn launch_with_debug_capabilities(
567 self,
568 ) -> eyre::Result<<DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
569 where
570 T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
571 DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
572 {
573 let Self { builder, task_executor } = self;
574
575 let engine_tree_config = builder.config.engine.tree_config();
576
577 let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
578 task_executor,
579 builder.config.datadir(),
580 engine_tree_config,
581 ));
582 builder.launch_with(launcher).await
583 }
584}
585
586pub struct BuilderContext<Node: FullNodeTypes> {
588 pub(crate) head: Head,
590 pub(crate) provider: Node::Provider,
592 pub(crate) executor: TaskExecutor,
594 pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
596}
597
598impl<Node: FullNodeTypes> BuilderContext<Node> {
599 pub const fn new(
601 head: Head,
602 provider: Node::Provider,
603 executor: TaskExecutor,
604 config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
605 ) -> Self {
606 Self { head, provider, executor, config_container }
607 }
608
609 pub const fn provider(&self) -> &Node::Provider {
611 &self.provider
612 }
613
614 pub const fn head(&self) -> Head {
616 self.head
617 }
618
619 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
621 &self.config_container.config
622 }
623
624 pub const fn reth_config(&self) -> &reth_config::Config {
626 &self.config_container.toml_config
627 }
628
629 pub const fn task_executor(&self) -> &TaskExecutor {
633 &self.executor
634 }
635
636 pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
638 self.provider().chain_spec()
639 }
640
641 pub const fn is_dev(&self) -> bool {
643 self.config().dev.dev
644 }
645
646 pub fn pool_config(&self) -> PoolConfig {
648 self.config().txpool.pool_config()
649 }
650
651 pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
653 Ok(EnvKzgSettings::Default)
654 }
655
656 pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
658 self.config().builder.clone()
659 }
660
661 pub fn start_network<N, Pool>(
666 &self,
667 builder: NetworkBuilder<(), (), N>,
668 pool: Pool,
669 ) -> NetworkHandle<N>
670 where
671 N: NetworkPrimitives,
672 Pool: TransactionPool<
673 Transaction: PoolTransaction<
674 Consensus = N::BroadcastedTransaction,
675 Pooled = N::PooledTransaction,
676 >,
677 > + Unpin
678 + 'static,
679 Node::Provider: BlockReaderFor<N>,
680 {
681 self.start_network_with(
682 builder,
683 pool,
684 self.config().network.transactions_manager_config(),
685 self.config().network.tx_propagation_policy,
686 )
687 }
688
689 pub fn start_network_with<Pool, N, Policy>(
696 &self,
697 builder: NetworkBuilder<(), (), N>,
698 pool: Pool,
699 tx_config: TransactionsManagerConfig,
700 propagation_policy: Policy,
701 ) -> NetworkHandle<N>
702 where
703 N: NetworkPrimitives,
704 Pool: TransactionPool<
705 Transaction: PoolTransaction<
706 Consensus = N::BroadcastedTransaction,
707 Pooled = N::PooledTransaction,
708 >,
709 > + Unpin
710 + 'static,
711 Node::Provider: BlockReaderFor<N>,
712 Policy: TransactionPropagationPolicy,
713 {
714 let (handle, network, txpool, eth) = builder
715 .transactions_with_policy(pool, tx_config, propagation_policy)
716 .request_handler(self.provider().clone())
717 .split_with_handle();
718
719 self.executor.spawn_critical("p2p txpool", txpool);
720 self.executor.spawn_critical("p2p eth request handler", eth);
721
722 let default_peers_path = self.config().datadir().known_peers();
723 let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
724 self.executor.spawn_critical_with_graceful_shutdown_signal(
725 "p2p network task",
726 |shutdown| {
727 network.run_until_graceful_shutdown(shutdown, |network| {
728 if let Some(peers_file) = known_peers_file {
729 let num_known_peers = network.num_known_peers();
730 trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
731 match network.write_peers_to_file(peers_file.as_path()) {
732 Ok(_) => {
733 info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
734 }
735 Err(err) => {
736 warn!(target: "reth::cli", %err, "Failed to write network peers to file");
737 }
738 }
739 }
740 })
741 },
742 );
743
744 handle
745 }
746
747 fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
749 let network_secret_path =
750 self.config().network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
751 let secret_key = get_secret_key(&network_secret_path)?;
752 Ok(secret_key)
753 }
754
755 pub fn build_network_config<N>(
757 &self,
758 network_builder: NetworkConfigBuilder<N>,
759 ) -> NetworkConfig<Node::Provider, N>
760 where
761 N: NetworkPrimitives,
762 Node::Types: NodeTypes<ChainSpec: Hardforks>,
763 {
764 network_builder.build(self.provider.clone())
765 }
766}
767
768impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
769 pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
771 where
772 N: NetworkPrimitives,
773 {
774 let network_config = self.network_config()?;
775 let builder = NetworkManager::builder(network_config).await?;
776 Ok(builder)
777 }
778
779 pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
781 where
782 N: NetworkPrimitives,
783 {
784 let network_builder = self.network_config_builder();
785 Ok(self.build_network_config(network_builder?))
786 }
787
788 pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
790 where
791 N: NetworkPrimitives,
792 {
793 let secret_key = self.network_secret(&self.config().datadir())?;
794 let default_peers_path = self.config().datadir().known_peers();
795 let builder = self
796 .config()
797 .network
798 .network_config(
799 self.reth_config(),
800 self.config().chain.clone(),
801 secret_key,
802 default_peers_path,
803 )
804 .with_task_executor(Box::new(self.executor.clone()))
805 .set_head(self.head);
806
807 Ok(builder)
808 }
809}
810
811impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
812 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
813 f.debug_struct("BuilderContext")
814 .field("head", &self.head)
815 .field("provider", &std::any::type_name::<Node::Provider>())
816 .field("executor", &self.executor)
817 .field("config", &self.config())
818 .finish()
819 }
820}