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