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