Skip to content

perf(verifier): verify STARK proofs in place via rkyv#769

Open
Oppen wants to merge 2 commits into
fix/recursion-precomputed-pagesfrom
perf/rkyv-serialization
Open

perf(verifier): verify STARK proofs in place via rkyv#769
Oppen wants to merge 2 commits into
fix/recursion-precomputed-pagesfrom
perf/rkyv-serialization

Conversation

@Oppen

@Oppen Oppen commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Verifies STARK proofs in place via rkyv, without dropping serde or duplicating the verifier.

Design

  • One verification implementation, two proof sources. crypto/stark/src/proof/view.rs adds a borrowed StarkProofView (and nested Table/FriDecommitment/DeepPolynomialOpening/PolynomialOpenings views), each constructible Owned(&T) or Archived(&ArchivedT). Scalars are copied out (to_native() vs. a plain copy); field-element/commitment arrays stay borrowed either way (slice_as_native on the archived side). The verifier (crypto/stark/src/verifier.rs) is written once against this view. multi_verify (owned) and multi_verify_archived (archived) are thin constructors that build the matching view and share every check — no serialize-then-delegate step, no duplicated logic.
  • serde is kept. StarkProof, MultiProof, FriDecommitment, BusPublicInputs, Table, PolynomialOpenings, and DeepPolynomialOpening dual-derive serde alongside rkyv, so existing serde-based call sites (examples CLI, disk-spill table serialization) are unaffected. Only the STARK proof wire format used by the prover/CLI/recursion guest moved from bincode to rkyv.
  • Recursion guest: unchanged DECODE/page-commitment + program_id design (this branch's approach, not a vkey digest). The private-input blob gets a 12-byte LVMR magic/version prefix so the rkyv archive lands 16-aligned in guest memory (the mapped payload itself is only 4-aligned); this prefix is local to the recursion blob and does not change the global private-input ABI (PRIVATE_INPUT_START + 4 is unchanged for every other guest). The guest borrows the blob in place (get_private_input_slice) and verifies straight from the archive — no deserialization pass.
  • ethrex quarantine: ethrex pins rkyv's unaligned feature, which cannot coexist with this workspace's aligned feature in one resolved dependency graph. Its host-reference tests move to a detached workspace, tooling/ethrex-tests (own cargo test), isolating Cargo.lock resolution.
  • CLI proof files switch bincode → rkyv.

Numbers

Recursion verifier guest, inner empty proof (make test-profile-recursion-single / -multi), measured against this branch's pre-rkyv commit (db2bfa8b):

before (serde/bincode) after (rkyv, this PR) Δ
1 query, blowup=2 116,957,166 cycles 91,615,910 −21.7%
multi-query, blowup=8 2,805,899,310 cycles 2,123,867,476 −24.3%

Regression check: regular (non-recursion) verify path

multi_verify's owned path no longer serializes to reach the shared implementation (it used to archive-then-delegate); checked this isn't a wash or regression via cargo bench -p stark --bench prover_benchmark --features test-utils -- quick, comparing this branch against main:

config main this branch Δ
verify/fib_2col_16k 582.6 µs 553.2 µs −5.0%
verify/fib_4col_8k 513.0 µs 490.8 µs −4.3%
verify/fib_4col_16k 590.9 µs 582.7 µs −1.4%

No regression on the regular verify path — dropping the serialization step is a small net win there too. prove times are unchanged (within noise) on all three configs, as expected (prove was never touched).

Verification

Full workspace suite green: cargo test -p stark --lib (188, +193 with disk-spill), cargo test -p lambda-vm-prover --lib (509, incl. continuation and recursion smoke tests), cargo test -p executor, tooling/ethrex-tests. cargo clippy -D warnings clean on both stark feature configurations. cargo build --workspace clean.

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Benchmark Results for modified programs 🚀

Command Mean [ms] Min [ms] Max [ms] Relative
head ecsm 3.3 ± 0.1 3.3 3.4 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
head hashmap 135.6 ± 4.1 128.7 141.8 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
head keccak 132.7 ± 1.7 130.5 135.3 1.00
Command Mean [ms] Min [ms] Max [ms] Relative
head syscall_commit 97.4 ± 1.1 95.6 98.8 1.00

Comment thread executor/tests/rust.rs

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved out of the main workspace due to rkyv/unaligned feature bubbling up to lambda_vm. Not actually deleted.

@Oppen Oppen force-pushed the pr/vkey branch 2 times, most recently from a5515d1 to 65f990f Compare July 3, 2026 21:09
crypto/stark: StarkProof, MultiProof, FriDecommitment,
BusPublicInputs, Table, PolynomialOpenings, and
DeepPolynomialOpening dual-derive rkyv alongside serde. The
verifier operates over a StarkProofView (owned or an
rkyv-archived buffer read in place); multi_verify and
multi_verify_archived both build the matching view and share one
verification implementation, so neither the owned nor the
archived path pays a serialization cost.

prover: the recursion guest verifies its inner proof straight
from the mmapped private-input region via a 12-byte aligning
magic/version prefix, with no deserialization pass. Continuation
and CLI verification go through the same proof-view path.

executor/syscalls: get_private_input_slice() borrows the private
input in place instead of copying it into a Vec.

bin/cli: proof file persistence uses rkyv instead of bincode.

tooling/ethrex-tests: ethrex's guest tests move into their own
detached workspace, since ethrex pulls in rkyv's "unaligned"
feature which cannot coexist with the root workspace's "aligned"
feature in one resolved dependency graph.
@Oppen Oppen force-pushed the perf/rkyv-serialization branch from ea14eeb to 2e68711 Compare July 8, 2026 20:09
@Oppen Oppen changed the base branch from pr/vkey to fix/recursion-precomputed-pages July 8, 2026 20:10
@Oppen Oppen changed the title perf: single-format rkyv serialization + zero-copy in-place verifier perf(verifier): verify STARK proofs in place via rkyv Jul 8, 2026
@Oppen

Oppen commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

/ai-review

@Oppen

Oppen commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator Author

/bench

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codex Code Review

Findings

  • High - access_unchecked is used on prover-supplied recursion input without bytecheck validation. In prover/src/lib.rs, verify_recursion_blob only validates magic/version/alignment, then calls rkyv::access_unchecked on the archive. The blob is explicitly prover-supplied in the recursion guest, so malformed archived vec pointers/lengths can make later as_slice() calls and zero-copy field casts create invalid references or out-of-bounds slices. That is Rust UB on host and can become a guest panic/trap/DoS instead of a clean verification failure. Use rkyv bytecheck validation (rkyv::access/checked archived root) before zero-copy access, and derive/implement CheckBytes for the archived proof/input types needed by this path.

No build or tests run, per the static-review constraints.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Benchmark — ethrex 20 transfers (median of 3)

Table parallelism: auto (cores / 3)

Metric main PR Δ
Peak heap 72744 MB 73423 MB +679 MB (+0.9%) ⚪
Prove time 40.472s 40.227s -0.245s (-0.6%) ⚪

✅ No significant change.

🔬 Looks like a small speedup (-0.6%) — below what 3 runs can confirm. Comment /bench-abba to run the drift-free ABBA tiebreaker (paired-t CI + exact Wilcoxon). Note: it occupies the bench server for ~30–40 min.
Optional pair count: /bench-abba 32 (20 resolves ~1%, 32 for ~0.6%).

✅ Low variance (time: 0.8%, heap: 1.1%)

Commit: 2e68711 · Baseline: cached · Runner: self-hosted bench

@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review: perf(verifier): verify STARK proofs in place via rkyv

Reviewed the core source changes (proof/view.rs, math/.../element.rs rkyv impls, table.rs archived views, verifier.rs, prover/src/lib.rs, syscalls.rs, merkle proof.rs, lookup.rs, continuation.rs, CLI, and the ethrex-test relocation). This is a well-engineered change: the single view-based verifier avoids logic duplication, the zero-copy soundness argument (LE-only reinterpret, #[repr(transparent)], compile_error! on big-endian) is sound and clearly documented, and the malformed-archive paths are deliberately hardened (checked_sub for the OOD column count, .get(j)? bounds, dimensions_consistent() + height() == 0 rejection, checked_mul, caller-supplied ProofOptions, magic/version prefix before the access_unchecked, and an aligned-copy fallback for misaligned host buffers). No soundness or false-accept issues found. A few minor notes below.

Low — archived Table::into_frame can panic on a malformed OOD table

ArchivedTable::into_frame (crypto/stark/src/table.rs) only debug_assert!s that height.is_multiple_of(step_size). In release, a prover-supplied archived proof whose trace_ood_evaluations height is not a multiple of the (trusted) air.step_size() — reachable for tables with step_size > 1, e.g. height = 1, step_size = 2 — steps past height and indexes row_major_data()[start..start+width] out of bounds, panicking. The guard added in multi_verify_views (dimensions_consistent() && height() != 0) doesn't cover this case. No soundness impact (a guest panic is just a verification failure), but it's inconsistent with the surrounding "reject, don't panic" discipline on the untrusted archived path. Consider adding height % step_size == 0 to the multi_verify_views guard (or having the archived into_frame return early), so a malformed archive fails gracefully rather than aborting.

Low — verify_recursion_blob is pub and relies on access_unchecked

verify_recursion_blob (prover/src/lib.rs) is a public API that calls rkyv::access_unchecked with no structural validation. On the guest this is safe by the VM memory model (documented), and current host callers are trusted (tests) — but on the host access_unchecked on an untrusted blob is real UB. The safety reasoning lives in an inner comment; consider surfacing the "host callers must pass self-produced blobs" contract in the public doc comment so external callers don't misuse it.

Low — CLI rkyv::from_bytes depends on buffer alignment

cmd_verify/cmd_verify_continuation (bin/cli/src/main.rs) pass a Vec<u8> from fs::read straight to rkyv::from_bytes. rkyv's checked access requires the buffer to meet the archive's alignment (8 here); it returns Err (not UB) otherwise. In practice the system allocator returns ≥16-byte-aligned buffers for large proof files, so this works — but it's a latent fragility worth a comment, since a misaligned buffer would surface as a spurious "Failed to deserialize proof".

Cargo.lock churn, example one-line edits, and the ethrex detached-workspace move all look correct.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

AI Review

PR #769 · 39 changed files

Warning: the diff was truncated before review.

⚠️ All 5 reviewers failed (see Reviewer Lanes below) — this is NOT a clean result; the review did not run.

Findings

No non-rejected structured findings were reported.

Reviewer Lanes

Lane Model Prompt Status Findings
glm openrouter/z-ai/glm-5.2 general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0
kimi openrouter/moonshotai/kimi-k2.7-code general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0
minimax minimax/MiniMax-M3 general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0
moonmath zro/minimax-m3 general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0
nemotron openrouter/nvidia/nemotron-3-ultra-550b-a55b general error: opencode failed (provider/auth/runtime error) and no findings were submitted 0

Native Codex and Claude reviews run separately and post their own comments. They are not included in this structured provenance report.

Raw lane outputs, candidates, final issues, and model metrics are uploaded as workflow artifacts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant