Skip to content

refactor(l1): make L1 stateless proving EIP-8025-only, drop eip-8025/rayon Cargo features#6730

Draft
ilitteri wants to merge 5 commits into
mainfrom
eip-8025-hegota-fork
Draft

refactor(l1): make L1 stateless proving EIP-8025-only, drop eip-8025/rayon Cargo features#6730
ilitteri wants to merge 5 commits into
mainfrom
eip-8025-hegota-fork

Conversation

@ilitteri
Copy link
Copy Markdown
Collaborator

@ilitteri ilitteri commented May 26, 2026

Motivation

L1 stateless proving in ethrex is EIP-8025 (Execution Layer Triggerable Proofs). It currently sits behind a compile-time eip-8025 Cargo feature, alongside a legacy rkyv "state-root commitment" L1 program that the feature flag toggles against. Two problems:

  1. The legacy L1 program is dead weight. There is no scenario where an L1 block is proved with the old state-root program — EIP-8025 is the L1 stateless-proving format. Keeping a feature flag to choose between "EIP-8025" and "legacy" for L1 only adds a wrong-build failure mode.
  2. eip-8025 and rayon were conflated cfg gates. Guards like cfg(all(feature = "rayon", not(feature = "eip-8025"))) were doing two unrelated jobs — gating fork/proof behaviour and gating whether rayon is available — across ~112 sites.

This PR collapses L1 proving to a single EIP-8025 path and removes both Cargo features.

Description

L1 proving is EIP-8025-only

  • Delete the legacy l1::execution_program (rkyv ProgramInput → state-root commitment) and LegacyProgramOutput.
  • l1::ProgramOutput is now the single EIP-8025 output shape; the ProgramOutput { Legacy, Eip8025 } enum is gone.
  • l1::execution_program is the SSZ wire-bytes program (the only L1 program).
  • ExecBackend::execute_core no longer dispatches: L1 always runs the EIP-8025 in-process path; L2 runs the L2 program.
  • Guest binaries collapse their cfg from (eip-8025 vs not) to (l2 vs not(l2))not(l2) is EIP-8025. The eip-8025 leaf feature is removed from all four guest crates and the test crate.

eip-8025 Cargo feature removed entirely

Dropped from cmd/ethrex, ethrex-common, ethrex-blockchain, ethrex-rpc, ethrex-l2-rpc, ethrex-vm, ethrex-levm, ethrex-prover, ethrex-guest-program, the four guest binary crates, and the test crate. The SSZ types (libssz*, eip8025_ssz) are now unconditional dependencies/modules.

rayon Cargo feature removed

It only existed to keep the conflated cfg(all(feature = "rayon", not(feature = "eip-8025"))) guards working. Rayon is now an unconditional dependency on ethrex-blockchain, ethrex-vm, and ethrex-levm; the speculative warmer, parallel sender recovery, and parallel prefetch run unconditionally on the L1 client.

A follow-up will introduce a target-based mechanism to keep rayon out of zkVM guest builds.

Not in this PR

No Hegotá fork. The execution-layer fork gating (when the node starts producing SSZ witnesses / advertising the EIP-8025 engine methods) is separate node-side work and will land with that implementation. The prover/guest is fork-agnostic — it proves the EIP-8025 input it is handed.

Validation

  • cargo build --workspace and cargo check --workspace are clean.
  • cargo test --workspace: 960 passed, 0 failed.
  • cargo check -p ethrex-prover clean for --features l2, --features sp1, and --features risc0.
  • Zero feature = "eip-8025" and zero feature = "rayon" references remain anywhere in the workspace.

Still owed before merge (out-of-band, needs zkVM / devnet infra):

  • Guest binary builds with the zkVM toolchains (sp1/risc0/zisk/openvm) — the most likely thing to surface, since rayon is now an unconditional dep reaching those targets.
  • zkevm-eest EIP-8025 fixtures.
  • ethrex-replay (external) migrates to the EIP-8025-only L1 program.

Checklist

  • Updated STORE_SCHEMA_VERSION (crates/storage/lib.rs) if the PR includes breaking changes to the Store requiring a re-sync. — N/A, no Store schema changes.

ilitteri added 3 commits May 26, 2026 15:24
…d rayon Cargo gates

EIP-8025 (Execution Layer Triggerable Proofs) ships in Hegotá. There is no
scenario where a mainnet operator wants to run a Hegotá chain without EIP-8025,
so treating it as a regular Ethereum fork (like Cancun, Prague, Osaka,
Amsterdam) eliminates the feature-flag plumbing and removes the failure mode
where a non-EIP-8025 build silently mis-frames Hegotá payloads.

Activation is now governed entirely by is_hegota_activated(timestamp) against
the chain config's hegota_time field. Pre-Hegotá behavior is byte-identical
because every fork-conditional site reads the active fork from chain config.

