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::{fmt::Debug, 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
165impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec> {
166 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
168 &self.config
169 }
170
171 pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
173 &mut self.config
174 }
175
176 pub const fn db(&self) -> &DB {
178 &self.database
179 }
180
181 pub const fn db_mut(&mut self) -> &mut DB {
183 &mut self.database
184 }
185
186 pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
188 where
189 F: FnOnce(Self) -> Result<Self, R>,
190 {
191 f(self)
192 }
193
194 pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
196 where
197 F: FnOnce(Self) -> Result<Self, R>,
198 {
199 if cond {
200 f(self)
201 } else {
202 Ok(self)
203 }
204 }
205
206 pub fn apply<F>(self, f: F) -> Self
208 where
209 F: FnOnce(Self) -> Self,
210 {
211 f(self)
212 }
213
214 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
216 where
217 F: FnOnce(Self) -> Self,
218 {
219 if cond {
220 f(self)
221 } else {
222 self
223 }
224 }
225}
226
227impl<DB, ChainSpec: EthChainSpec> NodeBuilder<DB, ChainSpec> {
228 pub fn with_database<D>(self, database: D) -> NodeBuilder<D, ChainSpec> {
230 NodeBuilder { config: self.config, database }
231 }
232
233 pub const fn with_launch_context(self, task_executor: TaskExecutor) -> WithLaunchContext<Self> {
237 WithLaunchContext { builder: self, task_executor }
238 }
239
240 #[cfg(feature = "test-utils")]
242 pub fn testing_node(
243 self,
244 task_executor: TaskExecutor,
245 ) -> WithLaunchContext<
246 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
247 > {
248 let path = reth_db::test_utils::tempdir_path();
249 self.testing_node_with_datadir(task_executor, path)
250 }
251
252 #[cfg(feature = "test-utils")]
254 pub fn testing_node_with_datadir(
255 mut self,
256 task_executor: TaskExecutor,
257 datadir: impl Into<std::path::PathBuf>,
258 ) -> WithLaunchContext<
259 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
260 > {
261 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(datadir.into());
262 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
263 datadir: path.clone(),
264 ..Default::default()
265 });
266
267 let data_dir =
268 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
269
270 let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
271
272 WithLaunchContext { builder: self.with_database(db), task_executor }
273 }
274}
275
276impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
277where
278 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
279 ChainSpec: EthChainSpec + EthereumHardforks,
280{
281 pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
283 where
284 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
285 {
286 self.with_types_and_provider()
287 }
288
289 pub fn with_types_and_provider<T, P>(
291 self,
292 ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
293 where
294 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
295 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
296 {
297 NodeBuilderWithTypes::new(self.config, self.database)
298 }
299
300 pub fn node<N>(
304 self,
305 node: N,
306 ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
307 where
308 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
309 {
310 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
311 }
312}
313
314pub struct WithLaunchContext<Builder> {
319 builder: Builder,
320 task_executor: TaskExecutor,
321}
322
323impl<Builder> WithLaunchContext<Builder> {
324 pub const fn task_executor(&self) -> &TaskExecutor {
326 &self.task_executor
327 }
328}
329
330impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
331 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
333 self.builder.config()
334 }
335}
336
337impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
338where
339 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
340 ChainSpec: EthChainSpec + EthereumHardforks,
341{
342 pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
344 where
345 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
346 {
347 WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
348 }
349
350 pub fn with_types_and_provider<T, P>(
352 self,
353 ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
354 where
355 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
356 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
357 {
358 WithLaunchContext {
359 builder: self.builder.with_types_and_provider(),
360 task_executor: self.task_executor,
361 }
362 }
363
364 pub fn node<N>(
368 self,
369 node: N,
370 ) -> WithLaunchContext<
371 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
372 >
373 where
374 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
375 {
376 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
377 }
378
379 pub async fn launch_node<N>(
385 self,
386 node: N,
387 ) -> eyre::Result<
388 <EngineNodeLauncher as LaunchNode<
389 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
390 >>::Node,
391 >
392 where
393 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
394 N::AddOns: RethRpcAddOns<
395 NodeAdapter<
396 RethFullAdapter<DB, N>,
397 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
398 >,
399 >,
400 N::Primitives: FullNodePrimitives,
401 EngineNodeLauncher: LaunchNode<
402 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
403 >,
404 {
405 self.node(node).launch().await
406 }
407}
408
409impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
410 pub fn with_components<CB>(
412 self,
413 components_builder: CB,
414 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
415 where
416 CB: NodeComponentsBuilder<T>,
417 {
418 WithLaunchContext {
419 builder: self.builder.with_components(components_builder),
420 task_executor: self.task_executor,
421 }
422 }
423}
424
425impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
426where
427 T: FullNodeTypes,
428 CB: NodeComponentsBuilder<T>,
429{
430 pub fn with_add_ons<AO>(
433 self,
434 add_ons: AO,
435 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
436 where
437 AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
438 {
439 WithLaunchContext {
440 builder: self.builder.with_add_ons(add_ons),
441 task_executor: self.task_executor,
442 }
443 }
444}
445
446impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
447where
448 T: FullNodeTypes,
449 CB: NodeComponentsBuilder<T>,
450 AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
451{
452 pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
454 &self.builder.config
455 }
456
457 pub const fn db(&self) -> &T::DB {
459 &self.builder.adapter.database
460 }
461
462 pub const fn db_mut(&mut self) -> &mut T::DB {
464 &mut self.builder.adapter.database
465 }
466
467 pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
469 where
470 F: FnOnce(Self) -> Result<Self, R>,
471 {
472 f(self)
473 }
474
475 pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
477 where
478 F: FnOnce(Self) -> Result<Self, R>,
479 {
480 if cond {
481 f(self)
482 } else {
483 Ok(self)
484 }
485 }
486
487 pub fn apply<F>(self, f: F) -> Self
489 where
490 F: FnOnce(Self) -> Self,
491 {
492 f(self)
493 }
494
495 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
497 where
498 F: FnOnce(Self) -> Self,
499 {
500 if cond {
501 f(self)
502 } else {
503 self
504 }
505 }
506
507 pub fn on_component_initialized<F>(self, hook: F) -> Self
509 where
510 F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
511 {
512 Self {
513 builder: self.builder.on_component_initialized(hook),
514 task_executor: self.task_executor,
515 }
516 }
517
518 pub fn on_node_started<F>(self, hook: F) -> Self
520 where
521 F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
522 + Send
523 + 'static,
524 {
525 Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
526 }
527
528 pub fn map_add_ons<F>(self, f: F) -> Self
530 where
531 F: FnOnce(AO) -> AO,
532 {
533 Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
534 }
535
536 pub fn on_rpc_started<F>(self, hook: F) -> Self
538 where
539 F: FnOnce(
540 RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
541 RethRpcServerHandles,
542 ) -> eyre::Result<()>
543 + Send
544 + 'static,
545 {
546 Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
547 }
548
549 pub fn extend_rpc_modules<F>(self, hook: F) -> Self
584 where
585 F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
586 + Send
587 + 'static,
588 {
589 Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
590 }
591
592 pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
598 where
599 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
600 R: Future<Output = eyre::Result<E>> + Send,
601 E: Future<Output = eyre::Result<()>> + Send,
602 {
603 Self {
604 builder: self.builder.install_exex(exex_id, exex),
605 task_executor: self.task_executor,
606 }
607 }
608
609 pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
615 where
616 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
617 R: Future<Output = eyre::Result<E>> + Send,
618 E: Future<Output = eyre::Result<()>> + Send,
619 {
620 if cond {
621 self.install_exex(exex_id, exex)
622 } else {
623 self
624 }
625 }
626
627 pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
629 where
630 L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
631 {
632 launcher.launch_node(self.builder).await
633 }
634
635 pub fn launch_with_fn<L, R>(self, launcher: L) -> R
637 where
638 L: FnOnce(Self) -> R,
639 {
640 launcher(self)
641 }
642
643 pub const fn check_launch(self) -> Self {
647 self
648 }
649
650 pub async fn launch(
652 self,
653 ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
654 where
655 EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
656 {
657 let launcher = self.engine_api_launcher();
658 self.builder.launch_with(launcher).await
659 }
660
661 pub async fn launch_with_debug_capabilities(
666 self,
667 ) -> eyre::Result<<DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
668 where
669 T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
670 DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
671 {
672 let Self { builder, task_executor } = self;
673
674 let engine_tree_config = builder.config.engine.tree_config();
675
676 let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
677 task_executor,
678 builder.config.datadir(),
679 engine_tree_config,
680 ));
681 builder.launch_with(launcher).await
682 }
683
684 pub fn engine_api_launcher(&self) -> EngineNodeLauncher {
687 let engine_tree_config = self.builder.config.engine.tree_config();
688 EngineNodeLauncher::new(
689 self.task_executor.clone(),
690 self.builder.config.datadir(),
691 engine_tree_config,
692 )
693 }
694}
695
696pub struct BuilderContext<Node: FullNodeTypes> {
698 pub(crate) head: Head,
700 pub(crate) provider: Node::Provider,
702 pub(crate) executor: TaskExecutor,
704 pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
706}
707
708impl<Node: FullNodeTypes> BuilderContext<Node> {
709 pub const fn new(
711 head: Head,
712 provider: Node::Provider,
713 executor: TaskExecutor,
714 config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
715 ) -> Self {
716 Self { head, provider, executor, config_container }
717 }
718
719 pub const fn provider(&self) -> &Node::Provider {
721 &self.provider
722 }
723
724 pub const fn head(&self) -> Head {
726 self.head
727 }
728
729 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
731 &self.config_container.config
732 }
733
734 pub const fn reth_config(&self) -> &reth_config::Config {
736 &self.config_container.toml_config
737 }
738
739 pub const fn task_executor(&self) -> &TaskExecutor {
743 &self.executor
744 }
745
746 pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
748 self.provider().chain_spec()
749 }
750
751 pub const fn is_dev(&self) -> bool {
753 self.config().dev.dev
754 }
755
756 pub fn pool_config(&self) -> PoolConfig {
758 self.config().txpool.pool_config()
759 }
760
761 pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
763 Ok(EnvKzgSettings::Default)
764 }
765
766 pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
768 self.config().builder.clone()
769 }
770
771 pub fn start_network<N, Pool>(
776 &self,
777 builder: NetworkBuilder<(), (), N>,
778 pool: Pool,
779 ) -> NetworkHandle<N>
780 where
781 N: NetworkPrimitives,
782 Pool: TransactionPool<
783 Transaction: PoolTransaction<
784 Consensus = N::BroadcastedTransaction,
785 Pooled = N::PooledTransaction,
786 >,
787 > + Unpin
788 + 'static,
789 Node::Provider: BlockReaderFor<N>,
790 {
791 self.start_network_with(
792 builder,
793 pool,
794 self.config().network.transactions_manager_config(),
795 self.config().network.tx_propagation_policy,
796 )
797 }
798
799 pub fn start_network_with<Pool, N, Policy>(
806 &self,
807 builder: NetworkBuilder<(), (), N>,
808 pool: Pool,
809 tx_config: TransactionsManagerConfig,
810 propagation_policy: Policy,
811 ) -> NetworkHandle<N>
812 where
813 N: NetworkPrimitives,
814 Pool: TransactionPool<
815 Transaction: PoolTransaction<
816 Consensus = N::BroadcastedTransaction,
817 Pooled = N::PooledTransaction,
818 >,
819 > + Unpin
820 + 'static,
821 Node::Provider: BlockReaderFor<N>,
822 Policy: TransactionPropagationPolicy + Debug,
823 {
824 let (handle, network, txpool, eth) = builder
825 .transactions_with_policy(pool, tx_config, propagation_policy)
826 .request_handler(self.provider().clone())
827 .split_with_handle();
828
829 self.executor.spawn_critical("p2p txpool", Box::pin(txpool));
830 self.executor.spawn_critical("p2p eth request handler", Box::pin(eth));
831
832 let default_peers_path = self.config().datadir().known_peers();
833 let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
834 self.executor.spawn_critical_with_graceful_shutdown_signal(
835 "p2p network task",
836 |shutdown| {
837 Box::pin(network.run_until_graceful_shutdown(shutdown, |network| {
838 if let Some(peers_file) = known_peers_file {
839 let num_known_peers = network.num_known_peers();
840 trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
841 match network.write_peers_to_file(peers_file.as_path()) {
842 Ok(_) => {
843 info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
844 }
845 Err(err) => {
846 warn!(target: "reth::cli", %err, "Failed to write network peers to file");
847 }
848 }
849 }
850 }))
851 },
852 );
853
854 handle
855 }
856
857 fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
859 let network_secret_path =
860 self.config().network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
861 let secret_key = get_secret_key(&network_secret_path)?;
862 Ok(secret_key)
863 }
864
865 pub fn build_network_config<N>(
867 &self,
868 network_builder: NetworkConfigBuilder<N>,
869 ) -> NetworkConfig<Node::Provider, N>
870 where
871 N: NetworkPrimitives,
872 Node::Types: NodeTypes<ChainSpec: Hardforks>,
873 {
874 network_builder.build(self.provider.clone())
875 }
876}
877
878impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
879 pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
881 where
882 N: NetworkPrimitives,
883 {
884 let network_config = self.network_config()?;
885 let builder = NetworkManager::builder(network_config).await?;
886 Ok(builder)
887 }
888
889 pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
891 where
892 N: NetworkPrimitives,
893 {
894 let network_builder = self.network_config_builder();
895 Ok(self.build_network_config(network_builder?))
896 }
897
898 pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
900 where
901 N: NetworkPrimitives,
902 {
903 let secret_key = self.network_secret(&self.config().datadir())?;
904 let default_peers_path = self.config().datadir().known_peers();
905 let builder = self
906 .config()
907 .network
908 .network_config(
909 self.reth_config(),
910 self.config().chain.clone(),
911 secret_key,
912 default_peers_path,
913 )
914 .with_task_executor(Box::new(self.executor.clone()))
915 .set_head(self.head);
916
917 Ok(builder)
918 }
919}
920
921impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
922 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
923 f.debug_struct("BuilderContext")
924 .field("head", &self.head)
925 .field("provider", &std::any::type_name::<Node::Provider>())
926 .field("executor", &self.executor)
927 .field("config", &self.config())
928 .finish()
929 }
930}