diff --git a/crates/stateless/src/validation.rs b/crates/stateless/src/validation.rs index 165deac1bb..120273a7eb 100644 --- a/crates/stateless/src/validation.rs +++ b/crates/stateless/src/validation.rs @@ -12,15 +12,16 @@ use alloc::{ vec::Vec, }; use alloy_consensus::{BlockHeader, Header}; -use alloy_primitives::B256; -use alloy_rlp::Decodable; +use alloy_primitives::{keccak256, B256}; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_consensus::{Consensus, HeaderValidator}; use reth_errors::ConsensusError; use reth_ethereum_consensus::{validate_block_post_execution, EthBeaconConsensus}; use reth_ethereum_primitives::{Block, EthPrimitives}; use reth_evm::{execute::Executor, ConfigureEvm}; -use reth_primitives_traits::{block::error::BlockRecoveryError, Block as _, RecoveredBlock}; +use reth_primitives_traits::{ + block::error::BlockRecoveryError, Block as _, RecoveredBlock, SealedHeader, +}; use reth_trie_common::{HashedPostState, KeccakKeyHasher}; /// Errors that can occur during stateless validation. @@ -167,12 +168,13 @@ where .try_into_recovered() .map_err(|err| StatelessValidationError::SignerRecovery(Box::new(err)))?; - let mut ancestor_headers: Vec
= witness + let mut ancestor_headers: Vec<_> = witness .headers .iter() - .map(|serialized_header| { - let bytes = serialized_header.as_ref(); - Header::decode(&mut &bytes[..]) + .map(|bytes| { + let hash = keccak256(bytes); + alloy_rlp::decode_exact::
(bytes) + .map(|h| SealedHeader::new(h, hash)) .map_err(|_| StatelessValidationError::HeaderDeserializationFailed) }) .collect::>()?; @@ -180,25 +182,22 @@ where // ascending order. ancestor_headers.sort_by_key(|header| header.number()); - // Validate block against pre-execution consensus rules - validate_block_consensus(chain_spec.clone(), ¤t_block)?; - // Check that the ancestor headers form a contiguous chain and are not just random headers. let ancestor_hashes = compute_ancestor_hashes(¤t_block, &ancestor_headers)?; - // Get the last ancestor header and retrieve its state root. - // - // There should be at least one ancestor header, this is because we need the parent header to - // retrieve the previous state root. + // There should be at least one ancestor header. // The edge case here would be the genesis block, but we do not create proofs for the genesis // block. - let pre_state_root = match ancestor_headers.last() { - Some(prev_header) => prev_header.state_root, + let parent = match ancestor_headers.last() { + Some(prev_header) => prev_header, None => return Err(StatelessValidationError::MissingAncestorHeader), }; + // Validate block against pre-execution consensus rules + validate_block_consensus(chain_spec.clone(), ¤t_block, parent)?; + // First verify that the pre-state reads are correct - let (mut trie, bytecode) = T::new(&witness, pre_state_root)?; + let (mut trie, bytecode) = T::new(&witness, parent.state_root)?; // Create an in-memory database that will use the reads to validate the block let db = WitnessDatabase::new(&trie, bytecode, ancestor_hashes); @@ -231,17 +230,14 @@ where /// /// This function validates a block against Ethereum consensus rules by: /// -/// 1. **Difficulty Validation:** Validates the header with total difficulty to verify proof-of-work -/// (pre-merge) or to enforce post-merge requirements. -/// -/// 2. **Header Validation:** Validates the sealed header against protocol specifications, +/// 1. **Header Validation:** Validates the sealed header against protocol specifications, /// including: /// - Gas limit checks /// - Base fee validation for EIP-1559 /// - Withdrawals root validation for Shanghai fork /// - Blob-related fields validation for Cancun fork /// -/// 3. **Pre-Execution Validation:** Validates block structure, transaction format, signature +/// 2. **Pre-Execution Validation:** Validates block structure, transaction format, signature /// validity, and other pre-execution requirements. /// /// This function acts as a preliminary validation before executing and validating the state @@ -249,6 +245,7 @@ where fn validate_block_consensus( chain_spec: Arc, block: &RecoveredBlock, + parent: &SealedHeader
, ) -> Result<(), StatelessValidationError> where ChainSpec: Send + Sync + EthChainSpec
+ EthereumHardforks + Debug, @@ -256,6 +253,7 @@ where let consensus = EthBeaconConsensus::new(chain_spec); consensus.validate_header(block.sealed_header())?; + consensus.validate_header_against_parent(block.sealed_header(), parent)?; consensus.validate_block_pre_execution(block)?; @@ -277,18 +275,18 @@ where /// ancestor header to its corresponding block hash. fn compute_ancestor_hashes( current_block: &RecoveredBlock, - ancestor_headers: &[Header], + ancestor_headers: &[SealedHeader], ) -> Result, StatelessValidationError> { let mut ancestor_hashes = BTreeMap::new(); - let mut child_header = current_block.header(); + let mut child_header = current_block.sealed_header(); // Next verify that headers supplied are contiguous for parent_header in ancestor_headers.iter().rev() { let parent_hash = child_header.parent_hash(); ancestor_hashes.insert(parent_header.number, parent_hash); - if parent_hash != parent_header.hash_slow() { + if parent_hash != parent_header.hash() { return Err(StatelessValidationError::InvalidAncestorChain); // Blocks must be contiguous }