Skip to content

Reuse sparse permutation commitments for one circuit - #479

Merged
ValarDragon merged 1 commit into
mainfrom
agent/single-permutation-prepared-difference-final-20260922
Sep 22, 2026
Merged

ValarDragon merged 1 commit into
mainfrom
agent/single-permutation-prepared-difference-final-20260922

Conversation

@ValarDragon

@ValarDragon ValarDragon commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Summary

The one-circuit parallel permutation path already recognizes its two sparse
product sets, but discarded their set indices before commitment. That forced
both products through generic 2,048-base Lagrange MSMs.

Every multi-circuit proof already takes prepare_batch, including both two-
and four-Action Orchard proofs. That path retains
UnpreparedSet::Scheduled { set_index } and uses the key-specific difference
bases. This PR only closes the single-circuit routing omission: it carries the
private set index through product construction and calls the same existing
prepared-difference helper. It adds no cache.

The retained cache is independent of the circuit witness and circuit index.
Key generation derives it only from the proving key's fixed identity-cell
mapping, active-row schedule, Lagrange suffix sums, and blinding generator.
Each proof still derives the commitment scalars from its own product
polynomial.

Benchmarks

Rust 1.97.1, balanced alternating control/candidate pairs, first proof after
building and preparing a fresh proving key. Every pair produced identical
proof bytes and hashes. The benchmark base was 3729c32; the subsequently
merged #477 changes the disjoint multi-opening phase, and the rebased commit's
permutation byte-equivalence test passes on current main.

  • Apple M4 (Mac16,10), 10 Rayon workers, 160 one-Action pairs:
    • permutation phase: 2.120030 ms -> 1.528187 ms, -0.591843 ms;
      paired bootstrap 95% CI [-0.621418, -0.562849] ms
    • complete proof: 43.546495 ms -> 42.866591 ms, -0.679904 ms;
      95% CI [-0.802080, -0.560407] ms
  • x86-64 Linux, 6 Rayon workers, explicit x86_64-asm, 160 one-Action
    pairs:
    • complete proof: 92.737180 ms -> 91.537239 ms, -1.199941 ms;
      95% CI [-1.721505, -0.687688] ms

The multi-Action path already used the prepared difference commitments and is
unchanged. Its screens detected no regression: the M4 four-Action
permutation-phase CI was [-0.190372, +0.036528] ms across 64 pairs, and the
Linux complete-proof CI was [-1.629897, +0.620813] ms across 48 pairs.

Key generation, proving-key preparation, retained memory, RNG order,
transcript order, proof bytes, and public APIs are unchanged.

Tests

  • cargo fmt --all -- --check
  • ./scripts/changelog.py check
  • cargo +1.97.1 check --locked -p zakura-halo2-proofs --no-default-features
  • cargo +1.97.1 test --locked --release -p zakura-halo2-proofs --lib plonk::permutation::prover -- --nocapture
  • RAYON_NUM_THREADS=1 cargo +1.97.1 test --locked --release -p zakura-orchard --features prover-fingerprint --lib circuit::prover_fingerprint::prover_capture -- --exact --nocapture
  • RAYON_NUM_THREADS=1 cargo +1.97.1 test --locked --release -p zakura-orchard --features prover-fingerprint --lib circuit::prover_fingerprint::prover_capture_two_actions -- --exact --nocapture

API surface: no pub, pub(crate), visibility, signature, trait, enum-variant,
alias, constant, constructor, or function-signature changes. The only new
field is private to the permutation prover module.

@ValarDragon
ValarDragon force-pushed the agent/single-permutation-prepared-difference-final-20260922 branch from ac6886a to ce3879b Compare September 22, 2026 19:21
@v12-auditor

v12-auditor Bot commented Sep 22, 2026

Copy link
Copy Markdown

Note

Complete: Audit complete. V12 found one issue worth reviewing.

Open the full results here.

FindingSeverityDetails
F-286301 🔵 Low
Zero common factor breaks sparse difference commitment

The single-circuit parallel permutation path now routes every sparse product set through commit_prepared_difference, whose precomputed bases only encode changes at suffix_rows = {0} ∪ {active.row+1} ∪ {fraction_rows+1..domain_size} derived from keygen's ActivePermutationSet. However, the single-circuit sparse product deliberately reproduces dense zero-denominator semantics: when an identity cell's common factor (beta*delta*omega^row + gamma + value) evaluates to zero, sparse_prefix_products zeroes the product from first_cancelled_zero + 1 through fraction_rows, and first_cancelled_zero can be an all-identity row that is not an active row (exactly the scenario unit-tested by sparse_product_preserves_common_zeros_at_boundaries, where all-identity rows carry the zero factor). The resulting change at index first_cancelled_zero + 1 is not in suffix_rows, so the difference-commitment scalars omit it and the commitment is computed to a different piecewise-constant polynomial than the actual product z used for the polynomial, coset, evaluations, and IPA argument. The invariant is only checked by a debug_assert! inside commit_prepared_difference (prover.rs:1421-1423), so release builds silently produce the wrong commitment. The multi-circuit batch path is immune because prepare_scheduled_fractions hard-codes first_cancelled_zero: None (prover.rs:833), making this a gap specific to the newly routed single-circuit path; before this PR that path committed the same product generically and correctly.

Analyzed one file, diff e7058ab...ac6886a.

@ValarDragon
ValarDragon force-pushed the agent/single-permutation-prepared-difference-final-20260922 branch from ce3879b to 6e72954 Compare September 22, 2026 19:30
@ValarDragon
ValarDragon marked this pull request as draft September 22, 2026 19:50
@ValarDragon
ValarDragon force-pushed the agent/single-permutation-prepared-difference-final-20260922 branch from 6e72954 to 4cda44e Compare September 22, 2026 19:56
@ValarDragon

Copy link
Copy Markdown
Contributor Author

Confirmed. A cancelled common zero on an inactive row introduces a product transition at row + 1 that is absent from the keygen-prepared suffix support, so the release-only route could commit to a different polynomial. The PR now falls back to the generic Lagrange commitment for any sparse set with first_cancelled_zero.is_some(). The prepared path remains unchanged for ordinary challenges. Added cancelled_common_zero_disables_prepared_difference_commitment; all six permutation-prover tests pass in release mode. Keeping the PR in draft until the refreshed CI matrix is green.

@ValarDragon
ValarDragon marked this pull request as ready for review September 22, 2026 20:49
@ValarDragon
ValarDragon merged commit 70c6c8e into main Sep 22, 2026
55 checks passed
@ValarDragon
ValarDragon deleted the agent/single-permutation-prepared-difference-final-20260922 branch September 22, 2026 22:56
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