From 21aa3c9a3e3aa3f7559646a3ebe2d51382026b7f Mon Sep 17 00:00:00 2001 From: Alexander Fedorovskyi Date: Mon, 1 Dec 2025 14:16:54 +0100 Subject: [PATCH 1/2] feat: prague activated --- crates/consensus/src/transaction/compress.rs | 2 +- crates/consensus/src/validation.rs | 107 ++++++++++++++++++- crates/evm/src/spec.rs | 4 +- 3 files changed, 105 insertions(+), 8 deletions(-) diff --git a/crates/consensus/src/transaction/compress.rs b/crates/consensus/src/transaction/compress.rs index 0334f4b..153025d 100644 --- a/crates/consensus/src/transaction/compress.rs +++ b/crates/consensus/src/transaction/compress.rs @@ -167,7 +167,7 @@ impl Compress for TaikoTxEnvelope { type Compressed = Vec; fn compress_to_buf>(&self, buf: &mut B) { - let _ = Compact::to_compact(self, buf); // Delegates to your Compact impl! + let _ = Compact::to_compact(self, buf); } } diff --git a/crates/consensus/src/validation.rs b/crates/consensus/src/validation.rs index 3d80007..a0965a8 100644 --- a/crates/consensus/src/validation.rs +++ b/crates/consensus/src/validation.rs @@ -1,8 +1,12 @@ use std::{fmt::Debug, sync::Arc}; -use alloy_consensus::{BlockHeader as AlloyBlockHeader, EMPTY_OMMER_ROOT_HASH, Transaction}; +use alloy_consensus::{ + BlockHeader as AlloyBlockHeader, EMPTY_OMMER_ROOT_HASH, Transaction, TxReceipt, + proofs::calculate_receipt_root, +}; +use alloy_eips::{Encodable2718, eip7685::Requests}; use alloy_hardforks::EthereumHardforks; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, B256, Bloom, Bytes, U256}; use alloy_sol_types::{SolCall, sol}; use reth_chainspec::EthChainSpec; use reth_consensus::{Consensus, ConsensusError, FullConsensus, HeaderValidator}; @@ -10,11 +14,10 @@ use reth_consensus_common::validation::{ validate_against_parent_hash_number, validate_body_against_header, validate_header_base_fee, validate_header_extra_data, validate_header_gas, }; -use reth_ethereum_consensus::validate_block_post_execution; use reth_evm::block::BlockExecutionResult; use reth_primitives_traits::{ - Block, BlockBody, BlockHeader, GotExpected, NodePrimitives, RecoveredBlock, SealedBlock, - SealedHeader, SignedTransaction, + Block, BlockBody, BlockHeader, GotExpected, NodePrimitives, Receipt, RecoveredBlock, + SealedBlock, SealedHeader, SignedTransaction, receipt::gas_spent_by_transactions, }; use reth_storage_api::BlockReader; @@ -332,6 +335,100 @@ fn validate_input_selector( Ok(()) } +/// Validate a block with regard to execution results: +/// +/// - Compares the receipts root in the block header to the block body +/// - Compares the gas used in the block header to the actual gas usage after execution +/// +/// Not: Taiko does not use requests hash validation, so that part is omitted. +pub fn validate_block_post_execution( + block: &RecoveredBlock, + chain_spec: &ChainSpec, + receipts: &[R], + _requests: &Requests, +) -> Result<(), ConsensusError> +where + B: Block, + R: Receipt, + ChainSpec: EthereumHardforks, +{ + // Check if gas used matches the value set in header. + let cumulative_gas_used = + receipts.last().map(|receipt| receipt.cumulative_gas_used()).unwrap_or(0); + if block.header().gas_used() != cumulative_gas_used { + return Err(ConsensusError::BlockGasUsed { + gas: GotExpected { got: cumulative_gas_used, expected: block.header().gas_used() }, + gas_spent_by_tx: gas_spent_by_transactions(receipts), + }) + } + + // Before Byzantium, receipts contained state root that would mean that expensive + // operation as hashing that is required for state root got calculated in every + // transaction This was replaced with is_success flag. + // See more about EIP here: https://eips.ethereum.org/EIPS/eip-658 + if chain_spec.is_byzantium_active_at_block(block.header().number()) { + if let Err(error) = + verify_receipts(block.header().receipts_root(), block.header().logs_bloom(), receipts) + { + let receipts = receipts + .iter() + .map(|r| Bytes::from(r.with_bloom_ref().encoded_2718())) + .collect::>(); + tracing::debug!(%error, ?receipts, "receipts verification failed"); + return Err(error) + } + } + + Ok(()) +} + +/// Calculate the receipts root, and compare it against the expected receipts root and logs +/// bloom. +fn verify_receipts( + expected_receipts_root: B256, + expected_logs_bloom: Bloom, + receipts: &[R], +) -> Result<(), ConsensusError> { + // Calculate receipts root. + let receipts_with_bloom = receipts.iter().map(TxReceipt::with_bloom_ref).collect::>(); + let receipts_root = calculate_receipt_root(&receipts_with_bloom); + + // Calculate header logs bloom. + let logs_bloom = receipts_with_bloom.iter().fold(Bloom::ZERO, |bloom, r| bloom | r.bloom_ref()); + + compare_receipts_root_and_logs_bloom( + receipts_root, + logs_bloom, + expected_receipts_root, + expected_logs_bloom, + )?; + + Ok(()) +} + +/// Compare the calculated receipts root with the expected receipts root, also compare +/// the calculated logs bloom with the expected logs bloom. +fn compare_receipts_root_and_logs_bloom( + calculated_receipts_root: B256, + calculated_logs_bloom: Bloom, + expected_receipts_root: B256, + expected_logs_bloom: Bloom, +) -> Result<(), ConsensusError> { + if calculated_receipts_root != expected_receipts_root { + return Err(ConsensusError::BodyReceiptRootDiff( + GotExpected { got: calculated_receipts_root, expected: expected_receipts_root }.into(), + )) + } + + if calculated_logs_bloom != expected_logs_bloom { + return Err(ConsensusError::BodyBloomLogDiff( + GotExpected { got: calculated_logs_bloom, expected: expected_logs_bloom }.into(), + )) + } + + Ok(()) +} + #[cfg(test)] mod test { use alloy_consensus::Header; diff --git a/crates/evm/src/spec.rs b/crates/evm/src/spec.rs index 0db2624..7ffa165 100644 --- a/crates/evm/src/spec.rs +++ b/crates/evm/src/spec.rs @@ -22,7 +22,7 @@ impl TaikoSpecId { /// Converts the [`TaikoSpecId`] into a [`SpecId`]. pub const fn into_eth_spec(self) -> SpecId { match self { - Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::CANCUN, + Self::GENESIS | Self::ONTAKE | Self::PACAYA | Self::SHASTA => SpecId::PRAGUE, } } @@ -84,7 +84,7 @@ mod tests { (SpecId::MERGE, true), (SpecId::SHANGHAI, true), (SpecId::CANCUN, true), - (SpecId::default(), false), + (SpecId::PRAGUE, true), ], vec![ (TaikoSpecId::GENESIS, true), From 0e40b28d4ca5e9cba8c83bd71116167b55120477 Mon Sep 17 00:00:00 2001 From: Alexander Fedorovskyi Date: Wed, 3 Dec 2025 11:03:46 +0100 Subject: [PATCH 2/2] cargo clippy --- crates/consensus/src/validation.rs | 24 +++++++++++++----------- crates/evm/src/handler/instructions.rs | 4 ---- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/crates/consensus/src/validation.rs b/crates/consensus/src/validation.rs index a0965a8..cdb7e8f 100644 --- a/crates/consensus/src/validation.rs +++ b/crates/consensus/src/validation.rs @@ -366,17 +366,19 @@ where // operation as hashing that is required for state root got calculated in every // transaction This was replaced with is_success flag. // See more about EIP here: https://eips.ethereum.org/EIPS/eip-658 - if chain_spec.is_byzantium_active_at_block(block.header().number()) { - if let Err(error) = - verify_receipts(block.header().receipts_root(), block.header().logs_bloom(), receipts) - { - let receipts = receipts - .iter() - .map(|r| Bytes::from(r.with_bloom_ref().encoded_2718())) - .collect::>(); - tracing::debug!(%error, ?receipts, "receipts verification failed"); - return Err(error) - } + if chain_spec.is_byzantium_active_at_block(block.header().number()) && + let Err(error) = verify_receipts( + block.header().receipts_root(), + block.header().logs_bloom(), + receipts, + ) + { + let receipts = receipts + .iter() + .map(|r| Bytes::from(r.with_bloom_ref().encoded_2718())) + .collect::>(); + tracing::debug!(%error, ?receipts, "receipts verification failed"); + return Err(error) } Ok(()) diff --git a/crates/evm/src/handler/instructions.rs b/crates/evm/src/handler/instructions.rs index 6ce8009..f84cf4b 100644 --- a/crates/evm/src/handler/instructions.rs +++ b/crates/evm/src/handler/instructions.rs @@ -84,8 +84,6 @@ pub fn blob_basefee( context: InstructionContext<'_, H, WIRE>, ) { context.interpreter.halt_not_activated(); - - return; } /// Custom implementation of BLOBHASH instruction for Taiko EVM. @@ -95,6 +93,4 @@ pub fn blob_hash( context: InstructionContext<'_, H, WIRE>, ) { context.interpreter.halt_not_activated(); - - return; }