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_db_api::{database::Database, database_metrics::DatabaseMetrics};
16use reth_exex::ExExContext;
17use reth_network::{
18 transactions::{TransactionPropagationPolicy, TransactionsManagerConfig},
19 NetworkBuilder, NetworkConfig, NetworkConfigBuilder, NetworkHandle, NetworkManager,
20 NetworkPrimitives,
21};
22use reth_node_api::{
23 FullNodeTypes, FullNodeTypesAdapter, NodeAddOns, NodeTypes, NodeTypesWithDBAdapter,
24};
25use reth_node_core::{
26 cli::config::{PayloadBuilderConfig, RethTransactionPoolConfig},
27 dirs::{ChainPath, DataDirPath},
28 node_config::NodeConfig,
29 primitives::Head,
30};
31use reth_provider::{
32 providers::{BlockchainProvider, NodeTypesForProvider},
33 ChainSpecProvider, FullProvider,
34};
35use reth_tasks::TaskExecutor;
36use reth_transaction_pool::{PoolConfig, PoolTransaction, TransactionPool};
37use secp256k1::SecretKey;
38use std::sync::Arc;
39use tracing::{info, trace, warn};
40
41pub mod add_ons;
42
43mod states;
44pub use states::*;
45
46pub type RethFullAdapter<DB, Types> =
49 FullNodeTypesAdapter<Types, DB, BlockchainProvider<NodeTypesWithDBAdapter<Types, DB>>>;
50
51#[expect(clippy::doc_markdown)]
52#[cfg_attr(doc, aquamarine::aquamarine)]
53pub 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
163impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec> {
164 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
166 &self.config
167 }
168
169 pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
171 &mut self.config
172 }
173
174 pub const fn db(&self) -> &DB {
176 &self.database
177 }
178
179 pub const fn db_mut(&mut self) -> &mut DB {
181 &mut self.database
182 }
183
184 pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
186 where
187 F: FnOnce(Self) -> Result<Self, R>,
188 {
189 f(self)
190 }
191
192 pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
194 where
195 F: FnOnce(Self) -> Result<Self, R>,
196 {
197 if cond {
198 f(self)
199 } else {
200 Ok(self)
201 }
202 }
203
204 pub fn apply<F>(self, f: F) -> Self
206 where
207 F: FnOnce(Self) -> Self,
208 {
209 f(self)
210 }
211
212 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
214 where
215 F: FnOnce(Self) -> Self,
216 {
217 if cond {
218 f(self)
219 } else {
220 self
221 }
222 }
223}
224
225impl<DB, ChainSpec: EthChainSpec> NodeBuilder<DB, ChainSpec> {
226 pub fn with_database<D>(self, database: D) -> NodeBuilder<D, ChainSpec> {
228 NodeBuilder { config: self.config, database }
229 }
230
231 pub const fn with_launch_context(self, task_executor: TaskExecutor) -> WithLaunchContext<Self> {
235 WithLaunchContext { builder: self, task_executor }
236 }
237
238 #[cfg(feature = "test-utils")]
240 pub fn testing_node(
241 self,
242 task_executor: TaskExecutor,
243 ) -> WithLaunchContext<
244 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
245 > {
246 let path = reth_db::test_utils::tempdir_path();
247 self.testing_node_with_datadir(task_executor, path)
248 }
249
250 #[cfg(feature = "test-utils")]
252 pub fn testing_node_with_datadir(
253 mut self,
254 task_executor: TaskExecutor,
255 datadir: impl Into<std::path::PathBuf>,
256 ) -> WithLaunchContext<
257 NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
258 > {
259 let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(datadir.into());
260 self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
261 datadir: path.clone(),
262 ..Default::default()
263 });
264
265 let data_dir =
266 path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
267
268 let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
269
270 WithLaunchContext { builder: self.with_database(db), task_executor }
271 }
272}
273
274impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
275where
276 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
277 ChainSpec: EthChainSpec + EthereumHardforks,
278{
279 pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
281 where
282 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
283 {
284 self.with_types_and_provider()
285 }
286
287 pub fn with_types_and_provider<T, P>(
289 self,
290 ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
291 where
292 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
293 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
294 {
295 NodeBuilderWithTypes::new(self.config, self.database)
296 }
297
298 pub fn node<N>(
302 self,
303 node: N,
304 ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
305 where
306 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
307 {
308 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
309 }
310}
311
312pub struct WithLaunchContext<Builder> {
317 builder: Builder,
318 task_executor: TaskExecutor,
319}
320
321impl<Builder> WithLaunchContext<Builder> {
322 pub const fn task_executor(&self) -> &TaskExecutor {
324 &self.task_executor
325 }
326}
327
328impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
329 pub const fn config(&self) -> &NodeConfig<ChainSpec> {
331 self.builder.config()
332 }
333
334 pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
336 self.builder.config_mut()
337 }
338}
339
340impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
341where
342 DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
343 ChainSpec: EthChainSpec + EthereumHardforks,
344{
345 pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
347 where
348 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
349 {
350 WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
351 }
352
353 pub fn with_types_and_provider<T, P>(
355 self,
356 ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
357 where
358 T: NodeTypesForProvider<ChainSpec = ChainSpec>,
359 P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
360 {
361 WithLaunchContext {
362 builder: self.builder.with_types_and_provider(),
363 task_executor: self.task_executor,
364 }
365 }
366
367 pub fn node<N>(
371 self,
372 node: N,
373 ) -> WithLaunchContext<
374 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
375 >
376 where
377 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
378 {
379 self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
380 }
381
382 pub async fn launch_node<N>(
388 self,
389 node: N,
390 ) -> eyre::Result<
391 <EngineNodeLauncher as LaunchNode<
392 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
393 >>::Node,
394 >
395 where
396 N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
397 N::AddOns: RethRpcAddOns<
398 NodeAdapter<
399 RethFullAdapter<DB, N>,
400 <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
401 >,
402 >,
403 EngineNodeLauncher: LaunchNode<
404 NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
405 >,
406 {
407 self.node(node).launch().await
408 }
409}
410
411impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
412 pub fn with_components<CB>(
414 self,
415 components_builder: CB,
416 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
417 where
418 CB: NodeComponentsBuilder<T>,
419 {
420 WithLaunchContext {
421 builder: self.builder.with_components(components_builder),
422 task_executor: self.task_executor,
423 }
424 }
425}
426
427impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
428where
429 T: FullNodeTypes,
430 CB: NodeComponentsBuilder<T>,
431{
432 pub fn with_add_ons<AO>(
435 self,
436 add_ons: AO,
437 ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
438 where
439 AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
440 {
441 WithLaunchContext {
442 builder: self.builder.with_add_ons(add_ons),
443 task_executor: self.task_executor,
444 }
445 }
446}
447
448impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
449where
450 T: FullNodeTypes,
451 CB: NodeComponentsBuilder<T>,
452 AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
453{
454 pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
456 &self.builder.config
457 }
458
459 pub const fn config_mut(&mut self) -> &mut NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
461 &mut self.builder.config
462 }
463
464 pub const fn db(&self) -> &T::DB {
466 &self.builder.adapter.database
467 }
468
469 pub const fn db_mut(&mut self) -> &mut T::DB {
471 &mut self.builder.adapter.database
472 }
473
474 pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
476 where
477 F: FnOnce(Self) -> Result<Self, R>,
478 {
479 f(self)
480 }
481
482 pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
484 where
485 F: FnOnce(Self) -> Result<Self, R>,
486 {
487 if cond {
488 f(self)
489 } else {
490 Ok(self)
491 }
492 }
493
494 pub fn apply<F>(self, f: F) -> Self
496 where
497 F: FnOnce(Self) -> Self,
498 {
499 f(self)
500 }
501
502 pub fn apply_if<F>(self, cond: bool, f: F) -> Self
504 where
505 F: FnOnce(Self) -> Self,
506 {
507 if cond {
508 f(self)
509 } else {
510 self
511 }
512 }
513
514 pub fn on_component_initialized<F>(self, hook: F) -> Self
516 where
517 F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
518 {
519 Self {
520 builder: self.builder.on_component_initialized(hook),
521 task_executor: self.task_executor,
522 }
523 }
524
525 pub fn on_node_started<F>(self, hook: F) -> Self
527 where
528 F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
529 + Send
530 + 'static,
531 {
532 Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
533 }
534
535 pub fn map_add_ons<F>(self, f: F) -> Self
537 where
538 F: FnOnce(AO) -> AO,
539 {
540 Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
541 }
542
543 pub fn on_rpc_started<F>(self, hook: F) -> Self
545 where
546 F: FnOnce(
547 RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
548 RethRpcServerHandles,
549 ) -> eyre::Result<()>
550 + Send
551 + 'static,
552 {
553 Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
554 }
555
556 pub fn extend_rpc_modules<F>(self, hook: F) -> Self
591 where
592 F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
593 + Send
594 + 'static,
595 {
596 Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
597 }
598
599 pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
605 where
606 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
607 R: Future<Output = eyre::Result<E>> + Send,
608 E: Future<Output = eyre::Result<()>> + Send,
609 {
610 Self {
611 builder: self.builder.install_exex(exex_id, exex),
612 task_executor: self.task_executor,
613 }
614 }
615
616 pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
622 where
623 F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
624 R: Future<Output = eyre::Result<E>> + Send,
625 E: Future<Output = eyre::Result<()>> + Send,
626 {
627 if cond {
628 self.install_exex(exex_id, exex)
629 } else {
630 self
631 }
632 }
633
634 pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
636 where
637 L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
638 {
639 launcher.launch_node(self.builder).await
640 }
641
642 pub fn launch_with_fn<L, R>(self, launcher: L) -> R
644 where
645 L: FnOnce(Self) -> R,
646 {
647 launcher(self)
648 }
649
650 pub const fn check_launch(self) -> Self {
654 self
655 }
656
657 pub async fn launch(
659 self,
660 ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
661 where
662 EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
663 {
664 let launcher = self.engine_api_launcher();
665 self.builder.launch_with(launcher).await
666 }
667
668 pub fn launch_with_debug_capabilities(
673 self,
674 ) -> <DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Future
675 where
676 T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
677 DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
678 {
679 let Self { builder, task_executor } = self;
680
681 let engine_tree_config = builder.config.engine.tree_config();
682
683 let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
684 task_executor,
685 builder.config.datadir(),
686 engine_tree_config,
687 ));
688 builder.launch_with(launcher)
689 }
690
691 pub fn engine_api_launcher(&self) -> EngineNodeLauncher {
694 let engine_tree_config = self.builder.config.engine.tree_config();
695 EngineNodeLauncher::new(
696 self.task_executor.clone(),
697 self.builder.config.datadir(),
698 engine_tree_config,
699 )
700 }
701}
702
703pub struct BuilderContext<Node: FullNodeTypes> {
705 pub(crate) head: Head,
707 pub(crate) provider: Node::Provider,
709 pub(crate) executor: TaskExecutor,
711 pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
713}
714
715impl<Node: FullNodeTypes> BuilderContext<Node> {
716 pub const fn new(
718 head: Head,
719 provider: Node::Provider,
720 executor: TaskExecutor,
721 config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
722 ) -> Self {
723 Self { head, provider, executor, config_container }
724 }
725
726 pub const fn provider(&self) -> &Node::Provider {
728 &self.provider
729 }
730
731 pub const fn head(&self) -> Head {
733 self.head
734 }
735
736 pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
738 &self.config_container.config
739 }
740
741 pub const fn config_mut(&mut self) -> &mut NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
743 &mut self.config_container.config
744 }
745
746 pub const fn reth_config(&self) -> &reth_config::Config {
748 &self.config_container.toml_config
749 }
750
751 pub const fn task_executor(&self) -> &TaskExecutor {
755 &self.executor
756 }
757
758 pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
760 self.provider().chain_spec()
761 }
762
763 pub const fn is_dev(&self) -> bool {
765 self.config().dev.dev
766 }
767
768 pub fn pool_config(&self) -> PoolConfig {
770 self.config().txpool.pool_config()
771 }
772
773 pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
775 Ok(EnvKzgSettings::Default)
776 }
777
778 pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
780 self.config().builder.clone()
781 }
782
783 pub fn start_network<N, Pool>(
788 &self,
789 builder: NetworkBuilder<(), (), N>,
790 pool: Pool,
791 ) -> NetworkHandle<N>
792 where
793 N: NetworkPrimitives,
794 Pool: TransactionPool<
795 Transaction: PoolTransaction<
796 Consensus = N::BroadcastedTransaction,
797 Pooled = N::PooledTransaction,
798 >,
799 > + Unpin
800 + 'static,
801 Node::Provider: BlockReaderFor<N>,
802 {
803 self.start_network_with(
804 builder,
805 pool,
806 self.config().network.transactions_manager_config(),
807 self.config().network.tx_propagation_policy,
808 )
809 }
810
811 pub fn start_network_with<Pool, N, Policy>(
818 &self,
819 builder: NetworkBuilder<(), (), N>,
820 pool: Pool,
821 tx_config: TransactionsManagerConfig,
822 propagation_policy: Policy,
823 ) -> NetworkHandle<N>
824 where
825 N: NetworkPrimitives,
826 Pool: TransactionPool<
827 Transaction: PoolTransaction<
828 Consensus = N::BroadcastedTransaction,
829 Pooled = N::PooledTransaction,
830 >,
831 > + Unpin
832 + 'static,
833 Node::Provider: BlockReaderFor<N>,
834 Policy: TransactionPropagationPolicy<N>,
835 {
836 let (handle, network, txpool, eth) = builder
837 .transactions_with_policy(pool, tx_config, propagation_policy)
838 .request_handler(self.provider().clone())
839 .split_with_handle();
840
841 self.executor.spawn_critical("p2p txpool", Box::pin(txpool));
842 self.executor.spawn_critical("p2p eth request handler", Box::pin(eth));
843
844 let default_peers_path = self.config().datadir().known_peers();
845 let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
846 self.executor.spawn_critical_with_graceful_shutdown_signal(
847 "p2p network task",
848 |shutdown| {
849 Box::pin(network.run_until_graceful_shutdown(shutdown, |network| {
850 if let Some(peers_file) = known_peers_file {
851 let num_known_peers = network.num_known_peers();
852 trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
853 match network.write_peers_to_file(peers_file.as_path()) {
854 Ok(_) => {
855 info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
856 }
857 Err(err) => {
858 warn!(target: "reth::cli", %err, "Failed to write network peers to file");
859 }
860 }
861 }
862 }))
863 },
864 );
865
866 handle
867 }
868
869 fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
871 let secret_key = self.config().network.secret_key(data_dir.p2p_secret())?;
872 Ok(secret_key)
873 }
874
875 pub fn build_network_config<N>(
877 &self,
878 network_builder: NetworkConfigBuilder<N>,
879 ) -> NetworkConfig<Node::Provider, N>
880 where
881 N: NetworkPrimitives,
882 Node::Types: NodeTypes<ChainSpec: Hardforks>,
883 {
884 network_builder.build(self.provider.clone())
885 }
886}
887
888impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
889 pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
891 where
892 N: NetworkPrimitives,
893 {
894 let network_config = self.network_config()?;
895 let builder = NetworkManager::builder(network_config).await?;
896 Ok(builder)
897 }
898
899 pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
901 where
902 N: NetworkPrimitives,
903 {
904 let network_builder = self.network_config_builder();
905 Ok(self.build_network_config(network_builder?))
906 }
907
908 pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
910 where
911 N: NetworkPrimitives,
912 {
913 let secret_key = self.network_secret(&self.config().datadir())?;
914 let default_peers_path = self.config().datadir().known_peers();
915 let builder = self
916 .config()
917 .network
918 .network_config(
919 self.reth_config(),
920 self.config().chain.clone(),
921 secret_key,
922 default_peers_path,
923 )
924 .with_task_executor(Box::new(self.executor.clone()))
925 .set_head(self.head);
926
927 Ok(builder)
928 }
929}
930
931impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
932 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
933 f.debug_struct("BuilderContext")
934 .field("head", &self.head)
935 .field("provider", &std::any::type_name::<Node::Provider>())
936 .field("executor", &self.executor)
937 .field("config", &self.config())
938 .finish()
939 }
940}