Hegotá fork
-----------

- Fork::Hegota = 26, ordered strictly above Amsterdam
- ChainConfig::hegota_time with hegotaTime / hezeTime / bogotaTime JSON aliases
- is_hegota_activated(ts) wired into get_fork, next_fork,
  get_last_scheduled_fork, get_activation_timestamp_for_fork, gather_forks,
  get_blob_schedule_for_fork (inherits Amsterdam), display_config

Runtime dispatch
----------------

The prover exec backend chooses the canonical EIP-8025 path vs the legacy path
at runtime on is_hegota_activated(first_block_timestamp). The two guest output
types (LegacyProgramOutput, Eip8025ProgramOutput) are now always compiled and
wrapped in a ProgramOutput enum; the eip-8025 wire-bytes entrypoint is renamed
execution_program_eip8025 so both variants coexist.

SSZ types always compile
------------------------

libssz / libssz-types / libssz-merkle / libssz-derive move from optional to
required deps on ethrex-common and ethrex-guest-program. The eip8025_ssz module
is unconditional.

Dropped eip-8025 Cargo feature from
-----------------------------------

  cmd/ethrex, ethrex-common, ethrex-blockchain, ethrex-rpc, ethrex-l2-rpc,
  ethrex-vm, ethrex-levm, ethrex-prover, ethrex-guest-program

Kept only as a local (empty) feature on the four guest binary crates
(bin/risc0, bin/sp1, bin/zisk, bin/openvm), where each ELF stays specialized
for one execution model; the prover-coordinator picks the right guest by fork.

Dropped rayon Cargo feature
---------------------------

The rayon feature only existed to keep the conflated
cfg(all(feature = "rayon", not(feature = "eip-8025"))) guards working. With
EIP-8025 moving to runtime dispatch, rayon becomes an unconditional dependency
on ethrex-blockchain, ethrex-vm and ethrex-levm; the speculative warmer,
parallel sender recovery and parallel prefetch now run unconditionally on the
L1 client. A future change will introduce a target-based mechanism to exclude
rayon from zkVM guest builds.

Behavior on pre-Hegotá chains is byte-identical.
…ctor

Formatter collapses blank lines left by removing the eip-8025 cfg gates and
rewraps the new hegota_time alias test. update-cargo-lock propagates the now-
unconditional libssz* deps (ethrex-common) and rayon (ethrex-vm / ethrex-levm)
into the dependent sub-manifest lockfiles, including the zkVM guest binaries.
Resolves a conflict in crates/vm/backends/levm/mod.rs introduced by #6669
(lazy BAL cursor) and #6655 (BAL optimistic merkleization), which rewrote the
same lines this branch un-gated. Took main's version of the file wholesale,
then re-stripped the rayon/eip-8025 cfg gates — keeping main's is_amsterdam
correctness guard and gen_db refactor.

The merge also pulled in new rayon/eip-8025 gates from #6669 in files that did
not conflict (auto-merged): crates/vm/levm/src/db/gen_db.rs, test/Cargo.toml,
and test/tests/levm/bal_view_tests.rs. Stripped those too, so the only
remaining eip-8025 gates are the four guest binary main.rs files and no rayon
feature gates remain. The bal_view_tests now run unconditionally.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 26, 2026

⚠️ Known Issues — intentionally skipped tests

Source: docs/known_issues.md

Known Issues

Tests intentionally excluded from CI. Source of truth for the Known
Issues
section the L1 workflow appends to each ef-tests job summary
and posts as a sticky PR comment.

EF Tests — Stateless coverage narrowed to EIP-8025 optional-proofs

make -C tooling/ef_tests/blockchain test calls test-stateless-zkevm
instead of test-stateless. The zkevm@v0.3.3 fixtures are filled against
bal@v5.6.1, out of sync with current bal spec; the broad target trips ~549
fixtures. Re-broaden once the zkevm bundle is regenerated.

Why and resolution path

PR #6527 broadened
test-stateless to extract the entire for_amsterdam/ tree from the
zkevm bundle and run all of it under --features stateless; combined with
this branch's bal-devnet-7 semantics that scope produces ~549
GasUsedMismatch / ReceiptsRootMismatch /
BlockAccessListHashMismatch failures.

test-stateless-zkevm filters cargo to the eip8025_optional_proofs
suite, which still validates the stateless harness without the bal-version
mismatch.

Re-broaden by switching test: back to test-stateless in
tooling/ef_tests/blockchain/Makefile once the zkevm bundle is regenerated
against the current bal spec.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 26, 2026

Lines of code report

Total lines added: 5
Total lines removed: 195
Total lines changed: 200

