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 DefaultNodeLauncher, LaunchNode, Node, NodeHandle,
11};
12use alloy_eips::eip4844::env_settings::EnvKzgSettings;
13use futures::Future;
14use reth_blockchain_tree::externals::NodeTypesForTree;
15use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
16use reth_cli_util::get_secret_key;
17use reth_db_api::{
18 database::Database,
19 database_metrics::{DatabaseMetadata, DatabaseMetrics},
20};
21use reth_exex::ExExContext;
22use reth_network::{
23 transactions::TransactionsManagerConfig, NetworkBuilder, NetworkConfig, NetworkConfigBuilder,
24 NetworkHandle, NetworkManager, NetworkPrimitives,
25};
26use reth_node_api::{
27 FullNodePrimitives, FullNodeTypes, FullNodeTypesAdapter, NodeAddOns, NodeTypes,
28 NodeTypesWithDBAdapter, NodeTypesWithEngine,
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},
38 BlockReader, 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> = FullNodeTypesAdapter<
54 NodeTypesWithDBAdapter<Types, DB>,
55 BlockchainProvider<NodeTypesWithDBAdapter<Types, DB>>,
56>;
57
58#[allow(clippy::doc_markdown)]
59#[cfg_attr(doc, aquamarine::aquamarine)]
60pub struct NodeBuilder<DB, ChainSpec> {
156 config: NodeConfig<ChainSpec>,
158 database: DB,
160}
161
162impl<ChainSpec> NodeBuilder<(), ChainSpec> {
163 pub const fn new(config: NodeConfig<ChainSpec>) -> Self {
165 Self { config, database: () }
166 }
167
168 pub fn apply<F>(self, f: F) -> Self
170 where
171 F: FnOnce(Self) -> Self,
172 {
173 f(self)
174 }
175
176 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
178 where
179 F: FnOnce(Self) -> Self,
180 {
181 if cond {
182 f(self)
183 } else {
184 self
185 }
186 }
187}
188
189impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec> {
190 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
192 &self.config
193 }
194
195 pub fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
197 &mut self.config
198 }
199}
200
201impl<DB, ChainSpec: EthChainSpec> NodeBuilder<DB, ChainSpec> {
202 pub fn with_database<D>(self, database: D) -> NodeBuilder<D, ChainSpec> {
204 NodeBuilder { config: self.config, database }
205 }
206
207 pub const fn with_launch_context(self, task_executor: TaskExecutor) -> WithLaunchContext<Self> {
211 WithLaunchContext { builder: self, task_executor }
212 }
213
214 #[cfg(feature = "test-utils")]
216 pub fn testing_node(
217 mut self,
218 task_executor: TaskExecutor,
219 ) -> WithLaunchContext<
220 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
221 > {
222 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(
223 reth_db::test_utils::tempdir_path(),
224 );
225 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
226 datadir: path.clone(),
227 ..Default::default()
228 });
229
230 let data_dir =
231 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
232
233 let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
234
235 WithLaunchContext { builder: self.with_database(db), task_executor }
236 }
237}
238
239impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
240where
241 DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static,
242 ChainSpec: EthChainSpec + EthereumHardforks,
243{
244 pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
246 where
247 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForTree,
248 {
249 self.with_types_and_provider()
250 }
251
252 pub fn with_types_and_provider<T, P>(
254 self,
255 ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<NodeTypesWithDBAdapter<T, DB>, P>>
256 where
257 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForProvider,
258 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
259 {
260 NodeBuilderWithTypes::new(self.config, self.database)
261 }
262
263 pub fn node<N>(
267 self,
268 node: N,
269 ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
270 where
271 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForTree,
272 {
273 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
274 }
275}
276
277pub struct WithLaunchContext<Builder> {
282 builder: Builder,
283 task_executor: TaskExecutor,
284}
285
286impl<Builder> WithLaunchContext<Builder> {
287 pub const fn task_executor(&self) -> &TaskExecutor {
289 &self.task_executor
290 }
291}
292
293impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
294 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
296 self.builder.config()
297 }
298}
299
300impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
301where
302 DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static,
303 ChainSpec: EthChainSpec + EthereumHardforks,
304{
305 pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
307 where
308 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForTree,
309 {
310 WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
311 }
312
313 pub fn with_types_and_provider<T, P>(
315 self,
316 ) -> WithLaunchContext<
317 NodeBuilderWithTypes<FullNodeTypesAdapter<NodeTypesWithDBAdapter<T, DB>, P>>,
318 >
319 where
320 T: NodeTypesWithEngine<ChainSpec = ChainSpec> + NodeTypesForProvider,
321 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
322 {
323 WithLaunchContext {
324 builder: self.builder.with_types_and_provider(),
325 task_executor: self.task_executor,
326 }
327 }
328
329 pub fn node<N>(
333 self,
334 node: N,
335 ) -> WithLaunchContext<
336 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
337 >
338 where
339 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForTree,
340 {
341 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
342 }
343
344 pub async fn launch_node<N>(
350 self,
351 node: N,
352 ) -> eyre::Result<
353 NodeHandle<
354 NodeAdapter<
355 RethFullAdapter<DB, N>,
356 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
357 >,
358 N::AddOns,
359 >,
360 >
361 where
362 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForTree,
363 N::AddOns: RethRpcAddOns<
364 NodeAdapter<
365 RethFullAdapter<DB, N>,
366 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
367 >,
368 >,
369 N::Primitives: FullNodePrimitives<BlockBody = reth_primitives::BlockBody>,
370 {
371 self.node(node).launch().await
372 }
373}
374
375impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
376 pub fn with_components<CB>(
378 self,
379 components_builder: CB,
380 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
381 where
382 CB: NodeComponentsBuilder<T>,
383 {
384 WithLaunchContext {
385 builder: self.builder.with_components(components_builder),
386 task_executor: self.task_executor,
387 }
388 }
389}
390
391impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
392where
393 T: FullNodeTypes,
394 CB: NodeComponentsBuilder<T>,
395{
396 pub fn with_add_ons<AO>(
399 self,
400 add_ons: AO,
401 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
402 where
403 AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
404 {
405 WithLaunchContext {
406 builder: self.builder.with_add_ons(add_ons),
407 task_executor: self.task_executor,
408 }
409 }
410}
411
412impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
413where
414 T: FullNodeTypes,
415 CB: NodeComponentsBuilder<T>,
416 AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
417{
418 pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
420 &self.builder.config
421 }
422
423 pub fn apply<F>(self, f: F) -> Self
425 where
426 F: FnOnce(Self) -> Self,
427 {
428 f(self)
429 }
430
431 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
433 where
434 F: FnOnce(Self) -> Self,
435 {
436 if cond {
437 f(self)
438 } else {
439 self
440 }
441 }
442
443 pub fn on_component_initialized<F>(self, hook: F) -> Self
445 where
446 F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
447 {
448 Self {
449 builder: self.builder.on_component_initialized(hook),
450 task_executor: self.task_executor,
451 }
452 }
453
454 pub fn on_node_started<F>(self, hook: F) -> Self
456 where
457 F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
458 + Send
459 + 'static,
460 {
461 Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
462 }
463
464 pub fn map_add_ons<F>(self, f: F) -> Self
466 where
467 F: FnOnce(AO) -> AO,
468 {
469 Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
470 }
471
472 pub fn on_rpc_started<F>(self, hook: F) -> Self
474 where
475 F: FnOnce(
476 RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
477 RethRpcServerHandles,
478 ) -> eyre::Result<()>
479 + Send
480 + 'static,
481 {
482 Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
483 }
484
485 pub fn extend_rpc_modules<F>(self, hook: F) -> Self
487 where
488 F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
489 + Send
490 + 'static,
491 {
492 Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
493 }
494
495 pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
501 where
502 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
503 R: Future<Output = eyre::Result<E>> + Send,
504 E: Future<Output = eyre::Result<()>> + Send,
505 {
506 Self {
507 builder: self.builder.install_exex(exex_id, exex),
508 task_executor: self.task_executor,
509 }
510 }
511
512 pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
518 where
519 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
520 R: Future<Output = eyre::Result<E>> + Send,
521 E: Future<Output = eyre::Result<()>> + Send,
522 {
523 if cond {
524 self.install_exex(exex_id, exex)
525 } else {
526 self
527 }
528 }
529
530 pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
532 where
533 L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
534 {
535 launcher.launch_node(self.builder).await
536 }
537
538 pub fn launch_with_fn<L, R>(self, launcher: L) -> R
540 where
541 L: FnOnce(Self) -> R,
542 {
543 launcher(self)
544 }
545
546 pub const fn check_launch(self) -> Self {
550 self
551 }
552}
553
554impl<T, DB, CB, AO> WithLaunchContext<NodeBuilderWithComponents<RethFullAdapter<DB, T>, CB, AO>>
555where
556 DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static,
557 T: NodeTypesWithEngine + NodeTypesForTree,
558 CB: NodeComponentsBuilder<RethFullAdapter<DB, T>>,
559 AO: RethRpcAddOns<NodeAdapter<RethFullAdapter<DB, T>, CB::Components>>,
560{
561 pub async fn launch(
563 self,
564 ) -> eyre::Result<NodeHandle<NodeAdapter<RethFullAdapter<DB, T>, CB::Components>, AO>> {
565 let Self { builder, task_executor } = self;
566
567 let launcher = DefaultNodeLauncher::new(task_executor, builder.config.datadir());
568 builder.launch_with(launcher).await
569 }
570}
571
572pub struct BuilderContext<Node: FullNodeTypes> {
574 pub(crate) head: Head,
576 pub(crate) provider: Node::Provider,
578 pub(crate) executor: TaskExecutor,
580 pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
582}
583
584impl<Node: FullNodeTypes> BuilderContext<Node> {
585 pub const fn new(
587 head: Head,
588 provider: Node::Provider,
589 executor: TaskExecutor,
590 config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
591 ) -> Self {
592 Self { head, provider, executor, config_container }
593 }
594
595 pub const fn provider(&self) -> &Node::Provider {
597 &self.provider
598 }
599
600 pub const fn head(&self) -> Head {
602 self.head
603 }
604
605 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
607 &self.config_container.config
608 }
609
610 pub const fn reth_config(&self) -> &reth_config::Config {
612 &self.config_container.toml_config
613 }
614
615 pub const fn task_executor(&self) -> &TaskExecutor {
619 &self.executor
620 }
621
622 pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
624 self.provider().chain_spec()
625 }
626
627 pub const fn is_dev(&self) -> bool {
629 self.config().dev.dev
630 }
631
632 pub fn pool_config(&self) -> PoolConfig {
634 self.config().txpool.pool_config()
635 }
636
637 pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
639 Ok(EnvKzgSettings::Default)
640 }
641
642 pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
644 self.config().builder.clone()
645 }
646
647 pub fn start_network<N, Pool>(
652 &self,
653 builder: NetworkBuilder<(), (), N>,
654 pool: Pool,
655 ) -> NetworkHandle<N>
656 where
657 N: NetworkPrimitives,
658 Pool: TransactionPool<
659 Transaction: PoolTransaction<
660 Consensus = N::BroadcastedTransaction,
661 Pooled = N::PooledTransaction,
662 >,
663 > + Unpin
664 + 'static,
665 Node::Provider: BlockReader<
666 Receipt = reth_primitives::Receipt,
667 Block = N::Block,
668 Header = N::BlockHeader,
669 >,
670 {
671 self.start_network_with(builder, pool, Default::default())
672 }
673
674 pub fn start_network_with<Pool, N>(
681 &self,
682 builder: NetworkBuilder<(), (), N>,
683 pool: Pool,
684 tx_config: TransactionsManagerConfig,
685 ) -> NetworkHandle<N>
686 where
687 N: NetworkPrimitives,
688 Pool: TransactionPool<
689 Transaction: PoolTransaction<
690 Consensus = N::BroadcastedTransaction,
691 Pooled = N::PooledTransaction,
692 >,
693 > + Unpin
694 + 'static,
695 Node::Provider: BlockReader<
696 Receipt = reth_primitives::Receipt,
697 Block = N::Block,
698 Header = N::BlockHeader,
699 >,
700 {
701 let (handle, network, txpool, eth) = builder
702 .transactions(pool, tx_config)
703 .request_handler(self.provider().clone())
704 .split_with_handle();
705
706 self.executor.spawn_critical("p2p txpool", txpool);
707 self.executor.spawn_critical("p2p eth request handler", eth);
708
709 let default_peers_path = self.config().datadir().known_peers();
710 let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
711 self.executor.spawn_critical_with_graceful_shutdown_signal(
712 "p2p network task",
713 |shutdown| {
714 network.run_until_graceful_shutdown(shutdown, |network| {
715 if let Some(peers_file) = known_peers_file {
716 let num_known_peers = network.num_known_peers();
717 trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
718 match network.write_peers_to_file(peers_file.as_path()) {
719 Ok(_) => {
720 info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
721 }
722 Err(err) => {
723 warn!(target: "reth::cli", %err, "Failed to write network peers to file");
724 }
725 }
726 }
727 })
728 },
729 );
730
731 handle
732 }
733
734 fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
736 let network_secret_path =
737 self.config().network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
738 let secret_key = get_secret_key(&network_secret_path)?;
739 Ok(secret_key)
740 }
741
742 pub fn build_network_config(
744 &self,
745 network_builder: NetworkConfigBuilder,
746 ) -> NetworkConfig<Node::Provider>
747 where
748 Node::Types: NodeTypes<ChainSpec: Hardforks>,
749 {
750 network_builder.build(self.provider.clone())
751 }
752}
753
754impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
755 pub async fn network_builder(&self) -> eyre::Result<NetworkBuilder<(), ()>> {
757 let network_config = self.network_config()?;
758 let builder = NetworkManager::builder(network_config).await?;
759 Ok(builder)
760 }
761
762 pub fn network_config(&self) -> eyre::Result<NetworkConfig<Node::Provider>> {
764 let network_builder = self.network_config_builder();
765 Ok(self.build_network_config(network_builder?))
766 }
767
768 pub fn network_config_builder(&self) -> eyre::Result<NetworkConfigBuilder> {
770 let secret_key = self.network_secret(&self.config().datadir())?;
771 let default_peers_path = self.config().datadir().known_peers();
772 let builder = self
773 .config()
774 .network
775 .network_config(
776 self.reth_config(),
777 self.config().chain.clone(),
778 secret_key,
779 default_peers_path,
780 )
781 .with_task_executor(Box::new(self.executor.clone()))
782 .set_head(self.head);
783
784 Ok(builder)
785 }
786}
787
788impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
789 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
790 f.debug_struct("BuilderContext")
791 .field("head", &self.head)
792 .field("provider", &std::any::type_name::<Node::Provider>())
793 .field("executor", &self.executor)
794 .field("config", &self.config())
795 .finish()
796 }
797}