reth_node_builder/builder/
mod.rs

1//! Customizable node builder.
2
3#![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
48/// The adapter type for a reth node with the builtin provider type
49// Note: we need to hardcode this because custom components might depend on it in associated types.
50pub type RethFullAdapter<DB, Types> =
51    FullNodeTypesAdapter<Types, DB, BlockchainProvider<NodeTypesWithDBAdapter<Types, DB>>>;
52
53#[expect(clippy::doc_markdown)]
54#[cfg_attr(doc, aquamarine::aquamarine)]
55/// Declaratively construct a node.
56///
57/// [`NodeBuilder`] provides a [builder-like interface][builder] for composing
58/// components of a node.
59///
60/// ## Order
61///
62/// Configuring a node starts out with a [`NodeConfig`] (this can be obtained from cli arguments for
63/// example) and then proceeds to configure the core static types of the node:
64/// [`NodeTypes`], these include the node's primitive types and the node's engine
65/// types.
66///
67/// Next all stateful components of the node are configured, these include all the
68/// components of the node that are downstream of those types, these include:
69///
70///  - The EVM and Executor configuration: [`ExecutorBuilder`](crate::components::ExecutorBuilder)
71///  - The transaction pool: [`PoolBuilder`](crate::components::PoolBuilder)
72///  - The network: [`NetworkBuilder`](crate::components::NetworkBuilder)
73///  - The payload builder: [`PayloadBuilder`](crate::components::PayloadServiceBuilder)
74///
75/// Once all the components are configured, the node is ready to be launched.
76///
77/// On launch the builder returns a fully type aware [`NodeHandle`] that has access to all the
78/// configured components and can interact with the node.
79///
80/// There are convenience functions for networks that come with a preset of types and components via
81/// the [`Node`] trait, see `reth_node_ethereum::EthereumNode` or `reth_optimism_node::OpNode`.
82///
83/// The [`NodeBuilder::node`] function configures the node's types and components in one step.
84///
85/// ## Components
86///
87/// All components are configured with a [`NodeComponentsBuilder`] that is responsible for actually
88/// creating the node components during the launch process. The
89/// [`ComponentsBuilder`](crate::components::ComponentsBuilder) is a general purpose implementation
90/// of the [`NodeComponentsBuilder`] trait that can be used to configure the executor, network,
91/// transaction pool and payload builder of the node. It enforces the correct order of
92/// configuration, for example the network and the payload builder depend on the transaction pool
93/// type that is configured first.
94///
95/// All builder traits are generic over the node types and are invoked with the [`BuilderContext`]
96/// that gives access to internals of the that are needed to configure the components. This include
97/// the original config, chain spec, the database provider and the task executor,
98///
99/// ## Hooks
100///
101/// Once all the components are configured, the builder can be used to set hooks that are run at
102/// specific points in the node's lifecycle. This way custom services can be spawned before the node
103/// is launched [`NodeBuilderWithComponents::on_component_initialized`], or once the rpc server(s)
104/// are launched [`NodeBuilderWithComponents::on_rpc_started`]. The
105/// [`NodeBuilderWithComponents::extend_rpc_modules`] can be used to inject custom rpc modules into
106/// the rpc server before it is launched. See also [`RpcContext`] All hooks accept a closure that is
107/// then invoked at the appropriate time in the node's launch process.
108///
109/// ## Flow
110///
111/// The [`NodeBuilder`] is intended to sit behind a CLI that provides the necessary [`NodeConfig`]
112/// input: [`NodeBuilder::new`]
113///
114/// From there the builder is configured with the node's types, components, and hooks, then launched
115/// with the [`WithLaunchContext::launch`] method. On launch all the builtin internals, such as the
116/// `Database` and its providers [`BlockchainProvider`] are initialized before the configured
117/// [`NodeComponentsBuilder`] is invoked with the [`BuilderContext`] to create the transaction pool,
118/// network, and payload builder components. When the RPC is configured, the corresponding hooks are
119/// invoked to allow for custom rpc modules to be injected into the rpc server:
120/// [`NodeBuilderWithComponents::extend_rpc_modules`]
121///
122/// Finally all components are created and all services are launched and a [`NodeHandle`] is
123/// returned that can be used to interact with the node: [`FullNode`]
124///
125/// The following diagram shows the flow of the node builder from CLI to a launched node.
126///
127/// include_mmd!("docs/mermaid/builder.mmd")
128///
129/// ## Internals
130///
131/// The node builder is fully type safe, it uses the [`NodeTypes`] trait to enforce that
132/// all components are configured with the correct types. However the database types and with that
133/// the provider trait implementations are currently created by the builder itself during the launch
134/// process, hence the database type is not part of the [`NodeTypes`] trait and the node's
135/// components, that depend on the database, are configured separately. In order to have a nice
136/// trait that encapsulates the entire node the
137/// [`FullNodeComponents`](reth_node_api::FullNodeComponents) trait was introduced. This
138/// trait has convenient associated types for all the components of the node. After
139/// [`WithLaunchContext::launch`] the [`NodeHandle`] contains an instance of [`FullNode`] that
140/// implements the [`FullNodeComponents`](reth_node_api::FullNodeComponents) trait and has access to
141/// all the components of the node. Internally the node builder uses several generic adapter types
142/// that are then map to traits with associated types for ease of use.
143///
144/// ### Limitations
145///
146/// Currently the launch process is limited to ethereum nodes and requires all the components
147/// specified above. It also expects beacon consensus with the ethereum engine API that is
148/// configured by the builder itself during launch. This might change in the future.
149///
150/// [builder]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
151pub struct NodeBuilder<DB, ChainSpec> {
152    /// All settings for how the node should be configured.
153    config: NodeConfig<ChainSpec>,
154    /// The configured database for the node.
155    database: DB,
156}
157
158impl<ChainSpec> NodeBuilder<(), ChainSpec> {
159    /// Create a new [`NodeBuilder`].
160    pub const fn new(config: NodeConfig<ChainSpec>) -> Self {
161        Self { config, database: () }
162    }
163}
164
165impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec> {
166    /// Returns a reference to the node builder's config.
167    pub const fn config(&self) -> &NodeConfig<ChainSpec> {
168        &self.config
169    }
170
171    /// Returns a mutable reference to the node builder's config.
172    pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
173        &mut self.config
174    }
175
176    /// Returns a reference to the node's database
177    pub const fn db(&self) -> &DB {
178        &self.database
179    }
180
181    /// Returns a mutable reference to the node's database
182    pub const fn db_mut(&mut self) -> &mut DB {
183        &mut self.database
184    }
185
186    /// Applies a fallible function to the builder.
187    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    /// Applies a fallible function to the builder, if the condition is `true`.
195    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    /// Apply a function to the builder
207    pub fn apply<F>(self, f: F) -> Self
208    where
209        F: FnOnce(Self) -> Self,
210    {
211        f(self)
212    }
213
214    /// Apply a function to the builder, if the condition is `true`.
215    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    /// Configures the underlying database that the node will use.
229    pub fn with_database<D>(self, database: D) -> NodeBuilder<D, ChainSpec> {
230        NodeBuilder { config: self.config, database }
231    }
232
233    /// Preconfigure the builder with the context to launch the node.
234    ///
235    /// This provides the task executor and the data directory for the node.
236    pub const fn with_launch_context(self, task_executor: TaskExecutor) -> WithLaunchContext<Self> {
237        WithLaunchContext { builder: self, task_executor }
238    }
239
240    /// Creates an _ephemeral_ preconfigured node for testing purposes.
241    #[cfg(feature = "test-utils")]
242    pub fn testing_node(
243        self,
244        task_executor: TaskExecutor,
245    ) -> WithLaunchContext<
246        NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
247    > {
248        let path = reth_db::test_utils::tempdir_path();
249        self.testing_node_with_datadir(task_executor, path)
250    }
251
252    /// Creates a preconfigured node for testing purposes with a specific datadir.
253    #[cfg(feature = "test-utils")]
254    pub fn testing_node_with_datadir(
255        mut self,
256        task_executor: TaskExecutor,
257        datadir: impl Into<std::path::PathBuf>,
258    ) -> WithLaunchContext<
259        NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
260    > {
261        let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(datadir.into());
262        self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
263            datadir: path.clone(),
264            ..Default::default()
265        });
266
267        let data_dir =
268            path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
269
270        let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
271
272        WithLaunchContext { builder: self.with_database(db), task_executor }
273    }
274}
275
276impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
277where
278    DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
279    ChainSpec: EthChainSpec + EthereumHardforks,
280{
281    /// Configures the types of the node.
282    pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
283    where
284        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
285    {
286        self.with_types_and_provider()
287    }
288
289    /// Configures the types of the node and the provider type that will be used by the node.
290    pub fn with_types_and_provider<T, P>(
291        self,
292    ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
293    where
294        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
295        P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
296    {
297        NodeBuilderWithTypes::new(self.config, self.database)
298    }
299
300    /// Preconfigures the node with a specific node implementation.
301    ///
302    /// This is a convenience method that sets the node's types and components in one call.
303    pub fn node<N>(
304        self,
305        node: N,
306    ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
307    where
308        N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
309    {
310        self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
311    }
312}
313
314/// A [`NodeBuilder`] with its launch context already configured.
315///
316/// This exposes the same methods as [`NodeBuilder`] but with the launch context already configured,
317/// See [`WithLaunchContext::launch`]
318pub struct WithLaunchContext<Builder> {
319    builder: Builder,
320    task_executor: TaskExecutor,
321}
322
323impl<Builder> WithLaunchContext<Builder> {
324    /// Returns a reference to the task executor.
325    pub const fn task_executor(&self) -> &TaskExecutor {
326        &self.task_executor
327    }
328}
329
330impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
331    /// Returns a reference to the node builder's config.
332    pub const fn config(&self) -> &NodeConfig<ChainSpec> {
333        self.builder.config()
334    }
335}
336
337impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
338where
339    DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
340    ChainSpec: EthChainSpec + EthereumHardforks,
341{
342    /// Configures the types of the node.
343    pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
344    where
345        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
346    {
347        WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
348    }
349
350    /// Configures the types of the node and the provider type that will be used by the node.
351    pub fn with_types_and_provider<T, P>(
352        self,
353    ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
354    where
355        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
356        P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
357    {
358        WithLaunchContext {
359            builder: self.builder.with_types_and_provider(),
360            task_executor: self.task_executor,
361        }
362    }
363
364    /// Preconfigures the node with a specific node implementation.
365    ///
366    /// This is a convenience method that sets the node's types and components in one call.
367    pub fn node<N>(
368        self,
369        node: N,
370    ) -> WithLaunchContext<
371        NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
372    >
373    where
374        N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
375    {
376        self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
377    }
378
379    /// Launches a preconfigured [Node]
380    ///
381    /// This bootstraps the node internals, creates all the components with the given [Node]
382    ///
383    /// Returns a [`NodeHandle`](crate::NodeHandle) that can be used to interact with the node.
384    pub async fn launch_node<N>(
385        self,
386        node: N,
387    ) -> eyre::Result<
388        <EngineNodeLauncher as LaunchNode<
389            NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
390        >>::Node,
391    >
392    where
393        N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
394        N::AddOns: RethRpcAddOns<
395            NodeAdapter<
396                RethFullAdapter<DB, N>,
397                <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
398            >,
399        >,
400        N::Primitives: FullNodePrimitives,
401        EngineNodeLauncher: LaunchNode<
402            NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
403        >,
404    {
405        self.node(node).launch().await
406    }
407}
408
409impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
410    /// Advances the state of the node builder to the next state where all components are configured
411    pub fn with_components<CB>(
412        self,
413        components_builder: CB,
414    ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
415    where
416        CB: NodeComponentsBuilder<T>,
417    {
418        WithLaunchContext {
419            builder: self.builder.with_components(components_builder),
420            task_executor: self.task_executor,
421        }
422    }
423}
424
425impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
426where
427    T: FullNodeTypes,
428    CB: NodeComponentsBuilder<T>,
429{
430    /// Advances the state of the node builder to the next state where all customizable
431    /// [`NodeAddOns`] types are configured.
432    pub fn with_add_ons<AO>(
433        self,
434        add_ons: AO,
435    ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
436    where
437        AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
438    {
439        WithLaunchContext {
440            builder: self.builder.with_add_ons(add_ons),
441            task_executor: self.task_executor,
442        }
443    }
444}
445
446impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
447where
448    T: FullNodeTypes,
449    CB: NodeComponentsBuilder<T>,
450    AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
451{
452    /// Returns a reference to the node builder's config.
453    pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
454        &self.builder.config
455    }
456
457    /// Returns a reference to node's database.
458    pub const fn db(&self) -> &T::DB {
459        &self.builder.adapter.database
460    }
461
462    /// Returns a mutable reference to node's database.
463    pub const fn db_mut(&mut self) -> &mut T::DB {
464        &mut self.builder.adapter.database
465    }
466
467    /// Applies a fallible function to the builder.
468    pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
469    where
470        F: FnOnce(Self) -> Result<Self, R>,
471    {
472        f(self)
473    }
474
475    /// Applies a fallible function to the builder, if the condition is `true`.
476    pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
477    where
478        F: FnOnce(Self) -> Result<Self, R>,
479    {
480        if cond {
481            f(self)
482        } else {
483            Ok(self)
484        }
485    }
486
487    /// Apply a function to the builder
488    pub fn apply<F>(self, f: F) -> Self
489    where
490        F: FnOnce(Self) -> Self,
491    {
492        f(self)
493    }
494
495    /// Apply a function to the builder, if the condition is `true`.
496    pub fn apply_if<F>(self, cond: bool, f: F) -> Self
497    where
498        F: FnOnce(Self) -> Self,
499    {
500        if cond {
501            f(self)
502        } else {
503            self
504        }
505    }
506
507    /// Sets the hook that is run once the node's components are initialized.
508    pub fn on_component_initialized<F>(self, hook: F) -> Self
509    where
510        F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
511    {
512        Self {
513            builder: self.builder.on_component_initialized(hook),
514            task_executor: self.task_executor,
515        }
516    }
517
518    /// Sets the hook that is run once the node has started.
519    pub fn on_node_started<F>(self, hook: F) -> Self
520    where
521        F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
522            + Send
523            + 'static,
524    {
525        Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
526    }
527
528    /// Modifies the addons with the given closure.
529    pub fn map_add_ons<F>(self, f: F) -> Self
530    where
531        F: FnOnce(AO) -> AO,
532    {
533        Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
534    }
535
536    /// Sets the hook that is run once the rpc server is started.
537    pub fn on_rpc_started<F>(self, hook: F) -> Self
538    where
539        F: FnOnce(
540                RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
541                RethRpcServerHandles,
542            ) -> eyre::Result<()>
543            + Send
544            + 'static,
545    {
546        Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
547    }
548
549    /// Sets the hook that is run to configure the rpc modules.
550    ///
551    /// This hook can obtain the node's components (txpool, provider, etc.) and can modify the
552    /// modules that the RPC server installs.
553    ///
554    /// # Examples
555    ///
556    /// ```rust,ignore
557    /// use jsonrpsee::{core::RpcResult, proc_macros::rpc};
558    ///
559    /// #[derive(Clone)]
560    /// struct CustomApi<Pool> { pool: Pool }
561    ///
562    /// #[rpc(server, namespace = "custom")]
563    /// impl CustomApi {
564    ///     #[method(name = "hello")]
565    ///     async fn hello(&self) -> RpcResult<String> {
566    ///         Ok("World".to_string())
567    ///     }
568    /// }
569    ///
570    /// let node = NodeBuilder::new(config)
571    ///     .node(EthereumNode::default())
572    ///     .extend_rpc_modules(|ctx| {
573    ///         // Access node components, so they can used by the CustomApi
574    ///         let pool = ctx.pool().clone();
575    ///         
576    ///         // Add custom RPC namespace
577    ///         ctx.modules.merge_configured(CustomApi { pool }.into_rpc())?;
578    ///         
579    ///         Ok(())
580    ///     })
581    ///     .build()?;
582    /// ```
583    pub fn extend_rpc_modules<F>(self, hook: F) -> Self
584    where
585        F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
586            + Send
587            + 'static,
588    {
589        Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
590    }
591
592    /// Installs an `ExEx` (Execution Extension) in the node.
593    ///
594    /// # Note
595    ///
596    /// The `ExEx` ID must be unique.
597    pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
598    where
599        F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
600        R: Future<Output = eyre::Result<E>> + Send,
601        E: Future<Output = eyre::Result<()>> + Send,
602    {
603        Self {
604            builder: self.builder.install_exex(exex_id, exex),
605            task_executor: self.task_executor,
606        }
607    }
608
609    /// Installs an `ExEx` (Execution Extension) in the node if the condition is true.
610    ///
611    /// # Note
612    ///
613    /// The `ExEx` ID must be unique.
614    pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
615    where
616        F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
617        R: Future<Output = eyre::Result<E>> + Send,
618        E: Future<Output = eyre::Result<()>> + Send,
619    {
620        if cond {
621            self.install_exex(exex_id, exex)
622        } else {
623            self
624        }
625    }
626
627    /// Launches the node with the given launcher.
628    pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
629    where
630        L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
631    {
632        launcher.launch_node(self.builder).await
633    }
634
635    /// Launches the node with the given closure.
636    pub fn launch_with_fn<L, R>(self, launcher: L) -> R
637    where
638        L: FnOnce(Self) -> R,
639    {
640        launcher(self)
641    }
642
643    /// Check that the builder can be launched
644    ///
645    /// This is useful when writing tests to ensure that the builder is configured correctly.
646    pub const fn check_launch(self) -> Self {
647        self
648    }
649
650    /// Launches the node with the [`EngineNodeLauncher`] that sets up engine API consensus and rpc
651    pub async fn launch(
652        self,
653    ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
654    where
655        EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
656    {
657        let launcher = self.engine_api_launcher();
658        self.builder.launch_with(launcher).await
659    }
660
661    /// Launches the node with the [`DebugNodeLauncher`].
662    ///
663    /// This is equivalent to [`WithLaunchContext::launch`], but will enable the debugging features,
664    /// if they are configured.
665    pub async fn launch_with_debug_capabilities(
666        self,
667    ) -> eyre::Result<<DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
668    where
669        T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
670        DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
671    {
672        let Self { builder, task_executor } = self;
673
674        let engine_tree_config = builder.config.engine.tree_config();
675
676        let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
677            task_executor,
678            builder.config.datadir(),
679            engine_tree_config,
680        ));
681        builder.launch_with(launcher).await
682    }
683
684    /// Returns an [`EngineNodeLauncher`] that can be used to launch the node with engine API
685    /// support.
686    pub fn engine_api_launcher(&self) -> EngineNodeLauncher {
687        let engine_tree_config = self.builder.config.engine.tree_config();
688        EngineNodeLauncher::new(
689            self.task_executor.clone(),
690            self.builder.config.datadir(),
691            engine_tree_config,
692        )
693    }
694}
695
696/// Captures the necessary context for building the components of the node.
697pub struct BuilderContext<Node: FullNodeTypes> {
698    /// The current head of the blockchain at launch.
699    pub(crate) head: Head,
700    /// The configured provider to interact with the blockchain.
701    pub(crate) provider: Node::Provider,
702    /// The executor of the node.
703    pub(crate) executor: TaskExecutor,
704    /// Config container
705    pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
706}
707
708impl<Node: FullNodeTypes> BuilderContext<Node> {
709    /// Create a new instance of [`BuilderContext`]
710    pub const fn new(
711        head: Head,
712        provider: Node::Provider,
713        executor: TaskExecutor,
714        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
715    ) -> Self {
716        Self { head, provider, executor, config_container }
717    }
718
719    /// Returns the configured provider to interact with the blockchain.
720    pub const fn provider(&self) -> &Node::Provider {
721        &self.provider
722    }
723
724    /// Returns the current head of the blockchain at launch.
725    pub const fn head(&self) -> Head {
726        self.head
727    }
728
729    /// Returns the config of the node.
730    pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
731        &self.config_container.config
732    }
733
734    /// Returns the loaded reh.toml config.
735    pub const fn reth_config(&self) -> &reth_config::Config {
736        &self.config_container.toml_config
737    }
738
739    /// Returns the executor of the node.
740    ///
741    /// This can be used to execute async tasks or functions during the setup.
742    pub const fn task_executor(&self) -> &TaskExecutor {
743        &self.executor
744    }
745
746    /// Returns the chain spec of the node.
747    pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
748        self.provider().chain_spec()
749    }
750
751    /// Returns true if the node is configured as --dev
752    pub const fn is_dev(&self) -> bool {
753        self.config().dev.dev
754    }
755
756    /// Returns the transaction pool config of the node.
757    pub fn pool_config(&self) -> PoolConfig {
758        self.config().txpool.pool_config()
759    }
760
761    /// Loads `EnvKzgSettings::Default`.
762    pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
763        Ok(EnvKzgSettings::Default)
764    }
765
766    /// Returns the config for payload building.
767    pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
768        self.config().builder.clone()
769    }
770
771    /// Convenience function to start the network tasks.
772    ///
773    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
774    /// connected to that network.
775    pub fn start_network<N, Pool>(
776        &self,
777        builder: NetworkBuilder<(), (), N>,
778        pool: Pool,
779    ) -> NetworkHandle<N>
780    where
781        N: NetworkPrimitives,
782        Pool: TransactionPool<
783                Transaction: PoolTransaction<
784                    Consensus = N::BroadcastedTransaction,
785                    Pooled = N::PooledTransaction,
786                >,
787            > + Unpin
788            + 'static,
789        Node::Provider: BlockReaderFor<N>,
790    {
791        self.start_network_with(
792            builder,
793            pool,
794            self.config().network.transactions_manager_config(),
795            self.config().network.tx_propagation_policy,
796        )
797    }
798
799    /// Convenience function to start the network tasks.
800    ///
801    /// Accepts the config for the transaction task and the policy for propagation.
802    ///
803    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
804    /// connected to that network.
805    pub fn start_network_with<Pool, N, Policy>(
806        &self,
807        builder: NetworkBuilder<(), (), N>,
808        pool: Pool,
809        tx_config: TransactionsManagerConfig,
810        propagation_policy: Policy,
811    ) -> NetworkHandle<N>
812    where
813        N: NetworkPrimitives,
814        Pool: TransactionPool<
815                Transaction: PoolTransaction<
816                    Consensus = N::BroadcastedTransaction,
817                    Pooled = N::PooledTransaction,
818                >,
819            > + Unpin
820            + 'static,
821        Node::Provider: BlockReaderFor<N>,
822        Policy: TransactionPropagationPolicy + Debug,
823    {
824        let (handle, network, txpool, eth) = builder
825            .transactions_with_policy(pool, tx_config, propagation_policy)
826            .request_handler(self.provider().clone())
827            .split_with_handle();
828
829        self.executor.spawn_critical("p2p txpool", Box::pin(txpool));
830        self.executor.spawn_critical("p2p eth request handler", Box::pin(eth));
831
832        let default_peers_path = self.config().datadir().known_peers();
833        let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
834        self.executor.spawn_critical_with_graceful_shutdown_signal(
835            "p2p network task",
836            |shutdown| {
837                Box::pin(network.run_until_graceful_shutdown(shutdown, |network| {
838                    if let Some(peers_file) = known_peers_file {
839                        let num_known_peers = network.num_known_peers();
840                        trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
841                        match network.write_peers_to_file(peers_file.as_path()) {
842                            Ok(_) => {
843                                info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
844                            }
845                            Err(err) => {
846                                warn!(target: "reth::cli", %err, "Failed to write network peers to file");
847                            }
848                        }
849                    }
850                }))
851            },
852        );
853
854        handle
855    }
856
857    /// Get the network secret from the given data dir
858    fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
859        let network_secret_path =
860            self.config().network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
861        let secret_key = get_secret_key(&network_secret_path)?;
862        Ok(secret_key)
863    }
864
865    /// Builds the [`NetworkConfig`].
866    pub fn build_network_config<N>(
867        &self,
868        network_builder: NetworkConfigBuilder<N>,
869    ) -> NetworkConfig<Node::Provider, N>
870    where
871        N: NetworkPrimitives,
872        Node::Types: NodeTypes<ChainSpec: Hardforks>,
873    {
874        network_builder.build(self.provider.clone())
875    }
876}
877
878impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
879    /// Creates the [`NetworkBuilder`] for the node.
880    pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
881    where
882        N: NetworkPrimitives,
883    {
884        let network_config = self.network_config()?;
885        let builder = NetworkManager::builder(network_config).await?;
886        Ok(builder)
887    }
888
889    /// Returns the default network config for the node.
890    pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
891    where
892        N: NetworkPrimitives,
893    {
894        let network_builder = self.network_config_builder();
895        Ok(self.build_network_config(network_builder?))
896    }
897
898    /// Get the [`NetworkConfigBuilder`].
899    pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
900    where
901        N: NetworkPrimitives,
902    {
903        let secret_key = self.network_secret(&self.config().datadir())?;
904        let default_peers_path = self.config().datadir().known_peers();
905        let builder = self
906            .config()
907            .network
908            .network_config(
909                self.reth_config(),
910                self.config().chain.clone(),
911                secret_key,
912                default_peers_path,
913            )
914            .with_task_executor(Box::new(self.executor.clone()))
915            .set_head(self.head);
916
917        Ok(builder)
918    }
919}
920
921impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
922    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
923        f.debug_struct("BuilderContext")
924            .field("head", &self.head)
925            .field("provider", &std::any::type_name::<Node::Provider>())
926            .field("executor", &self.executor)
927            .field("config", &self.config())
928            .finish()
929    }
930}