recursion: supply DECODE/page commitments via private input; bind them to the inner ELF via a program_id fold#782
recursion: supply DECODE/page commitments via private input; bind them to the inner ELF via a program_id fold#782Oppen wants to merge 5 commits into
Conversation
|
/ai-review |
Codex Code ReviewFindings
Static review only; I did not build or run tests per the sandbox instructions. |
|
Review: recursion guest — private-input DECODE/page commitments + committed program identity Read the full diff and traced the dependencies (
Findings
Nothing else blocking. The intentionally-insecure |
AI ReviewPR #782 · 8 changed files Findings
Status column reflects the verdict from the verifier: deepseek-verifier (openrouter/deepseek/deepseek-v4-pro). AI-001: New test file recursion_soundness_gap_poc.rs missing fri_final_poly_log_degree in MIN_PROOF_OPTIONS struct literal
Claim The Evidence Lines 31-37 of recursion_soundness_gap_poc.rs set only blowup_factor/fri_number_of_queries/coset_offset/grinding_factor; struct definition at crypto/stark/src/proof/options.rs:44-53 requires Suggested fix Add AI-002: absorb_statement call in recursion_soundness_gap_poc.rs missing fri_final_poly_log_degree argument
Claim The Evidence statement.rs:86-96 declares Suggested fix Add AI-003: test_recursion_blob_decodes_and_verifies_on_host no longer ignored — panics if empty.elf missing
Claim The PR removed Evidence Diff context shows the test was Suggested fix Either keep AI-004: test_recursion_rejects_corrupted_commitment panics if empty.elf artifact missing
Claim This new test (added by the PR) runs by default and calls Evidence New test at line 611-635 added by the PR, no Suggested fix Either skip the test with a clear message when the artifact is missing (e.g., return early with Reviewer Lanes
Verification Lanes
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. |
|
/ai-review |
Codex Code ReviewFindings:
I did not run builds or tests per the review constraints. |
|
Review — recursion: supply DECODE/page commitments via private input; bind via program_id fold Reviewed the diff (VM verifier guest, program_id fold, verify_with_options doc/trust-model changes, Makefile presets, tests). Overall the change is coherent and the soundness reasoning holds up. No Critical/High issues found. Soundness (confirmed OK). The design is sound under its stated trust model. verify_with_options intentionally does not bind the supplied roots to inner_elf, and the PoC test correctly demonstrates that. The program_id fold closes the gap: because the discriminating DECODE root is folded alongside elf_digest(inner_elf), a root substitution that verify accepts (decode_y != decode_x) yields forged_id != honest_id, so an external native recompute rejects it. The only way to make a forgery undetectable would require decode_y == decode_x AND matching folded page roots — i.e. identical decoded instructions and data pages, which is no meaningful substitution. Fold is sufficient. Worth stating explicitly: this PR only makes the guest commit program_id; nothing here enforces the host-side recompute-and-compare. The tests build the expected value from the same honestly-precomputed roots fed to the guest, so they do not exercise the rejection path against a forged id — the committed program_id is only checked for self-consistency. That enforcement is (correctly) documented as out-of-scope; just flagging that the binding itself is unverified by the added tests. Low.
Nits (non-blocking).
|
…it elf identity Replaces the build-time-embedded-target scheme (reverted in the prior three commits) with a uniform verifier: decode_commitment/page_commitments for the inner program are supplied via private input instead of recomputed in-VM (~45x fewer cycles), and ProofOptions is fixed per build (`min`/`blowup8` Cargo features) so a malicious private input can't downgrade security level. On success the guest commits elf_digest(inner_elf) || decode_commitment || page_commitments, attesting exactly what it verified without needing to recompute or re-embed the inner ELF at every recursion level.
…ram_id The verifier guest supplies DECODE/page preprocessed roots via private input and previously committed a bare elf_digest, which a custom prover could decouple from the constrained program (a proof of Y committed as X — see the new recursion_soundness_gap_poc). Commit program_id(inner_elf, decode, pages) || inner_public_output instead: folding the roots into the committed identity makes a supplied-root substitution diverge from an honest native recompute, which the top-level host checks. verify_with_options itself does not bind the supplied roots to the ELF; that binding is external and its docstring (and VmAirs::new's) is corrected to say so instead of overstating root validation. Also: commit the inner proof's public_output so the attestation covers the result, not just identity; serialize the recursion-elf presets (.NOTPARALLEL) to avoid the parallel-build clobber; build recursion ELFs in test-fast/ test-prover and un-ignore the artifact-gated recursion tests.
77ab236 to
acf996e
Compare
test-prover reads prebuilt recursion ELFs at test-execution time (recursion_smoke_test's non-ignored tests) but never built or cached them, unlike test-prover-comprehensive which already does. CI failed with "failed to read .../recursion/empty.elf" on every shard.
Summary
The recursion verifier guest verifies an inner lambda-vm proof in-VM. It receives the inner program's
(VmProof, elf, decode_commitment, page_commitments)via private input and passes the precomputed DECODE/ELF-data-page roots toverify_with_options— skipping the in-VM FFT+Merkle rebuild.On success it commits
program_id(inner_elf, decode, pages) || inner_public_output, whereprogram_idis a domain-tagged Keccak fold ofelf_digest || pc_start || decode_root || sorted(page_base || page_commitment).Soundness
verify_with_optionsdoes not bind the supplied roots toinner_elf: the verifier's preprocessed-root check compares the proof's root against the supplied root, and when the supplied root is prover-controlled (as it is here) both sides are attacker-chosen. A custom prover can therefore constrain program Y while absorbingelf_digest(X)into the statement, and verify returnsOk(true). This is demonstrated byrecursion_soundness_gap_poc.The
program_idfold closes this: because the supplied roots are folded into the committed identity, a substituted root yields aprogram_idthat differs from the one an honest native recompute for the intended program produces. The binding is established outside the guest — the top-level host recomputesprogram_idfrom the trusted ELF (an expensive FFT+Merkle pass, done once, off-VM) and rejects on mismatch. The guest itself does not self-enforce the id; it only commits it. Committinginner_public_outputalongside makes the attestation cover the result, not just identity (bound via the COMMIT bus-balance check, not merely the transcript).ProofOptionsis not private input — it is fixed per build via one of two mutually-exclusive Cargo features (min/blowup8), producingrecursion-min.elf/recursion-blowup8.elf. Otherwise a malicious input could pick trivially weak options. Onlyblowup8is sound;min(blowup 2 / 1 query) is an intentionally-insecure diagnostic underbench_vs/.Scope
This is a single-step verifier guest and is sound under the trust model above (host recomputes
program_idand checks the output). It is not multi-level recursion: the guest commits its immediate inner program's identity, not a base-program claim threaded up a chain. Sound multi-level recursion (IVC) additionally requires a carried claim + a verifier-identity anchor; that is a separate, future change.Measurements
recursion-min(empty inner, 1 query): 116.9M cycles — unchanged vs the precomputed-roots baseline; the fold adds only a few Keccaks.recursion-blowup8(empty inner, 128-bit): ~3.0B cycles.program_id+ 8-byte output); empty committed 32; in-VM verify accepted in both.Test plan
recursion_soundness_gap_poc— a custom prover commitself_digest(X)while constraining Y; assertsverify_with_optionsaccepts (verify is not the binding) and that theprogram_idfold makes the substitution detectable (forged_id != honest_id). Includes a not-vacuous guard and an honest-recompute (None,None) negative control.recursion_smoke_test— blob round-trip + host verify; corrupted-commitment rejection; execute/prove pipelines assert the guest commitsprogram_id || outputbyte-for-byte.cargo clippy/cargo fmt --checkclean on the prover crate.Build
.NOTPARALLEL(both build the same crate to the same target dir; the post-lockcpcould race undermake -j).test-fast/test-provernow build the recursion ELFs, and the artifact-gated recursion tests are no longer#[ignore]d (needing a build artifact is not a reason to skip a test).