Skip to content

chore: Update p3 - #74

Open
samuelburnham wants to merge 6 commits into
mainfrom
update-p3
Open

chore: Update p3#74
samuelburnham wants to merge 6 commits into
mainfrom
update-p3

Conversation

@samuelburnham

Copy link
Copy Markdown
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).
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