Skip to content

feat(l1): unify guest programs and publish assets ourselves - #7115

Merged
ilitteri merged 46 commits into
mainfrom
feat/stateless-validator-upstream-v2
Aug 14, 2026
Merged

ilitteri merged 46 commits into
mainfrom
feat/stateless-validator-upstream-v2

Conversation

@iovoid

@iovoid iovoid commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Motivation

Takes ownership of the stateless execution guest program release processes.

Currently our guest program deserializes using legacy logic instead of the standard, so we get rid of the legacy path.

Description

Removes the unused legacy guest program path.

Uses ere-compiler to produce the ELF and VKs, and minisign to sign them.

Closes #7011

@github-actions github-actions Bot added the L1 Ethereum client label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

⚠️ Known Issues — intentionally skipped tests

Source: docs/known_issues.md

rpc-compat log-bearing cases excluded

Where: KNOWN_EXCLUDED_TESTS in .github/scripts/check-hive-results.sh counts out
eight hive rpc-compat cases — the four eth_getLogs cases, eth_getBlockReceipts/get-block-receipts-latest,
and three eth_getTransactionReceipt cases. They are exactly the cases whose recorded
response contains at least one log object; every case with an empty log array still runs.
Note this leaves eth_getLogs with no rpc-compat coverage at all, since all four of its
cases are in the set.

Why: ethrex populates blockTimestamp on log objects, as geth, besu, nethermind, reth
and erigon all do. hive's rpc-compat compares responses byte-exactly (jsondiff.FullMatch;
the lenient checkJSONStructure path applies only to cases upstream marks speconly), and
the corpus is pinned to execution-apis d08382ae (2025-02-10), whose recordings predate the
field — it entered the schema in execution-apis#639 and the fixtures in #846 (2026-07-22).
So the extra key cannot match, and this is a property of the pin rather than of the response.

The pin cannot move, and this is not temporary. The pin sits one commit before
execution-apis#627, which moved the test chain to a pre-merge genesis: the current corpus has
~36 proof-of-work blocks before its terminal total difficulty. ethrex does not support
pre-merge chains and will not, so importing that chain.rlp fails at block 1 —
validate_block_header has no pre-London base-fee path. Every revision carrying
blockTimestamp in its fixtures also carries that chain, so there is no revision that
satisfies both. Nor can the corpus be patched locally: rpc-compat's Dockerfile clones
ethereum/execution-apis by hard-coded URL, so the branch buildarg cannot point at a fork.

Coverage: the field itself is pinned by
block_timestamp_is_on_the_log_and_not_on_the_receipt in
crates/networking/rpc/types/receipt.rs, which asserts it is present on each log and absent
from the receipt level.

Removal: delete the entries if ethrex ever gains pre-merge chain import, or if upstream
marks these cases speconly so they are type-checked instead of compared byte-for-byte.


Stateless EF tests: 45 of 105 fixtures skipped

