Repository navigation
Conversation
The Fleet worker (4849354, chore/ziren-v2-stateless + fix/ethproofs-consecutive) forked from the guest optimisation line at 9215aa6 and shipped the older guest: no in-guest JUMPDEST table (the host's revm JumpTable was trusted under a code hash that covers only the code bytes), no zkVM syscalls for 256-bit arithmetic and the curves, no arena tries. This squashes perf/guest-hashed-key-memo (7147f4e: feat/guest-syscalls 4a15fee plus the trie-key memo and the checked logs bloom). Resolution: - bin/eth-proofs, the root Cargo.toml and Cargo.lock: the worker's. The worker keeps the git-locked Ziren 2.0 graph; the opt line's sibling-checkout [patch] section is dropped. - Guest, mpt and the witness RPC db: the opt line's. The worker's 2c0245a (preserve untouched storage roots, remove empty accounts) was already in ArenaState::update; its 41317cd (omitted storage of an absent account or an authenticated empty storage root reads zero, any other missing storage trie is an error) is now ArenaState::storage, with its three tests. a75cb33 (verified bytecode recovery) is the opt line's complete_account_code; the duplicated structured-header decoder is kept once. - bin/guest keeps the opt line's graph (precompile forks); its zkm-zkvm/zkm-lib [patch] points at the sibling Ziren checkout, whose guest-side crates (crates/zkvm, crates/build) are identical to 0dd65a15 and 31f6e8eb. Validation: - cargo test -p mpt: 24 passed (the arena storage rule and the update rule). - host (this tree) fetched and executed 26184581-26184583 natively: 278.70M, 269.57M, 52.72M cycles, block hashes as the chain's. - GPU, one card through Fleet (image e786dd = Ziren 31f6e8eb), guest 941629f6: 53.73/53.40/18.01 s, every proof verified against a 31f6e8eb client, public values identical to the deployed guest's (334b45b8: 73.97/73.64/20.61 s on the same blocks, -24.5% overall).
eigmax
force-pushed
the
perf/ethproofs-guest-opt
branch
from
October 15, 2026 01:18
d572e65 to
f30cf7d
Compare
The proof commits only the block hash, so the chain spec and the beneficiary override in the input came from the prover unchecked. The guest now accepts mainnet inputs only, with no custom beneficiary. The cycle-tracker wrapper around every precompile call turned the precompile set into a map probed on each call. It is now behind the precompile-tracking feature, for profiling builds. Measured on the executor (blocks 26184948 / 26184949, same committed hashes): cycles -0.19% / -0.28%, main-trace area -0.11% / -0.16%.
The guest derives every executed code's jump table itself, a walk over ~3 M opcodes per mainnet block. Compiled, the walk is nine instructions per opcode, two of them nops in branch delay slots. jumpdest.s fills the slots (the cursor advance, and the next opcode's load, which reads at most 32 bytes into the code's zero padding): six per opcode. Off the zkVM the Rust walk stays. Checked in the executor against the Rust walk on every code of two mainnet blocks and 2,000 random codes. Measured (blocks 26184948 / 26184949, same committed hashes): cycles -1.39% / -1.79%, main-trace area -0.80% / -1.00%.
Moves the guest to the fork's patch-31.0.2-mips32-dispatch branch, which adds to the 32-bit fast paths: static gas charged on the low word of the remaining gas, JUMPDEST folded into the taken jump that lands on it, and PUSH2 + JUMP / JUMPI run as one instruction. Measured on the executor (blocks 26184948 / 26184949, same committed hashes): cycles -3.20% / -3.44%, main-trace area -2.06% / -2.16%.
Every Ziren dependency now names the perf/shamir-msm branch (Ziren #562: Shamir's trick in the zkVM lib's two-point MSM, which ECDSA recovery runs per transaction), and the guest's comment says the Ziren checkout next to this repo, which its zkvm crates come from, is to be on that branch. The precompile patches still declare Ziren unbranched, and cargo cannot redirect a git source to another branch of it, so the lockfile also keeps their main-branch zkm-lib, zkm-primitives and zkm-zkvm. Those edges exist only for the zkVM target, where the guest's path patch replaces both with the one checkout. Back to main when #562 lands. Measured on the executor (blocks 26184948 / 26184949, same committed hashes): main-trace area -1.83% / -1.71%, cycles -1.05% / -1.00%.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #23. This brings the optimized guest into the Ziren 2.0 EthProofs worker.
Why
The Fleet worker (#23 and its follow-ups) branched from the guest optimization line in April and still ships the older guest:
ClientExecutorInput.bytecodescarries revmBytecode, including the host'sJumpTable. The guest checks the code hash withhash_slow, which hashes only the code bytes. A prover can therefore mark a0x5binside PUSH data as a valid jump target, or a realJUMPDESTas invalid, under the correct code hash. The optimized guest takes the raw code (RawCode) and derives the jump table itself.What is in it
Two commits on top of #23:
Prepare consecutive EthProofs blocks in parallel: the worker change the current deployment runs.guest: the optimised guest line in the Ziren 2.0 EthProofs worker: the guest optimization line (feat/guest-syscalls, whose commits are public there) squashed into the worker, plus two further guest changes:TrieDBand the post-state root pass reusekeccak(address)andkeccak(slot)inside the zkVM.verify_receiptsalready compared with the receipts, instead of building every receipt bloom a second time. Optimism still accumulates it.Its message describes how the two lines were reconciled. In short:
bin/eth-proofs, the root manifest and the lock come from the worker. The git-locked Ziren 2.0 graph is kept.mptand the witness RPC database come from the optimization line.ArenaState::update). Omitted storage of an absent account, or of one with an authenticated empty root, reads as zero, and any other missing trie is an error; that rule is nowArenaState::storage, with its three tests.complete_account_code.Results
Same blocks, identical committed block hashes, every proof verified:
The times are single-card prove times. Running as the production worker on 8 cards, the first 24 blocks after the switch averaged 7.97 s per block (median 7.82 s, p90 9.89 s). The last 100 blocks of the previous guest averaged 11.98 s (median 10.92 s, p90 16.51 s).
941629f6acb9a229d810a7cb32dbd0c5d57ad050bf1ad43b5edd7d2a21454e1b0x00e87eb3c2f0e827d29811dddb265bf03653e65acc08ca48e88536c83ed9de79cargo test -p mpt: 24 passed.Further guest changes
Four more commits make the guest cheaper and close one more input hole:
perf(guest): bind the chain to mainnet, gate the precompile tracker. The proof commits only the block hash, so the chain spec and the beneficiary override in the input came from the prover unchecked. The guest now accepts mainnet inputs only, with no custom beneficiary. The per-precompile cycle-tracker wrapper, which turned the precompile set into a map probed on every call, is now behind aprecompile-trackingfeature.perf(guest): walk the JUMPDEST map in assembly on the zkVM. The in-guest jump-table walk is hand-scheduled MIPS (jumpdest.s): six instructions per opcode instead of nine, with the branch delay slots filled and a read-ahead that stays inside the code's zero padding. It was checked against the Rust walk on every code of two mainnet blocks and on 2,000 random codes. Native builds keep the Rust walk.perf(guest): revm fork with cheaper dispatch and jumps. This moves to ziren-patches/revmpatch-31.0.2-mips32-dispatch(interpreter: cheaper dispatch and jumps on 32-bit ziren-patches/revm#4): static gas charged on the low word of the remaining gas, JUMPDEST folded into the taken jump that lands on it, and PUSH2 + JUMP / JUMPI run as one instruction.deps: Ziren from perf/shamir-msm. This uses perf(zkvm-lib): Shamir's trick in the two-point MSM Ziren#562 (Shamir's trick in the zkVM lib's two-point MSM, which ECDSA recovery runs per transaction). The Ziren checkout the guest builds from is to be on that branch. The lockfile also keeps the precompile patches' main-branch zkm-lib, zkm-primitives and zkm-zkvm; those edges exist only for the zkVM target, where the guest's path patch replaces them.Once revm#4 and Ziren#562 merge, the revm and Ziren dependencies go back to
patch-31.0.2-mips32andmain.Measured in the executor on blocks 26184948 / 26184949, with identical committed block hashes:
f30cf7dThese four have not been proved on a GPU yet. On the current 5-card production, proving time fits 4.44 s + 33.7 ms per million cycles over the last 200 blocks, which puts the expected gain at about 4% of the mean block time (12.45 s → ~12.0 s). The program vk changes with the guest ELF.