Skip to content

Add optional decode preprocessed commitment#640

Merged
MauroToscano merged 7 commits into
mainfrom
decode-preprocessed-commit
Jun 2, 2026
Merged

Add optional decode preprocessed commitment#640
MauroToscano merged 7 commits into
mainfrom
decode-preprocessed-commit

Conversation

@nicole-graus
Copy link
Copy Markdown
Collaborator

Description

verify_with_options and VmAirs::new now accept an optional precomputed DECODE preprocessed commitment. When Some, the supplied value is used directly and the in-verifier FFT + Merkle build for the DECODE preprocessed columns is skipped. When None, behavior is unchanged from main. The trust anchor: value must come from the caller's compiled binary (a const [u8; 32]), never from prover-supplied bytes. Wrong values surface as Fiat-Shamir transcript divergence.

Verifier cycles reduction

Measured on the recursion guest (the verifier compiled as a RISC-V program verifying an empty inner proof, blowup=2, 1 query):

Baseline (Bitwise + keccak_rc): 2,895 M
This PR (+ Decode optional): 2,889 M (~6.8 M reduction)

@nicole-graus nicole-graus marked this pull request as ready for review June 1, 2026 19:18
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Codex Code Review

Findings

  • High - decode_commitment can decouple verification from the ELF. In prover/src/lib.rs, verify_with_options accepts Some(decode_commitment) and then skips recomputing the DECODE commitment from elf_bytes before passing it into VmAirs::new at prover/src/lib.rs. That makes the ELF-to-instruction binding depend entirely on caller discipline. If an integration ever sources this commitment from prover-controlled input, the verifier can accept a proof against a DECODE table not derived from the claimed ELF. The current tests only flip a commitment for a proof generated with the correct commitment; they don’t cover a prover generating the proof against the same malicious supplied commitment. Keep the safe public verifier recomputing from ELF, or move this behind an explicitly trusted/internal API with a hard-to-misuse type/name and narrow call sites.

No other issues found in the reviewed diff.

@claude
Copy link
Copy Markdown

claude Bot commented Jun 1, 2026

Review: Add optional decode preprocessed commitment

Overview

This PR adds an optional decode_commitment: Option<Commitment> parameter to VmAirs::new and verify_with_options, allowing callers (specifically the recursion guest) to supply a precomputed DECODE preprocessed commitment instead of recomputing it via FFT + Merkle build at verification time. Saves ~6.8M cycles in the recursion guest.

Security Model — Correct

Traced the full path through verifier.rs:

// verifier.rs:748-763
let expected_precomputed = air.precomputed_commitment();
match &proof.lde_trace_precomputed_merkle_root {
    Some(actual) if *actual == expected_precomputed => { /* OK */ }
    Some(actual) => return false,   // commitment mismatch
    None => return false,
}

The verifier checks the proof's DECODE commitment against the expected value (either computed from the ELF or supplied by the caller). A prover cannot inject a forged decode_commitment — it's a parameter to the verifier, not a field in the proof bundle. Wrong values (including the all-zero test case) cause Fiat-Shamir rejection. Soundness holds.

Tests — Good Coverage

  • decode_commitment_some_matches_default_pathSome(correct)None
  • decode_commitment_wrong_value_rejects — flipped byte → rejection ✓
  • decode_commitment_zero_bytes_rejects — all-zero sentinel → rejection ✓
  • decode_commitment_compile_time_const_accepts — validates the hardcoded constant ✓
  • print_decode_commitment_for_sub#[ignore] regeneration helper ✓

The decode_commitment_compile_time_const_accepts test will fail automatically if the AIR/FFT pipeline changes and the hardcoded constant drifts, which is the right behavior.

Issues

Low — dead-code fallback in precomputed_commitment()

precomputed_commitment() returns unwrap_or([0u8; 32]) (lookup.rs:1175), but it is only ever called from the verifier when is_preprocessed() is true, which requires preprocessed_commitment to be Some. The fallback is unreachable. Pre-existing code, not introduced here, but worth noting so a future reader doesn't confuse the two layers of None (AIR-level "no preprocessed columns" vs caller-level "compute from ELF").

No other issues found. The change is minimal, all call sites are updated correctly, the API extension is clean, and the trust model is documented accurately.

MauroToscano and others added 2 commits June 2, 2026 15:49
A wrong supplied decode_commitment is caught either by the verifier's
explicit precomputed-commitment equality check or by Fiat-Shamir
transcript divergence — not by FS alone. Reword both doc comments to
reflect the "or", since the earlier wording attributed rejection solely
to Fiat-Shamir.
@MauroToscano MauroToscano added this pull request to the merge queue Jun 2, 2026
Merged via the queue into main with commit 89a26b8 Jun 2, 2026
12 checks passed
@MauroToscano MauroToscano deleted the decode-preprocessed-commit branch June 2, 2026 19:37
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.

4 participants