Skip to main content

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