feat: make nodetypes stateless and move evm to components (#7992)

This commit is contained in:
Matthias Seitz
2024-04-30 18:56:34 +02:00
committed by GitHub
parent 581682605c
commit d04d9556fa
20 changed files with 402 additions and 171 deletions

View File

@ -27,6 +27,15 @@ pub struct NoopBlockchainTree {
pub canon_state_notification_sender: Option<CanonStateNotificationSender>,
}
impl NoopBlockchainTree {
/// Create a new NoopBlockchainTree with a canon state notification sender.
pub fn with_canon_state_notifications(
canon_state_notification_sender: CanonStateNotificationSender,
) -> Self {
Self { canon_state_notification_sender: Some(canon_state_notification_sender) }
}
}
impl BlockchainTreeEngine for NoopBlockchainTree {
fn buffer_block(&self, _block: SealedBlockWithSenders) -> Result<(), InsertBlockError> {
Ok(())

View File

@ -80,7 +80,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
/// This represents the set of methods used to configure the EVM's environment before block
/// execution.
pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone {
pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static {
/// The type of the transaction metadata that should be used to fill fields in the transaction
/// environment.
///

View File

@ -4,7 +4,9 @@ use crate::{EthEngineTypes, EthEvmConfig};
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
use reth_network::NetworkHandle;
use reth_node_builder::{
components::{ComponentsBuilder, NetworkBuilder, PayloadServiceBuilder, PoolBuilder},
components::{
ComponentsBuilder, ExecutorBuilder, NetworkBuilder, PayloadServiceBuilder, PoolBuilder,
},
node::{FullNodeTypes, NodeTypes},
BuilderContext, Node, PayloadBuilderConfig,
};
@ -23,8 +25,13 @@ pub struct EthereumNode;
impl EthereumNode {
/// Returns a [ComponentsBuilder] configured for a regular Ethereum node.
pub fn components<Node>(
) -> ComponentsBuilder<Node, EthereumPoolBuilder, EthereumPayloadBuilder, EthereumNetworkBuilder>
pub fn components<Node>() -> ComponentsBuilder<
Node,
EthereumPoolBuilder,
EthereumPayloadBuilder,
EthereumNetworkBuilder,
EthereumExecutorBuilder,
>
where
Node: FullNodeTypes<Engine = EthEngineTypes>,
{
@ -33,31 +40,48 @@ impl EthereumNode {
.pool(EthereumPoolBuilder::default())
.payload(EthereumPayloadBuilder::default())
.network(EthereumNetworkBuilder::default())
.executor(EthereumExecutorBuilder::default())
}
}
impl NodeTypes for EthereumNode {
type Primitives = ();
type Engine = EthEngineTypes;
type Evm = EthEvmConfig;
fn evm_config(&self) -> Self::Evm {
EthEvmConfig::default()
}
}
impl<N> Node<N> for EthereumNode
where
N: FullNodeTypes<Engine = EthEngineTypes>,
{
type ComponentsBuilder =
ComponentsBuilder<N, EthereumPoolBuilder, EthereumPayloadBuilder, EthereumNetworkBuilder>;
type ComponentsBuilder = ComponentsBuilder<
N,
EthereumPoolBuilder,
EthereumPayloadBuilder,
EthereumNetworkBuilder,
EthereumExecutorBuilder,
>;
fn components_builder(self) -> Self::ComponentsBuilder {
Self::components()
}
}
/// A regular ethereum evm and executor builder.
#[derive(Debug, Default, Clone, Copy)]
#[non_exhaustive]
pub struct EthereumExecutorBuilder;
impl<Node> ExecutorBuilder<Node> for EthereumExecutorBuilder
where
Node: FullNodeTypes,
{
type EVM = EthEvmConfig;
async fn build_evm(self, _ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
Ok(EthEvmConfig::default())
}
}
/// A basic ethereum transaction pool.
///
/// This contains various settings that can be configured and take precedence over the node's

View File

@ -13,7 +13,7 @@ fn test_basic_setup() {
let msg = "On components".to_string();
let _builder = NodeBuilder::new(config)
.with_database(db)
.with_types(EthereumNode::default())
.with_types::<EthereumNode>()
.with_components(EthereumNode::components())
.on_component_initialized(move |ctx| {
let _provider = ctx.provider();

View File

@ -31,7 +31,7 @@ fn basic_exex() {
let db = create_test_rw_db();
let _builder = NodeBuilder::new(config)
.with_database(db)
.with_types(EthereumNode::default())
.with_types::<EthereumNode>()
.with_components(EthereumNode::components())
.install_exex("dummy", move |ctx| future::ok(DummyExEx { _ctx: ctx }))
.check_launch();

View File

@ -1,4 +1,4 @@
//! Traits for configuring a node
//! Traits for configuring a node.
use crate::{primitives::NodePrimitives, ConfigureEvm, EngineTypes};
use reth_db::{
@ -15,21 +15,20 @@ use std::marker::PhantomData;
/// The type that configures the essential types of an ethereum like node.
///
/// This includes the primitive types of a node, the engine API types for communication with the
/// consensus layer, and the EVM configuration type for setting up the Ethereum Virtual Machine.
/// consensus layer.
///
/// This trait is intended to be stateless and only define the types of the node.
pub trait NodeTypes: Send + Sync + 'static {
/// The node's primitive types, defining basic operations and structures.
type Primitives: NodePrimitives;
/// The node's engine types, defining the interaction with the consensus engine.
type Engine: EngineTypes;
/// The node's EVM configuration, defining settings for the Ethereum Virtual Machine.
type Evm: ConfigureEvm;
/// Returns the node's evm config.
fn evm_config(&self) -> Self::Evm;
}
/// A helper trait that is downstream of the [NodeTypes] trait and adds stateful components to the
/// node.
///
/// Its types are configured by node internally and are not intended to be user configurable.
pub trait FullNodeTypes: NodeTypes + 'static {
/// Underlying database type used by the node to store and retrieve data.
type DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static;
@ -41,7 +40,7 @@ pub trait FullNodeTypes: NodeTypes + 'static {
#[derive(Debug)]
pub struct FullNodeTypesAdapter<Types, DB, Provider> {
/// An instance of the user configured node types.
pub types: Types,
pub types: PhantomData<Types>,
/// The database type used by the node.
pub db: PhantomData<DB>,
/// The provider type used by the node.
@ -49,9 +48,15 @@ pub struct FullNodeTypesAdapter<Types, DB, 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() }
/// Create a new adapter with the configured types.
pub fn new() -> Self {
Self { types: Default::default(), db: Default::default(), provider: Default::default() }
}
}
impl<Types, DB, Provider> Default for FullNodeTypesAdapter<Types, DB, Provider> {
fn default() -> Self {
Self::new()
}
}
@ -63,11 +68,6 @@ where
{
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>
@ -85,9 +85,15 @@ pub trait FullNodeComponents: FullNodeTypes + 'static {
/// The transaction pool of the node.
type Pool: TransactionPool + Unpin;
/// The node's EVM configuration, defining settings for the Ethereum Virtual Machine.
type Evm: ConfigureEvm;
/// Returns the transaction pool of the node.
fn pool(&self) -> &Self::Pool;
/// Returns the node's evm config.
fn evm_config(&self) -> &Self::Evm;
/// Returns the provider of the node.
fn provider(&self) -> &Self::Provider;

View File

@ -50,10 +50,10 @@ pub type RethFullAdapter<DB, Types> = FullNodeTypesAdapter<Types, DB, Blockchain
/// example) 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
/// [ConfigureEvm](reth_node_api::evm::ConfigureEvm), the database [Database] and all the
/// Next all stateful components of the node are configured, these include all the
/// components of the node that are downstream of those types, these include:
///
/// - The EVM and Executor configuration: [ExecutorBuilder](crate::components::ExecutorBuilder)
/// - The transaction pool: [PoolBuilder]
/// - The network: [NetworkBuilder](crate::components::NetworkBuilder)
/// - The payload builder: [PayloadBuilder](crate::components::PayloadServiceBuilder)
@ -71,9 +71,10 @@ pub type RethFullAdapter<DB, Types> = FullNodeTypesAdapter<Types, DB, Blockchain
/// ## Components
///
/// All components are configured with a [NodeComponentsBuilder] that is responsible for actually
/// creating the node components during the launch process. The [ComponentsBuilder] is a general
/// purpose implementation of the [NodeComponentsBuilder] trait that can be used to configure the
/// network, transaction pool and payload builder of the node. It enforces the correct order of
/// creating the node components during the launch process. The
/// [ComponentsBuilder](crate::components::ComponentsBuilder) is a general purpose implementation of
/// the [NodeComponentsBuilder] trait that can be used to configure the executor, network,
/// transaction pool and payload builder of the node. It enforces the correct order of
/// configuration, for example the network and the payload builder depend on the transaction pool
/// type that is configured first.
///
@ -127,8 +128,8 @@ pub type RethFullAdapter<DB, Types> = FullNodeTypesAdapter<Types, DB, Blockchain
/// ### Limitations
///
/// Currently the launch process is limited to ethereum nodes and requires all the components
/// specified above. It also expect beacon consensus with the ethereum engine API that is configured
/// by the builder itself during launch. This might change in the future.
/// specified above. It also expects beacon consensus with the ethereum engine API that is
/// configured by the builder itself during launch. This might change in the future.
///
/// [builder]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
pub struct NodeBuilder<DB> {
@ -187,12 +188,11 @@ where
DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static,
{
/// Configures the types of the node.
pub fn with_types<T>(self, types: T) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
pub fn with_types<T>(self) -> NodeBuilderWithTypes<RethFullAdapter<DB, T>>
where
T: NodeTypes,
{
let types = FullNodeTypesAdapter::new(types);
NodeBuilderWithTypes::new(self.config, types, self.database)
NodeBuilderWithTypes::new(self.config, self.database)
}
/// Preconfigures the node with a specific node implementation.
@ -205,7 +205,7 @@ where
where
N: Node<RethFullAdapter<DB, N>>,
{
self.with_types(node.clone()).with_components(node.components_builder())
self.with_types().with_components(node.components_builder())
}
}
@ -236,15 +236,12 @@ where
DB: Database + DatabaseMetrics + DatabaseMetadata + Clone + Unpin + 'static,
{
/// Configures the types of the node.
pub fn with_types<T>(
self,
types: T,
) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
pub fn with_types<T>(self) -> WithLaunchContext<NodeBuilderWithTypes<RethFullAdapter<DB, T>>>
where
T: NodeTypes,
{
WithLaunchContext {
builder: self.builder.with_types(types),
builder: self.builder.with_types(),
task_executor: self.task_executor,
data_dir: self.data_dir,
}
@ -260,7 +257,7 @@ where
where
N: Node<RethFullAdapter<DB, N>>,
{
self.with_types(node.clone()).with_components(node.components_builder())
self.with_types().with_components(node.components_builder())
}
/// Launches a preconfigured [Node]
@ -428,8 +425,6 @@ pub struct BuilderContext<Node: FullNodeTypes> {
pub(crate) config: NodeConfig,
/// loaded config
pub(crate) reth_config: reth_config::Config,
/// EVM config of the node
pub(crate) evm_config: Node::Evm,
}
impl<Node: FullNodeTypes> BuilderContext<Node> {
@ -441,9 +436,8 @@ impl<Node: FullNodeTypes> BuilderContext<Node> {
data_dir: ChainPath<DataDirPath>,
config: NodeConfig,
reth_config: reth_config::Config,
evm_config: Node::Evm,
) -> Self {
Self { head, provider, executor, data_dir, config, reth_config, evm_config }
Self { head, provider, executor, data_dir, config, reth_config }
}
/// Returns the configured provider to interact with the blockchain.
@ -451,11 +445,6 @@ impl<Node: FullNodeTypes> BuilderContext<Node> {
&self.provider
}
/// Returns the configured evm.
pub fn evm_config(&self) -> &Node::Evm {
&self.evm_config
}
/// Returns the current head of the blockchain at launch.
pub fn head(&self) -> Head {
self.head

View File

@ -31,8 +31,8 @@ pub struct NodeBuilderWithTypes<T: FullNodeTypes> {
impl<T: FullNodeTypes> NodeBuilderWithTypes<T> {
/// Creates a new instance of the node builder with the given configuration and types.
pub fn new(config: NodeConfig, types: T, database: T::DB) -> Self {
Self { config, adapter: NodeTypesAdapter::new(types, database) }
pub 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
@ -59,14 +59,12 @@ impl<T: FullNodeTypes> NodeBuilderWithTypes<T> {
pub(crate) struct NodeTypesAdapter<T: FullNodeTypes> {
/// The database type used by the node.
pub(crate) database: T::DB,
// TODO(mattsse): make this stateless
pub(crate) types: T,
}
impl<T: FullNodeTypes> NodeTypesAdapter<T> {
/// Create a new adapter from the given node types.
pub(crate) fn new(types: T, database: T::DB) -> Self {
Self { types, database }
pub(crate) fn new(database: T::DB) -> Self {
Self { database }
}
}
@ -85,18 +83,11 @@ pub struct NodeAdapter<T: FullNodeTypes, C: NodeComponents<T>> {
pub task_executor: TaskExecutor,
/// The provider of the node.
pub provider: T::Provider,
/// EVM config
pub evm: T::Evm,
}
impl<T: FullNodeTypes, C: NodeComponents<T>> NodeTypes for NodeAdapter<T, C> {
type Primitives = T::Primitives;
type Engine = T::Engine;
type Evm = T::Evm;
fn evm_config(&self) -> Self::Evm {
self.evm.clone()
}
}
impl<T: FullNodeTypes, C: NodeComponents<T>> FullNodeTypes for NodeAdapter<T, C> {
@ -106,11 +97,16 @@ impl<T: FullNodeTypes, C: NodeComponents<T>> FullNodeTypes for NodeAdapter<T, C>
impl<T: FullNodeTypes, C: NodeComponents<T>> FullNodeComponents for NodeAdapter<T, C> {
type Pool = C::Pool;
type Evm = C::Evm;
fn pool(&self) -> &Self::Pool {
self.components.pool()
}
fn evm_config(&self) -> &Self::Evm {
self.components.evm_config()
}
fn provider(&self) -> &Self::Provider {
&self.provider
}
@ -134,7 +130,6 @@ impl<T: FullNodeTypes, C: NodeComponents<T>> Clone for NodeAdapter<T, C> {
components: self.components.clone(),
task_executor: self.task_executor.clone(),
provider: self.provider.clone(),
evm: self.evm.clone(),
}
}
}

View File

@ -1,13 +1,16 @@
//! A generic [NodeComponentsBuilder]
use crate::{
components::{Components, NetworkBuilder, NodeComponents, PayloadServiceBuilder, PoolBuilder},
BuilderContext, FullNodeTypes,
components::{
Components, ExecutorBuilder, NetworkBuilder, NodeComponents, PayloadServiceBuilder,
PoolBuilder,
},
BuilderContext, ConfigureEvm, FullNodeTypes,
};
use reth_transaction_pool::TransactionPool;
use std::{future::Future, marker::PhantomData};
/// A generic, customizable [`NodeComponentsBuilder`].
/// A generic, general purpose and customizable [`NodeComponentsBuilder`] implementation.
///
/// This type is stateful and captures the configuration of the node's components.
///
@ -27,21 +30,31 @@ use std::{future::Future, marker::PhantomData};
/// 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> {
pub struct ComponentsBuilder<Node, PoolB, PayloadB, NetworkB, ExecB> {
pool_builder: PoolB,
payload_builder: PayloadB,
network_builder: NetworkB,
executor_builder: ExecB,
_marker: PhantomData<Node>,
}
impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, NetworkB> {
impl<Node, PoolB, PayloadB, NetworkB, ExecB>
ComponentsBuilder<Node, PoolB, PayloadB, NetworkB, ExecB>
{
/// Configures the node types.
pub fn node_types<Types>(self) -> ComponentsBuilder<Types, PoolB, PayloadB, NetworkB>
pub fn node_types<Types>(self) -> ComponentsBuilder<Types, PoolB, PayloadB, NetworkB, ExecB>
where
Types: FullNodeTypes,
{
let Self { pool_builder, payload_builder, network_builder, _marker } = self;
let Self {
pool_builder,
payload_builder,
network_builder,
executor_builder: evm_builder,
_marker,
} = self;
ComponentsBuilder {
executor_builder: evm_builder,
pool_builder,
payload_builder,
network_builder,
@ -55,6 +68,7 @@ impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, N
pool_builder: f(self.pool_builder),
payload_builder: self.payload_builder,
network_builder: self.network_builder,
executor_builder: self.executor_builder,
_marker: self._marker,
}
}
@ -65,6 +79,7 @@ impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, N
pool_builder: self.pool_builder,
payload_builder: f(self.payload_builder),
network_builder: self.network_builder,
executor_builder: self.executor_builder,
_marker: self._marker,
}
}
@ -75,12 +90,25 @@ impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, N
pool_builder: self.pool_builder,
payload_builder: self.payload_builder,
network_builder: f(self.network_builder),
executor_builder: self.executor_builder,
_marker: self._marker,
}
}
/// Apply a function to the executor builder.
pub fn map_executor(self, f: impl FnOnce(ExecB) -> ExecB) -> Self {
Self {
pool_builder: self.pool_builder,
payload_builder: self.payload_builder,
network_builder: self.network_builder,
executor_builder: f(self.executor_builder),
_marker: self._marker,
}
}
}
impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, NetworkB>
impl<Node, PoolB, PayloadB, NetworkB, ExecB>
ComponentsBuilder<Node, PoolB, PayloadB, NetworkB, ExecB>
where
Node: FullNodeTypes,
{
@ -88,16 +116,32 @@ where
///
/// This accepts a [PoolBuilder] instance that will be used to create the node's transaction
/// pool.
pub fn pool<PB>(self, pool_builder: PB) -> ComponentsBuilder<Node, PB, PayloadB, NetworkB>
pub fn pool<PB>(
self,
pool_builder: PB,
) -> ComponentsBuilder<Node, PB, PayloadB, NetworkB, ExecB>
where
PB: PoolBuilder<Node>,
{
let Self { pool_builder: _, payload_builder, network_builder, _marker } = self;
ComponentsBuilder { pool_builder, payload_builder, network_builder, _marker }
let Self {
pool_builder: _,
payload_builder,
network_builder,
executor_builder: evm_builder,
_marker,
} = self;
ComponentsBuilder {
pool_builder,
payload_builder,
network_builder,
executor_builder: evm_builder,
_marker,
}
}
}
impl<Node, PoolB, PayloadB, NetworkB> ComponentsBuilder<Node, PoolB, PayloadB, NetworkB>
impl<Node, PoolB, PayloadB, NetworkB, ExecB>
ComponentsBuilder<Node, PoolB, PayloadB, NetworkB, ExecB>
where
Node: FullNodeTypes,
PoolB: PoolBuilder<Node>,
@ -106,57 +150,118 @@ where
///
/// This accepts a [NetworkBuilder] instance that will be used to create the node's network
/// stack.
pub fn network<NB>(self, network_builder: NB) -> ComponentsBuilder<Node, PoolB, PayloadB, NB>
pub fn network<NB>(
self,
network_builder: NB,
) -> ComponentsBuilder<Node, PoolB, PayloadB, NB, ExecB>
where
NB: NetworkBuilder<Node, PoolB::Pool>,
{
let Self { pool_builder, payload_builder, network_builder: _, _marker } = self;
ComponentsBuilder { pool_builder, payload_builder, network_builder, _marker }
let Self {
pool_builder,
payload_builder,
network_builder: _,
executor_builder: evm_builder,
_marker,
} = self;
ComponentsBuilder {
pool_builder,
payload_builder,
network_builder,
executor_builder: evm_builder,
_marker,
}
}
/// Configures the payload builder.
///
/// This accepts a [PayloadServiceBuilder] instance that will be used to create the node's
/// payload builder service.
pub fn payload<PB>(self, payload_builder: PB) -> ComponentsBuilder<Node, PoolB, PB, NetworkB>
pub fn payload<PB>(
self,
payload_builder: PB,
) -> ComponentsBuilder<Node, PoolB, PB, NetworkB, ExecB>
where
PB: PayloadServiceBuilder<Node, PoolB::Pool>,
{
let Self { pool_builder, payload_builder: _, network_builder, _marker } = self;
ComponentsBuilder { pool_builder, payload_builder, network_builder, _marker }
let Self {
pool_builder,
payload_builder: _,
network_builder,
executor_builder: evm_builder,
_marker,
} = self;
ComponentsBuilder {
pool_builder,
payload_builder,
network_builder,
executor_builder: evm_builder,
_marker,
}
}
/// Configures the executor builder.
///
/// This accepts a [ExecutorBuilder] instance that will be used to create the node's components
/// for execution.
pub fn executor<EB>(
self,
executor_builder: EB,
) -> ComponentsBuilder<Node, PoolB, PayloadB, NetworkB, EB>
where
EB: ExecutorBuilder<Node>,
{
let Self { pool_builder, payload_builder, network_builder, executor_builder: _, _marker } =
self;
ComponentsBuilder {
pool_builder,
payload_builder,
network_builder,
executor_builder,
_marker,
}
}
}
impl<Node, PoolB, PayloadB, NetworkB> NodeComponentsBuilder<Node>
for ComponentsBuilder<Node, PoolB, PayloadB, NetworkB>
impl<Node, PoolB, PayloadB, NetworkB, ExecB> NodeComponentsBuilder<Node>
for ComponentsBuilder<Node, PoolB, PayloadB, NetworkB, ExecB>
where
Node: FullNodeTypes,
PoolB: PoolBuilder<Node>,
NetworkB: NetworkBuilder<Node, PoolB::Pool>,
PayloadB: PayloadServiceBuilder<Node, PoolB::Pool>,
ExecB: ExecutorBuilder<Node>,
{
type Components = Components<Node, PoolB::Pool>;
type Components = Components<Node, PoolB::Pool, ExecB::EVM>;
async fn build_components(
self,
context: &BuilderContext<Node>,
) -> eyre::Result<Self::Components> {
let Self { pool_builder, payload_builder, network_builder, _marker } = self;
let Self {
pool_builder,
payload_builder,
network_builder,
executor_builder: evm_builder,
_marker,
} = self;
let evm_config = evm_builder.build_evm(context).await?;
let pool = pool_builder.build_pool(context).await?;
let network = network_builder.build_network(context, pool.clone()).await?;
let payload_builder = payload_builder.spawn_payload_service(context, pool.clone()).await?;
Ok(Components { transaction_pool: pool, network, payload_builder })
Ok(Components { transaction_pool: pool, evm_config, network, payload_builder })
}
}
impl Default for ComponentsBuilder<(), (), (), ()> {
impl Default for ComponentsBuilder<(), (), (), (), ()> {
fn default() -> Self {
Self {
pool_builder: (),
payload_builder: (),
network_builder: (),
executor_builder: (),
_marker: Default::default(),
}
}
@ -167,9 +272,9 @@ impl Default for ComponentsBuilder<(), (), (), ()> {
/// Implementers of this trait are responsible for building all the components of the node: See
/// [NodeComponents].
///
/// The [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.
/// The [ComponentsBuilder] is a generic, general purpose 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.
/// A type that's responsible for building the components of the node.
pub trait NodeComponentsBuilder<Node: FullNodeTypes>: Send {
/// The components for the node with the given types
@ -182,14 +287,15 @@ pub trait NodeComponentsBuilder<Node: FullNodeTypes>: Send {
) -> impl Future<Output = eyre::Result<Self::Components>> + Send;
}
impl<Node, F, Fut, Pool> NodeComponentsBuilder<Node> for F
impl<Node, F, Fut, Pool, EVM> NodeComponentsBuilder<Node> for F
where
Node: FullNodeTypes,
F: FnOnce(&BuilderContext<Node>) -> Fut + Send,
Fut: Future<Output = eyre::Result<Components<Node, Pool>>> + Send,
Fut: Future<Output = eyre::Result<Components<Node, Pool, EVM>>> + Send,
Pool: TransactionPool + Unpin + 'static,
EVM: ConfigureEvm,
{
type Components = Components<Node, Pool>;
type Components = Components<Node, Pool, EVM>;
fn build_components(
self,

View File

@ -0,0 +1,34 @@
//! EVM component for the node builder.
use crate::{BuilderContext, FullNodeTypes};
use reth_node_api::ConfigureEvm;
use std::future::Future;
/// A type that knows how to build the executor types.
pub trait ExecutorBuilder<Node: FullNodeTypes>: Send {
/// The EVM config to build.
type EVM: ConfigureEvm;
// TODO(mattsse): integrate `Executor`
/// Creates the transaction pool.
fn build_evm(
self,
ctx: &BuilderContext<Node>,
) -> impl Future<Output = eyre::Result<Self::EVM>> + Send;
}
impl<Node, F, Fut, EVM> ExecutorBuilder<Node> for F
where
Node: FullNodeTypes,
EVM: ConfigureEvm,
F: FnOnce(&BuilderContext<Node>) -> Fut + Send,
Fut: Future<Output = eyre::Result<EVM>> + Send,
{
type EVM = EVM;
fn build_evm(
self,
ctx: &BuilderContext<Node>,
) -> impl Future<Output = eyre::Result<Self::EVM>> {
self(ctx)
}
}

View File

@ -7,8 +7,9 @@
//!
//! Components depend on a fully type configured node: [FullNodeTypes](crate::node::FullNodeTypes).
use crate::FullNodeTypes;
use crate::{ConfigureEvm, FullNodeTypes};
pub use builder::*;
pub use execute::*;
pub use network::*;
pub use payload::*;
pub use pool::*;
@ -17,11 +18,13 @@ use reth_payload_builder::PayloadBuilderHandle;
use reth_transaction_pool::TransactionPool;
mod builder;
mod execute;
mod network;
mod payload;
mod pool;
/// An abstraction over the components of a node, consisting of:
/// - evm and executor
/// - transaction pool
/// - network
/// - payload builder.
@ -29,9 +32,15 @@ pub trait NodeComponents<NodeTypes: FullNodeTypes>: Clone + Send + Sync + 'stati
/// The transaction pool of the node.
type Pool: TransactionPool + Unpin;
/// The node's EVM configuration, defining settings for the Ethereum Virtual Machine.
type Evm: ConfigureEvm;
/// Returns the transaction pool of the node.
fn pool(&self) -> &Self::Pool;
/// Returns the node's evm config.
fn evm_config(&self) -> &Self::Evm;
/// Returns the handle to the network
fn network(&self) -> &NetworkHandle;
@ -43,26 +52,34 @@ pub trait NodeComponents<NodeTypes: FullNodeTypes>: Clone + Send + Sync + 'stati
///
/// This provides access to all the components of the node.
#[derive(Debug)]
pub struct Components<Node: FullNodeTypes, Pool> {
pub struct Components<Node: FullNodeTypes, Pool, EVM> {
/// The transaction pool of the node.
pub transaction_pool: Pool,
/// The node's EVM configuration, defining settings for the Ethereum Virtual Machine.
pub evm_config: EVM,
/// The network implementation of the node.
pub network: NetworkHandle,
/// The handle to the payload builder service.
pub payload_builder: PayloadBuilderHandle<Node::Engine>,
}
impl<Node, Pool> NodeComponents<Node> for Components<Node, Pool>
impl<Node, Pool, EVM> NodeComponents<Node> for Components<Node, Pool, EVM>
where
Node: FullNodeTypes,
Pool: TransactionPool + Unpin + 'static,
EVM: ConfigureEvm,
{
type Pool = Pool;
type Evm = EVM;
fn pool(&self) -> &Self::Pool {
&self.transaction_pool
}
fn evm_config(&self) -> &Self::Evm {
&self.evm_config
}
fn network(&self) -> &NetworkHandle {
&self.network
}
@ -72,14 +89,16 @@ where
}
}
impl<Node, Pool> Clone for Components<Node, Pool>
impl<Node, Pool, EVM> Clone for Components<Node, Pool, EVM>
where
Node: FullNodeTypes,
Pool: TransactionPool,
EVM: ConfigureEvm,
{
fn clone(&self) -> Self {
Self {
transaction_pool: self.transaction_pool.clone(),
evm_config: self.evm_config.clone(),
network: self.network.clone(),
payload_builder: self.payload_builder.clone(),
}

View File

@ -40,6 +40,7 @@ use tokio::sync::{mpsc::unbounded_channel, oneshot};
pub mod common;
pub use common::LaunchContext;
use reth_blockchain_tree::noop::NoopBlockchainTree;
/// A general purpose trait that launches a new node of any kind.
///
@ -83,7 +84,7 @@ where
) -> eyre::Result<Self::Node> {
let Self { ctx } = self;
let NodeBuilderWithComponents {
adapter: NodeTypesAdapter { types, database },
adapter: NodeTypesAdapter { database },
components_builder,
add_ons: NodeAddOns { hooks, rpc, exexs: installed_exex },
config,
@ -124,27 +125,22 @@ where
let sync_metrics_listener = reth_stages::MetricsListener::new(sync_metrics_rx);
ctx.task_executor().spawn_critical("stages metrics listener task", sync_metrics_listener);
// Configure the blockchain tree for the node
let evm_config = types.evm_config();
let tree_config = BlockchainTreeConfig::default();
let tree_externals = TreeExternals::new(
ctx.provider_factory().clone(),
consensus.clone(),
EvmProcessorFactory::new(ctx.chain_spec(), evm_config.clone()),
);
let tree = BlockchainTree::new(tree_externals, tree_config, ctx.prune_modes())?
.with_sync_metrics_tx(sync_metrics_tx.clone());
let canon_state_notification_sender = tree.canon_state_notification_sender();
let blockchain_tree = Arc::new(ShareableBlockchainTree::new(tree));
debug!(target: "reth::cli", "configured blockchain tree");
// fetch the head block from the database
let head = ctx.lookup_head()?;
// setup the blockchain provider
let blockchain_db =
BlockchainProvider::new(ctx.provider_factory().clone(), blockchain_tree.clone())?;
// Configure the blockchain tree for the node
let tree_config = BlockchainTreeConfig::default();
// NOTE: This is a temporary workaround to provide the canon state notification sender to the components builder because there's a cyclic dependency between the blockchain provider and the tree component. This will be removed once the Blockchain provider no longer depends on an instance of the tree: <https://github.com/paradigmxyz/reth/issues/7154>
let (canon_state_notification_sender, _receiver) =
tokio::sync::broadcast::channel(tree_config.max_reorg_depth() as usize * 2);
let blockchain_db = BlockchainProvider::new(
ctx.provider_factory().clone(),
Arc::new(NoopBlockchainTree::with_canon_state_notifications(
canon_state_notification_sender.clone(),
)),
)?;
let builder_ctx = BuilderContext::new(
head,
@ -153,19 +149,37 @@ where
ctx.data_dir().clone(),
ctx.node_config().clone(),
ctx.toml_config().clone(),
evm_config.clone(),
);
debug!(target: "reth::cli", "creating components");
let components = components_builder.build_components(&builder_ctx).await?;
let tree_externals = TreeExternals::new(
ctx.provider_factory().clone(),
consensus.clone(),
EvmProcessorFactory::new(ctx.chain_spec(), components.evm_config().clone()),
);
let tree = BlockchainTree::new(tree_externals, tree_config, ctx.prune_modes())?
.with_sync_metrics_tx(sync_metrics_tx.clone())
// Note: This is required because we need to ensure that both the components and the
// tree are using the same channel for canon state notifications. This will be removed
// once the Blockchain provider no longer depends on an instance of the tree
.with_canon_state_notification_sender(canon_state_notification_sender);
let canon_state_notification_sender = tree.canon_state_notification_sender();
let blockchain_tree = Arc::new(ShareableBlockchainTree::new(tree));
// Replace the tree component with the actual tree
let blockchain_db = blockchain_db.with_tree(blockchain_tree);
debug!(target: "reth::cli", "configured blockchain tree");
let NodeHooks { on_component_initialized, on_node_started, .. } = hooks;
let node_adapter = NodeAdapter {
components,
task_executor: ctx.task_executor().clone(),
provider: blockchain_db.clone(),
evm: evm_config.clone(),
};
debug!(target: "reth::cli", "calling on_component_initialized hook");
@ -225,7 +239,7 @@ where
});
// send notifications from the blockchain tree to exex manager
let mut canon_state_notifications = blockchain_tree.subscribe_to_canonical_state();
let mut canon_state_notifications = blockchain_db.subscribe_to_canonical_state();
let mut handle = exex_manager_handle.clone();
ctx.task_executor().spawn_critical(
"exex manager blockchain tree notifications",
@ -305,7 +319,7 @@ address.to_string(), format_ether(alloc.balance));
consensus_engine_tx.clone(),
canon_state_notification_sender,
mining_mode,
evm_config.clone(),
node_adapter.components.evm_config().clone(),
)
.build();
@ -320,7 +334,7 @@ address.to_string(), format_ether(alloc.balance));
ctx.prune_config(),
max_block,
static_file_producer,
evm_config,
node_adapter.components.evm_config().clone(),
pipeline_exex_handle,
)
.await?;
@ -343,7 +357,7 @@ address.to_string(), format_ether(alloc.balance));
ctx.prune_config(),
max_block,
static_file_producer,
evm_config,
node_adapter.components.evm_config().clone(),
pipeline_exex_handle,
)
.await?;
@ -447,7 +461,7 @@ address.to_string(), format_ether(alloc.balance));
});
let full_node = FullNode {
evm_config: node_adapter.evm.clone(),
evm_config: node_adapter.components.evm_config().clone(),
pool: node_adapter.components.pool().clone(),
network: node_adapter.components.network().clone(),
provider: node_adapter.provider.clone(),

View File

@ -274,7 +274,7 @@ where
.with_network(node.network().clone())
.with_events(node.provider().clone())
.with_executor(node.task_executor().clone())
.with_evm_config(node.evm_config())
.with_evm_config(node.evm_config().clone())
.build_with_auth_server(module_config, engine_api);
let mut registry = RpcRegistry { registry };

View File

@ -6,10 +6,13 @@ use crate::{
OptimismEngineTypes,
};
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
use reth_evm::ConfigureEvm;
use reth_evm_optimism::OptimismEvmConfig;
use reth_network::{NetworkHandle, NetworkManager};
use reth_node_builder::{
components::{ComponentsBuilder, NetworkBuilder, PayloadServiceBuilder, PoolBuilder},
components::{
ComponentsBuilder, ExecutorBuilder, NetworkBuilder, PayloadServiceBuilder, PoolBuilder,
},
node::{FullNodeTypes, NodeTypes},
BuilderContext, Node, PayloadBuilderConfig,
};
@ -38,7 +41,13 @@ impl OptimismNode {
/// Returns the components for the given [RollupArgs].
pub fn components<Node>(
args: RollupArgs,
) -> ComponentsBuilder<Node, OptimismPoolBuilder, OptimismPayloadBuilder, OptimismNetworkBuilder>
) -> ComponentsBuilder<
Node,
OptimismPoolBuilder,
OptimismPayloadBuilder,
OptimismNetworkBuilder,
OptimismExecutorBuilder,
>
where
Node: FullNodeTypes<Engine = OptimismEngineTypes>,
{
@ -46,8 +55,12 @@ impl OptimismNode {
ComponentsBuilder::default()
.node_types::<Node>()
.pool(OptimismPoolBuilder::default())
.payload(OptimismPayloadBuilder::new(compute_pending_block))
.payload(OptimismPayloadBuilder::new(
compute_pending_block,
OptimismEvmConfig::default(),
))
.network(OptimismNetworkBuilder { disable_txpool_gossip })
.executor(OptimismExecutorBuilder::default())
}
}
@ -55,8 +68,13 @@ impl<N> Node<N> for OptimismNode
where
N: FullNodeTypes<Engine = OptimismEngineTypes>,
{
type ComponentsBuilder =
ComponentsBuilder<N, OptimismPoolBuilder, OptimismPayloadBuilder, OptimismNetworkBuilder>;
type ComponentsBuilder = ComponentsBuilder<
N,
OptimismPoolBuilder,
OptimismPayloadBuilder,
OptimismNetworkBuilder,
OptimismExecutorBuilder,
>;
fn components_builder(self) -> Self::ComponentsBuilder {
let Self { args } = self;
@ -67,10 +85,21 @@ where
impl NodeTypes for OptimismNode {
type Primitives = ();
type Engine = OptimismEngineTypes;
type Evm = OptimismEvmConfig;
}
fn evm_config(&self) -> Self::Evm {
OptimismEvmConfig::default()
/// A regular optimism evm and executor builder.
#[derive(Debug, Default, Clone, Copy)]
#[non_exhaustive]
pub struct OptimismExecutorBuilder;
impl<Node> ExecutorBuilder<Node> for OptimismExecutorBuilder
where
Node: FullNodeTypes,
{
type EVM = OptimismEvmConfig;
async fn build_evm(self, _ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
Ok(OptimismEvmConfig::default())
}
}
@ -151,7 +180,7 @@ where
/// A basic optimism payload service builder
#[derive(Debug, Default, Clone)]
pub struct OptimismPayloadBuilder {
pub struct OptimismPayloadBuilder<EVM = OptimismEvmConfig> {
/// By default the pending block equals the latest block
/// to save resources and not leak txs from the tx-pool,
/// this flag enables computing of the pending block
@ -161,19 +190,22 @@ pub struct OptimismPayloadBuilder {
/// will use the payload attributes from the latest block. Note
/// that this flag is not yet functional.
pub compute_pending_block: bool,
/// The EVM configuration to use for the payload builder.
pub evm_config: EVM,
}
impl OptimismPayloadBuilder {
/// Create a new instance with the given `compute_pending_block` flag.
pub const fn new(compute_pending_block: bool) -> Self {
Self { compute_pending_block }
impl<EVM> OptimismPayloadBuilder<EVM> {
/// Create a new instance with the given `compute_pending_block` flag and evm config.
pub const fn new(compute_pending_block: bool, evm_config: EVM) -> Self {
Self { compute_pending_block, evm_config }
}
}
impl<Node, Pool> PayloadServiceBuilder<Node, Pool> for OptimismPayloadBuilder
impl<Node, EVM, Pool> PayloadServiceBuilder<Node, Pool> for OptimismPayloadBuilder<EVM>
where
Node: FullNodeTypes<Engine = OptimismEngineTypes>,
Pool: TransactionPool + Unpin + 'static,
EVM: ConfigureEvm,
{
async fn spawn_payload_service(
self,
@ -182,7 +214,7 @@ where
) -> eyre::Result<PayloadBuilderHandle<Node::Engine>> {
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::new(
ctx.chain_spec(),
ctx.evm_config().clone(),
self.evm_config,
)
.set_compute_pending_block(self.compute_pending_block);
let conf = ctx.payload_builder_config();

View File

@ -12,7 +12,7 @@ fn test_basic_setup() {
let db = create_test_rw_db();
let _builder = NodeBuilder::new(config)
.with_database(db)
.with_types(OptimismNode::default())
.with_types::<OptimismNode>()
.with_components(OptimismNode::components(Default::default()))
.on_component_initialized(move |ctx| {
let _provider = ctx.provider();

View File

@ -89,6 +89,13 @@ impl<DB> BlockchainProvider<DB> {
) -> Self {
Self { database, tree, chain_info: ChainInfoTracker::new(latest) }
}
/// Sets the treeviewer for the provider.
#[doc(hidden)]
pub fn with_tree(mut self, tree: Arc<dyn TreeViewer>) -> Self {
self.tree = tree;
self
}
}
impl<DB> BlockchainProvider<DB>