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 mut self,
244 task_executor: TaskExecutor,
245 ) -> WithLaunchContext<
246 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
247 > {
248 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(
249 reth_db::test_utils::tempdir_path(),
250 );
251 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
252 datadir: path.clone(),
253 ..Default::default()
254 });
255
256 let data_dir =
257 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
258
259 let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
260
261 WithLaunchContext { builder: self.with_database(db), task_executor }
262 }
263}
264
265impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
266where
267 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
268 ChainSpec: EthChainSpec + EthereumHardforks,
269{
270 pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
272 where
273 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
274 {
275 self.with_types_and_provider()
276 }
277
278 pub fn with_types_and_provider<T, P>(
280 self,
281 ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
282 where
283 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
284 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
285 {
286 NodeBuilderWithTypes::new(self.config, self.database)
287 }
288
289 pub fn node<N>(
293 self,
294 node: N,
295 ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
296 where
297 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
298 {
299 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
300 }
301}
302
303pub struct WithLaunchContext<Builder> {
308 builder: Builder,
309 task_executor: TaskExecutor,
310}
311
312impl<Builder> WithLaunchContext<Builder> {
313 pub const fn task_executor(&self) -> &TaskExecutor {
315 &self.task_executor
316 }
317}
318
319impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
320 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
322 self.builder.config()
323 }
324}
325
326impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
327where
328 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
329 ChainSpec: EthChainSpec + EthereumHardforks,
330{
331 pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
333 where
334 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
335 {
336 WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
337 }
338
339 pub fn with_types_and_provider<T, P>(
341 self,
342 ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
343 where
344 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
345 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
346 {
347 WithLaunchContext {
348 builder: self.builder.with_types_and_provider(),
349 task_executor: self.task_executor,
350 }
351 }
352
353 pub fn node<N>(
357 self,
358 node: N,
359 ) -> WithLaunchContext<
360 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
361 >
362 where
363 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
364 {
365 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
366 }
367
368 pub async fn launch_node<N>(
374 self,
375 node: N,
376 ) -> eyre::Result<
377 <EngineNodeLauncher as LaunchNode<
378 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
379 >>::Node,
380 >
381 where
382 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
383 N::AddOns: RethRpcAddOns<
384 NodeAdapter<
385 RethFullAdapter<DB, N>,
386 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
387 >,
388 >,
389 N::Primitives: FullNodePrimitives,
390 EngineNodeLauncher: LaunchNode<
391 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
392 >,
393 {
394 self.node(node).launch().await
395 }
396}
397
398impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
399 pub fn with_components<CB>(
401 self,
402 components_builder: CB,
403 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
404 where
405 CB: NodeComponentsBuilder<T>,
406 {
407 WithLaunchContext {
408 builder: self.builder.with_components(components_builder),
409 task_executor: self.task_executor,
410 }
411 }
412}
413
414impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
415where
416 T: FullNodeTypes,
417 CB: NodeComponentsBuilder<T>,
418{
419 pub fn with_add_ons<AO>(
422 self,
423 add_ons: AO,
424 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
425 where
426 AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
427 {
428 WithLaunchContext {
429 builder: self.builder.with_add_ons(add_ons),
430 task_executor: self.task_executor,
431 }
432 }
433}
434
435impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
436where
437 T: FullNodeTypes,
438 CB: NodeComponentsBuilder<T>,
439 AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
440{
441 pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
443 &self.builder.config
444 }
445
446 pub const fn db(&self) -> &T::DB {
448 &self.builder.adapter.database
449 }
450
451 pub const fn db_mut(&mut self) -> &mut T::DB {
453 &mut self.builder.adapter.database
454 }
455
456 pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
458 where
459 F: FnOnce(Self) -> Result<Self, R>,
460 {
461 f(self)
462 }
463
464 pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
466 where
467 F: FnOnce(Self) -> Result<Self, R>,
468 {
469 if cond {
470 f(self)
471 } else {
472 Ok(self)
473 }
474 }
475
476 pub fn apply<F>(self, f: F) -> Self
478 where
479 F: FnOnce(Self) -> Self,
480 {
481 f(self)
482 }
483
484 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
486 where
487 F: FnOnce(Self) -> Self,
488 {
489 if cond {
490 f(self)
491 } else {
492 self
493 }
494 }
495
496 pub fn on_component_initialized<F>(self, hook: F) -> Self
498 where
499 F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
500 {
501 Self {
502 builder: self.builder.on_component_initialized(hook),
503 task_executor: self.task_executor,
504 }
505 }
506
507 pub fn on_node_started<F>(self, hook: F) -> Self
509 where
510 F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
511 + Send
512 + 'static,
513 {
514 Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
515 }
516
517 pub fn map_add_ons<F>(self, f: F) -> Self
519 where
520 F: FnOnce(AO) -> AO,
521 {
522 Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
523 }
524
525 pub fn on_rpc_started<F>(self, hook: F) -> Self
527 where
528 F: FnOnce(
529 RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
530 RethRpcServerHandles,
531 ) -> eyre::Result<()>
532 + Send
533 + 'static,
534 {
535 Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
536 }
537
538 pub fn extend_rpc_modules<F>(self, hook: F) -> Self
540 where
541 F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
542 + Send
543 + 'static,
544 {
545 Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
546 }
547
548 pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
554 where
555 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
556 R: Future<Output = eyre::Result<E>> + Send,
557 E: Future<Output = eyre::Result<()>> + Send,
558 {
559 Self {
560 builder: self.builder.install_exex(exex_id, exex),
561 task_executor: self.task_executor,
562 }
563 }
564
565 pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
571 where
572 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
573 R: Future<Output = eyre::Result<E>> + Send,
574 E: Future<Output = eyre::Result<()>> + Send,
575 {
576 if cond {
577 self.install_exex(exex_id, exex)
578 } else {
579 self
580 }
581 }
582
583 pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
585 where
586 L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
587 {
588 launcher.launch_node(self.builder).await
589 }
590
591 pub fn launch_with_fn<L, R>(self, launcher: L) -> R
593 where
594 L: FnOnce(Self) -> R,
595 {
596 launcher(self)
597 }
598
599 pub const fn check_launch(self) -> Self {
603 self
604 }
605
606 pub async fn launch(
608 self,
609 ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
610 where
611 EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
612 {
613 let launcher = self.engine_api_launcher();
614 self.builder.launch_with(launcher).await
615 }
616
617 pub async fn launch_with_debug_capabilities(
622 self,
623 ) -> eyre::Result<<DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
624 where
625 T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
626 DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
627 {
628 let Self { builder, task_executor } = self;
629
630 let engine_tree_config = builder.config.engine.tree_config();
631
632 let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
633 task_executor,
634 builder.config.datadir(),
635 engine_tree_config,
636 ));
637 builder.launch_with(launcher).await
638 }
639
640 pub fn engine_api_launcher(&self) -> EngineNodeLauncher {
643 let engine_tree_config = self.builder.config.engine.tree_config();
644 EngineNodeLauncher::new(
645 self.task_executor.clone(),
646 self.builder.config.datadir(),
647 engine_tree_config,
648 )
649 }
650}
651
652pub struct BuilderContext<Node: FullNodeTypes> {
654 pub(crate) head: Head,
656 pub(crate) provider: Node::Provider,
658 pub(crate) executor: TaskExecutor,
660 pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
662}
663
664impl<Node: FullNodeTypes> BuilderContext<Node> {
665 pub const fn new(
667 head: Head,
668 provider: Node::Provider,
669 executor: TaskExecutor,
670 config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
671 ) -> Self {
672 Self { head, provider, executor, config_container }
673 }
674
675 pub const fn provider(&self) -> &Node::Provider {
677 &self.provider
678 }
679
680 pub const fn head(&self) -> Head {
682 self.head
683 }
684
685 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
687 &self.config_container.config
688 }
689
690 pub const fn reth_config(&self) -> &reth_config::Config {
692 &self.config_container.toml_config
693 }
694
695 pub const fn task_executor(&self) -> &TaskExecutor {
699 &self.executor
700 }
701
702 pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
704 self.provider().chain_spec()
705 }
706
707 pub const fn is_dev(&self) -> bool {
709 self.config().dev.dev
710 }
711
712 pub fn pool_config(&self) -> PoolConfig {
714 self.config().txpool.pool_config()
715 }
716
717 pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
719 Ok(EnvKzgSettings::Default)
720 }
721
722 pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
724 self.config().builder.clone()
725 }
726
727 pub fn start_network<N, Pool>(
732 &self,
733 builder: NetworkBuilder<(), (), N>,
734 pool: Pool,
735 ) -> NetworkHandle<N>
736 where
737 N: NetworkPrimitives,
738 Pool: TransactionPool<
739 Transaction: PoolTransaction<
740 Consensus = N::BroadcastedTransaction,
741 Pooled = N::PooledTransaction,
742 >,
743 > + Unpin
744 + 'static,
745 Node::Provider: BlockReaderFor<N>,
746 {
747 self.start_network_with(
748 builder,
749 pool,
750 self.config().network.transactions_manager_config(),
751 self.config().network.tx_propagation_policy,
752 )
753 }
754
755 pub fn start_network_with<Pool, N, Policy>(
762 &self,
763 builder: NetworkBuilder<(), (), N>,
764 pool: Pool,
765 tx_config: TransactionsManagerConfig,
766 propagation_policy: Policy,
767 ) -> NetworkHandle<N>
768 where
769 N: NetworkPrimitives,
770 Pool: TransactionPool<
771 Transaction: PoolTransaction<
772 Consensus = N::BroadcastedTransaction,
773 Pooled = N::PooledTransaction,
774 >,
775 > + Unpin
776 + 'static,
777 Node::Provider: BlockReaderFor<N>,
778 Policy: TransactionPropagationPolicy + Debug,
779 {
780 let (handle, network, txpool, eth) = builder
781 .transactions_with_policy(pool, tx_config, propagation_policy)
782 .request_handler(self.provider().clone())
783 .split_with_handle();
784
785 self.executor.spawn_critical("p2p txpool", txpool);
786 self.executor.spawn_critical("p2p eth request handler", eth);
787
788 let default_peers_path = self.config().datadir().known_peers();
789 let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
790 self.executor.spawn_critical_with_graceful_shutdown_signal(
791 "p2p network task",
792 |shutdown| {
793 network.run_until_graceful_shutdown(shutdown, |network| {
794 if let Some(peers_file) = known_peers_file {
795 let num_known_peers = network.num_known_peers();
796 trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
797 match network.write_peers_to_file(peers_file.as_path()) {
798 Ok(_) => {
799 info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
800 }
801 Err(err) => {
802 warn!(target: "reth::cli", %err, "Failed to write network peers to file");
803 }
804 }
805 }
806 })
807 },
808 );
809
810 handle
811 }
812
813 fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
815 let network_secret_path =
816 self.config().network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
817 let secret_key = get_secret_key(&network_secret_path)?;
818 Ok(secret_key)
819 }
820
821 pub fn build_network_config<N>(
823 &self,
824 network_builder: NetworkConfigBuilder<N>,
825 ) -> NetworkConfig<Node::Provider, N>
826 where
827 N: NetworkPrimitives,
828 Node::Types: NodeTypes<ChainSpec: Hardforks>,
829 {
830 network_builder.build(self.provider.clone())
831 }
832}
833
834impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
835 pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
837 where
838 N: NetworkPrimitives,
839 {
840 let network_config = self.network_config()?;
841 let builder = NetworkManager::builder(network_config).await?;
842 Ok(builder)
843 }
844
845 pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
847 where
848 N: NetworkPrimitives,
849 {
850 let network_builder = self.network_config_builder();
851 Ok(self.build_network_config(network_builder?))
852 }
853
854 pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
856 where
857 N: NetworkPrimitives,
858 {
859 let secret_key = self.network_secret(&self.config().datadir())?;
860 let default_peers_path = self.config().datadir().known_peers();
861 let builder = self
862 .config()
863 .network
864 .network_config(
865 self.reth_config(),
866 self.config().chain.clone(),
867 secret_key,
868 default_peers_path,
869 )
870 .with_task_executor(Box::new(self.executor.clone()))
871 .set_head(self.head);
872
873 Ok(builder)
874 }
875}
876
877impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
878 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
879 f.debug_struct("BuilderContext")
880 .field("head", &self.head)
881 .field("provider", &std::any::type_name::<Node::Provider>())
882 .field("executor", &self.executor)
883 .field("config", &self.config())
884 .finish()
885 }
886}