Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d381499
Bump ethrex to 4f658c2b, regenerate fixtures
jotabulacios Aug 3, 2026
4657bbb
Cover ethrex_bench_4.bin in the checksum gate
jotabulacios Aug 3, 2026
0fd77ef
Merge branch 'main' into chore/bump-ethrex-rev
jotabulacios Aug 3, 2026
03d3ffe
Point the real-block fixture at the post-bump asset
jotabulacios Aug 3, 2026
1e53cd3
Merge branch 'chore/bump-ethrex-rev' of github.com:yetanotherco/lambd…
jotabulacios Aug 3, 2026
a9ec7ce
Merge branch 'main' into chore/bump-ethrex-rev
jotabulacios Aug 4, 2026
25d5e4b
Merge remote-tracking branch 'origin/main' into chore/bump-ethrex-rev…
diegokingston Aug 10, 2026
55b400c
docs(ethrex): point the fixture docs at the rev-suffixed asset
diegokingston Aug 10, 2026
50b4ce2
Merge origin/main into chore/bump-ethrex-rev
diegokingston Aug 19, 2026
7d76e2f
Merge branch 'main' into chore/bump-ethrex-rev
jotabulacios Aug 25, 2026
d829a2c
Move the ethrex pin from 4f658c2b to 797df554, off the unmerged
jotabulacios Aug 26, 2026
fdbd91c
rm file
jotabulacios Aug 26, 2026
21114f5
Merge branch 'main' into chore/bump-ethrex-rev
jotabulacios Sep 7, 2026
d2424cc
Regenerate the ethrex-crypto lock, add --locked
jotabulacios Sep 9, 2026
fd164cc
Gate the ethrex rev and locks in CI
jotabulacios Sep 9, 2026
908868f
Drop the dead lambda_vm patch sections
jotabulacios Sep 9, 2026
c141ac5
Pin the missing BLS12-381 backend with a test
jotabulacios Sep 9, 2026
55941ae
Key the real-block benchmark on the fixture digest
jotabulacios Sep 9, 2026
6cfb43c
Repoint the ethrex docs at 797df554
jotabulacios Sep 9, 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
59 changes: 46 additions & 13 deletions .github/workflows/benchmark-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ env:
ELF: executor/program_artifacts/rust/ethrex.elf
# The workload: a real Ethereum block. WHICH block lives in
# the Makefile and nowhere else — nothing in this file names one, so a repoint
# moves this job without editing it. At the current default that is 50.78M cycles,
# 10,478 keccak calls and 116 ecsm calls: ~5.8x the work of the synthetic block with a
# ~18x different keccak:ecrecover mix. That is the whole point — a prover change
# can move the synthetic number and the real one in opposite directions.
# moves this job without editing it. At the current default and pinned ethrex rev that
# is 30,498,818 cycles, 10,659 keccak calls and 116 ecsm calls — several times the work
# of the synthetic block, at a very different keccak:ecrecover mix. That is the whole
# point: a prover change can move the synthetic number and the real one in opposite
# directions. All three figures move with the ethrex rev, so re-measure on a bump
# rather than carrying them across one.
#
# The path is resolved from the Makefile (`make -s print-real-block-fixture`) into
# REAL_INPUT at run time. The ~1 MB .bin is gitignored and FETCHED by URL + sha256
Expand Down Expand Up @@ -191,11 +193,13 @@ jobs:

- name: Generate ethrex bench fixtures
run: |
# Generated, not committed (gitignored via executor/.gitignore). They are
# untracked, so they survive the baseline `git checkout origin/main` below —
# the SAME workload (ELF + inputs) is proven on both the PR and main sides.
# Generated, not committed (gitignored via executor/.gitignore) — except
# ethrex_bench_4.bin, which the same file re-includes and is therefore tracked.
# The gitignored ones survive the baseline `git checkout origin/main` below on
# their own; bench_4 is restored explicitly there. Either way the SAME workload
# (ELF + inputs) is proven on both the PR and main sides.
# distinct = N independent genesis-funded senders -> N distinct recipients.
( cd tooling/ethrex-fixtures && cargo build --release )
( cd tooling/ethrex-fixtures && cargo build --locked --release )
GEN=tooling/ethrex-fixtures/target/release/ethrex-fixtures
for n in $GROWTH_STEPS; do
"$GEN" "$n" "executor/tests/ethrex_bench_${n}.bin" distinct
Expand Down Expand Up @@ -459,6 +463,11 @@ jobs:
echo "real_time_spread=$TIME_SPREAD"
echo "real_all_times=$ALL_TIMES"
echo "real_input=$(basename "$REAL_INPUT")"
# The basename is derived from network+block, so it is a complete key for
# WHICH block but not for which bytes: the archived ProgramInput layout moves
# with the ethrex rev, so one basename can name two byte streams. The digest
# is what makes the comparison guard below able to see a fixture swap.
echo "real_input_sha=$(sha256sum "$REAL_INPUT" | cut -c1-12)"
} | tee -a /tmp/metrics.txt >> "$GITHUB_OUTPUT"

