mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: Introduce payload primitives (#8642)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -6206,6 +6206,7 @@ dependencies = [
|
||||
"reth-node-optimism",
|
||||
"reth-optimism-primitives",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-primitives",
|
||||
"reth-payload-validator",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
@ -6263,9 +6264,9 @@ dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"metrics",
|
||||
"reth-engine-primitives",
|
||||
"reth-metrics",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-primitives",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-revm",
|
||||
@ -6300,6 +6301,7 @@ dependencies = [
|
||||
"reth-metrics",
|
||||
"reth-network-p2p",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-primitives",
|
||||
"reth-payload-validator",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
@ -6686,10 +6688,9 @@ dependencies = [
|
||||
name = "reth-engine-primitives"
|
||||
version = "0.2.0-beta.9"
|
||||
dependencies = [
|
||||
"reth-payload-primitives",
|
||||
"reth-primitives",
|
||||
"reth-rpc-types",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6775,6 +6776,7 @@ version = "0.2.0-beta.9"
|
||||
dependencies = [
|
||||
"alloy-rlp",
|
||||
"reth-engine-primitives",
|
||||
"reth-payload-primitives",
|
||||
"reth-primitives",
|
||||
"reth-rpc-types",
|
||||
"reth-rpc-types-compat",
|
||||
@ -7180,6 +7182,7 @@ dependencies = [
|
||||
"reth-evm",
|
||||
"reth-network",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-primitives",
|
||||
"reth-provider",
|
||||
"reth-tasks",
|
||||
"reth-transaction-pool",
|
||||
@ -7396,10 +7399,10 @@ version = "0.2.0-beta.9"
|
||||
dependencies = [
|
||||
"alloy-rlp",
|
||||
"reth-basic-payload-builder",
|
||||
"reth-engine-primitives",
|
||||
"reth-evm",
|
||||
"reth-evm-optimism",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-primitives",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-revm",
|
||||
@ -7426,6 +7429,7 @@ dependencies = [
|
||||
"reth-errors",
|
||||
"reth-ethereum-engine-primitives",
|
||||
"reth-metrics",
|
||||
"reth-payload-primitives",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-rpc-types",
|
||||
@ -7438,6 +7442,19 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-payload-primitives"
|
||||
version = "0.2.0-beta.9"
|
||||
dependencies = [
|
||||
"reth-errors",
|
||||
"reth-primitives",
|
||||
"reth-rpc-types",
|
||||
"reth-transaction-pool",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-payload-validator"
|
||||
version = "0.2.0-beta.9"
|
||||
@ -7715,6 +7732,7 @@ dependencies = [
|
||||
"reth-evm",
|
||||
"reth-metrics",
|
||||
"reth-payload-builder",
|
||||
"reth-payload-primitives",
|
||||
"reth-primitives",
|
||||
"reth-provider",
|
||||
"reth-rpc-api",
|
||||
|
||||
@ -50,6 +50,7 @@ members = [
|
||||
"crates/payload/basic/",
|
||||
"crates/payload/builder/",
|
||||
"crates/payload/ethereum/",
|
||||
"crates/payload/primitives/",
|
||||
"crates/payload/validator/",
|
||||
"crates/primitives/",
|
||||
"crates/prune/",
|
||||
@ -104,7 +105,7 @@ members = [
|
||||
"examples/rpc-db/",
|
||||
"examples/txpool-tracing/",
|
||||
"testing/ef-tests/",
|
||||
"testing/testing-utils",
|
||||
"testing/testing-utils"
|
||||
]
|
||||
default-members = ["bin/reth"]
|
||||
|
||||
@ -290,6 +291,7 @@ reth-optimism-consensus = { path = "crates/optimism/consensus" }
|
||||
reth-optimism-payload-builder = { path = "crates/optimism/payload" }
|
||||
reth-optimism-primitives = { path = "crates/optimism/primitives" }
|
||||
reth-payload-builder = { path = "crates/payload/builder" }
|
||||
reth-payload-primitives = { path = "crates/payload/primitives" }
|
||||
reth-payload-validator = { path = "crates/payload/validator" }
|
||||
reth-primitives = { path = "crates/primitives" }
|
||||
reth-provider = { path = "crates/storage/provider" }
|
||||
|
||||
@ -44,6 +44,7 @@ reth-tracing.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-ethereum-payload-builder.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-payload-validator.workspace = true
|
||||
reth-basic-payload-builder.workspace = true
|
||||
reth-discv4.workspace = true
|
||||
|
||||
@ -37,6 +37,7 @@ pub mod utils;
|
||||
/// Re-exported payload related types
|
||||
pub mod payload {
|
||||
pub use reth_payload_builder::*;
|
||||
pub use reth_payload_primitives::*;
|
||||
pub use reth_payload_validator::ExecutionPayloadValidator;
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ reth-provider.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-payload-validator.workspace = true
|
||||
reth-prune.workspace = true
|
||||
reth-static-file.workspace = true
|
||||
|
||||
@ -5,7 +5,7 @@ use reth_blockchain_tree_api::{
|
||||
BlockStatus, BlockValidationKind, BlockchainTreeEngine, CanonicalOutcome, InsertPayloadOk,
|
||||
};
|
||||
use reth_db_api::database::Database;
|
||||
use reth_engine_primitives::{EngineTypes, PayloadAttributes, PayloadBuilderAttributes};
|
||||
use reth_engine_primitives::EngineTypes;
|
||||
use reth_errors::{BlockValidationError, ProviderResult, RethError, RethResult};
|
||||
use reth_network_p2p::{
|
||||
bodies::client::BodiesClient,
|
||||
@ -13,6 +13,7 @@ use reth_network_p2p::{
|
||||
sync::{NetworkSyncUpdater, SyncState},
|
||||
};
|
||||
use reth_payload_builder::PayloadBuilderHandle;
|
||||
use reth_payload_primitives::{PayloadAttributes, PayloadBuilderAttributes};
|
||||
use reth_payload_validator::ExecutionPayloadValidator;
|
||||
use reth_primitives::{
|
||||
constants::EPOCH_SLOTS,
|
||||
|
||||
@ -13,8 +13,7 @@ workspace = true
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-primitives.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
|
||||
# misc
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@ -11,20 +11,12 @@
|
||||
use core::fmt;
|
||||
use reth_primitives::ChainSpec;
|
||||
|
||||
/// 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 reth_payload_primitives::{
|
||||
BuiltPayload, EngineApiMessageVersion, EngineObjectValidationError, PayloadAttributes,
|
||||
PayloadBuilderAttributes, PayloadOrAttributes,
|
||||
};
|
||||
|
||||
use serde::{de::DeserializeOwned, ser::Serialize};
|
||||
pub use traits::{BuiltPayload, PayloadAttributes, PayloadBuilderAttributes};
|
||||
|
||||
/// Contains error types used in the traits defined in this crate.
|
||||
pub mod error;
|
||||
pub use error::{EngineObjectValidationError, VersionSpecificValidationError};
|
||||
|
||||
/// Contains types used in implementations of the [`PayloadAttributes`] trait.
|
||||
pub mod payload;
|
||||
pub use payload::PayloadOrAttributes;
|
||||
|
||||
/// The types that are used by the engine API.
|
||||
pub trait EngineTypes:
|
||||
DeserializeOwned + Serialize + fmt::Debug + Unpin + Send + Sync + Clone
|
||||
@ -63,313 +55,3 @@ pub trait EngineTypes:
|
||||
payload_or_attrs: PayloadOrAttributes<'_, Self::PayloadAttributes>,
|
||||
) -> Result<(), EngineObjectValidationError>;
|
||||
}
|
||||
|
||||
/// Validates the timestamp depending on the version called:
|
||||
///
|
||||
/// * If V2, this ensures that the payload timestamp is pre-Cancun.
|
||||
/// * If V3, this ensures that the payload timestamp is within the Cancun timestamp.
|
||||
/// * If V4, this ensures that the payload timestamp is within the Prague timestamp.
|
||||
///
|
||||
/// Otherwise, this will return [`EngineObjectValidationError::UnsupportedFork`].
|
||||
pub fn validate_payload_timestamp(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
let is_cancun = chain_spec.is_cancun_active_at_timestamp(timestamp);
|
||||
if version == EngineApiMessageVersion::V2 && is_cancun {
|
||||
// From the Engine API spec:
|
||||
//
|
||||
// ### Update the methods of previous forks
|
||||
//
|
||||
// This document defines how Cancun payload should be handled by the [`Shanghai
|
||||
// API`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md).
|
||||
//
|
||||
// For the following methods:
|
||||
//
|
||||
// - [`engine_forkchoiceUpdatedV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_forkchoiceupdatedv2)
|
||||
// - [`engine_newPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_newpayloadV2)
|
||||
// - [`engine_getPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_getpayloadv2)
|
||||
//
|
||||
// a validation **MUST** be added:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// payload or payloadAttributes is greater or equal to the Cancun activation timestamp.
|
||||
return Err(EngineObjectValidationError::UnsupportedFork)
|
||||
}
|
||||
|
||||
if version == EngineApiMessageVersion::V3 && !is_cancun {
|
||||
// From the Engine API spec:
|
||||
// <https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/cancun.md#specification-2>
|
||||
//
|
||||
// For `engine_getPayloadV3`:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the built payload does not fall within the time frame of the Cancun fork.
|
||||
//
|
||||
// For `engine_forkchoiceUpdatedV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within
|
||||
// the time frame of the Cancun fork.
|
||||
//
|
||||
// For `engine_newPayloadV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the payload does not fall within the time frame of the Cancun fork.
|
||||
return Err(EngineObjectValidationError::UnsupportedFork)
|
||||
}
|
||||
|
||||
let is_prague = chain_spec.is_prague_active_at_timestamp(timestamp);
|
||||
if version == EngineApiMessageVersion::V4 && !is_prague {
|
||||
// From the Engine API spec:
|
||||
// <https://github.com/ethereum/execution-apis/blob/7907424db935b93c2fe6a3c0faab943adebe8557/src/engine/prague.md#specification-1>
|
||||
//
|
||||
// For `engine_getPayloadV4`:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the built payload does not fall within the time frame of the Prague fork.
|
||||
//
|
||||
// For `engine_forkchoiceUpdatedV4`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within
|
||||
// the time frame of the Prague fork.
|
||||
//
|
||||
// For `engine_newPayloadV4`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the payload does not fall within the time frame of the Prague fork.
|
||||
return Err(EngineObjectValidationError::UnsupportedFork)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates the presence of the `withdrawals` field according to the payload timestamp.
|
||||
/// After Shanghai, withdrawals field must be [Some].
|
||||
/// Before Shanghai, withdrawals field must be [None];
|
||||
pub fn validate_withdrawals_presence(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
message_validation_kind: MessageValidationKind,
|
||||
timestamp: u64,
|
||||
has_withdrawals: bool,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
let is_shanghai_active = chain_spec.is_shanghai_active_at_timestamp(timestamp);
|
||||
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 => {
|
||||
if has_withdrawals {
|
||||
return Err(message_validation_kind
|
||||
.to_error(VersionSpecificValidationError::WithdrawalsNotSupportedInV1))
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V2 | EngineApiMessageVersion::V3 | EngineApiMessageVersion::V4 => {
|
||||
if is_shanghai_active && !has_withdrawals {
|
||||
return Err(message_validation_kind
|
||||
.to_error(VersionSpecificValidationError::NoWithdrawalsPostShanghai))
|
||||
}
|
||||
if !is_shanghai_active && has_withdrawals {
|
||||
return Err(message_validation_kind
|
||||
.to_error(VersionSpecificValidationError::HasWithdrawalsPreShanghai))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate the presence of the `parentBeaconBlockRoot` field according to the given timestamp.
|
||||
/// This method is meant to be used with either a `payloadAttributes` field or a full payload, with
|
||||
/// the `engine_forkchoiceUpdated` and `engine_newPayload` methods respectively.
|
||||
///
|
||||
/// After Cancun, the `parentBeaconBlockRoot` field must be [Some].
|
||||
/// Before Cancun, the `parentBeaconBlockRoot` field must be [None].
|
||||
///
|
||||
/// If the engine API message version is V1 or V2, and the timestamp is post-Cancun, then this will
|
||||
/// return [`EngineObjectValidationError::UnsupportedFork`].
|
||||
///
|
||||
/// If the timestamp is before the Cancun fork and the engine API message version is V3, then this
|
||||
/// will return [`EngineObjectValidationError::UnsupportedFork`].
|
||||
///
|
||||
/// If the engine API message version is V3, but the `parentBeaconBlockRoot` is [None], then
|
||||
/// this will return [`VersionSpecificValidationError::NoParentBeaconBlockRootPostCancun`].
|
||||
///
|
||||
/// This implements the following Engine API spec rules:
|
||||
///
|
||||
/// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
/// matches the expected one and return `-32602: Invalid params` error if this check fails. Any
|
||||
/// field having `null` value **MUST** be considered as not provided.
|
||||
///
|
||||
/// For `engine_forkchoiceUpdatedV3`:
|
||||
///
|
||||
/// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
/// matches the expected one and return `-32602: Invalid params` error if this check fails. Any
|
||||
/// field having `null` value **MUST** be considered as not provided.
|
||||
///
|
||||
/// 2. Extend point (7) of the `engine_forkchoiceUpdatedV1` specification by defining the following
|
||||
/// sequence of checks that **MUST** be run over `payloadAttributes`:
|
||||
/// 1. `payloadAttributes` matches the `PayloadAttributesV3` structure, return `-38003: Invalid
|
||||
/// payload attributes` on failure.
|
||||
/// 2. `payloadAttributes.timestamp` falls within the time frame of the Cancun fork, return
|
||||
/// `-38005: Unsupported fork` on failure.
|
||||
/// 3. `payloadAttributes.timestamp` is greater than `timestamp` of a block referenced by
|
||||
/// `forkchoiceState.headBlockHash`, return `-38003: Invalid payload attributes` on failure.
|
||||
/// 4. If any of the above checks fails, the `forkchoiceState` update **MUST NOT** be rolled
|
||||
/// back.
|
||||
///
|
||||
/// For `engine_newPayloadV3`:
|
||||
///
|
||||
/// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the
|
||||
/// payload does not fall within the time frame of the Cancun fork.
|
||||
///
|
||||
/// For `engine_newPayloadV4`:
|
||||
///
|
||||
/// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the
|
||||
/// payload does not fall within the time frame of the Prague fork.
|
||||
///
|
||||
/// Returning the right error code (ie, if the client should return `-38003: Invalid payload
|
||||
/// attributes` is handled by the `message_validation_kind` parameter. If the parameter is
|
||||
/// `MessageValidationKind::Payload`, then the error code will be `-32602: Invalid params`. If the
|
||||
/// parameter is `MessageValidationKind::PayloadAttributes`, then the error code will be `-38003:
|
||||
/// Invalid payload attributes`.
|
||||
pub fn validate_parent_beacon_block_root_presence(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
validation_kind: MessageValidationKind,
|
||||
timestamp: u64,
|
||||
has_parent_beacon_block_root: bool,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
// matches the expected one and return `-32602: Invalid params` error if this check fails.
|
||||
// Any field having `null` value **MUST** be considered as not provided.
|
||||
//
|
||||
// For `engine_forkchoiceUpdatedV3`:
|
||||
//
|
||||
// 2. Extend point (7) of the `engine_forkchoiceUpdatedV1` specification by defining the
|
||||
// following sequence of checks that **MUST** be run over `payloadAttributes`:
|
||||
// 1. `payloadAttributes` matches the `PayloadAttributesV3` structure, return `-38003:
|
||||
// Invalid payload attributes` on failure.
|
||||
// 2. `payloadAttributes.timestamp` falls within the time frame of the Cancun fork, return
|
||||
// `-38005: Unsupported fork` on failure.
|
||||
// 3. `payloadAttributes.timestamp` is greater than `timestamp` of a block referenced by
|
||||
// `forkchoiceState.headBlockHash`, return `-38003: Invalid payload attributes` on
|
||||
// failure.
|
||||
// 4. If any of the above checks fails, the `forkchoiceState` update **MUST NOT** be rolled
|
||||
// back.
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 | EngineApiMessageVersion::V2 => {
|
||||
if has_parent_beacon_block_root {
|
||||
return Err(validation_kind.to_error(
|
||||
VersionSpecificValidationError::ParentBeaconBlockRootNotSupportedBeforeV3,
|
||||
))
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V3 | EngineApiMessageVersion::V4 => {
|
||||
if !has_parent_beacon_block_root {
|
||||
return Err(validation_kind
|
||||
.to_error(VersionSpecificValidationError::NoParentBeaconBlockRootPostCancun))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// For `engine_forkchoiceUpdatedV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within the
|
||||
// time frame of the Cancun fork.
|
||||
//
|
||||
// For `engine_newPayloadV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the
|
||||
// payload does not fall within the time frame of the Cancun fork.
|
||||
validate_payload_timestamp(chain_spec, version, timestamp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A type that represents whether or not we are validating a payload or payload attributes.
|
||||
///
|
||||
/// This is used to ensure that the correct error code is returned when validating the payload or
|
||||
/// payload attributes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MessageValidationKind {
|
||||
/// We are validating fields of a payload attributes.
|
||||
PayloadAttributes,
|
||||
/// We are validating fields of a payload.
|
||||
Payload,
|
||||
}
|
||||
|
||||
impl MessageValidationKind {
|
||||
/// Returns an `EngineObjectValidationError` based on the given
|
||||
/// `VersionSpecificValidationError` and the current validation kind.
|
||||
pub const fn to_error(
|
||||
self,
|
||||
error: VersionSpecificValidationError,
|
||||
) -> EngineObjectValidationError {
|
||||
match self {
|
||||
Self::Payload => EngineObjectValidationError::Payload(error),
|
||||
Self::PayloadAttributes => EngineObjectValidationError::PayloadAttributes(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the presence or exclusion of fork-specific fields based on the ethereum execution
|
||||
/// payload, or payload attributes, and the message version.
|
||||
///
|
||||
/// The object being validated is provided by the [`PayloadOrAttributes`] argument, which can be
|
||||
/// either an execution payload, or payload attributes.
|
||||
///
|
||||
/// The version is provided by the [`EngineApiMessageVersion`] argument.
|
||||
pub fn validate_version_specific_fields<Type>(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
payload_or_attrs: PayloadOrAttributes<'_, Type>,
|
||||
) -> Result<(), EngineObjectValidationError>
|
||||
where
|
||||
Type: PayloadAttributes,
|
||||
{
|
||||
validate_withdrawals_presence(
|
||||
chain_spec,
|
||||
version,
|
||||
payload_or_attrs.message_validation_kind(),
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.withdrawals().is_some(),
|
||||
)?;
|
||||
validate_parent_beacon_block_root_presence(
|
||||
chain_spec,
|
||||
version,
|
||||
payload_or_attrs.message_validation_kind(),
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.parent_beacon_block_root().is_some(),
|
||||
)
|
||||
}
|
||||
|
||||
/// The version of Engine API message.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum EngineApiMessageVersion {
|
||||
/// Version 1
|
||||
V1,
|
||||
/// Version 2
|
||||
///
|
||||
/// Added in the Shanghai hardfork.
|
||||
V2,
|
||||
/// Version 3
|
||||
///
|
||||
/// Added in the Cancun hardfork.
|
||||
V3,
|
||||
/// Version 4
|
||||
///
|
||||
/// Added in the Prague hardfork.
|
||||
V4,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn version_ord() {
|
||||
assert!(EngineApiMessageVersion::V4 > EngineApiMessageVersion::V3);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ workspace = true
|
||||
# reth
|
||||
reth-primitives.workspace = true
|
||||
reth-engine-primitives.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
revm-primitives.workspace = true
|
||||
|
||||
@ -11,9 +11,10 @@
|
||||
mod payload;
|
||||
pub use payload::{EthBuiltPayload, EthPayloadBuilderAttributes};
|
||||
|
||||
use reth_engine_primitives::{
|
||||
use reth_engine_primitives::EngineTypes;
|
||||
use reth_payload_primitives::{
|
||||
validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError,
|
||||
EngineTypes, PayloadOrAttributes,
|
||||
PayloadOrAttributes,
|
||||
};
|
||||
use reth_primitives::ChainSpec;
|
||||
use reth_rpc_types::{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! Contains types required for building a payload.
|
||||
|
||||
use alloy_rlp::Encodable;
|
||||
use reth_engine_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_primitives::{
|
||||
constants::EIP1559_INITIAL_BASE_FEE, revm::config::revm_spec_by_timestamp_after_merge, Address,
|
||||
BlobTransactionSidecar, ChainSpec, Hardfork, Header, SealedBlock, Withdrawals, B256, U256,
|
||||
|
||||
@ -19,4 +19,5 @@ reth-engine-primitives.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-network.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
|
||||
@ -12,6 +12,10 @@
|
||||
pub use reth_engine_primitives as engine;
|
||||
pub use reth_engine_primitives::*;
|
||||
|
||||
/// Traits and helper types used to abstract over payload types.
|
||||
pub use reth_payload_primitives as payload;
|
||||
pub use reth_payload_primitives::*;
|
||||
|
||||
/// Traits and helper types used to abstract over EVM methods and types.
|
||||
pub use reth_evm::{ConfigureEvm, ConfigureEvmEnv};
|
||||
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
use reth_node_api::{
|
||||
engine::validate_parent_beacon_block_root_presence, EngineApiMessageVersion,
|
||||
EngineObjectValidationError, EngineTypes, MessageValidationKind, PayloadOrAttributes,
|
||||
payload::{
|
||||
validate_parent_beacon_block_root_presence, EngineApiMessageVersion,
|
||||
EngineObjectValidationError, MessageValidationKind, PayloadOrAttributes,
|
||||
VersionSpecificValidationError,
|
||||
},
|
||||
EngineTypes,
|
||||
};
|
||||
use reth_optimism_payload_builder::{OptimismBuiltPayload, OptimismPayloadBuilderAttributes};
|
||||
use reth_primitives::{ChainSpec, Hardfork};
|
||||
|
||||
@ -19,10 +19,10 @@ reth-transaction-pool.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
reth-engine-primitives.workspace = true
|
||||
reth-evm.workspace = true
|
||||
reth-evm-optimism.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-basic-payload-builder.workspace = true
|
||||
|
||||
# ethereum
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
//! Optimism builder support
|
||||
|
||||
use alloy_rlp::Encodable;
|
||||
use reth_engine_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_payload_builder::EthPayloadBuilderAttributes;
|
||||
use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_primitives::{
|
||||
revm::config::revm_spec_by_timestamp_after_merge,
|
||||
revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, SpecId},
|
||||
|
||||
@ -18,8 +18,8 @@ reth-revm.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-engine-primitives.workspace = true
|
||||
|
||||
# ethereum
|
||||
alloy-rlp.workspace = true
|
||||
|
||||
@ -11,11 +11,11 @@
|
||||
use crate::metrics::PayloadBuilderMetrics;
|
||||
use futures_core::ready;
|
||||
use futures_util::FutureExt;
|
||||
use reth_engine_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_payload_builder::{
|
||||
database::CachedReads, error::PayloadBuilderError, KeepPayloadJobAlive, PayloadId, PayloadJob,
|
||||
PayloadJobGenerator,
|
||||
};
|
||||
use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_primitives::{
|
||||
constants::{EMPTY_WITHDRAWALS, RETH_CLIENT_VERSION, SLOT_DURATION},
|
||||
proofs, BlockNumberOrTag, Bytes, ChainSpec, Request, SealedBlock, Withdrawals, B256, U256,
|
||||
|
||||
@ -19,6 +19,7 @@ reth-transaction-pool.workspace = true
|
||||
reth-errors.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-engine-primitives.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-ethereum-engine-primitives.workspace = true
|
||||
|
||||
# async
|
||||
|
||||
@ -2,7 +2,8 @@
|
||||
|
||||
use crate::{service::PayloadServiceCommand, PayloadBuilderHandle};
|
||||
use futures_util::{ready, StreamExt};
|
||||
use reth_engine_primitives::{EngineTypes, PayloadBuilderAttributes};
|
||||
use reth_engine_primitives::EngineTypes;
|
||||
use reth_payload_primitives::PayloadBuilderAttributes;
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
|
||||
@ -11,7 +11,8 @@ use crate::{
|
||||
KeepPayloadJobAlive, PayloadJob,
|
||||
};
|
||||
use futures_util::{future::FutureExt, Stream, StreamExt};
|
||||
use reth_engine_primitives::{BuiltPayload, EngineTypes, PayloadBuilderAttributes};
|
||||
use reth_engine_primitives::EngineTypes;
|
||||
use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_provider::CanonStateNotification;
|
||||
use reth_rpc_types::engine::PayloadId;
|
||||
use std::{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
//! Trait abstractions used by the payload crate.
|
||||
|
||||
use crate::error::PayloadBuilderError;
|
||||
use reth_engine_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
||||
use reth_provider::CanonStateNotification;
|
||||
use std::future::Future;
|
||||
|
||||
|
||||
26
crates/payload/primitives/Cargo.toml
Normal file
26
crates/payload/primitives/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "reth-payload-primitives"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
exclude.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-errors.workspace = true
|
||||
reth-primitives.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
reth-rpc-types.workspace = true
|
||||
|
||||
# async
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
|
||||
# misc
|
||||
thiserror.workspace = true
|
||||
serde.workspace = true
|
||||
117
crates/payload/primitives/src/error.rs
Normal file
117
crates/payload/primitives/src/error.rs
Normal file
@ -0,0 +1,117 @@
|
||||
//! Error types emitted by types or implementations of this crate.
|
||||
|
||||
use reth_errors::{ProviderError, RethError};
|
||||
use reth_primitives::{revm_primitives::EVMError, B256};
|
||||
use reth_transaction_pool::BlobStoreError;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
/// Possible error variants during payload building.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PayloadBuilderError {
|
||||
/// Thrown when the parent block is missing.
|
||||
#[error("missing parent block {0}")]
|
||||
MissingParentBlock(B256),
|
||||
/// An oneshot channels has been closed.
|
||||
#[error("sender has been dropped")]
|
||||
ChannelClosed,
|
||||
/// Error occurring in the blob store.
|
||||
#[error(transparent)]
|
||||
BlobStore(#[from] BlobStoreError),
|
||||
/// Other internal error
|
||||
#[error(transparent)]
|
||||
Internal(#[from] RethError),
|
||||
/// Unrecoverable error during evm execution.
|
||||
#[error("evm execution error: {0}")]
|
||||
EvmExecutionError(EVMError<ProviderError>),
|
||||
/// Thrown if the payload requests withdrawals before Shanghai activation.
|
||||
#[error("withdrawals set before Shanghai activation")]
|
||||
WithdrawalsBeforeShanghai,
|
||||
/// Any other payload building errors.
|
||||
#[error(transparent)]
|
||||
Other(Box<dyn std::error::Error + Send + Sync>),
|
||||
}
|
||||
|
||||
impl PayloadBuilderError {
|
||||
/// Create a new error from a boxed error.
|
||||
pub fn other<E>(error: E) -> Self
|
||||
where
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
Self::Other(Box::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProviderError> for PayloadBuilderError {
|
||||
fn from(error: ProviderError) -> Self {
|
||||
Self::Internal(RethError::Provider(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<oneshot::error::RecvError> for PayloadBuilderError {
|
||||
fn from(_: oneshot::error::RecvError) -> Self {
|
||||
Self::ChannelClosed
|
||||
}
|
||||
}
|
||||
|
||||
/// Thrown when the payload or attributes are known to be invalid before processing.
|
||||
///
|
||||
/// This is used mainly for
|
||||
/// [`validate_version_specific_fields`](crate::validate_version_specific_fields), which validates
|
||||
/// both execution payloads and forkchoice update attributes with respect to a method version.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum EngineObjectValidationError {
|
||||
/// Thrown when the underlying validation error occurred while validating an
|
||||
/// `ExecutionPayload`.
|
||||
#[error("Payload validation error: {0}")]
|
||||
Payload(VersionSpecificValidationError),
|
||||
|
||||
/// Thrown when the underlying validation error occurred while validating a
|
||||
/// `PayloadAttributes`.
|
||||
#[error("Payload attributes validation error: {0}")]
|
||||
PayloadAttributes(VersionSpecificValidationError),
|
||||
|
||||
/// Thrown if `PayloadAttributes` or `ExecutionPayload` were provided with a timestamp, but the
|
||||
/// version of the engine method called is meant for a fork that occurs after the provided
|
||||
/// timestamp.
|
||||
#[error("Unsupported fork")]
|
||||
UnsupportedFork,
|
||||
/// Another type of error that is not covered by the above variants.
|
||||
#[error("Invalid params: {0}")]
|
||||
InvalidParams(#[from] Box<dyn std::error::Error + Send + Sync>),
|
||||
}
|
||||
|
||||
/// Thrown when validating an execution payload OR payload attributes fails due to:
|
||||
/// * The existence of a new field that is not supported in the given engine method version, or
|
||||
/// * The absence of a field that is required in the given engine method version
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum VersionSpecificValidationError {
|
||||
/// Thrown if the pre-V3 `PayloadAttributes` or `ExecutionPayload` contains a parent beacon
|
||||
/// block root
|
||||
#[error("parent beacon block root not supported before V3")]
|
||||
ParentBeaconBlockRootNotSupportedBeforeV3,
|
||||
/// Thrown if `engine_forkchoiceUpdatedV1` or `engine_newPayloadV1` contains withdrawals
|
||||
#[error("withdrawals not supported in V1")]
|
||||
WithdrawalsNotSupportedInV1,
|
||||
/// Thrown if `engine_forkchoiceUpdated` or `engine_newPayload` contains no withdrawals after
|
||||
/// Shanghai
|
||||
#[error("no withdrawals post-Shanghai")]
|
||||
NoWithdrawalsPostShanghai,
|
||||
/// Thrown if `engine_forkchoiceUpdated` or `engine_newPayload` contains withdrawals before
|
||||
/// Shanghai
|
||||
#[error("withdrawals pre-Shanghai")]
|
||||
HasWithdrawalsPreShanghai,
|
||||
/// Thrown if the `PayloadAttributes` or `ExecutionPayload` contains no parent beacon block
|
||||
/// root after Cancun
|
||||
#[error("no parent beacon block root post-cancun")]
|
||||
NoParentBeaconBlockRootPostCancun,
|
||||
}
|
||||
|
||||
impl EngineObjectValidationError {
|
||||
/// Creates an instance of the `InvalidParams` variant with the given error.
|
||||
pub fn invalid_params<E>(error: E) -> Self
|
||||
where
|
||||
E: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
Self::InvalidParams(Box::new(error))
|
||||
}
|
||||
}
|
||||
333
crates/payload/primitives/src/lib.rs
Normal file
333
crates/payload/primitives/src/lib.rs
Normal file
@ -0,0 +1,333 @@
|
||||
//! This crate defines abstractions to create and update payloads (blocks)
|
||||
|
||||
#![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(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
mod error;
|
||||
pub use error::{EngineObjectValidationError, PayloadBuilderError, VersionSpecificValidationError};
|
||||
|
||||
/// Contains traits to abstract over payload attributes types and default implementations of the
|
||||
/// [`PayloadAttributes`] trait for ethereum mainnet and optimism types.
|
||||
mod traits;
|
||||
pub use traits::{BuiltPayload, PayloadAttributes, PayloadBuilderAttributes};
|
||||
|
||||
mod payload;
|
||||
pub use payload::PayloadOrAttributes;
|
||||
|
||||
use reth_primitives::ChainSpec;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Validates the timestamp depending on the version called:
|
||||
///
|
||||
/// * If V2, this ensures that the payload timestamp is pre-Cancun.
|
||||
/// * If V3, this ensures that the payload timestamp is within the Cancun timestamp.
|
||||
/// * If V4, this ensures that the payload timestamp is within the Prague timestamp.
|
||||
///
|
||||
/// Otherwise, this will return [`EngineObjectValidationError::UnsupportedFork`].
|
||||
pub fn validate_payload_timestamp(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
timestamp: u64,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
let is_cancun = chain_spec.is_cancun_active_at_timestamp(timestamp);
|
||||
if version == EngineApiMessageVersion::V2 && is_cancun {
|
||||
// From the Engine API spec:
|
||||
//
|
||||
// ### Update the methods of previous forks
|
||||
//
|
||||
// This document defines how Cancun payload should be handled by the [`Shanghai
|
||||
// API`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md).
|
||||
//
|
||||
// For the following methods:
|
||||
//
|
||||
// - [`engine_forkchoiceUpdatedV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_forkchoiceupdatedv2)
|
||||
// - [`engine_newPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_newpayloadV2)
|
||||
// - [`engine_getPayloadV2`](https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/shanghai.md#engine_getpayloadv2)
|
||||
//
|
||||
// a validation **MUST** be added:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// payload or payloadAttributes is greater or equal to the Cancun activation timestamp.
|
||||
return Err(EngineObjectValidationError::UnsupportedFork)
|
||||
}
|
||||
|
||||
if version == EngineApiMessageVersion::V3 && !is_cancun {
|
||||
// From the Engine API spec:
|
||||
// <https://github.com/ethereum/execution-apis/blob/ff43500e653abde45aec0f545564abfb648317af/src/engine/cancun.md#specification-2>
|
||||
//
|
||||
// For `engine_getPayloadV3`:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the built payload does not fall within the time frame of the Cancun fork.
|
||||
//
|
||||
// For `engine_forkchoiceUpdatedV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within
|
||||
// the time frame of the Cancun fork.
|
||||
//
|
||||
// For `engine_newPayloadV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the payload does not fall within the time frame of the Cancun fork.
|
||||
return Err(EngineObjectValidationError::UnsupportedFork)
|
||||
}
|
||||
|
||||
let is_prague = chain_spec.is_prague_active_at_timestamp(timestamp);
|
||||
if version == EngineApiMessageVersion::V4 && !is_prague {
|
||||
// From the Engine API spec:
|
||||
// <https://github.com/ethereum/execution-apis/blob/7907424db935b93c2fe6a3c0faab943adebe8557/src/engine/prague.md#specification-1>
|
||||
//
|
||||
// For `engine_getPayloadV4`:
|
||||
//
|
||||
// 1. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the built payload does not fall within the time frame of the Prague fork.
|
||||
//
|
||||
// For `engine_forkchoiceUpdatedV4`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within
|
||||
// the time frame of the Prague fork.
|
||||
//
|
||||
// For `engine_newPayloadV4`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of
|
||||
// the payload does not fall within the time frame of the Prague fork.
|
||||
return Err(EngineObjectValidationError::UnsupportedFork)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates the presence of the `withdrawals` field according to the payload timestamp.
|
||||
/// After Shanghai, withdrawals field must be [Some].
|
||||
/// Before Shanghai, withdrawals field must be [None];
|
||||
pub fn validate_withdrawals_presence(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
message_validation_kind: MessageValidationKind,
|
||||
timestamp: u64,
|
||||
has_withdrawals: bool,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
let is_shanghai_active = chain_spec.is_shanghai_active_at_timestamp(timestamp);
|
||||
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 => {
|
||||
if has_withdrawals {
|
||||
return Err(message_validation_kind
|
||||
.to_error(VersionSpecificValidationError::WithdrawalsNotSupportedInV1))
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V2 | EngineApiMessageVersion::V3 | EngineApiMessageVersion::V4 => {
|
||||
if is_shanghai_active && !has_withdrawals {
|
||||
return Err(message_validation_kind
|
||||
.to_error(VersionSpecificValidationError::NoWithdrawalsPostShanghai))
|
||||
}
|
||||
if !is_shanghai_active && has_withdrawals {
|
||||
return Err(message_validation_kind
|
||||
.to_error(VersionSpecificValidationError::HasWithdrawalsPreShanghai))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate the presence of the `parentBeaconBlockRoot` field according to the given timestamp.
|
||||
/// This method is meant to be used with either a `payloadAttributes` field or a full payload, with
|
||||
/// the `engine_forkchoiceUpdated` and `engine_newPayload` methods respectively.
|
||||
///
|
||||
/// After Cancun, the `parentBeaconBlockRoot` field must be [Some].
|
||||
/// Before Cancun, the `parentBeaconBlockRoot` field must be [None].
|
||||
///
|
||||
/// If the engine API message version is V1 or V2, and the timestamp is post-Cancun, then this will
|
||||
/// return [`EngineObjectValidationError::UnsupportedFork`].
|
||||
///
|
||||
/// If the timestamp is before the Cancun fork and the engine API message version is V3, then this
|
||||
/// will return [`EngineObjectValidationError::UnsupportedFork`].
|
||||
///
|
||||
/// If the engine API message version is V3, but the `parentBeaconBlockRoot` is [None], then
|
||||
/// this will return [`VersionSpecificValidationError::NoParentBeaconBlockRootPostCancun`].
|
||||
///
|
||||
/// This implements the following Engine API spec rules:
|
||||
///
|
||||
/// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
/// matches the expected one and return `-32602: Invalid params` error if this check fails. Any
|
||||
/// field having `null` value **MUST** be considered as not provided.
|
||||
///
|
||||
/// For `engine_forkchoiceUpdatedV3`:
|
||||
///
|
||||
/// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
/// matches the expected one and return `-32602: Invalid params` error if this check fails. Any
|
||||
/// field having `null` value **MUST** be considered as not provided.
|
||||
///
|
||||
/// 2. Extend point (7) of the `engine_forkchoiceUpdatedV1` specification by defining the following
|
||||
/// sequence of checks that **MUST** be run over `payloadAttributes`:
|
||||
/// 1. `payloadAttributes` matches the `PayloadAttributesV3` structure, return `-38003: Invalid
|
||||
/// payload attributes` on failure.
|
||||
/// 2. `payloadAttributes.timestamp` falls within the time frame of the Cancun fork, return
|
||||
/// `-38005: Unsupported fork` on failure.
|
||||
/// 3. `payloadAttributes.timestamp` is greater than `timestamp` of a block referenced by
|
||||
/// `forkchoiceState.headBlockHash`, return `-38003: Invalid payload attributes` on failure.
|
||||
/// 4. If any of the above checks fails, the `forkchoiceState` update **MUST NOT** be rolled
|
||||
/// back.
|
||||
///
|
||||
/// For `engine_newPayloadV3`:
|
||||
///
|
||||
/// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the
|
||||
/// payload does not fall within the time frame of the Cancun fork.
|
||||
///
|
||||
/// For `engine_newPayloadV4`:
|
||||
///
|
||||
/// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the
|
||||
/// payload does not fall within the time frame of the Prague fork.
|
||||
///
|
||||
/// Returning the right error code (ie, if the client should return `-38003: Invalid payload
|
||||
/// attributes` is handled by the `message_validation_kind` parameter. If the parameter is
|
||||
/// `MessageValidationKind::Payload`, then the error code will be `-32602: Invalid params`. If the
|
||||
/// parameter is `MessageValidationKind::PayloadAttributes`, then the error code will be `-38003:
|
||||
/// Invalid payload attributes`.
|
||||
pub fn validate_parent_beacon_block_root_presence(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
validation_kind: MessageValidationKind,
|
||||
timestamp: u64,
|
||||
has_parent_beacon_block_root: bool,
|
||||
) -> Result<(), EngineObjectValidationError> {
|
||||
// 1. Client software **MUST** check that provided set of parameters and their fields strictly
|
||||
// matches the expected one and return `-32602: Invalid params` error if this check fails.
|
||||
// Any field having `null` value **MUST** be considered as not provided.
|
||||
//
|
||||
// For `engine_forkchoiceUpdatedV3`:
|
||||
//
|
||||
// 2. Extend point (7) of the `engine_forkchoiceUpdatedV1` specification by defining the
|
||||
// following sequence of checks that **MUST** be run over `payloadAttributes`:
|
||||
// 1. `payloadAttributes` matches the `PayloadAttributesV3` structure, return `-38003:
|
||||
// Invalid payload attributes` on failure.
|
||||
// 2. `payloadAttributes.timestamp` falls within the time frame of the Cancun fork, return
|
||||
// `-38005: Unsupported fork` on failure.
|
||||
// 3. `payloadAttributes.timestamp` is greater than `timestamp` of a block referenced by
|
||||
// `forkchoiceState.headBlockHash`, return `-38003: Invalid payload attributes` on
|
||||
// failure.
|
||||
// 4. If any of the above checks fails, the `forkchoiceState` update **MUST NOT** be rolled
|
||||
// back.
|
||||
match version {
|
||||
EngineApiMessageVersion::V1 | EngineApiMessageVersion::V2 => {
|
||||
if has_parent_beacon_block_root {
|
||||
return Err(validation_kind.to_error(
|
||||
VersionSpecificValidationError::ParentBeaconBlockRootNotSupportedBeforeV3,
|
||||
))
|
||||
}
|
||||
}
|
||||
EngineApiMessageVersion::V3 | EngineApiMessageVersion::V4 => {
|
||||
if !has_parent_beacon_block_root {
|
||||
return Err(validation_kind
|
||||
.to_error(VersionSpecificValidationError::NoParentBeaconBlockRootPostCancun))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// For `engine_forkchoiceUpdatedV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the
|
||||
// `payloadAttributes` is set and the `payloadAttributes.timestamp` does not fall within the
|
||||
// time frame of the Cancun fork.
|
||||
//
|
||||
// For `engine_newPayloadV3`:
|
||||
//
|
||||
// 2. Client software **MUST** return `-38005: Unsupported fork` error if the `timestamp` of the
|
||||
// payload does not fall within the time frame of the Cancun fork.
|
||||
validate_payload_timestamp(chain_spec, version, timestamp)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A type that represents whether or not we are validating a payload or payload attributes.
|
||||
///
|
||||
/// This is used to ensure that the correct error code is returned when validating the payload or
|
||||
/// payload attributes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MessageValidationKind {
|
||||
/// We are validating fields of a payload attributes.
|
||||
PayloadAttributes,
|
||||
/// We are validating fields of a payload.
|
||||
Payload,
|
||||
}
|
||||
|
||||
impl MessageValidationKind {
|
||||
/// Returns an `EngineObjectValidationError` based on the given
|
||||
/// `VersionSpecificValidationError` and the current validation kind.
|
||||
pub const fn to_error(
|
||||
self,
|
||||
error: VersionSpecificValidationError,
|
||||
) -> EngineObjectValidationError {
|
||||
match self {
|
||||
Self::Payload => EngineObjectValidationError::Payload(error),
|
||||
Self::PayloadAttributes => EngineObjectValidationError::PayloadAttributes(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates the presence or exclusion of fork-specific fields based on the ethereum execution
|
||||
/// payload, or payload attributes, and the message version.
|
||||
///
|
||||
/// The object being validated is provided by the [`PayloadOrAttributes`] argument, which can be
|
||||
/// either an execution payload, or payload attributes.
|
||||
///
|
||||
/// The version is provided by the [`EngineApiMessageVersion`] argument.
|
||||
pub fn validate_version_specific_fields<Type>(
|
||||
chain_spec: &ChainSpec,
|
||||
version: EngineApiMessageVersion,
|
||||
payload_or_attrs: PayloadOrAttributes<'_, Type>,
|
||||
) -> Result<(), EngineObjectValidationError>
|
||||
where
|
||||
Type: PayloadAttributes,
|
||||
{
|
||||
validate_withdrawals_presence(
|
||||
chain_spec,
|
||||
version,
|
||||
payload_or_attrs.message_validation_kind(),
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.withdrawals().is_some(),
|
||||
)?;
|
||||
validate_parent_beacon_block_root_presence(
|
||||
chain_spec,
|
||||
version,
|
||||
payload_or_attrs.message_validation_kind(),
|
||||
payload_or_attrs.timestamp(),
|
||||
payload_or_attrs.parent_beacon_block_root().is_some(),
|
||||
)
|
||||
}
|
||||
|
||||
/// The version of Engine API message.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum EngineApiMessageVersion {
|
||||
/// Version 1
|
||||
V1,
|
||||
/// Version 2
|
||||
///
|
||||
/// Added in the Shanghai hardfork.
|
||||
V2,
|
||||
/// Version 3
|
||||
///
|
||||
/// Added in the Cancun hardfork.
|
||||
V3,
|
||||
/// Version 4
|
||||
///
|
||||
/// Added in the Prague hardfork.
|
||||
V4,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn version_ord() {
|
||||
assert!(EngineApiMessageVersion::V4 > EngineApiMessageVersion::V3);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
use crate::PayloadAttributes;
|
||||
use crate::{MessageValidationKind, PayloadAttributes};
|
||||
use reth_primitives::B256;
|
||||
use reth_rpc_types::engine::ExecutionPayload;
|
||||
|
||||
use super::MessageValidationKind;
|
||||
|
||||
/// Either an [`ExecutionPayload`] or a types that implements the [`PayloadAttributes`] trait.
|
||||
#[derive(Debug)]
|
||||
pub enum PayloadOrAttributes<'a, AttributesType> {
|
||||
@ -1,6 +1,3 @@
|
||||
use crate::{
|
||||
validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError,
|
||||
};
|
||||
use reth_primitives::{
|
||||
revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg},
|
||||
Address, ChainSpec, Header, SealedBlock, Withdrawals, B256, U256,
|
||||
@ -10,6 +7,10 @@ use reth_rpc_types::{
|
||||
Withdrawal,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError,
|
||||
};
|
||||
|
||||
/// Represents a built payload type that contains a built [`SealedBlock`] and can be converted into
|
||||
/// engine API execution payloads.
|
||||
pub trait BuiltPayload: Send + Sync + std::fmt::Debug {
|
||||
@ -19,6 +19,7 @@ reth-rpc-types.workspace = true
|
||||
reth-storage-api.workspace = true
|
||||
reth-beacon-consensus.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-rpc-types-compat.workspace = true
|
||||
reth-engine-primitives.workspace = true
|
||||
|
||||
@ -2,12 +2,13 @@ use crate::{metrics::EngineApiMetrics, EngineApiError, EngineApiResult};
|
||||
use async_trait::async_trait;
|
||||
use jsonrpsee_core::RpcResult;
|
||||
use reth_beacon_consensus::BeaconConsensusEngineHandle;
|
||||
use reth_engine_primitives::{
|
||||
validate_payload_timestamp, EngineApiMessageVersion, EngineTypes, PayloadAttributes,
|
||||
PayloadBuilderAttributes, PayloadOrAttributes,
|
||||
};
|
||||
use reth_engine_primitives::EngineTypes;
|
||||
use reth_evm::provider::EvmEnvProvider;
|
||||
use reth_payload_builder::PayloadStore;
|
||||
use reth_payload_primitives::{
|
||||
validate_payload_timestamp, EngineApiMessageVersion, PayloadAttributes,
|
||||
PayloadBuilderAttributes, PayloadOrAttributes,
|
||||
};
|
||||
use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, B256, U64};
|
||||
use reth_rpc_api::EngineApiServer;
|
||||
use reth_rpc_types::engine::{
|
||||
|
||||
@ -2,8 +2,8 @@ use jsonrpsee_types::error::{
|
||||
INTERNAL_ERROR_CODE, INVALID_PARAMS_CODE, INVALID_PARAMS_MSG, SERVER_ERROR_MSG,
|
||||
};
|
||||
use reth_beacon_consensus::{BeaconForkChoiceUpdateError, BeaconOnNewPayloadError};
|
||||
use reth_engine_primitives::EngineObjectValidationError;
|
||||
use reth_payload_builder::error::PayloadBuilderError;
|
||||
use reth_payload_primitives::EngineObjectValidationError;
|
||||
use reth_primitives::{B256, U256};
|
||||
use reth_rpc_types::ToRpcError;
|
||||
use thiserror::Error;
|
||||
|
||||
@ -17,6 +17,11 @@
|
||||
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
|
||||
use std::convert::Infallible;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use reth::{
|
||||
builder::{
|
||||
components::{ComponentsBuilder, PayloadServiceBuilder},
|
||||
@ -33,8 +38,8 @@ use reth_basic_payload_builder::{
|
||||
PayloadBuilder, PayloadConfig,
|
||||
};
|
||||
use reth_node_api::{
|
||||
validate_version_specific_fields, EngineApiMessageVersion, EngineObjectValidationError,
|
||||
EngineTypes, PayloadAttributes, PayloadBuilderAttributes, PayloadOrAttributes,
|
||||
payload::{EngineApiMessageVersion, EngineObjectValidationError, PayloadOrAttributes},
|
||||
validate_version_specific_fields, EngineTypes, PayloadAttributes, PayloadBuilderAttributes,
|
||||
};
|
||||
use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig};
|
||||
use reth_node_ethereum::node::{
|
||||
@ -53,9 +58,6 @@ use reth_rpc_types::{
|
||||
ExecutionPayloadV1, Withdrawal,
|
||||
};
|
||||
use reth_tracing::{RethTracer, Tracer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::Infallible;
|
||||
use thiserror::Error;
|
||||
|
||||
/// A custom payload attributes type.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user