chore: Update p3 - #74
Open
samuelburnham wants to merge 6 commits into
Open
Conversation
samuelburnham
commented
Aug 22, 2026
Member
- Addresses performance and soundness fixes from upstream Plonky3
- Bumps the Rust version to v1.98
Four fixes surfaced by auditing upstream Plonky3 changes since our pin: - Bind the message width into the lookup fingerprint as its leading coefficient: m = β + Σ args[i]·γ^i + W·γ^W. Messages of different widths are now distinct as polynomials in γ, so a shorter tuple can never systematically alias a longer one whose high arguments are zero (upstream classes this as a soundness issue, Plonky3#1912). Per-row zero padding within a slot stays transparent; claims are fingerprinted the same way. Transcript-incompatible with prior proofs. - Enforce the logUp multiplicity height bound Σ wᵢ·hᵢ + |claims| < p (Plonky3#1748/#1771): Lookup gains a declared per-row multiplicity bound (default 1; count-column pulls declare a budget), the prover fails fast and the verifier rejects with MultiplicityOverflow. This makes field-level accumulator balance imply integer multiset balance. - Reject an out-of-domain point ζ that lands inside a trace domain with a typed OodPointInDomain error instead of panicking in the Lagrange selector inversion (Plonky3#1760/#1791). - Correct the lookup soundness bound in the verifier docs: the message is degree W in γ, not affine, and a vanishing denominator frees the accumulator on its row; the lookup term is N·(W+2)/|F_ext|, not N/|F_ext|.
- rust-toolchain.toml channel and Cargo.toml rust-version: 1.91 → 1.98.
- flake.nix: new fenix toolchain hash; flake.lock: update fenix (and its
nixpkgs/rust-analyzer inputs) so the 1.98 release manifest is available.
- Drop clippy::from_iter_instead_of_collect from [lints.clippy]: the lint
was removed in clippy 1.98 ("has proved problematic") and now warns as
unknown.
- blake3 test: chunks_exact(4) → as_chunks::<4>(), per the new
clippy::chunks_exact_to_as_chunks lint; drops the per-chunk try_into.
CI needs no changes: ci.yml derives RUST_VERSION from rust-toolchain.toml.
Replace the clippy-only `-D warnings` argument with a workflow-level CARGO_BUILD_WARNINGS=deny (cargo's build.warnings config, stable since 1.97). Every cargo invocation in CI — check, clippy, doctests, nextest — now fails on warnings from workspace-local packages, not just the clippy leg, while local builds keep warning without failing. Unlike -D warnings it only applies to local packages, so dependency warnings can't break CI, and it doesn't perturb RUSTFLAGS-keyed build caches.
396 commits since the previous pin (e9d7561, 2026-03-27). Migration surface was small: - p3_adapter: AirBuilder gained a required PeriodicVar associated type and is_transition is now required (is_transition_window gained a default); periodic columns stay unsupported. - types: delete the DeterministicPow challenger wrapper — upstream SerializingChallenger64::grind now short-circuits bits == 0 to F::ZERO (Plonky3#1602), which is exactly what the wrapper existed to provide. - num-bigint 0.4 → 0.5, matching upstream's bump (Plonky3#1930). - config/system docs: exceeding the quotient-degree cap no longer produces invalid proofs upstream (get_evaluations_on_domain's coefficient-recovery path returned wrongly-ordered rows before Plonky3#1982); the cap now exists to stay on the cheap LDE-truncation path, so describe it that way. Notable inherited changes: pruned Merkle multiproofs across the FRI query phase (Plonky3#1918/#1919) shrink the serialize-test proof from 77,637 to 22,017 bytes and cut verifier hashing; canonical Goldilocks serde (Plonky3#1765) removes proof-byte malleability; assorted verifier hardening and Radix2DitParallel/field perf. Verified: cargo test (33/33), clippy, fmt, cargo-deny, nix flake check.
4334b88 seeded every message fingerprint with its slot width so that messages of different widths can never alias. That is sound for any circuit family, but it breaks a circuit pattern the fingerprint's zero-padding transparency used to license: branch-shared lookup slots, where mutually exclusive branches superpose messages of different natural widths into one accumulator slot at the maximum width, relying on the padded send matching the natural-width provider. Aiur's function circuits are built on this pattern, and no repair is free — padding providers and claims to a global width inflates every message Horner and the lookup witness, and splitting slots by width adds stage-2 accumulator columns. For such families the seed is also redundant: when every message's natural width is a function of a constant-constrained leading prefix (channel tag plus per-channel discriminator), messages are prefix-free — a zero-extension collision can only equate a padded message with its own natural form, which is exactly the intended semantics. So make the policy explicit: `WidthBinding::Fingerprint` (the default, seeded as before) or `WidthBinding::ByConstruction` (plain Horner fold; the prefix-freeness contract is the circuit author's, with the same epistemic standing as `Lookup::max_multiplicity`). The choice lives on `StarkGenericConfig` (defaulted; `GoldilocksBlake3Config` stores it via `with_width_binding`) and is threaded to the four evaluation sites: witness stage-2 messages, the direct constraint evaluation on the prover's quotient domain and at the verifier's ζ, and the claim fingerprints on both sides. It changes every fingerprint, so `observe_shape` binds it into the Fiat-Shamir transcript alongside the other shape data. The executable spec (`synthesize_lookups`) takes the policy too and the spec pin now runs the group-size matrix under both modes; the fingerprint unit test covers the ByConstruction transparency identity alongside the width-separation identity.
Plonky3 v0.6.0 ships FRI query openings as pruned Merkle multiproofs, verified by an amortized sort/merge walk. The in-circuit recursive verifier consumes one full authentication path per query instead — its per-query control flow is a far smaller circuit than the amortized walk's bookkeeping — so the two need different transports for the same proof. `advice::proof_to_advice_bytes` converts a natively-verified Proof into that per-query advice encoding (bincode fixint-LE, the same layout the pre-0.6 per-query proofs used). The advice bytes are untrusted verifier input, never digest-bound: the transcript binds the commitments, and every expanded sibling is authenticated against them per query, so pruning vs expansion is pure transport — a valid encoding of one form exists iff one of the other does. The expansion reuses p3's own machinery rather than reimplementing the frontier walk: `verify_fri` runs against a MerkleTreeMmcs whose compression function records every (inputs -> output) call, and a challenger wrapper records `sample_bits` to recover the query indices. Each query's path is then read back top-down from its cap entry through the recorded map — Blake3 collisions aside, the map is functional, so the read-back is unambiguous; matrix-injection levels are recognized from the same dimension-derived schedule the per-path verifier uses and consume no wire sibling. Commit-phase sibling *values* come straight off the multiproof; only paths are read back. verify_multiple_claims is split at the pcs.verify seam: the transcript replay and round reconstruction now live in pcs_verification_context, which the advice module drives with instrumented components against the same rounds and challenger state — no second copy of the Fiat-Shamir replay exists. Tested against p3's retained per-path verify_batch as the oracle: every expanded input-round path for every query must verify, at cap heights 0 and 1, over a system with matrix injection and a preprocessed round; commit-phase paths are length- and sibling-checked here and covered end-to-end by the downstream in-circuit verifier suite. Expansion refuses invalid proofs (native verification runs first).
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.