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::TransactionsManagerConfig, NetworkBuilder, NetworkConfig, NetworkConfigBuilder,
20 NetworkHandle, NetworkManager, NetworkPrimitives,
21};
22use reth_node_api::{
23 FullNodePrimitives, FullNodeTypes, FullNodeTypesAdapter, NodeAddOns, NodeTypes,
24 NodeTypesWithDBAdapter, NodeTypesWithEngine,
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::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#[allow(clippy::doc_markdown)]
53#[cfg_attr(doc, aquamarine::aquamarine)]
54pub struct NodeBuilder<DB, ChainSpec> {
150 config: NodeConfig<ChainSpec>,
152 database: DB,
154}
155
156impl<ChainSpec> NodeBuilder<(), ChainSpec> {
157 pub const fn new(config: NodeConfig<ChainSpec>) -> Self {
159 Self { config, database: () }
160 }
161
162 pub fn apply<F>(self, f: F) -> Self
164 where
165 F: FnOnce(Self) -> Self,
166 {
167 f(self)
168 }
169
170 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
172 where
173 F: FnOnce(Self) -> Self,
174 {
175 if cond {
176 f(self)
177 } else {
178 self
179 }
180 }
181}
182
183impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec> {
184 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
186 &self.config
187 }
188
189 pub fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
191 &mut self.config
192 }
193}
194
195impl<DB, ChainSpec: EthChainSpec> NodeBuilder<DB, ChainSpec> {
196 pub fn with_database<D>(self, database: D) -> NodeBuilder<D, ChainSpec> {
198 NodeBuilder { config: self.config, database }
199 }
200
201 pub const fn with_launch_context(self, task_executor: TaskExecutor) -> WithLaunchContext<Self> {
205 WithLaunchContext { builder: self, task_executor }
206 }
207
208 #[cfg(feature = "test-utils")]
210 pub fn testing_node(
211 mut self,
212 task_executor: TaskExecutor,
213 ) -> WithLaunchContext<
214 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
215 > {
216 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(
217 reth_db::test_utils::tempdir_path(),
218 );
219 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
220 datadir: path.clone(),
221 ..Default::default()
222 });
223
224 let data_dir =
225 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
226
227 let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
228
229 WithLaunchContext { builder: self.with_database(db), task_executor }
230 }
231}
232
233impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
234where
235 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
236 ChainSpec: EthChainSpec + EthereumHardforks,
237{
238 pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
240 where
241 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForProvider,
242 {
243 self.with_types_and_provider()
244 }
245
246 pub fn with_types_and_provider<T, P>(
248 self,
249 ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
250 where
251 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForProvider,
252 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
253 {
254 NodeBuilderWithTypes::new(self.config, self.database)
255 }
256
257 pub fn node<N>(
261 self,
262 node: N,
263 ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
264 where
265 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
266 {
267 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
268 }
269}
270
271pub struct WithLaunchContext<Builder> {
276 builder: Builder,
277 task_executor: TaskExecutor,
278}
279
280impl<Builder> WithLaunchContext<Builder> {
281 pub const fn task_executor(&self) -> &TaskExecutor {
283 &self.task_executor
284 }
285}
286
287impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
288 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
290 self.builder.config()
291 }
292}
293
294impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
295where
296 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
297 ChainSpec: EthChainSpec + EthereumHardforks,
298{
299 pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
301 where
302 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForProvider,
303 {
304 WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
305 }
306
307 pub fn with_types_and_provider<T, P>(
309 self,
310 ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
311 where
312 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForProvider,
313 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
314 {
315 WithLaunchContext {
316 builder: self.builder.with_types_and_provider(),
317 task_executor: self.task_executor,
318 }
319 }
320
321 pub fn node<N>(
325 self,
326 node: N,
327 ) -> WithLaunchContext<
328 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
329 >
330 where
331 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
332 {
333 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
334 }
335
336 pub async fn launch_node<N>(
342 self,
343 node: N,
344 ) -> eyre::Result<
345 <EngineNodeLauncher as LaunchNode<
346 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
347 >>::Node,
348 >
349 where
350 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
351 N::AddOns: RethRpcAddOns<
352 NodeAdapter<
353 RethFullAdapter<DB, N>,
354 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
355 >,
356 >,
357 N::Primitives: FullNodePrimitives,
358 EngineNodeLauncher: LaunchNode<
359 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
360 >,
361 {
362 self.node(node).launch().await
363 }
364}
365
366impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
367 pub fn with_components<CB>(
369 self,
370 components_builder: CB,
371 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
372 where
373 CB: NodeComponentsBuilder<T>,
374 {
375 WithLaunchContext {
376 builder: self.builder.with_components(components_builder),
377 task_executor: self.task_executor,
378 }
379 }
380}
381
382impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
383where
384 T: FullNodeTypes,
385 CB: NodeComponentsBuilder<T>,
386{
387 pub fn with_add_ons<AO>(
390 self,
391 add_ons: AO,
392 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
393 where
394 AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
395 {
396 WithLaunchContext {
397 builder: self.builder.with_add_ons(add_ons),
398 task_executor: self.task_executor,
399 }
400 }
401}
402
403impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
404where
405 T: FullNodeTypes,
406 CB: NodeComponentsBuilder<T>,
407 AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
408{
409 pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
411 &self.builder.config
412 }
413
414 pub fn apply<F>(self, f: F) -> Self
416 where
417 F: FnOnce(Self) -> Self,
418 {
419 f(self)
420 }
421
422 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
424 where
425 F: FnOnce(Self) -> Self,
426 {
427 if cond {
428 f(self)
429 } else {
430 self
431 }
432 }
433
434 pub fn on_component_initialized<F>(self, hook: F) -> Self
436 where
437 F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
438 {
439 Self {
440 builder: self.builder.on_component_initialized(hook),
441 task_executor: self.task_executor,
442 }
443 }
444
445 pub fn on_node_started<F>(self, hook: F) -> Self
447 where
448 F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
449 + Send
450 + 'static,
451 {
452 Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
453 }
454
455 pub fn map_add_ons<F>(self, f: F) -> Self
457 where
458 F: FnOnce(AO) -> AO,
459 {
460 Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
461 }
462
463 pub fn on_rpc_started<F>(self, hook: F) -> Self
465 where
466 F: FnOnce(
467 RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
468 RethRpcServerHandles,
469 ) -> eyre::Result<()>
470 + Send
471 + 'static,
472 {
473 Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
474 }
475
476 pub fn extend_rpc_modules<F>(self, hook: F) -> Self
478 where
479 F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
480 + Send
481 + 'static,
482 {
483 Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
484 }
485
486 pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
492 where
493 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
494 R: Future<Output = eyre::Result<E>> + Send,
495 E: Future<Output = eyre::Result<()>> + Send,
496 {
497 Self {
498 builder: self.builder.install_exex(exex_id, exex),
499 task_executor: self.task_executor,
500 }
501 }
502
503 pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
509 where
510 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
511 R: Future<Output = eyre::Result<E>> + Send,
512 E: Future<Output = eyre::Result<()>> + Send,
513 {
514 if cond {
515 self.install_exex(exex_id, exex)
516 } else {
517 self
518 }
519 }
520
521 pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
523 where
524 L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
525 {
526 launcher.launch_node(self.builder).await
527 }
528
529 pub fn launch_with_fn<L, R>(self, launcher: L) -> R
531 where
532 L: FnOnce(Self) -> R,
533 {
534 launcher(self)
535 }
536
537 pub const fn check_launch(self) -> Self {
541 self
542 }
543
544 pub async fn launch(
546 self,
547 ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
548 where
549 EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
550 {
551 let Self { builder, task_executor } = self;
552
553 let engine_tree_config = builder.config.engine.tree_config();
554
555 let launcher =
556 EngineNodeLauncher::new(task_executor, builder.config.datadir(), engine_tree_config);
557 builder.launch_with(launcher).await
558 }
559
560 pub async fn launch_with_debug_capabilities(
565 self,
566 ) -> eyre::Result<<DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
567 where
568 T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
569 DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
570 {
571 let Self { builder, task_executor } = self;
572
573 let engine_tree_config = builder.config.engine.tree_config();
574
575 let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
576 task_executor,
577 builder.config.datadir(),
578 engine_tree_config,
579 ));
580 builder.launch_with(launcher).await
581 }
582}
583
584pub struct BuilderContext<Node: FullNodeTypes> {
586 pub(crate) head: Head,
588 pub(crate) provider: Node::Provider,
590 pub(crate) executor: TaskExecutor,
592 pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
594}
595
596impl<Node: FullNodeTypes> BuilderContext<Node> {
597 pub const fn new(
599 head: Head,
600 provider: Node::Provider,
601 executor: TaskExecutor,
602 config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
603 ) -> Self {
604 Self { head, provider, executor, config_container }
605 }
606
607 pub const fn provider(&self) -> &Node::Provider {
609 &self.provider
610 }
611
612 pub const fn head(&self) -> Head {
614 self.head
615 }
616
617 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
619 &self.config_container.config
620 }
621
622 pub const fn reth_config(&self) -> &reth_config::Config {
624 &self.config_container.toml_config
625 }
626
627 pub const fn task_executor(&self) -> &TaskExecutor {
631 &self.executor
632 }
633
634 pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
636 self.provider().chain_spec()
637 }
638
639 pub const fn is_dev(&self) -> bool {
641 self.config().dev.dev
642 }
643
644 pub fn pool_config(&self) -> PoolConfig {
646 self.config().txpool.pool_config()
647 }
648
649 pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
651 Ok(EnvKzgSettings::Default)
652 }
653
654 pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
656 self.config().builder.clone()
657 }
658
659 pub fn start_network<N, Pool>(
664 &self,
665 builder: NetworkBuilder<(), (), N>,
666 pool: Pool,
667 ) -> NetworkHandle<N>
668 where
669 N: NetworkPrimitives,
670 Pool: TransactionPool<
671 Transaction: PoolTransaction<
672 Consensus = N::BroadcastedTransaction,
673 Pooled = N::PooledTransaction,
674 >,
675 > + Unpin
676 + 'static,
677 Node::Provider: BlockReaderFor<N>,
678 {
679 self.start_network_with(builder, pool, Default::default())
680 }
681
682 pub fn start_network_with<Pool, N>(
689 &self,
690 builder: NetworkBuilder<(), (), N>,
691 pool: Pool,
692 tx_config: TransactionsManagerConfig,
693 ) -> NetworkHandle<N>
694 where
695 N: NetworkPrimitives,
696 Pool: TransactionPool<
697 Transaction: PoolTransaction<
698 Consensus = N::BroadcastedTransaction,
699 Pooled = N::PooledTransaction,
700 >,
701 > + Unpin
702 + 'static,
703 Node::Provider: BlockReaderFor<N>,
704 {
705 let (handle, network, txpool, eth) = builder
706 .transactions(pool, tx_config)
707 .request_handler(self.provider().clone())
708 .split_with_handle();
709
710 self.executor.spawn_critical("p2p txpool", txpool);
711 self.executor.spawn_critical("p2p eth request handler", eth);
712
713 let default_peers_path = self.config().datadir().known_peers();
714 let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
715 self.executor.spawn_critical_with_graceful_shutdown_signal(
716 "p2p network task",
717 |shutdown| {
718 network.run_until_graceful_shutdown(shutdown, |network| {
719 if let Some(peers_file) = known_peers_file {
720 let num_known_peers = network.num_known_peers();
721 trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
722 match network.write_peers_to_file(peers_file.as_path()) {
723 Ok(_) => {
724 info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
725 }
726 Err(err) => {
727 warn!(target: "reth::cli", %err, "Failed to write network peers to file");
728 }
729 }
730 }
731 })
732 },
733 );
734
735 handle
736 }
737
738 fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
740 let network_secret_path =
741 self.config().network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
742 let secret_key = get_secret_key(&network_secret_path)?;
743 Ok(secret_key)
744 }
745
746 pub fn build_network_config<N>(
748 &self,
749 network_builder: NetworkConfigBuilder<N>,
750 ) -> NetworkConfig<Node::Provider, N>
751 where
752 N: NetworkPrimitives,
753 Node::Types: NodeTypes<ChainSpec: Hardforks>,
754 {
755 network_builder.build(self.provider.clone())
756 }
757}
758
759impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
760 pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
762 where
763 N: NetworkPrimitives,
764 {
765 let network_config = self.network_config()?;
766 let builder = NetworkManager::builder(network_config).await?;
767 Ok(builder)
768 }
769
770 pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
772 where
773 N: NetworkPrimitives,
774 {
775 let network_builder = self.network_config_builder();
776 Ok(self.build_network_config(network_builder?))
777 }
778
779 pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
781 where
782 N: NetworkPrimitives,
783 {
784 let secret_key = self.network_secret(&self.config().datadir())?;
785 let default_peers_path = self.config().datadir().known_peers();
786 let builder = self
787 .config()
788 .network
789 .network_config(
790 self.reth_config(),
791 self.config().chain.clone(),
792 secret_key,
793 default_peers_path,
794 )
795 .with_task_executor(Box::new(self.executor.clone()))
796 .set_head(self.head);
797
798 Ok(builder)
799 }
800}
801
802impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
803 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
804 f.debug_struct("BuilderContext")
805 .field("head", &self.head)
806 .field("provider", &std::any::type_name::<Node::Provider>())
807 .field("executor", &self.executor)
808 .field("config", &self.config())
809 .finish()
810 }
811}