diff --git a/backend/plonk/bls12-377/prove.go b/backend/plonk/bls12-377/prove.go index 924b04f21e..6c74757478 100644 --- a/backend/plonk/bls12-377/prove.go +++ b/backend/plonk/bls12-377/prove.go @@ -52,11 +52,6 @@ import ( "github.com/consensys/gnark/logger" ) -// TODO in gnark-crypto: -// * remove everything linked to the blinding -// * add SetCoeff method -// * modify GetCoeff -> if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -101,12 +96,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -151,14 +146,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -192,9 +184,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -217,7 +206,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -248,7 +236,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -267,8 +254,8 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi } else { s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -490,8 +477,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -639,44 +626,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -756,13 +705,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -771,27 +713,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -1279,16 +1219,24 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { @@ -1296,11 +1244,11 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1311,27 +1259,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1343,43 +1299,39 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical diff --git a/backend/plonk/bls12-377/verify.go b/backend/plonk/bls12-377/verify.go index 6234beb7a3..1d5ae92d42 100644 --- a/backend/plonk/bls12-377/verify.go +++ b/backend/plonk/bls12-377/verify.go @@ -39,11 +39,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "bls12-377").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -79,7 +80,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -96,37 +97,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -101,12 +96,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -151,14 +146,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -192,9 +184,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -217,7 +206,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -248,7 +236,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -267,8 +254,8 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi } else { s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -490,8 +477,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -639,44 +626,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -756,13 +705,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -771,27 +713,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -1279,16 +1219,24 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { @@ -1296,11 +1244,11 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1311,27 +1259,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1343,43 +1299,39 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical diff --git a/backend/plonk/bls12-381/verify.go b/backend/plonk/bls12-381/verify.go index ea78e72096..d16ed249fa 100644 --- a/backend/plonk/bls12-381/verify.go +++ b/backend/plonk/bls12-381/verify.go @@ -39,11 +39,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "bls12-381").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -79,7 +80,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -96,37 +97,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -101,12 +96,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -151,14 +146,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -192,9 +184,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -217,7 +206,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -248,7 +236,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -267,8 +254,8 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi } else { s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -490,8 +477,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -639,44 +626,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -756,13 +705,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -771,27 +713,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -1279,16 +1219,24 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { @@ -1296,11 +1244,11 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1311,27 +1259,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1343,43 +1299,39 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical diff --git a/backend/plonk/bls24-315/verify.go b/backend/plonk/bls24-315/verify.go index 64076b69b5..2b4089f3b6 100644 --- a/backend/plonk/bls24-315/verify.go +++ b/backend/plonk/bls24-315/verify.go @@ -39,11 +39,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "bls24-315").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -79,7 +80,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -96,37 +97,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -101,12 +96,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -151,14 +146,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -192,9 +184,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -217,7 +206,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -248,7 +236,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -267,8 +254,8 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi } else { s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -490,8 +477,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -639,44 +626,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -756,13 +705,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -771,27 +713,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -1279,16 +1219,24 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { @@ -1296,11 +1244,11 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1311,27 +1259,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1343,43 +1299,39 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical diff --git a/backend/plonk/bls24-317/verify.go b/backend/plonk/bls24-317/verify.go index ceda45d811..9e4589b53f 100644 --- a/backend/plonk/bls24-317/verify.go +++ b/backend/plonk/bls24-317/verify.go @@ -39,11 +39,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "bls24-317").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -79,7 +80,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -96,37 +97,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -101,12 +96,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -151,14 +146,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -192,9 +184,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -217,7 +206,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -248,7 +236,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -267,8 +254,8 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi } else { s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -490,8 +477,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -639,44 +626,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -756,13 +705,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -771,27 +713,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -1279,16 +1219,24 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { @@ -1296,11 +1244,11 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1311,27 +1259,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1343,43 +1299,39 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical diff --git a/backend/plonk/bn254/solidity.go b/backend/plonk/bn254/solidity.go index 02bd82412e..300fef7340 100644 --- a/backend/plonk/bn254/solidity.go +++ b/backend/plonk/bn254/solidity.go @@ -61,54 +61,53 @@ contract PlonkVerifier { {{ end }} {{ range $index, $element := .CommitmentConstraintIndexes -}} - uint256 private constant VK_INDEX_COMMIT_API{{ $index }} = {{ $element }}; + uint256 private constant VK_INDEX_COMMIT_API_{{ $index }} = {{ $element }}; {{ end -}} uint256 private constant VK_NB_CUSTOM_GATES = {{ len .CommitmentConstraintIndexes }}; // ------------------------------------------------ // offset proof - uint256 private constant PROOF_L_COM_X = 0x00; - uint256 private constant PROOF_L_COM_Y = 0x20; - uint256 private constant PROOF_R_COM_X = 0x40; - uint256 private constant PROOF_R_COM_Y = 0x60; - uint256 private constant PROOF_O_COM_X = 0x80; - uint256 private constant PROOF_O_COM_Y = 0xa0; + {{ $offset := 0 }} + uint256 private constant PROOF_L_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_L_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_R_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_R_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_O_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_O_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} // h = h_0 + x^{n+2}h_1 + x^{2(n+2)}h_2 - uint256 private constant PROOF_H_0_X = 0xc0; - uint256 private constant PROOF_H_0_Y = 0xe0; - uint256 private constant PROOF_H_1_X = 0x100; - uint256 private constant PROOF_H_1_Y = 0x120; - uint256 private constant PROOF_H_2_X = 0x140; - uint256 private constant PROOF_H_2_Y = 0x160; + uint256 private constant PROOF_H_0_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_0_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_1_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_1_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_2_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_2_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} // wire values at zeta - uint256 private constant PROOF_L_AT_ZETA = 0x180; - uint256 private constant PROOF_R_AT_ZETA = 0x1a0; - uint256 private constant PROOF_O_AT_ZETA = 0x1c0; + uint256 private constant PROOF_L_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_R_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_O_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant PROOF_S1_AT_ZETA = 0x1e0; // Sσ1(zeta) - uint256 private constant PROOF_S2_AT_ZETA = 0x200; // Sσ2(zeta) + // S1(zeta),S2(zeta) + uint256 private constant PROOF_S1_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} // Sσ1(zeta) + uint256 private constant PROOF_S2_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} // Sσ2(zeta) - //Bn254.G1Point grand_product_commitment; // [z(x)] - uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_X = 0x220; - uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_Y = 0x240; + // [Z] + uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} - uint256 private constant PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA = 0x260; // z(w*zeta) - uint256 private constant PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA = 0x280; // t(zeta) - uint256 private constant PROOF_LINEARISED_POLYNOMIAL_AT_ZETA = 0x2a0; // r(zeta) + uint256 private constant PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA = {{ hex $offset }};{{ $offset = add $offset 0x20}} // z(w*zeta) - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = 0x2c0; // [Wzeta] - uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = 0x2e0; + // Folded proof for the opening of linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} - uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = 0x300; - uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = 0x320; + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} - uint256 private constant PROOF_OPENING_QCP_AT_ZETA = 0x340; - uint256 private constant PROOF_COMMITMENTS_WIRES_CUSTOM_GATES = {{ hex (add 832 (mul (len .CommitmentConstraintIndexes) 32 ) )}}; + uint256 private constant PROOF_OPENING_QCP_AT_ZETA = {{ hex $offset }}; + uint256 private constant PROOF_BSB_COMMITMENTS = {{ hex (add $offset (mul (len .CommitmentConstraintIndexes) 32 ) )}}; // -> next part of proof is // [ openings_selector_commits || commitments_wires_commit_api] @@ -116,39 +115,33 @@ contract PlonkVerifier { // -------- offset state // challenges to check the claimed quotient - uint256 private constant STATE_ALPHA = 0x00; - uint256 private constant STATE_BETA = 0x20; - uint256 private constant STATE_GAMMA = 0x40; - uint256 private constant STATE_ZETA = 0x60; - - // reusable value - uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = 0x80; - - // commitment to H - uint256 private constant STATE_FOLDED_H_X = 0xa0; - uint256 private constant STATE_FOLDED_H_Y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = 0xe0; - uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant STATE_FOLDED_CLAIMED_VALUES = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant STATE_FOLDED_DIGESTS_X = 0x140; - uint256 private constant STATE_FOLDED_DIGESTS_Y = 0x160; - - uint256 private constant STATE_PI = 0x180; - - uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = 0x1a0; - - uint256 private constant STATE_GAMMA_KZG = 0x1c0; - - uint256 private constant STATE_SUCCESS = 0x1e0; - uint256 private constant STATE_CHECK_VAR = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant STATE_LAST_MEM = 0x220; + {{ $offset = 0 }} + uint256 private constant STATE_ALPHA = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_BETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_GAMMA = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_FOLDED_H_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_FOLDED_H_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_FOLDED_CLAIMED_VALUES = {{ hex $offset }};{{ $offset = add $offset 0x20}} // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_DIGESTS_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant STATE_FOLDED_DIGESTS_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_PI = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_GAMMA_KZG = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_SUCCESS = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant STATE_CHECK_VAR = {{ hex $offset }};{{ $offset = add $offset 0x20}} // /!\ this slot is used for debugging only + uint256 private constant STATE_LAST_MEM = {{ hex $offset }};{{ $offset = add $offset 0x20}} + + // -------- utils (for Fiat Shamir) + uint256 private constant FS_ALPHA = 0x616C706861; // "alpha" + uint256 private constant FS_BETA = 0x62657461; // "beta" + uint256 private constant FS_GAMMA = 0x67616d6d61; // "gamma" + uint256 private constant FS_ZETA = 0x7a657461; // "zeta" + uint256 private constant FS_GAMMA_KZG = 0x67616d6d61; // "gamma" // -------- errors uint256 private constant ERROR_STRING_ID = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) @@ -157,12 +150,17 @@ contract PlonkVerifier { // -------- utils (for hash_fr) uint256 private constant HASH_FR_BB = 340282366920938463463374607431768211456; // 2**128 uint256 private constant HASH_FR_ZERO_UINT256 = 0; - uint8 private constant HASH_FR_LEN_IN_BYTES = 48; uint8 private constant HASH_FR_SIZE_DOMAIN = 11; uint8 private constant HASH_FR_ONE = 1; uint8 private constant HASH_FR_TWO = 2; {{ end }} + + // -------- precompiles + uint8 private constant MOD_EXP = 0x5; + uint8 private constant EC_ADD = 0x6; + uint8 private constant EC_MUL = 0x7; + uint8 private constant EC_PAIR = 0x8; /// Verify a Plonk proof. /// Reverts if the proof or the public inputs are malformed. @@ -198,13 +196,13 @@ contract PlonkVerifier { // public inputs contribution let l_pi := sum_pi_wo_api_commit(public_inputs.offset, public_inputs.length, freeMem) {{ if (gt (len .CommitmentConstraintIndexes) 0 ) -}} - let l_wocommit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) - l_pi := addmod(l_wocommit, l_pi, R_MOD) + let l_pi_commit := sum_pi_commit(proof.offset, public_inputs.length, freeMem) + l_pi := addmod(l_pi_commit, l_pi, R_MOD) {{ end -}} mstore(add(mem, STATE_PI), l_pi) compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(proof.offset) + verify_opening_linearised_polynomial(proof.offset) fold_h(proof.offset) compute_commitment_linearised_polynomial(proof.offset) compute_gamma_kzg(proof.offset) @@ -268,6 +266,15 @@ contract PlonkVerifier { revert(ptError, 0x64) } + function error_pairing() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0xd) + mstore(add(ptError, 0x44), "error pairing") + revert(ptError, 0x64) + } + function error_verify() { let ptError := mload(0x40) mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) @@ -312,7 +319,7 @@ contract PlonkVerifier { /// Checks if the proof is of the correct size /// @param actual_proof_size size of the proof (not the expected size) function check_proof_size(actual_proof_size) { - let expected_proof_size := add(0x340, mul(VK_NB_CUSTOM_GATES,0x60)) + let expected_proof_size := add(0x300, mul(VK_NB_CUSTOM_GATES,0x60)) if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } @@ -322,22 +329,9 @@ contract PlonkVerifier { /// @param aproof pointer to the beginning of the proof /// @dev the 'a' prepending proof is to have a local name function check_proof_openings_size(aproof) { - - - // linearised polynomial at zeta - let p := add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA) - if gt(calldataload(p), R_MOD_MINUS_ONE) { - error_proof_openings_size() - } - - // quotient polynomial at zeta - p := add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA) - if gt(calldataload(p), R_MOD_MINUS_ONE) { - error_proof_openings_size() - } // PROOF_L_AT_ZETA - p := add(aproof, PROOF_L_AT_ZETA) + let p := add(aproof, PROOF_L_AT_ZETA) if gt(calldataload(p), R_MOD_MINUS_ONE) { error_proof_openings_size() } @@ -412,7 +406,7 @@ contract PlonkVerifier { // gamma // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] // (same for alpha, beta, zeta) - mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(mPtr, FS_GAMMA) // "gamma" mstore(add(mPtr, 0x20), VK_S1_COM_X) mstore(add(mPtr, 0x40), VK_S1_COM_Y) @@ -472,7 +466,7 @@ contract PlonkVerifier { let mPtr := add(mload(0x40), STATE_LAST_MEM) // beta - mstore(mPtr, 0x62657461) // "beta" + mstore(mPtr, FS_BETA) // "beta" mstore(add(mPtr, 0x20), gamma_not_reduced) let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" if iszero(l_success) { @@ -496,13 +490,13 @@ contract PlonkVerifier { let full_size := 0x65 // size("alpha") + 0x20 (previous challenge) // alpha - mstore(mPtr, 0x616C706861) // "alpha" + mstore(mPtr, FS_ALPHA) // "alpha" let _mPtr := add(mPtr, 0x20) mstore(_mPtr, beta_not_reduced) _mPtr := add(_mPtr, 0x20) {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} // Bsb22Commitments - let proof_bsb_commitments := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) + let proof_bsb_commitments := add(aproof, PROOF_BSB_COMMITMENTS) let size_bsb_commitments := mul(0x40, VK_NB_CUSTOM_GATES) calldatacopy(_mPtr, proof_bsb_commitments, size_bsb_commitments) _mPtr := add(_mPtr, size_bsb_commitments) @@ -530,7 +524,7 @@ contract PlonkVerifier { let mPtr := add(mload(0x40), STATE_LAST_MEM) // zeta - mstore(mPtr, 0x7a657461) // "zeta" + mstore(mPtr, FS_ZETA) // "zeta" mstore(add(mPtr, 0x20), alpha_not_reduced) calldatacopy(add(mPtr, 0x40), add(aproof, PROOF_H_0_X), 0xc0) let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) @@ -639,13 +633,13 @@ contract PlonkVerifier { let z := mload(add(state, STATE_ZETA)) let zpnmo := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)) - let p := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) + let p := add(aproof, PROOF_BSB_COMMITMENTS) let h_fr, ith_lagrange {{ range $index, $element := .CommitmentConstraintIndexes}} h_fr := hash_fr(calldataload(p), calldataload(add(p, 0x20)), mPtr) - ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, VK_INDEX_COMMIT_API{{ $index }}), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, VK_INDEX_COMMIT_API_{{ $index }}), mPtr) pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, R_MOD), R_MOD) p := add(p, 0x40) {{ end }} @@ -892,14 +886,15 @@ contract PlonkVerifier { // TODO test the staticcall using the method from audit_4-5 let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20) + if iszero(l_success) { + error_pairing() + } let res_pairing := mload(0x00) - let s_success := mload(add(state, STATE_SUCCESS)) - res_pairing := and(and(res_pairing, l_success), s_success) mstore(add(state, STATE_SUCCESS), res_pairing) } /// @notice Fold the opening proofs at ζ: - /// * at state+state_folded_digest we store: [H] + γ[Linearised_polynomial]+γ²[L] + γ³[R] + γ⁴[O] + γ⁵[S₁] +γ⁶[S₂] + ∑ᵢγ⁶⁺ⁱ[Pi_{i}] + /// * at state+state_folded_digest we store: [Linearised_polynomial]+γ[L] + γ²[R] + γ³[O] + γ⁴[S₁] +γ⁵[S₂] + ∑ᵢγ⁵⁺ⁱ[Pi_{i}] /// * at state+state_folded_claimed_values we store: H(ζ) + γLinearised_polynomial(ζ)+γ²L(ζ) + γ³R(ζ)+ γ⁴O(ζ) + γ⁵S₁(ζ) +γ⁶S₂(ζ) + ∑ᵢγ⁶⁺ⁱPi_{i}(ζ) /// @param aproof pointer to the proof /// acc_gamma stores the γⁱ @@ -914,14 +909,10 @@ contract PlonkVerifier { let acc_gamma := l_gamma_kzg let state_folded_digests := add(state, STATE_FOLDED_DIGESTS_X) - mstore(add(state, STATE_FOLDED_DIGESTS_X), mload(add(state, STATE_FOLDED_H_X))) - mstore(add(state, STATE_FOLDED_DIGESTS_Y), mload(add(state, STATE_FOLDED_H_Y))) - mstore(add(state, STATE_FOLDED_CLAIMED_VALUES), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA))) - - point_acc_mul(state_folded_digests, add(state, STATE_LINEARISED_POLYNOMIAL_X), acc_gamma, mPtr) - fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA), acc_gamma) + mstore(add(state, STATE_FOLDED_DIGESTS_X), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X))) + mstore(add(state, STATE_FOLDED_DIGESTS_Y), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y))) + mstore(add(state, STATE_FOLDED_CLAIMED_VALUES), mload(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA))) - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) point_acc_mul_calldata(add(state, STATE_FOLDED_DIGESTS_X), add(aproof, PROOF_L_COM_X), acc_gamma, mPtr) fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_L_AT_ZETA), acc_gamma) @@ -946,18 +937,16 @@ contract PlonkVerifier { fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_S2_AT_ZETA), acc_gamma) {{- if (gt (len .CommitmentConstraintIndexes) 0 ) }} - let poscaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) - {{ end -}} - + let poqaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) {{ range $index, $element := .CommitmentConstraintIndexes }} acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) mstore(mPtr, VK_QCP_{{ $index }}_X) mstore(mPtr20, VK_QCP_{{ $index }}_Y) point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40) - fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) + fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), poqaz, acc_gamma) + poqaz := add(poqaz, 0x20) {{ end }} - + {{ end -}} } /// @notice generate the challenge (using Fiat Shamir) to fold the opening proofs @@ -965,13 +954,11 @@ contract PlonkVerifier { /// The process for deriving γ is the same as in derive_gamma but this time the inputs are /// in this order (the [] means it's a commitment): /// * ζ - /// * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) /// * [Linearised polynomial] /// * [L], [R], [O] /// * [S₁] [S₂] /// * [Pi_{i}] (wires associated to custom gates) /// Then there are the purported evaluations of the previous committed polynomials: - /// * H(ζ) /// * Linearised_polynomial(ζ) /// * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) /// * Pi_{i}(ζ) @@ -981,48 +968,43 @@ contract PlonkVerifier { let state := mload(0x40) let mPtr := add(mload(0x40), STATE_LAST_MEM) - mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(mPtr, FS_GAMMA_KZG) // "gamma" mstore(add(mPtr, 0x20), mload(add(state, STATE_ZETA))) - mstore(add(mPtr,0x40), mload(add(state, STATE_FOLDED_H_X))) - mstore(add(mPtr,0x60), mload(add(state, STATE_FOLDED_H_Y))) - mstore(add(mPtr,0x80), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X))) - mstore(add(mPtr,0xa0), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y))) - calldatacopy(add(mPtr, 0xc0), add(aproof, PROOF_L_COM_X), 0xc0) - mstore(add(mPtr,0x180), VK_S1_COM_X) - mstore(add(mPtr,0x1a0), VK_S1_COM_Y) - mstore(add(mPtr,0x1c0), VK_S2_COM_X) - mstore(add(mPtr,0x1e0), VK_S2_COM_Y) + mstore(add(mPtr,0x40), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X))) + mstore(add(mPtr,0x60), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y))) + calldatacopy(add(mPtr, 0x80), add(aproof, PROOF_L_COM_X), 0xc0) + mstore(add(mPtr,0x140), VK_S1_COM_X) + mstore(add(mPtr,0x160), VK_S1_COM_Y) + mstore(add(mPtr,0x180), VK_S2_COM_X) + mstore(add(mPtr,0x1a0), VK_S2_COM_Y) - let offset := 0x200 - {{ range $index, $element := .CommitmentConstraintIndexes }} + let offset := 0x1c0 + + {{ range $index, $element := .CommitmentConstraintIndexes -}} mstore(add(mPtr,offset), VK_QCP_{{ $index }}_X) mstore(add(mPtr,add(offset, 0x20)), VK_QCP_{{ $index }}_Y) offset := add(offset, 0x40) - {{ end }} + {{ end -}} + + mstore(add(mPtr, offset), mload(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA))) + mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_L_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_R_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_O_AT_ZETA))) + mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_S1_AT_ZETA))) + mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S2_AT_ZETA))) - mstore(add(mPtr, offset), calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_L_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_R_AT_ZETA))) - mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_O_AT_ZETA))) - mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S1_AT_ZETA))) - mstore(add(mPtr, add(offset, 0xc0)), calldataload(add(aproof, PROOF_S2_AT_ZETA))) + let _mPtr := add(mPtr, add(offset, 0xc0)) - let _mPtr := add(mPtr, add(offset, 0xe0)) {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - let _poscaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) - for {let i:=0} lt(i, VK_NB_CUSTOM_GATES) {i:=add(i,1)} - { - mstore(_mPtr, calldataload(_poscaz)) - _poscaz := add(_poscaz, 0x20) - _mPtr := add(_mPtr, 0x20) - } + let _poqaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + calldatacopy(_mPtr, _poqaz, mul(VK_NB_CUSTOM_GATES, 0x20)) + _mPtr := add(_mPtr, mul(VK_NB_CUSTOM_GATES, 0x20)) {{ end }} mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA))) let start_input := 0x1b // 00.."gamma" - let size_input := add(0x17, mul(VK_NB_CUSTOM_GATES,3)) // number of 32bytes elmts = 0x17 (zeta+2*7+7 for the digests+openings) + 2*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + VK_NB_CUSTOM_GATES (for the openings of the selectors) + let size_input := add(0x14, mul(VK_NB_CUSTOM_GATES,3)) // number of 32bytes elmts = 0x17 (zeta+3*6 for the digests+openings) + 3*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + 1 (opening of Z at ζω) size_input := add(0x5, mul(size_input, 0x20)) // size in bytes: 15*32 bytes + 5 bytes for gamma let check_staticcall := staticcall(gas(), 0x2, add(mPtr,start_input), size_input, add(state, STATE_GAMMA_KZG), 0x20) if iszero(check_staticcall) { @@ -1032,6 +1014,7 @@ contract PlonkVerifier { } function compute_commitment_linearised_polynomial_ec(aproof, s1, s2) { + let state := mload(0x40) let mPtr := add(mload(0x40), STATE_LAST_MEM) @@ -1076,24 +1059,26 @@ contract PlonkVerifier { add(mPtr, 0x40) ) - let commits_api_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA) - let commits_api := add(aproof, PROOF_COMMITMENTS_WIRES_CUSTOM_GATES) + {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} + let qcp_opening_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA) + let bsb_commitments := add(aproof, PROOF_BSB_COMMITMENTS) for { let i := 0 } lt(i, VK_NB_CUSTOM_GATES) { i := add(i, 1) } { - mstore(mPtr, calldataload(commits_api)) - mstore(add(mPtr, 0x20), calldataload(add(commits_api, 0x20))) + mstore(mPtr, calldataload(bsb_commitments)) + mstore(add(mPtr, 0x20), calldataload(add(bsb_commitments, 0x20))) point_acc_mul( add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, - calldataload(commits_api_at_zeta), + calldataload(qcp_opening_at_zeta), add(mPtr, 0x40) ) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 0x40) + qcp_opening_at_zeta := add(qcp_opening_at_zeta, 0x20) + bsb_commitments := add(bsb_commitments, 0x40) } + {{ end }} mstore(mPtr, VK_S3_COM_X) mstore(add(mPtr, 0x20), VK_S3_COM_Y) @@ -1102,15 +1087,22 @@ contract PlonkVerifier { mstore(mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X))) mstore(add(mPtr, 0x20), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_Y))) point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, s2, add(mPtr, 0x40)) + + point_add( + add(state, STATE_LINEARISED_POLYNOMIAL_X), + add(state, STATE_LINEARISED_POLYNOMIAL_X), + add(state, STATE_FOLDED_H_X), + mPtr) } /// @notice Compute the commitment to the linearized polynomial equal to /// L(ζ)[Qₗ]+r(ζ)[Qᵣ]+R(ζ)L(ζ)[Qₘ]+O(ζ)[Qₒ]+[Qₖ]+Σᵢqc'ᵢ(ζ)[BsbCommitmentᵢ] + - /// α*( Z(μζ)(L(ζ)+β*S₁(ζ)+γ)*(R(ζ)+β*S₂(ζ)+γ)[S₃]-[Z](L(ζ)+β*id_{1}(ζ)+γ)*(R(ζ)+β*id_{2(ζ)+γ)*(O(ζ)+β*id_{3}(ζ)+γ) ) + - /// α²*L₁(ζ)[Z] + /// α*( Z(μζ)(L(ζ)+β*S₁(ζ)+γ)*(R(ζ)+β*S₂(ζ)+γ)[S₃]-[Z](L(ζ)+β*id_{1}(ζ)+γ)*(R(ζ)+β*id_{2}(ζ)+γ)*(O(ζ)+β*id_{3}(ζ)+γ) ) + + /// α²*L₁(ζ)[Z] - Z_{H}(ζ)*(([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾*[H₂]) /// where /// * id_1 = id, id_2 = vk_coset_shift*id, id_3 = vk_coset_shift^{2}*id /// * the [] means that it's a commitment (i.e. a point on Bn254(F_p)) + /// * Z_{H}(ζ) = ζ^n-1 /// @param aproof pointer to the proof function compute_commitment_linearised_polynomial(aproof) { let state := mload(0x40) @@ -1158,7 +1150,7 @@ contract PlonkVerifier { compute_commitment_linearised_polynomial_ec(aproof, s1, s2) } - /// @notice compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + /// @notice compute -z_h(ζ)*([H₁] + ζᵐ⁺²[H₂] + ζ²⁽ᵐ⁺²⁾[H₃]) and store the result at /// state + state_folded_h /// @param aproof pointer to the proof function fold_h(aproof) { @@ -1170,49 +1162,49 @@ contract PlonkVerifier { point_add_calldata(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_1_X), mPtr) point_mul(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), zeta_power_n_plus_two, mPtr) point_add_calldata(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_0_X), mPtr) + point_mul(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)), mPtr) + let folded_h_y := mload(add(state, STATE_FOLDED_H_Y)) + folded_h_y := sub(P_MOD, folded_h_y) + mstore(add(state, STATE_FOLDED_H_Y), folded_h_y) } - /// @notice check that - /// L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - /// α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - /// + α²*L₁(ζ) = - /// (ζⁿ-1)H(ζ) + /// @notice check that the opening of the linearised polynomial at zeta is equal to + /// - [ PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ) ] /// @param aproof pointer to the proof - function verify_quotient_poly_eval_at_zeta(aproof) { + function verify_opening_linearised_polynomial(aproof) { + let state := mload(0x40) // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), STATE_LAST_MEM) - mstore(s1, mulmod(calldataload(add(aproof, PROOF_S1_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) - mstore(s1, addmod(mload(s1), mload(add(state, STATE_GAMMA)), R_MOD)) - mstore(s1, addmod(mload(s1), calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)) + let s1 + s1 := mulmod(calldataload(add(aproof, PROOF_S1_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD) + s1 := addmod(s1, mload(add(state, STATE_GAMMA)), R_MOD) + s1 := addmod(s1, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD) // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1, 0x20) - mstore(s2, mulmod(calldataload(add(aproof, PROOF_S2_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)) - mstore(s2, addmod(mload(s2), mload(add(state, STATE_GAMMA)), R_MOD)) - mstore(s2, addmod(mload(s2), calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)) - // _s2 := mload(s2) + let s2 + s2 := mulmod(calldataload(add(aproof, PROOF_S2_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD) + s2 := addmod(s2, mload(add(state, STATE_GAMMA)), R_MOD) + s2 := addmod(s2, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD) // (o(ζ)+γ) - let o := add(s1, 0x40) - mstore(o, addmod(calldataload(add(aproof, PROOF_O_AT_ZETA)), mload(add(state, STATE_GAMMA)), R_MOD)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), R_MOD)) - mstore(s1, mulmod(mload(s1), mload(o), R_MOD)) - mstore(s1, mulmod(mload(s1), mload(add(state, STATE_ALPHA)), R_MOD)) - mstore(s1, mulmod(mload(s1), calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD)) - - let computed_quotient := add(s1, 0x60) - - // linearizedpolynomial + pi(zeta) - mstore(computed_quotient,addmod(calldataload(add(aproof, PROOF_LINEARISED_POLYNOMIAL_AT_ZETA)), mload(add(state, STATE_PI)), R_MOD)) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), R_MOD)) - mstore(computed_quotient,addmod(mload(computed_quotient), sub(R_MOD, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0))), R_MOD)) - mstore(s2,mulmod(calldataload(add(aproof, PROOF_QUOTIENT_POLYNOMIAL_AT_ZETA)),mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)),R_MOD)) + let o + o := addmod(calldataload(add(aproof, PROOF_O_AT_ZETA)), mload(add(state, STATE_GAMMA)), R_MOD) + + // α*Z(μζ)*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + s1 := mulmod(s1, s2, R_MOD) + s1 := mulmod(s1, o, R_MOD) + s1 := mulmod(s1, mload(add(state, STATE_ALPHA)), R_MOD) + s1 := mulmod(s1, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD) + + // PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ) + s1 := addmod(s1, mload(add(state, STATE_PI)), R_MOD) + s2 := mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0)) + s2 := sub(R_MOD, s2) + s1 := addmod(s1, s2, R_MOD) + s1 := sub(R_MOD, s1) - mstore(add(state, STATE_SUCCESS), eq(mload(computed_quotient), mload(s2))) + mstore(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA), s1) } // BEGINNING utils math functions ------------------------------------------------- @@ -1222,12 +1214,11 @@ contract PlonkVerifier { /// @param q pointer to the second point /// @param mPtr pointer to free memory function point_add(dst, p, q, mPtr) { - let state := mload(0x40) mstore(mPtr, mload(p)) mstore(add(mPtr, 0x20), mload(add(p, 0x20))) mstore(add(mPtr, 0x40), mload(q)) mstore(add(mPtr, 0x60), mload(add(q, 0x20))) - let l_success := staticcall(gas(),6,mPtr,0x80,dst,0x40) + let l_success := staticcall(gas(),EC_ADD,mPtr,0x80,dst,0x40) if iszero(l_success) { error_ec_op() } @@ -1238,12 +1229,11 @@ contract PlonkVerifier { /// @param q pointer to the second point (calladata) /// @param mPtr pointer to free memory function point_add_calldata(dst, p, q, mPtr) { - let state := mload(0x40) mstore(mPtr, mload(p)) mstore(add(mPtr, 0x20), mload(add(p, 0x20))) mstore(add(mPtr, 0x40), calldataload(q)) mstore(add(mPtr, 0x60), calldataload(add(q, 0x20))) - let l_success := staticcall(gas(), 6, mPtr, 0x80, dst, 0x40) + let l_success := staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40) if iszero(l_success) { error_ec_op() } @@ -1254,11 +1244,10 @@ contract PlonkVerifier { /// @param s scalar /// @param mPtr free memory function point_mul(dst,src,s, mPtr) { - let state := mload(0x40) mstore(mPtr,mload(src)) mstore(add(mPtr,0x20),mload(add(src,0x20))) mstore(add(mPtr,0x40),s) - let l_success := staticcall(gas(),7,mPtr,0x60,dst,0x40) + let l_success := staticcall(gas(),EC_MUL,mPtr,0x60,dst,0x40) if iszero(l_success) { error_ec_op() } @@ -1269,11 +1258,10 @@ contract PlonkVerifier { /// @param s scalar /// @param mPtr free memory function point_mul_calldata(dst, src, s, mPtr) { - let state := mload(0x40) mstore(mPtr, calldataload(src)) mstore(add(mPtr, 0x20), calldataload(add(src, 0x20))) mstore(add(mPtr, 0x40), s) - let l_success := staticcall(gas(), 7, mPtr, 0x60, dst, 0x40) + let l_success := staticcall(gas(), EC_MUL, mPtr, 0x60, dst, 0x40) if iszero(l_success) { error_ec_op() } @@ -1285,14 +1273,13 @@ contract PlonkVerifier { /// @param s scalar /// @param mPtr free memory function point_acc_mul(dst,src,s, mPtr) { - let state := mload(0x40) mstore(mPtr,mload(src)) mstore(add(mPtr,0x20),mload(add(src,0x20))) mstore(add(mPtr,0x40),s) let l_success := staticcall(gas(),7,mPtr,0x60,mPtr,0x40) mstore(add(mPtr,0x40),mload(dst)) mstore(add(mPtr,0x60),mload(add(dst,0x20))) - l_success := and(l_success, staticcall(gas(),6,mPtr,0x80,dst, 0x40)) + l_success := and(l_success, staticcall(gas(),EC_ADD,mPtr,0x80,dst, 0x40)) if iszero(l_success) { error_ec_op() } @@ -1311,7 +1298,7 @@ contract PlonkVerifier { let l_success := staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40) mstore(add(mPtr, 0x40), mload(dst)) mstore(add(mPtr, 0x60), mload(add(dst, 0x20))) - l_success := and(l_success, staticcall(gas(), 6, mPtr, 0x80, dst, 0x40)) + l_success := and(l_success, staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40)) if iszero(l_success) { error_ec_op() } @@ -1337,9 +1324,9 @@ contract PlonkVerifier { mstore(add(mPtr, 0x60), x) mstore(add(mPtr, 0x80), e) mstore(add(mPtr, 0xa0), R_MOD) - let check_staticcall := staticcall(gas(),0x05,mPtr,0xc0,mPtr,0x20) + let check_staticcall := staticcall(gas(),MOD_EXP,mPtr,0xc0,mPtr,0x20) if eq(check_staticcall, 0) { - error_verify() + } res := mload(mPtr) } @@ -1348,7 +1335,7 @@ contract PlonkVerifier { } ` -// MarshalSolidity converts a proof to a byte array that can be used in a +// MarshalSolidity convert s a proof to a byte array that can be used in a // Solidity contract. func (proof *Proof) MarshalSolidity() []byte { @@ -1383,7 +1370,7 @@ func (proof *Proof) MarshalSolidity() []byte { // uint256 o_at_zeta; // uint256 s1_at_zeta; // uint256 s2_at_zeta; - for i := 2; i < 7; i++ { + for i := 1; i < 6; i++ { tmp32 = proof.BatchedProof.ClaimedValues[i].Bytes() res = append(res, tmp32[:]...) } @@ -1397,12 +1384,8 @@ func (proof *Proof) MarshalSolidity() []byte { tmp32 = proof.ZShiftedOpening.ClaimedValue.Bytes() res = append(res, tmp32[:]...) - // uint256 quotient_polynomial_at_zeta; - // uint256 linearization_polynomial_at_zeta; - tmp32 = proof.BatchedProof.ClaimedValues[0].Bytes() - res = append(res, tmp32[:]...) - tmp32 = proof.BatchedProof.ClaimedValues[1].Bytes() - res = append(res, tmp32[:]...) + // we skip the claimed value of the linearised polynomial at zeta because it + // is recomputed by the verifier and plugged in the batch opening proof directly // uint256 opening_at_zeta_proof_x; // uint256 opening_at_zeta_proof_y; @@ -1418,7 +1401,7 @@ func (proof *Proof) MarshalSolidity() []byte { // uint256[] wire_committed_commitments; if len(proof.Bsb22Commitments) > 0 { for i := 0; i < len(proof.Bsb22Commitments); i++ { - tmp32 = proof.BatchedProof.ClaimedValues[7+i].Bytes() + tmp32 = proof.BatchedProof.ClaimedValues[6+i].Bytes() res = append(res, tmp32[:]...) } diff --git a/backend/plonk/bn254/unmarshal.go b/backend/plonk/bn254/unmarshal.go index bdc578cac2..cc3e7b8add 100644 --- a/backend/plonk/bn254/unmarshal.go +++ b/backend/plonk/bn254/unmarshal.go @@ -41,7 +41,7 @@ func UnmarshalSolidity(s []byte, nbCommits int) Proof { // uint256 o_at_zeta; // uint256 s1_at_zeta; // uint256 s2_at_zeta; - for i := 2; i < 7; i++ { + for i := 1; i < 6; i++ { proof.BatchedProof.ClaimedValues[i].SetBytes(s[offset : offset+fr_size]) offset += fr_size } @@ -59,8 +59,6 @@ func UnmarshalSolidity(s []byte, nbCommits int) Proof { // uint256 linearization_polynomial_at_zeta; proof.BatchedProof.ClaimedValues[0].SetBytes(s[offset : offset+fr_size]) offset += fr_size - proof.BatchedProof.ClaimedValues[1].SetBytes(s[offset : offset+fr_size]) - offset += fr_size // uint256 opening_at_zeta_proof_x; // uint256 opening_at_zeta_proof_y; diff --git a/backend/plonk/bn254/verify.go b/backend/plonk/bn254/verify.go index b119a71c9b..6f70830650 100644 --- a/backend/plonk/bn254/verify.go +++ b/backend/plonk/bn254/verify.go @@ -39,11 +39,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "bn254").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -79,7 +80,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -96,37 +97,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -101,12 +96,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -151,14 +146,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -192,9 +184,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -217,7 +206,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -248,7 +236,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -267,8 +254,8 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi } else { s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -490,8 +477,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -639,44 +626,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -756,13 +705,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -771,27 +713,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -1279,16 +1219,24 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { @@ -1296,11 +1244,11 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1311,27 +1259,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1343,43 +1299,39 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical diff --git a/backend/plonk/bw6-633/verify.go b/backend/plonk/bw6-633/verify.go index 5004b6955e..d7d09efba1 100644 --- a/backend/plonk/bw6-633/verify.go +++ b/backend/plonk/bw6-633/verify.go @@ -39,11 +39,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "bw6-633").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -79,7 +80,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -96,37 +97,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -101,12 +96,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -151,14 +146,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -192,9 +184,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -217,7 +206,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -248,7 +236,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -267,8 +254,8 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi } else { s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -490,8 +477,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -639,44 +626,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -756,13 +705,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -771,27 +713,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -1279,16 +1219,24 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { @@ -1296,11 +1244,11 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1311,27 +1259,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1343,43 +1299,39 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical diff --git a/backend/plonk/bw6-761/verify.go b/backend/plonk/bw6-761/verify.go index 17edc2fdef..7709c1d0ab 100644 --- a/backend/plonk/bw6-761/verify.go +++ b/backend/plonk/bw6-761/verify.go @@ -39,11 +39,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "bw6-761").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -79,7 +80,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -96,37 +97,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i if the poly is shifted and in canonical form the index is computed differently - const ( id_L int = iota id_R @@ -78,12 +73,12 @@ type Proof struct { // Commitment to Z, the permutation polynomial Z kzg.Digest - // Commitments to h1, h2, h3 such that h = h1 + Xh2 + X**2h3 is the quotient polynomial + // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial H [3]kzg.Digest Bsb22Commitments []kzg.Digest - // Batch opening proof of h1 + zeta*h2 + zeta**2h3, linearizedPolynomial, l, r, o, s1, s2, qCPrime + // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime BatchedProof kzg.BatchOpeningProof // Opening proof of Z at zeta*mu @@ -128,14 +123,11 @@ func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts g.Go(instance.buildRatioCopyConstraint) // compute h - g.Go(instance.evaluateConstraints) + g.Go(instance.computeQuotient) // open Z (blinded) at ωζ (proof.ZShiftedOpening) g.Go(instance.openZ) - // fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) - g.Go(instance.foldH) - // linearized polynomial g.Go(instance.computeLinearizedPolynomial) @@ -169,9 +161,6 @@ type instance struct { h *iop.Polynomial // h is the quotient polynomial blindedZ []fr.Element // blindedZ is the blinded version of Z - foldedH []fr.Element // foldedH is the folded version of H - foldedHDigest kzg.Digest // foldedHDigest is the kzg commitment of foldedH - linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -194,7 +183,6 @@ type instance struct { chRestoreLRO, chZOpening, chLinearizedPolynomial, - chFoldedH, chGammaBeta chan struct{} domain0, domain1 *fft.Domain @@ -225,7 +213,6 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi chH: make(chan struct{}, 1), chZOpening: make(chan struct{}, 1), chLinearizedPolynomial: make(chan struct{}, 1), - chFoldedH: make(chan struct{}, 1), chRestoreLRO: make(chan struct{}, 1), } s.initBSB22Commitments() @@ -234,18 +221,18 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi // init fft domains nbConstraints := spr.GetNbConstraints() sizeSystem := uint64(nbConstraints + len(spr.Public)) // len(spr.Public) is for the placeholder constraints - s.domain0 = fft.NewDomain(sizeSystem) + s.domain0 = fft.NewDomain(sizeSystem) // h, the quotient polynomial is of degree 3(n+1)+2, so it's in a 3(n+2) dim vector space, // the domain is the next power of 2 superior to 3(n+2). 4*domainNum is enough in all cases // except when n<6. if sizeSystem < 6 { - s.domain1 = fft.NewDomain(8 * sizeSystem, fft.WithoutPrecompute()) + s.domain1 = fft.NewDomain(8*sizeSystem, fft.WithoutPrecompute()) } else { - s.domain1 = fft.NewDomain(4 * sizeSystem, fft.WithoutPrecompute()) + s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) } - // TODO @gbotrel domain1 is used for only 1 FFT --> precomputing the twiddles - // and storing them in memory is costly given its size. --> do a FFT on the fly + // TODO @gbotrel domain1 is used for only 1 FFT → precomputing the twiddles + // and storing them in memory is costly given its size. → do a FFT on the fly // build trace s.trace = NewTrace(spr, s.domain0) @@ -270,7 +257,7 @@ func (s *instance) initBSB22Commitments() { // override the hint for the commitment constraints bsb22ID := solver.GetHintID(fcs.Bsb22CommitmentComputePlaceholder) - s.opt.SolverOpts = append(s.opt.SolverOpts, solver.OverrideHint(bsb22ID, s.bsb22Hint)) + s.opt.SolverOpts = append(s.opt.SolverOpts, solver.OverrideHint(bsb22ID, s.bsb22Hint)) } // Computing and verifying Bsb22 multi-commits explained in https://hackmd.io/x8KsadW3RRyX7YTCFJIkHg @@ -278,7 +265,7 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { var err error commDepth := int(ins[0].Int64()) ins = ins[1:] - + res := &s.commitmentVal[commDepth] commitmentInfo := s.spr.CommitmentInfo.(constraint.PlonkCommitments)[commDepth] @@ -297,13 +284,13 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { if s.proof.Bsb22Commitments[commDepth], err = kzg.Commit(s.cCommitments[commDepth].Coefficients(), s.pk.KzgLagrange); err != nil { return err } - + s.htfFunc.Write(s.proof.Bsb22Commitments[commDepth].Marshal()) hashBts := s.htfFunc.Sum(nil) s.htfFunc.Reset() nbBuf := fr.Bytes if s.htfFunc.Size() < fr.Bytes { - nbBuf = s.htfFunc.Size() + nbBuf = s.htfFunc.Size() } res.SetBytes(hashBts[:nbBuf]) // TODO @Tabaie use CommitmentIndex for this; create a new variable CommitmentConstraintIndex for other uses res.BigInt(outs[0]) @@ -467,8 +454,8 @@ func (s *instance) deriveZeta() (err error) { return } -// evaluateConstraints computes H -func (s *instance) evaluateConstraints() (err error) { +// computeQuotient computes H +func (s *instance) computeQuotient() (err error) { s.x[id_Ql] = s.trace.Ql s.x[id_Qr] = s.trace.Qr s.x[id_Qm] = s.trace.Qm @@ -616,44 +603,6 @@ func (s *instance) h3() []fr.Element { return h3 } -// fold the commitment to H ([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾[H₂]) -func (s *instance) foldH() error { - // wait for H to be committed and zeta to be derived (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chH: - } - var n big.Int - n.SetUint64(s.domain0.Cardinality + 2) - - var zetaPowerNplusTwo fr.Element - zetaPowerNplusTwo.Exp(s.zeta, &n) - zetaPowerNplusTwo.BigInt(&n) - - s.foldedHDigest.ScalarMultiplication(&s.proof.H[2], &n) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[1]) // ζᵐ⁺²*Comm(h3) - s.foldedHDigest.ScalarMultiplication(&s.foldedHDigest, &n) // ζ²⁽ᵐ⁺²⁾*Comm(h3) + ζᵐ⁺²*Comm(h2) - s.foldedHDigest.Add(&s.foldedHDigest, &s.proof.H[0]) - - // fold H (H₀ + ζᵐ⁺²*H₁ + ζ²⁽ᵐ⁺²⁾H₂)) - h1 := s.h1() - h2 := s.h2() - s.foldedH = s.h3() - - for i := 0; i < int(s.domain0.Cardinality)+2; i++ { - s.foldedH[i]. - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h2[i]). - Mul(&s.foldedH[i], &zetaPowerNplusTwo). - Add(&s.foldedH[i], &h1[i]) - } - - close(s.chFoldedH) - - return nil -} - func (s *instance) computeLinearizedPolynomial() error { // wait for H to be committed and zeta to be derived (or ctx.Done()) @@ -733,13 +682,6 @@ func (s *instance) batchOpening() error { case <-s.chLRO: } - // wait for foldedH to be computed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chFoldedH: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -748,27 +690,25 @@ func (s *instance) batchOpening() error { } polysQcp := coefficients(s.trace.Qcp) - polysToOpen := make([][]fr.Element, 7+len(polysQcp)) - copy(polysToOpen[7:], polysQcp) - - polysToOpen[0] = s.foldedH - polysToOpen[1] = s.linearizedPolynomial - polysToOpen[2] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) - polysToOpen[3] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) - polysToOpen[4] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) - polysToOpen[5] = s.trace.S1.Coefficients() - polysToOpen[6] = s.trace.S2.Coefficients() - - digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+7) - copy(digestsToOpen[7:], s.pk.Vk.Qcp) - - digestsToOpen[0] = s.foldedHDigest - digestsToOpen[1] = s.linearizedPolynomialDigest - digestsToOpen[2] = s.proof.LRO[0] - digestsToOpen[3] = s.proof.LRO[1] - digestsToOpen[4] = s.proof.LRO[2] - digestsToOpen[5] = s.pk.Vk.S[0] - digestsToOpen[6] = s.pk.Vk.S[1] + polysToOpen := make([][]fr.Element, 6+len(polysQcp)) + copy(polysToOpen[6:], polysQcp) + + polysToOpen[0] = s.linearizedPolynomial + polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) + polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) + polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) + polysToOpen[4] = s.trace.S1.Coefficients() + polysToOpen[5] = s.trace.S2.Coefficients() + + digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) + copy(digestsToOpen[6:], s.pk.Vk.Qcp) + + digestsToOpen[0] = s.linearizedPolynomialDigest + digestsToOpen[1] = s.proof.LRO[0] + digestsToOpen[2] = s.proof.LRO[1] + digestsToOpen[3] = s.proof.LRO[2] + digestsToOpen[4] = s.pk.Vk.S[0] + digestsToOpen[5] = s.pk.Vk.S[1] var err error s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( @@ -786,7 +726,7 @@ func (s *instance) batchOpening() error { // evaluate the full set of constraints, all polynomials in x are back in // canonical regular form at the end func (s *instance) computeNumerator() (*iop.Polynomial, error) { - // init vectors that are used multiple times throughout the computation + // init vectors that are used multiple times throughout the computation n := s.domain0.Cardinality twiddles0 := make([]fr.Element, n) if n == 1 { @@ -811,8 +751,6 @@ func (s *instance) computeNumerator() (*iop.Polynomial, error) { case <-s.chQk: } - - nbBsbGates := (len(s.x) - id_Qci + 1) >> 1 gateConstraint := func(u ...fr.Element) fr.Element { @@ -834,7 +772,6 @@ func (s *instance) computeNumerator() (*iop.Polynomial, error) { return ic } - var cs, css fr.Element cs.Set(&s.domain1.FrMultiplicativeGen) css.Square(&cs) @@ -1031,7 +968,7 @@ func (s *instance) computeNumerator() (*iop.Polynomial, error) { batchApply(s.x, func(p *iop.Polynomial) { if p == nil { - return + return } p.ToCanonical(s.domain0, 8).ToRegular() scalePowers(p, cs) @@ -1259,28 +1196,36 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // The Linearized polynomial is: // // α²*L₁(ζ)*Z(X) -// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) -// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) +// + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) +// + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) +// - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { + // TODO @gbotrel rename - // first part: individual constraints + + // l(ζ)r(ζ) var rl fr.Element rl.Mul(&rZeta, &lZeta) - // second part: - // Z(μζ)(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*s3(X)-Z(X)(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ) + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + // the linearised polynomial is + // α²*L₁(ζ)*Z(X) + + // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + + // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - + // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) var s1, s2 fr.Element chS1 := make(chan struct{}, 1) go func() { - s1 = s.trace.S1.Evaluate(zeta) // s1(ζ) + s1 = s.trace.S1.Evaluate(zeta) // s1(ζ) s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) close(chS1) }() - // ps2 := iop.NewPolynomial(&pk.S2Canonical, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) - tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) + + tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) <-chS1 - s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta) // (l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α var uzeta, uuzeta fr.Element uzeta.Mul(&zeta, &pk.Vk.CosetShift) @@ -1291,27 +1236,35 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - s2.Neg(&s2) // -(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + s2.Neg(&s2).Mul(&s2, &alpha) - // third part L₁(ζ)*α²*Z - var lagrangeZeta, one, den, frNbElmt fr.Element + // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - lagrangeZeta.Set(&zeta). - Exp(lagrangeZeta, big.NewInt(nbElmt)). - Sub(&lagrangeZeta, &one) + alphaSquareLagrangeOne.Set(&zeta).Exp(alphaSquareLagrangeOne, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeOne, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeOne.Sub(&alphaSquareLagrangeOne, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeOne) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - den.Sub(&zeta, &one). - Inverse(&den) - lagrangeZeta.Mul(&lagrangeZeta, &den). // L₁ = (ζⁿ⁻¹)/(ζ-1) - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &alpha). - Mul(&lagrangeZeta, &s.domain0.CardinalityInv) // (1/n)*α²*L₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeOne.Mul(&alphaSquareLagrangeOne, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &alpha). + Mul(&alphaSquareLagrangeOne, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() + // the hi are all of the same length + h1 := s.h1() + h2 := s.h2() + h3 := s.h3() + + // at this stage we have + // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) + // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) utils.Parallelize(len(blindedZCanonical), func(start, end int) { cql := s.trace.Ql.Coefficients() @@ -1323,46 +1276,42 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, var t, t0, t1 fr.Element for i := start; i < end; i++ { - - t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) - + t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) if i < len(s3canonical) { - - t0.Mul(&s3canonical[i], &s1) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*β*s3(X) - + t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) t.Add(&t, &t0) } - - t.Mul(&t, &alpha) // α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*Z(μζ)*s3(X) - Z(X)*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)) - if i < len(cqm) { - - t1.Mul(&cqm[i], &rl) // linPol = linPol + l(ζ)r(ζ)*Qm(X) - - t0.Mul(&cql[i], &lZeta) - t0.Add(&t0, &t1) - - t.Add(&t, &t0) // linPol = linPol + l(ζ)*Ql(X) - - t0.Mul(&cqr[i], &rZeta) - t.Add(&t, &t0) // linPol = linPol + r(ζ)*Qr(X) - - t0.Mul(&cqo[i], &oZeta) - t0.Add(&t0, &cqk[i]) - - t.Add(&t, &t0) // linPol = linPol + o(ζ)*Qo(X) + Qk(X) - - for j := range qcpZeta { + t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) + t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) + t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) + t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) + t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) + t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) + t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) + t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) + t.Add(&t, &cqk[i]) // linPol += Qk(X) + for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) t.Add(&t, &t0) } } - t0.Mul(&blindedZCanonical[i], &lagrangeZeta) - blindedZCanonical[i].Add(&t, &t0) // finish the computation + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + + if i < len(h1) { + t.Mul(&h3[i], &zetaNPlusTwo). + Add(&t, &h2[i]). + Mul(&t, &zetaNPlusTwo). + Add(&t, &h1[i]) + t.Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } + } }) return blindedZCanonical } -var errContextDone = errors.New("context done") \ No newline at end of file +var errContextDone = errors.New("context done") diff --git a/internal/generator/backend/template/zkpschemes/plonk/plonk.verify.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/plonk.verify.go.tmpl index 6d2c6bccb0..3f509fdfee 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/plonk.verify.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/plonk.verify.go.tmpl @@ -22,11 +22,12 @@ import ( ) var ( - errWrongClaimedQuotient = errors.New("claimed quotient is not as expected") - errInvalidWitness = errors.New("witness length is invalid") + errAlgebraicRelation = errors.New("algebraic relation does not hold") + errInvalidWitness = errors.New("witness length is invalid") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { + log := logger.Logger().With().Str("curve", "{{ toLower .Curve }}").Str("backend", "plonk").Logger() start := time.Now() cfg, err := backend.NewVerifierConfig(opts...) @@ -42,7 +43,6 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } - // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -63,7 +63,7 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // derive alpha from Comm(l), Comm(r), Comm(o), Com(Z), Bsb22Commitments + // derive alpha from Com(Z), Bsb22Commitments alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1) for i := range proof.Bsb22Commitments { alphaDeps[i] = &proof.Bsb22Commitments[i] @@ -80,37 +80,42 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return err } - // evaluation of Z=Xⁿ⁻¹ at ζ - var zetaPowerM, zzeta fr.Element + // evaluation of zhZeta=ζⁿ-1 + var zetaPowerM, zhZeta, lagrangeOne fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - zzeta.Sub(&zetaPowerM, &one) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeOne.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeOne). // 1/(ζ-1) + Mul(&lagrangeOne, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeOne, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i 0 && loopCounter2[i]*3+loopCounter1[i] != 0 { for k := 0; k < n; k++ { - result = pr.MulBy014(result, + prodLines = pr.Mul014By014( + pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), ) + result = pr.MulBy01245(result, prodLines) + } + } else { + // if number of lines is odd, mul last line by res + // works for n=1 as well + if n%2 != 0 { + // ℓ × res + result = pr.MulBy014(result, + pr.curveF.Mul(&lines[n-1][0][i].R1, yInv[n-1]), + pr.curveF.Mul(&lines[n-1][0][i].R0, xNegOverY[n-1]), + ) + } + // mul lines 2-by-2 + for k := 1; k < n; k += 2 { + prodLines = pr.Mul014By014( + pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), + pr.curveF.Mul(&lines[k-1][0][i].R1, yInv[k-1]), + pr.curveF.Mul(&lines[k-1][0][i].R0, xNegOverY[k-1]), + ) + result = pr.MulBy01245(result, prodLines) } } } diff --git a/std/algebra/native/fields_bls12377/e12_pairing.go b/std/algebra/native/fields_bls12377/e12_pairing.go index c6f87c4a0a..a98616c84d 100644 --- a/std/algebra/native/fields_bls12377/e12_pairing.go +++ b/std/algebra/native/fields_bls12377/e12_pairing.go @@ -62,8 +62,7 @@ func (e *E12) MulBy034(api frontend.API, c3, c4 E2) *E12 { // Mul034By034 multiplication of sparse element (1,0,0,c3,c4,0) by sparse element (1,0,0,d3,d4,0) func Mul034By034(api frontend.API, d3, d4, c3, c4 E2) *[5]E2 { - var one, tmp, x00, x3, x4, x04, x03, x34 E2 - one.SetOne() + var tmp, x00, x3, x4, x04, x03, x34 E2 x3.Mul(api, c3, d3) x4.Mul(api, c4, d4) x04.Add(api, c4, d4) diff --git a/std/recursion/plonk/verifier.go b/std/recursion/plonk/verifier.go index 67a597de09..5ae3ee0673 100644 --- a/std/recursion/plonk/verifier.go +++ b/std/recursion/plonk/verifier.go @@ -262,7 +262,7 @@ func PlaceholderProof[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El alg nbCommitments := len(ccs.GetCommitments().CommitmentIndexes()) ret := Proof[FR, G1El, G2El]{ BatchedProof: kzg.BatchOpeningProof[FR, G1El]{ - ClaimedValues: make([]emulated.Element[FR], 7+nbCommitments), + ClaimedValues: make([]emulated.Element[FR], 6+nbCommitments), }, Bsb22Commitments: make([]kzg.Commitment[G1El], nbCommitments), } @@ -828,20 +828,19 @@ func (v *Verifier[FR, G1El, G2El, GtEl]) PrepareVerification(vk VerifyingKey[FR, return nil, nil, nil, err } - // evaluation of Z=Xⁿ-1 at ζ + // evaluation of zhZetaZ=ζⁿ-1 one := v.scalarApi.One() - zetaPowerM := v.fixedExpN(vk.Size, zeta) // ζⁿ - zetaPowerMMinusOne := v.scalarApi.Sub(zetaPowerM, one) // ζⁿ-1 + zetaPowerN := v.fixedExpN(vk.Size, zeta) // ζⁿ + zhZeta := v.scalarApi.Sub(zetaPowerN, one) // ζⁿ-1 // L1 = (1/n)(ζⁿ-1)/(ζ-1) denom := v.scalarApi.Sub(zeta, one) - lagrangeOne := v.scalarApi.Div(zetaPowerMMinusOne, denom) + lagrangeOne := v.scalarApi.Div(zhZeta, denom) lagrangeOne = v.scalarApi.Mul(lagrangeOne, &vk.SizeInv) lagrange := lagrangeOne + // compute PI = ∑_{i= 0; i-- { + omegai := v.scalarApi.Select(iBits[maxExpBits-1], &vk.Generator, one) + for i := maxExpBits - 2; i >= 0; i-- { omegai = v.scalarApi.Mul(omegai, omegai) tmp := v.scalarApi.Mul(omegai, &vk.Generator) omegai = v.scalarApi.Select(iBits[i], tmp, omegai) diff --git a/std/recursion/plonk/verifier_test.go b/std/recursion/plonk/verifier_test.go index 630807e2ad..e37dac2c0c 100644 --- a/std/recursion/plonk/verifier_test.go +++ b/std/recursion/plonk/verifier_test.go @@ -99,12 +99,14 @@ func TestBLS12InBW6WoCommit(t *testing.T) { Proof: PlaceholderProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine](innerCcs), VerifyingKey: circuitVk, } + assert.NoError(err) outerAssignment := &OuterCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ InnerWitness: circuitWitness, Proof: circuitProof, } err = test.IsSolved(outerCircuit, outerAssignment, ecc.BW6_761.ScalarField()) assert.NoError(err) + } func TestBW6InBN254WoCommit(t *testing.T) { @@ -401,7 +403,7 @@ func getRandomParametricProof(assert *test.Assert, field, outer *big.Int, ccss [ return idx, innerPubWitness, innerProof } -type AggregagationCircuit[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT] struct { +type AggregationCircuit[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT] struct { BaseKey BaseVerifyingKey[FR, G1El, G2El] `gnark:"-"` CircuitKeys []CircuitVerifyingKey[FR, G1El] Selectors []frontend.Variable @@ -409,7 +411,7 @@ type AggregagationCircuit[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El Witnesses []Witness[FR] `gnark:",public"` } -func (c *AggregagationCircuit[FR, G1El, G2El, GtEl]) Define(api frontend.API) error { +func (c *AggregationCircuit[FR, G1El, G2El, GtEl]) Define(api frontend.API) error { v, err := NewVerifier[FR, G1El, G2El, GtEl](api) if err != nil { return fmt.Errorf("new verifier: %w", err) @@ -455,7 +457,7 @@ func TestBLS12InBW6Multi(t *testing.T) { circuitWitnesses[i], err = ValueOfWitness[sw_bls12377.ScalarField](innerWitnesses[i]) assert.NoError(err) } - aggCircuit := &AggregagationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ + aggCircuit := &AggregationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ BaseKey: circuitBvk, CircuitKeys: make([]CircuitVerifyingKey[sw_bls12377.ScalarField, sw_bls12377.G1Affine], nbCircuits), Selectors: make([]frontend.Variable, nbProofs), @@ -469,7 +471,7 @@ func TestBLS12InBW6Multi(t *testing.T) { aggCircuit.Proofs[i] = PlaceholderProof[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine](ccss[0]) aggCircuit.Witnesses[i] = PlaceholderWitness[sw_bls12377.ScalarField](ccss[0]) } - aggAssignment := &AggregagationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ + aggAssignment := &AggregationCircuit[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ CircuitKeys: circuitVks, Selectors: circuitSelector, Proofs: circuitProofs, @@ -479,7 +481,7 @@ func TestBLS12InBW6Multi(t *testing.T) { assert.NoError(err) } -type AggregagationCircuitWithHash[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT] struct { +type AggregationCircuitWithHash[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra.GtElementT] struct { BaseKey BaseVerifyingKey[FR, G1El, G2El] `gnark:"-"` CircuitKeys []CircuitVerifyingKey[FR, G1El] Selectors []frontend.Variable @@ -488,7 +490,7 @@ type AggregagationCircuitWithHash[FR emulated.FieldParams, G1El algebra.G1Elemen WitnessHash frontend.Variable } -func (c *AggregagationCircuitWithHash[FR, G1El, G2El, GtEl]) Define(api frontend.API) error { +func (c *AggregationCircuitWithHash[FR, G1El, G2El, GtEl]) Define(api frontend.API) error { v, err := NewVerifier[FR, G1El, G2El, GtEl](api) if err != nil { return fmt.Errorf("new verifier: %w", err) @@ -572,7 +574,7 @@ func TestBLS12InBW6MultiHashed(t *testing.T) { } digest := h.Sum(nil) - aggAssignment := &AggregagationCircuitWithHash[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ + aggAssignment := &AggregationCircuitWithHash[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ CircuitKeys: circuitVks, Selectors: circuitSelector, Proofs: circuitProofs, @@ -580,7 +582,7 @@ func TestBLS12InBW6MultiHashed(t *testing.T) { WitnessHash: digest, } - aggCircuit := &AggregagationCircuitWithHash[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ + aggCircuit := &AggregationCircuitWithHash[sw_bls12377.ScalarField, sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]{ BaseKey: circuitBvk, CircuitKeys: make([]CircuitVerifyingKey[sw_bls12377.ScalarField, sw_bls12377.G1Affine], nbCircuits), Selectors: make([]frontend.Variable, nbProofs),