Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: astral-sh/setup-uv@v3
- uses: taiki-e/install-action@nextest
- uses: Swatinem/rust-cache@v2
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/target

testing/ef-specs/
# EF test fixtures (downloaded by scripts/setup_ef_tests.sh)
testing/ef-tests/ethereum-tests
testing/ef-tests/execution-spec-tests
64 changes: 45 additions & 19 deletions scripts/run_ef_tests.sh
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ==============================================================================
# Run script for official Ethereum Execution Specification Tests (EEST)
# ==============================================================================

# Default trie implementation
TRIE_IMPL="default"

if [[ $# -ne 1 ]]; then
echo "Usage: $0 <default|zeth>"
exit 1
# Allow overriding the trie implementation via the first argument
if [ $# -gt 0 ]; then
TRIE_IMPL="$1"
fi

TRIE_IMPL="$1"
case "$TRIE_IMPL" in
default|zeth) ;;
*)
echo "Invalid trie implementation: $TRIE_IMPL"
echo "Expected one of: default, zeth"
exit 1
;;
esac

# Setup test fixtures
"$SCRIPT_DIR/setup_ef_tests.sh"

# Run EF tests
EF_TEST_TRIE="$TRIE_IMPL" cargo nextest run --no-fail-fast -p ef-tests --release --features "asm-keccak ef-tests"
# Validate the provided trie implementation
if [[ "$TRIE_IMPL" != "default" && "$TRIE_IMPL" != "zeth" ]]; then
echo "Error: Invalid trie implementation '$TRIE_IMPL'."
echo "Expected one of: 'default', 'zeth'"
exit 1
fi

# Increase open file limit to prevent "Too many open files" (os error 24).
# This is critical for EF tests as they create many temporary database providers.
ulimit -n 10240

# Ensure fixtures are set up before running tests
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
bash "$SCRIPT_DIR/setup_ef_tests.sh"

echo "-------------------------------------------------------------------------------"
echo "Running EF tests with EF_TEST_TRIE=$TRIE_IMPL"
echo "Resource Limit: ulimit -n 10240"
echo "-------------------------------------------------------------------------------"

# Prefer cargo-nextest for better test isolation and reporting, but fallback to cargo test.
if command -v cargo-nextest &> /dev/null; then
echo "Using cargo-nextest..."
EF_TEST_TRIE="$TRIE_IMPL" cargo nextest run \
--no-fail-fast \
-p ef-tests \
--release \
--features "asm-keccak ef-tests"
else
echo "cargo-nextest not found, falling back to cargo test with --test-threads=1..."
EF_TEST_TRIE="$TRIE_IMPL" cargo test \
-p ef-tests \
--release \
--features "asm-keccak ef-tests" \
-- --test-threads=1
fi
78 changes: 64 additions & 14 deletions scripts/setup_ef_tests.sh
Original file line number Diff line number Diff line change
@@ -1,30 +1,80 @@
#!/usr/bin/env bash
set -euo pipefail

ETHEREUM_TESTS_REF="81862e4848585a438d64f911a19b3825f0f4cd95"
EEST_TESTS_TAG="v4.5.0"
# ==============================================================================
# Setup script for official Ethereum Execution Specification Tests (EEST)
# This script implements the new witness generation flow using the
# execution-specs repository and the 'uv' tool.
# ==============================================================================

# Paths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EF_TESTS_DIR="$SCRIPT_DIR/../testing/ef-tests"
EF_SPECS_DIR="$SCRIPT_DIR/../testing/ef-specs"
FIXTURES_DEST="$EF_TESTS_DIR/execution-spec-tests"

# Clone ethereum/tests if not already present
# Options
FILL_ALL=false

if [[ "${1:-}" == "--all" ]]; then
FILL_ALL=true
fi

# Ensure dependencies are installed
if ! command -v uv &> /dev/null; then
echo "Error: 'uv' is not installed. Please install it via: curl -LsSf https://astral.sh/uv/install.sh | sh"
exit 1
fi

# 1. Clone ethereum/tests (Legacy requirement for general test structure)
if [ ! -d "$EF_TESTS_DIR/ethereum-tests" ]; then
echo "Cloning ethereum/tests at $ETHEREUM_TESTS_REF..."
echo "Cloning ethereum/tests..."
mkdir -p "$EF_TESTS_DIR"
git clone --depth 1 https://github.com/ethereum/tests "$EF_TESTS_DIR/ethereum-tests"
git -C "$EF_TESTS_DIR/ethereum-tests" fetch --depth 1 origin "$ETHEREUM_TESTS_REF"
git -C "$EF_TESTS_DIR/ethereum-tests" checkout "$ETHEREUM_TESTS_REF"
else
echo "ethereum-tests already exists, skipping clone."
fi

# Download EEST fixtures if not already present
if [ ! -d "$EF_TESTS_DIR/execution-spec-tests" ] || [ -z "$(ls -A "$EF_TESTS_DIR/execution-spec-tests" 2>/dev/null)" ]; then
echo "Downloading EEST fixtures ($EEST_TESTS_TAG)..."
mkdir -p "$EF_TESTS_DIR/execution-spec-tests"
URL="https://github.com/ethereum/execution-spec-tests/releases/download/${EEST_TESTS_TAG}/fixtures_stable.tar.gz"
curl -L "$URL" | tar -xz --strip-components=1 -C "$EF_TESTS_DIR/execution-spec-tests"
# 2. Setup execution-specs repository
if [ ! -d "$EF_SPECS_DIR" ]; then
echo "Cloning execution-specs repository..."
git clone https://github.com/ethereum/execution-specs "$EF_SPECS_DIR"
cd "$EF_SPECS_DIR"
git checkout projects/zkevm-bal-devnet-3
cd "$SCRIPT_DIR/.."
else
echo "execution-spec-tests already exists, skipping download."
echo "execution-specs already exists, skipping clone."
cd "$EF_SPECS_DIR"
git checkout projects/zkevm-bal-devnet-3
cd "$SCRIPT_DIR/.."
fi

echo "EF test fixtures are ready."
# 3. Fill fixtures using the official specification tool
echo "Generating execution witnesses using uv run fill..."
cd "$EF_SPECS_DIR"

# Priority 1: Fill the most focused cases (EIP-8025 optional proofs) - Always run
echo "Filling focused Amsterdam eip8025_optional_proofs tests..."
uv run fill --clean -m "blockchain_test" --fork Amsterdam -s ./tests/amsterdam/eip8025_optional_proofs

# Priority 2: Fill the general EEST cases (>16k tests) - Optional
if [ "$FILL_ALL" = true ]; then
echo "Filling general blockchain test cases (all forks)..."
uv run fill --clean -m "blockchain_test"
else
echo "Skipping general blockchain test cases (use --all to enable)."
fi

cd "$SCRIPT_DIR/.."

# 4. Synchronize filled fixtures to the ef-tests directory
echo "Syncing filled fixtures to $FIXTURES_DEST..."
mkdir -p "$FIXTURES_DEST"

# We copy the contents of the fixtures/blockchain_tests directory from the specs repo.
# The Rust test runner uses WalkDir, so preserving the directory structure is important.
cp -r "$EF_SPECS_DIR/fixtures/blockchain_tests/"* "$FIXTURES_DEST/"

echo "Official EF test fixtures are ready."
echo "You can now run tests using: EF_TEST_TRIE=default cargo test -p ef-tests --release --features \"asm-keccak ef-tests\""
echo "To generate all fork fixtures next time, run: $0 --all"
21 changes: 14 additions & 7 deletions testing/ef-tests/src/cases/blockchain_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,13 +330,20 @@ where
validate_block_post_execution(block, &chain_spec, &output.result, None)
.map_err(|err| Error::block_failed(block_number, program_inputs.clone(), err))?;

// Generate the stateless witness
let exec_witness = witness_record.into_execution_witness(
&state_provider,
&provider,
block_number,
ExecutionWitnessMode::default(),
)?;
// In case execution witnesses are not there, generate them
let exec_witness: stateless::ExecutionWitness =
match &case.blocks[block_index].execution_witness {
Some(exec_witness) => exec_witness.clone().into(),
None => {
// Generate the stateless witness
witness_record.into_execution_witness(
&state_provider,
&provider,
block_number,
ExecutionWitnessMode::default(),
)?
}
};

program_inputs.push((block.clone(), exec_witness));

Expand Down
35 changes: 34 additions & 1 deletion testing/ef-tests/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use alloy_primitives::{Address, B64, B256, Bloom, Bytes, U256, keccak256};
use reth_chainspec::{ChainSpec, ChainSpecBuilder, EthereumHardfork, ForkCondition};
use reth_db_api::{cursor::DbDupCursorRO, tables, transaction::DbTx};
use reth_primitives_traits::SealedHeader;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
ops::Deref,
Expand Down Expand Up @@ -142,6 +142,8 @@ pub struct Block {
pub transaction_sequence: Option<Vec<TransactionSequence>>,
/// Withdrawals
pub withdrawals: Option<Withdrawals>,
/// Execution witness for the block.
pub execution_witness: Option<ExecutionWitness>,
}

/// Transaction sequence in block
Expand All @@ -154,6 +156,34 @@ pub struct TransactionSequence {
valid: String,
}

/// Represents the execution witness of a block. Contains lists of required preimages and
/// headers used during execution and verification.
/// Wrapper around the stateless execution witness.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExecutionWitness {
/// List of all hashed trie nodes preimages that were required during the execution of
/// the block, including during state root recomputation.
pub state: Vec<Bytes>,
/// List of all contract codes (created / accessed) preimages that were required during
/// the execution of the block, including during state root recomputation.
pub codes: Vec<Bytes>,
/// Optional since official spec does not include keys in the witness.
pub keys: Option<Vec<Bytes>>,
/// RLP-encoded block headers required for proving correctness of stateless execution.
pub headers: Vec<Bytes>,
}

impl From<ExecutionWitness> for stateless::ExecutionWitness {
fn from(witness: ExecutionWitness) -> Self {
stateless::ExecutionWitness {
state: witness.state,
codes: witness.codes,
keys: Vec::new(),
headers: witness.headers,
}
}
}

/// Ethereum blockchain test data state.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Default)]
pub struct State(BTreeMap<Address, Account>);
Expand Down Expand Up @@ -265,6 +295,8 @@ impl Account {
/// Fork specification.
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Ord, Clone, Copy, Deserialize)]
pub enum ForkSpec {
/// Amsterdam
Amsterdam,
/// Frontier
Frontier,
/// Frontier to Homestead
Expand Down Expand Up @@ -354,6 +386,7 @@ impl ForkSpec {
let spec_builder = ChainSpecBuilder::mainnet().reset();

match self {
Self::Amsterdam => spec_builder.amsterdam_activated(),
Self::Frontier => spec_builder.frontier_activated(),
Self::FrontierToHomesteadAt5 => spec_builder
.frontier_activated()
Expand Down
Loading
Loading