reth_node_builder/builder/
states.rs

1//! Node builder states and helper traits.
2//!
3//! Keeps track of the current state of the node builder.
4//!
5//! The node builder process is essentially a state machine that transitions through various states
6//! before the node can be launched.
7
8use crate::{
9    components::{NodeComponents, NodeComponentsBuilder},
10    hooks::NodeHooks,
11    launch::LaunchNode,
12    rpc::{RethRpcAddOns, RethRpcServerHandles, RpcContext},
13    AddOns, ComponentsFor, FullNode,
14};
15
16use reth_exex::ExExContext;
17use reth_node_api::{FullNodeComponents, FullNodeTypes, NodeAddOns, NodeTypes};
18use reth_node_core::node_config::NodeConfig;
19use reth_tasks::TaskExecutor;
20use std::{fmt, fmt::Debug, future::Future};
21
22/// A node builder that also has the configured types.
23pub struct NodeBuilderWithTypes<T: FullNodeTypes> {
24    /// All settings for how the node should be configured.
25    config: NodeConfig<<T::Types as NodeTypes>::ChainSpec>,
26    /// The configured database for the node.
27    adapter: NodeTypesAdapter<T>,
28}
29
30impl<T: FullNodeTypes> NodeBuilderWithTypes<T> {
31    /// Creates a new instance of the node builder with the given configuration and types.
32    pub const fn new(
33        config: NodeConfig<<T::Types as NodeTypes>::ChainSpec>,
34        database: T::DB,
35    ) -> Self {
36        Self { config, adapter: NodeTypesAdapter::new(database) }
37    }
38
39    /// Advances the state of the node builder to the next state where all components are configured
40    pub fn with_components<CB>(self, components_builder: CB) -> NodeBuilderWithComponents<T, CB, ()>
41    where
42        CB: NodeComponentsBuilder<T>,
43    {
44        let Self { config, adapter } = self;
45
46        NodeBuilderWithComponents {
47            config,
48            adapter,
49            components_builder,
50            add_ons: AddOns { hooks: NodeHooks::default(), exexs: Vec::new(), add_ons: () },
51        }
52    }
53}
54
55/// Container for the node's types and the database the node uses.
56pub struct NodeTypesAdapter<T: FullNodeTypes> {
57    /// The database type used by the node.
58    pub database: T::DB,
59}
60
61impl<T: FullNodeTypes> NodeTypesAdapter<T> {
62    /// Create a new adapter from the given node types.
63    pub(crate) const fn new(database: T::DB) -> Self {
64        Self { database }
65    }
66}
67
68impl<T: FullNodeTypes> fmt::Debug for NodeTypesAdapter<T> {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.debug_struct("NodeTypesAdapter").field("db", &"...").field("types", &"...").finish()
71    }
72}
73
74/// Container for the node's types and the components and other internals that can be used by
75/// addons of the node.
76#[derive(Debug)]
77pub struct NodeAdapter<T: FullNodeTypes, C: NodeComponents<T> = ComponentsFor<T>> {
78    /// The components of the node.
79    pub components: C,
80    /// The task executor for the node.
81    pub task_executor: TaskExecutor,
82    /// The provider of the node.
83    pub provider: T::Provider,
84}
85
86impl<T: FullNodeTypes, C: NodeComponents<T>> FullNodeTypes for NodeAdapter<T, C> {
87    type Types = T::Types;
88    type DB = T::DB;
89    type Provider = T::Provider;
90}
91
92impl<T: FullNodeTypes, C: NodeComponents<T>> FullNodeComponents for NodeAdapter<T, C> {
93    type Pool = C::Pool;
94    type Evm = C::Evm;
95    type Consensus = C::Consensus;
96    type Network = C::Network;
97
98    fn pool(&self) -> &Self::Pool {
99        self.components.pool()
100    }
101
102    fn evm_config(&self) -> &Self::Evm {
103        self.components.evm_config()
104    }
105
106    fn consensus(&self) -> &Self::Consensus {
107        self.components.consensus()
108    }
109
110    fn network(&self) -> &Self::Network {
111        self.components.network()
112    }
113
114    fn payload_builder_handle(
115        &self,
116    ) -> &reth_payload_builder::PayloadBuilderHandle<
117        <Self::Types as reth_node_api::NodeTypes>::Payload,
118    > {
119        self.components.payload_builder_handle()
120    }
121
122    fn provider(&self) -> &Self::Provider {
123        &self.provider
124    }
125
126    fn task_executor(&self) -> &TaskExecutor {
127        &self.task_executor
128    }
129}
130
131impl<T: FullNodeTypes, C: NodeComponents<T>> Clone for NodeAdapter<T, C> {
132    fn clone(&self) -> Self {
133        Self {
134            components: self.components.clone(),
135            task_executor: self.task_executor.clone(),
136            provider: self.provider.clone(),
137        }
138    }
139}
140
141/// A fully type configured node builder.
142///
143/// Supports adding additional addons to the node.
144pub struct NodeBuilderWithComponents<
145    T: FullNodeTypes,
146    CB: NodeComponentsBuilder<T>,
147    AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
148> {
149    /// All settings for how the node should be configured.
150    pub config: NodeConfig<<T::Types as NodeTypes>::ChainSpec>,
151    /// Adapter for the underlying node types and database
152    pub adapter: NodeTypesAdapter<T>,
153    /// container for type specific components
154    pub components_builder: CB,
155    /// Additional node extensions.
156    pub add_ons: AddOns<NodeAdapter<T, CB::Components>, AO>,
157}
158
159impl<T, CB> NodeBuilderWithComponents<T, CB, ()>
160where
161    T: FullNodeTypes,
162    CB: NodeComponentsBuilder<T>,
163{
164    /// Advances the state of the node builder to the next state where all customizable
165    /// [`NodeAddOns`] types are configured.
166    pub fn with_add_ons<AO>(self, add_ons: AO) -> NodeBuilderWithComponents<T, CB, AO>
167    where
168        AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
169    {
170        let Self { config, adapter, components_builder, .. } = self;
171
172        NodeBuilderWithComponents {
173            config,
174            adapter,
175            components_builder,
176            add_ons: AddOns { hooks: NodeHooks::default(), exexs: Vec::new(), add_ons },
177        }
178    }
179}
180
181impl<T, CB, AO> NodeBuilderWithComponents<T, CB, AO>
182where
183    T: FullNodeTypes,
184    CB: NodeComponentsBuilder<T>,
185    AO: NodeAddOns<NodeAdapter<T, CB::Components>>,
186{
187    /// Sets the hook that is run once the node's components are initialized.
188    pub fn on_component_initialized<F>(mut self, hook: F) -> Self
189    where
190        F: FnOnce(NodeAdapter<T, CB::Components>) -> eyre::Result<()> + Send + 'static,
191    {
192        self.add_ons.hooks.set_on_component_initialized(hook);
193        self
194    }
195
196    /// Sets the hook that is run once the node has started.
197    pub fn on_node_started<F>(mut self, hook: F) -> Self
198    where
199        F: FnOnce(FullNode<NodeAdapter<T, CB::Components>, AO>) -> eyre::Result<()>
200            + Send
201            + 'static,
202    {
203        self.add_ons.hooks.set_on_node_started(hook);
204        self
205    }
206
207    /// Installs an `ExEx` (Execution Extension) in the node.
208    ///
209    /// # Note
210    ///
211    /// The `ExEx` ID must be unique.
212    pub fn install_exex<F, R, E>(mut self, exex_id: impl Into<String>, exex: F) -> Self
213    where
214        F: FnOnce(ExExContext<NodeAdapter<T, CB::Components>>) -> R + Send + 'static,
215        R: Future<Output = eyre::Result<E>> + Send,
216        E: Future<Output = eyre::Result<()>> + Send,
217    {
218        self.add_ons.exexs.push((exex_id.into(), Box::new(exex)));
219        self
220    }
221
222    /// Launches the node with the given closure.
223    pub fn launch_with_fn<L, R>(self, launcher: L) -> R
224    where
225        L: FnOnce(Self) -> R,
226    {
227        launcher(self)
228    }
229
230    /// Check that the builder can be launched
231    ///
232    /// This is useful when writing tests to ensure that the builder is configured correctly.
233    pub const fn check_launch(self) -> Self {
234        self
235    }
236
237    /// Modifies the addons with the given closure.
238    ///
239    /// This method provides access to methods on the addons type that don't have
240    /// direct builder methods. It's useful for advanced configuration scenarios
241    /// where you need to call addon-specific methods.
242    ///
243    /// # Examples
244    ///
245    /// ```rust,ignore
246    /// use tower::layer::util::Identity;
247    ///
248    /// let builder = NodeBuilder::new(config)
249    ///     .with_types::<EthereumNode>()
250    ///     .with_components(EthereumNode::components())
251    ///     .with_add_ons(EthereumAddOns::default())
252    ///     .map_add_ons(|addons| addons.with_rpc_middleware(Identity::default()));
253    /// ```
254    ///
255    /// # See also
256    ///
257    /// - [`NodeAddOns`] trait for available addon types
258    /// - [`crate::NodeBuilderWithComponents::extend_rpc_modules`] for RPC module configuration
259    pub fn map_add_ons<F>(mut self, f: F) -> Self
260    where
261        F: FnOnce(AO) -> AO,
262    {
263        self.add_ons.add_ons = f(self.add_ons.add_ons);
264        self
265    }
266}
267
268impl<T, CB, AO> NodeBuilderWithComponents<T, CB, AO>
269where
270    T: FullNodeTypes,
271    CB: NodeComponentsBuilder<T>,
272    AO: RethRpcAddOns<NodeAdapter<T, CB::Components>>,
273{
274    /// Launches the node with the given launcher.
275    pub fn launch_with<L>(self, launcher: L) -> L::Future
276    where
277        L: LaunchNode<Self>,
278    {
279        launcher.launch_node(self)
280    }
281
282    /// Sets the hook that is run once the rpc server is started.
283    pub fn on_rpc_started<F>(self, hook: F) -> Self
284    where
285        F: FnOnce(
286                RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>,
287                RethRpcServerHandles,
288            ) -> eyre::Result<()>
289            + Send
290            + 'static,
291    {
292        self.map_add_ons(|mut add_ons| {
293            add_ons.hooks_mut().set_on_rpc_started(hook);
294            add_ons
295        })
296    }
297
298    /// Sets the hook that is run to configure the rpc modules.
299    pub fn extend_rpc_modules<F>(self, hook: F) -> Self
300    where
301        F: FnOnce(RpcContext<'_, NodeAdapter<T, CB::Components>, AO::EthApi>) -> eyre::Result<()>
302            + Send
303            + 'static,
304    {
305        self.map_add_ons(|mut add_ons| {
306            add_ons.hooks_mut().set_extend_rpc_modules(hook);
307            add_ons
308        })
309    }
310}
311
312#[cfg(test)]
313mod test {
314    use super::*;
315    use crate::components::Components;
316    use reth_consensus::noop::NoopConsensus;
317    use reth_db_api::mock::DatabaseMock;
318    use reth_ethereum_engine_primitives::EthEngineTypes;
319    use reth_evm::noop::NoopEvmConfig;
320    use reth_evm_ethereum::MockEvmConfig;
321    use reth_network::EthNetworkPrimitives;
322    use reth_network_api::noop::NoopNetwork;
323    use reth_node_api::FullNodeTypesAdapter;
324    use reth_node_ethereum::EthereumNode;
325    use reth_payload_builder::PayloadBuilderHandle;
326    use reth_provider::noop::NoopProvider;
327    use reth_tasks::TaskManager;
328    use reth_transaction_pool::noop::NoopTransactionPool;
329
330    #[test]
331    fn test_noop_components() {
332        let components = Components::<
333            FullNodeTypesAdapter<EthereumNode, DatabaseMock, NoopProvider>,
334            NoopNetwork<EthNetworkPrimitives>,
335            _,
336            NoopEvmConfig<MockEvmConfig>,
337            _,
338        > {
339            transaction_pool: NoopTransactionPool::default(),
340            evm_config: NoopEvmConfig::default(),
341            consensus: NoopConsensus::default(),
342            network: NoopNetwork::default(),
343            payload_builder_handle: PayloadBuilderHandle::<EthEngineTypes>::noop(),
344        };
345
346        let task_executor = {
347            let runtime = tokio::runtime::Runtime::new().unwrap();
348            let handle = runtime.handle().clone();
349            let manager = TaskManager::new(handle);
350            manager.executor()
351        };
352
353        let node = NodeAdapter { components, task_executor, provider: NoopProvider::default() };
354
355        // test that node implements `FullNodeComponents``
356        <NodeAdapter<_, _> as FullNodeComponents>::pool(&node);
357    }
358}