zorch.pcs.ipa.math¶
Check-polynomial kernels behind the IPA fold — no EC point, no MSM.
The pieces a verifier (and an accumulation consumer) needs that touch only the
scalar field, factored out of verifier.py so they run and test on the scalar
field independent of the curve's lax.msm path (the same split KZG draws between
_quotient_and_eval and its MSMs). The generic monomial-basis vector
b = (1, x, …, x^{n-1}) lives in zorch.poly.univariate.powers; the two kernels
here are IPA-specific:
challenge_vector— the size-nvectorswithG_final = ⟨s, G⟩(the one expensive MSM the verifier/decider owes). The dense coefficients of the check polynomialhbelow, built by the exact inverse of the prover's basis fold, so⟨s, G⟩reproduces the prover's collapsed basis by construction rather than by a re-derived closed form.eval_challenge_poly—h(x) = ∏_j (1 + u_j · x^{2^{k-1-j}}), the O(log n) evaluation of the check polynomial whose coefficients ares. This is the folded scalarbwithout materializings, and the reason an accumulation step stays succinct:his pinned by thek = log nchallenges alone.
Both use the no-inverse form (1 + u_j·X^…, not u_j⁻¹ + u_j·X^…). That formula
IS the contract; it matches arkworks' check polynomial (the poly-commit crate's
ipa_pc succinct check, compute_coeffs / evaluate), pinned against that
oracle (see docs/blocks/pcs.md) so the decider's final-key MSM
byte-matches it — treat the arkworks symbol names as a pointer that may move, the
formula as the spec.
challenge_vector and eval_challenge_poly are two readings of the same object
— eval_challenge_poly(u, x) == ⟨challenge_vector(u), powers(x, n)⟩ — and a test
pins that identity so the succinct path and the explicit path cannot drift.
challenge_vector ¶
challenge_vector(u: Array) -> Array
The size-n combiner s with G_final = ⟨s, G⟩ and b_final = ⟨s, b⟩,
where u[j] is round j's challenge (n = 2^k, k = len(u)). These are the
dense coefficients of the check polynomial h.
Derived as the exact inverse of the prover's basis fold
G^{(j+1)}_t = G^{(j)}_t + u_j·G^{(j)}_{t+m}: a coefficient c on a folded
entry splits into c on the low half and c·u_j on the high half, so
unrolling from the collapsed scalar [1] back out gives s ← concat(s, u_j·s)
per round (rounds replayed last-to-first). Both the basis (G) and the
evaluation vector (b) fold with this same low/high pattern, so the one s
serves both ⟨s, G⟩ and ⟨s, b⟩.
Source code in zorch/pcs/ipa/math.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
eval_challenge_poly ¶
eval_challenge_poly(u: Array, x: Array) -> Array
h(x) = ∏_{j=0}^{k-1} (1 + u_j · x^{2^{k-1-j}}) in O(k) — the folded scalar
b_final without materializing the size-n s (the succinct read of
challenge_vector). x^{2^m} comes from repeated squaring, so no field pow
by a large exponent is needed.
Source code in zorch/pcs/ipa/math.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | |