Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
97436e2
feat(l1): EIP-3155 StructLog tracer (geth structLogLegacy compatible)
edg-l May 8, 2026
638c075
refactor(test): move EIP-3155 fixtures under test/tests/levm/
edg-l May 8, 2026
f4b4045
refactor(l1): use strict EIP-3155 step format instead of geth structL…
edg-l May 11, 2026
bab41c0
chore(test): add trailing newlines to EIP-3155 fixtures
edg-l May 11, 2026
f9e50ff
refactor(test): drop EIP-3155 snapshot fixtures, keep one smoke test
edg-l May 11, 2026
3ddcd3e
refactor(rpc): rename structLog tracer to opcodeTracer
edg-l May 11, 2026
d3773e7
refactor: rename StructLog types to OpcodeStep (opcodeTracer)
edg-l May 11, 2026
f9dd5c9
refactor(test): move opcodeTracer tests under test/, drop unit tests
edg-l May 11, 2026
7af79f1
chore(perf): drop opcode_tracer disabled-path microbench
edg-l May 11, 2026
dd9ac5a
chore(rpc): silence enum_variant_names on TracerType
edg-l May 11, 2026
c6112c2
refactor(l1): opcodeTracer cleanup and SLOAD/SSTORE storage-context fix
edg-l May 11, 2026
4f8f3f9
refactor(l1): opcodeTracer review fixes
edg-l May 12, 2026
d5d13cf
fix(l1): opcodeTracer wrapper alignment + JUMPDEST under fused jump
edg-l May 12, 2026
bdb0c49
chore(localnet): bump ethereum-package, EL/CL images, mark ethrex sup…
edg-l May 12, 2026
f49a152
refactor(levm): share build_step between pre_step_capture + synth JUM…
edg-l May 12, 2026
cb8f2be
Merge branch 'main' into feat/eip-3155-tracer
ElFantasma May 14, 2026
d058237
Merge branch 'main' into feat/eip-3155-tracer
ElFantasma May 15, 2026
450f5c2
Merge branch 'main' into feat/eip-3155-tracer
ElFantasma May 15, 2026
dc11a20
fix(common): emit EIP-3155-compliant op/opName/gas/stack fields in Op…
ElFantasma May 15, 2026
8c0118b
Merge branch 'main' into feat/eip-3155-tracer
ElFantasma May 18, 2026
c157f7a
feat(tooling): add trace_compare helper for diffing debug_traceTransa…
ElFantasma May 18, 2026
7576dec
fix(tooling): make trace_compare script work on macOS's bash 3.2
ElFantasma May 18, 2026
0197fc7
fix(tooling): parse service name from column 2 of kurtosis enclave in…
ElFantasma May 18, 2026
92c00ff
fix(tooling): explicitly request opcodeTracer for ethrex (default dif…
ElFantasma May 18, 2026
bfcb482
refactor(common): separate OpcodeStep data from wire format via wrapp…
ElFantasma May 18, 2026
2e86843
feat(common): gate structLogger memSize/returnData/refund emission vi…
ElFantasma May 18, 2026
e1f38c2
fix(common): emit structLogger refund when non-zero, matching geth om…
ElFantasma May 18, 2026
3498f58
feat(levm): accumulate touched storage slots across opcode trace
ElFantasma May 19, 2026
29e8a15
fix(levm): patch refund post-opcode in finalize_step to match geth st…
ElFantasma May 19, 2026
52c717c
Merge branch 'main' into feat/eip-3155-tracer
ElFantasma May 19, 2026
3c6f9b0
feat(l1): add statetest subcommand to ef_tests-statev2 for goevmlab f…
ElFantasma May 15, 2026
d0efbb1
fix(l1): distinguish statetest internal errors from root mismatches, …
ElFantasma May 15, 2026
7d640ba
feat(l1): emit EIP-3155 wire shape from statetest sink via Eip3155Ste…
ElFantasma May 19, 2026
fc178d3
fix(l1): propagate fixture parse errors instead of panicking
ElFantasma May 19, 2026
887155c
test(l1): pin EIP-3155 wire format and stateRoot line shape for goevmlab
ElFantasma May 19, 2026
37be064
Merge branch 'main' into feat/6575-statetest-cli
ElFantasma May 21, 2026
8f202b5
Merge branch 'main' into feat/6575-statetest-cli
ElFantasma May 21, 2026
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
49 changes: 46 additions & 3 deletions tooling/ef_tests/state_v2/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,57 @@
#![allow(clippy::all)]

use clap::Parser;
use std::process::ExitCode;

use clap::{Parser, Subcommand};
use ef_tests_statev2::modules::{
error::RunnerError,
parser::{RunnerOptions, parse_tests},
statetest::{self, StatetestOptions},
};

#[derive(Parser, Debug)]
#[command(name = "ef-tests-state-v2")]
struct Cli {
#[command(subcommand)]
command: Option<Command>,

/// Default (no subcommand): bulk-run the EF state-test suite.
#[command(flatten)]
runner: RunnerOptions,
}

#[derive(Subcommand, Debug)]
enum Command {
/// Run a single EF state-test fixture and emit EIP-3155 trace + stateRoot to
/// stderr. Designed for goevmlab differential fuzzing.
Statetest(StatetestOptions),
}

#[tokio::main]
pub async fn main() -> Result<(), RunnerError> {
let mut runner_options = RunnerOptions::parse();
pub async fn main() -> ExitCode {
let cli = Cli::parse();

// Errors from a subcommand map to exit code 2 so that goevmlab can distinguish
// a state-root mismatch (deliberate exit 1) from an actual internal failure.
match cli.command {
Some(Command::Statetest(opts)) => match statetest::run(opts).await {
Ok(code) => code,
Err(e) => {
eprintln!("statetest error: {e:?}");
ExitCode::from(2)
}
},
None => match run_bulk(cli.runner).await {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("error: {e:?}");
ExitCode::from(2)
}
},
}
}

async fn run_bulk(mut runner_options: RunnerOptions) -> Result<(), RunnerError> {
println!("Runner options: {:#?}", runner_options);

println!("\nParsing test files...");
Expand Down
17 changes: 14 additions & 3 deletions tooling/ef_tests/state_v2/src/modules/deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,16 @@ where
let post_deserialized = HashMap::<String, Vec<RawPostValue>>::deserialize(deserializer)?;
let mut post_parsed = HashMap::new();
for (fork_str, values) in post_deserialized {
// Keep names in sync with the `Fork` enum in `crates/common/types/genesis.rs`.
// An unknown fork name is a hard error so that newly-emitted fixture forks
// surface as a build break (forcing a deserializer/Fork update), rather than
// silently dropping test coverage.
let fork = match fork_str.as_str() {
"Frontier" => Fork::Frontier,
"Homestead" => Fork::Homestead,
"EIP150" => Fork::Tangerine,
"EIP158" => Fork::SpuriousDragon,
"Byzantium" => Fork::Byzantium,
"Constantinople" => Fork::Constantinople,
"ConstantinopleFix" | "Petersburg" => Fork::Petersburg,
"Istanbul" => Fork::Istanbul,
Expand All @@ -150,9 +157,13 @@ where
"Shanghai" => Fork::Shanghai,
"Cancun" => Fork::Cancun,
"Prague" => Fork::Prague,
"Byzantium" => Fork::Byzantium,
"EIP158" => Fork::SpuriousDragon,
"EIP150" => Fork::Tangerine,
"Osaka" => Fork::Osaka,
"BPO1" => Fork::BPO1,
"BPO2" => Fork::BPO2,
"BPO3" => Fork::BPO3,
"BPO4" => Fork::BPO4,
"BPO5" => Fork::BPO5,
"Amsterdam" => Fork::Amsterdam,
other => {
return Err(serde::de::Error::custom(format!(
"Unknown fork name: {other}",
Expand Down
8 changes: 8 additions & 0 deletions tooling/ef_tests/state_v2/src/modules/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::path::PathBuf;

use ethrex_levm::errors::VMError;

#[derive(Debug)]
Expand All @@ -6,5 +8,11 @@ pub enum RunnerError {
VMError(VMError),
EIP7702ShouldNotBeCreateType,
FailedToGetIndexValue(String),
/// Wraps an I/O or serde error encountered while parsing a fixture.
/// Holds the offending path and the underlying error message.
ParseFixture {
path: PathBuf,
source: String,
},
Custom(String),
}
1 change: 1 addition & 0 deletions tooling/ef_tests/state_v2/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ pub mod parser;
pub mod report;
pub mod result_check;
pub mod runner;
pub mod statetest;
pub mod types;
pub mod utils;
24 changes: 20 additions & 4 deletions tooling/ef_tests/state_v2/src/modules/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,15 @@ pub fn parse_file(path: &PathBuf, log_parse_file: bool) -> Result<Vec<Test>, Run
if log_parse_file {
println!("Parsing file: {:?}", path);
}
let test_file = std::fs::File::open(path.clone()).unwrap();
let mut tests: Tests = serde_json::from_reader(test_file).unwrap();
let test_file = std::fs::File::open(path).map_err(|e| RunnerError::ParseFixture {
path: path.clone(),
source: format!("open: {e}"),
})?;
let mut tests: Tests =
serde_json::from_reader(test_file).map_err(|e| RunnerError::ParseFixture {
path: path.clone(),
source: format!("deserialize: {e}"),
})?;
for test in tests.0.iter_mut() {
test.path = path.clone();
}
Expand All @@ -71,14 +78,23 @@ pub fn parse_dir(
if log_parse_dir {
println!("Parsing test directory: {:?}", path);
}
let dir_entries: Vec<_> = std::fs::read_dir(path.clone()).unwrap().flatten().collect();
let dir_entries: Vec<_> = std::fs::read_dir(path)
.map_err(|e| RunnerError::ParseFixture {
path: path.clone(),
source: format!("read_dir: {e}"),
})?
.flatten()
.collect();

// Process directory entries in parallel
let directory_tests_results: Vec<_> = dir_entries
.into_par_iter()
.map(|entry| -> Result<Option<Vec<Test>>, RunnerError> {
// Check entry type
let entry_type = entry.file_type().unwrap();
let entry_type = entry.file_type().map_err(|e| RunnerError::ParseFixture {
path: entry.path(),
source: format!("file_type: {e}"),
})?;
if entry_type.is_dir() {
let dir_tests = parse_dir(
&entry.path(),
Expand Down
Loading
Loading