Skip to main content

reth_node_builder/builder/
mod.rs

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