Conversation
The public-input round squeezed r_pi, then had the prover send the two low memory limbs' evaluations at (r_pi, 0, ..., 0), which the verifier checked against the public-input line before pooling them with a zero top limb as claims on MEM_LO, MEM_HI and MEM_TOP. Each is the line through the public input's own limbs, which the verifier already has, so both sides now compute it: limb i claims interp_k(pi[0].c_i, pi[1].c_i, r_pi), and nothing is transmitted. r_pi is still squeezed at the same transcript position, after the commitment and the table sumcheck, so the committed memory cannot depend on it, and the squeeze still ratchets the state for flock's challenges. The claim values are functions of the seeded statement and r_pi, so they are fixed before the opening's batching challenge. All three verifiers move together: - Rust: bind_pi_claim evaluates the limb lines from the layout's public input on both sides; the line check and its two stream reads go. - Python: step 5 evaluates the limb lines; the dead Y constant goes. - Guest: canonical_limbs hints each public word's low limb and binds both limbs with assert_in_k, replacing two fs_next absorbs (two BLAKE2s compressions per verified child) and the reassembly assert. The proof stream is two scalars shorter. The spec (sec:e2e-pi) and the comments that said every pooled value rode the stream are updated. a_proof_binds_the_memory_to_the_public_input runs the prover on the real input but seeds its transcript with another (prove_claiming, doc-hidden) and requires the opening to reject it, for a low-limb and a high-limb difference. It fails if the public-input claims are dropped from finish_claims, which no honest-prover test notices. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Summary
In the public-input round the prover used to send
c_0, c_1, the evaluations of the two low memory limbs at(r_pi, 0, ..., 0), which the verifier checked against the public-input line. Both are lines through the public input's own limbs, so the verifier now computes them: limbiclaimsinterp_k(pi[0].c_i, pi[1].c_i, r_pi), and nothing is transmitted. The proof stream is two scalars shorter, and the recursive guest drops two BLAKE2s compressions per verified child.Fiat-Shamir
r_piis still squeezed at the same position (after the commitment and the table sumcheck, before flock), so the committed memory cannot depend on it, and the squeeze ratchets the state for every later challenge.r_pi, so they are fixed before the opening's batching challenge. Nothing new needs absorbing.Changes (all three verifiers move together)
crates/leanvm_core/src/cpu/mod.rs):bind_pi_claimcomputes the limb lines from the layout's public input on both sides. The prover's twoadd_scalarcalls, the verifier's twonext_scalarreads and the line check are gone.python-verifier/verifier.py): step 5 evaluates the limb lines; the unusedYconstant is removed.crates/rec_aggregation/guests/lean_ethereum.py): newcanonical_limbshints each public word's low limb and binds both limbs withassert_in_k(unique tower representation, top limb forced zero), replacing twofs_nextabsorbs and the reassembly assert.doc/leanvm/body/08-end-to-end-protocol.tex,sec:e2e-pi) and the comments that said every pooled claim value rode the stream.Tests
vm_proofs::a_proof_binds_the_memory_to_the_public_input: a forging prover (prove_claiming,#[doc(hidden)]) runs on the real input but seeds its transcript with another, and the opening must reject it, for a low-limb and a high-limb difference. Mutation-checked: with the public-input claims removed fromfinish_claimsthis test fails, while every honest-prover test still passes.cargo test --release --workspacepasses, includingaggregate_two_to_one,public_api_end_to_endand the Python verifier test. On a 15 GB machinerec_aggregationneeds--test-threads=1to avoid an OOM kill.cargo clippyall,cargo docall,cargo fmt --checkand ruff are clean.Base branch
leanvm-mainisleanEthereum/leanVMmainat248da071, pushed so this PR shows only this change; the fork'smainhas diverged from leanVM.