Detailed view
+----------------------------------------------------+-------+------+
| File                                               | Lines | Diff |
+----------------------------------------------------+-------+------+
| ethrex/crates/blockchain/blockchain.rs             | 2545  | -6   |
+----------------------------------------------------+-------+------+
| ethrex/crates/common/types/block.rs                | 970   | -8   |
+----------------------------------------------------+-------+------+
| ethrex/crates/common/types/mod.rs                  | 30    | -1   |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/openvm/src/main.rs | 23    | -10  |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/risc0/src/main.rs  | 29    | -10  |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/sp1/src/main.rs    | 20    | -9   |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/zisk/src/main.rs   | 20    | -9   |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l1/input.rs        | 202   | -23  |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l1/mod.rs          | 11    | -2   |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l1/output.rs       | 16    | -26  |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l1/program.rs      | 340   | -53  |
+----------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l2/program.rs      | 62    | +1   |
+----------------------------------------------------+-------+------+
| ethrex/crates/l2/tee/quote-gen/src/main.rs         | 90    | +2   |
+----------------------------------------------------+-------+------+
| ethrex/crates/prover/src/backend/exec.rs           | 92    | +2   |
+----------------------------------------------------+-------+------+
| ethrex/crates/vm/backends/levm/mod.rs              | 2369  | -27  |
+----------------------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/db/gen_db.rs             | 754   | -8   |
+----------------------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/db/mod.rs                | 145   | -3   |
+----------------------------------------------------+-------+------+

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 26, 2026

Benchmark Results Comparison

No significant difference was registered for any benchmark run.

Detailed Results

Benchmark Results: BubbleSort

Command Mean [s] Min [s] Max [s] Relative
main_revm_BubbleSort 2.969 ± 0.023 2.939 3.011 1.07 ± 0.01
main_levm_BubbleSort 2.766 ± 0.022 2.729 2.794 1.00
pr_revm_BubbleSort 2.952 ± 0.015 2.935 2.982 1.07 ± 0.01
pr_levm_BubbleSort 2.786 ± 0.053 2.743 2.919 1.01 ± 0.02

Benchmark Results: ERC20Approval

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Approval 992.6 ± 9.8 979.8 1004.9 1.02 ± 0.01
main_levm_ERC20Approval 1047.7 ± 7.4 1034.3 1063.7 1.07 ± 0.01
pr_revm_ERC20Approval 975.5 ± 9.7 960.2 989.5 1.00
pr_levm_ERC20Approval 1043.3 ± 9.2 1026.9 1055.7 1.07 ± 0.01

Benchmark Results: ERC20Mint

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Mint 132.0 ± 1.1 131.0 133.8 1.00
main_levm_ERC20Mint 155.5 ± 1.6 154.0 159.9 1.18 ± 0.02
pr_revm_ERC20Mint 132.3 ± 1.2 131.1 135.1 1.00 ± 0.01
pr_levm_ERC20Mint 155.0 ± 0.7 154.2 156.1 1.17 ± 0.01

Benchmark Results: ERC20Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Transfer 233.3 ± 2.2 231.3 238.3 1.00
main_levm_ERC20Transfer 258.9 ± 2.2 255.3 263.9 1.11 ± 0.01
pr_revm_ERC20Transfer 235.8 ± 7.0 230.8 253.4 1.01 ± 0.03
pr_levm_ERC20Transfer 259.7 ± 3.0 256.1 264.5 1.11 ± 0.02

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Factorial 224.8 ± 1.0 222.6 226.0 1.00
main_levm_Factorial 266.1 ± 4.7 261.8 275.0 1.18 ± 0.02
pr_revm_Factorial 228.1 ± 13.1 222.9 265.2 1.01 ± 0.06
pr_levm_Factorial 262.6 ± 1.4 260.6 264.7 1.17 ± 0.01

Benchmark Results: FactorialRecursive

Command Mean [s] Min [s] Max [s] Relative
main_revm_FactorialRecursive 1.684 ± 0.034 1.635 1.733 1.05 ± 0.03
main_levm_FactorialRecursive 1.641 ± 0.019 1.610 1.671 1.02 ± 0.02
pr_revm_FactorialRecursive 1.688 ± 0.041 1.633 1.764 1.05 ± 0.03
pr_levm_FactorialRecursive 1.605 ± 0.033 1.580 1.689 1.00

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Fibonacci 202.6 ± 0.6 201.8 203.7 1.01 ± 0.01
main_levm_Fibonacci 242.0 ± 4.4 238.7 250.9 1.20 ± 0.02
pr_revm_Fibonacci 201.4 ± 0.9 199.4 202.5 1.00
pr_levm_Fibonacci 240.2 ± 1.8 238.4 244.5 1.19 ± 0.01

