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}