feat: make payload builder generic over attributes type (#5948)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Dan Cline
2024-01-10 05:21:43 -05:00
committed by GitHub
parent 42326fd2a5
commit cb96fe6d09
50 changed files with 1912 additions and 999 deletions

View File

@@ -54,6 +54,8 @@ reth-prune.workspace = true
reth-snapshot = { workspace = true, features = ["clap"] }
reth-trie.workspace = true
reth-nippy-jar.workspace = true
reth-node-api.workspace = true
reth-node-builder.workspace = true
# crypto
alloy-rlp.workspace = true

View File

@@ -18,6 +18,7 @@ use clap::{
};
use futures::TryFutureExt;
use reth_network_api::{NetworkInfo, Peers};
use reth_node_api::EngineTypes;
use reth_provider::{
AccountReader, BlockReaderIdExt, CanonStateSubscriptions, ChainSpecProvider, ChangeSetReader,
EvmEnvProvider, HeaderProvider, StateProviderFactory,
@@ -224,7 +225,7 @@ impl RpcServerArgs {
/// Returns the handles for the launched regular RPC server(s) (if any) and the server handle
/// for the auth server that handles the `engine_` API that's accessed by the consensus
/// layer.
pub async fn start_servers<Reth, Engine, Conf>(
pub async fn start_servers<Reth, Engine, Conf, EngineT: EngineTypes>(
&self,
components: &Reth,
engine_api: Engine,
@@ -233,7 +234,7 @@ impl RpcServerArgs {
) -> eyre::Result<RethRpcServerHandles>
where
Reth: RethNodeComponents,
Engine: EngineApiServer,
Engine: EngineApiServer<EngineT>,
Conf: RethNodeCommandConfig,
{
let auth_config = self.auth_server_config(jwt_secret)?;
@@ -322,13 +323,13 @@ impl RpcServerArgs {
}
/// Create Engine API server.
pub async fn start_auth_server<Provider, Pool, Network, Tasks>(
pub async fn start_auth_server<Provider, Pool, Network, Tasks, EngineT>(
&self,
provider: Provider,
pool: Pool,
network: Network,
executor: Tasks,
engine_api: EngineApi<Provider>,
engine_api: EngineApi<Provider, EngineT>,
jwt_secret: JwtSecret,
) -> Result<AuthServerHandle, RpcError>
where
@@ -343,6 +344,7 @@ impl RpcServerArgs {
Pool: TransactionPool + Clone + 'static,
Network: NetworkInfo + Peers + Clone + 'static,
Tasks: TaskSpawner + Clone + 'static,
EngineT: EngineTypes + 'static,
{
let socket_address = SocketAddr::new(self.auth_addr, self.auth_port);

View File

@@ -59,6 +59,11 @@ use reth_interfaces::{
};
use reth_network::{NetworkBuilder, NetworkConfig, NetworkEvents, NetworkHandle, NetworkManager};
use reth_network_api::{NetworkInfo, PeersInfo};
#[cfg(not(feature = "optimism"))]
use reth_node_builder::EthEngineTypes;
#[cfg(feature = "optimism")]
use reth_node_builder::OptimismEngineTypes;
use reth_payload_builder::PayloadBuilderHandle;
use reth_primitives::{
constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP},
kzg::KzgSettings,
@@ -1109,8 +1114,24 @@ impl<DB: Database + DatabaseMetrics + DatabaseMetadata + 'static> NodeBuilderWit
ext.on_components_initialized(&components)?;
debug!(target: "reth::cli", "Spawning payload builder service");
let payload_builder =
ext.spawn_payload_builder_service(&self.config.builder, &components)?;
// TODO: stateful node builder should handle this in with_payload_builder
// Optimism's payload builder is implemented on the OptimismPayloadBuilder type.
#[cfg(feature = "optimism")]
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default()
.set_compute_pending_block(self.config.builder.compute_pending_block);
#[cfg(feature = "optimism")]
let payload_builder: PayloadBuilderHandle<OptimismEngineTypes> =
ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?;
// The default payload builder is implemented on the unit type.
#[cfg(not(feature = "optimism"))]
let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default();
#[cfg(not(feature = "optimism"))]
let payload_builder: PayloadBuilderHandle<EthEngineTypes> =
ext.spawn_payload_builder_service(&self.config.builder, &components, payload_builder)?;
let (consensus_engine_tx, mut consensus_engine_rx) = unbounded_channel();
if let Some(store_path) = self.config.debug.engine_api_store.clone() {
@@ -1279,7 +1300,7 @@ impl<DB: Database + DatabaseMetrics + DatabaseMetadata + 'static> NodeBuilderWit
#[cfg(feature = "optimism")]
if self.config.chain.is_optimism() && !self.config.rollup.enable_genesis_walkback {
let client = rpc_server_handles.auth.http_client();
reth_rpc_api::EngineApiClient::fork_choice_updated_v2(
reth_rpc_api::EngineApiClient::<OptimismEngineTypes>::fork_choice_updated_v2(
&client,
reth_rpc_types::engine::ForkchoiceState {
head_block_hash: head.hash,

View File

@@ -5,7 +5,10 @@ use crate::cli::{
config::{PayloadBuilderConfig, RethNetworkConfig, RethRpcConfig},
};
use clap::Args;
use reth_basic_payload_builder::{BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig};
use reth_basic_payload_builder::{
BasicPayloadJobGenerator, BasicPayloadJobGeneratorConfig, PayloadBuilder,
};
use reth_node_api::EngineTypes;
use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService};
use reth_provider::CanonStateSubscriptions;
use reth_tasks::TaskSpawner;
@@ -125,14 +128,22 @@ pub trait RethNodeCommandConfig: fmt::Debug {
///
/// By default this spawns a [BasicPayloadJobGenerator] with the default configuration
/// [BasicPayloadJobGeneratorConfig].
fn spawn_payload_builder_service<Conf, Reth>(
fn spawn_payload_builder_service<Conf, Reth, Builder, Engine>(
&mut self,
conf: &Conf,
components: &Reth,
) -> eyre::Result<PayloadBuilderHandle>
payload_builder: Builder,
) -> eyre::Result<PayloadBuilderHandle<Engine>>
where
Conf: PayloadBuilderConfig,
Reth: RethNodeComponents,
Engine: EngineTypes + 'static,
Builder: PayloadBuilder<
Reth::Pool,
Reth::Provider,
Attributes = Engine::PayloadBuilderAttributes,
> + Unpin
+ 'static,
{
let payload_job_config = BasicPayloadJobGeneratorConfig::default()
.interval(conf.interval())
@@ -145,15 +156,6 @@ pub trait RethNodeCommandConfig: fmt::Debug {
#[cfg(feature = "optimism")]
let payload_job_config = payload_job_config.extradata(Default::default());
// The default payload builder is implemented on the unit type.
#[cfg(not(feature = "optimism"))]
let payload_builder = reth_ethereum_payload_builder::EthereumPayloadBuilder::default();
// Optimism's payload builder is implemented on the OptimismPayloadBuilder type.
#[cfg(feature = "optimism")]
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default()
.set_compute_pending_block(conf.compute_pending_block());
let payload_generator = BasicPayloadJobGenerator::with_builder(
components.provider(),
components.pool(),
@@ -315,18 +317,26 @@ impl<T: RethNodeCommandConfig> RethNodeCommandConfig for NoArgs<T> {
}
}
fn spawn_payload_builder_service<Conf, Reth>(
fn spawn_payload_builder_service<Conf, Reth, Builder, Engine>(
&mut self,
conf: &Conf,
components: &Reth,
) -> eyre::Result<PayloadBuilderHandle>
payload_builder: Builder,
) -> eyre::Result<PayloadBuilderHandle<Engine>>
where
Conf: PayloadBuilderConfig,
Reth: RethNodeComponents,
Engine: EngineTypes + 'static,
Builder: PayloadBuilder<
Reth::Pool,
Reth::Provider,
Attributes = Engine::PayloadBuilderAttributes,
> + Unpin
+ 'static,
{
self.inner_mut()
.ok_or_else(|| eyre::eyre!("config value must be set"))?
.spawn_payload_builder_service(conf, components)
.spawn_payload_builder_service(conf, components, payload_builder)
}
}

View File

@@ -20,7 +20,10 @@ use reth_blockchain_tree::{
};
use reth_db::{init_db, DatabaseEnv};
use reth_interfaces::{consensus::Consensus, RethResult};
use reth_payload_builder::{database::CachedReads, PayloadBuilderAttributes};
use reth_node_api::PayloadBuilderAttributes;
use reth_payload_builder::database::CachedReads;
#[cfg(feature = "optimism")]
use reth_payload_builder::OptimismPayloadBuilderAttributes;
use reth_primitives::{
constants::eip4844::{LoadKzgSettingsError, MAINNET_KZG_TRUSTED_SETUP},
fs,
@@ -34,6 +37,8 @@ use reth_provider::{
ProviderFactory, StageCheckpointReader, StateProviderFactory,
};
use reth_revm::EvmProcessorFactory;
#[cfg(feature = "optimism")]
use reth_rpc_types::engine::OptimismPayloadAttributes;
use reth_rpc_types::engine::{BlobsBundleV1, PayloadAttributes};
use reth_transaction_pool::{
blobstore::InMemoryBlobStore, BlobStore, EthPooledTransaction, PoolConfig, TransactionOrigin,
@@ -42,6 +47,9 @@ use reth_transaction_pool::{
use std::{path::PathBuf, str::FromStr, sync::Arc};
use tracing::*;
#[cfg(not(feature = "optimism"))]
use reth_payload_builder::EthPayloadBuilderAttributes;
/// `reth debug build-block` command
/// This debug routine requires that the node is positioned at the block before the target.
/// The script will then parse the block and attempt to build a similar one.
@@ -235,16 +243,31 @@ impl Command {
suggested_fee_recipient: self.suggested_fee_recipient,
// TODO: add support for withdrawals
withdrawals: None,
#[cfg(feature = "optimism")]
optimism_payload_attributes: reth_rpc_types::engine::OptimismPayloadAttributes::default(
),
};
#[cfg(feature = "optimism")]
let payload_config = PayloadConfig::new(
Arc::clone(&best_block),
Bytes::default(),
PayloadBuilderAttributes::try_new(best_block.hash, payload_attrs)?,
OptimismPayloadBuilderAttributes::try_new(
best_block.hash,
OptimismPayloadAttributes {
payload_attributes: payload_attrs,
transactions: None,
no_tx_pool: None,
gas_limit: None,
},
)?,
self.chain.clone(),
);
#[cfg(not(feature = "optimism"))]
let payload_config = PayloadConfig::new(
Arc::clone(&best_block),
Bytes::default(),
EthPayloadBuilderAttributes::try_new(best_block.hash, payload_attrs)?,
self.chain.clone(),
);
let args = BuildArguments::new(
blockchain_db.clone(),
transaction_pool,

View File

@@ -21,7 +21,12 @@ use reth_db::{init_db, DatabaseEnv};
use reth_interfaces::consensus::Consensus;
use reth_network::NetworkHandle;
use reth_network_api::NetworkInfo;
use reth_payload_builder::PayloadBuilderService;
use reth_node_api::EngineTypes;
#[cfg(not(feature = "optimism"))]
use reth_node_builder::EthEngineTypes;
#[cfg(feature = "optimism")]
use reth_node_builder::OptimismEngineTypes;
use reth_payload_builder::{PayloadBuilderHandle, PayloadBuilderService};
use reth_primitives::{
fs::{self},
ChainSpec,
@@ -29,7 +34,7 @@ use reth_primitives::{
use reth_provider::{providers::BlockchainProvider, CanonStateSubscriptions, ProviderFactory};
use reth_revm::EvmProcessorFactory;
use reth_rpc_types::{
engine::{CancunPayloadFields, ForkchoiceState, PayloadAttributes},
engine::{CancunPayloadFields, ForkchoiceState},
ExecutionPayload,
};
use reth_stages::Pipeline;
@@ -175,8 +180,17 @@ impl Command {
self.chain.clone(),
payload_builder,
);
let (payload_service, payload_builder) =
#[cfg(feature = "optimism")]
let (payload_service, payload_builder): (
_,
PayloadBuilderHandle<OptimismEngineTypes>,
) = PayloadBuilderService::new(payload_generator, blockchain_db.canonical_state_stream());
#[cfg(not(feature = "optimism"))]
let (payload_service, payload_builder): (_, PayloadBuilderHandle<EthEngineTypes>) =
PayloadBuilderService::new(payload_generator, blockchain_db.canonical_state_stream());
ctx.task_executor.spawn_critical("payload builder service", Box::pin(payload_service));
// Configure the consensus engine
@@ -245,8 +259,8 @@ impl Command {
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
enum StoredEngineApiMessage {
ForkchoiceUpdated { state: ForkchoiceState, payload_attrs: Option<PayloadAttributes> },
enum StoredEngineApiMessage<Attributes> {
ForkchoiceUpdated { state: ForkchoiceState, payload_attrs: Option<Attributes> },
NewPayload { payload: ExecutionPayload, cancun_fields: Option<CancunPayloadFields> },
}
@@ -260,7 +274,14 @@ impl EngineApiStore {
Self { path }
}
fn on_message(&self, msg: &BeaconEngineMessage, received_at: SystemTime) -> eyre::Result<()> {
fn on_message<Engine>(
&self,
msg: &BeaconEngineMessage<Engine>,
received_at: SystemTime,
) -> eyre::Result<()>
where
Engine: EngineTypes,
{
fs::create_dir_all(&self.path)?; // ensure that store path had been created
let timestamp = received_at.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis();
match msg {
@@ -278,10 +299,12 @@ impl EngineApiStore {
let filename = format!("{}-new_payload-{}.json", timestamp, payload.block_hash());
fs::write(
self.path.join(filename),
serde_json::to_vec(&StoredEngineApiMessage::NewPayload {
payload: payload.clone(),
cancun_fields: cancun_fields.clone(),
})?,
serde_json::to_vec(
&StoredEngineApiMessage::<Engine::PayloadAttributes>::NewPayload {
payload: payload.clone(),
cancun_fields: cancun_fields.clone(),
},
)?,
)?;
}
// noop
@@ -310,11 +333,14 @@ impl EngineApiStore {
Ok(filenames_by_ts.into_iter().flat_map(|(_, paths)| paths))
}
pub(crate) async fn intercept(
pub(crate) async fn intercept<Engine>(
self,
mut rx: UnboundedReceiver<BeaconEngineMessage>,
to_engine: UnboundedSender<BeaconEngineMessage>,
) {
mut rx: UnboundedReceiver<BeaconEngineMessage<Engine>>,
to_engine: UnboundedSender<BeaconEngineMessage<Engine>>,
) where
Engine: EngineTypes,
BeaconEngineMessage<Engine>: std::fmt::Debug,
{
loop {
let Some(msg) = rx.recv().await else { break };
if let Err(error) = self.on_message(&msg, SystemTime::now()) {