mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-16 01:46:50 -05:00
122 lines
4.8 KiB
Rust
122 lines
4.8 KiB
Rust
//! Payload Validation support.
|
|
|
|
#![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))]
|
|
|
|
pub mod cancun;
|
|
pub mod prague;
|
|
pub mod shanghai;
|
|
|
|
use alloy_rpc_types_engine::{ExecutionData, PayloadError};
|
|
use reth_chainspec::EthereumHardforks;
|
|
use reth_primitives::SealedBlock;
|
|
use reth_primitives_traits::{Block, SignedTransaction};
|
|
use std::sync::Arc;
|
|
|
|
/// Execution payload validator.
|
|
#[derive(Clone, Debug)]
|
|
pub struct ExecutionPayloadValidator<ChainSpec> {
|
|
/// Chain spec to validate against.
|
|
chain_spec: Arc<ChainSpec>,
|
|
}
|
|
|
|
impl<ChainSpec> ExecutionPayloadValidator<ChainSpec> {
|
|
/// Create a new validator.
|
|
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
|
|
Self { chain_spec }
|
|
}
|
|
|
|
/// Returns the chain spec used by the validator.
|
|
#[inline]
|
|
pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
|
|
&self.chain_spec
|
|
}
|
|
}
|
|
|
|
impl<ChainSpec: EthereumHardforks> ExecutionPayloadValidator<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
|
|
///
|
|
/// 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<T: SignedTransaction>(
|
|
&self,
|
|
payload: ExecutionData,
|
|
) -> Result<SealedBlock<reth_primitives::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)
|
|
}
|
|
}
|