feat(api,rpc): improve engine API abstraction (#6871)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Federico Gimenez
2024-03-05 17:05:17 +01:00
committed by GitHub
parent b24f9de141
commit 96fcdfbac8
19 changed files with 312 additions and 137 deletions

14
Cargo.lock generated
View File

@ -174,7 +174,7 @@ dependencies = [
[[package]]
name = "alloy-eips"
version = "0.1.0"
source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e"
source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d"
dependencies = [
"alloy-primitives",
"alloy-rlp",
@ -184,7 +184,7 @@ dependencies = [
[[package]]
name = "alloy-genesis"
version = "0.1.0"
source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e"
source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d"
dependencies = [
"alloy-primitives",
"alloy-rpc-types",
@ -206,7 +206,7 @@ dependencies = [
[[package]]
name = "alloy-node-bindings"
version = "0.1.0"
source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e"
source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d"
dependencies = [
"alloy-genesis",
"alloy-primitives",
@ -267,7 +267,7 @@ dependencies = [
[[package]]
name = "alloy-rpc-engine-types"
version = "0.1.0"
source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e"
source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d"
dependencies = [
"alloy-primitives",
"alloy-rlp",
@ -282,7 +282,7 @@ dependencies = [
[[package]]
name = "alloy-rpc-trace-types"
version = "0.1.0"
source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e"
source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d"
dependencies = [
"alloy-primitives",
"alloy-rpc-types",
@ -293,7 +293,7 @@ dependencies = [
[[package]]
name = "alloy-rpc-types"
version = "0.1.0"
source = "git+https://github.com/alloy-rs/alloy?rev=76c70fb#76c70fb9d44ace661bbf33408c2527e3874c964e"
source = "git+https://github.com/alloy-rs/alloy?rev=785c667#785c667813a6c76794044b943df58fc6e397734d"
dependencies = [
"alloy-primitives",
"alloy-rlp",
@ -7039,7 +7039,7 @@ dependencies = [
[[package]]
name = "revm-inspectors"
version = "0.1.0"
source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=75a187b#75a187ba967a29b30af2e5e848073c755068da06"
source = "git+https://github.com/paradigmxyz/evm-inspectors?rev=7068d39#7068d395eb56ee5fee8e6acc7c32d3dbc4616f3b"
dependencies = [
"alloy-primitives",
"alloy-rpc-trace-types",

View File

@ -178,7 +178,7 @@ reth-trie = { path = "crates/trie" }
# revm
revm = { version = "6.1.0", features = ["std", "secp256k1"], default-features = false }
revm-primitives = { version = "2.1.0", features = ["std"], default-features = false }
revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "75a187b" }
revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "7068d39" }
# eth
alloy-chains = { version = "0.1", feature = ["serde", "rlp", "arbitrary"] }
@ -187,12 +187,12 @@ alloy-dyn-abi = "0.6"
alloy-sol-types = "0.6"
alloy-rlp = "0.3"
alloy-trie = "0.3"
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" }
alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" }
alloy-rpc-engine-types = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" }
alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" }
alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" }
alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "76c70fb" }
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" }
alloy-rpc-trace-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" }
alloy-rpc-engine-types = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" }
alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" }
alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" }
alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "785c667" }
ethers-core = { version = "2.0", default-features = false }
ethers-providers = { version = "2.0", default-features = false }
ethers-signers = { version = "2.0", default-features = false }

View File

@ -296,7 +296,8 @@ impl Command {
);
#[cfg(feature = "optimism")]
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default()
let payload_builder =
reth_optimism_payload_builder::OptimismPayloadBuilder::new(self.chain.clone())
.compute_pending_block();
#[cfg(not(feature = "optimism"))]

View File

@ -169,7 +169,8 @@ impl Command {
// Optimism's payload builder is implemented on the OptimismPayloadBuilder type.
#[cfg(feature = "optimism")]
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default();
let payload_builder =
reth_optimism_payload_builder::OptimismPayloadBuilder::new(self.chain.clone());
let payload_generator = BasicPayloadJobGenerator::with_builder(
blockchain_db.clone(),

View File

@ -6,6 +6,7 @@ use reth_primitives::{ChainSpec, Hardfork};
/// Contains traits to abstract over payload attributes types and default implementations of the
/// [PayloadAttributes] trait for ethereum mainnet and optimism types.
pub mod traits;
use serde::{de::DeserializeOwned, ser::Serialize};
pub use traits::{BuiltPayload, PayloadAttributes, PayloadBuilderAttributes};
/// Contains error types used in the traits defined in this crate.
@ -18,7 +19,7 @@ pub use payload::PayloadOrAttributes;
/// The types that are used by the engine API.
pub trait EngineTypes:
serde::de::DeserializeOwned + fmt::Debug + Unpin + Send + Sync + Clone
serde::de::DeserializeOwned + Serialize + fmt::Debug + Unpin + Send + Sync + Clone
{
/// The RPC payload attributes type the CL node emits via the engine API.
type PayloadAttributes: PayloadAttributes + Unpin;
@ -29,7 +30,19 @@ pub trait EngineTypes:
+ Unpin;
/// The built payload type.
type BuiltPayload: BuiltPayload + Clone + Unpin;
type BuiltPayload: BuiltPayload
+ Clone
+ Unpin
+ TryInto<Self::ExecutionPayloadV1>
+ TryInto<Self::ExecutionPayloadV2>
+ TryInto<Self::ExecutionPayloadV3>;
/// Execution Payload V1 type.
type ExecutionPayloadV1: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static;
/// Execution Payload V2 type.
type ExecutionPayloadV2: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static;
/// Execution Payload V3 type.
type ExecutionPayloadV3: DeserializeOwned + Serialize + Clone + Unpin + Send + Sync + 'static;
/// Validates the presence or exclusion of fork-specific fields based on the payload attributes
/// and the message version.

View File

@ -4,12 +4,8 @@ use reth_primitives::{
Address, ChainSpec, Header, SealedBlock, Withdrawals, B256, U256,
};
use reth_rpc_types::{
engine::{
ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, OptimismPayloadAttributes,
PayloadAttributes as EthPayloadAttributes, PayloadId,
},
engine::{OptimismPayloadAttributes, PayloadAttributes as EthPayloadAttributes, PayloadId},
withdrawal::Withdrawal,
ExecutionPayloadV1,
};
/// Represents a built payload type that contains a built [SealedBlock] and can be converted into
@ -20,15 +16,6 @@ pub trait BuiltPayload: Send + Sync + std::fmt::Debug {
/// Returns the fees collected for the built block
fn fees(&self) -> U256;
/// Converts the type into the response expected by `engine_getPayloadV1`
fn into_v1_payload(self) -> ExecutionPayloadV1;
/// Converts the type into the response expected by `engine_getPayloadV2`
fn into_v2_payload(self) -> ExecutionPayloadEnvelopeV2;
/// Converts the type into the response expected by `engine_getPayloadV3`
fn into_v3_payload(self) -> ExecutionPayloadEnvelopeV3;
}
/// This can be implemented by types that describe a currently running payload job.

View File

@ -4,10 +4,16 @@ use reth_node_api::{
};
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes};
use reth_primitives::ChainSpec;
use reth_rpc_types::engine::PayloadAttributes as EthPayloadAttributes;
use reth_rpc_types::{
engine::{
ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3,
PayloadAttributes as EthPayloadAttributes,
},
ExecutionPayloadV1,
};
/// The types used in the default mainnet ethereum beacon consensus engine.
#[derive(Debug, Default, Clone, serde::Deserialize)]
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub struct EthEngineTypes;
@ -15,6 +21,9 @@ impl EngineTypes for EthEngineTypes {
type PayloadAttributes = EthPayloadAttributes;
type PayloadBuilderAttributes = EthPayloadBuilderAttributes;
type BuiltPayload = EthBuiltPayload;
type ExecutionPayloadV1 = ExecutionPayloadV1;
type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2;
type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3;
fn validate_version_specific_fields(
chain_spec: &ChainSpec,

View File

@ -2,19 +2,27 @@ use reth_node_api::{
engine::validate_parent_beacon_block_root_presence, AttributesValidationError,
EngineApiMessageVersion, EngineTypes, PayloadOrAttributes,
};
use reth_payload_builder::{EthBuiltPayload, OptimismPayloadBuilderAttributes};
use reth_payload_builder::{OptimismBuiltPayload, OptimismPayloadBuilderAttributes};
use reth_primitives::{ChainSpec, Hardfork};
use reth_rpc_types::engine::OptimismPayloadAttributes;
use reth_rpc_types::{
engine::{
ExecutionPayloadEnvelopeV2, OptimismExecutionPayloadEnvelopeV3, OptimismPayloadAttributes,
},
ExecutionPayloadV1,
};
/// The types used in the optimism beacon consensus engine.
#[derive(Debug, Default, Clone, serde::Deserialize)]
#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub struct OptimismEngineTypes;
impl EngineTypes for OptimismEngineTypes {
type PayloadAttributes = OptimismPayloadAttributes;
type PayloadBuilderAttributes = OptimismPayloadBuilderAttributes;
type BuiltPayload = EthBuiltPayload;
type BuiltPayload = OptimismBuiltPayload;
type ExecutionPayloadV1 = ExecutionPayloadV1;
type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2;
type ExecutionPayloadV3 = OptimismExecutionPayloadEnvelopeV3;
fn validate_version_specific_fields(
chain_spec: &ChainSpec,

View File

@ -172,7 +172,8 @@ where
ctx: &BuilderContext<Node>,
pool: Pool,
) -> eyre::Result<PayloadBuilderHandle<Node::Engine>> {
let payload_builder = reth_optimism_payload_builder::OptimismPayloadBuilder::default()
let payload_builder =
reth_optimism_payload_builder::OptimismPayloadBuilder::new(ctx.chain_spec())
.set_compute_pending_block(self.compute_pending_block);
let conf = ctx.payload_builder_config();

View File

@ -15,8 +15,8 @@ use futures_util::FutureExt;
use reth_interfaces::RethResult;
use reth_node_api::{BuiltPayload, PayloadBuilderAttributes};
use reth_payload_builder::{
database::CachedReads, error::PayloadBuilderError, EthBuiltPayload, KeepPayloadJobAlive,
PayloadId, PayloadJob, PayloadJobGenerator,
database::CachedReads, error::PayloadBuilderError, KeepPayloadJobAlive, PayloadId, PayloadJob,
PayloadJobGenerator,
};
use reth_primitives::{
bytes::BytesMut,
@ -890,7 +890,7 @@ where
///
/// This compares the total fees of the blocks, higher is better.
#[inline(always)]
pub fn is_better_payload(best_payload: Option<&EthBuiltPayload>, new_fees: U256) -> bool {
pub fn is_better_payload(best_payload: Option<impl BuiltPayload>, new_fees: U256) -> bool {
if let Some(best_payload) = best_payload {
new_fees > best_payload.fees()
} else {

View File

@ -115,7 +115,7 @@ pub mod noop;
#[cfg(any(test, feature = "test-utils"))]
pub mod test_utils;
pub use optimism::OptimismPayloadBuilderAttributes;
pub use optimism::{OptimismBuiltPayload, OptimismPayloadBuilderAttributes};
pub use payload::{EthBuiltPayload, EthPayloadBuilderAttributes};
pub use reth_rpc_types::engine::PayloadId;
pub use service::{PayloadBuilderHandle, PayloadBuilderService, PayloadStore};

View File

@ -1,13 +1,21 @@
use crate::EthPayloadBuilderAttributes;
use alloy_rlp::{Encodable, Error as DecodeError};
use reth_node_api::PayloadBuilderAttributes;
use reth_node_api::{BuiltPayload, PayloadBuilderAttributes};
use reth_primitives::{
revm::config::revm_spec_by_timestamp_after_merge,
revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, SpecId},
Address, ChainSpec, Header, TransactionSigned, Withdrawals, B256, U256,
Address, BlobTransactionSidecar, ChainSpec, Header, SealedBlock, TransactionSigned,
Withdrawals, B256, U256,
};
use reth_rpc_types::engine::{OptimismPayloadAttributes, PayloadId};
use reth_rpc_types_compat::engine::payload::convert_standalone_withdraw_to_withdrawal;
use reth_rpc_types::engine::{
ExecutionPayloadEnvelopeV2, ExecutionPayloadV1, OptimismExecutionPayloadEnvelopeV3,
OptimismPayloadAttributes, PayloadId,
};
use reth_rpc_types_compat::engine::payload::{
block_to_payload_v3, convert_block_to_payload_field_v2,
convert_standalone_withdraw_to_withdrawal, try_block_to_payload_v1,
};
use std::sync::Arc;
/// Optimism Payload Builder Attributes
#[derive(Debug, Clone, PartialEq, Eq)]
@ -152,6 +160,126 @@ impl PayloadBuilderAttributes for OptimismPayloadBuilderAttributes {
}
}
/// Contains the built payload.
#[derive(Debug, Clone)]
pub struct OptimismBuiltPayload {
/// Identifier of the payload
pub(crate) id: PayloadId,
/// The built block
pub(crate) block: SealedBlock,
/// The fees of the block
pub(crate) fees: U256,
/// The blobs, proofs, and commitments in the block. If the block is pre-cancun, this will be
/// empty.
pub(crate) sidecars: Vec<BlobTransactionSidecar>,
/// The rollup's chainspec.
pub(crate) chain_spec: Arc<ChainSpec>,
/// The payload attributes.
pub(crate) attributes: OptimismPayloadBuilderAttributes,
}
// === impl BuiltPayload ===
impl OptimismBuiltPayload {
/// Initializes the payload with the given initial block.
pub fn new(
id: PayloadId,
block: SealedBlock,
fees: U256,
chain_spec: Arc<ChainSpec>,
attributes: OptimismPayloadBuilderAttributes,
) -> Self {
Self { id, block, fees, sidecars: Vec::new(), chain_spec, attributes }
}
/// Returns the identifier of the payload.
pub fn id(&self) -> PayloadId {
self.id
}
/// Returns the built block(sealed)
pub fn block(&self) -> &SealedBlock {
&self.block
}
/// Fees of the block
pub fn fees(&self) -> U256 {
self.fees
}
/// Adds sidecars to the payload.
pub fn extend_sidecars(&mut self, sidecars: Vec<BlobTransactionSidecar>) {
self.sidecars.extend(sidecars)
}
}
impl BuiltPayload for OptimismBuiltPayload {
fn block(&self) -> &SealedBlock {
&self.block
}
fn fees(&self) -> U256 {
self.fees
}
}
impl<'a> BuiltPayload for &'a OptimismBuiltPayload {
fn block(&self) -> &SealedBlock {
(**self).block()
}
fn fees(&self) -> U256 {
(**self).fees()
}
}
// V1 engine_getPayloadV1 response
impl From<OptimismBuiltPayload> for ExecutionPayloadV1 {
fn from(value: OptimismBuiltPayload) -> Self {
try_block_to_payload_v1(value.block)
}
}
// V2 engine_getPayloadV2 response
impl From<OptimismBuiltPayload> for ExecutionPayloadEnvelopeV2 {
fn from(value: OptimismBuiltPayload) -> Self {
let OptimismBuiltPayload { block, fees, .. } = value;
ExecutionPayloadEnvelopeV2 {
block_value: fees,
execution_payload: convert_block_to_payload_field_v2(block),
}
}
}
impl From<OptimismBuiltPayload> for OptimismExecutionPayloadEnvelopeV3 {
fn from(value: OptimismBuiltPayload) -> Self {
let OptimismBuiltPayload { block, fees, sidecars, chain_spec, attributes, .. } = value;
let parent_beacon_block_root =
if chain_spec.is_cancun_active_at_timestamp(attributes.timestamp()) {
attributes.parent_beacon_block_root().unwrap_or(B256::ZERO)
} else {
B256::ZERO
};
OptimismExecutionPayloadEnvelopeV3 {
execution_payload: block_to_payload_v3(block.clone()),
block_value: fees,
// From the engine API spec:
//
// > Client software **MAY** use any heuristics to decide whether to set
// `shouldOverrideBuilder` flag or not. If client software does not implement any
// heuristic this flag **SHOULD** be set to `false`.
//
// Spec:
// <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification-2>
should_override_builder: false,
blobs_bundle: sidecars.into_iter().map(Into::into).collect::<Vec<_>>().into(),
parent_beacon_block_root,
}
}
}
/// Generates the payload id for the configured payload from the [OptimismPayloadAttributes].
///
/// Returns an 8-byte identifier by hashing the payload components with sha256 hash.

View File

@ -62,21 +62,6 @@ impl EthBuiltPayload {
pub fn extend_sidecars(&mut self, sidecars: Vec<BlobTransactionSidecar>) {
self.sidecars.extend(sidecars)
}
/// Converts the type into the response expected by `engine_getPayloadV1`
pub fn into_v1_payload(self) -> ExecutionPayloadV1 {
self.into()
}
/// Converts the type into the response expected by `engine_getPayloadV2`
pub fn into_v2_payload(self) -> ExecutionPayloadEnvelopeV2 {
self.into()
}
/// Converts the type into the response expected by `engine_getPayloadV3`
pub fn into_v3_payload(self) -> ExecutionPayloadEnvelopeV3 {
self.into()
}
}
impl BuiltPayload for EthBuiltPayload {
@ -87,17 +72,15 @@ impl BuiltPayload for EthBuiltPayload {
fn fees(&self) -> U256 {
self.fees
}
fn into_v1_payload(self) -> ExecutionPayloadV1 {
self.into()
}
fn into_v2_payload(self) -> ExecutionPayloadEnvelopeV2 {
self.into()
impl<'a> BuiltPayload for &'a EthBuiltPayload {
fn block(&self) -> &SealedBlock {
(**self).block()
}
fn into_v3_payload(self) -> ExecutionPayloadEnvelopeV3 {
self.into()
fn fees(&self) -> U256 {
(**self).fees()
}
}
@ -137,10 +120,6 @@ impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV3 {
// <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification-2>
should_override_builder: false,
blobs_bundle: sidecars.into_iter().map(Into::into).collect::<Vec<_>>().into(),
// Optimism-specific: Post-cancun, the parent beacon block root is included in the
// enveloped payload. We set this as `None` here so that optimism-specific
// handling can fill the value.
parent_beacon_block_root: None,
}
}
}

View File

@ -18,14 +18,14 @@ mod builder {
use crate::error::OptimismPayloadBuilderError;
use reth_basic_payload_builder::*;
use reth_payload_builder::{
error::PayloadBuilderError, EthBuiltPayload, OptimismPayloadBuilderAttributes,
error::PayloadBuilderError, OptimismBuiltPayload, OptimismPayloadBuilderAttributes,
};
use reth_primitives::{
constants::{BEACON_NONCE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS},
eip4844::calculate_excess_blob_gas,
proofs,
revm::env::tx_env_with_recovered,
Block, Hardfork, Header, IntoRecoveredTransaction, Receipt, Receipts, TxType,
Block, ChainSpec, Hardfork, Header, IntoRecoveredTransaction, Receipt, Receipts, TxType,
EMPTY_OMMER_ROOT_HASH, U256,
};
use reth_provider::{BundleStateWithReceipts, StateProviderFactory};
@ -36,18 +36,25 @@ mod builder {
primitives::{EVMError, EnvWithHandlerCfg, InvalidTransaction, ResultAndState},
DatabaseCommit, State,
};
use std::sync::Arc;
use tracing::{debug, trace, warn};
/// Optimism's payload builder
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OptimismPayloadBuilder {
/// The rollup's compute pending block configuration option.
// TODO(clabby): Implement this feature.
compute_pending_block: bool,
/// The rollup's chain spec.
chain_spec: Arc<ChainSpec>,
}
impl OptimismPayloadBuilder {
/// OptimismPayloadBuilder constructor.
pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { compute_pending_block: true, chain_spec }
}
/// Sets the rollup's compute pending block configuration option.
pub fn set_compute_pending_block(mut self, compute_pending_block: bool) -> Self {
self.compute_pending_block = compute_pending_block;
@ -63,6 +70,12 @@ mod builder {
pub fn is_compute_pending_block(&self) -> bool {
self.compute_pending_block
}
/// Sets the rollup's chainspec.
pub fn set_chain_spec(mut self, chain_spec: Arc<ChainSpec>) -> Self {
self.chain_spec = chain_spec;
self
}
}
/// Implementation of the [PayloadBuilder] trait for [OptimismPayloadBuilder].
@ -72,19 +85,29 @@ mod builder {
Pool: TransactionPool,
{
type Attributes = OptimismPayloadBuilderAttributes;
type BuiltPayload = EthBuiltPayload;
type BuiltPayload = OptimismBuiltPayload;
fn try_build(
&self,
args: BuildArguments<Pool, Client, OptimismPayloadBuilderAttributes, EthBuiltPayload>,
) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError> {
args: BuildArguments<
Pool,
Client,
OptimismPayloadBuilderAttributes,
OptimismBuiltPayload,
>,
) -> Result<BuildOutcome<OptimismBuiltPayload>, PayloadBuilderError> {
optimism_payload_builder(args, self.compute_pending_block)
}
fn on_missing_payload(
&self,
args: BuildArguments<Pool, Client, OptimismPayloadBuilderAttributes, EthBuiltPayload>,
) -> Option<EthBuiltPayload> {
args: BuildArguments<
Pool,
Client,
OptimismPayloadBuilderAttributes,
OptimismBuiltPayload,
>,
) -> Option<OptimismBuiltPayload> {
// In Optimism, the PayloadAttributes can specify a `no_tx_pool` option that implies we
// should not pull transactions from the tx pool. In this case, we build the payload
// upfront with the list of transactions sent in the attributes without caring about
@ -102,7 +125,7 @@ mod builder {
fn build_empty_payload(
client: &Client,
config: PayloadConfig<Self::Attributes>,
) -> Result<EthBuiltPayload, PayloadBuilderError> {
) -> Result<OptimismBuiltPayload, PayloadBuilderError> {
let extra_data = config.extra_data();
let PayloadConfig {
initialized_block_env,
@ -205,10 +228,12 @@ mod builder {
let block = Block { header, body: vec![], ommers: vec![], withdrawals };
let sealed_block = block.seal_slow();
Ok(EthBuiltPayload::new(
Ok(OptimismBuiltPayload::new(
attributes.payload_attributes.payload_id(),
sealed_block,
U256::ZERO,
chain_spec,
attributes,
))
}
}
@ -223,9 +248,9 @@ mod builder {
/// a result indicating success with the payload or an error in case of failure.
#[inline]
pub(crate) fn optimism_payload_builder<Pool, Client>(
args: BuildArguments<Pool, Client, OptimismPayloadBuilderAttributes, EthBuiltPayload>,
args: BuildArguments<Pool, Client, OptimismPayloadBuilderAttributes, OptimismBuiltPayload>,
_compute_pending_block: bool,
) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError>
) -> Result<BuildOutcome<OptimismBuiltPayload>, PayloadBuilderError>
where
Client: StateProviderFactory,
Pool: TransactionPool,
@ -491,7 +516,7 @@ mod builder {
&mut db,
&chain_spec,
attributes.payload_attributes.timestamp,
attributes.payload_attributes.withdrawals,
attributes.clone().payload_attributes.withdrawals,
)?;
// merge all transitions into bundle state, this would apply the withdrawal balance changes
@ -567,8 +592,13 @@ mod builder {
let sealed_block = block.seal_slow();
debug!(target: "payload_builder", ?sealed_block, "sealed built block");
let mut payload =
EthBuiltPayload::new(attributes.payload_attributes.id, sealed_block, total_fees);
let mut payload = OptimismBuiltPayload::new(
attributes.payload_attributes.id,
sealed_block,
total_fees,
chain_spec,
attributes,
);
// extend the payload with the blob sidecars from the executed txs
payload.extend_sidecars(blob_sidecars);

View File

@ -353,7 +353,7 @@ pub static BASE_MAINNET: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
/// A wrapper around [BaseFeeParams] that allows for specifying constant or dynamic EIP-1559
/// parameters based on the active [Hardfork].
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum BaseFeeParamsKind {
/// Constant [BaseFeeParams]; used for chains that don't have dynamic EIP-1559 parameters
@ -377,7 +377,7 @@ impl From<ForkBaseFeeParams> for BaseFeeParamsKind {
/// A type alias to a vector of tuples of [Hardfork] and [BaseFeeParams], sorted by [Hardfork]
/// activation order. This is used to specify dynamic EIP-1559 parameters for chains like Optimism.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ForkBaseFeeParams(Vec<(Hardfork, BaseFeeParams)>);
impl From<Vec<(Hardfork, BaseFeeParams)>> for ForkBaseFeeParams {
@ -456,7 +456,7 @@ impl BaseFeeParams {
/// - Meta-information about the chain (the chain ID)
/// - The genesis block of the chain ([`Genesis`])
/// - What hardforks are activated, and under which conditions
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct ChainSpec {
/// The chain ID
pub chain: Chain,

View File

@ -8,9 +8,8 @@ use reth_node_api::EngineTypes;
use reth_primitives::{Address, BlockHash, BlockId, BlockNumberOrTag, Bytes, B256, U256, U64};
use reth_rpc_types::{
engine::{
ExecutionPayloadBodiesV1, ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3,
ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3, ForkchoiceState,
ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration,
ExecutionPayloadBodiesV1, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3,
ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration,
},
state::StateOverride,
BlockOverrides, Filter, Log, RichBlock, SyncStatus, TransactionRequest,
@ -96,7 +95,7 @@ pub trait EngineApi<Engine: EngineTypes> {
/// Note:
/// > Provider software MAY stop the corresponding build process after serving this call.
#[method(name = "getPayloadV1")]
async fn get_payload_v1(&self, payload_id: PayloadId) -> RpcResult<ExecutionPayloadV1>;
async fn get_payload_v1(&self, payload_id: PayloadId) -> RpcResult<Engine::ExecutionPayloadV1>;
/// See also <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#engine_getpayloadv2>
///
@ -104,7 +103,7 @@ pub trait EngineApi<Engine: EngineTypes> {
/// payload build process at the time of receiving this call. Note:
/// > Provider software MAY stop the corresponding build process after serving this call.
#[method(name = "getPayloadV2")]
async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult<ExecutionPayloadEnvelopeV2>;
async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult<Engine::ExecutionPayloadV2>;
/// Post Cancun payload handler which also returns a blobs bundle.
///
@ -114,7 +113,7 @@ pub trait EngineApi<Engine: EngineTypes> {
/// payload build process at the time of receiving this call. Note:
/// > Provider software MAY stop the corresponding build process after serving this call.
#[method(name = "getPayloadV3")]
async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult<ExecutionPayloadEnvelopeV3>;
async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult<Engine::ExecutionPayloadV3>;
/// See also <https://github.com/ethereum/execution-apis/blob/6452a6b194d7db269bf1dbd087a267251d3cc7f8/src/engine/shanghai.md#engine_getpayloadbodiesbyhashv1>
#[method(name = "getPayloadBodiesByHashV1")]

View File

@ -4,17 +4,17 @@ use jsonrpsee_core::RpcResult;
use reth_beacon_consensus::BeaconConsensusEngineHandle;
use reth_interfaces::consensus::ForkchoiceState;
use reth_node_api::{
validate_payload_timestamp, BuiltPayload, EngineApiMessageVersion, EngineTypes,
PayloadAttributes, PayloadBuilderAttributes, PayloadOrAttributes,
validate_payload_timestamp, EngineApiMessageVersion, EngineTypes, PayloadAttributes,
PayloadBuilderAttributes, PayloadOrAttributes,
};
use reth_payload_builder::PayloadStore;
use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, B256, U64};
use reth_provider::{BlockReader, EvmEnvProvider, HeaderProvider, StateProviderFactory};
use reth_rpc_api::EngineApiServer;
use reth_rpc_types::engine::{
CancunPayloadFields, ExecutionPayload, ExecutionPayloadBodiesV1, ExecutionPayloadEnvelopeV2,
ExecutionPayloadEnvelopeV3, ExecutionPayloadInputV2, ExecutionPayloadV1, ExecutionPayloadV3,
ForkchoiceUpdated, PayloadId, PayloadStatus, TransitionConfiguration, CAPABILITIES,
CancunPayloadFields, ExecutionPayload, ExecutionPayloadBodiesV1, ExecutionPayloadInputV2,
ExecutionPayloadV1, ExecutionPayloadV3, ForkchoiceUpdated, PayloadId, PayloadStatus,
TransitionConfiguration, CAPABILITIES,
};
use reth_rpc_types_compat::engine::payload::{
convert_payload_input_v2_to_payload, convert_to_payload_body_v1,
@ -22,7 +22,7 @@ use reth_rpc_types_compat::engine::payload::{
use reth_tasks::TaskSpawner;
use std::{sync::Arc, time::Instant};
use tokio::sync::oneshot;
use tracing::trace;
use tracing::{trace, warn};
/// The Engine API response sender.
pub type EngineApiSender<Ok> = oneshot::Sender<EngineApiResult<Ok>>;
@ -202,14 +202,18 @@ where
pub async fn get_payload_v1(
&self,
payload_id: PayloadId,
) -> EngineApiResult<ExecutionPayloadV1> {
Ok(self
.inner
) -> EngineApiResult<EngineT::ExecutionPayloadV1> {
self.inner
.payload_store
.resolve(payload_id)
.await
.ok_or(EngineApiError::UnknownPayload)?
.map(|payload| payload.into_v1_payload())?)
.map_err(|_| EngineApiError::UnknownPayload)?
.try_into()
.map_err(|_| {
warn!("could not transform built payload into ExecutionPayloadV1");
EngineApiError::UnknownPayload
})
}
/// Returns the most recent version of the payload that is available in the corresponding
@ -222,7 +226,7 @@ where
pub async fn get_payload_v2(
&self,
payload_id: PayloadId,
) -> EngineApiResult<ExecutionPayloadEnvelopeV2> {
) -> EngineApiResult<EngineT::ExecutionPayloadV2> {
// First we fetch the payload attributes to check the timestamp
let attributes = self.get_payload_attributes(payload_id).await?;
@ -234,13 +238,17 @@ where
)?;
// Now resolve the payload
Ok(self
.inner
self.inner
.payload_store
.resolve(payload_id)
.await
.ok_or(EngineApiError::UnknownPayload)?
.map(|payload| payload.into_v2_payload())?)
.map_err(|_| EngineApiError::UnknownPayload)?
.try_into()
.map_err(|_| {
warn!("could not transform built payload into ExecutionPayloadV2");
EngineApiError::UnknownPayload
})
}
/// Returns the most recent version of the payload that is available in the corresponding
@ -253,7 +261,7 @@ where
pub async fn get_payload_v3(
&self,
payload_id: PayloadId,
) -> EngineApiResult<ExecutionPayloadEnvelopeV3> {
) -> EngineApiResult<EngineT::ExecutionPayloadV3> {
// First we fetch the payload attributes to check the timestamp
let attributes = self.get_payload_attributes(payload_id).await?;
@ -265,25 +273,17 @@ where
)?;
// Now resolve the payload
let mut resolved_payload = self
.inner
self.inner
.payload_store
.resolve(payload_id)
.await
.ok_or(EngineApiError::UnknownPayload)?
.map(|payload| payload.into_v3_payload())?;
// After `Cancun` is enabled on optimism, an extra field `parent_beacon_block_root` is
// included in the enveloped V3 payload. On ethereum, this field is not included.
if self.inner.chain_spec.is_optimism() &&
self.inner
.chain_spec
.is_fork_active_at_timestamp(Hardfork::Cancun, attributes.timestamp())
{
resolved_payload.parent_beacon_block_root = attributes.parent_beacon_block_root();
}
Ok(resolved_payload)
.map_err(|_| EngineApiError::UnknownPayload)?
.try_into()
.map_err(|_| {
warn!("could not transform built payload into ExecutionPayloadV2");
EngineApiError::UnknownPayload
})
}
/// Returns the execution payload bodies by the range starting at `start`, containing `count`
@ -581,7 +581,10 @@ where
///
/// Note:
/// > Provider software MAY stop the corresponding build process after serving this call.
async fn get_payload_v1(&self, payload_id: PayloadId) -> RpcResult<ExecutionPayloadV1> {
async fn get_payload_v1(
&self,
payload_id: PayloadId,
) -> RpcResult<EngineT::ExecutionPayloadV1> {
trace!(target: "rpc::engine", "Serving engine_getPayloadV1");
let start = Instant::now();
let res = EngineApi::get_payload_v1(self, payload_id).await;
@ -598,7 +601,10 @@ where
///
/// Note:
/// > Provider software MAY stop the corresponding build process after serving this call.
async fn get_payload_v2(&self, payload_id: PayloadId) -> RpcResult<ExecutionPayloadEnvelopeV2> {
async fn get_payload_v2(
&self,
payload_id: PayloadId,
) -> RpcResult<EngineT::ExecutionPayloadV2> {
trace!(target: "rpc::engine", "Serving engine_getPayloadV2");
let start = Instant::now();
let res = EngineApi::get_payload_v2(self, payload_id).await;
@ -615,7 +621,10 @@ where
///
/// Note:
/// > Provider software MAY stop the corresponding build process after serving this call.
async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult<ExecutionPayloadEnvelopeV3> {
async fn get_payload_v3(
&self,
payload_id: PayloadId,
) -> RpcResult<EngineT::ExecutionPayloadV3> {
trace!(target: "rpc::engine", "Serving engine_getPayloadV3");
let start = Instant::now();
let res = EngineApi::get_payload_v3(self, payload_id).await;

View File

@ -45,6 +45,7 @@ allow = [
"ISC",
"Unicode-DFS-2016",
"Unlicense",
"Unicode-3.0",
# https://github.com/briansmith/ring/issues/902
"LicenseRef-ring",
# https://github.com/briansmith/webpki/issues/148

View File

@ -48,8 +48,12 @@ use reth_payload_builder::{
};
use reth_primitives::{Address, ChainSpec, Genesis, Header, Withdrawals, B256};
use reth_rpc_types::{
engine::{PayloadAttributes as EthPayloadAttributes, PayloadId},
engine::{
ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3,
PayloadAttributes as EthPayloadAttributes, PayloadId,
},
withdrawal::Withdrawal,
ExecutionPayloadV1,
};
use reth_tracing::{RethTracer, Tracer};
use serde::{Deserialize, Serialize};
@ -95,7 +99,9 @@ impl PayloadAttributes for CustomPayloadAttributes {
// custom validation logic - ensure that the custom field is not zero
if self.custom == 0 {
return Err(AttributesValidationError::invalid_params(CustomError::CustomFieldIsNotZero))
return Err(AttributesValidationError::invalid_params(
CustomError::CustomFieldIsNotZero,
));
}
Ok(())
@ -153,7 +159,7 @@ impl PayloadBuilderAttributes for CustomPayloadBuilderAttributes {
/// Custom engine types - uses a custom payload attributes RPC type, but uses the default
/// payload builder attributes type.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[non_exhaustive]
pub struct CustomEngineTypes;
@ -161,6 +167,9 @@ impl EngineTypes for CustomEngineTypes {
type PayloadAttributes = CustomPayloadAttributes;
type PayloadBuilderAttributes = CustomPayloadBuilderAttributes;
type BuiltPayload = EthBuiltPayload;
type ExecutionPayloadV1 = ExecutionPayloadV1;
type ExecutionPayloadV2 = ExecutionPayloadEnvelopeV2;
type ExecutionPayloadV3 = ExecutionPayloadEnvelopeV3;
fn validate_version_specific_fields(
chain_spec: &ChainSpec,