Benchmark Results: FibonacciRecursive

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_FibonacciRecursive 852.1 ± 4.3 847.1 858.4 1.18 ± 0.01
main_levm_FibonacciRecursive 724.7 ± 7.7 716.0 739.6 1.00 ± 0.01
pr_revm_FibonacciRecursive 858.8 ± 6.4 849.1 868.8 1.19 ± 0.01
pr_levm_FibonacciRecursive 722.2 ± 3.3 718.6 727.3 1.00

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ManyHashes 8.8 ± 0.1 8.6 8.9 1.01 ± 0.02
main_levm_ManyHashes 10.1 ± 0.1 9.9 10.3 1.17 ± 0.02
pr_revm_ManyHashes 8.7 ± 0.1 8.5 8.9 1.00
pr_levm_ManyHashes 10.1 ± 0.1 9.9 10.2 1.16 ± 0.02

Benchmark Results: MstoreBench

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_MstoreBench 263.0 ± 11.8 254.0 294.1 1.09 ± 0.05
main_levm_MstoreBench 241.2 ± 2.7 237.5 245.0 1.00 ± 0.03
pr_revm_MstoreBench 256.4 ± 5.2 251.9 270.3 1.06 ± 0.03
pr_levm_MstoreBench 240.9 ± 5.4 236.6 253.7 1.00

Benchmark Results: Push

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Push 294.8 ± 2.0 291.5 296.8 1.02 ± 0.01
main_levm_Push 289.6 ± 2.4 286.9 293.5 1.00 ± 0.01
pr_revm_Push 291.4 ± 2.8 289.2 298.8 1.01 ± 0.01
pr_levm_Push 289.2 ± 2.6 287.0 295.3 1.00

Benchmark Results: SstoreBench_no_opt

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_SstoreBench_no_opt 172.0 ± 7.7 163.2 187.9 1.70 ± 0.08
main_levm_SstoreBench_no_opt 101.5 ± 1.3 100.6 105.1 1.00 ± 0.01
pr_revm_SstoreBench_no_opt 168.4 ± 3.0 162.5 173.8 1.66 ± 0.03
pr_levm_SstoreBench_no_opt 101.3 ± 0.3 100.8 101.8 1.00

…á fork scaffolding

Follow-up correction on this branch. EIP-8025 is the only L1 stateless-proving
format — there is no scenario where an L1 block is proved with the old rkyv
state-root program. So the L1 guest/prover collapses to a single EIP-8025 path:

  - Delete the legacy l1::execution_program (rkyv ProgramInput -> state-root
    commitment) and LegacyProgramOutput.
  - l1::ProgramOutput is now the single EIP-8025 output; the
    ProgramOutput { Legacy, Eip8025 } enum is gone.
  - l1::execution_program is the SSZ wire-bytes program (was
    execution_program_eip8025).
  - ExecBackend::execute_core no longer dispatches: L1 always runs the EIP-8025
    in-process path, L2 runs the L2 program.
  - Guest binaries collapse their cfg from (eip-8025 vs not) to (l2 vs not(l2));
    not(l2) is EIP-8025. The eip-8025 leaf feature is removed from all four
    guest crates and the test crate.

Because the L1 prover is now fork-agnostic, the Hegotá fork scaffolding added
earlier on this branch (Fork::Hegota, hegota_time, is_hegota_activated and its
wiring) had no remaining consumer, so it is removed. genesis.rs and the
admin_nodeinfo RPC fixture are back to their main state. Hegotá-fork gating of
node-side payload/witness/engine behaviour is future work and will land with
that node-side implementation.

The rayon Cargo feature removal and always-compiled SSZ types from the earlier
commits are unchanged. ethrex-replay (external) will be updated downstream to
the EIP-8025-only L1 program.
@ilitteri ilitteri changed the title refactor(l1): treat EIP-8025 as a Hegotá fork, drop eip-8025/rayon gates refactor(l1): make L1 stateless proving EIP-8025-only, drop eip-8025/rayon Cargo features May 26, 2026
@github-actions github-actions Bot added the L1 Ethereum client label May 26, 2026
The guest main.rs files had a cfg split where L1 called execution_program(&bytes)
and L2 called execution_program(typed_input) after a separate rkyv-decode line.
Adapt the L2 program to take raw input bytes and rkyv-decode internally, exactly
as the L1 program decodes its own SSZ wire format. The guest entrypoint is now
uniform for both:

    let input = read_vec();
    let output = execution_program(&input, crypto).unwrap();

No per-target rkyv-decode line, no owned-vs-borrowed call split.

In-process callers that hold a typed ProgramInput (the ExecBackend and the TEE
quote-gen) serialize it with rkyv before calling — the same encoding the real
zkVM backends produce via serialize_input, so the in-process path now faithfully
mirrors the guest input path. quote-gen gains a direct rkyv dependency (pinned to
the workspace 0.8.10) for this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant