chore: move validation to standalone fns (#17582)

This commit is contained in:
Matthias Seitz
2025-07-23 15:44:33 +02:00
committed by GitHub
parent 9ff444ea9e
commit a72fe7a2d0
2 changed files with 133 additions and 121 deletions

View File

@@ -28,83 +28,80 @@ impl<ChainSpec> EthereumExecutionPayloadValidator<ChainSpec> {
}
impl<ChainSpec: EthereumHardforks> EthereumExecutionPayloadValidator<ChainSpec> {
/// Returns true if the Cancun hardfork is active at the given timestamp.
#[inline]
fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool {
self.chain_spec().is_cancun_active_at_timestamp(timestamp)
}
/// Returns true if the Shanghai hardfork is active at the given timestamp.
#[inline]
fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool {
self.chain_spec().is_shanghai_active_at_timestamp(timestamp)
}
/// Returns true if the Prague hardfork is active at the given timestamp.
#[inline]
fn is_prague_active_at_timestamp(&self, timestamp: u64) -> bool {
self.chain_spec().is_prague_active_at_timestamp(timestamp)
}
/// Ensures that the given payload does not violate any consensus rules that concern the block's
/// layout, like:
/// - missing or invalid base fee
/// - invalid extra data
/// - invalid transactions
/// - incorrect hash
/// - the versioned hashes passed with the payload do not exactly match transaction versioned
/// hashes
/// - the block does not contain blob transactions if it is pre-cancun
/// layout,
///
/// The checks are done in the order that conforms with the engine-API specification.
///
/// This is intended to be invoked after receiving the payload from the CLI.
/// The additional [`MaybeCancunPayloadFields`](alloy_rpc_types_engine::MaybeCancunPayloadFields) are not part of the payload, but are additional fields in the `engine_newPayloadV3` RPC call, See also <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#engine_newpayloadv3>
///
/// If the cancun fields are provided this also validates that the versioned hashes in the block
/// match the versioned hashes passed in the
/// [`CancunPayloadFields`](alloy_rpc_types_engine::CancunPayloadFields), if the cancun payload
/// fields are provided. If the payload fields are not provided, but versioned hashes exist
/// in the block, this is considered an error: [`PayloadError::InvalidVersionedHashes`].
///
/// This validates versioned hashes according to the Engine API Cancun spec:
/// <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification>
/// See also [`ensure_well_formed_payload`]
pub fn ensure_well_formed_payload<T: SignedTransaction>(
&self,
payload: ExecutionData,
) -> Result<SealedBlock<Block<T>>, PayloadError> {
let ExecutionData { payload, sidecar } = payload;
let expected_hash = payload.block_hash();
// First parse the block
let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
// Ensure the hash included in the payload matches the block hash
if expected_hash != sealed_block.hash() {
return Err(PayloadError::BlockHash {
execution: sealed_block.hash(),
consensus: expected_hash,
})
}
shanghai::ensure_well_formed_fields(
sealed_block.body(),
self.is_shanghai_active_at_timestamp(sealed_block.timestamp),
)?;
cancun::ensure_well_formed_fields(
&sealed_block,
sidecar.cancun(),
self.is_cancun_active_at_timestamp(sealed_block.timestamp),
)?;
prague::ensure_well_formed_fields(
sealed_block.body(),
sidecar.prague(),
self.is_prague_active_at_timestamp(sealed_block.timestamp),
)?;
Ok(sealed_block)
ensure_well_formed_payload(&self.chain_spec, payload)
}
}
/// Ensures that the given payload does not violate any consensus rules that concern the block's
/// layout, like:
/// - missing or invalid base fee
/// - invalid extra data
/// - invalid transactions
/// - incorrect hash
/// - the versioned hashes passed with the payload do not exactly match transaction versioned
/// hashes
/// - the block does not contain blob transactions if it is pre-cancun
///
/// The checks are done in the order that conforms with the engine-API specification.
///
/// This is intended to be invoked after receiving the payload from the CLI.
/// The additional [`MaybeCancunPayloadFields`](alloy_rpc_types_engine::MaybeCancunPayloadFields) are not part of the payload, but are additional fields in the `engine_newPayloadV3` RPC call, See also <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#engine_newpayloadv3>
///
/// If the cancun fields are provided this also validates that the versioned hashes in the block
/// match the versioned hashes passed in the
/// [`CancunPayloadFields`](alloy_rpc_types_engine::CancunPayloadFields), if the cancun payload
/// fields are provided. If the payload fields are not provided, but versioned hashes exist
/// in the block, this is considered an error: [`PayloadError::InvalidVersionedHashes`].
///
/// This validates versioned hashes according to the Engine API Cancun spec:
/// <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification>
pub fn ensure_well_formed_payload<ChainSpec, T>(
chain_spec: ChainSpec,
payload: ExecutionData,
) -> Result<SealedBlock<Block<T>>, PayloadError>
where
ChainSpec: EthereumHardforks,
T: SignedTransaction,
{
let ExecutionData { payload, sidecar } = payload;
let expected_hash = payload.block_hash();
// First parse the block
let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
// Ensure the hash included in the payload matches the block hash
if expected_hash != sealed_block.hash() {
return Err(PayloadError::BlockHash {
execution: sealed_block.hash(),
consensus: expected_hash,
})
}
shanghai::ensure_well_formed_fields(
sealed_block.body(),
chain_spec.is_shanghai_active_at_timestamp(sealed_block.timestamp),
)?;
cancun::ensure_well_formed_fields(
&sealed_block,
sidecar.cancun(),
chain_spec.is_cancun_active_at_timestamp(sealed_block.timestamp),
)?;
prague::ensure_well_formed_fields(
sealed_block.body(),
sidecar.prague(),
chain_spec.is_prague_active_at_timestamp(sealed_block.timestamp),
)?;
Ok(sealed_block)
}

View File

@@ -27,59 +27,74 @@ where
}
/// Ensures that the given payload does not violate any consensus rules that concern the block's
/// layout, like:
/// - missing or invalid base fee
/// - invalid extra data
/// - invalid transactions
/// - incorrect hash
/// - block contains blob transactions or blob versioned hashes
/// - block contains l1 withdrawals
/// layout.
///
/// The checks are done in the order that conforms with the engine-API specification.
///
/// This is intended to be invoked after receiving the payload from the CLI.
/// The additional fields, starting with [`MaybeCancunPayloadFields`](alloy_rpc_types_engine::MaybeCancunPayloadFields), are not part of the payload, but are additional fields starting in the `engine_newPayloadV3` RPC call, See also <https://specs.optimism.io/protocol/exec-engine.html#engine_newpayloadv3>
///
/// If the cancun fields are provided this also validates that the versioned hashes in the block
/// are empty as well as those passed in the sidecar. If the payload fields are not provided.
///
/// Validation according to specs <https://specs.optimism.io/protocol/exec-engine.html#engine-api>.
/// See also [`ensure_well_formed_payload`].
pub fn ensure_well_formed_payload<T: SignedTransaction>(
&self,
payload: OpExecutionData,
) -> Result<SealedBlock<Block<T>>, OpPayloadError> {
let OpExecutionData { payload, sidecar } = payload;
let expected_hash = payload.block_hash();
// First parse the block
let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
// Ensure the hash included in the payload matches the block hash
if expected_hash != sealed_block.hash() {
return Err(PayloadError::BlockHash {
execution: sealed_block.hash(),
consensus: expected_hash,
})?
}
shanghai::ensure_well_formed_fields(
sealed_block.body(),
self.is_shanghai_active_at_timestamp(sealed_block.timestamp),
)?;
cancun::ensure_well_formed_header_and_sidecar_fields(
&sealed_block,
sidecar.ecotone(),
self.is_cancun_active_at_timestamp(sealed_block.timestamp),
)?;
prague::ensure_well_formed_fields(
sealed_block.body(),
sidecar.isthmus(),
self.is_prague_active_at_timestamp(sealed_block.timestamp),
)?;
Ok(sealed_block)
ensure_well_formed_payload(self.chain_spec(), payload)
}
}
/// Ensures that the given payload does not violate any consensus rules that concern the block's
/// layout, like:
/// - missing or invalid base fee
/// - invalid extra data
/// - invalid transactions
/// - incorrect hash
/// - block contains blob transactions or blob versioned hashes
/// - block contains l1 withdrawals
///
/// The checks are done in the order that conforms with the engine-API specification.
///
/// This is intended to be invoked after receiving the payload from the CLI.
/// The additional fields, starting with [`MaybeCancunPayloadFields`](alloy_rpc_types_engine::MaybeCancunPayloadFields), are not part of the payload, but are additional fields starting in the `engine_newPayloadV3` RPC call, See also <https://specs.optimism.io/protocol/exec-engine.html#engine_newpayloadv3>
///
/// If the cancun fields are provided this also validates that the versioned hashes in the block
/// are empty as well as those passed in the sidecar. If the payload fields are not provided.
///
/// Validation according to specs <https://specs.optimism.io/protocol/exec-engine.html#engine-api>.
pub fn ensure_well_formed_payload<ChainSpec, T>(
chain_spec: ChainSpec,
payload: OpExecutionData,
) -> Result<SealedBlock<Block<T>>, OpPayloadError>
where
ChainSpec: OpHardforks,
T: SignedTransaction,
{
let OpExecutionData { payload, sidecar } = payload;
let expected_hash = payload.block_hash();
// First parse the block
let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
// Ensure the hash included in the payload matches the block hash
if expected_hash != sealed_block.hash() {
return Err(PayloadError::BlockHash {
execution: sealed_block.hash(),
consensus: expected_hash,
})?
}
shanghai::ensure_well_formed_fields(
sealed_block.body(),
chain_spec.is_shanghai_active_at_timestamp(sealed_block.timestamp),
)?;
cancun::ensure_well_formed_header_and_sidecar_fields(
&sealed_block,
sidecar.ecotone(),
chain_spec.is_cancun_active_at_timestamp(sealed_block.timestamp),
)?;
prague::ensure_well_formed_fields(
sealed_block.body(),
sidecar.isthmus(),
chain_spec.is_prague_active_at_timestamp(sealed_block.timestamp),
)?;
Ok(sealed_block)
}