feat: declarative builder v2 (#6447)

Co-authored-by: Oliver Nordbjerg <onbjerg@users.noreply.github.com>
This commit is contained in:
Matthias Seitz
2024-02-13 23:51:38 +01:00
committed by GitHub
parent 11dbd0867e
commit cfc914669b
39 changed files with 2213 additions and 17 deletions

41
Cargo.lock generated
View File

@@ -6368,6 +6368,38 @@ dependencies = [
"thiserror",
]
[[package]]
name = "reth-node-builder"
version = "0.1.0-alpha.17"
dependencies = [
"confy",
"eyre",
"fdlimit",
"futures",
"reth-auto-seal-consensus",
"reth-beacon-consensus",
"reth-blockchain-tree",
"reth-config",
"reth-db",
"reth-interfaces",
"reth-network",
"reth-node-api",
"reth-node-core",
"reth-payload-builder",
"reth-primitives",
"reth-provider",
"reth-prune",
"reth-revm",
"reth-rpc",
"reth-rpc-engine-api",
"reth-snapshot",
"reth-stages",
"reth-tasks",
"reth-tracing",
"reth-transaction-pool",
"tokio",
]
[[package]]
name = "reth-node-core"
version = "0.1.0-alpha.18"
@@ -6441,10 +6473,19 @@ dependencies = [
name = "reth-node-ethereum"
version = "0.1.0-alpha.18"
dependencies = [
"eyre",
"reth-basic-payload-builder",
"reth-db",
"reth-ethereum-payload-builder",
"reth-network",
"reth-node-api",
"reth-node-builder",
"reth-payload-builder",
"reth-primitives",
"reth-provider",
"reth-rpc-types",
"reth-tracing",
"reth-transaction-pool",
"serde",
]

View File

@@ -37,6 +37,7 @@ members = [
"crates/rpc/rpc-types/",
"crates/rpc/rpc-types-compat/",
"crates/node-ethereum/",
"crates/node-builder/",
"crates/node-optimism/",
"crates/node-core/",
"crates/node-api/",
@@ -125,6 +126,7 @@ reth-consensus-common = { path = "crates/consensus/common" }
reth-db = { path = "crates/storage/db" }
reth-discv4 = { path = "crates/net/discv4" }
reth-dns-discovery = { path = "crates/net/dns" }
reth-node-builder = { path = "crates/node-builder" }
reth-node-ethereum = { path = "crates/node-ethereum" }
reth-node-optimism = { path = "crates/node-optimism" }
reth-node-core = { path = "crates/node-core" }
@@ -269,4 +271,4 @@ similar-asserts = "1.5.0"
[workspace.metadata.cargo-udeps.ignore]
# ignored because this is mutually exclusive with the optimism payload builder via feature flags
normal = ["reth-ethereum-payload-builder"]
normal = ["reth-ethereum-payload-builder", "reth-node-ethereum"]

View File

@@ -161,6 +161,7 @@ optimism = [
"reth-payload-builder/optimism",
"reth-optimism-payload-builder/optimism",
"reth-ethereum-payload-builder/optimism",
"reth-node-ethereum/optimism",
"dep:reth-node-optimism",
"reth-node-core/optimism",
]

View File

@@ -1,5 +1,6 @@
//! This contains the [EngineTypes] trait and implementations for ethereum mainnet types.
use core::fmt;
use reth_primitives::{ChainSpec, Hardfork};
/// Contains traits to abstract over payload attributes types and default implementations of the
@@ -15,8 +16,10 @@ pub use error::AttributesValidationError;
pub mod payload;
pub use payload::PayloadOrAttributes;
/// The types that are used by the engine.
pub trait EngineTypes: serde::de::DeserializeOwned + Send + Sync + Clone {
/// The types that are used by the engine API.
pub trait EngineTypes:
serde::de::DeserializeOwned + fmt::Debug + Unpin + Send + Sync + Clone
{
/// The RPC payload attributes type the CL node emits via the engine API.
type PayloadAttributes: PayloadAttributes + Unpin;

View File

@@ -1,6 +1,9 @@
use reth_primitives::{revm::env::fill_block_env, Address, ChainSpec, Header, Transaction, U256};
use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, SpecId, TxEnv};
/// EVM configuration trait.
pub trait EvmConfig: ConfigureEvmEnv + Clone + Send + Sync + 'static {}
/// This represents the set of methods used to configure the EVM before execution.
pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone {
/// The type of the transaction metadata.

View File

@@ -20,3 +20,5 @@ pub use engine::{
/// Traits and helper types used to abstract over EVM methods and types.
pub mod evm;
pub use evm::ConfigureEvmEnv;
pub mod primitives;

View File

@@ -0,0 +1,8 @@
//! Type abstraction for node primitive types.
/// Configures all the primitive types of the node.
// TODO(mattsse): this is currently a placeholder
pub trait NodePrimitives {}
// TODO(mattsse): Placeholder
impl NodePrimitives for () {}

View File

@@ -0,0 +1,50 @@
[package]
name = "reth-node-builder"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
[lints]
workspace = true
[dependencies]
## reth
reth-auto-seal-consensus.workspace = true
reth-beacon-consensus.workspace = true
reth-blockchain-tree.workspace = true
reth-provider.workspace = true
reth-revm.workspace = true
reth-db.workspace = true
reth-rpc-engine-api.workspace = true
reth-rpc.workspace = true
reth-node-api.workspace = true
reth-node-core.workspace = true
reth-network.workspace = true
reth-primitives.workspace = true
reth-payload-builder.workspace = true
reth-transaction-pool.workspace = true
reth-tasks.workspace = true
reth-tracing.workspace = true
reth-interfaces.workspace = true
reth-snapshot.workspace = true
reth-prune.workspace = true
reth-stages.workspace = true
reth-config.workspace = true
## async
futures.workspace = true
tokio = { workspace = true, features = [
"sync",
"macros",
"time",
"rt-multi-thread",
] }
## misc
eyre.workspace = true
fdlimit = "0.3.0"
confy.workspace = true

View File

@@ -0,0 +1,786 @@
//! Customizable node builder.
#![allow(clippy::type_complexity, missing_debug_implementations)]
use crate::{
components::{
FullNodeComponents, FullNodeComponentsAdapter, NodeComponents, NodeComponentsBuilder,
},
hooks::NodeHooks,
node::{FullNode, FullNodeTypes, FullNodeTypesAdapter, NodeTypes},
rpc::{RethRpcServerHandles, RpcContext, RpcHooks},
NodeHandle,
};
use eyre::Context;
use futures::{future::Either, stream, stream_select, StreamExt};
use reth_beacon_consensus::{
hooks::{EngineHooks, PruneHook},
BeaconConsensusEngine,
};
use reth_blockchain_tree::{BlockchainTreeConfig, ShareableBlockchainTree};
use reth_db::{
database::Database,
database_metrics::{DatabaseMetadata, DatabaseMetrics},
};
use reth_interfaces::p2p::either::EitherDownloader;
use reth_network::{NetworkBuilder, NetworkEvents, NetworkHandle};
use reth_node_core::{
cli::config::{PayloadBuilderConfig, RethRpcConfig, RethTransactionPoolConfig},
dirs::{ChainPath, DataDirPath},
events::cl::ConsensusLayerHealthEvents,
exit::NodeExitFuture,
init::init_genesis,
node_config::NodeConfig,
primitives::{kzg::KzgSettings, Head},
utils::write_peers_to_file,
};
use reth_primitives::{
constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP},
ChainSpec, DisplayHardforks,
};
use reth_provider::{providers::BlockchainProvider, ChainSpecProvider, ProviderFactory};
use reth_prune::{PrunerBuilder, PrunerEvent};
use reth_revm::EvmProcessorFactory;
use reth_rpc_engine_api::EngineApi;
use reth_tasks::TaskExecutor;
use reth_tracing::tracing::{debug, info};
use reth_transaction_pool::{PoolConfig, TransactionPool};
use std::sync::Arc;
use tokio::sync::{mpsc::unbounded_channel, oneshot};
/// The builtin provider type of the reth node.
// Note: we need to hardcode this because custom components might depend on it in associated types.
type RethFullProviderType<DB, Evm> =
BlockchainProvider<DB, ShareableBlockchainTree<DB, EvmProcessorFactory<Evm>>>;
/// Declaratively construct a node.
///
/// [`NodeBuilder`] provides a [builder-like interface][builder] for composing
/// components of a node.
///
/// Configuring a node starts out with a [`NodeConfig`] and then proceeds to configure the core
/// static types of the node: [NodeTypes], these include the node's primitive types and the node's
/// engine types.
///
/// Next all stateful components of the node are configured, these include the
/// [EvmConfig](reth_node_api::evm::EvmConfig), the database [Database] and finally all the
/// components of the node that are downstream of those types, these include:
///
/// - The transaction pool: [PoolBuilder](crate::components::PoolBuilder)
/// - The network: [NetworkBuilder](crate::components::NetworkBuilder)
/// - The payload builder: [PayloadBuilder](crate::components::PayloadServiceBuilder)
///
/// Finally, the node is ready to launch [NodeBuilder::launch]
///
/// [builder]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
pub struct NodeBuilder<DB, State> {
/// All settings for how the node should be configured.
config: NodeConfig,
/// State of the node builder process.
state: State,
/// The configured database for the node.
database: DB,
}
impl<DB, State> NodeBuilder<DB, State> {
/// Returns a reference to the node builder's config.
pub fn config(&self) -> &NodeConfig {
&self.config
}
/// Loads the reth config with the given datadir root
fn load_config(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<reth_config::Config> {
let config_path = self.config.config.clone().unwrap_or_else(|| data_dir.config_path());
let mut config = confy::load_path::<reth_config::Config>(&config_path)
.wrap_err_with(|| format!("Could not load config file {:?}", config_path))?;
info!(target: "reth::cli", path = ?config_path, "Configuration loaded");
// Update the config with the command line arguments
config.peers.connect_trusted_nodes_only = self.config.network.trusted_only;
if !self.config.network.trusted_peers.is_empty() {
info!(target: "reth::cli", "Adding trusted nodes");
self.config.network.trusted_peers.iter().for_each(|peer| {
config.peers.trusted_nodes.insert(*peer);
});
}
Ok(config)
}
}
impl NodeBuilder<(), InitState> {
/// Create a new [`NodeBuilder`].
pub fn new(config: NodeConfig) -> Self {
Self { config, database: (), state: InitState::default() }
}
}
impl<DB> NodeBuilder<DB, InitState> {
/// Configures the additional external context, e.g. additional context captured via CLI args.
pub fn with_database<D>(self, database: D) -> NodeBuilder<D, InitState> {
NodeBuilder { config: self.config, state: self.state, database }
}
}
impl<DB> NodeBuilder<DB, InitState>
where
DB: Database + Clone + 'static,
{
/// Configures the types of the node.
pub fn with_types<T>(self, types: T) -> NodeBuilder<DB, TypesState<T, DB>>
where
T: NodeTypes,
{
NodeBuilder {
config: self.config,
state: TypesState { adapter: FullNodeTypesAdapter::new(types) },
database: self.database,
}
}
}
impl<DB, Types> NodeBuilder<DB, TypesState<Types, DB>>
where
Types: NodeTypes,
DB: Database + Clone + Unpin + 'static,
{
/// Configures the node's components.
pub fn with_components<Components>(
self,
components_builder: Components,
) -> NodeBuilder<
DB,
ComponentsState<
Types,
Components,
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
Components::Pool,
>,
>,
>
where
Components: NodeComponentsBuilder<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
>,
{
NodeBuilder {
config: self.config,
database: self.database,
state: ComponentsState {
types: self.state.adapter.types,
components_builder,
hooks: NodeHooks::new(),
rpc: RpcHooks::new(),
},
}
}
}
impl<DB, Types, Components>
NodeBuilder<
DB,
ComponentsState<
Types,
Components,
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
Components::Pool,
>,
>,
>
where
DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static,
Types: NodeTypes,
Components: NodeComponentsBuilder<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
>,
{
/// Apply a function to the components builder.
pub fn map_components(self, f: impl FnOnce(Components) -> Components) -> Self {
Self {
config: self.config,
database: self.database,
state: ComponentsState {
types: self.state.types,
components_builder: f(self.state.components_builder),
hooks: self.state.hooks,
rpc: self.state.rpc,
},
}
}
/// Resets the setup process to the components stage.
///
/// CAUTION: All previously configured hooks will be lost.
pub fn fuse_components<C>(
self,
components_builder: C,
) -> NodeBuilder<
DB,
ComponentsState<
Types,
C,
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
C::Pool,
>,
>,
>
where
C: NodeComponentsBuilder<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
>,
{
NodeBuilder {
config: self.config,
database: self.database,
state: ComponentsState {
types: self.state.types,
components_builder,
hooks: NodeHooks::new(),
rpc: RpcHooks::new(),
},
}
}
/// Sets the hook that is run once the node's components are initialized.
pub fn on_component_initialized<F>(mut self, hook: F) -> Self
where
F: Fn(
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
Components::Pool,
>,
) -> eyre::Result<()>
+ 'static,
{
self.state.hooks.set_on_component_initialized(hook);
self
}
/// Sets the hook that is run once the node has started.
pub fn on_node_started<F>(mut self, hook: F) -> Self
where
F: Fn(
FullNode<
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
Components::Pool,
>,
>,
) -> eyre::Result<()>
+ 'static,
{
self.state.hooks.set_on_node_started(hook);
self
}
/// Sets the hook that is run once the rpc server is started.
pub fn on_rpc_started<F>(mut self, hook: F) -> Self
where
F: Fn(
RpcContext<
'_,
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
Components::Pool,
>,
>,
RethRpcServerHandles,
) -> eyre::Result<()>
+ 'static,
{
self.state.rpc.set_on_rpc_started(hook);
self
}
/// Sets the hook that is run to configure the rpc modules.
pub fn extend_rpc_modules<F>(mut self, hook: F) -> Self
where
F: Fn(
RpcContext<
'_,
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
Components::Pool,
>,
>,
) -> eyre::Result<()>
+ 'static,
{
self.state.rpc.set_extend_rpc_modules(hook);
self
}
/// Launches the node and returns a handle to it.
pub async fn launch(
self,
executor: TaskExecutor,
data_dir: ChainPath<DataDirPath>,
) -> eyre::Result<
NodeHandle<
FullNodeComponentsAdapter<
FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
Components::Pool,
>,
>,
> {
// get config
let reth_config = self.load_config(&data_dir)?;
let Self {
config,
state: ComponentsState { types, components_builder, hooks, rpc },
database,
} = self;
// Raise the fd limit of the process.
// Does not do anything on windows.
fdlimit::raise_fd_limit()?;
let prometheus_handle = config.install_prometheus_recorder()?;
config.start_metrics_endpoint(prometheus_handle, database.clone()).await?;
info!(target: "reth::cli", "Database opened");
let mut provider_factory =
ProviderFactory::new(database.clone(), Arc::clone(&config.chain));
// configure snapshotter
let snapshotter = reth_snapshot::Snapshotter::new(
provider_factory.clone(),
data_dir.snapshots_path(),
config.chain.snapshot_block_interval,
)?;
provider_factory = provider_factory
.with_snapshots(data_dir.snapshots_path(), snapshotter.highest_snapshot_receiver())?;
debug!(target: "reth::cli", chain=%config.chain.chain, genesis=?config.chain.genesis_hash(), "Initializing genesis");
let genesis_hash = init_genesis(database.clone(), config.chain.clone())?;
info!(target: "reth::cli", "{}", DisplayHardforks::new(config.chain.hardforks()));
let consensus = config.consensus();
debug!(target: "reth::cli", "Spawning stages metrics listener task");
let (sync_metrics_tx, sync_metrics_rx) = unbounded_channel();
let sync_metrics_listener = reth_stages::MetricsListener::new(sync_metrics_rx);
executor.spawn_critical("stages metrics listener task", sync_metrics_listener);
let prune_config = config.prune_config()?.or(reth_config.prune.clone());
let evm_config = types.evm_config();
let tree_config = BlockchainTreeConfig::default();
let tree = config.build_blockchain_tree(
provider_factory.clone(),
consensus.clone(),
prune_config.clone(),
sync_metrics_tx.clone(),
tree_config,
evm_config.clone(),
)?;
let canon_state_notification_sender = tree.canon_state_notification_sender();
let blockchain_tree = ShareableBlockchainTree::new(tree);
debug!(target: "reth::cli", "configured blockchain tree");
// fetch the head block from the database
let head =
config.lookup_head(provider_factory.clone()).wrap_err("the head block is missing")?;
// setup the blockchain provider
let blockchain_db =
BlockchainProvider::new(provider_factory.clone(), blockchain_tree.clone())?;
let ctx = BuilderContext {
head,
provider: blockchain_db,
executor,
data_dir,
config,
reth_config,
};
debug!(target: "reth::cli", "creating components");
let NodeComponents { transaction_pool, network, payload_builder } =
components_builder.build_components(&ctx)?;
let BuilderContext {
provider: blockchain_db,
executor,
data_dir,
mut config,
reth_config,
..
} = ctx;
let NodeHooks { on_component_initialized, on_node_started, .. } = hooks;
let node_components = FullNodeComponentsAdapter {
evm_config: evm_config.clone(),
pool: transaction_pool.clone(),
network: network.clone(),
provider: blockchain_db.clone(),
payload_builder: payload_builder.clone(),
executor: executor.clone(),
};
debug!(target: "reth::cli", "calling on_component_initialized hook");
on_component_initialized.on_event(node_components.clone())?;
// create pipeline
let network_client = network.fetch_client().await?;
let (consensus_engine_tx, consensus_engine_rx) = unbounded_channel();
let max_block = config.max_block(&network_client, provider_factory.clone()).await?;
// Configure the pipeline
let (mut pipeline, client) = if config.dev.dev {
info!(target: "reth::cli", "Starting Reth in dev mode");
let mining_mode = config.mining_mode(transaction_pool.pending_transactions_listener());
let (_, client, mut task) = reth_auto_seal_consensus::AutoSealBuilder::new(
Arc::clone(&config.chain),
blockchain_db.clone(),
transaction_pool.clone(),
consensus_engine_tx.clone(),
canon_state_notification_sender,
mining_mode,
evm_config.clone(),
)
.build();
let mut pipeline = config
.build_networked_pipeline(
&reth_config.stages,
client.clone(),
Arc::clone(&consensus),
provider_factory.clone(),
&executor,
sync_metrics_tx,
prune_config.clone(),
max_block,
evm_config,
)
.await?;
let pipeline_events = pipeline.events();
task.set_pipeline_events(pipeline_events);
debug!(target: "reth::cli", "Spawning auto mine task");
executor.spawn(Box::pin(task));
(pipeline, EitherDownloader::Left(client))
} else {
let pipeline = config
.build_networked_pipeline(
&reth_config.stages,
network_client.clone(),
Arc::clone(&consensus),
provider_factory.clone(),
&executor,
sync_metrics_tx,
prune_config.clone(),
max_block,
evm_config,
)
.await?;
(pipeline, EitherDownloader::Right(network_client))
};
let pipeline_events = pipeline.events();
let initial_target = config.initial_pipeline_target(genesis_hash);
let mut hooks = EngineHooks::new();
let pruner_events = if let Some(prune_config) = prune_config {
let mut pruner = PrunerBuilder::new(prune_config.clone())
.max_reorg_depth(tree_config.max_reorg_depth() as usize)
.prune_delete_limit(config.chain.prune_delete_limit)
.build(provider_factory, snapshotter.highest_snapshot_receiver());
let events = pruner.events();
hooks.add(PruneHook::new(pruner, Box::new(executor.clone())));
info!(target: "reth::cli", ?prune_config, "Pruner initialized");
Either::Left(events)
} else {
Either::Right(stream::empty::<PrunerEvent>())
};
// Configure the consensus engine
let (beacon_consensus_engine, beacon_engine_handle) = BeaconConsensusEngine::with_channel(
client,
pipeline,
blockchain_db.clone(),
Box::new(executor.clone()),
Box::new(network.clone()),
max_block,
config.debug.continuous,
payload_builder.clone(),
initial_target,
reth_beacon_consensus::MIN_BLOCKS_FOR_PIPELINE_RUN,
consensus_engine_tx,
consensus_engine_rx,
hooks,
)?;
info!(target: "reth::cli", "Consensus engine initialized");
let events = stream_select!(
network.event_listener().map(Into::into),
beacon_engine_handle.event_listener().map(Into::into),
pipeline_events.map(Into::into),
if config.debug.tip.is_none() {
Either::Left(
ConsensusLayerHealthEvents::new(Box::new(blockchain_db.clone()))
.map(Into::into),
)
} else {
Either::Right(stream::empty())
},
pruner_events.map(Into::into)
);
executor.spawn_critical(
"events task",
reth_node_core::events::node::handle_events(
Some(network.clone()),
Some(head.number),
events,
database.clone(),
),
);
let engine_api = EngineApi::new(
blockchain_db.clone(),
config.chain.clone(),
beacon_engine_handle,
payload_builder.into(),
Box::new(executor.clone()),
);
info!(target: "reth::cli", "Engine API handler initialized");
// extract the jwt secret from the args if possible
let default_jwt_path = data_dir.jwt_path();
let jwt_secret = config.rpc.auth_jwt_secret(default_jwt_path)?;
// adjust rpc port numbers based on instance number
config.adjust_instance_ports();
// Start RPC servers
let (rpc_server_handles, rpc_registry) = crate::rpc::launch_rpc_servers(
node_components.clone(),
engine_api,
&config,
jwt_secret,
rpc,
)
.await?;
// Run consensus engine to completion
let (tx, rx) = oneshot::channel();
info!(target: "reth::cli", "Starting consensus engine");
executor.spawn_critical_blocking("consensus engine", async move {
let res = beacon_consensus_engine.await;
let _ = tx.send(res);
});
let FullNodeComponentsAdapter {
evm_config,
pool,
network,
provider,
payload_builder,
executor,
} = node_components;
let full_node = FullNode {
evm_config,
pool,
network,
provider,
payload_builder,
executor,
rpc_server_handles,
rpc_registry,
config,
data_dir,
};
// Notify on node started
on_node_started.on_event(full_node.clone())?;
let handle = NodeHandle {
node_exit_future: NodeExitFuture::new(rx, full_node.config.debug.terminate),
node: full_node,
};
Ok(handle)
}
/// Check that the builder can be launched
///
/// This is useful when writing tests to ensure that the builder is configured correctly.
pub fn check_launch(self) -> Self {
self
}
}
/// Captures the necessary context for building the components of the node.
#[derive(Debug)]
pub struct BuilderContext<Node: FullNodeTypes> {
/// The current head of the blockchain at launch.
head: Head,
/// The configured provider to interact with the blockchain.
provider: Node::Provider,
/// The executor of the node.
executor: TaskExecutor,
/// The data dir of the node.
data_dir: ChainPath<DataDirPath>,
/// The config of the node
config: NodeConfig,
/// loaded config
reth_config: reth_config::Config,
}
impl<Node: FullNodeTypes> BuilderContext<Node> {
/// Returns the configured provider to interact with the blockchain.
pub fn provider(&self) -> &Node::Provider {
&self.provider
}
/// Returns the current head of the blockchain at launch.
pub fn head(&self) -> Head {
self.head
}
/// Returns the config of the node.
pub fn config(&self) -> &NodeConfig {
&self.config
}
/// Returns the data dir of the node.
///
/// This gives access to all relevant files and directories of the node's datadir.
pub fn data_dir(&self) -> &ChainPath<DataDirPath> {
&self.data_dir
}
/// Returns the executor of the node.
///
/// This can be used to execute async tasks or functions during the setup.
pub fn task_executor(&self) -> &TaskExecutor {
&self.executor
}
/// Returns the chain spec of the node.
pub fn chain_spec(&self) -> Arc<ChainSpec> {
self.provider().chain_spec()
}
/// Returns the transaction pool config of the node.
pub fn pool_config(&self) -> PoolConfig {
self.config().txpool.pool_config()
}
/// Loads the trusted setup params from a given file path or falls back to
/// `MAINNET_KZG_TRUSTED_SETUP`.
pub fn kzg_settings(&self) -> eyre::Result<Arc<KzgSettings>> {
if let Some(ref trusted_setup_file) = self.config().trusted_setup_file {
let trusted_setup = KzgSettings::load_trusted_setup_file(trusted_setup_file)
.map_err(LoadKzgSettingsError::KzgError)?;
Ok(Arc::new(trusted_setup))
} else {
Ok(Arc::clone(&MAINNET_KZG_TRUSTED_SETUP))
}
}
/// Returns the config for payload building.
pub fn payload_builder_config(&self) -> impl PayloadBuilderConfig {
self.config.builder.clone()
}
/// Creates the [NetworkBuilder] for the node.
pub async fn network_builder(&self) -> eyre::Result<NetworkBuilder<Node::Provider, (), ()>> {
self.config
.build_network(
&self.reth_config,
self.provider.clone(),
self.executor.clone(),
self.head,
self.data_dir(),
)
.await
}
/// Creates the [NetworkBuilder] for the node and blocks until it is ready.
pub fn network_builder_blocking(&self) -> eyre::Result<NetworkBuilder<Node::Provider, (), ()>> {
self.executor.block_on(self.network_builder())
}
/// Spawns the configured network and associated tasks and returns the [NetworkHandle] connected
/// to that network.
pub fn start_network<Pool>(
&self,
builder: NetworkBuilder<Node::Provider, (), ()>,
pool: Pool,
) -> NetworkHandle
where
Pool: TransactionPool + Unpin + 'static,
{
let (handle, network, txpool, eth) =
builder.transactions(pool).request_handler(self.provider().clone()).split_with_handle();
self.executor.spawn_critical("p2p txpool", txpool);
self.executor.spawn_critical("p2p eth request handler", eth);
let default_peers_path = self.data_dir().known_peers_path();
let known_peers_file = self.config.network.persistent_peers_file(default_peers_path);
self.executor.spawn_critical_with_graceful_shutdown_signal(
"p2p network task",
|shutdown| {
network.run_until_graceful_shutdown(shutdown, |network| {
write_peers_to_file(network, known_peers_file)
})
},
);
handle
}
}
/// The initial state of the node builder process.
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct InitState;
/// The state after all types of the node have been configured.
#[derive(Debug)]
pub struct TypesState<Types, DB>
where
DB: Database + Clone + 'static,
Types: NodeTypes,
{
adapter: FullNodeTypesAdapter<Types, DB, RethFullProviderType<DB, Types::Evm>>,
}
/// The state of the node builder process after the node's components have been configured.
///
/// With this state all types and components of the node are known and the node can be launched.
///
/// Additionally, this state captures additional hooks that are called at specific points in the
/// node's launch lifecycle.
#[derive(Debug)]
pub struct ComponentsState<Types, Components, FullNode: FullNodeComponents> {
/// The types of the node.
types: Types,
/// Type that builds the components of the node.
components_builder: Components,
/// Additional NodeHooks that are called at specific points in the node's launch lifecycle.
hooks: NodeHooks<FullNode>,
/// Additional RPC hooks.
rpc: RpcHooks<FullNode>,
}

View File

@@ -0,0 +1,156 @@
//! A generic [NodeComponentsBuilder]
use crate::{
components::{
NetworkBuilder, NodeComponents, NodeComponentsBuilder, PayloadServiceBuilder, PoolBuilder,
},
node::FullNodeTypes,
BuilderContext,
};
use std::marker::PhantomData;
/// A generic, customizable [`NodeComponentsBuilder`].
///
/// This type is stateful and captures the configuration of the node's components.
///
/// ## Component dependencies:
///
/// The components of the node depend on each other:
/// - The payload builder service depends on the transaction pool.
/// - The network depends on the transaction pool.
///
/// We distinguish between different kind of components:
/// - Components that are standalone, such as the transaction pool.
/// - Components that are spawned as a service, such as the payload builder service or the network.
///
/// ## Builder lifecycle:
///
/// First all standalone components are built. Then the service components are spawned.
/// All component builders are captured in the builder state and will be consumed once the node is
/// launched.
#[derive(Debug)]
pub struct ComponentsBuilder<Node, PoolB, PayloadB, NetworkB> {
pool_builder: PoolB,
payload_builder: PayloadB,
network_builder: NetworkB,
_marker: PhantomData<Node>,
}
impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, NetworkB> {
/// Configures the node types.
pub fn node_types<Types>(self) -> ComponentsBuilder<Types, PoolB, PayloadB, NetworkB>
where
Types: FullNodeTypes,
{
let Self { pool_builder, payload_builder, network_builder, _marker } = self;
ComponentsBuilder {
pool_builder,
payload_builder,
network_builder,
_marker: Default::default(),
}
}
/// Apply a function to the pool builder.
pub fn map_pool(self, f: impl FnOnce(PoolB) -> PoolB) -> Self {
Self {
pool_builder: f(self.pool_builder),
payload_builder: self.payload_builder,
network_builder: self.network_builder,
_marker: self._marker,
}
}
/// Apply a function to the payload builder.
pub fn map_payload(self, f: impl FnOnce(PayloadB) -> PayloadB) -> Self {
Self {
pool_builder: self.pool_builder,
payload_builder: f(self.payload_builder),
network_builder: self.network_builder,
_marker: self._marker,
}
}
/// Apply a function to the network builder.
pub fn map_network(self, f: impl FnOnce(NetworkB) -> NetworkB) -> Self {
Self {
pool_builder: self.pool_builder,
payload_builder: self.payload_builder,
network_builder: f(self.network_builder),
_marker: self._marker,
}
}
}
impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, NetworkB>
where
Node: FullNodeTypes,
{
/// Configures the pool builder.
pub fn pool<PB>(self, pool_builder: PB) -> ComponentsBuilder<Node, PB, PayloadB, NetworkB>
where
PB: PoolBuilder<Node>,
{
let Self { payload_builder, network_builder, _marker, .. } = self;
ComponentsBuilder { pool_builder, payload_builder, network_builder, _marker }
}
}
impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, NetworkB>
where
Node: FullNodeTypes,
PoolB: PoolBuilder<Node>,
{
/// Configures the network builder.
pub fn network<NB>(self, network_builder: NB) -> ComponentsBuilder<Node, PoolB, PayloadB, NB>
where
NB: NetworkBuilder<Node, PoolB::Pool>,
{
let Self { payload_builder, pool_builder, _marker, .. } = self;
ComponentsBuilder { pool_builder, payload_builder, network_builder, _marker }
}
/// Configures the payload builder.
pub fn payload<PB>(self, payload_builder: PB) -> ComponentsBuilder<Node, PoolB, PB, NetworkB>
where
PB: PayloadServiceBuilder<Node, PoolB::Pool>,
{
let Self { pool_builder, network_builder, _marker, .. } = self;
ComponentsBuilder { pool_builder, payload_builder, network_builder, _marker }
}
}
impl<Node, PoolB, PayloadB, NetworkB> NodeComponentsBuilder<Node>
for ComponentsBuilder<Node, PoolB, PayloadB, NetworkB>
where
Node: FullNodeTypes,
PoolB: PoolBuilder<Node>,
NetworkB: NetworkBuilder<Node, PoolB::Pool>,
PayloadB: PayloadServiceBuilder<Node, PoolB::Pool>,
{
type Pool = PoolB::Pool;
fn build_components(
self,
context: &BuilderContext<Node>,
) -> eyre::Result<NodeComponents<Node, Self::Pool>> {
let Self { pool_builder, payload_builder, network_builder, _marker } = self;
let pool = pool_builder.build_pool(context)?;
let network = network_builder.build_network(context, pool.clone())?;
let payload_builder = payload_builder.spawn_payload_service(context, pool.clone())?;
Ok(NodeComponents { transaction_pool: pool, network, payload_builder })
}
}
impl Default for ComponentsBuilder<(), (), (), ()> {
fn default() -> Self {
Self {
pool_builder: (),
payload_builder: (),
network_builder: (),
_marker: Default::default(),
}
}
}

View File

@@ -0,0 +1,43 @@
//! Support for configuring the components of a node.
//!
//! Customizable components of the node include:
//! - The transaction pool.
//! - The network implementation.
//! - The payload builder service.
//!
//! Components depend on a fully type configured node: [FullNodeTypes](crate::node::FullNodeTypes).
use crate::node::FullNodeTypes;
pub use builder::*;
pub use network::*;
pub use payload::*;
pub use pool::*;
use reth_network::NetworkHandle;
use reth_payload_builder::PayloadBuilderHandle;
pub use traits::*;
mod builder;
mod network;
mod payload;
mod pool;
mod traits;
/// All the components of the node.
///
/// This provides access to all the components of the node.
#[derive(Debug)]
pub struct NodeComponents<Node: FullNodeTypes, Pool> {
/// The transaction pool of the node.
pub transaction_pool: Pool,
/// The network implementation of the node.
pub network: NetworkHandle,
/// The handle to the payload builder service.
pub payload_builder: PayloadBuilderHandle<Node::Engine>,
}
impl<Node: FullNodeTypes, Pool> NodeComponents<Node, Pool> {
/// Returns the handle to the payload builder service.
pub fn payload_builder(&self) -> PayloadBuilderHandle<Node::Engine> {
self.payload_builder.clone()
}
}

View File

@@ -0,0 +1,22 @@
//! Network component for the node builder.
use crate::{node::FullNodeTypes, BuilderContext};
use reth_network::NetworkHandle;
use reth_transaction_pool::TransactionPool;
/// A type that knows how to build the network implementation.
pub trait NetworkBuilder<Node: FullNodeTypes, Pool: TransactionPool> {
/// Launches the network implementation and returns the handle to it.
fn build_network(self, ctx: &BuilderContext<Node>, pool: Pool) -> eyre::Result<NetworkHandle>;
}
impl<Node, F, Pool> NetworkBuilder<Node, Pool> for F
where
Node: FullNodeTypes,
Pool: TransactionPool,
F: FnOnce(&BuilderContext<Node>, Pool) -> eyre::Result<NetworkHandle>,
{
fn build_network(self, ctx: &BuilderContext<Node>, pool: Pool) -> eyre::Result<NetworkHandle> {
self(ctx, pool)
}
}

View File

@@ -0,0 +1,30 @@
//! Payload service component for the node builder.
use crate::{node::FullNodeTypes, BuilderContext};
use reth_payload_builder::PayloadBuilderHandle;
use reth_transaction_pool::TransactionPool;
/// A type that knows how to spawn the payload service.
pub trait PayloadServiceBuilder<Node: FullNodeTypes, Pool: TransactionPool> {
/// Spawns the payload service and returns the handle to it.
fn spawn_payload_service(
self,
ctx: &BuilderContext<Node>,
pool: Pool,
) -> eyre::Result<PayloadBuilderHandle<Node::Engine>>;
}
impl<Node, F, Pool> PayloadServiceBuilder<Node, Pool> for F
where
Node: FullNodeTypes,
Pool: TransactionPool,
F: FnOnce(&BuilderContext<Node>, Pool) -> eyre::Result<PayloadBuilderHandle<Node::Engine>>,
{
fn spawn_payload_service(
self,
ctx: &BuilderContext<Node>,
pool: Pool,
) -> eyre::Result<PayloadBuilderHandle<Node::Engine>> {
self(ctx, pool)
}
}

View File

@@ -0,0 +1,25 @@
//! Pool component for the node builder.
use crate::{node::FullNodeTypes, BuilderContext};
use reth_transaction_pool::TransactionPool;
/// A type that knows how to build the transaction pool.
pub trait PoolBuilder<Node: FullNodeTypes> {
/// The transaction pool to build.
type Pool: TransactionPool + Unpin + 'static;
/// Creates the transaction pool.
fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool>;
}
impl<Node, F, Pool> PoolBuilder<Node> for F
where
Node: FullNodeTypes,
Pool: TransactionPool + Unpin + 'static,
F: FnOnce(&BuilderContext<Node>) -> eyre::Result<Pool>,
{
type Pool = Pool;
fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> {
self(ctx)
}
}

View File

@@ -0,0 +1,145 @@
//! Traits for the builder
use crate::{
components::NodeComponents,
node::{FullNodeTypes, NodeTypes},
BuilderContext,
};
use reth_network::NetworkHandle;
use reth_payload_builder::PayloadBuilderHandle;
use reth_tasks::TaskExecutor;
use reth_transaction_pool::TransactionPool;
/// Encapsulates all types and components of the node.
pub trait FullNodeComponents: FullNodeTypes + 'static {
/// The transaction pool of the node.
type Pool: TransactionPool;
/// Returns the transaction pool of the node.
fn pool(&self) -> &Self::Pool;
/// Returns the provider of the node.
fn provider(&self) -> &Self::Provider;
/// Returns the handle to the network
fn network(&self) -> &NetworkHandle;
/// Returns the handle to the payload builder service.
fn payload_builder(&self) -> &PayloadBuilderHandle<Self::Engine>;
/// Returns the task executor.
fn task_executor(&self) -> &TaskExecutor;
}
/// A type that encapsulates all the components of the node.
#[derive(Debug)]
pub struct FullNodeComponentsAdapter<Node: FullNodeTypes, Pool> {
pub(crate) evm_config: Node::Evm,
pub(crate) pool: Pool,
pub(crate) network: NetworkHandle,
pub(crate) provider: Node::Provider,
pub(crate) payload_builder: PayloadBuilderHandle<Node::Engine>,
pub(crate) executor: TaskExecutor,
}
impl<Node, Pool> FullNodeTypes for FullNodeComponentsAdapter<Node, Pool>
where
Node: FullNodeTypes,
Pool: TransactionPool + 'static,
{
type DB = Node::DB;
type Provider = Node::Provider;
}
impl<Node, Pool> NodeTypes for FullNodeComponentsAdapter<Node, Pool>
where
Node: FullNodeTypes,
Pool: TransactionPool + 'static,
{
type Primitives = Node::Primitives;
type Engine = Node::Engine;
type Evm = Node::Evm;
fn evm_config(&self) -> Self::Evm {
self.evm_config.clone()
}
}
impl<Node, Pool> FullNodeComponents for FullNodeComponentsAdapter<Node, Pool>
where
Node: FullNodeTypes,
Pool: TransactionPool + 'static,
{
type Pool = Pool;
fn pool(&self) -> &Self::Pool {
&self.pool
}
fn provider(&self) -> &Self::Provider {
&self.provider
}
fn network(&self) -> &NetworkHandle {
&self.network
}
fn payload_builder(&self) -> &PayloadBuilderHandle<Self::Engine> {
&self.payload_builder
}
fn task_executor(&self) -> &TaskExecutor {
&self.executor
}
}
impl<Node: FullNodeTypes, Pool> Clone for FullNodeComponentsAdapter<Node, Pool>
where
Pool: Clone,
{
fn clone(&self) -> Self {
Self {
evm_config: self.evm_config.clone(),
pool: self.pool.clone(),
network: self.network.clone(),
provider: self.provider.clone(),
payload_builder: self.payload_builder.clone(),
executor: self.executor.clone(),
}
}
}
/// A type that configures all the customizable components of the node and knows how to build them.
///
/// Implementors of this trait are responsible for building all the components of the node: See
/// [NodeComponents].
///
/// The [ComponentsBuilder](crate::components::builder::ComponentsBuilder) is a generic
/// implementation of this trait that can be used to customize certain components of the node using
/// the builder pattern and defaults, e.g. Ethereum and Optimism.
pub trait NodeComponentsBuilder<Node: FullNodeTypes> {
/// The transaction pool to use.
type Pool: TransactionPool + Unpin + 'static;
/// Builds the components of the node.
fn build_components(
self,
context: &BuilderContext<Node>,
) -> eyre::Result<NodeComponents<Node, Self::Pool>>;
}
impl<Node, F, Pool> NodeComponentsBuilder<Node> for F
where
Node: FullNodeTypes,
F: FnOnce(&BuilderContext<Node>) -> eyre::Result<NodeComponents<Node, Pool>>,
Pool: TransactionPool + Unpin + 'static,
{
type Pool = Pool;
fn build_components(
self,
ctx: &BuilderContext<Node>,
) -> eyre::Result<NodeComponents<Node, Pool>> {
self(ctx)
}
}

View File

@@ -0,0 +1,20 @@
use crate::{components::FullNodeComponents, node::FullNode};
use reth_node_core::exit::NodeExitFuture;
use std::fmt;
/// A Handle to the launched node.
pub struct NodeHandle<Node: FullNodeComponents> {
/// All node components.
pub node: FullNode<Node>,
/// The exit future of the node.
pub node_exit_future: NodeExitFuture,
}
impl<Node: FullNodeComponents> fmt::Debug for NodeHandle<Node> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeHandle")
.field("node", &"...")
.field("node_exit_future", &self.node_exit_future)
.finish()
}
}

View File

@@ -0,0 +1,119 @@
use crate::{components::FullNodeComponents, node::FullNode};
use std::fmt;
/// Container for all the configurable hook functions.
pub(crate) struct NodeHooks<Node: FullNodeComponents> {
pub(crate) on_component_initialized: Box<dyn OnComponentInitializedHook<Node>>,
pub(crate) on_node_started: Box<dyn OnNodeStartedHook<Node>>,
pub(crate) _marker: std::marker::PhantomData<Node>,
}
impl<Node: FullNodeComponents> NodeHooks<Node> {
/// Creates a new, empty [NodeHooks] instance for the given node type.
pub(crate) fn new() -> Self {
Self {
on_component_initialized: Box::<()>::default(),
on_node_started: Box::<()>::default(),
_marker: Default::default(),
}
}
/// Sets the hook that is run once the node's components are initialized.
pub(crate) fn set_on_component_initialized<F>(&mut self, hook: F) -> &mut Self
where
F: OnComponentInitializedHook<Node> + 'static,
{
self.on_component_initialized = Box::new(hook);
self
}
/// Sets the hook that is run once the node's components are initialized.
#[allow(unused)]
pub(crate) fn on_component_initialized<F>(mut self, hook: F) -> Self
where
F: OnComponentInitializedHook<Node> + 'static,
{
self.set_on_component_initialized(hook);
self
}
/// Sets the hook that is run once the node has started.
pub(crate) fn set_on_node_started<F>(&mut self, hook: F) -> &mut Self
where
F: OnNodeStartedHook<Node> + 'static,
{
self.on_node_started = Box::new(hook);
self
}
/// Sets the hook that is run once the node has started.
#[allow(unused)]
pub(crate) fn on_node_started<F>(mut self, hook: F) -> Self
where
F: OnNodeStartedHook<Node> + 'static,
{
self.set_on_node_started(hook);
self
}
}
impl<Node: FullNodeComponents> Default for NodeHooks<Node> {
fn default() -> Self {
Self::new()
}
}
impl<Node: FullNodeComponents> fmt::Debug for NodeHooks<Node> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NodeHooks")
.field("on_component_initialized", &"...")
.field("on_node_started", &"...")
.finish()
}
}
/// A helper trait for the event hook that is run once the node is initialized.
pub trait OnComponentInitializedHook<Node> {
/// Consumes the event hook and runs it.
///
/// If this returns an error, the node launch will be aborted.
fn on_event(&self, node: Node) -> eyre::Result<()>;
}
impl<Node, F> OnComponentInitializedHook<Node> for F
where
F: Fn(Node) -> eyre::Result<()>,
{
fn on_event(&self, node: Node) -> eyre::Result<()> {
self(node)
}
}
/// A helper trait that is run once the node is started.
pub trait OnNodeStartedHook<Node: FullNodeComponents> {
/// Consumes the event hook and runs it.
///
/// If this returns an error, the node launch will be aborted.
fn on_event(&self, node: FullNode<Node>) -> eyre::Result<()>;
}
impl<Node, F> OnNodeStartedHook<Node> for F
where
Node: FullNodeComponents,
F: Fn(FullNode<Node>) -> eyre::Result<()>,
{
fn on_event(&self, node: FullNode<Node>) -> eyre::Result<()> {
self(node)
}
}
impl<Node> OnComponentInitializedHook<Node> for () {
fn on_event(&self, _node: Node) -> eyre::Result<()> {
Ok(())
}
}
impl<Node: FullNodeComponents> OnNodeStartedHook<Node> for () {
fn on_event(&self, _node: FullNode<Node>) -> eyre::Result<()> {
Ok(())
}
}

View File

@@ -0,0 +1,34 @@
//! Standalone crate for Reth configuration and builder types.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![warn(unused_crate_dependencies)]
/// Node event hooks.
pub mod hooks;
/// Support for configuring the higher level node types.
pub mod node;
/// Support for configuring the components of a node.
pub mod components;
mod builder;
mod handle;
pub mod rpc;
pub mod provider;
pub use builder::*;
pub use handle::NodeHandle;
/// Re-export the core configuration traits.
pub use reth_node_core::cli::config::{
PayloadBuilderConfig, RethNetworkConfig, RethRpcConfig, RethTransactionPoolConfig,
};
pub use reth_node_core::node_config::NodeConfig;

View File

@@ -0,0 +1,110 @@
use crate::{components::FullNodeComponents, rpc::RethRpcServerHandles};
use reth_db::database::Database;
use reth_network::NetworkHandle;
use reth_node_api::{evm::EvmConfig, primitives::NodePrimitives, EngineTypes};
use reth_node_core::{
cli::components::FullProvider,
dirs::{ChainPath, DataDirPath},
node_config::NodeConfig,
};
use reth_payload_builder::PayloadBuilderHandle;
use reth_tasks::TaskExecutor;
use std::marker::PhantomData;
use crate::rpc::RpcRegistry;
/// The type that configures stateless node types, the node's primitive types.
pub trait NodeTypes: Send + Sync + 'static {
/// The node's primitive types.
type Primitives: NodePrimitives;
/// The node's engine types.
type Engine: EngineTypes;
/// The node's evm configuration.
type Evm: EvmConfig;
/// Returns the node's evm config.
fn evm_config(&self) -> Self::Evm;
}
/// A helper type that is downstream of the node types and adds stateful components to the node.
pub trait FullNodeTypes: NodeTypes + 'static {
/// Underlying database type.
type DB: Database + Clone + 'static;
/// The provider type used to interact with the node.
type Provider: FullProvider<Self::DB>;
}
/// An adapter type that adds the builtin provider type to the user configured node types.
#[derive(Debug)]
pub struct FullNodeTypesAdapter<Types, DB, Provider> {
pub(crate) types: Types,
_db: PhantomData<DB>,
_provider: PhantomData<Provider>,
}
impl<Types, DB, Provider> FullNodeTypesAdapter<Types, DB, Provider> {
/// Create a new adapter from the given node types.
pub fn new(types: Types) -> Self {
Self { types, _db: Default::default(), _provider: Default::default() }
}
}
impl<Types, DB, Provider> NodeTypes for FullNodeTypesAdapter<Types, DB, Provider>
where
Types: NodeTypes,
DB: Send + Sync + 'static,
Provider: Send + Sync + 'static,
{
type Primitives = Types::Primitives;
type Engine = Types::Engine;
type Evm = Types::Evm;
fn evm_config(&self) -> Self::Evm {
self.types.evm_config()
}
}
impl<Types, DB, Provider> FullNodeTypes for FullNodeTypesAdapter<Types, DB, Provider>
where
Types: NodeTypes,
Provider: FullProvider<DB>,
DB: Database + Clone + 'static,
{
type DB = DB;
type Provider = Provider;
}
/// The launched node with all components including RPC handlers.
#[derive(Debug)]
pub struct FullNode<Node: FullNodeComponents> {
pub(crate) evm_config: Node::Evm,
pub(crate) pool: Node::Pool,
pub(crate) network: NetworkHandle,
pub(crate) provider: Node::Provider,
pub(crate) payload_builder: PayloadBuilderHandle<Node::Engine>,
pub(crate) executor: TaskExecutor,
pub(crate) rpc_server_handles: RethRpcServerHandles,
pub(crate) rpc_registry: RpcRegistry<Node>,
/// The initial node config.
pub(crate) config: NodeConfig,
/// The data dir of the node.
pub(crate) data_dir: ChainPath<DataDirPath>,
}
impl<Node: FullNodeComponents> Clone for FullNode<Node> {
fn clone(&self) -> Self {
Self {
evm_config: self.evm_config.clone(),
pool: self.pool.clone(),
network: self.network.clone(),
provider: self.provider.clone(),
payload_builder: self.payload_builder.clone(),
executor: self.executor.clone(),
rpc_server_handles: self.rpc_server_handles.clone(),
rpc_registry: self.rpc_registry.clone(),
config: self.config.clone(),
data_dir: self.data_dir.clone(),
}
}
}

