reth_optimism_payload_builder/
validator.rs

1//! Validates execution payload wrt Optimism consensus rules
2
3use alloc::sync::Arc;
4use alloy_consensus::Block;
5use alloy_rpc_types_engine::PayloadError;
6use derive_more::{Constructor, Deref};
7use op_alloy_rpc_types_engine::{OpExecutionData, OpPayloadError};
8use reth_optimism_forks::OpHardforks;
9use reth_payload_validator::{cancun, prague, shanghai};
10use reth_primitives_traits::{Block as _, SealedBlock, SignedTransaction};
11
12/// Execution payload validator.
13#[derive(Clone, Debug, Deref, Constructor)]
14pub struct OpExecutionPayloadValidator<ChainSpec> {
15    /// Chain spec to validate against.
16    #[deref]
17    inner: Arc<ChainSpec>,
18}
19
20impl<ChainSpec> OpExecutionPayloadValidator<ChainSpec>
21where
22    ChainSpec: OpHardforks,
23{
24    /// Returns reference to chain spec.
25    pub fn chain_spec(&self) -> &ChainSpec {
26        &self.inner
27    }
28
29    /// Ensures that the given payload does not violate any consensus rules that concern the block's
30    /// layout.
31    ///
32    /// See also [`ensure_well_formed_payload`].
33    pub fn ensure_well_formed_payload<T: SignedTransaction>(
34        &self,
35        payload: OpExecutionData,
36    ) -> Result<SealedBlock<Block<T>>, OpPayloadError> {
37        ensure_well_formed_payload(self.chain_spec(), payload)
38    }
39}
40
41/// Ensures that the given payload does not violate any consensus rules that concern the block's
42/// layout, like:
43///    - missing or invalid base fee
44///    - invalid extra data
45///    - invalid transactions
46///    - incorrect hash
47///    - block contains blob transactions or blob versioned hashes
48///    - block contains l1 withdrawals
49///
50/// The checks are done in the order that conforms with the engine-API specification.
51///
52/// This is intended to be invoked after receiving the payload from the CLI.
53/// 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>
54///
55/// If the cancun fields are provided this also validates that the versioned hashes in the block
56/// are empty as well as those passed in the sidecar. If the payload fields are not provided.
57///
58/// Validation according to specs <https://specs.optimism.io/protocol/exec-engine.html#engine-api>.
59pub fn ensure_well_formed_payload<ChainSpec, T>(
60    chain_spec: ChainSpec,
61    payload: OpExecutionData,
62) -> Result<SealedBlock<Block<T>>, OpPayloadError>
63where
64    ChainSpec: OpHardforks,
65    T: SignedTransaction,
66{
67    let OpExecutionData { payload, sidecar } = payload;
68
69    let expected_hash = payload.block_hash();
70
71    // First parse the block
72    let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
73
74    // Ensure the hash included in the payload matches the block hash
75    if expected_hash != sealed_block.hash() {
76        return Err(PayloadError::BlockHash {
77            execution: sealed_block.hash(),
78            consensus: expected_hash,
79        })?
80    }
81
82    shanghai::ensure_well_formed_fields(
83        sealed_block.body(),
84        chain_spec.is_shanghai_active_at_timestamp(sealed_block.timestamp),
85    )?;
86
87    cancun::ensure_well_formed_header_and_sidecar_fields(
88        &sealed_block,
89        sidecar.ecotone(),
90        chain_spec.is_cancun_active_at_timestamp(sealed_block.timestamp),
91    )?;
92
93    prague::ensure_well_formed_fields(
94        sealed_block.body(),
95        sidecar.isthmus(),
96        chain_spec.is_prague_active_at_timestamp(sealed_block.timestamp),
97    )?;
98
99    Ok(sealed_block)
100}