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` or `reth_optimism_node::OpNode`.
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    #[cfg(feature = "test-utils")]
255    pub fn testing_node_with_datadir(
256        mut self,
257        task_executor: TaskExecutor,
258        datadir: impl Into<std::path::PathBuf>,
259    ) -> WithLaunchContext<
260        NodeBuilder<Arc<reth_db::test_utils::TempDatabase<reth_db::DatabaseEnv>>, ChainSpec>,
261    > {
262        let path = reth_node_core::dirs::MaybePlatformPath::<DataDirPath>::from(datadir.into());
263        self.config = self.config.with_datadir_args(reth_node_core::args::DatadirArgs {
264            datadir: path.clone(),
265            ..Default::default()
266        });
267
268        let data_dir =
269            path.unwrap_or_chain_default(self.config.chain.chain(), self.config.datadir.clone());
270
271        let db = reth_db::test_utils::create_test_rw_db_with_path(data_dir.db());
272
273        WithLaunchContext { builder: self.with_database(db), task_executor }
274    }
275}
276
277impl<DB, ChainSpec> NodeBuilder<DB, ChainSpec>
278where
279    DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
280    ChainSpec: EthChainSpec + EthereumHardforks,
281{
282    /// Configures the types of the node.
283    pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
284    where
285        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
286    {
287        self.with_types_and_provider()
288    }
289
290    /// Configures the types of the node and the provider type that will be used by the node.
291    pub fn with_types_and_provider<T, P>(
292        self,
293    ) -> NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>
294    where
295        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
296        P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
297    {
298        NodeBuilderWithTypes::new(self.config, self.database)
299    }
300
301    /// Preconfigures the node with a specific node implementation.
302    ///
303    /// This is a convenience method that sets the node's types and components in one call.
304    pub fn node<N>(
305        self,
306        node: N,
307    ) -> NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>
308    where
309        N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
310    {
311        self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
312    }
313}
314
315/// A [`NodeBuilder`] with its launch context already configured.
316///
317/// This exposes the same methods as [`NodeBuilder`] but with the launch context already configured,
318/// See [`WithLaunchContext::launch`]
319pub struct WithLaunchContext<Builder> {
320    builder: Builder,
321    task_executor: TaskExecutor,
322}
323
324impl<Builder> WithLaunchContext<Builder> {
325    /// Returns a reference to the task executor.
326    pub const fn task_executor(&self) -> &TaskExecutor {
327        &self.task_executor
328    }
329}
330
331impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>> {
332    /// Returns a reference to the node builder's config.
333    pub const fn config(&self) -> &NodeConfig<ChainSpec> {
334        self.builder.config()
335    }
336
337    /// Returns a mutable reference to the node builder's config.
338    pub const fn config_mut(&mut self) -> &mut NodeConfig<ChainSpec> {
339        self.builder.config_mut()
340    }
341}
342
343impl<DB, ChainSpec> WithLaunchContext<NodeBuilder<DB, ChainSpec>>
344where
345    DB: Database + DatabaseMetrics + Clone + Unpin + 'static,
346    ChainSpec: EthChainSpec + EthereumHardforks,
347{
348    /// Configures the types of the node.
349    pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
350    where
351        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
352    {
353        WithLaunchContext { builder: self.builder.with_types(), task_executor: self.task_executor }
354    }
355
356    /// Configures the types of the node and the provider type that will be used by the node.
357    pub fn with_types_and_provider<T, P>(
358        self,
359    ) -> WithLaunchContext<NodeBuilderWithTypes<FullNodeTypesAdapter<T, DB, P>>>
360    where
361        T: NodeTypesForProvider<ChainSpec = ChainSpec>,
362        P: FullProvider<NodeTypesWithDBAdapter<T, DB>>,
363    {
364        WithLaunchContext {
365            builder: self.builder.with_types_and_provider(),
366            task_executor: self.task_executor,
367        }
368    }
369
370    /// Preconfigures the node with a specific node implementation.
371    ///
372    /// This is a convenience method that sets the node's types and components in one call.
373    pub fn node<N>(
374        self,
375        node: N,
376    ) -> WithLaunchContext<
377        NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
378    >
379    where
380        N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
381    {
382        self.with_types().with_components(node.components_builder()).with_add_ons(node.add_ons())
383    }
384
385    /// Launches a preconfigured [Node]
386    ///
387    /// This bootstraps the node internals, creates all the components with the given [Node]
388    ///
389    /// Returns a [`NodeHandle`](crate::NodeHandle) that can be used to interact with the node.
390    pub async fn launch_node<N>(
391        self,
392        node: N,
393    ) -> eyre::Result<
394        <EngineNodeLauncher as LaunchNode<
395            NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
396        >>::Node,
397    >
398    where
399        N: Node<RethFullAdapter<DB, N>, ChainSpec = ChainSpec> + NodeTypesForProvider,
400        N::AddOns: RethRpcAddOns<
401            NodeAdapter<
402                RethFullAdapter<DB, N>,
403                <N::ComponentsBuilder as NodeComponentsBuilder<RethFullAdapter<DB, N>>>::Components,
404            >,
405        >,
406        EngineNodeLauncher: LaunchNode<
407            NodeBuilderWithComponents<RethFullAdapter<DB, N>, N::ComponentsBuilder, N::AddOns>,
408        >,
409    {
410        self.node(node).launch().await
411    }
412}
413
414impl<T: FullNodeTypes> WithLaunchContext<NodeBuilderWithTypes<T>> {
415    /// Advances the state of the node builder to the next state where all components are configured
416    pub fn with_components<CB>(
417        self,
418        components_builder: CB,
419    ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
420    where
421        CB: NodeComponentsBuilder<T>,
422    {
423        WithLaunchContext {
424            builder: self.builder.with_components(components_builder),
425            task_executor: self.task_executor,
426        }
427    }
428}
429
430impl<T, CB> WithLaunchContext<NodeBuilderWithComponents<T, CB, ()>>
431where
432    T: FullNodeTypes,
433    CB: NodeComponentsBuilder<T>,
434{
435    /// Advances the state of the node builder to the next state where all customizable
436    /// [`NodeAddOns`] types are configured.
437    pub fn with_add_ons<AO>(
438        self,
439        add_ons: AO,
440    ) -> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
441    where
442        AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
443    {
444        WithLaunchContext {
445            builder: self.builder.with_add_ons(add_ons),
446            task_executor: self.task_executor,
447        }
448    }
449}
450
451impl<T, CB, AO> WithLaunchContext<NodeBuilderWithComponents<T, CB, AO>>
452where
453    T: FullNodeTypes,
454    CB: NodeComponentsBuilder<T>,
455    AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
456{
457    /// Returns a reference to the node builder's config.
458    pub const fn config(&self) -> &NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
459        &self.builder.config
460    }
461
462    /// Returns a mutable reference to the node builder's config.
463    pub const fn config_mut(&mut self) -> &mut NodeConfig<<T::Types as NodeTypes>::ChainSpec> {
464        &mut self.builder.config
465    }
466
467    /// Returns a reference to node's database.
468    pub const fn db(&self) -> &T::DB {
469        &self.builder.adapter.database
470    }
471
472    /// Returns a mutable reference to node's database.
473    pub const fn db_mut(&mut self) -> &mut T::DB {
474        &mut self.builder.adapter.database
475    }
476
477    /// Applies a fallible function to the builder.
478    pub fn try_apply<F, R>(self, f: F) -> Result<Self, R>
479    where
480        F: FnOnce(Self) -> Result<Self, R>,
481    {
482        f(self)
483    }
484
485    /// Applies a fallible function to the builder, if the condition is `true`.
486    pub fn try_apply_if<F, R>(self, cond: bool, f: F) -> Result<Self, R>
487    where
488        F: FnOnce(Self) -> Result<Self, R>,
489    {
490        if cond {
491            f(self)
492        } else {
493            Ok(self)
494        }
495    }
496
497    /// Apply a function to the builder
498    pub fn apply<F>(self, f: F) -> Self
499    where
500        F: FnOnce(Self) -> Self,
501    {
502        f(self)
503    }
504
505    /// Apply a function to the builder, if the condition is `true`.
506    pub fn apply_if<F>(self, cond: bool, f: F) -> Self
507    where
508        F: FnOnce(Self) -> Self,
509    {
510        if cond {
511            f(self)
512        } else {
513            self
514        }
515    }
516
517    /// Sets the hook that is run once the node's components are initialized.
518    pub fn on_component_initialized<F>(self, hook: F) -> Self
519    where
520        F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
521    {
522        Self {
523            builder: self.builder.on_component_initialized(hook),
524            task_executor: self.task_executor,
525        }
526    }
527
528    /// Sets the hook that is run once the node has started.
529    pub fn on_node_started<F>(self, hook: F) -> Self
530    where
531        F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
532            + Send
533            + 'static,
534    {
535        Self { builder: self.builder.on_node_started(hook), task_executor: self.task_executor }
536    }
537
538    /// Modifies the addons with the given closure.
539    ///
540    /// This method provides access to methods on the addons type that don't have
541    /// direct builder methods. It's useful for advanced configuration scenarios
542    /// where you need to call addon-specific methods.
543    ///
544    /// # Examples
545    ///
546    /// ```rust,ignore
547    /// use tower::layer::util::Identity;
548    ///
549    /// let builder = NodeBuilder::new(config)
550    ///     .with_types::<EthereumNode>()
551    ///     .with_components(EthereumNode::components())
552    ///     .with_add_ons(EthereumAddOns::default())
553    ///     .map_add_ons(|addons| addons.with_rpc_middleware(Identity::default()));
554    /// ```
555    ///
556    /// # See also
557    ///
558    /// - [`NodeAddOns`] trait for available addon types
559    /// - [`crate::NodeBuilderWithComponents::extend_rpc_modules`] for RPC module configuration
560    pub fn map_add_ons<F>(self, f: F) -> Self
561    where
562        F: FnOnce(AO) -> AO,
563    {
564        Self { builder: self.builder.map_add_ons(f), task_executor: self.task_executor }
565    }
566
567    /// Sets the hook that is run once the rpc server is started.
568    pub fn on_rpc_started<F>(self, hook: F) -> Self
569    where
570        F: FnOnce(
571                RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
572                RethRpcServerHandles,
573            ) -> eyre::Result<()>
574            + Send
575            + 'static,
576    {
577        Self { builder: self.builder.on_rpc_started(hook), task_executor: self.task_executor }
578    }
579
580    /// Sets the hook that is run to configure the rpc modules.
581    ///
582    /// This hook can obtain the node's components (txpool, provider, etc.) and can modify the
583    /// modules that the RPC server installs.
584    ///
585    /// # Examples
586    ///
587    /// ```rust,ignore
588    /// use jsonrpsee::{core::RpcResult, proc_macros::rpc};
589    ///
590    /// #[derive(Clone)]
591    /// struct CustomApi<Pool> { pool: Pool }
592    ///
593    /// #[rpc(server, namespace = "custom")]
594    /// impl CustomApi {
595    ///     #[method(name = "hello")]
596    ///     async fn hello(&self) -> RpcResult<String> {
597    ///         Ok("World".to_string())
598    ///     }
599    /// }
600    ///
601    /// let node = NodeBuilder::new(config)
602    ///     .node(EthereumNode::default())
603    ///     .extend_rpc_modules(|ctx| {
604    ///         // Access node components, so they can used by the CustomApi
605    ///         let pool = ctx.pool().clone();
606    ///
607    ///         // Add custom RPC namespace
608    ///         ctx.modules.merge_configured(CustomApi { pool }.into_rpc())?;
609    ///
610    ///         Ok(())
611    ///     })
612    ///     .build()?;
613    /// ```
614    pub fn extend_rpc_modules<F>(self, hook: F) -> Self
615    where
616        F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
617            + Send
618            + 'static,
619    {
620        Self { builder: self.builder.extend_rpc_modules(hook), task_executor: self.task_executor }
621    }
622
623    /// Installs an `ExEx` (Execution Extension) in the node.
624    ///
625    /// # Note
626    ///
627    /// The `ExEx` ID must be unique.
628    pub fn install_exex<F, R, E>(self, exex_id: impl Into<String>, exex: F) -> Self
629    where
630        F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
631        R: Future<Output = eyre::Result<E>> + Send,
632        E: Future<Output = eyre::Result<()>> + Send,
633    {
634        Self {
635            builder: self.builder.install_exex(exex_id, exex),
636            task_executor: self.task_executor,
637        }
638    }
639
640    /// Installs an `ExEx` (Execution Extension) in the node if the condition is true.
641    ///
642    /// # Note
643    ///
644    /// The `ExEx` ID must be unique.
645    pub fn install_exex_if<F, R, E>(self, cond: bool, exex_id: impl Into<String>, exex: F) -> Self
646    where
647        F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
648        R: Future<Output = eyre::Result<E>> + Send,
649        E: Future<Output = eyre::Result<()>> + Send,
650    {
651        if cond {
652            self.install_exex(exex_id, exex)
653        } else {
654            self
655        }
656    }
657
658    /// Launches the node with the given launcher.
659    pub async fn launch_with<L>(self, launcher: L) -> eyre::Result<L::Node>
660    where
661        L: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
662    {
663        launcher.launch_node(self.builder).await
664    }
665
666    /// Launches the node with the given closure.
667    pub fn launch_with_fn<L, R>(self, launcher: L) -> R
668    where
669        L: FnOnce(Self) -> R,
670    {
671        launcher(self)
672    }
673
674    /// Check that the builder can be launched
675    ///
676    /// This is useful when writing tests to ensure that the builder is configured correctly.
677    pub const fn check_launch(self) -> Self {
678        self
679    }
680
681    /// Launches the node with the [`EngineNodeLauncher`] that sets up engine API consensus and rpc
682    pub async fn launch(
683        self,
684    ) -> eyre::Result<<EngineNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Node>
685    where
686        EngineNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
687    {
688        let launcher = self.engine_api_launcher();
689        self.builder.launch_with(launcher).await
690    }
691
692    /// Launches the node with the [`DebugNodeLauncher`].
693    ///
694    /// This is equivalent to [`WithLaunchContext::launch`], but will enable the debugging features,
695    /// if they are configured.
696    pub fn launch_with_debug_capabilities(
697        self,
698    ) -> <DebugNodeLauncher as LaunchNode<NodeBuilderWithComponents<T, CB, AO>>>::Future
699    where
700        T::Types: DebugNode<NodeAdapter<T, CB::Components>>,
701        DebugNodeLauncher: LaunchNode<NodeBuilderWithComponents<T, CB, AO>>,
702    {
703        let Self { builder, task_executor } = self;
704
705        let engine_tree_config = builder.config.engine.tree_config();
706
707        let launcher = DebugNodeLauncher::new(EngineNodeLauncher::new(
708            task_executor,
709            builder.config.datadir(),
710            engine_tree_config,
711        ));
712        builder.launch_with(launcher)
713    }
714
715    /// Returns an [`EngineNodeLauncher`] that can be used to launch the node with engine API
716    /// support.
717    pub fn engine_api_launcher(&self) -> EngineNodeLauncher {
718        let engine_tree_config = self.builder.config.engine.tree_config();
719        EngineNodeLauncher::new(
720            self.task_executor.clone(),
721            self.builder.config.datadir(),
722            engine_tree_config,
723        )
724    }
725}
726
727/// Captures the necessary context for building the components of the node.
728pub struct BuilderContext<Node: FullNodeTypes> {
729    /// The current head of the blockchain at launch.
730    pub(crate) head: Head,
731    /// The configured provider to interact with the blockchain.
732    pub(crate) provider: Node::Provider,
733    /// The executor of the node.
734    pub(crate) executor: TaskExecutor,
735    /// Config container
736    pub(crate) config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
737}
738
739impl<Node: FullNodeTypes> BuilderContext<Node> {
740    /// Create a new instance of [`BuilderContext`]
741    pub const fn new(
742        head: Head,
743        provider: Node::Provider,
744        executor: TaskExecutor,
745        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
746    ) -> Self {
747        Self { head, provider, executor, config_container }
748    }
749
750    /// Returns the configured provider to interact with the blockchain.
751    pub const fn provider(&self) -> &Node::Provider {
752        &self.provider
753    }
754
755    /// Returns the current head of the blockchain at launch.
756    pub const fn head(&self) -> Head {
757        self.head
758    }
759
760    /// Returns the config of the node.
761    pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
762        &self.config_container.config
763    }
764
765    /// Returns a mutable reference to the config of the node.
766    pub const fn config_mut(&mut self) -> &mut NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
767        &mut self.config_container.config
768    }
769
770    /// Returns the loaded reh.toml config.
771    pub const fn reth_config(&self) -> &reth_config::Config {
772        &self.config_container.toml_config
773    }
774
775    /// Returns the executor of the node.
776    ///
777    /// This can be used to execute async tasks or functions during the setup.
778    pub const fn task_executor(&self) -> &TaskExecutor {
779        &self.executor
780    }
781
782    /// Returns the chain spec of the node.
783    pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
784        self.provider().chain_spec()
785    }
786
787    /// Returns true if the node is configured as --dev
788    pub const fn is_dev(&self) -> bool {
789        self.config().dev.dev
790    }
791
792    /// Returns the transaction pool config of the node.
793    pub fn pool_config(&self) -> PoolConfig {
794        self.config().txpool.pool_config()
795    }
796
797    /// Loads `EnvKzgSettings::Default`.
798    pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
799        Ok(EnvKzgSettings::Default)
800    }
801
802    /// Returns the config for payload building.
803    pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
804        self.config().builder.clone()
805    }
806
807    /// Convenience function to start the network tasks.
808    ///
809    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
810    /// connected to that network.
811    pub fn start_network<N, Pool>(
812        &self,
813        builder: NetworkBuilder<(), (), N>,
814        pool: Pool,
815    ) -> NetworkHandle<N>
816    where
817        N: NetworkPrimitives,
818        Pool: TransactionPool<
819                Transaction: PoolTransaction<
820                    Consensus = N::BroadcastedTransaction,
821                    Pooled = N::PooledTransaction,
822                >,
823            > + Unpin
824            + 'static,
825        Node::Provider: BlockReaderFor<N>,
826    {
827        self.start_network_with(
828            builder,
829            pool,
830            self.config().network.transactions_manager_config(),
831            self.config().network.tx_propagation_policy,
832        )
833    }
834
835    /// Convenience function to start the network tasks.
836    ///
837    /// Accepts the config for the transaction task and the policy for propagation.
838    /// Uses the default [`StrictEthAnnouncementFilter`] for announcement filtering.
839    ///
840    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
841    /// connected to that network.
842    pub fn start_network_with<Pool, N, Policy>(
843        &self,
844        builder: NetworkBuilder<(), (), N>,
845        pool: Pool,
846        tx_config: TransactionsManagerConfig,
847        propagation_policy: Policy,
848    ) -> NetworkHandle<N>
849    where
850        N: NetworkPrimitives,
851        Pool: TransactionPool<
852                Transaction: PoolTransaction<
853                    Consensus = N::BroadcastedTransaction,
854                    Pooled = N::PooledTransaction,
855                >,
856            > + Unpin
857            + 'static,
858        Node::Provider: BlockReaderFor<N>,
859        Policy: TransactionPropagationPolicy<N>,
860    {
861        self.start_network_with_policies(
862            builder,
863            pool,
864            tx_config,
865            propagation_policy,
866            StrictEthAnnouncementFilter::default(),
867        )
868    }
869
870    /// Convenience function to start the network tasks with custom policies.
871    ///
872    /// Accepts the config for the transaction task, the policy for propagation,
873    /// and a custom announcement filter. This is useful for configuring which tx types are accepted
874    /// in announcements.
875    ///
876    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
877    /// connected to that network.
878    pub fn start_network_with_policies<Pool, N, PropPolicy, AnnPolicy>(
879        &self,
880        builder: NetworkBuilder<(), (), N>,
881        pool: Pool,
882        tx_config: TransactionsManagerConfig,
883        propagation_policy: PropPolicy,
884        announcement_policy: AnnPolicy,
885    ) -> NetworkHandle<N>
886    where
887        N: NetworkPrimitives,
888        Pool: TransactionPool<
889                Transaction: PoolTransaction<
890                    Consensus = N::BroadcastedTransaction,
891                    Pooled = N::PooledTransaction,
892                >,
893            > + Unpin
894            + 'static,
895        Node::Provider: BlockReaderFor<N>,
896        PropPolicy: TransactionPropagationPolicy<N>,
897        AnnPolicy: AnnouncementFilteringPolicy<N>,
898    {
899        let (handle, network, txpool, eth) = builder
900            .transactions_with_policies(pool, tx_config, propagation_policy, announcement_policy)
901            .request_handler(self.provider().clone())
902            .split_with_handle();
903
904        self.executor.spawn_critical_blocking("p2p txpool", Box::pin(txpool));
905        self.executor.spawn_critical_blocking("p2p eth request handler", Box::pin(eth));
906
907        let default_peers_path = self.config().datadir().known_peers();
908        let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
909        self.executor.spawn_critical_with_graceful_shutdown_signal(
910            "p2p network task",
911            |shutdown| {
912                Box::pin(network.run_until_graceful_shutdown(shutdown, |network| {
913                    if let Some(peers_file) = known_peers_file {
914                        let num_known_peers = network.num_known_peers();
915                        trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
916                        match network.write_peers_to_file(peers_file.as_path()) {
917                            Ok(_) => {
918                                info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
919                            }
920                            Err(err) => {
921                                warn!(target: "reth::cli", %err, "Failed to write network peers to file");
922                            }
923                        }
924                    }
925                }))
926            },
927        );
928
929        handle
930    }
931
932    /// Get the network secret from the given data dir
933    fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
934        let secret_key = self.config().network.secret_key(data_dir.p2p_secret())?;
935        Ok(secret_key)
936    }
937
938    /// Builds the [`NetworkConfig`].
939    pub fn build_network_config<N>(
940        &self,
941        network_builder: NetworkConfigBuilder<N>,
942    ) -> NetworkConfig<Node::Provider, N>
943    where
944        N: NetworkPrimitives,
945        Node::Types: NodeTypes<ChainSpec: Hardforks>,
946    {
947        network_builder.build(self.provider.clone())
948    }
949}
950
951impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
952    /// Creates the [`NetworkBuilder`] for the node.
953    pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
954    where
955        N: NetworkPrimitives,
956    {
957        let network_config = self.network_config()?;
958        let builder = NetworkManager::builder(network_config).await?;
959        Ok(builder)
960    }
961
962    /// Returns the default network config for the node.
963    pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
964    where
965        N: NetworkPrimitives,
966    {
967        let network_builder = self.network_config_builder();
968        Ok(self.build_network_config(network_builder?))
969    }
970
971    /// Get the [`NetworkConfigBuilder`].
972    pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
973    where
974        N: NetworkPrimitives,
975    {
976        let secret_key = self.network_secret(&self.config().datadir())?;
977        let default_peers_path = self.config().datadir().known_peers();
978        let builder = self
979            .config()
980            .network
981            .network_config(
982                self.reth_config(),
983                self.config().chain.clone(),
984                secret_key,
985                default_peers_path,
986            )
987            .with_task_executor(Box::new(self.executor.clone()))
988            .set_head(self.head);
989
990        Ok(builder)
991    }
992}
993
994impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
995    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
996        f.debug_struct("BuilderContext")
997            .field("head", &self.head)
998            .field("provider", &std::any::type_name::<Node::Provider>())
999            .field("executor", &self.executor)
1000            .field("config", &self.config())
1001            .finish()
1002    }
1003}