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, like:
31    ///    - missing or invalid base fee
32    ///    - invalid extra data
33    ///    - invalid transactions
34    ///    - incorrect hash
35    ///    - block contains blob transactions or blob versioned hashes
36    ///    - block contains l1 withdrawals
37    ///
38    /// The checks are done in the order that conforms with the engine-API specification.
39    ///
40    /// This is intended to be invoked after receiving the payload from the CLI.
41    /// 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>
42    ///
43    /// If the cancun fields are provided this also validates that the versioned hashes in the block
44    /// are empty as well as those passed in the sidecar. If the payload fields are not provided.
45    ///
46    /// Validation according to specs <https://specs.optimism.io/protocol/exec-engine.html#engine-api>.
47    pub fn ensure_well_formed_payload<T: SignedTransaction>(
48        &self,
49        payload: OpExecutionData,
50    ) -> Result<SealedBlock<Block<T>>, OpPayloadError> {
51        let OpExecutionData { payload, sidecar } = payload;
52
53        let expected_hash = payload.block_hash();
54
55        // First parse the block
56        let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
57
58        // Ensure the hash included in the payload matches the block hash
59        if expected_hash != sealed_block.hash() {
60            return Err(PayloadError::BlockHash {
61                execution: sealed_block.hash(),
62                consensus: expected_hash,
63            })?
64        }
65
66        shanghai::ensure_well_formed_fields(
67            sealed_block.body(),
68            self.is_shanghai_active_at_timestamp(sealed_block.timestamp),
69        )?;
70
71        cancun::ensure_well_formed_header_and_sidecar_fields(
72            &sealed_block,
73            sidecar.canyon(),
74            self.is_cancun_active_at_timestamp(sealed_block.timestamp),
75        )?;
76
77        prague::ensure_well_formed_fields(
78            sealed_block.body(),
79            sidecar.isthmus(),
80            self.is_prague_active_at_timestamp(sealed_block.timestamp),
81        )?;
82
83        Ok(sealed_block)
84    }
85}