diff --git a/.changelog/easy-clouds-meow.md b/.changelog/easy-clouds-meow.md new file mode 100644 index 0000000000..cb47beed7e --- /dev/null +++ b/.changelog/easy-clouds-meow.md @@ -0,0 +1,5 @@ +--- +ef-tests: patch +--- + +Removed reth-stateless crate and stateless validation from ef-tests. diff --git a/.github/scripts/check_rv32imac.sh b/.github/scripts/check_rv32imac.sh index dbfbfbf684..1fed4afe45 100755 --- a/.github/scripts/check_rv32imac.sh +++ b/.github/scripts/check_rv32imac.sh @@ -27,7 +27,6 @@ crates_to_check=( reth-ethereum-forks reth-ethereum-primitives reth-ethereum-consensus - reth-stateless ) any_failed=0 diff --git a/Cargo.lock b/Cargo.lock index 60caad374c..d342b208f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3328,7 +3328,6 @@ dependencies = [ "reth-primitives-traits", "reth-provider", "reth-revm", - "reth-stateless", "reth-tracing", "reth-trie", "reth-trie-db", @@ -10268,32 +10267,6 @@ dependencies = [ "serde", ] -[[package]] -name = "reth-stateless" -version = "1.10.2" -dependencies = [ - "alloy-consensus", - "alloy-genesis", - "alloy-primitives", - "alloy-rlp", - "alloy-rpc-types-debug", - "alloy-trie", - "itertools 0.14.0", - "reth-chainspec", - "reth-consensus", - "reth-errors", - "reth-ethereum-consensus", - "reth-ethereum-primitives", - "reth-evm", - "reth-primitives-traits", - "reth-revm", - "reth-trie-common", - "reth-trie-sparse", - "serde", - "serde_with", - "thiserror 2.0.18", -] - [[package]] name = "reth-static-file" version = "1.10.2" diff --git a/Cargo.toml b/Cargo.toml index 263a19fe4f..307982d1ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,7 +99,6 @@ members = [ "crates/stages/api/", "crates/stages/stages/", "crates/stages/types/", - "crates/stateless", "crates/static-file/static-file", "crates/static-file/types/", "crates/storage/codecs/", @@ -420,7 +419,6 @@ reth-rpc-convert = { path = "crates/rpc/rpc-convert" } reth-stages = { path = "crates/stages/stages" } reth-stages-api = { path = "crates/stages/api" } reth-stages-types = { path = "crates/stages/types", default-features = false } -reth-stateless = { path = "crates/stateless", default-features = false } reth-static-file = { path = "crates/static-file/static-file" } reth-static-file-types = { path = "crates/static-file/types", default-features = false } reth-storage-api = { path = "crates/storage/storage-api", default-features = false } diff --git a/crates/stateless/Cargo.toml b/crates/stateless/Cargo.toml deleted file mode 100644 index d826e2f375..0000000000 --- a/crates/stateless/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "reth-stateless" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true -exclude.workspace = true - -[lints] -workspace = true - -[dependencies] -# alloy -alloy-primitives.workspace = true -alloy-rlp.workspace = true -alloy-trie.workspace = true -alloy-consensus.workspace = true -alloy-rpc-types-debug.workspace = true -alloy-genesis = { workspace = true, features = ["serde-bincode-compat"] } - -# reth -reth-ethereum-consensus.workspace = true -reth-primitives-traits.workspace = true -reth-ethereum-primitives = { workspace = true, features = ["serde", "serde-bincode-compat"] } -reth-errors.workspace = true -reth-evm.workspace = true -reth-revm.workspace = true -reth-trie-common.workspace = true -reth-trie-sparse.workspace = true -reth-chainspec.workspace = true -reth-consensus.workspace = true - -# misc -thiserror.workspace = true -itertools.workspace = true -serde.workspace = true -serde_with.workspace = true diff --git a/crates/stateless/src/lib.rs b/crates/stateless/src/lib.rs deleted file mode 100644 index 06540d319f..0000000000 --- a/crates/stateless/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Provides types and functions for stateless execution and validation of Ethereum blocks. -//! -//! This crate enables the verification of block execution without requiring access to a -//! full node's persistent database. Instead, it relies on pre-generated "witness" data -//! that proves the specific state accessed during the block's execution. -//! -//! # Key Components -//! -//! * `WitnessDatabase`: An implementation of [`reth_revm::Database`] that uses a -//! [`reth_trie_sparse::SparseStateTrie`] populated from witness data, along with provided -//! bytecode and ancestor block hashes, to serve state reads during execution. -//! * `stateless_validation`: The core function that orchestrates the stateless validation process. -//! It takes a block, its execution witness, ancestor headers, and chain specification, then -//! performs: -//! 1. Witness verification against the parent block's state root. -//! 2. Block execution using the `WitnessDatabase`. -//! 3. Post-execution consensus checks. -//! 4. Post-state root calculation and comparison against the block header. -//! -//! # Usage -//! -//! The primary entry point is typically the `validation::stateless_validation` function. Callers -//! need to provide the block to be validated along with accurately generated `ExecutionWitness` -//! data corresponding to that block's execution trace and the necessary Headers of ancestor -//! blocks. - -#![doc( - html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", - html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", - issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" -)] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![no_std] - -extern crate alloc; - -mod recover_block; -/// Sparse trie implementation for stateless validation -pub mod trie; - -use alloy_genesis::ChainConfig; -#[doc(inline)] -pub use recover_block::UncompressedPublicKey; -#[doc(inline)] -pub use trie::StatelessTrie; -#[doc(inline)] -pub use validation::stateless_validation; -#[doc(inline)] -pub use validation::stateless_validation_with_trie; - -/// Implementation of stateless validation -pub mod validation; -pub(crate) mod witness_db; - -#[doc(inline)] -pub use alloy_rpc_types_debug::ExecutionWitness; - -pub use alloy_genesis::Genesis; - -use reth_ethereum_primitives::Block; - -/// `StatelessInput` is a convenience structure for serializing the input needed -/// for the stateless validation function. -#[serde_with::serde_as] -#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -pub struct StatelessInput { - /// The block being executed in the stateless validation function - #[serde_as( - as = "reth_primitives_traits::serde_bincode_compat::Block" - )] - pub block: Block, - /// `ExecutionWitness` for the stateless validation function - pub witness: ExecutionWitness, - /// Chain configuration for the stateless validation function - #[serde_as(as = "alloy_genesis::serde_bincode_compat::ChainConfig<'_>")] - pub chain_config: ChainConfig, -} diff --git a/crates/stateless/src/recover_block.rs b/crates/stateless/src/recover_block.rs deleted file mode 100644 index 7fc2cb2fb8..0000000000 --- a/crates/stateless/src/recover_block.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::validation::StatelessValidationError; -use alloc::vec::Vec; -use alloy_consensus::BlockHeader; -use alloy_primitives::Address; -use core::ops::Deref; -use reth_chainspec::EthereumHardforks; -use reth_ethereum_primitives::{Block, TransactionSigned}; -use reth_primitives_traits::{Block as _, RecoveredBlock}; -use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, Bytes}; - -/// Serialized uncompressed public key -#[serde_as] -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UncompressedPublicKey(#[serde_as(as = "Bytes")] pub [u8; 65]); - -impl Deref for UncompressedPublicKey { - type Target = [u8]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// Verifies all transactions in a block against a list of public keys and signatures. -/// -/// Returns a `RecoveredBlock` -pub(crate) fn recover_block_with_public_keys( - block: Block, - public_keys: Vec, - chain_spec: &ChainSpec, -) -> Result, StatelessValidationError> -where - ChainSpec: EthereumHardforks, -{ - if block.body().transactions.len() != public_keys.len() { - return Err(StatelessValidationError::Custom( - "Number of public keys must match number of transactions", - )); - } - - // Determine if we're in the Homestead fork for signature validation - let is_homestead = chain_spec.is_homestead_active_at_block(block.header().number()); - - // Verify each transaction signature against its corresponding public key - let senders = public_keys - .iter() - .zip(block.body().transactions()) - .map(|(vk, tx)| verify_and_compute_sender(vk, tx, is_homestead)) - .collect::, _>>()?; - - // Create RecoveredBlock with verified senders - let block_hash = block.hash_slow(); - Ok(RecoveredBlock::new(block, senders, block_hash)) -} - -/// Verifies a transaction using its signature and the given public key. -/// -/// Note: If the signature or the public key is incorrect, then this method -/// will return an error. -/// -/// Returns the address derived from the public key. -fn verify_and_compute_sender( - vk: &UncompressedPublicKey, - tx: &TransactionSigned, - is_homestead: bool, -) -> Result { - let sig = tx.signature(); - - // non-normalized signatures are only valid pre-homestead - let sig_is_normalized = sig.normalize_s().is_none(); - if is_homestead && !sig_is_normalized { - return Err(StatelessValidationError::HomesteadSignatureNotNormalized); - } - let sig_hash = tx.signature_hash(); - alloy_consensus::crypto::secp256k1::verify_and_compute_signer_unchecked(&vk.0, sig, sig_hash) - .map_err(|_| StatelessValidationError::SignerRecovery) -} diff --git a/crates/stateless/src/trie.rs b/crates/stateless/src/trie.rs deleted file mode 100644 index c4bfc762af..0000000000 --- a/crates/stateless/src/trie.rs +++ /dev/null @@ -1,311 +0,0 @@ -use crate::validation::StatelessValidationError; -use alloc::{format, vec::Vec}; -use alloy_primitives::{keccak256, map::B256Map, Address, B256, U256}; -use alloy_rlp::{Decodable, Encodable}; -use alloy_rpc_types_debug::ExecutionWitness; -use alloy_trie::{TrieAccount, EMPTY_ROOT_HASH}; -use itertools::Itertools; -use reth_errors::ProviderError; -use reth_revm::state::Bytecode; -use reth_trie_common::{HashedPostState, Nibbles, TRIE_ACCOUNT_RLP_MAX_SIZE}; -use reth_trie_sparse::{ - errors::SparseStateTrieResult, - provider::{DefaultTrieNodeProvider, DefaultTrieNodeProviderFactory}, - RevealableSparseTrie, SparseStateTrie, SparseTrie, -}; - -/// Trait for stateless trie implementations that can be used for stateless validation. -pub trait StatelessTrie: core::fmt::Debug { - /// Initialize the stateless trie using the `ExecutionWitness` - fn new( - witness: &ExecutionWitness, - pre_state_root: B256, - ) -> Result<(Self, B256Map), StatelessValidationError> - where - Self: Sized; - - /// Returns the `TrieAccount` that corresponds to the `Address` - /// - /// This method will error if the `ExecutionWitness` is not able to guarantee - /// that the account is missing from the Trie _and_ the witness was complete. - fn account(&self, address: Address) -> Result, ProviderError>; - - /// Returns the storage slot value that corresponds to the given (address, slot) tuple. - /// - /// This method will error if the `ExecutionWitness` is not able to guarantee - /// that the storage was missing from the Trie _and_ the witness was complete. - fn storage(&self, address: Address, slot: U256) -> Result; - - /// Computes the new state root from the `HashedPostState`. - fn calculate_state_root( - &mut self, - state: HashedPostState, - ) -> Result; -} - -/// `StatelessSparseTrie` structure for usage during stateless validation -#[derive(Debug)] -pub struct StatelessSparseTrie { - inner: SparseStateTrie, -} - -impl StatelessSparseTrie { - /// Initialize the stateless trie using the `ExecutionWitness` - /// - /// Note: Currently this method does not check that the `ExecutionWitness` - /// is complete for all of the preimage keys. - pub fn new( - witness: &ExecutionWitness, - pre_state_root: B256, - ) -> Result<(Self, B256Map), StatelessValidationError> { - verify_execution_witness(witness, pre_state_root) - .map(|(inner, bytecode)| (Self { inner }, bytecode)) - } - - /// Returns the `TrieAccount` that corresponds to the `Address` - /// - /// This method will error if the `ExecutionWitness` is not able to guarantee - /// that the account is missing from the Trie _and_ the witness was complete. - pub fn account(&self, address: Address) -> Result, ProviderError> { - let hashed_address = keccak256(address); - - if let Some(bytes) = self.inner.get_account_value(&hashed_address) { - let account = TrieAccount::decode(&mut bytes.as_slice())?; - return Ok(Some(account)) - } - - if !self.inner.check_valid_account_witness(hashed_address) { - return Err(ProviderError::TrieWitnessError(format!( - "incomplete account witness for {hashed_address:?}" - ))); - } - - Ok(None) - } - - /// Returns the storage slot value that corresponds to the given (address, slot) tuple. - /// - /// This method will error if the `ExecutionWitness` is not able to guarantee - /// that the storage was missing from the Trie _and_ the witness was complete. - pub fn storage(&self, address: Address, slot: U256) -> Result { - let hashed_address = keccak256(address); - let hashed_slot = keccak256(B256::from(slot)); - - if let Some(raw) = self.inner.get_storage_slot_value(&hashed_address, &hashed_slot) { - return Ok(U256::decode(&mut raw.as_slice())?) - } - - // Storage slot value is not present in the trie, validate that the witness is complete. - // If the account exists in the trie... - if let Some(bytes) = self.inner.get_account_value(&hashed_address) { - // ...check that its storage is either empty or the storage trie was sufficiently - // revealed... - let account = TrieAccount::decode(&mut bytes.as_slice())?; - if account.storage_root != EMPTY_ROOT_HASH && - !self.inner.check_valid_storage_witness(hashed_address, hashed_slot) - { - return Err(ProviderError::TrieWitnessError(format!( - "incomplete storage witness: prover must supply exclusion proof for slot {hashed_slot:?} in account {hashed_address:?}" - ))); - } - } else if !self.inner.check_valid_account_witness(hashed_address) { - // ...else if account is missing, validate that the account trie was sufficiently - // revealed. - return Err(ProviderError::TrieWitnessError(format!( - "incomplete account witness for {hashed_address:?}" - ))); - } - - Ok(U256::ZERO) - } - - /// Computes the new state root from the `HashedPostState`. - pub fn calculate_state_root( - &mut self, - state: HashedPostState, - ) -> Result { - calculate_state_root(&mut self.inner, state) - .map_err(|_e| StatelessValidationError::StatelessStateRootCalculationFailed) - } -} - -impl StatelessTrie for StatelessSparseTrie { - fn new( - witness: &ExecutionWitness, - pre_state_root: B256, - ) -> Result<(Self, B256Map), StatelessValidationError> { - Self::new(witness, pre_state_root) - } - - fn account(&self, address: Address) -> Result, ProviderError> { - self.account(address) - } - - fn storage(&self, address: Address, slot: U256) -> Result { - self.storage(address, slot) - } - - fn calculate_state_root( - &mut self, - state: HashedPostState, - ) -> Result { - self.calculate_state_root(state) - } -} - -/// Verifies execution witness [`ExecutionWitness`] against an expected pre-state root. -/// -/// This function takes the RLP-encoded values provided in [`ExecutionWitness`] -/// (which includes state trie nodes, storage trie nodes, and contract bytecode) -/// and uses it to populate a new [`SparseStateTrie`]. -/// -/// If the computed root hash matches the `pre_state_root`, it signifies that the -/// provided execution witness is consistent with that pre-state root. In this case, the function -/// returns the populated [`SparseStateTrie`] and a [`B256Map`] containing the -/// contract bytecode (mapping code hash to [`Bytecode`]). -/// -/// The bytecode has a separate mapping because the [`SparseStateTrie`] does not store the -/// contract bytecode, only the hash of it (code hash). -/// -/// If the roots do not match, it returns an error indicating the witness is invalid -/// for the given `pre_state_root` (see `StatelessValidationError::PreStateRootMismatch`). -// Note: This approach might be inefficient for ZKVMs requiring minimal memory operations, which -// would explain why they have for the most part re-implemented this function. -fn verify_execution_witness( - witness: &ExecutionWitness, - pre_state_root: B256, -) -> Result<(SparseStateTrie, B256Map), StatelessValidationError> { - let provider_factory = DefaultTrieNodeProviderFactory; - let mut trie = SparseStateTrie::new(); - let mut state_witness = B256Map::default(); - let mut bytecode = B256Map::default(); - - for rlp_encoded in &witness.state { - let hash = keccak256(rlp_encoded); - state_witness.insert(hash, rlp_encoded.clone()); - } - for rlp_encoded in &witness.codes { - let hash = keccak256(rlp_encoded); - bytecode.insert(hash, Bytecode::new_raw(rlp_encoded.clone())); - } - - // Reveal the witness with our state root - // This method builds a trie using the sparse trie using the state_witness with - // the root being the pre_state_root. - // Here are some things to note: - // - You can pass in more witnesses than is needed for the block execution. - // - If you try to get an account and it has not been seen. This means that the account - // was not inserted into the Trie. It does not mean that the account does not exist. - // In order to determine an account not existing, we must do an exclusion proof. - trie.reveal_witness(pre_state_root, &state_witness) - .map_err(|_e| StatelessValidationError::WitnessRevealFailed { pre_state_root })?; - - // Calculate the root - let computed_root = trie - .root(&provider_factory) - .map_err(|_e| StatelessValidationError::StatelessPreStateRootCalculationFailed)?; - - if computed_root == pre_state_root { - Ok((trie, bytecode)) - } else { - Err(StatelessValidationError::PreStateRootMismatch { - got: computed_root, - expected: pre_state_root, - }) - } -} - -// Copied and modified from ress: https://github.com/paradigmxyz/ress/blob/06bf2c4788e45b8fcbd640e38b6243e6f87c4d0e/crates/engine/src/tree/root.rs -/// Calculates the post-execution state root by applying state changes to a sparse trie. -/// -/// This function takes a [`SparseStateTrie`] with the pre-state and a [`HashedPostState`] -/// containing account and storage changes resulting from block execution (state diff). -/// -/// It modifies the input `trie` in place to reflect these changes and then calculates the -/// final post-execution state root. -fn calculate_state_root( - trie: &mut SparseStateTrie, - state: HashedPostState, -) -> SparseStateTrieResult { - // 1. Apply storage‑slot updates and compute each contract’s storage root - // - // - // We walk over every (address, storage) pair in deterministic order - // and update the corresponding per‑account storage trie in‑place. - // When we’re done we collect (address, updated_storage_trie) in a `Vec` - // so that we can insert them back into the outer state trie afterwards ― this avoids - // borrowing issues. - let mut storage_results = Vec::with_capacity(state.storages.len()); - - // In `verify_execution_witness` a `DefaultTrieNodeProviderFactory` is used, so we use the same - // again in here. - let provider_factory = DefaultTrieNodeProviderFactory; - let storage_provider = DefaultTrieNodeProvider; - - for (address, storage) in state.storages.into_iter().sorted_unstable_by_key(|(addr, _)| *addr) { - // Take the existing storage trie (or create an empty, “revealed” one) - let mut storage_trie = - trie.take_storage_trie(&address).unwrap_or_else(RevealableSparseTrie::revealed_empty); - - if storage.wiped { - storage_trie.wipe()?; - } - - // Apply slot‑level changes - for (hashed_slot, value) in - storage.storage.into_iter().sorted_unstable_by_key(|(slot, _)| *slot) - { - let nibbles = Nibbles::unpack(hashed_slot); - if value.is_zero() { - storage_trie.remove_leaf(&nibbles, &storage_provider)?; - } else { - storage_trie.update_leaf( - nibbles, - alloy_rlp::encode_fixed_size(&value).to_vec(), - &storage_provider, - )?; - } - } - - // Finalise the storage‑trie root before pushing the result - storage_trie.root(); - storage_results.push((address, storage_trie)); - } - - // Insert every updated storage trie back into the outer state trie - for (address, storage_trie) in storage_results { - trie.insert_storage_trie(address, storage_trie); - } - - // 2. Apply account‑level updates and (re)encode the account nodes - // Update accounts with new values - // TODO: upstream changes into reth so that `SparseStateTrie::update_account` handles this - let mut account_rlp_buf = Vec::with_capacity(TRIE_ACCOUNT_RLP_MAX_SIZE); - - for (hashed_address, account) in - state.accounts.into_iter().sorted_unstable_by_key(|(addr, _)| *addr) - { - let nibbles = Nibbles::unpack(hashed_address); - - // Determine which storage root should be used for this account - let storage_root = if let Some(storage_trie) = trie.storage_trie_mut(&hashed_address) { - storage_trie.root() - } else if let Some(value) = trie.get_account_value(&hashed_address) { - TrieAccount::decode(&mut &value[..])?.storage_root - } else { - EMPTY_ROOT_HASH - }; - - // Decide whether to remove or update the account leaf - if let Some(account) = account { - account_rlp_buf.clear(); - account.into_trie_account(storage_root).encode(&mut account_rlp_buf); - trie.update_account_leaf(nibbles, account_rlp_buf.clone(), &provider_factory)?; - } else { - trie.remove_account_leaf(&nibbles, &provider_factory)?; - } - } - - // Return new state root - trie.root(&provider_factory) -} diff --git a/crates/stateless/src/validation.rs b/crates/stateless/src/validation.rs deleted file mode 100644 index a3a8ba7b2d..0000000000 --- a/crates/stateless/src/validation.rs +++ /dev/null @@ -1,330 +0,0 @@ -use crate::{ - recover_block::{recover_block_with_public_keys, UncompressedPublicKey}, - trie::{StatelessSparseTrie, StatelessTrie}, - witness_db::WitnessDatabase, - ExecutionWitness, -}; -use alloc::{ - collections::BTreeMap, - fmt::Debug, - string::{String, ToString}, - sync::Arc, - vec::Vec, -}; -use alloy_consensus::{BlockHeader, Header}; -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, EthereumReceipt}; -use reth_evm::{ - execute::{BlockExecutionOutput, Executor}, - ConfigureEvm, -}; -use reth_primitives_traits::{RecoveredBlock, SealedHeader}; -use reth_trie_common::{HashedPostState, KeccakKeyHasher}; - -/// BLOCKHASH ancestor lookup window limit per EVM (number of most recent blocks accessible). -const BLOCKHASH_ANCESTOR_LIMIT: usize = 256; - -/// Errors that can occur during stateless validation. -#[derive(Debug, thiserror::Error)] -pub enum StatelessValidationError { - /// Error when the number of ancestor headers exceeds the limit. - #[error("ancestor header count ({count}) exceeds limit ({limit})")] - AncestorHeaderLimitExceeded { - /// The number of headers provided. - count: usize, - /// The limit. - limit: usize, - }, - - /// Error when the ancestor headers do not form a contiguous chain. - #[error("invalid ancestor chain")] - InvalidAncestorChain, - - /// Error when revealing the witness data failed. - #[error("failed to reveal witness data for pre-state root {pre_state_root}")] - WitnessRevealFailed { - /// The pre-state root used for verification. - pre_state_root: B256, - }, - - /// Error during stateless block execution. - #[error("stateless block execution failed: {0}")] - StatelessExecutionFailed(String), - - /// Error during consensus validation of the block. - #[error("consensus validation failed: {0}")] - ConsensusValidationFailed(#[from] ConsensusError), - - /// Error during stateless state root calculation. - #[error("stateless state root calculation failed")] - StatelessStateRootCalculationFailed, - - /// Error calculating the pre-state root from the witness data. - #[error("stateless pre-state root calculation failed")] - StatelessPreStateRootCalculationFailed, - - /// Error when required ancestor headers are missing (e.g., parent header for pre-state root). - #[error("missing required ancestor headers")] - MissingAncestorHeader, - - /// Error when deserializing ancestor headers - #[error("could not deserialize ancestor headers")] - HeaderDeserializationFailed, - - /// Error when the computed state root does not match the one in the block header. - #[error("mismatched post-state root: {got}\n {expected}")] - PostStateRootMismatch { - /// The computed post-state root - got: B256, - /// The expected post-state root; in the block header - expected: B256, - }, - - /// Error when the computed pre-state root does not match the expected one. - #[error("mismatched pre-state root: {got} \n {expected}")] - PreStateRootMismatch { - /// The computed pre-state root - got: B256, - /// The expected pre-state root from the previous block - expected: B256, - }, - - /// Error during signer recovery. - #[error("signer recovery failed")] - SignerRecovery, - - /// Error when signature has non-normalized s value in homestead block. - #[error("signature s value not normalized for homestead block")] - HomesteadSignatureNotNormalized, - - /// Custom error. - #[error("{0}")] - Custom(&'static str), -} - -/// Performs stateless validation of a block using the provided witness data. -/// -/// This function attempts to fully validate a given `current_block` statelessly, ie without access -/// to a persistent database. -/// It relies entirely on the `witness` data and `ancestor_headers` -/// provided alongside the block. -/// -/// The witness data is validated in the following way: -/// -/// 1. **Ancestor Header Verification:** Checks if the `ancestor_headers` are present, form a -/// contiguous chain back from `current_block`'s parent, and do not exceed the `BLOCKHASH` opcode -/// limit using `compute_ancestor_hashes`. We must have at least one ancestor header, even if the -/// `BLOCKHASH` opcode is not used because we need the state root of the previous block to verify -/// the pre state reads. -/// -/// 2. **Pre-State Verification:** Retrieves the expected `pre_state_root` from the parent header -/// from `ancestor_headers`. Verifies the provided [`ExecutionWitness`] against the -/// `pre_state_root`. -/// -/// 3. **Chain Verification:** The code currently does not verify the [`EthChainSpec`] and expects a -/// higher level function to assert that this is correct by, for example, asserting that it is -/// equal to the Ethereum Mainnet `ChainSpec` or asserting against the genesis hash that this -/// `ChainSpec` defines. -/// -/// High Level Overview of functionality: -/// -/// - Verify all state accesses against a trusted pre-state root -/// - Put all state accesses into an in-memory database -/// - Use the in-memory database to execute the block -/// - Validate the output of block execution (e.g. receipts, logs, requests) -/// - Compute the post-state root using the state-diff from block execution -/// - Check that the post-state root is the state root in the block. -/// -/// If all steps succeed the function returns `Some` containing the hash of the validated -/// `current_block`. -pub fn stateless_validation( - current_block: Block, - public_keys: Vec, - witness: ExecutionWitness, - chain_spec: Arc, - evm_config: E, -) -> Result<(B256, BlockExecutionOutput), StatelessValidationError> -where - ChainSpec: Send + Sync + EthChainSpec
+ EthereumHardforks + Debug, - E: ConfigureEvm + Clone + 'static, -{ - stateless_validation_with_trie::( - current_block, - public_keys, - witness, - chain_spec, - evm_config, - ) -} - -/// Performs stateless validation of a block using a custom `StatelessTrie` implementation. -/// -/// This is a generic version of `stateless_validation` that allows users to provide their own -/// implementation of the `StatelessTrie` for custom trie backends or optimizations. -/// -/// See `stateless_validation` for detailed documentation of the validation process. -pub fn stateless_validation_with_trie( - current_block: Block, - public_keys: Vec, - witness: ExecutionWitness, - chain_spec: Arc, - evm_config: E, -) -> Result<(B256, BlockExecutionOutput), StatelessValidationError> -where - T: StatelessTrie, - ChainSpec: Send + Sync + EthChainSpec
+ EthereumHardforks + Debug, - E: ConfigureEvm + Clone + 'static, -{ - let current_block = recover_block_with_public_keys(current_block, public_keys, &*chain_spec)?; - - let mut ancestor_headers: Vec<_> = witness - .headers - .iter() - .map(|bytes| { - let hash = keccak256(bytes); - alloy_rlp::decode_exact::
(bytes) - .map(|h| SealedHeader::new(h, hash)) - .map_err(|_| StatelessValidationError::HeaderDeserializationFailed) - }) - .collect::>()?; - // Sort the headers by their block number to ensure that they are in - // ascending order. - ancestor_headers.sort_by_key(|header| header.number()); - - // Enforce BLOCKHASH ancestor headers limit (256 most recent blocks) - let count = ancestor_headers.len(); - if count > BLOCKHASH_ANCESTOR_LIMIT { - return Err(StatelessValidationError::AncestorHeaderLimitExceeded { - count, - limit: BLOCKHASH_ANCESTOR_LIMIT, - }); - } - - // 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)?; - - // 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 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, 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); - - // Execute the block - let executor = evm_config.executor(db); - let output = executor - .execute(¤t_block) - .map_err(|e| StatelessValidationError::StatelessExecutionFailed(e.to_string()))?; - - // Post validation checks - validate_block_post_execution( - ¤t_block, - &chain_spec, - &output.receipts, - &output.requests, - None, - ) - .map_err(StatelessValidationError::ConsensusValidationFailed)?; - - // Compute and check the post state root - let hashed_state = HashedPostState::from_bundle_state::(&output.state.state); - let state_root = trie.calculate_state_root(hashed_state)?; - if state_root != current_block.state_root { - return Err(StatelessValidationError::PostStateRootMismatch { - got: state_root, - expected: current_block.state_root, - }); - } - - // Return block hash - Ok((current_block.hash_slow(), output)) -} - -/// Performs consensus validation checks on a block without execution or state validation. -/// -/// This function validates a block against Ethereum consensus rules by: -/// -/// 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 -/// -/// 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 -/// transition function. -fn validate_block_consensus( - chain_spec: Arc, - block: &RecoveredBlock, - parent: &SealedHeader
, -) -> Result<(), StatelessValidationError> -where - ChainSpec: Send + Sync + EthChainSpec
+ EthereumHardforks + Debug, -{ - 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)?; - - Ok(()) -} - -/// Verifies the contiguity, number of ancestor headers and extracts their hashes. -/// -/// This function is used to prepare the data required for the `BLOCKHASH` -/// opcode in a stateless execution context. -/// -/// It verifies that the provided `ancestor_headers` form a valid, unbroken chain leading back from -/// the parent of the `current_block`. -/// -/// Note: This function becomes obsolete if EIP-2935 is implemented. -/// Note: The headers are assumed to be in ascending order. -/// -/// If both checks pass, it returns a [`BTreeMap`] mapping the block number of each -/// ancestor header to its corresponding block hash. -fn compute_ancestor_hashes( - current_block: &RecoveredBlock, - ancestor_headers: &[SealedHeader], -) -> Result, StatelessValidationError> { - let mut ancestor_hashes = BTreeMap::new(); - - 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() { - return Err(StatelessValidationError::InvalidAncestorChain); // Blocks must be contiguous - } - - if parent_header.number + 1 != child_header.number { - return Err(StatelessValidationError::InvalidAncestorChain); // Header number should be - // contiguous - } - - child_header = parent_header - } - - Ok(ancestor_hashes) -} diff --git a/crates/stateless/src/witness_db.rs b/crates/stateless/src/witness_db.rs deleted file mode 100644 index 86ced51804..0000000000 --- a/crates/stateless/src/witness_db.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! Provides the [`WitnessDatabase`] type, an implementation of [`reth_revm::Database`] -//! specifically designed for stateless execution environments. - -use crate::trie::StatelessTrie; -use alloc::{collections::btree_map::BTreeMap, format}; -use alloy_primitives::{map::B256Map, Address, B256, U256}; -use reth_errors::ProviderError; -use reth_revm::{bytecode::Bytecode, state::AccountInfo, Database}; - -/// An EVM database implementation backed by witness data. -/// -/// This struct implements the [`reth_revm::Database`] trait, allowing the EVM to execute -/// transactions using: -/// - Account and storage slot data provided by a [`StatelessTrie`] implementation. -/// - Bytecode and ancestor block hashes provided by in-memory maps. -/// -/// This is designed for stateless execution scenarios where direct access to a full node's -/// database is not available or desired. -#[derive(Debug)] -pub(crate) struct WitnessDatabase<'a, T> -where - T: StatelessTrie, -{ - /// Map of block numbers to block hashes. - /// This is used to service the `BLOCKHASH` opcode. - block_hashes_by_block_number: BTreeMap, - /// Map of code hashes to bytecode. - /// Used to fetch contract code needed during execution. - bytecode: B256Map, - /// The sparse Merkle Patricia Trie containing account and storage state. - /// This is used to provide account/storage values during EVM execution. - trie: &'a T, -} - -impl<'a, T> WitnessDatabase<'a, T> -where - T: StatelessTrie, -{ - /// Creates a new [`WitnessDatabase`] instance. - /// - /// # Assumptions - /// - /// This function assumes: - /// 1. The provided `trie` has been populated with state data consistent with a known state root - /// (e.g., using witness data and verifying against a parent block's state root). - /// 2. The `bytecode` map contains all bytecode corresponding to code hashes present in the - /// account data within the `trie`. - /// 3. The `ancestor_hashes` map contains the block hashes for the relevant ancestor blocks (up - /// to 256 including the current block number). It assumes these hashes correspond to a - /// contiguous chain of blocks. The caller is responsible for verifying the contiguity and - /// the block limit. - pub(crate) const fn new( - trie: &'a T, - bytecode: B256Map, - ancestor_hashes: BTreeMap, - ) -> Self { - Self { trie, block_hashes_by_block_number: ancestor_hashes, bytecode } - } -} - -impl Database for WitnessDatabase<'_, T> -where - T: StatelessTrie, -{ - /// The database error type. - type Error = ProviderError; - - /// Get basic account information by hashing the address and looking up the account RLP - /// in the underlying [`StatelessTrie`] implementation. - /// - /// Returns `Ok(None)` if the account is not found in the trie. - fn basic(&mut self, address: Address) -> Result, Self::Error> { - self.trie.account(address).map(|opt| { - opt.map(|account| AccountInfo { - balance: account.balance, - nonce: account.nonce, - code_hash: account.code_hash, - code: None, - account_id: None, - }) - }) - } - - /// Get storage value of an account at a specific slot. - /// - /// Returns `U256::ZERO` if the slot is not found in the trie. - fn storage(&mut self, address: Address, slot: U256) -> Result { - self.trie.storage(address, slot) - } - - /// Get account code by its hash from the provided bytecode map. - /// - /// Returns an error if the bytecode for the given hash is not found in the map. - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.bytecode.get(&code_hash).cloned().ok_or_else(|| { - ProviderError::TrieWitnessError(format!("bytecode for {code_hash} not found")) - }) - } - - /// Get block hash by block number from the provided ancestor hashes map. - /// - /// Returns an error if the hash for the given block number is not found in the map. - fn block_hash(&mut self, block_number: u64) -> Result { - self.block_hashes_by_block_number - .get(&block_number) - .copied() - .ok_or(ProviderError::StateForNumberNotFound(block_number)) - } -} diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index 21f0076a12..a18a01f3a3 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -27,8 +27,7 @@ reth-provider = { workspace = true, features = ["test-utils"] } reth-evm.workspace = true reth-evm-ethereum.workspace = true reth-ethereum-consensus.workspace = true -reth-revm = { workspace = true, features = ["std", "witness"] } -reth-stateless = { workspace = true } +reth-revm.workspace = true reth-tracing.workspace = true reth-trie.workspace = true reth-trie-db.workspace = true diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index c4bfbc34b4..ecfb60cf42 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -4,28 +4,22 @@ use crate::{ models::{BlockchainTest, ForkSpec}, Case, Error, Suite, }; -use alloy_rlp::{Decodable, Encodable}; +use alloy_rlp::Decodable; use rayon::iter::{IndexedParallelIterator, ParallelIterator}; use reth_chainspec::ChainSpec; use reth_consensus::{Consensus, HeaderValidator}; use reth_db_common::init::{insert_genesis_hashes, insert_genesis_history, insert_genesis_state}; use reth_ethereum_consensus::{validate_block_post_execution, EthBeaconConsensus}; -use reth_ethereum_primitives::{Block, TransactionSigned}; +use reth_ethereum_primitives::Block; use reth_evm::{execute::Executor, ConfigureEvm}; use reth_evm_ethereum::EthEvmConfig; -use reth_primitives_traits::{ - Block as BlockTrait, ParallelBridgeBuffered, RecoveredBlock, SealedBlock, -}; +use reth_primitives_traits::{ParallelBridgeBuffered, RecoveredBlock, SealedBlock}; use reth_provider::{ test_utils::create_test_provider_factory_with_chain_spec, BlockWriter, DatabaseProviderFactory, - ExecutionOutcome, HeaderProvider, HistoryWriter, OriginalValuesKnown, StateProofProvider, - StateWriteConfig, StateWriter, StaticFileProviderFactory, StaticFileSegment, StaticFileWriter, -}; -use reth_revm::{database::StateProviderDatabase, witness::ExecutionWitnessRecord, State}; -use reth_stateless::{ - trie::StatelessSparseTrie, validation::stateless_validation_with_trie, ExecutionWitness, - UncompressedPublicKey, + ExecutionOutcome, HistoryWriter, OriginalValuesKnown, StateWriteConfig, StateWriter, + StaticFileProviderFactory, StaticFileSegment, StaticFileWriter, }; +use reth_revm::database::StateProviderDatabase; use reth_trie::{HashedPostState, KeccakKeyHasher, StateRoot}; use reth_trie_db::DatabaseStateRoot; use std::{ @@ -103,35 +97,31 @@ impl BlockchainTestCase { } /// Execute a single `BlockchainTest`, validating the outcome against the - /// expectations encoded in the JSON file. Returns the list of executed blocks - /// with their execution witnesses. - pub fn run_single_case( - name: &str, - case: &BlockchainTest, - ) -> Result, ExecutionWitness)>, Error> { + /// expectations encoded in the JSON file. + pub fn run_single_case(name: &str, case: &BlockchainTest) -> Result<(), Error> { let expectation = Self::expected_failure(case); match run_case(case) { // All blocks executed successfully. - Ok(program_inputs) => { + Ok(()) => { // Check if the test case specifies that it should have failed if let Some((block, msg)) = expectation { Err(Error::Assertion(format!( "Test case: {name}\nExpected failure at block {block} - {msg}, but all blocks succeeded", ))) } else { - Ok(program_inputs) + Ok(()) } } // A block processing failure occurred. - Err(Error::BlockProcessingFailed { block_number, partial_program_inputs, err }) => { + Err(Error::BlockProcessingFailed { block_number, err }) => { match expectation { // It happened on exactly the block we were told to fail on - Some((expected, _)) if block_number == expected => Ok(partial_program_inputs), + Some((expected, _)) if block_number == expected => Ok(()), // Uncle side‑chain edge case, we accept as long as it failed. // But we don't check the exact block number. - _ if Self::is_uncle_sidechain_case(name) => Ok(partial_program_inputs), + _ if Self::is_uncle_sidechain_case(name) => Ok(()), // Expected failure, but block number does not match Some((expected, _)) => Err(Error::Assertion(format!( @@ -139,7 +129,7 @@ impl BlockchainTestCase { ))), // No failure expected at all - bubble up original error. - None => Err(Error::BlockProcessingFailed { block_number, partial_program_inputs, err }), + None => Err(Error::BlockProcessingFailed { block_number, err }), } } @@ -198,13 +188,9 @@ impl Case for BlockchainTestCase { /// outcome. /// /// Returns: -/// - `Ok(_)` if all blocks execute successfully, returning recovered blocks and full block -/// execution witness. -/// - `Err(Error)` if any block fails to execute correctly, returning a partial block execution -/// witness if the error is of variant `BlockProcessingFailed`. -fn run_case( - case: &BlockchainTest, -) -> Result, ExecutionWitness)>, Error> { +/// - `Ok(())` if all blocks execute successfully. +/// - `Err(Error)` if any block fails to execute correctly. +fn run_case(case: &BlockchainTest) -> Result<(), Error> { // Create a new test database and initialize a provider for the test case. let chain_spec = case.network.to_chain_spec(); let factory = create_test_provider_factory_with_chain_spec(chain_spec.clone()); @@ -218,53 +204,43 @@ fn run_case( .try_recover() .unwrap(); - provider - .insert_block(&genesis_block) - .map_err(|err| Error::block_failed(0, Default::default(), err))?; + provider.insert_block(&genesis_block).map_err(|err| Error::block_failed(0, err))?; // Increment block number for receipts static file provider .static_file_provider() .latest_writer(StaticFileSegment::Receipts) .and_then(|mut writer| writer.increment_block(0)) - .map_err(|err| Error::block_failed(0, Default::default(), err))?; + .map_err(|err| Error::block_failed(0, err))?; let genesis_state = case.pre.clone().into_genesis_state(); insert_genesis_state(&provider, genesis_state.iter()) - .map_err(|err| Error::block_failed(0, Default::default(), err))?; + .map_err(|err| Error::block_failed(0, err))?; insert_genesis_hashes(&provider, genesis_state.iter()) - .map_err(|err| Error::block_failed(0, Default::default(), err))?; + .map_err(|err| Error::block_failed(0, err))?; insert_genesis_history(&provider, genesis_state.iter()) - .map_err(|err| Error::block_failed(0, Default::default(), err))?; + .map_err(|err| Error::block_failed(0, err))?; // Decode blocks let blocks = decode_blocks(&case.blocks)?; let executor_provider = EthEvmConfig::ethereum(chain_spec.clone()); let mut parent = genesis_block; - let mut program_inputs = Vec::new(); for (block_index, block) in blocks.iter().enumerate() { // Note: same as the comment on `decode_blocks` as to why we cannot use block.number let block_number = (block_index + 1) as u64; // Insert the block into the database - provider - .insert_block(block) - .map_err(|err| Error::block_failed(block_number, Default::default(), err))?; - // Commit static files, so we can query the headers for stateless execution below + provider.insert_block(block).map_err(|err| Error::block_failed(block_number, err))?; provider .static_file_provider() .commit() - .map_err(|err| Error::block_failed(block_number, Default::default(), err))?; + .map_err(|err| Error::block_failed(block_number, err))?; // Consensus checks before block execution - pre_execution_checks(chain_spec.clone(), &parent, block).map_err(|err| { - program_inputs.push((block.clone(), execution_witness_with_parent(&parent))); - Error::block_failed(block_number, program_inputs.clone(), err) - })?; - - let mut witness_record = ExecutionWitnessRecord::default(); + pre_execution_checks(chain_spec.clone(), &parent, block) + .map_err(|err| Error::block_failed(block_number, err))?; // Execute the block let state_provider = provider.latest(); @@ -272,41 +248,12 @@ fn run_case( let executor = executor_provider.batch_executor(state_db); let output = executor - .execute_with_state_closure_always(&(*block).clone(), |statedb: &State<_>| { - witness_record.record_executed_state(statedb); - }) - .map_err(|err| Error::block_failed(block_number, program_inputs.clone(), err))?; + .execute(&(*block).clone()) + .map_err(|err| Error::block_failed(block_number, err))?; // Consensus checks after block execution validate_block_post_execution(block, &chain_spec, &output.receipts, &output.requests, None) - .map_err(|err| Error::block_failed(block_number, program_inputs.clone(), err))?; - - // Generate the stateless witness - // TODO: Most of this code is copy-pasted from debug_executionWitness - let ExecutionWitnessRecord { hashed_state, codes, keys, lowest_block_number } = - witness_record; - let state = state_provider.witness(Default::default(), hashed_state)?; - let mut exec_witness = ExecutionWitness { state, codes, keys, headers: Default::default() }; - - let smallest = lowest_block_number.unwrap_or_else(|| { - // Return only the parent header, if there were no calls to the - // BLOCKHASH opcode. - block_number.saturating_sub(1) - }); - - let range = smallest..block_number; - - exec_witness.headers = provider - .headers_range(range)? - .into_iter() - .map(|header| { - let mut serialized_header = Vec::new(); - header.encode(&mut serialized_header); - serialized_header.into() - }) - .collect(); - - program_inputs.push((block.clone(), exec_witness)); + .map_err(|err| Error::block_failed(block_number, err))?; // Compute and check the post state root let hashed_state = @@ -315,11 +262,10 @@ fn run_case( provider.tx_ref(), &hashed_state.clone_into_sorted(), ) - .map_err(|err| Error::block_failed(block_number, program_inputs.clone(), err))?; + .map_err(|err| Error::block_failed(block_number, err))?; if computed_state_root != block.state_root { return Err(Error::block_failed( block_number, - program_inputs.clone(), Error::Assertion("state root mismatch".to_string()), )); } @@ -331,14 +277,14 @@ fn run_case( OriginalValuesKnown::Yes, StateWriteConfig::default(), ) - .map_err(|err| Error::block_failed(block_number, program_inputs.clone(), err))?; + .map_err(|err| Error::block_failed(block_number, err))?; provider .write_hashed_state(&hashed_state.into_sorted()) - .map_err(|err| Error::block_failed(block_number, program_inputs.clone(), err))?; + .map_err(|err| Error::block_failed(block_number, err))?; provider .update_history_indices(block.number..=block.number) - .map_err(|err| Error::block_failed(block_number, program_inputs.clone(), err))?; + .map_err(|err| Error::block_failed(block_number, err))?; // Since there were no errors, update the parent block parent = block.clone() @@ -365,25 +311,7 @@ fn run_case( } } - // Now validate using the stateless client if everything else passes - for (recovered_block, execution_witness) in &program_inputs { - let block = recovered_block.clone().into_block(); - - // Recover the actual public keys from the transaction signatures - let public_keys = recover_signers(block.body().transactions()) - .expect("Failed to recover public keys from transaction signatures"); - - stateless_validation_with_trie::( - block, - public_keys, - execution_witness.clone(), - chain_spec.clone(), - EthEvmConfig::new(chain_spec.clone()), - ) - .expect("stateless validation failed"); - } - - Ok(program_inputs) + Ok(()) } fn decode_blocks( @@ -396,12 +324,10 @@ fn decode_blocks( let block_number = (block_index + 1) as u64; let decoded = SealedBlock::::decode(&mut block.rlp.as_ref()) - .map_err(|err| Error::block_failed(block_number, Default::default(), err))?; + .map_err(|err| Error::block_failed(block_number, err))?; - let recovered_block = decoded - .clone() - .try_recover() - .map_err(|err| Error::block_failed(block_number, Default::default(), err))?; + let recovered_block = + decoded.clone().try_recover().map_err(|err| Error::block_failed(block_number, err))?; blocks.push(recovered_block); } @@ -430,26 +356,6 @@ fn pre_execution_checks( Ok(()) } -/// Recover public keys from transaction signatures. -fn recover_signers<'a, I>(txs: I) -> Result, Box> -where - I: IntoIterator, -{ - txs.into_iter() - .enumerate() - .map(|(i, tx)| { - tx.signature() - .recover_from_prehash(&tx.signature_hash()) - .map(|keys| { - UncompressedPublicKey( - keys.to_encoded_point(false).as_bytes().try_into().unwrap(), - ) - }) - .map_err(|e| format!("failed to recover signature for tx #{i}: {e}").into()) - }) - .collect::, _>>() -} - /// Returns whether the test at the given path should be skipped. /// /// Some tests are edge cases that cannot happen on mainnet, while others are skipped for @@ -516,9 +422,3 @@ fn path_contains(path_str: &str, rhs: &[&str]) -> bool { let rhs = rhs.join(std::path::MAIN_SEPARATOR_STR); path_str.contains(&rhs) } - -fn execution_witness_with_parent(parent: &RecoveredBlock) -> ExecutionWitness { - let mut serialized_header = Vec::new(); - parent.header().encode(&mut serialized_header); - ExecutionWitness { headers: vec![serialized_header.into()], ..Default::default() } -} diff --git a/testing/ef-tests/src/result.rs b/testing/ef-tests/src/result.rs index 2d7c923997..beca01f6ac 100644 --- a/testing/ef-tests/src/result.rs +++ b/testing/ef-tests/src/result.rs @@ -1,10 +1,7 @@ //! Test results and errors use reth_db::DatabaseError; -use reth_ethereum_primitives::Block; -use reth_primitives_traits::RecoveredBlock; use reth_provider::ProviderError; -use reth_stateless::ExecutionWitness; use std::path::{Path, PathBuf}; use thiserror::Error; @@ -26,9 +23,6 @@ pub enum Error { BlockProcessingFailed { /// The block number for the block that failed block_number: u64, - /// Contains the inputs necessary for the block stateless validation guest program used in - /// zkVMs to prove the block is invalid. - partial_program_inputs: Vec<(RecoveredBlock, ExecutionWitness)>, /// The specific error #[source] err: Box, @@ -72,10 +66,9 @@ impl Error { /// Create a new [`Error::BlockProcessingFailed`] error. pub fn block_failed( block_number: u64, - partial_program_inputs: Vec<(RecoveredBlock, ExecutionWitness)>, err: impl std::error::Error + Send + Sync + 'static, ) -> Self { - Self::BlockProcessingFailed { block_number, partial_program_inputs, err: Box::new(err) } + Self::BlockProcessingFailed { block_number, err: Box::new(err) } } }