Where: tooling/ef_tests/blockchain — make test-stateless, which runs the
generated vectors_stateless_3278/ conformance set (execution-specs
3c3b6f4af, i.e. #3248 progressive SSZ + #3278 ChainConfig removal).
make test-levm is unaffected.

Skipped: 45 fixtures that fail in ordinary execution

Listed in EXTRA_SKIPS in tests/all.rs. All fail in add_block_pipeline with
GasUsedMismatch or ReceiptsRootMismatch, before any stateless validation
runs, so they are ordinary block-execution divergences rather than stateless
conformance gaps. 60 fixtures still execute, which is what keeps the witness and
SSZ paths covered and keeps the empty-suite guard in parse_and_execute
meaningful.

Why: the vectors and the client are one devnet apart on gas. 3c3b6f4af
carries the glamsterdam-devnet-7 schedule; this client implements devnet-8.

3c3b6f4af (devnet-7) this client (devnet-8)
COLD_STORAGE_ACCESS 3000 2100
ACCOUNT_WRITE 8000 9000
CALL_VALUE 10300 11300
access-list storage key full cold cost cold − WARM_ACCESS

Five of the 45 failed even while the client was still on devnet-7, so those are
skew against 3c3b6f4af itself; the other 40 appeared with the devnet-8 move.
The two groups are commented separately in EXTRA_SKIPS because they lift at
different times.

Corroborating that these are spec-base skew and not client bugs: ethrex passes
the five original cases against the tests-zkevm@v0.6.2 versions of the same
tests, and the eth-act witness dashboard has ethrex at 22829/22829 on
eels/consume-engine-witness and 19413/19413 on stateless-validator sp1 for
that bundle. Diffing witness_codes_reset_delegation across the two fixture
sets: pre and genesisBlockHeader are byte-identical and it is the same
transaction hash, yet postState, receipts, stateRoot, receiptTrie and
blockAccessListHash all differ — the receipt's cumulativeGasUsed goes
30816 → 24653. Same input, different output.

The regular vectors/ bundle has no eip8025_optional_proofs tests, so
make test-levm never sees any of this.

The pin cannot move today. eip8025_optional_proofs does not exist on the
current forks/amsterdam default branch, and 3c3b6f4af has diverged from it
(124 ahead, 315 behind), so it was never merged there. devnets/glamsterdam/8
is 315 commits behind 3c3b6f4af: it has the devnet-8 gas schedule but not the
#3278 schema, so vectors filled from it would carry the pre-#3278 69-byte
statelessOutputBytes this client cannot parse. No upstream revision has both.

Removal: bump SPEC_SHA in scripts/gen_stateless_vectors.sh once an
upstream revision carries both eip8025_optional_proofs and the devnet-8 gas
schedule, then delete the 40-entry group. Re-check the original five at the same
time; if they still fail against a matching base they are real execution bugs
and must not stay skipped.

Why this file previously claimed an Amsterdam-wide skip

parse_and_execute used to drop every network >= Fork::Amsterdam fixture from
the stateless run. Every test in the tests-zkevm bundle it read is
network: Amsterdam (23,946 of 23,946), so the stateless suite executed nothing
at all while reporting success. parse_and_execute now fails any stateless
fixture file that runs zero tests without a named skip, so a structural skip
cannot silently empty the suite again.


ZisK guest program hash changes with the unsync_cell gate

Where: crates/common/types/block.rs, transaction.rs.

The gate on the single-threaded unsync_cell::OnceCell moved from
all(feature = "eip-8025", target_arch = "riscv64") to
all(feature = "zisk", target_arch = "riscv64") when the eip-8025 feature was removed.

The guest ELFs were previously built --features "<zkvm>-build-elf,ci", which never enabled
eip-8025, so they compiled the atomic once_cell variant. bin/zisk/Cargo.toml does enable
ethrex-common/zisk, so the ZisK guest now compiles the unsafe impl Sync cell instead.
That changes the ELF bytes and therefore the program hash and verification key.

This is intended (the guest is single-threaded, so the unsync cell is sound and cheaper), but it
is a VK change rather than a no-op refactor, and the diffstat presents it as a file rename
(eip8025_cell.rs → unsync_cell.rs). Anyone pinning a ZisK VK across this change must
re-register it. The stateless-validator crate now forwards ethrex-common/zisk from its own
zisk feature so the two ZisK guests do not disagree on the cell type.


Release signing key is an unprotected repository secret

Where: .github/workflows/tag_release.yaml.

MINISIGN_SECRET_KEY is a plain repository secret. There is no environment: on
finalize-release or dry-run-release-assets, and gh api repos/lambdaclass/ethrex/rulesets
shows only branch-targeted rulesets, so the github.ref_type == 'tag' condition is a workflow
check rather than an enforced boundary: anyone who can push a tag can reach the signing key.

This is a repository-settings change, not a code change, so it is recorded here rather than
fixed in the tree. Recommended:

  1. Move MINISIGN_SECRET_KEY / MINISIGN_PASSWORD into a GitHub Environment with required
    reviewers, and add environment: to the two jobs that sign.
  2. Add a ruleset targeting refs/tags/v* restricting who may create release tags.

Until then, the compromise of that key is silent and durable: signatures would still verify
against the committed .github/minisign.pub.

@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

Lines of code report

Total lines added: 1010
Total lines removed: 862
Total lines changed: 1872

Detailed view
+------------------------------------------------------------------------------+-------+------+
| File                                                                         | Lines | Diff |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/prewarm.rs                                          | 575   | -2   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/blockchain/stateless.rs                                        | 54    | -2   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/common/constants.rs                                            | 56    | +4   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/common/crypto/provider.rs                                      | 529   | +21  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/common/types/block_execution_witness.rs                        | 845   | -90  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/common/types/mod.rs                                            | 32    | -2   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/common/types/stateless_ssz.rs                                  | 656   | -129 |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/common/types/transaction.rs                                    | 5068  | +21  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/common/types/unsync_cell.rs                                    | 90    | +90  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/openvm/src/main.rs                           | 23    | -10  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/risc0/src/main.rs                            | 32    | -7   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/sp1/src/main.rs                              | 23    | -6   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/bin/zisk/src/main.rs                             | 23    | -6   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l1/input.rs                                  | 33    | -262 |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l1/mod.rs                                    | 8     | -12  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l1/program.rs                                | 325   | -312 |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/l2/program.rs                                | 68    | +5   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/src/lib.rs                                       | 48    | -4   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/bin/openvm/openvm_init.rs    | 3     | +3   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/bin/openvm/src/main.rs       | 6     | +6   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/bin/sp1/src/main.rs          | 7     | +7   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/bin/zisk/src/main.rs         | 7     | +7   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/src/crypto/mod.rs            | 15    | +15  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/src/crypto/openvm.rs         | 439   | +439 |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/src/crypto/zkvm_interface.rs | 309   | +309 |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/src/lib.rs                   | 12    | +12  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/guest-program/stateless-validator/src/platform.rs              | 18    | +18  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/l2/sequencer/native_rollup/l1_advancer.rs                      | 663   | +1   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/prover/src/backend/exec.rs                                     | 83    | -5   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/prover/src/backend/openvm.rs                                   | 106   | +3   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/prover/src/backend/risc0.rs                                    | 135   | +3   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/prover/src/backend/sp1.rs                                      | 184   | +3   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/prover/src/backend/zisk.rs                                     | 169   | +3   |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/prover/src/lib.rs                                              | 15    | -10  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/vm/levm/src/execute_precompile.rs                              | 376   | +40  |
+------------------------------------------------------------------------------+-------+------+
| ethrex/crates/vm/system_contracts.rs                                         | 94    | -3   |
+------------------------------------------------------------------------------+-------+------+

@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

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 3.277 ± 0.056 3.237 3.405 1.16 ± 0.02
main_levm_BubbleSort 2.816 ± 0.014 2.803 2.839 1.00
pr_revm_BubbleSort 3.254 ± 0.016 3.233 3.284 1.16 ± 0.01
pr_levm_BubbleSort 2.846 ± 0.028 2.821 2.919 1.01 ± 0.01

Benchmark Results: ERC20Approval

Command Mean [s] Min [s] Max [s] Relative
main_revm_ERC20Approval 1.062 ± 0.022 1.042 1.109 1.02 ± 0.02
main_levm_ERC20Approval 1.040 ± 0.010 1.028 1.055 1.00
pr_revm_ERC20Approval 1.060 ± 0.023 1.043 1.105 1.02 ± 0.02
pr_levm_ERC20Approval 1.050 ± 0.013 1.038 1.074 1.01 ± 0.02

Benchmark Results: ERC20Mint

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Mint 143.4 ± 3.1 140.9 151.2 1.01 ± 0.02
main_levm_ERC20Mint 158.4 ± 1.3 157.2 161.1 1.12 ± 0.01
pr_revm_ERC20Mint 141.8 ± 1.5 140.6 145.0 1.00
pr_levm_ERC20Mint 159.1 ± 1.4 157.7 161.9 1.12 ± 0.02

Benchmark Results: ERC20Transfer

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ERC20Transfer 251.5 ± 3.1 247.8 257.2 1.00 ± 0.02
main_levm_ERC20Transfer 265.5 ± 2.8 263.1 272.2 1.06 ± 0.02
pr_revm_ERC20Transfer 250.4 ± 4.4 247.3 259.7 1.00
pr_levm_ERC20Transfer 266.1 ± 1.7 263.9 269.6 1.06 ± 0.02

Benchmark Results: Factorial

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Factorial 237.3 ± 5.9 230.4 246.8 1.00
main_levm_Factorial 261.0 ± 2.7 258.5 267.4 1.10 ± 0.03
pr_revm_Factorial 238.2 ± 10.7 228.8 255.8 1.00 ± 0.05
pr_levm_Factorial 259.9 ± 2.5 256.5 263.8 1.10 ± 0.03

Benchmark Results: FactorialRecursive

Command Mean [s] Min [s] Max [s] Relative
main_revm_FactorialRecursive 1.606 ± 0.028 1.545 1.652 1.00
main_levm_FactorialRecursive 9.633 ± 0.040 9.576 9.691 6.00 ± 0.11
pr_revm_FactorialRecursive 1.629 ± 0.028 1.579 1.686 1.01 ± 0.02
pr_levm_FactorialRecursive 9.708 ± 0.118 9.441 9.805 6.04 ± 0.13

Benchmark Results: Fibonacci

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Fibonacci 233.4 ± 6.7 224.3 241.8 1.02 ± 0.04
main_levm_Fibonacci 234.7 ± 7.4 226.2 248.7 1.02 ± 0.04
pr_revm_Fibonacci 229.6 ± 6.7 223.3 243.4 1.00
pr_levm_Fibonacci 237.4 ± 10.8 229.3 265.8 1.03 ± 0.06

Benchmark Results: FibonacciRecursive

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_FibonacciRecursive 863.4 ± 10.4 848.3 887.1 1.17 ± 0.03
main_levm_FibonacciRecursive 741.7 ± 6.6 728.4 750.2 1.01 ± 0.02
pr_revm_FibonacciRecursive 845.6 ± 10.5 827.6 860.2 1.15 ± 0.03
pr_levm_FibonacciRecursive 737.1 ± 13.2 724.0 765.9 1.00

Benchmark Results: ManyHashes

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_ManyHashes 9.5 ± 0.1 9.4 9.6 1.01 ± 0.02
main_levm_ManyHashes 10.7 ± 0.1 10.6 10.8 1.14 ± 0.02
pr_revm_ManyHashes 9.4 ± 0.2 9.3 9.8 1.00
pr_levm_ManyHashes 10.6 ± 0.1 10.4 10.7 1.12 ± 0.02

Benchmark Results: MstoreBench

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_MstoreBench 298.6 ± 3.9 294.3 305.6 1.42 ± 0.04
main_levm_MstoreBench 211.3 ± 5.0 205.2 218.6 1.00 ± 0.04
pr_revm_MstoreBench 299.6 ± 4.8 293.1 305.5 1.42 ± 0.04
pr_levm_MstoreBench 210.4 ± 5.6 201.6 221.2 1.00

Benchmark Results: Push

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_Push 341.7 ± 28.7 320.6 420.8 1.28 ± 0.11
main_levm_Push 267.2 ± 7.2 261.1 281.3 1.00 ± 0.04
pr_revm_Push 335.3 ± 7.1 326.2 348.7 1.26 ± 0.04
pr_levm_Push 267.1 ± 7.0 256.6 277.0 1.00

Benchmark Results: SstoreBench_no_opt

Command Mean [ms] Min [ms] Max [ms] Relative
main_revm_SstoreBench_no_opt 179.2 ± 4.8 176.3 192.5 1.56 ± 0.04
main_levm_SstoreBench_no_opt 116.8 ± 5.3 114.2 131.8 1.02 ± 0.05
pr_revm_SstoreBench_no_opt 181.2 ± 6.9 176.6 197.2 1.58 ± 0.06
pr_levm_SstoreBench_no_opt 115.1 ± 0.7 114.2 116.5 1.00

@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown

Too many files changed for review (103 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@ethrex-project-sync ethrex-project-sync Bot moved this to In Review in ethrex_l1 Aug 10, 2026
@github-actions

Copy link
Copy Markdown

🤖 Kimi Code Review

⚠️ Warning: Diff was truncated from 14724 to 10000 lines. Some changes were not reviewed.

Looking at this substantial PR that consolidates stateless validation (EIP-8025) and adds signed artifact release workflows, here are my findings:

Critical Security & Correctness

1. Deposit Contract Address Hardcoding (crates/common/types/block_execution_witness.rs:1118-1120)

The amsterdam_chain_config function correctly sets the mainnet deposit contract address, but ensure this is appropriate for all target chains. The comment notes that #3278 leaves only chain_id on the wire, so this assumes all stateless validation uses mainnet's deposit contract. If supporting custom networks with different deposit contracts, this would fail silently (as noted in the test amsterdam_chain_config_sets_deposit_contract).

2. Schema ID Validation (crates/guest-program/src/l1/input.rs:45-50)

The decode_stateless_input function correctly rejects any schema ID other than 0x1501 (Amsterdam). This is load-bearing for consensus: since fork rules are determined by this prefix, verify that:

  • The endianness is correct (big-endian per spec)
  • No other schema IDs are accidentally accepted during development

3. Public Key Validation (crates/guest-program/src/l1/program.rs:186-220)

The validate_public_keys function prevents signature malleability attacks by verifying supplied public keys derive to recovered senders. Ensure this is called before any state-changing operations (confirmed: it's called in verify_stateless_block at line 247 before execute_blocks).

CI/CD & Infrastructure

4. Signing Script Robustness (.github/scripts/sign-stateless-artifacts.sh:39)

printf '%s\n' "$MINISIGN_SECRET_KEY" > "$WORK_DIR/minisign.key"

Issue: If MINISIGN_SECRET_KEY contains % characters, printf will interpret them. Use:

printf '%s\n' "$MINISIGN_SECRET_KEY" > "$WORK_DIR/minisign.key"

Actually, this is correct (uses %s format), but verify the secret doesn't contain trailing newlines that might be stripped.

5. ERE Server Startup Timeout (.github/workflows/tag_release.yaml:284-306)

The 900s timeout for OpenVM STARK keygen is generous, but monitor this: if ere-server exits before health checks begin, the docker ps check catches it, but ensure logs are captured (they are via docker logs ere-server).

6. Artifact Pattern Matching (.github/workflows/tag_release.yaml:489)

pattern: "*ethrex*"

Good fix from ethrex* — the stateless validator artifacts are named stateless-validator-ethrex-*, so the leading wildcard is essential.

Code Quality

7. Progressive SSZ Implementation (crates/common/types/stateless_ssz.rs:193-218)

The hand-written HashTreeRoot for ExecutionPayload uses progressive_container_root. Verify:

  • Field order matches declaration exactly (SSZ requires this)
  • The mix_in_active_fields correctly handles the active fields bitmap

The comment references test/tests/common/progressive_ssz_tests.rs — ensure these tests run in CI to prevent regression if libssz is upgraded.

8. Unsafe Curve Operations (crates/guest-program/stateless-validator/src/crypto/openvm.rs:395-410)

unsafe { bn::G1Affine::from_xy(px, py) }

Safety: The unsafe blocks rely on OpenVM's subgroup checking. Ensure is_in_correct_subgroup() is called after (confirmed: lines 402-407 check this).

9. Feature Flag Cleanup (crates/blockchain/blockchain.rs:55-60)

Good cleanup removing eip-8025 feature gating from rayon paths. The new structure:

  • rayon feature enables parallel warming
  • zisk feature (riscv64 target) uses unsync_cell instead of once_cell

Minor Issues

10. Python Script Error Handling (.github/scripts/ere-execute.py:97-100)

The protobuf varint decoder raises ValueError for unsupported wire types, but doesn't handle the case where the server returns HTTP 200 with malformed protobuf. Add validation for empty public_values in decode_execute_response.

11. Hardcoded Constants (crates/common/types/stateless_ssz.rs:371-372)

pub const STATELESS_INPUT_SCHEMA_ID: u16 = 0x1501;

Consider deriving this from fork metadata rather than magic number, though the comment explains this is Amsterdam (fork 0x15, revision 0x01).

Testing Recommendations

  1. Sign Script Testing: The stateless-signing job in pr_lint_gha.yaml comprehensively tests refusal paths, but add a test for key rotation (when committed key differs from secret).
  2. Conformance Vector: The extract-stateless-fixture.sh script deterministically selects a TRUE-SUCCESS case — verify this runs against the correct schema version (0x1501).

Summary

This is a well-structured consolidation PR. The main risks are:

  • Consensus-critical: Schema ID validation and progressive SSZ merkleization correctness
  • Security: Deposit contract address assumption and public key validation ordering
  • Operational: ERE server timeouts and signing script edge cases

The removal of RISC0 support (due to c-kzg version incompatibility) is noted and tracked. The addition of OpenVM support follows the established patterns for ZisK and SP1.


Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt

@github-actions

Copy link
Copy Markdown

🤖 Codex Code Review

Findings:

  1. tooling/ef_tests/blockchain/test_runner.rs:158-166 and :643-653 can produce a false-green stateless pass. has_fixture_witness becomes true when a block has only stateless_input_bytes, but run_stateless_from_fixture() only exercises the wire path when it has both input and output, and otherwise silently continues if execution_witness is also missing. In that case the stateful run passes, the stateless branch does no validation for that block, and the test still reports success. Tighten the gate to require (statelessInputBytes && statelessOutputBytes) || executionWitness, or make incomplete stateless fixture tuples an error.

  2. .github/scripts/ere-execute.py:57-60 accepts truncated protobuf fields as if they were valid. buf[pos : pos + length] silently returns a shorter slice when pos + length > len(buf), so a malformed or cut-off ExecuteResponse is parsed as a shorter payload instead of failing fast. For the release acceptance check, that weakens the transport/protocol validation and can misattribute server-side corruption to the ELF comparison step. Add an explicit bounds check before yielding the field.

No other major correctness or EVM-rule regressions stood out from the diff. I could not run the targeted cargo builds in this sandbox because rustup/cargo need write access under ~/.rustup and ~/.cargo, which is blocked here.


Automated review by OpenAI Codex · gpt-5.4 · custom prompt

iovoid and others added 10 commits August 11, 2026 17:47
Resolve four conflicts, all in the blockchain EF-test harness and its
known-issues entry.

main's #7094 added a fork-based `skip_stateless_amsterdam` in
`parse_and_execute`, gated on the `Option<BackendType>` parameter, because the
stateless run read the devnet-7-filled `vectors_zkevm/` bundle while the client
had moved to devnet-8. This branch removes that parameter (there is no prover
backend to dispatch on any more) and points the stateless run at the generated
`vectors_stateless_3278/` set instead, so the skip has neither a mechanism nor a
subject left. `test_runner.rs` and `tests/all.rs` therefore keep this branch's
side, and the superseded known-issues section is dropped in favour of the entry
describing the five named `EXTRA_SKIPS`.

The two additions from main that are unrelated to the stateless harness are
kept: the `rpc-compat log-bearing cases excluded` entry from #7142, and the
`$(AMSTERDAM_ARTIFACT).part` cleanup from #7140, unioned with this branch's
`$(STATELESS_VECTORS_ROOT)` and `.stateless-vector-work` removals.
…s path

`BlockHeader::hash` is backed by `unsync_cell::OnceCell` under
`all(feature = "zisk", target_arch = "riscv64")` and by `once_cell::sync::OnceCell`
otherwise. Moving that gate off the removed `eip-8025` feature made the unsync side
live for the first time — both ZisK guests enable `ethrex-common/zisk` — and it has no
`take()`, which `into_with_burned_fees` calls, so `cargo build` for either ZisK guest
fails with E0599. No PR job catches it: the nostd matrix checks `host`, `ere` and
`zkvm-interface` on x86_64, where the cfg is false. Add `take()`; verified by relaxing
the gate to the host and building every workspace lib and bin against the unsync cell.

Alongside it, four findings from a review of this branch:

- `verify_stateless_block` re-ran `validate_block_access_list_hash` on a BAL
  `execute_blocks` had already validated. The check reads only `timestamp`,
  `gas_limit` and `block_access_list_hash`, none of which `into_with_burned_fees`
  changes, so the second call could not disagree — it was a duplicate full BAL
  traversal plus another RLP-encode-and-keccak, paid in proving cycles.
- `ExecutionWitness::from_ssz` called `headers_as_vecs()` twice, copying every
  witness header out of the SSZ list a second time only to drop the first copy.
- The hand-written `HashTreeRoot` impls for `ExecutionPayload` and
  `ExecutionRequests` listed fields in a literal array, so a field added to either
  struct would have been silently excluded from the root and from `active_fields`
  with no compile error. Destructure `Self` without `..` so omission stops the build.
- `ef_tests-blockchain` kept its `ethrex-prover` dependency after the last use of
  `BackendType` went away, and `test-stateless-zkevm` had become a strict subset of
  `test-stateless` under a name saying otherwise. Drop both, and stop
  `download-test-vectors` fetching a zkEVM bundle no target reads.

The per-PR regeneration of the stateless vectors is tracked separately in #7150.
…sion bump

The three `stateless-validator/bin/*` guest workspaces each carry their own
committed lockfile, and all three still recorded the ethrex path crates at
23.0.0. Merging main brought in the v24.0.0 workspace version bump, so
`cargo metadata --locked` could no longer resolve them and `check-cargo-lock`
failed on the first one it reached.

Only the version strings move; no dependency is added, removed or upgraded.
The locks matter because tag_release builds and signs these guests with
`--guest-dir`, so without a resolvable lock the published ELF and VK bytes are
not pinned.
Merging main moved this client from the glamsterdam-devnet-7 gas schedule to
devnet-8, while the generated conformance vectors are still filled at
execution-specs 3c3b6f4af, which carries devnet-7: COLD_STORAGE_ACCESS 3000 vs
2100, ACCOUNT_WRITE 8000 vs 9000, CALL_VALUE 10300 vs 11300, and access-list
storage keys at the full cold cost rather than cold minus WARM_ACCESS. 40
fixtures that passed before the merge now fail in add_block_pipeline with
GasUsedMismatch or ReceiptsRootMismatch, before any stateless validation runs,
which is the same class as the five already skipped.

The pin cannot move to fix this. `eip8025_optional_proofs` does not exist on the
current `forks/amsterdam` default branch and 3c3b6f4af has diverged from it (124
ahead, 315 behind), so it was never merged there; `devnets/glamsterdam/8` is 315
commits behind 3c3b6f4af, carrying the devnet-8 schedule but not the #3278
schema, so vectors filled from it would have the pre-#3278 69-byte
statelessOutputBytes this client cannot parse. No revision has both.

So skip them by name, in a group kept separate from the original five because
the two lift at different times: the five are skew against 3c3b6f4af itself and
predate the devnet-8 move. 60 of the 105 fixtures still execute, so the witness
and SSZ paths stay covered and the empty-suite guard in `parse_and_execute` is
still meaningful — no file is left running zero tests.

Verified locally against freshly generated vectors: 100 files, 0 failures. The
skip list is derived from the fixtures' own JSON keys rather than file paths,
and checked so that no entry is a substring of a key that must keep running.
@ilitteri
ilitteri added this pull request to the merge queue Aug 14, 2026
Merged via the queue into main with commit b527188 Aug 14, 2026
78 checks passed
@ilitteri
ilitteri deleted the feat/stateless-validator-upstream-v2 branch August 14, 2026 19:23
@github-project-automation github-project-automation Bot moved this from In Review to Done in ethrex_l1 Aug 14, 2026
ilitteri added a commit that referenced this pull request Aug 18, 2026
Four conflicts plus one semantic break that git could not see.

`.github/actions/install-zisk/action.yml`: both sides pin the ZisK Rust
toolchain instead of riding `latest`. main's #7010 is the later, dedicated fix
and additionally handles ziskup >= v1.0.0-alpha resolving its install dir from
XDG_CONFIG_HOME, which this branch's version predates and would regress. The
branch's contribution here was purely additive, so main's side is taken whole.

`tooling/ef_tests/blockchain/{Cargo.toml,test_runner.rs}`: #7115 removed the
`BackendType` dispatch from the EF-test runner, so the branch's `zisk` feature
and its `BackendType::ZisK` stub arm — which existed only to keep that match
exhaustive and returned "not wired into ef_tests-blockchain" — no longer have
anything to gate. Both take main's side, and the now-unreferenced
`zisk = ["ethrex-prover/zisk"]` feature is dropped along with the
`features = ["zisk"]` that `tooling/zkevm_bench` requested from it. zkevm_bench
uses `blocks_and_witness_for_test`, `types::TestUnit` and `fork::Fork`, none of
which were ever behind that feature.

Resolved hunk-by-hunk rather than by taking main's file: this branch adds
`blocks_and_witness_for_test` outside the conflict regions, and zkevm_bench
imports it.

`apply_fork_choice` grew a fifth `max_reorg_depth` parameter on main. That
branch-new call site is the only one the merge left at four arguments; it now
passes `None`, matching the three existing call sites in the same file.

`tooling/Cargo.lock` and `tooling/zkevm_bench/Cargo.lock` regenerated.
ilitteri added a commit that referenced this pull request Aug 18, 2026
#7115 redefined the L1 `ProgramInput` as `Vec<u8>` — the spec's schema-prefixed
`statelessInputBytes` — so `ProgramInput::new(blocks, witness)` and the `.blocks`
field this tool relied on no longer exist. Any merge of main into this branch
hits it; the zisk clippy job was the only thing building the crate, which is why
it was the sole failure.

`build_ssz_stateless_input` is the one encoder in the tree that produces those
bytes. It sits under `crates/l2/sequencer/native_rollup/` but touches nothing
beyond `ethrex-common` and `NativeCrypto`, so `ethrex-l2` is taken as a
dependency to reach it. That is a heavy edge for a benchmark tool — the lock
goes from 468 to 920 packages — and the alternative would be to move the encoder
down into `ethrex-common` next to the types it converts.

`SszStatelessInput` carries a single `new_payload_request`, while a cache holds
a `Vec<Block>`. Every ethrex-replay cache is one block, so the conversion
destructures to exactly one and errors otherwise rather than silently
benchmarking the first block of a multi-block cache.

Enabling `ethrex-l2` does not disturb the input type: its `l2` feature is inert
(`l2 = []`) and it depends on `ethrex-guest-program` with default features,
which are `["secp256k1"]`. `ProgramInput` therefore stays `Vec<u8>` rather than
flipping to the L2 rkyv struct through feature unification.

Tests assert the schema-id prefix and non-empty bytes, the structural facts that
survive the type change.
AnkushinDaniil pushed a commit to AnkushinDaniil/ethrex that referenced this pull request Aug 30, 2026
**Motivation**

Merges the `release/v25.0.0` branch back into `main` now that
[v25.0.0](https://github.com/lambdaclass/ethrex/releases/tag/v25.0.0) is
published, per the last step of the [release
process](https://github.com/lambdaclass/ethrex/blob/main/docs/developers/release-process.md).

The second commit matters on its own: **`main`'s release workflow is
currently broken**, and this is what fixes it. Any tag cut from `main`
before this merges would build every artifact and then fail to publish a
release.

**Description**

Two commits, no conflicts with `main`:

- `chore(l1,l2): bump version to 25.0.0` — the 25 internal version pins
across 9 `Cargo.toml` files, the regenerated lockfiles, and both
`--builder.extra-data` defaults in `docs/CLI.md`.

Includes the four
`crates/guest-program/stateless-validator/**/Cargo.lock` files, which
are each their own workspace and reach the ethrex crates through path
dependencies, so they pin the workspace version and go stale on a bump.
`make update-cargo-lock` does not cover them while `check-cargo-lock`
does, so they were refreshed by hand — worth closing that asymmetry in a
follow-up, since every future bump hits it.

- `ci(l1,l2): stop the release asset download from matching docker build
records` — `finalize-release` downloaded artifacts with `pattern:
"*ethrex*"`, which also matches the build records the docker jobs upload
as `<org>~ethrex~<id>.dockerbuild`. Those are not zip archives, so
`download-artifact` exhausted its retries trying to extract them and the
job died before publishing anything.

This is a regression from lambdaclass#7115, which widened the pattern to pick up
the new `stateless-validator-ethrex-*` artifacts; the previous anchored
pattern never matched the docker records. v25.0.0-rc.1 hit it — all 13
build jobs green, no release created. The dry-run rehearsal could not
have caught it either: that job only waits on the guest builds, so it
runs before any docker record exists.

Fixed with two anchored patterns, `ethrex*` and
`stateless-validator-ethrex-*`, verified against the run's real 17
artifacts to select exactly the 12 release assets. rc.2 published all 22
assets correctly.

**Validation**

v25.0.0 went through the full release checklist: three mainnet canaries
paired with Prysm/Teku/Grandine, an eth-docker mainnet node, 13
consecutive successful multisync runs across hoodi/sepolia/mainnet, the
SP1 GPU integration suite, and the L2 upgrade test — the last of which
ran with a real SP1 GPU prover for the first time (see lambdaclass#7203).

**Checklist**

- [ ] Updated `STORE_SCHEMA_VERSION` (crates/storage/lib.rs) if the PR
includes breaking changes to the `Store` requiring a re-sync.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L1 Ethereum client

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Upstream the Ethrex stateless validator and officially publish guest program ELFs

2 participants