1#![expect(clippy::type_complexity)]
4#![allow(missing_debug_implementations)]
5
6use crate::{
7 common::WithConfigs,
8 components::NodeComponentsBuilder,
9 node::FullNode,
10 rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext},
11 BlockReaderFor, DebugNode, DebugNodeLauncher, EngineNodeLauncher, LaunchNode, Node,
12};
13use alloy_eips::eip4844::env_settings::EnvKzgSettings;
14use futures::Future;
15use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
16use reth_db_api::{database::Database, database_metrics::DatabaseMetrics};
17use reth_exex::ExExContext;
18use reth_network::{
19 transactions::{
20 config::{AnnouncementFilteringPolicy, StrictEthAnnouncementFilter},
21 TransactionPropagationPolicy, TransactionsManagerConfig,
22 },
23 NetworkBuilder, NetworkConfig, NetworkConfigBuilder, NetworkHandle, NetworkManager,
24 NetworkPrimitives,
25};
26use reth_node_api::{
27 FullNodeTypes, FullNodeTypesAdapter, NodeAddOns, NodeTypes, NodeTypesWithDBAdapter,
28};
29use reth_node_core::{
30 cli::config::{PayloadBuilderConfig, RethTransactionPoolConfig},
31 dirs::{ChainPath, DataDirPath},
32 node_config::NodeConfig,
33 primitives::Head,
34};
35use reth_provider::{
36 providers::{BlockchainProvider, NodeTypesForProvider, RocksDBProvider},
37 ChainSpecProvider, FullProvider,
38};
39use reth_tasks::TaskExecutor;
40use reth_transaction_pool::{PoolConfig, PoolTransaction, TransactionPool};
41use secp256k1::SecretKey;
42use std::sync::Arc;
43use tracing::{info, trace, warn};
44
45pub mod add_ons;
46
47mod states;
48pub use states::*;
49
50pub type RethFullAdapter<DB, Types> =
53 FullNodeTypesAdapter<Types, DB, BlockchainProvider<NodeTypesWithDBAdapter<Types, DB>>>;
54
55#[expect(clippy::doc_markdown)]
56#[cfg_attr(doc, aquamarine::aquamarine)]
57pub struct NodeBuilder<DB, ChainSpec> {
154 config: NodeConfig<ChainSpec>,
156 database: DB,
158 rocksdb_provider: Option<RocksDBProvider>,
160}
161
162impl<ChainSpec> NodeBuilder<(), ChainSpec> {
163 pub const fn new(config: NodeConfig<ChainSpec>) -> Self {
165 Self { config, database: (), rocksdb_provider: None }
166 }
167}
168
169impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec> {
170 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
172 &self.config
173 }
174
175 pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
177 &mut self.config
178 }
179
180 pub const fn db(&self) -> &DB {
182 &self.database
183 }
184
185 pub const fn db_mut(&mut self) -> &mut DB {
187 &mut self.database
188 }
189
190 pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
192 where
193 F: FnOnce(Self) -> Result<Self, R>,
194 {
195 f(self)
196 }
197
198 pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
200 where
201 F: FnOnce(Self) -> Result<Self, R>,
202 {
203 if cond {
204 f(self)
205 } else {
206 Ok(self)
207 }
208 }
209
210 pub fn apply<F>(self, f: F) -> Self
212 where
213 F: FnOnce(Self) -> Self,
214 {
215 f(self)
216 }
217
218 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
220 where
221 F: FnOnce(Self) -> Self,
222 {
223 if cond {
224 f(self)
225 } else {
226 self
227 }
228 }
229}
230
231impl<DB, ChainSpec: EthChainSpec> NodeBuilder<DB, ChainSpec> {
232 pub fn with_database<D>(self, database: D) -> NodeBuilder<D, ChainSpec> {
234 NodeBuilder { config: self.config, database, rocksdb_provider: self.rocksdb_provider }
235 }
236
237 pub fn with_rocksdb_provider(mut self, rocksdb_provider: RocksDBProvider) -> Self {
239 self.rocksdb_provider = Some(rocksdb_provider);
240 self
241 }
242
243 pub const fn with_launch_context(self, task_executor: TaskExecutor) -> WithLaunchContext<Self> {
247 WithLaunchContext { builder: self, task_executor }
248 }
249
250 #[cfg(feature = "test-utils")]
252 pub fn testing_node(
253 self,
254 task_executor: TaskExecutor,
255 ) -> WithLaunchContext<
256 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
257 > {
258 let path = reth_db::test_utils::tempdir_path();
259 self.testing_node_with_datadir(task_executor, path)
260 }
261
262 #[cfg(feature = "test-utils")]
266 pub fn testing_node_with_datadir(
267 mut self,
268 task_executor: TaskExecutor,
269 datadir: impl Into<std::path::PathBuf>,
270 ) -> WithLaunchContext<
271 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
272 > {
273 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(datadir.into());
274 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
275 datadir: path.clone(),
276 ..Default::default()
277 });
278
279 let data_dir =
280 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
281
282 let db = reth_db::test_utils::create_test_rw_db_with_datadir(data_dir.data_dir());
283
284 WithLaunchContext { builder: self.with_database(db), task_executor }
285 }
286
287 #[cfg(feature = "test-utils")]
292 pub fn testing_node_with_persistent_datadir(
293 mut self,
294 task_executor: TaskExecutor,
295 datadir: impl Into<std::path::PathBuf>,
296 ) -> WithLaunchContext<NodeBuilder<Arc<reth_db::DatabaseEnv>, ChainSpec>> {
297 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(datadir.into());
298 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
299 datadir: path.clone(),
300 ..Default::default()
301 });
302
303 let data_dir =
304 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
305 let db_path = data_dir.data_dir().join("db");
306 let db = reth_db::init_db(&db_path, reth_db::mdbx::DatabaseArguments::test())
307 .unwrap_or_else(|error| {
308 panic!("could not create test database at {db_path:?}: {error}")
309 });
310
311 WithLaunchContext { builder: self.with_database(Arc::new(db)), task_executor }
312 }
313}
314
315impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
316where
317 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
318 ChainSpec: EthChainSpec + EthereumHardforks,
319{
320 pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
322 where
323 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
324 {
325 self.with_types_and_provider()
326 }
327
328 pub fn with_types_and_provider<T, P>(
330 self,
331 ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
332 where
333 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
334 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
335 {
336 NodeBuilderWithTypes::new(self.config, self.database, self.rocksdb_provider)
337 }
338
339 pub fn node<N>(
343 self,
344 node: N,
345 ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
346 where
347 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
348 {
349 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
350 }
351}
352
353pub struct WithLaunchContext<Builder> {
358 builder: Builder,
359 task_executor: TaskExecutor,
360}
361
362impl<Builder> WithLaunchContext<Builder> {
363 pub const fn task_executor(&self) -> &TaskExecutor {
365 &self.task_executor
366 }
367}
368
369impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
370 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
372 self.builder.config()
373 }
374
375 pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
377 self.builder.config_mut()
378 }
379}
380
381impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
382where
383 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
384 ChainSpec: EthChainSpec + EthereumHardforks,
385{
386 pub fn with_rocksdb_provider(mut self, rocksdb_provider: RocksDBProvider) -> Self {
388 self.builder.rocksdb_provider = Some(rocksdb_provider);
389 self
390 }
391
392 pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
394 where
395 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
396 {
397 WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
398 }
399
400 pub fn with_types_and_provider<T, P>(
402 self,
403 ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
404 where
405 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
406 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
407 {
408 WithLaunchContext {
409 builder: self.builder.with_types_and_provider(),
410 task_executor: self.task_executor,
411 }
412 }
413
414 pub fn node<N>(
418 self,
419 node: N,
420 ) -> WithLaunchContext<
421 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
422 >
423 where
424 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
425 {
426 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
427 }
428
429 pub async fn launch_node<N>(
435 self,
436 node: N,
437 ) -> eyre::Result<
438 <EngineNodeLauncher as LaunchNode<
439 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
440 >>::Node,
441 >
442 where
443 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
444 N::AddOns: RethRpcAddOns<
445 NodeAdapter<
446 RethFullAdapter<DB, N>,
447 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
448 >,
449 >,
450 EngineNodeLauncher: LaunchNode<
451 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
452 >,
453 {
454 self.node(node).launch().await
455 }
456}
457
458impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
459 pub fn with_components<CB>(
461 self,
462 components_builder: CB,
463 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
464 where
465 CB: NodeComponentsBuilder<T>,
466 {
467 WithLaunchContext {
468 builder: self.builder.with_components(components_builder),
469 task_executor: self.task_executor,
470 }
471 }
472}
473
474impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
475where
476 T: FullNodeTypes,
477 CB: NodeComponentsBuilder<T>,
478{
479 pub fn with_add_ons<AO>(
482 self,
483 add_ons: AO,
484 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
485 where
486 AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
487 {
488 WithLaunchContext {
489 builder: self.builder.with_add_ons(add_ons),
490 task_executor: self.task_executor,
491 }
492 }
493}
494
495impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
496where
497 T: FullNodeTypes,
498 CB: NodeComponentsBuilder<T>,
499 AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
500{
501 pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
503 &self.builder.config
504 }
505
506 pub const fn config_mut(&mut self) -> &mut NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
508 &mut self.builder.config
509 }
510
511 pub const fn db(&self) -> &T::DB {
513 &self.builder.adapter.database
514 }
515
516 pub const fn db_mut(&mut self) -> &mut T::DB {
518 &mut self.builder.adapter.database
519 }
520
521 pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
523 where
524 F: FnOnce(Self) -> Result<Self, R>,
525 {
526 f(self)
527 }
528
529 pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
531 where
532 F: FnOnce(Self) -> Result<Self, R>,
533 {
534 if cond {
535 f(self)
536 } else {
537 Ok(self)
538 }
539 }
540
541 pub fn apply<F>(self, f: F) -> Self
543 where
544 F: FnOnce(Self) -> Self,
545 {
546 f(self)
547 }
548
549 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
551 where
552 F: FnOnce(Self) -> Self,
553 {
554 if cond {
555 f(self)
556 } else {
557 self
558 }
559 }
560
561 pub fn on_component_initialized<F>(self, hook: F) -> Self
563 where
564 F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
565 {
566 Self {
567 builder: self.builder.on_component_initialized(hook),
568 task_executor: self.task_executor,
569 }
570 }
571
572 pub fn on_node_started<F>(self, hook: F) -> Self
574 where
575 F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
576 + Send
577 + 'static,
578 {
579 Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
580 }
581
582 pub fn map_add_ons<F>(self, f: F) -> Self
605 where
606 F: FnOnce(AO) -> AO,
607 {
608 Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
609 }
610
611 pub fn on_rpc_started<F>(self, hook: F) -> Self
613 where
614 F: FnOnce(
615 RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
616 RethRpcServerHandles,
617 ) -> eyre::Result<()>
618 + Send
619 + 'static,
620 {
621 Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
622 }
623
624 pub fn extend_rpc_modules<F>(self, hook: F) -> Self
659 where
660 F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
661 + Send
662 + 'static,
663 {
664 Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
665 }
666
667 pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
673 where
674 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
675 R: Future<Output = eyre::Result<E>> + Send,
676 E: Future<Output = eyre::Result<()>> + Send,
677 {
678 Self {
679 builder: self.builder.install_exex(exex_id, exex),
680 task_executor: self.task_executor,
681 }
682 }
683
684 pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
690 where
691 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
692 R: Future<Output = eyre::Result<E>> + Send,
693 E: Future<Output = eyre::Result<()>> + Send,
694 {
695 if cond {
696 self.install_exex(exex_id, exex)
697 } else {
698 self
699 }
700 }
701
702 pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
704 where
705 L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
706 {
707 launcher.launch_node(self.builder).await
708 }
709
710 pub fn launch_with_fn<L, R>(self, launcher: L) -> R
712 where
713 L: FnOnce(Self) -> R,
714 {
715 launcher(self)
716 }
717
718 pub const fn check_launch(self) -> Self {
722 self
723 }
724
725 pub async fn launch(
727 self,
728 ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
729 where
730 EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
731 {
732 let launcher = self.engine_api_launcher();
733 self.builder.launch_with(launcher).await
734 }
735
736 pub fn launch_with_debug_capabilities(
741 self,
742 ) -> <DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Future
743 where
744 T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
745 DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
746 {
747 let Self { builder, task_executor } = self;
748
749 let engine_tree_config = builder.config.tree_config();
750
751 let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
752 task_executor,
753 builder.config.datadir(),
754 engine_tree_config,
755 ));
756 builder.launch_with(launcher)
757 }
758
759 pub fn engine_api_launcher(&self) -> EngineNodeLauncher {
762 let engine_tree_config = self.builder.config.tree_config();
763 EngineNodeLauncher::new(
764 self.task_executor.clone(),
765 self.builder.config.datadir(),
766 engine_tree_config,
767 )
768 }
769}
770
771pub struct BuilderContext<Node: FullNodeTypes> {
773 pub(crate) head: Head,
775 pub(crate) provider: Node::Provider,
777 pub(crate) executor: TaskExecutor,
779 pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
781 sender_recovery_cache: Option<reth_evm::SenderRecoveryCache>,
783}
784
785impl<Node: FullNodeTypes> BuilderContext<Node> {
786 pub fn new(
788 head: Head,
789 provider: Node::Provider,
790 executor: TaskExecutor,
791 config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
792 ) -> Self {
793 let sender_recovery_cache = config_container
794 .config
795 .engine
796 .sender_recovery_cache_enabled
797 .then(reth_evm::SenderRecoveryCache::default);
798 Self { head, provider, executor, config_container, sender_recovery_cache }
799 }
800
801 pub const fn provider(&self) -> &Node::Provider {
803 &self.provider
804 }
805
806 pub const fn head(&self) -> Head {
808 self.head
809 }
810
811 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
813 &self.config_container.config
814 }
815
816 pub const fn config_mut(&mut self) -> &mut NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
818 &mut self.config_container.config
819 }
820
821 pub const fn reth_config(&self) -> &reth_config::Config {
823 &self.config_container.toml_config
824 }
825
826 pub const fn task_executor(&self) -> &TaskExecutor {
830 &self.executor
831 }
832
833 pub const fn sender_recovery_cache(&self) -> Option<&reth_evm::SenderRecoveryCache> {
835 self.sender_recovery_cache.as_ref()
836 }
837
838 pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
840 self.provider().chain_spec()
841 }
842
843 pub const fn is_dev(&self) -> bool {
845 self.config().dev.dev
846 }
847
848 pub fn pool_config(&self) -> PoolConfig {
850 self.config().txpool.pool_config()
851 }
852
853 pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
855 Ok(EnvKzgSettings::Default)
856 }
857
858 pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
860 self.config().builder.clone()
861 }
862
863 pub fn start_network<N, Pool>(
868 &self,
869 builder: NetworkBuilder<(), (), N>,
870 pool: Pool,
871 ) -> NetworkHandle<N>
872 where
873 N: NetworkPrimitives,
874 Pool: TransactionPool<
875 Transaction: PoolTransaction<
876 Consensus = N::BroadcastedTransaction,
877 Pooled = N::PooledTransaction,
878 >,
879 > + Unpin
880 + 'static,
881 Node::Provider: BlockReaderFor<N>,
882 {
883 self.start_network_with(
884 builder,
885 pool,
886 self.config().network.transactions_manager_config(),
887 self.config().network.tx_propagation_policy,
888 )
889 }
890
891 pub fn start_network_with<Pool, N, Policy>(
899 &self,
900 builder: NetworkBuilder<(), (), N>,
901 pool: Pool,
902 tx_config: TransactionsManagerConfig,
903 propagation_policy: Policy,
904 ) -> NetworkHandle<N>
905 where
906 N: NetworkPrimitives,
907 Pool: TransactionPool<
908 Transaction: PoolTransaction<
909 Consensus = N::BroadcastedTransaction,
910 Pooled = N::PooledTransaction,
911 >,
912 > + Unpin
913 + 'static,
914 Node::Provider: BlockReaderFor<N>,
915 Policy: TransactionPropagationPolicy<N>,
916 {
917 self.start_network_with_policies(
918 builder,
919 pool,
920 tx_config,
921 propagation_policy,
922 StrictEthAnnouncementFilter::default(),
923 )
924 }
925
926 pub fn start_network_with_policies<Pool, N, PropPolicy, AnnPolicy>(
935 &self,
936 builder: NetworkBuilder<(), (), N>,
937 pool: Pool,
938 tx_config: TransactionsManagerConfig,
939 propagation_policy: PropPolicy,
940 announcement_policy: AnnPolicy,
941 ) -> NetworkHandle<N>
942 where
943 N: NetworkPrimitives,
944 Pool: TransactionPool<
945 Transaction: PoolTransaction<
946 Consensus = N::BroadcastedTransaction,
947 Pooled = N::PooledTransaction,
948 >,
949 > + Unpin
950 + 'static,
951 Node::Provider: BlockReaderFor<N>,
952 PropPolicy: TransactionPropagationPolicy<N>,
953 AnnPolicy: AnnouncementFilteringPolicy<N>,
954 {
955 let (handle, network, txpool, eth) = builder
956 .transactions_with_policies(
957 pool.clone(),
958 tx_config,
959 propagation_policy,
960 announcement_policy,
961 )
962 .map_transactions(|transactions| {
963 if let Some(cache) = self.sender_recovery_cache.clone() {
964 transactions.with_sender_recovery_cache(cache)
965 } else {
966 transactions
967 }
968 })
969 .request_handler_with_blob_store(self.provider().clone(), pool.blob_store())
970 .split_with_handle();
971
972 self.executor.spawn_critical_blocking_task("p2p txpool", txpool);
973 self.executor.spawn_critical_blocking_task("p2p eth request handler", eth);
974
975 let default_peers_path = self.config().datadir().known_peers();
976 let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
977 self.executor.spawn_critical_with_graceful_shutdown_signal(
978 "p2p network task",
979 |shutdown| {
980 network.run_until_graceful_shutdown(shutdown, |network| {
981 if let Some(peers_file) = known_peers_file {
982 let num_known_peers = network.num_known_peers();
983 trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
984 match network.write_peers_to_file(peers_file.as_path()) {
985 Ok(_) => {
986 info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
987 }
988 Err(err) => {
989 warn!(target: "reth::cli", %err, "Failed to write network peers to file");
990 }
991 }
992 }
993 })
994 },
995 );
996
997 handle
998 }
999
1000 fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
1002 let secret_key = self.config().network.secret_key(data_dir.p2p_secret())?;
1003 Ok(secret_key)
1004 }
1005
1006 pub fn build_network_config<N>(
1008 &self,
1009 network_builder: NetworkConfigBuilder<N>,
1010 ) -> NetworkConfig<Node::Provider, N>
1011 where
1012 N: NetworkPrimitives,
1013 Node::Types: NodeTypes<ChainSpec: Hardforks>,
1014 {
1015 network_builder.build(self.provider.clone())
1016 }
1017}
1018
1019impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
1020 pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
1022 where
1023 N: NetworkPrimitives,
1024 {
1025 let network_config = self.network_config()?;
1026 let builder = NetworkManager::builder(network_config).await?;
1027 Ok(builder)
1028 }
1029
1030 pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
1032 where
1033 N: NetworkPrimitives,
1034 {
1035 let network_builder = self.network_config_builder();
1036 Ok(self.build_network_config(network_builder?))
1037 }
1038
1039 pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
1041 where
1042 N: NetworkPrimitives,
1043 {
1044 let secret_key = self.network_secret(&self.config().datadir())?;
1045 let default_peers_path = self.config().datadir().known_peers();
1046 let builder = self
1047 .config()
1048 .network
1049 .network_config(
1050 self.reth_config(),
1051 self.config().chain.clone(),
1052 secret_key,
1053 default_peers_path,
1054 self.executor.clone(),
1055 )
1056 .set_head(self.head);
1057
1058 Ok(builder)
1059 }
1060}
1061
1062impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
1063 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1064 f.debug_struct("BuilderContext")
1065 .field("head", &self.head)
1066 .field("provider", &std::any::type_name::<Node::Provider>())
1067 .field("executor", &self.executor)
1068 .field("config", &self.config())
1069 .finish()
1070 }
1071}
1072
1073#[cfg(all(test, feature = "test-utils"))]
1074mod tests {
1075 use super::*;
1076 use reth_chainspec::ChainSpec;
1077 use reth_tasks::Runtime;
1078
1079 #[test]
1080 fn persistent_test_datadir_can_be_reopened() {
1081 let root = tempfile::tempdir().unwrap();
1082 let datadir = root.path().join("node");
1083 let runtime = Runtime::test();
1084
1085 let config = || NodeConfig::new(Arc::new(ChainSpec::<alloy_consensus::Header>::default()));
1086 let first = NodeBuilder::new(config())
1087 .testing_node_with_persistent_datadir(runtime.clone(), datadir.clone());
1088 assert!(datadir.join("db").exists());
1089 drop(first);
1090 assert!(datadir.join("db").exists());
1091
1092 let reopened = NodeBuilder::new(config())
1093 .testing_node_with_persistent_datadir(runtime, datadir.clone());
1094 drop(reopened);
1095 assert!(datadir.join("db").exists());
1096 }
1097}