Skip to main content

reth_node_builder/builder/
mod.rs

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