Skip to main content

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