View File

@@ -0,0 +1,38 @@
//! Helper provider traits to encapsulate all provider traits for simplicity.
use reth_db::database::Database;
use reth_provider::{
AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader,
DatabaseProviderFactory, EvmEnvProvider, StateProviderFactory,
};
/// Helper trait to unify all provider traits for simplicity.
pub trait FullProvider<DB: Database>:
DatabaseProviderFactory<DB>
+ BlockReaderIdExt
+ AccountReader
+ StateProviderFactory
+ EvmEnvProvider
+ ChainSpecProvider
+ ChangeSetReader
+ CanonStateSubscriptions
+ Clone
+ Unpin
+ 'static
{
}
impl<T, DB: Database> FullProvider<DB> for T where
T: DatabaseProviderFactory<DB>
+ BlockReaderIdExt
+ AccountReader
+ StateProviderFactory
+ EvmEnvProvider
+ ChainSpecProvider
+ ChangeSetReader
+ CanonStateSubscriptions
+ Clone
+ Unpin
+ 'static
{
}

View File

@@ -0,0 +1,301 @@
//! Builder support for rpc components.
use crate::components::FullNodeComponents;
use futures::TryFutureExt;
use reth_network::NetworkHandle;
use reth_node_core::{
cli::config::RethRpcConfig,
node_config::NodeConfig,
rpc::{
api::EngineApiServer,
builder::{
auth::{AuthRpcModule, AuthServerHandle},
RethModuleRegistry, RpcModuleBuilder, RpcServerHandle, TransportRpcModules,
},
},
};
use reth_rpc::JwtSecret;
use reth_tasks::TaskExecutor;
use reth_tracing::tracing::{debug, info};
use std::{
fmt,
ops::{Deref, DerefMut},
};
/// Contains the handles to the spawned RPC servers.
///
/// This can be used to access the endpoints of the servers.
#[derive(Debug, Clone)]
pub struct RethRpcServerHandles {
/// The regular RPC server handle to all configured transports.
pub rpc: RpcServerHandle,
/// The handle to the auth server (engine API)
pub auth: AuthServerHandle,
}
/// Contains hooks that are called during the rpc setup.
pub(crate) struct RpcHooks<Node: FullNodeComponents> {
pub(crate) on_rpc_started: Box<dyn OnRpcStarted<Node>>,
pub(crate) extend_rpc_modules: Box<dyn ExtendRpcModules<Node>>,
}
impl<Node: FullNodeComponents> RpcHooks<Node> {
/// Creates a new, empty [RpcHooks] instance for the given node type.
pub(crate) fn new() -> Self {
Self { on_rpc_started: Box::<()>::default(), extend_rpc_modules: Box::<()>::default() }
}
/// Sets the hook that is run once the rpc server is started.
pub(crate) fn set_on_rpc_started<F>(&mut self, hook: F) -> &mut Self
where
F: OnRpcStarted<Node> + 'static,
{
self.on_rpc_started = Box::new(hook);
self
}
/// Sets the hook that is run once the rpc server is started.
#[allow(unused)]
pub(crate) fn on_rpc_started<F>(mut self, hook: F) -> Self
where
F: OnRpcStarted<Node> + 'static,
{
self.set_on_rpc_started(hook);
self
}
/// Sets the hook that is run to configure the rpc modules.
pub(crate) fn set_extend_rpc_modules<F>(&mut self, hook: F) -> &mut Self
where
F: ExtendRpcModules<Node> + 'static,
{
self.extend_rpc_modules = Box::new(hook);
self
}
/// Sets the hook that is run to configure the rpc modules.
#[allow(unused)]
pub(crate) fn extend_rpc_modules<F>(mut self, hook: F) -> Self
where
F: ExtendRpcModules<Node> + 'static,
{
self.set_extend_rpc_modules(hook);
self
}
}
impl<Node: FullNodeComponents> fmt::Debug for RpcHooks<Node> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RpcHooks")
.field("on_rpc_started", &"...")
.field("extend_rpc_modules", &"...")
.finish()
}
}
/// Event hook that is called once the rpc server is started.
pub trait OnRpcStarted<Node: FullNodeComponents> {
/// The hook that is called once the rpc server is started.
fn on_rpc_started(
&self,
ctx: RpcContext<'_, Node>,
handles: RethRpcServerHandles,
) -> eyre::Result<()>;
}
impl<Node, F> OnRpcStarted<Node> for F
where
F: Fn(RpcContext<'_, Node>, RethRpcServerHandles) -> eyre::Result<()>,
Node: FullNodeComponents,
{
fn on_rpc_started(
&self,
ctx: RpcContext<'_, Node>,
handles: RethRpcServerHandles,
) -> eyre::Result<()> {
self(ctx, handles)
}
}
impl<Node: FullNodeComponents> OnRpcStarted<Node> for () {
fn on_rpc_started(&self, _: RpcContext<'_, Node>, _: RethRpcServerHandles) -> eyre::Result<()> {
Ok(())
}
}
/// Event hook that is called when the rpc server is started.
pub trait ExtendRpcModules<Node: FullNodeComponents> {
/// The hook that is called once the rpc server is started.
fn extend_rpc_modules(&self, ctx: RpcContext<'_, Node>) -> eyre::Result<()>;
}
impl<Node, F> ExtendRpcModules<Node> for F
where
F: Fn(RpcContext<'_, Node>) -> eyre::Result<()>,
Node: FullNodeComponents,
{
fn extend_rpc_modules(&self, ctx: RpcContext<'_, Node>) -> eyre::Result<()> {
self(ctx)
}
}
impl<Node: FullNodeComponents> ExtendRpcModules<Node> for () {
fn extend_rpc_modules(&self, _: RpcContext<'_, Node>) -> eyre::Result<()> {
Ok(())
}
}
/// Helper wrapper type to encapsulate the [RethModuleRegistry] over components trait.
#[derive(Debug)]
pub struct RpcRegistry<Node: FullNodeComponents> {
pub(crate) registry: RethModuleRegistry<
Node::Provider,
Node::Pool,
NetworkHandle,
TaskExecutor,
Node::Provider,
Node::Evm,
>,
}
impl<Node: FullNodeComponents> Deref for RpcRegistry<Node> {
type Target = RethModuleRegistry<
Node::Provider,
Node::Pool,
NetworkHandle,
TaskExecutor,
Node::Provider,
Node::Evm,
>;
fn deref(&self) -> &Self::Target {
&self.registry
}
}
impl<Node: FullNodeComponents> DerefMut for RpcRegistry<Node> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.registry
}
}
impl<Node: FullNodeComponents> Clone for RpcRegistry<Node> {
fn clone(&self) -> Self {
Self { registry: self.registry.clone() }
}
}
/// Helper container to encapsulate [RethModuleRegistry], [TransportRpcModules] and [AuthRpcModule].
///
/// This can be used to access installed modules, or create commonly used handlers like
/// [reth_rpc::EthApi], and ultimately merge additional rpc handler into the configured transport
/// modules [TransportRpcModules] as well as configured authenticated methods [AuthRpcModule].
#[allow(missing_debug_implementations)]
pub struct RpcContext<'a, Node: FullNodeComponents> {
/// The node components.
pub(crate) node: Node,
/// Gives access to the node configuration.
pub(crate) config: &'a NodeConfig,
/// A Helper type the holds instances of the configured modules.
///
/// This provides easy access to rpc handlers, such as [RethModuleRegistry::eth_api].
pub registry: &'a mut RpcRegistry<Node>,
/// Holds installed modules per transport type.
///
/// This can be used to merge additional modules into the configured transports (http, ipc,
/// ws). See [TransportRpcModules::merge_configured]
pub modules: &'a mut TransportRpcModules,
/// Holds jwt authenticated rpc module.
///
/// This can be used to merge additional modules into the configured authenticated methods
pub auth_module: &'a mut AuthRpcModule,
}
impl<'a, Node: FullNodeComponents> RpcContext<'a, Node> {
/// Returns the config of the node.
pub fn config(&self) -> &NodeConfig {
self.config
}
/// Returns a reference to the configured node.
pub fn node(&self) -> &Node {
&self.node
}
}
/// Launch the rpc servers.
pub(crate) async fn launch_rpc_servers<Node, Engine>(
node: Node,
engine_api: Engine,
config: &NodeConfig,
jwt_secret: JwtSecret,
hooks: RpcHooks<Node>,
) -> eyre::Result<(RethRpcServerHandles, RpcRegistry<Node>)>
where
Node: FullNodeComponents + Clone,
Engine: EngineApiServer<Node::Engine>,
{
let RpcHooks { on_rpc_started, extend_rpc_modules } = hooks;
let auth_config = config.rpc.auth_server_config(jwt_secret)?;
let module_config = config.rpc.transport_rpc_module_config();
debug!(target: "reth::cli", http=?module_config.http(), ws=?module_config.ws(), "Using RPC module config");
let (mut modules, mut auth_module, registry) = RpcModuleBuilder::default()
.with_provider(node.provider().clone())
.with_pool(node.pool().clone())
.with_network(node.network().clone())
.with_events(node.provider().clone())
.with_executor(node.task_executor().clone())
.with_evm_config(node.evm_config())
.build_with_auth_server(module_config, engine_api);
let mut registry = RpcRegistry { registry };
let ctx = RpcContext {
node: node.clone(),
config,
registry: &mut registry,
modules: &mut modules,
auth_module: &mut auth_module,
};
extend_rpc_modules.extend_rpc_modules(ctx)?;
let server_config = config.rpc.rpc_server_config();
let launch_rpc = modules.clone().start_server(server_config).map_ok(|handle| {
if let Some(url) = handle.ipc_endpoint() {
info!(target: "reth::cli", url=%url, "RPC IPC server started");
}
if let Some(addr) = handle.http_local_addr() {
info!(target: "reth::cli", url=%addr, "RPC HTTP server started");
}
if let Some(addr) = handle.ws_local_addr() {
info!(target: "reth::cli", url=%addr, "RPC WS server started");
}
handle
});
let launch_auth = auth_module.clone().start_server(auth_config).map_ok(|handle| {
let addr = handle.local_addr();
info!(target: "reth::cli", url=%addr, "RPC auth server started");
handle
});
// launch servers concurrently
let (rpc, auth) = futures::future::try_join(launch_rpc, launch_auth).await?;
let handles = RethRpcServerHandles { rpc, auth };
let ctx = RpcContext {
node,
config,
registry: &mut registry,
modules: &mut modules,
auth_module: &mut auth_module,
};
on_rpc_started.on_rpc_started(ctx, handles.clone())?;
Ok((handles, registry))
}

