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.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.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    /// Cache of recovered transaction senders shared by node components, if enabled.
755    sender_recovery_cache: Option<reth_evm::SenderRecoveryCache>,
756}
757
758impl<Node: FullNodeTypes> BuilderContext<Node> {
759    /// Create a new instance of [`BuilderContext`]
760    pub fn new(
761        head: Head,
762        provider: Node::Provider,
763        executor: TaskExecutor,
764        config_container: WithConfigs<<Node::Types as NodeTypes>::ChainSpec>,
765    ) -> Self {
766        let sender_recovery_cache = config_container
767            .config
768            .engine
769            .sender_recovery_cache_enabled
770            .then(reth_evm::SenderRecoveryCache::default);
771        Self { head, provider, executor, config_container, sender_recovery_cache }
772    }
773
774    /// Returns the configured provider to interact with the blockchain.
775    pub const fn provider(&self) -> &Node::Provider {
776        &self.provider
777    }
778
779    /// Returns the current head of the blockchain at launch.
780    pub const fn head(&self) -> Head {
781        self.head
782    }
783
784    /// Returns the config of the node.
785    pub const fn config(&self) -> &NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
786        &self.config_container.config
787    }
788
789    /// Returns a mutable reference to the config of the node.
790    pub const fn config_mut(&mut self) -> &mut NodeConfig<<Node::Types as NodeTypes>::ChainSpec> {
791        &mut self.config_container.config
792    }
793
794    /// Returns the loaded reh.toml config.
795    pub const fn reth_config(&self) -> &reth_config::Config {
796        &self.config_container.toml_config
797    }
798
799    /// Returns the executor of the node.
800    ///
801    /// This can be used to execute async tasks or functions during the setup.
802    pub const fn task_executor(&self) -> &TaskExecutor {
803        &self.executor
804    }
805
806    /// Returns the sender recovery cache shared by node components, if enabled.
807    pub const fn sender_recovery_cache(&self) -> Option<&reth_evm::SenderRecoveryCache> {
808        self.sender_recovery_cache.as_ref()
809    }
810
811    /// Returns the chain spec of the node.
812    pub fn chain_spec(&self) -> Arc<<Node::Types as NodeTypes>::ChainSpec> {
813        self.provider().chain_spec()
814    }
815
816    /// Returns true if the node is configured as --dev
817    pub const fn is_dev(&self) -> bool {
818        self.config().dev.dev
819    }
820
821    /// Returns the transaction pool config of the node.
822    pub fn pool_config(&self) -> PoolConfig {
823        self.config().txpool.pool_config()
824    }
825
826    /// Loads `EnvKzgSettings::Default`.
827    pub const fn kzg_settings(&self) -> eyre::Result<EnvKzgSettings> {
828        Ok(EnvKzgSettings::Default)
829    }
830
831    /// Returns the config for payload building.
832    pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
833        self.config().builder.clone()
834    }
835
836    /// Convenience function to start the network tasks.
837    ///
838    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
839    /// connected to that network.
840    pub fn start_network<N, Pool>(
841        &self,
842        builder: NetworkBuilder<(), (), N>,
843        pool: Pool,
844    ) -> NetworkHandle<N>
845    where
846        N: NetworkPrimitives,
847        Pool: TransactionPool<
848                Transaction: PoolTransaction<
849                    Consensus = N::BroadcastedTransaction,
850                    Pooled = N::PooledTransaction,
851                >,
852            > + Unpin
853            + 'static,
854        Node::Provider: BlockReaderFor<N>,
855    {
856        self.start_network_with(
857            builder,
858            pool,
859            self.config().network.transactions_manager_config(),
860            self.config().network.tx_propagation_policy,
861        )
862    }
863
864    /// Convenience function to start the network tasks.
865    ///
866    /// Accepts the config for the transaction task and the policy for propagation.
867    /// Uses the default [`StrictEthAnnouncementFilter`] for announcement filtering.
868    ///
869    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
870    /// connected to that network.
871    pub fn start_network_with<Pool, N, Policy>(
872        &self,
873        builder: NetworkBuilder<(), (), N>,
874        pool: Pool,
875        tx_config: TransactionsManagerConfig,
876        propagation_policy: Policy,
877    ) -> NetworkHandle<N>
878    where
879        N: NetworkPrimitives,
880        Pool: TransactionPool<
881                Transaction: PoolTransaction<
882                    Consensus = N::BroadcastedTransaction,
883                    Pooled = N::PooledTransaction,
884                >,
885            > + Unpin
886            + 'static,
887        Node::Provider: BlockReaderFor<N>,
888        Policy: TransactionPropagationPolicy<N>,
889    {
890        self.start_network_with_policies(
891            builder,
892            pool,
893            tx_config,
894            propagation_policy,
895            StrictEthAnnouncementFilter::default(),
896        )
897    }
898
899    /// Convenience function to start the network tasks with custom policies.
900    ///
901    /// Accepts the config for the transaction task, the policy for propagation,
902    /// and a custom announcement filter. This is useful for configuring which tx types are accepted
903    /// in announcements.
904    ///
905    /// Spawns the configured network and associated tasks and returns the [`NetworkHandle`]
906    /// connected to that network.
907    pub fn start_network_with_policies<Pool, N, PropPolicy, AnnPolicy>(
908        &self,
909        builder: NetworkBuilder<(), (), N>,
910        pool: Pool,
911        tx_config: TransactionsManagerConfig,
912        propagation_policy: PropPolicy,
913        announcement_policy: AnnPolicy,
914    ) -> NetworkHandle<N>
915    where
916        N: NetworkPrimitives,
917        Pool: TransactionPool<
918                Transaction: PoolTransaction<
919                    Consensus = N::BroadcastedTransaction,
920                    Pooled = N::PooledTransaction,
921                >,
922            > + Unpin
923            + 'static,
924        Node::Provider: BlockReaderFor<N>,
925        PropPolicy: TransactionPropagationPolicy<N>,
926        AnnPolicy: AnnouncementFilteringPolicy<N>,
927    {
928        let (handle, network, txpool, eth) = builder
929            .transactions_with_policies(
930                pool.clone(),
931                tx_config,
932                propagation_policy,
933                announcement_policy,
934            )
935            .map_transactions(|transactions| {
936                if let Some(cache) = self.sender_recovery_cache.clone() {
937                    transactions.with_sender_recovery_cache(cache)
938                } else {
939                    transactions
940                }
941            })
942            .request_handler_with_blob_store(self.provider().clone(), pool.blob_store())
943            .split_with_handle();
944
945        self.executor.spawn_critical_blocking_task("p2p txpool", txpool);
946        self.executor.spawn_critical_blocking_task("p2p eth request handler", eth);
947
948        let default_peers_path = self.config().datadir().known_peers();
949        let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);
950        self.executor.spawn_critical_with_graceful_shutdown_signal(
951            "p2p network task",
952            |shutdown| {
953                network.run_until_graceful_shutdown(shutdown, |network| {
954                    if let Some(peers_file) = known_peers_file {
955                        let num_known_peers = network.num_known_peers();
956                        trace!(target: "reth::cli", peers_file=?peers_file, num_peers=%num_known_peers, "Saving current peers");
957                        match network.write_peers_to_file(peers_file.as_path()) {
958                            Ok(_) => {
959                                info!(target: "reth::cli", peers_file=?peers_file, "Wrote network peers to file");
960                            }
961                            Err(err) => {
962                                warn!(target: "reth::cli", %err, "Failed to write network peers to file");
963                            }
964                        }
965                    }
966                })
967            },
968        );
969
970        handle
971    }
972
973    /// Get the network secret from the given data dir
974    fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
975        let secret_key = self.config().network.secret_key(data_dir.p2p_secret())?;
976        Ok(secret_key)
977    }
978
979    /// Builds the [`NetworkConfig`].
980    pub fn build_network_config<N>(
981        &self,
982        network_builder: NetworkConfigBuilder<N>,
983    ) -> NetworkConfig<Node::Provider, N>
984    where
985        N: NetworkPrimitives,
986        Node::Types: NodeTypes<ChainSpec: Hardforks>,
987    {
988        network_builder.build(self.provider.clone())
989    }
990}
991
992impl<Node: FullNodeTypes<Types: NodeTypes<ChainSpec: Hardforks>>> BuilderContext<Node> {
993    /// Creates the [`NetworkBuilder`] for the node.
994    pub async fn network_builder<N>(&self) -> eyre::Result<NetworkBuilder<(), (), N>>
995    where
996        N: NetworkPrimitives,
997    {
998        let network_config = self.network_config()?;
999        let builder = NetworkManager::builder(network_config).await?;
1000        Ok(builder)
1001    }
1002
1003    /// Returns the default network config for the node.
1004    pub fn network_config<N>(&self) -> eyre::Result<NetworkConfig<Node::Provider, N>>
1005    where
1006        N: NetworkPrimitives,
1007    {
1008        let network_builder = self.network_config_builder();
1009        Ok(self.build_network_config(network_builder?))
1010    }
1011
1012    /// Get the [`NetworkConfigBuilder`].
1013    pub fn network_config_builder<N>(&self) -> eyre::Result<NetworkConfigBuilder<N>>
1014    where
1015        N: NetworkPrimitives,
1016    {
1017        let secret_key = self.network_secret(&self.config().datadir())?;
1018        let default_peers_path = self.config().datadir().known_peers();
1019        let builder = self
1020            .config()
1021            .network
1022            .network_config(
1023                self.reth_config(),
1024                self.config().chain.clone(),
1025                secret_key,
1026                default_peers_path,
1027                self.executor.clone(),
1028            )
1029            .set_head(self.head);
1030
1031        Ok(builder)
1032    }
1033}
1034
1035impl<Node: FullNodeTypes> std::fmt::Debug for BuilderContext<Node> {
1036    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1037        f.debug_struct("BuilderContext")
1038            .field("head", &self.head)
1039            .field("provider", &std::any::type_name::<Node::Provider>())
1040            .field("executor", &self.executor)
1041            .field("config", &self.config())
1042            .finish()
1043    }
1044}