- name: Upload metrics artifact
Expand Down Expand Up @@ -491,7 +500,8 @@ jobs:
# grep could match two lines and write a multi-line step output.
get() { grep "^$1=" "$BASELINE_FILE" | head -1 | cut -d= -f2; }
for key in growth_heaps growth_slope_mb growth_r2 \
real_time_s real_peak_mb real_time_spread real_input; do
real_time_s real_peak_mb real_time_spread real_input \
real_input_sha; do
# A baseline predating the real block simply has no real_* keys; empty
# values hide the table rather than producing a bogus comparison.
echo "$key=$(get "$key")" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -521,11 +531,19 @@ jobs:
PR_SHA=$(git rev-parse HEAD)

# Checkout main and rebuild the prover (CLI) only. The workload — the gitignored
# ethrex ELF and the generated, untracked bench fixtures — is left untouched by
# the checkout, so the same inputs are proven on both the PR and main sides.
# ethrex ELF and the real-block fixture — is left untouched by the checkout, so
# the same inputs are proven on both the PR and main sides.
git fetch origin main
git checkout origin/main

# ...with one exception: ethrex_bench_4.bin is TRACKED (executor/.gitignore
# re-includes it), so the checkout above replaces it with main's copy. The
# generator is deterministic, so the file is clean against HEAD and the checkout
# succeeds silently. On a PR that bumps the ethrex rev those bytes are a
# different archive layout, and bench_4 is index 0 of GROWTH_PROGRAMS — the
# point would be a fabricated delta. Put the PR's copy back.
git checkout "$PR_SHA" -- executor/tests/ethrex_bench_4.bin

cargo build --release -p cli --features jemalloc-stats

# --- Growth benchmarks (default parallelism, 1 sample each) ---
Expand Down Expand Up @@ -642,6 +660,7 @@ jobs:
BA_REAL_PEAK: ${{ steps.baseline-artifact.outputs.real_peak_mb }}
BA_REAL_SPREAD: ${{ steps.baseline-artifact.outputs.real_time_spread }}
BA_REAL_INPUT: ${{ steps.baseline-artifact.outputs.real_input }}
BA_REAL_INPUT_SHA: ${{ steps.baseline-artifact.outputs.real_input_sha }}
# Baseline run outputs
BR_GROWTH_HEAPS: ${{ steps.baseline-run.outputs.growth_heaps }}
BR_GROWTH_SLOPE: ${{ steps.baseline-run.outputs.growth_slope_mb }}
Expand All @@ -657,6 +676,7 @@ jobs:
PR_REAL_PEAK: ${{ steps.pr-real.outputs.real_peak_mb }}
PR_REAL_EPOCHS: ${{ steps.pr-real.outputs.real_epochs }}
PR_REAL_INPUT: ${{ steps.pr-real.outputs.real_input }}
PR_REAL_INPUT_SHA: ${{ steps.pr-real.outputs.real_input_sha }}
PR_REAL_RUNS: ${{ steps.pr-real.outputs.real_runs }}
PR_REAL_TIME_SPREAD: ${{ steps.pr-real.outputs.real_time_spread }}
PR_REAL_ALL_TIMES: ${{ steps.pr-real.outputs.real_all_times }}
Expand All @@ -671,6 +691,7 @@ jobs:
BASELINE_REAL_PEAK="$BA_REAL_PEAK"
BASELINE_REAL_SPREAD="$BA_REAL_SPREAD"
BASELINE_REAL_INPUT="$BA_REAL_INPUT"
BASELINE_REAL_INPUT_SHA="$BA_REAL_INPUT_SHA"
else
BASELINE_SRC="built from main"
BASELINE_GROWTH_HEAPS="$BR_GROWTH_HEAPS"
Expand All @@ -684,6 +705,7 @@ jobs:
# Freshly proven on this runner from $REAL_INPUT, so by construction the
# same block the PR side used; the cached path carries its own label.
BASELINE_REAL_INPUT="$PR_REAL_INPUT"
BASELINE_REAL_INPUT_SHA="$PR_REAL_INPUT_SHA"
fi

