//! Node builder states and helper traits. //! //! Keeps track of the current state of the node builder. //! //! The node builder process is essentially a state machine that transitions through various states //! before the node can be launched. use std::{fmt, future::Future, marker::PhantomData}; use reth_exex::ExExContext; use reth_node_api::{FullNodeComponents, FullNodeTypes, NodeAddOns, NodeTypes}; use reth_node_core::{ node_config::NodeConfig, rpc::eth::{helpers::AddDevSigners, FullEthApiServer}, }; use reth_payload_builder::PayloadBuilderHandle; use reth_tasks::TaskExecutor; use crate::{ components::{NodeComponents, NodeComponentsBuilder}, hooks::NodeHooks, launch::LaunchNode, rpc::{EthApiBuilderProvider, RethRpcServerHandles, RpcContext, RpcHooks}, AddOns, FullNode, RpcAddOns, }; /// A node builder that also has the configured types. pub struct NodeBuilderWithTypes { /// All settings for how the node should be configured. config: NodeConfig, /// The configured database for the node. adapter: NodeTypesAdapter, } impl NodeBuilderWithTypes { /// Creates a new instance of the node builder with the given configuration and types. pub const fn new(config: NodeConfig, database: T::DB) -> Self { Self { config, adapter: NodeTypesAdapter::new(database) } } /// Advances the state of the node builder to the next state where all components are configured pub fn with_components(self, components_builder: CB) -> NodeBuilderWithComponents where CB: NodeComponentsBuilder, { let Self { config, adapter } = self; NodeBuilderWithComponents { config, adapter, components_builder, add_ons: AddOns { hooks: NodeHooks::default(), rpc: RpcAddOns { _eth_api: PhantomData::<()>, hooks: RpcHooks::default() }, exexs: Vec::new(), }, } } } /// Container for the node's types and the database the node uses. pub struct NodeTypesAdapter { /// The database type used by the node. pub database: T::DB, } impl NodeTypesAdapter { /// Create a new adapter from the given node types. pub(crate) const fn new(database: T::DB) -> Self { Self { database } } } impl fmt::Debug for NodeTypesAdapter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("NodeTypesAdapter").field("db", &"...").field("types", &"...").finish() } } /// Container for the node's types and the components and other internals that can be used by addons /// of the node. pub struct NodeAdapter> { /// The components of the node. pub components: C, /// The task executor for the node. pub task_executor: TaskExecutor, /// The provider of the node. pub provider: T::Provider, } impl> NodeTypes for NodeAdapter { type Primitives = T::Primitives; type Engine = T::Engine; } impl> FullNodeTypes for NodeAdapter { type DB = T::DB; type Provider = T::Provider; } impl> FullNodeComponents for NodeAdapter { type Pool = C::Pool; type Evm = C::Evm; type Executor = C::Executor; type Network = C::Network; fn pool(&self) -> &Self::Pool { self.components.pool() } fn evm_config(&self) -> &Self::Evm { self.components.evm_config() } fn block_executor(&self) -> &Self::Executor { self.components.block_executor() } fn provider(&self) -> &Self::Provider { &self.provider } fn network(&self) -> &Self::Network { self.components.network() } fn payload_builder(&self) -> &PayloadBuilderHandle { self.components.payload_builder() } fn task_executor(&self) -> &TaskExecutor { &self.task_executor } } impl> Clone for NodeAdapter { fn clone(&self) -> Self { Self { components: self.components.clone(), task_executor: self.task_executor.clone(), provider: self.provider.clone(), } } } /// A fully type configured node builder. /// /// Supports adding additional addons to the node. pub struct NodeBuilderWithComponents< T: FullNodeTypes, CB: NodeComponentsBuilder, AO: NodeAddOns>, > { /// All settings for how the node should be configured. pub config: NodeConfig, /// Adapter for the underlying node types and database pub adapter: NodeTypesAdapter, /// container for type specific components pub components_builder: CB, /// Additional node extensions. pub add_ons: AddOns, AO>, } impl NodeBuilderWithComponents where T: FullNodeTypes, CB: NodeComponentsBuilder, { /// Advances the state of the node builder to the next state where all customizable /// [`NodeAddOns`] types are configured. pub fn with_add_ons(self) -> NodeBuilderWithComponents where AO: NodeAddOns>, { let Self { config, adapter, components_builder, .. } = self; NodeBuilderWithComponents { config, adapter, components_builder, add_ons: AddOns { hooks: NodeHooks::default(), rpc: RpcAddOns { _eth_api: PhantomData::, hooks: RpcHooks::default() }, exexs: Vec::new(), }, } } } impl NodeBuilderWithComponents where T: FullNodeTypes, CB: NodeComponentsBuilder, AO: NodeAddOns>, { /// Sets the hook that is run once the node's components are initialized. pub fn on_component_initialized(mut self, hook: F) -> Self where F: FnOnce(NodeAdapter) -> eyre::Result<()> + Send + 'static, { self.add_ons.hooks.set_on_component_initialized(hook); self } /// Sets the hook that is run once the node has started. pub fn on_node_started(mut self, hook: F) -> Self where F: FnOnce(FullNode, AO>) -> eyre::Result<()> + Send + 'static, { self.add_ons.hooks.set_on_node_started(hook); self } /// Sets the hook that is run once the rpc server is started. pub fn on_rpc_started(mut self, hook: F) -> Self where F: FnOnce( RpcContext<'_, NodeAdapter, AO::EthApi>, RethRpcServerHandles, ) -> eyre::Result<()> + Send + 'static, { self.add_ons.rpc.hooks.set_on_rpc_started(hook); self } /// Sets the hook that is run to configure the rpc modules. pub fn extend_rpc_modules(mut self, hook: F) -> Self where F: FnOnce(RpcContext<'_, NodeAdapter, AO::EthApi>) -> eyre::Result<()> + Send + 'static, { self.add_ons.rpc.hooks.set_extend_rpc_modules(hook); self } /// Installs an `ExEx` (Execution Extension) in the node. /// /// # Note /// /// The `ExEx` ID must be unique. pub fn install_exex(mut self, exex_id: impl Into, exex: F) -> Self where F: FnOnce(ExExContext>) -> R + Send + 'static, R: Future> + Send, E: Future> + Send, { self.add_ons.exexs.push((exex_id.into(), Box::new(exex))); self } /// Launches the node with the given closure. pub fn launch_with_fn(self, launcher: L) -> R where L: FnOnce(Self) -> R, { launcher(self) } /// Check that the builder can be launched /// /// This is useful when writing tests to ensure that the builder is configured correctly. pub const fn check_launch(self) -> Self { self } } impl NodeBuilderWithComponents where T: FullNodeTypes, CB: NodeComponentsBuilder, AO: NodeAddOns>, AO::EthApi: EthApiBuilderProvider> + FullEthApiServer + AddDevSigners, { /// Launches the node with the given launcher. pub async fn launch_with(self, launcher: L) -> eyre::Result where L: LaunchNode, { launcher.launch_node(self).await } }