View File

@@ -5,7 +5,7 @@ use reth_primitives::{TxHash, B256};
use std::path::PathBuf;
/// Parameters for debugging purposes
#[derive(Debug, Args, PartialEq, Default)]
#[derive(Debug, Clone, Args, PartialEq, Default)]
#[clap(next_help_heading = "Debug")]
pub struct DebugArgs {
/// Prompt the downloader to download blocks one at a time.

View File

@@ -11,7 +11,7 @@ use secp256k1::SecretKey;
use std::{net::Ipv4Addr, path::PathBuf, sync::Arc};
/// Parameters for configuring the network more granularity via CLI
#[derive(Debug, Args, PartialEq, Eq)]
#[derive(Debug, Clone, Args, PartialEq, Eq)]
#[clap(next_help_heading = "Networking")]
pub struct NetworkArgs {
/// Disable the discovery service.
@@ -160,7 +160,7 @@ impl Default for NetworkArgs {
}
/// Arguments to setup discovery
#[derive(Debug, Args, PartialEq, Eq)]
#[derive(Debug, Clone, Args, PartialEq, Eq)]
pub struct DiscoveryArgs {
/// Disable the discovery service.
#[arg(short, long, default_value_if("dev", "true", "true"))]

View File

@@ -12,7 +12,7 @@ use reth_primitives::constants::{
use std::{borrow::Cow, ffi::OsStr, time::Duration};
/// Parameters for configuring the Payload Builder
#[derive(Debug, Args, PartialEq)]
#[derive(Debug, Clone, Args, PartialEq)]
#[clap(next_help_heading = "Builder")]
pub struct PayloadBuilderArgs {
/// Block extra data set by the payload builder.

View File

@@ -8,7 +8,7 @@ use reth_primitives::{
use std::sync::Arc;
/// Parameters for pruning and full node
#[derive(Debug, Args, PartialEq, Default)]
#[derive(Debug, Clone, Args, PartialEq, Default)]
#[clap(next_help_heading = "Pruning")]
pub struct PruningArgs {
/// Run full node. Only the most recent [`MINIMUM_PRUNING_DISTANCE`] block states are stored.

View File

@@ -1,7 +1,7 @@
//! clap [Args](clap::Args) for op-reth rollup configuration
/// Parameters for rollup configuration
#[derive(Debug, Default, PartialEq, Eq, clap::Args)]
#[derive(Debug, Clone, Default, PartialEq, Eq, clap::Args)]
#[clap(next_help_heading = "Rollup")]
pub struct RollupArgs {
/// HTTP endpoint for the sequencer mempool

View File

@@ -9,7 +9,7 @@ use reth_transaction_pool::{
TXPOOL_SUBPOOL_MAX_SIZE_MB_DEFAULT, TXPOOL_SUBPOOL_MAX_TXS_DEFAULT,
};
/// Parameters for debugging purposes
#[derive(Debug, Args, PartialEq)]
#[derive(Debug, Clone, Args, PartialEq)]
#[clap(next_help_heading = "TxPool")]
pub struct TxPoolArgs {
/// Max number of transaction in the pending sub-pool.

View File

@@ -26,6 +26,7 @@ pub trait FullProvider<DB: Database>:
+ EvmEnvProvider
+ ChainSpecProvider
+ ChangeSetReader
+ CanonStateSubscriptions
+ Clone
+ Unpin
+ 'static
@@ -40,6 +41,7 @@ impl<T, DB: Database> FullProvider<DB> for T where
+ EvmEnvProvider
+ ChainSpecProvider
+ ChangeSetReader
+ CanonStateSubscriptions
+ Clone
+ Unpin
+ 'static

View File

@@ -13,7 +13,7 @@ use std::{str::FromStr, sync::Arc};
/// A type that represents either a _real_ (represented by a path), or _test_ database, which will
/// use a [TempDatabase].
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum DatabaseBuilder {
/// The real database type, with a specified data dir
Real(MaybePlatformPath<DataDirPath>),

View File

@@ -47,7 +47,7 @@ impl From<DatabaseError> for InitDatabaseError {
/// Write the genesis block if it has not already been written
pub fn init_genesis<DB: Database>(
db: Arc<DB>,
db: DB,
chain: Arc<ChainSpec>,
) -> Result<B256, InitDatabaseError> {
let genesis = chain.genesis();

View File

@@ -136,7 +136,7 @@ pub static PROMETHEUS_RECORDER_HANDLE: Lazy<PrometheusHandle> =
/// let builder = builder.with_rpc(rpc);
/// }
/// ```
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct NodeConfig {
/// The test database
pub database: DatabaseBuilder,
@@ -349,6 +349,11 @@ impl NodeConfig {
}
}
/// Returns pruning configuration.
pub fn prune_config(&self) -> eyre::Result<Option<PruneConfig>> {
self.pruning.prune_config(Arc::clone(&self.chain))
}
/// Returns the max block that the node should run to, looking it up from the network if
/// necessary
pub async fn max_block<Provider, Client>(
@@ -383,7 +388,9 @@ impl NodeConfig {
}
}
/// Build a network and spawn it
/// Create the [NetworkBuilder].
///
/// This only configures it and does not spawn it.
pub async fn build_network<C>(
&self,
config: &Config,

View File

@@ -14,8 +14,24 @@ workspace = true
# reth
reth-primitives.workspace = true
reth-payload-builder.workspace = true
reth-basic-payload-builder.workspace = true
reth-ethereum-payload-builder.workspace = true
reth-rpc-types.workspace = true
reth-node-api.workspace = true
reth-node-builder.workspace = true
reth-tracing.workspace = true
reth-provider.workspace = true
reth-transaction-pool.workspace = true
reth-network.workspace = true
# io
# misc
eyre.workspace = true
serde.workspace = true
[dev-dependencies]
reth-db.workspace = true
[features]
# This is a workaround for reth-cli crate to allow this as mandatory dependency without breaking the build even if unused.
# This makes managing features and testing workspace easier because clippy always builds all members if --workspace is provided
optimism = []

View File

@@ -1,4 +1,4 @@
use reth_node_api::ConfigureEvmEnv;
use reth_node_api::{evm::EvmConfig, ConfigureEvmEnv};
use reth_primitives::{
revm::{config::revm_spec, env::fill_tx_env},
revm_primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv},
@@ -44,6 +44,9 @@ impl ConfigureEvmEnv for EthEvmConfig {
}
}
// TODO
impl EvmConfig for EthEvmConfig {}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -16,3 +16,6 @@ pub use engine::EthEngineTypes;
/// [ConfigureEvmEnv](reth_node_api::ConfigureEvmEnv) trait.
pub mod evm;
pub use evm::EthEvmConfig;
#[cfg(not(feature = "optimism"))]
pub mod node;

View File

@@ -0,0 +1,182 @@
//! Node types config.
//! Ethereum node types
use crate::{EthEngineTypes, EthEvmConfig};
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
use reth_network::NetworkHandle;
use reth_node_builder::{
components::{ComponentsBuilder, NetworkBuilder, PayloadServiceBuilder, PoolBuilder},
node::{FullNodeTypes, NodeTypes},
BuilderContext, PayloadBuilderConfig,
};
use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService};
use reth_provider::CanonStateSubscriptions;
use reth_tracing::tracing::{debug, info};
use reth_transaction_pool::{
blobstore::DiskFileBlobStore, EthTransactionPool, TransactionPool,
TransactionValidationTaskExecutor,
};
/// Type configuration for a regular Ethereum node.
#[derive(Debug, Default, Clone, Copy)]
#[non_exhaustive]
pub struct EthereumNode;
// TODO make this stateful with evm config
impl EthereumNode {
/// Returns a [ComponentsBuilder] configured for a regular Ethereum node.
pub fn components<Node>(
) -> ComponentsBuilder<Node, EthereumPoolBuilder, EthereumPayloadBuilder, EthereumNetwork>
where
Node: FullNodeTypes<Engine = EthEngineTypes>,
{
ComponentsBuilder::default()
.node_types::<Node>()
.pool(EthereumPoolBuilder::default())
.payload(EthereumPayloadBuilder::default())
.network(EthereumNetwork::default())
}
}
impl NodeTypes for EthereumNode {
type Primitives = ();
type Engine = EthEngineTypes;
type Evm = EthEvmConfig;
fn evm_config(&self) -> Self::Evm {
todo!()
}
}
/// A basic ethereum transaction pool.
///
/// This contains various settings that can be configured and take precedence over the node's
/// config.
#[derive(Debug, Default, Clone, Copy)]
#[non_exhaustive]
pub struct EthereumPoolBuilder {
// TODO add options for txpool args
}
impl<Node> PoolBuilder<Node> for EthereumPoolBuilder
where
Node: FullNodeTypes,
{
type Pool = EthTransactionPool<Node::Provider, DiskFileBlobStore>;
fn build_pool(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Pool> {
let data_dir = ctx.data_dir();
let blob_store = DiskFileBlobStore::open(data_dir.blobstore_path(), Default::default())?;
let validator = TransactionValidationTaskExecutor::eth_builder(ctx.chain_spec())
.with_head_timestamp(ctx.head().timestamp)
.kzg_settings(ctx.kzg_settings()?)
.with_additional_tasks(1)
.build_with_tasks(
ctx.provider().clone(),
ctx.task_executor().clone(),
blob_store.clone(),
);
let transaction_pool =
reth_transaction_pool::Pool::eth_pool(validator, blob_store, ctx.pool_config());
info!(target: "reth::cli", "Transaction pool initialized");
let transactions_path = data_dir.txpool_transactions_path();
// spawn txpool maintenance task
{
let pool = transaction_pool.clone();
let chain_events = ctx.provider().canonical_state_stream();
let client = ctx.provider().clone();
let transactions_backup_config =
reth_transaction_pool::maintain::LocalTransactionBackupConfig::with_local_txs_backup(transactions_path);
ctx.task_executor().spawn_critical_with_graceful_shutdown_signal(
"local transactions backup task",
|shutdown| {
reth_transaction_pool::maintain::backup_local_transactions_task(
shutdown,
pool.clone(),
transactions_backup_config,
)
},
);
// spawn the maintenance task
ctx.task_executor().spawn_critical(
"txpool maintenance task",
reth_transaction_pool::maintain::maintain_transaction_pool_future(
client,
pool,
chain_events,
ctx.task_executor().clone(),
Default::default(),
),
);
debug!(target: "reth::cli", "Spawned txpool maintenance task");
}
Ok(transaction_pool)
}
}
/// A basic ethereum payload service.
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct EthereumPayloadBuilder;
impl<Node, Pool> PayloadServiceBuilder<Node, Pool> for EthereumPayloadBuilder
where
Node: FullNodeTypes<Engine = EthEngineTypes>,
Pool: TransactionPool + Unpin + 'static,
{
fn spawn_payload_service(
self,
ctx: &BuilderContext<Node>,
pool: Pool,
) -> eyre::Result<PayloadBuilderHandle<Node::Engine>> {
let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default();
let conf = ctx.payload_builder_config();
let payload_job_config = BasicPayloadJobGeneratorConfig::default()
.interval(conf.interval())
.deadline(conf.deadline())
.max_payload_tasks(conf.max_payload_tasks())
.extradata(conf.extradata_rlp_bytes())
.max_gas_limit(conf.max_gas_limit());
let payload_generator = BasicPayloadJobGenerator::with_builder(
ctx.provider().clone(),
pool,
ctx.task_executor().clone(),
payload_job_config,
ctx.chain_spec(),
payload_builder,
);
let (payload_service, payload_builder) =
PayloadBuilderService::new(payload_generator, ctx.provider().canonical_state_stream());
ctx.task_executor().spawn_critical("payload builder service", Box::pin(payload_service));
Ok(payload_builder)
}
}
/// A basic ethereum payload service.
#[derive(Debug, Default, Clone, Copy)]
pub struct EthereumNetwork {
// TODO add closure to modify network
}
impl<Node, Pool> NetworkBuilder<Node, Pool> for EthereumNetwork
where
Node: FullNodeTypes,
Pool: TransactionPool + Unpin + 'static,
{
fn build_network(self, ctx: &BuilderContext<Node>, pool: Pool) -> eyre::Result<NetworkHandle> {
let network = ctx.network_builder_blocking()?;
let handle = ctx.start_network(network, pool);
Ok(handle)
}
}

View File

@@ -0,0 +1,34 @@
//! Node builder setup tests.
use reth_db::test_utils::create_test_rw_db;
use reth_node_builder::{components::FullNodeComponents, NodeBuilder, NodeConfig};
use reth_node_ethereum::node::EthereumNode;
#[test]
fn test_basic_setup() {
// parse CLI -> config
let config = NodeConfig::test();
let db = create_test_rw_db();
let msg = "On components".to_string();
let _builder = NodeBuilder::new(config)
.with_database(db)
.with_types(EthereumNode::default())
.with_components(EthereumNode::components())
.on_component_initialized(move |ctx| {
let _provider = ctx.provider();
println!("{msg}");
Ok(())
})
.on_node_started(|_full_node| Ok(()))
.on_rpc_started(|_ctx, handles| {
let _client = handles.rpc.http_client();
Ok(())
})
.extend_rpc_modules(|ctx| {
let _ = ctx.config();
let _ = ctx.node().provider();
Ok(())
})
.check_launch();
}

View File

@@ -0,0 +1,4 @@
#[cfg(not(feature = "optimism"))]
mod builder;
fn main() {}

View File

@@ -928,7 +928,7 @@ impl Serialize for RethRpcModule {
}
/// A Helper type the holds instances of the configured modules.
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct RethModuleRegistry<Provider, Pool, Network, Tasks, Events, EvmConfig> {
provider: Provider,
pool: Pool,

View File

@@ -298,6 +298,12 @@ impl TaskExecutor {
&self.on_shutdown
}
/// Runs a future to completion on this Handle's associated Runtime.
#[track_caller]
pub fn block_on<F: Future>(&self, future: F) -> F::Output {
self.handle.block_on(future)
}
/// Spawns a future on the tokio runtime depending on the [TaskKind]
fn spawn_on_rt<F>(&self, fut: F, task_kind: TaskKind) -> JoinHandle<()>
where