echo "baseline_src=$BASELINE_SRC" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -714,9 +736,20 @@ jobs:
# except on a mismatch, which real_mismatch below already carries.

if [ -n "$PR_REAL_TIME" ] && [ -n "$BASELINE_REAL_TIME" ]; then
# Two keys, because they catch different things: the basename catches a
# repoint to another block, the digest catches the same block re-archived at
# a different ethrex rev (a baseline older than a pin bump). A baseline
# predating the digest key has an empty one and is compared on name alone,
# which is the pre-existing behaviour rather than a new mismatch.
REAL_SKEW=""
if [ -n "$BASELINE_REAL_INPUT" ] && [ "$BASELINE_REAL_INPUT" != "$PR_REAL_INPUT" ]; then
echo "::warning::Baseline real block ($BASELINE_REAL_INPUT) differs from the PR's ($PR_REAL_INPUT); not comparing."
echo "real_mismatch=$BASELINE_REAL_INPUT" >> "$GITHUB_OUTPUT"
REAL_SKEW="$BASELINE_REAL_INPUT"
elif [ -n "$BASELINE_REAL_INPUT_SHA" ] && [ "$BASELINE_REAL_INPUT_SHA" != "$PR_REAL_INPUT_SHA" ]; then
REAL_SKEW="$BASELINE_REAL_INPUT@$BASELINE_REAL_INPUT_SHA"
fi
if [ -n "$REAL_SKEW" ]; then
echo "::warning::Baseline real block ($REAL_SKEW) differs from the PR's ($PR_REAL_INPUT@$PR_REAL_INPUT_SHA); not comparing."
echo "real_mismatch=$REAL_SKEW" >> "$GITHUB_OUTPUT"
else
REAL_TIME_DIFF=$(awk "BEGIN { printf \"%.3f\", $PR_REAL_TIME - $BASELINE_REAL_TIME }")
REAL_TIME_PCT=$(awk "BEGIN { printf \"%.1f\", (($PR_REAL_TIME - $BASELINE_REAL_TIME) * 100) / $BASELINE_REAL_TIME }")
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/pr_main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ jobs:
- name: Check ethrex fixture checksums
run: make check-ethrex-fixture-checksums

# Five manifests carry the ethrex rev and they must agree: a skew is not a build
# failure, it is a fixture the guest decodes as the all-zero default. `--show`
# exits non-zero on MIXED. It reads manifests only; the matching locks are held by
# `--locked` on each workspace's own test/build step.
- name: Check the ethrex rev is consistent across manifests
run: scripts/set_ethrex_rev.sh --show

# The benchmark comment harness: verifies every steps.X.outputs.Y consumed in
# benchmark-pr.yml has a producer (the orphaned-wiring class that once broke
# /bench-growth silently), then renders the full comment scenario suite.
Expand Down Expand Up @@ -153,7 +160,7 @@ jobs:
- name: Run ethrex host-reference tests (detached workspace)
run: |
cd tooling/ethrex-tests && \
cargo test --release -- --include-ignored --skip test_ethrex_real_block
cargo test --locked --release -- --include-ignored --skip test_ethrex_real_block

test-cli:
name: CLI tests
Expand Down
23 changes: 16 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,12 @@ test-rust: compile-programs-rust
# measured cost.
ETHREX_REAL_BLOCK_NETWORK := mainnet
ETHREX_REAL_BLOCK := 25368371
ETHREX_REAL_BLOCK_FIXTURE_URL := https://github.com/yetanotherco/lambda_vm/releases/download/bench-fixtures-v1/ethrex_mainnet_25368371.bin
ETHREX_REAL_BLOCK_FIXTURE_SHA256 := 61eba49b6b254f4a05def5a47b08a21ae3eee56f0d37bcd7b3a24b0cc1e4a300
# The asset name carries the ethrex rev because the bytes are a function of it: the
# archived ProgramInput layout moves with the pin, so one block has one fixture per rev.
# Uploading under a new name rather than replacing the old one keeps `main` — which still
# expects the pre-bump sha256 — fetching its own artifact while this branch is open.
ETHREX_REAL_BLOCK_FIXTURE_URL := https://github.com/yetanotherco/lambda_vm/releases/download/bench-fixtures-v1/ethrex_mainnet_25368371_797df554.bin
ETHREX_REAL_BLOCK_FIXTURE_SHA256 := 573004e62e3680a00d3cdbae19dc4897e2ec60d6ec0c1d05d9ef118cb8aef17f
# The block's source cache, hosted in the same release. Only `regen-real-block-fixture`
# reads it — the converter's TESTS use a different, upstream-pinned cache (below).
ETHREX_REAL_BLOCK_CACHE_URL := https://github.com/yetanotherco/lambda_vm/releases/download/bench-fixtures-v1/cache_mainnet_25368371.json
Expand Down Expand Up @@ -433,7 +437,7 @@ ethrex-real-block-converter-cache: $(ETHREX_CONVERTER_CACHE)
# network-rejection guard, and the reproducibility digest. Runs on changes to the
# converter (see .github/workflows/ethrex-block-converter.yml), not on every PR.
test-ethrex-real-block-converter: $(ETHREX_CONVERTER_CACHE)
cd tooling/ethrex-block-converter && cargo test --release
cd tooling/ethrex-block-converter && cargo test --locked --release

