refactor(l1): make L1 stateless proving EIP-8025-only, drop eip-8025/rayon Cargo features#6730
refactor(l1): make L1 stateless proving EIP-8025-only, drop eip-8025/rayon Cargo features#6730ilitteri wants to merge 5 commits into
Conversation
…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.
|
Lines of code reportTotal lines added: Detailed view |
Benchmark Results ComparisonNo significant difference was registered for any benchmark run. Detailed ResultsBenchmark Results: BubbleSort
Benchmark Results: ERC20Approval
Benchmark Results: ERC20Mint
Benchmark Results: ERC20Transfer
Benchmark Results: Factorial
Benchmark Results: FactorialRecursive
Benchmark Results: Fibonacci
Benchmark Results: FibonacciRecursive
Benchmark Results: ManyHashes
Benchmark Results: MstoreBench
Benchmark Results: Push
Benchmark Results: SstoreBench_no_opt
|
…á 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.
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.
Motivation
L1 stateless proving in ethrex is EIP-8025 (Execution Layer Triggerable Proofs). It currently sits behind a compile-time
eip-8025Cargo feature, alongside a legacy rkyv "state-root commitment" L1 program that the feature flag toggles against. Two problems:eip-8025andrayonwere conflated cfg gates. Guards likecfg(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
l1::execution_program(rkyvProgramInput→ state-root commitment) andLegacyProgramOutput.l1::ProgramOutputis now the single EIP-8025 output shape; theProgramOutput { Legacy, Eip8025 }enum is gone.l1::execution_programis the SSZ wire-bytes program (the only L1 program).ExecBackend::execute_coreno longer dispatches: L1 always runs the EIP-8025 in-process path; L2 runs the L2 program.cfgfrom(eip-8025 vs not)to(l2 vs not(l2))—not(l2)is EIP-8025. Theeip-8025leaf feature is removed from all four guest crates and the test crate.eip-8025Cargo feature removed entirelyDropped 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 thetestcrate. The SSZ types (libssz*,eip8025_ssz) are now unconditional dependencies/modules.rayonCargo feature removedIt only existed to keep the conflated
cfg(all(feature = "rayon", not(feature = "eip-8025")))guards working. Rayon is now an unconditional dependency onethrex-blockchain,ethrex-vm, andethrex-levm; the speculative warmer, parallel sender recovery, and parallel prefetch run unconditionally on the L1 client.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 --workspaceandcargo check --workspaceare clean.cargo test --workspace: 960 passed, 0 failed.cargo check -p ethrex-proverclean for--features l2,--features sp1, and--features risc0.feature = "eip-8025"and zerofeature = "rayon"references remain anywhere in the workspace.Still owed before merge (out-of-band, needs zkVM / devnet infra):
ethrex-replay(external) migrates to the EIP-8025-only L1 program.Checklist
STORE_SCHEMA_VERSION(crates/storage/lib.rs) if the PR includes breaking changes to theStorerequiring a re-sync. — N/A, noStoreschema changes.