# Manual regeneration of the BENCHMARK fixture (not the converter's test block):
# fetches that block's own cache and re-converts it, overwriting the fixture in
Expand All @@ -459,7 +463,7 @@ test-ethrex: compile-programs-rust ethrex-real-block-fixture
# otherwise fail on a clean checkout. The committed synthetic fixtures and
# `no_kzg_backend_linked` still run.
test-ethrex-offline: compile-programs-rust
cd tooling/ethrex-tests && cargo test --release -- --include-ignored --skip test_ethrex_real_block
cd tooling/ethrex-tests && cargo test --locked --release -- --include-ignored --skip test_ethrex_real_block

test-flamegraph:
cargo test -p executor --test flamegraph
Expand Down Expand Up @@ -499,7 +503,8 @@ regen-ethrex-fixtures:
cd tooling/ethrex-fixtures && \
cargo run --release -- 0 ../../executor/tests/ethrex_empty_block.bin && \
cargo run --release -- 1 ../../executor/tests/ethrex_simple_tx.bin && \
cargo run --release -- 10 ../../executor/tests/ethrex_10_transfers.bin
cargo run --release -- 10 ../../executor/tests/ethrex_10_transfers.bin && \
cargo run --release -- 4 ../../executor/tests/ethrex_bench_4.bin distinct
$(MAKE) update-ethrex-fixture-checksums

update-ethrex-fixture-checksums:
Expand Down Expand Up @@ -530,9 +535,13 @@ test-syscalls:
# exercises the implementation that actually ships; the debug run is kept because
# its magnitude debug_asserts turn a contract violation into a loud panic instead
# of a silently wrong value.
# `--locked` because this is the only target that builds that workspace: without it
# cargo re-resolves and REWRITES the committed lock on a manifest/lock mismatch, so
# the job passes while the lock describes a different build than the one that ran.
# That is how the ethrex rev bump left this lock a rev behind the manifest.
test-ethrex-crypto:
cd crypto/ethrex-crypto && cargo test
cd crypto/ethrex-crypto && cargo test --release
cd crypto/ethrex-crypto && cargo test --locked
cd crypto/ethrex-crypto && cargo test --locked --release

test: compile-programs test-syscalls test-ethrex-crypto
cargo test
Expand Down
7 changes: 7 additions & 0 deletions bench_vs/README_ethrex.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ rkyv-encoded) for the ethrex commit pinned (as `rev`) in
`executor/programs/rust/ethrex/Cargo.toml`. The guest reads it via
`get_private_input()` and runs ethrex's `execution_program`.

Both the fixtures and those instruction counts are therefore a function of that pin: the
counts above were measured at `156cb8d6` and have not been re-measured since the
`797df554` bump, which regenerated both fixtures. Treat them as an order of magnitude,
and re-run the benchmark for real numbers.

The timing window is **single-shot end-to-end prove** (ELF load + execution +
trace build + AIR construction + STARK prove); it **excludes** verification.

Expand Down Expand Up @@ -42,6 +47,8 @@ Output (example):
ethrex 1 tx 47.302s 4392951
```

(Example output, taken at ethrex `156cb8d6` — see the note above.)

With `--report-dir DIR` it also writes:
- `DIR/ethrex_summary.md` — markdown table
- `DIR/ethrex_metrics.txt` — `<slug>_time_s=` / `<slug>_cycles=` per block
Expand Down
Loading
Loading