diff --git a/backend/backend.go b/backend/backend.go index 7ee17d6793..7c427e5825 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -60,6 +60,7 @@ type ProverConfig struct { ChallengeHash hash.Hash KZGFoldingHash hash.Hash Accelerator string + StatisticalZK bool } // NewProverConfig returns a default ProverConfig with given prover options opts @@ -133,6 +134,16 @@ func WithIcicleAcceleration() ProverOption { } } +// WithStatisticalZeroKnowledge ensures that statistical zero knowledgeness is achieved. +// This option makes the prover more memory costly, as there are 3 more size n (size of the circuit) +// allocations. +func WithStatisticalZeroKnowledge() ProverOption { + return func(pc *ProverConfig) error { + pc.StatisticalZK = true + return nil + } +} + // VerifierOption defines option for altering the behavior of the verifier. See // the descriptions of functions returning instances of this type for // implemented options. diff --git a/backend/plonk/bls12-377/prove.go b/backend/plonk/bls12-377/prove.go index 8dcd0479f0..c5380230ea 100644 --- a/backend/plonk/bls12-377/prove.go +++ b/backend/plonk/bls12-377/prove.go @@ -179,10 +179,11 @@ type instance struct { htfFunc hash.Hash // hash to field function // polynomials - x []*iop.Polynomial // x stores tracks the polynomial we need - bp []*iop.Polynomial // blinding polynomials - h *iop.Polynomial // h is the quotient polynomial - blindedZ []fr.Element // blindedZ is the blinded version of Z + x []*iop.Polynomial // x stores tracks the polynomial we need + bp []*iop.Polynomial // blinding polynomials + h *iop.Polynomial // h is the quotient polynomial + blindedZ []fr.Element // blindedZ is the blinded version of Z + quotientShardsRandomizers [2]fr.Element // random elements for blinding the shards of the quotient linearizedPolynomial []fr.Element linearizedPolynomialDigest kzg.Digest @@ -246,6 +247,12 @@ func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWi sizeSystem := uint64(nbConstraints + len(spr.Public)) // len(spr.Public) is for the placeholder constraints s.domain0 = fft.NewDomain(sizeSystem) + // sampling random numbers for blinding the quotient + if opts.StatisticalZK { + s.quotientShardsRandomizers[0].SetRandom() + s.quotientShardsRandomizers[1].SetRandom() + } + // 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. @@ -320,7 +327,7 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { } // solveConstraints computes the evaluation of the polynomials L, R, O -// and sets x[id_L], x[id_R], x[id_O] in canonical form +// and sets x[id_L], x[id_R], x[id_O] in Lagrange form func (s *instance) solveConstraints() error { _solution, err := s.spr.Solve(s.fullWitness, s.opt.SolverOpts...) if err != nil { @@ -529,7 +536,7 @@ func (s *instance) computeQuotient() (err error) { return err } - s.h, err = divideByXMinusOne(numerator, [2]*fft.Domain{s.domain0, s.domain1}) + s.h, err = divideByZH(numerator, [2]*fft.Domain{s.domain0, s.domain1}) if err != nil { return err } @@ -610,17 +617,39 @@ func (s *instance) openZ() (err error) { } func (s *instance) h1() []fr.Element { - h1 := s.h.Coefficients()[:s.domain0.Cardinality+2] + var h1 []fr.Element + if !s.opt.StatisticalZK { + h1 = s.h.Coefficients()[:s.domain0.Cardinality+2] + } else { + h1 = make([]fr.Element, s.domain0.Cardinality+3) + copy(h1, s.h.Coefficients()[:s.domain0.Cardinality+2]) + h1[s.domain0.Cardinality+2].Set(&s.quotientShardsRandomizers[0]) + } return h1 } func (s *instance) h2() []fr.Element { - h2 := s.h.Coefficients()[s.domain0.Cardinality+2 : 2*(s.domain0.Cardinality+2)] + var h2 []fr.Element + if !s.opt.StatisticalZK { + h2 = s.h.Coefficients()[s.domain0.Cardinality+2 : 2*(s.domain0.Cardinality+2)] + } else { + h2 = make([]fr.Element, s.domain0.Cardinality+3) + copy(h2, s.h.Coefficients()[s.domain0.Cardinality+2:2*(s.domain0.Cardinality+2)]) + h2[0].Sub(&h2[0], &s.quotientShardsRandomizers[0]) + h2[s.domain0.Cardinality+2].Set(&s.quotientShardsRandomizers[1]) + } return h2 } func (s *instance) h3() []fr.Element { - h3 := s.h.Coefficients()[2*(s.domain0.Cardinality+2) : 3*(s.domain0.Cardinality+2)] + var h3 []fr.Element + if !s.opt.StatisticalZK { + h3 = s.h.Coefficients()[2*(s.domain0.Cardinality+2) : 3*(s.domain0.Cardinality+2)] + } else { + h3 = make([]fr.Element, s.domain0.Cardinality+2) + copy(h3, s.h.Coefficients()[2*(s.domain0.Cardinality+2):3*(s.domain0.Cardinality+2)]) + h3[0].Sub(&h3[0], &s.quotientShardsRandomizers[1]) + } return h3 } @@ -696,13 +725,6 @@ func (s *instance) computeLinearizedPolynomial() error { func (s *instance) batchOpening() error { - // wait for LRO to be committed (or ctx.Done()) - select { - case <-s.ctx.Done(): - return errContextDone - case <-s.chLRO: - } - // wait for linearizedPolynomial to be computed (or ctx.Done()) select { case <-s.ctx.Done(): @@ -772,7 +794,7 @@ func (s *instance) computeNumerator() (*iop.Polynomial, error) { case <-s.chQk: } - nbBsbGates := (len(s.x) - id_Qci + 1) >> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1077,7 +1099,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1150,10 +1172,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1193,7 +1215,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1220,6 +1242,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1258,25 +1282,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - 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₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1316,20 +1341,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) - blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + 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/setup.go b/backend/plonk/bls12-377/setup.go index 0b9158e9a5..933d0e9038 100644 --- a/backend/plonk/bls12-377/setup.go +++ b/backend/plonk/bls12-377/setup.go @@ -18,7 +18,6 @@ package plonk import ( "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/iop" @@ -96,7 +95,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -154,9 +153,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) diff --git a/backend/plonk/bls12-377/verify.go b/backend/plonk/bls12-377/verify.go index c3df8fc8c0..9dd652cc0e 100644 --- a/backend/plonk/bls12-377/verify.go +++ b/backend/plonk/bls12-377/verify.go @@ -42,6 +42,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -61,6 +62,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -99,16 +126,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - 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) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1077,7 +1099,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1150,10 +1172,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1193,7 +1215,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1220,6 +1242,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1258,25 +1282,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - 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₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1316,20 +1341,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) - blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + 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/setup.go b/backend/plonk/bls12-381/setup.go index e0d7e50c06..16bc19bb53 100644 --- a/backend/plonk/bls12-381/setup.go +++ b/backend/plonk/bls12-381/setup.go @@ -18,7 +18,6 @@ package plonk import ( "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/iop" @@ -96,7 +95,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -154,9 +153,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) diff --git a/backend/plonk/bls12-381/verify.go b/backend/plonk/bls12-381/verify.go index 9fa93ce23a..97006ffa6f 100644 --- a/backend/plonk/bls12-381/verify.go +++ b/backend/plonk/bls12-381/verify.go @@ -42,6 +42,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -61,6 +62,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -99,16 +126,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - 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) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1077,7 +1099,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1150,10 +1172,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1193,7 +1215,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1220,6 +1242,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1258,25 +1282,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - 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₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1316,20 +1341,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) - blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + 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/setup.go b/backend/plonk/bls24-315/setup.go index 8035bbed8a..7a6a4b5ac9 100644 --- a/backend/plonk/bls24-315/setup.go +++ b/backend/plonk/bls24-315/setup.go @@ -18,7 +18,6 @@ package plonk import ( "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/iop" @@ -96,7 +95,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -154,9 +153,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) diff --git a/backend/plonk/bls24-315/verify.go b/backend/plonk/bls24-315/verify.go index 3b1385b347..2a646df122 100644 --- a/backend/plonk/bls24-315/verify.go +++ b/backend/plonk/bls24-315/verify.go @@ -42,6 +42,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -61,6 +62,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -99,16 +126,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - 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) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1077,7 +1099,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1150,10 +1172,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1193,7 +1215,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1220,6 +1242,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1258,25 +1282,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - 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₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1316,20 +1341,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) - blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + 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/setup.go b/backend/plonk/bls24-317/setup.go index 1359bb5097..212657f6a0 100644 --- a/backend/plonk/bls24-317/setup.go +++ b/backend/plonk/bls24-317/setup.go @@ -18,7 +18,6 @@ package plonk import ( "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/iop" @@ -96,7 +95,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -154,9 +153,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) diff --git a/backend/plonk/bls24-317/verify.go b/backend/plonk/bls24-317/verify.go index c625bf83d5..a5d7049753 100644 --- a/backend/plonk/bls24-317/verify.go +++ b/backend/plonk/bls24-317/verify.go @@ -42,6 +42,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -61,6 +62,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -99,16 +126,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - 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) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1077,7 +1099,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1150,10 +1172,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1193,7 +1215,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1220,6 +1242,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1258,25 +1282,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - 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₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1316,20 +1341,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) - blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + 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/setup.go b/backend/plonk/bn254/setup.go index 5d916034f6..0b01ec1cf0 100644 --- a/backend/plonk/bn254/setup.go +++ b/backend/plonk/bn254/setup.go @@ -18,7 +18,6 @@ package plonk import ( "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/fft" "github.com/consensys/gnark-crypto/ecc/bn254/fr/iop" @@ -96,7 +95,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -154,9 +153,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) diff --git a/backend/plonk/bn254/solidity.go b/backend/plonk/bn254/solidity.go index b4c8361400..29966d952d 100644 --- a/backend/plonk/bn254/solidity.go +++ b/backend/plonk/bn254/solidity.go @@ -67,6 +67,9 @@ contract PlonkVerifier { // ------------------------------------------------ + // size of the proof without call custom gate + uint256 private constant FIXED_PROOF_SIZE = 0x300; + // offset proof {{ $offset := 0 }} uint256 private constant PROOF_L_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} @@ -77,14 +80,14 @@ contract PlonkVerifier { 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 = {{ 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_H_0_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_0_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_1_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_1_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_2_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} + uint256 private constant PROOF_H_2_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}} + + // "evaluations of wire polynomials at zeta 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}} @@ -109,9 +112,6 @@ contract PlonkVerifier { uint256 private constant PROOF_OPENING_QCP_AT_ZETA = {{ hex $offset }}; uint256 private constant PROOF_BSB_COMMITMENTS = {{ hex (add $offset (mul (len .Vk.CommitmentConstraintIndexes) 32 ) )}}; - // -> next part of proof is - // [ openings_selector_commits || commitments_wires_commit_api] - // -------- offset state // challenges to check the claimed quotient @@ -127,7 +127,7 @@ contract PlonkVerifier { 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_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} // 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}} @@ -157,6 +157,7 @@ contract PlonkVerifier { {{ end }} // -------- precompiles + uint8 private constant SHA2 = 0x2; uint8 private constant MOD_EXP = 0x5; uint8 private constant EC_ADD = 0x6; uint8 private constant EC_MUL = 0x7; @@ -202,7 +203,7 @@ contract PlonkVerifier { mstore(add(mem, STATE_PI), l_pi) compute_alpha_square_lagrange_0() - verify_opening_linearised_polynomial(proof.offset) + compute_opening_linearised_polynomial(proof.offset) fold_h(proof.offset) compute_commitment_linearised_polynomial(proof.offset) compute_gamma_kzg(proof.offset) @@ -222,6 +223,16 @@ contract PlonkVerifier { revert(ptError, 0x64) } + /// Called when an exponentiation mod r fails + function error_mod_exp() { + let ptError := mload(0x40) + mstore(ptError, ERROR_STRING_ID) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0xc) + mstore(add(ptError, 0x44), "error mod exp") + revert(ptError, 0x64) + } + /// Called when an operation on Bn254 fails /// @dev for instance when calling EcMul on a point not on Bn254. function error_ec_op() { @@ -319,7 +330,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(0x300, mul(VK_NB_CUSTOM_GATES,0x60)) + let expected_proof_size := add(FIXED_PROOF_SIZE, mul(VK_NB_CUSTOM_GATES,0x60)) if iszero(eq(actual_proof_size, expected_proof_size)) { error_proof_size() } @@ -403,30 +414,28 @@ contract PlonkVerifier { let state := mload(0x40) let mPtr := add(state, STATE_LAST_MEM) - // gamma - // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] - // (same for alpha, beta, zeta) mstore(mPtr, FS_GAMMA) // "gamma" - mstore(add(mPtr, 0x20), VK_S1_COM_X) - mstore(add(mPtr, 0x40), VK_S1_COM_Y) - mstore(add(mPtr, 0x60), VK_S2_COM_X) - mstore(add(mPtr, 0x80), VK_S2_COM_Y) - mstore(add(mPtr, 0xa0), VK_S3_COM_X) - mstore(add(mPtr, 0xc0), VK_S3_COM_Y) - mstore(add(mPtr, 0xe0), VK_QL_COM_X) - mstore(add(mPtr, 0x100), VK_QL_COM_Y) - mstore(add(mPtr, 0x120), VK_QR_COM_X) - mstore(add(mPtr, 0x140), VK_QR_COM_Y) - mstore(add(mPtr, 0x160), VK_QM_COM_X) - mstore(add(mPtr, 0x180), VK_QM_COM_Y) - mstore(add(mPtr, 0x1a0), VK_QO_COM_X) - mstore(add(mPtr, 0x1c0), VK_QO_COM_Y) - mstore(add(mPtr, 0x1e0), VK_QK_COM_X) - mstore(add(mPtr, 0x200), VK_QK_COM_Y) + {{ $offset = 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), VK_S1_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_S1_COM_Y) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_S2_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_S2_COM_Y) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_S3_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_S3_COM_Y) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QL_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QL_COM_Y) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QR_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QR_COM_Y) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QM_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QM_COM_Y) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QO_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QO_COM_Y) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QK_COM_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QK_COM_Y) {{ $offset = add $offset 0x20}} {{ range $index, $element := .Vk.CommitmentConstraintIndexes}} - mstore(add(mPtr, {{ hex (add 544 (mul $index 64)) }}), VK_QCP_{{ $index }}_X) - mstore(add(mPtr, {{ hex (add 576 (mul $index 64)) }}), VK_QCP_{{ $index }}_Y) + mstore(add(mPtr, {{ hex $offset }}), VK_QCP_{{ $index }}_X) {{ $offset = add $offset 0x20}} + mstore(add(mPtr, {{ hex $offset }}), VK_QCP_{{ $index }}_Y) {{ $offset = add $offset 0x20}} {{ end }} // public inputs let _mPtr := add(mPtr, {{ hex (add (mul (len .Vk.CommitmentConstraintIndexes) 64) 544) }}) @@ -447,7 +456,7 @@ contract PlonkVerifier { {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 )}} size := add(size, mul(VK_NB_CUSTOM_GATES, 0x40)) {{ end -}} - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" + let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" if iszero(l_success) { error_verify() } @@ -468,7 +477,7 @@ contract PlonkVerifier { // 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" + let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" if iszero(l_success) { error_verify() } @@ -504,7 +513,7 @@ contract PlonkVerifier { {{ end }} // [Z], the commitment to the grand product polynomial calldatacopy(_mPtr, add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X), 0x40) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), full_size, mPtr, 0x20) + let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1b), full_size, mPtr, 0x20) if iszero(l_success) { error_verify() } @@ -526,8 +535,8 @@ contract PlonkVerifier { // 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) + calldatacopy(add(mPtr, 0x40), add(aproof, PROOF_H_0_COM_X), 0xc0) + let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) if iszero(l_success) { error_verify() } @@ -567,24 +576,24 @@ contract PlonkVerifier { /// batch_compute_lagranges_at_z computes [L_0(z), .., L_{n-1}(z)] /// @param z point at which the Lagranges are evaluated /// @param zpnmo ζⁿ-1 - /// @param n number of public inputs (number of Lagranges to compute) + /// @param n_pub number of public inputs (number of Lagranges to compute) /// @param mPtr pointer to which the results are stored - function batch_compute_lagranges_at_z(z, zpnmo, n, mPtr) { + function batch_compute_lagranges_at_z(z, zpnmo, n_pub, mPtr) { let zn := mulmod(zpnmo, VK_INV_DOMAIN_SIZE, R_MOD) // 1/n * (ζⁿ - 1) let _w := 1 let _mPtr := mPtr - for {let i:=0} lt(i,n) {i:=add(i,1)} + for {let i:=0} lt(i,n_pub) {i:=add(i,1)} { mstore(_mPtr, addmod(z,sub(R_MOD, _w), R_MOD)) _w := mulmod(_w, VK_OMEGA, R_MOD) _mPtr := add(_mPtr, 0x20) } - batch_invert(mPtr, n, _mPtr) + batch_invert(mPtr, n_pub, _mPtr) _mPtr := mPtr _w := 1 - for {let i:=0} lt(i,n) {i:=add(i,1)} + for {let i:=0} lt(i,n_pub) {i:=add(i,1)} { mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn , R_MOD), _w, R_MOD)) _mPtr := add(_mPtr, 0x20) @@ -641,8 +650,10 @@ contract PlonkVerifier { 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) pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, R_MOD), R_MOD) + {{ if (lt (inc $index) (len $.Vk.CommitmentConstraintIndexes) )}} p := add(p, 0x40) {{ end }} + {{ end }} } @@ -702,7 +713,7 @@ contract PlonkVerifier { // size domain mstore8(add(mPtr, 0x8e), HASH_FR_SIZE_DOMAIN) - let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + let l_success := staticcall(gas(), SHA2, mPtr, 0x8f, mPtr, 0x20) if iszero(l_success) { error_verify() } @@ -726,7 +737,7 @@ contract PlonkVerifier { mstore8(add(mPtr, 0x2b), 0x6b) mstore8(add(mPtr, 0x2c), HASH_FR_SIZE_DOMAIN) // size domain - l_success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) + l_success := staticcall(gas(), SHA2, mPtr, 0x2d, mPtr, 0x20) if iszero(l_success) { error_verify() } @@ -753,14 +764,14 @@ contract PlonkVerifier { mstore8(add(mPtr, 0x4c), HASH_FR_SIZE_DOMAIN) // size domain let offset := add(mPtr, 0x20) - l_success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) + l_success := staticcall(gas(), SHA2, offset, 0x2d, offset, 0x20) if iszero(l_success) { error_verify() } // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) - // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] + // the result is then 2**(8*16)*mPtr[:32] + mPtr[32:48] res := mulmod(mload(mPtr), HASH_FR_BB, R_MOD) // <- res = 2**128 * mPtr[:32] let b1 := shr(128, mload(add(mPtr, 0x20))) // b1 <- [0, 0, .., 0 || b2[:16] ] res := addmod(res, b1, R_MOD) @@ -801,7 +812,7 @@ contract PlonkVerifier { // derive a random number. As there is no random generator, we // do an FS like challenge derivation, depending on both digests and - // ζ to ensure that the prover cannot control the random numger. + // ζ to ensure that the prover cannot control the random number. // Note: adding the other point ζω is not needed, as ω is known beforehand. mstore(mPtr, mload(add(state, STATE_FOLDED_DIGESTS_X))) mstore(add(mPtr, 0x20), mload(add(state, STATE_FOLDED_DIGESTS_Y))) @@ -813,7 +824,7 @@ contract PlonkVerifier { mstore(add(mPtr, 0xe0), calldataload(add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_Y))) mstore(add(mPtr, 0x100), mload(add(state, STATE_ZETA))) mstore(add(mPtr, 0x120), mload(add(state, STATE_GAMMA_KZG))) - let random := staticcall(gas(), 0x2, mPtr, 0x140, mPtr, 0x20) + let random := staticcall(gas(), SHA2, mPtr, 0x140, mPtr, 0x20) if iszero(random){ error_random_generation() } @@ -863,17 +874,18 @@ contract PlonkVerifier { mstore(folded_quotients_y, sub(P_MOD, mload(folded_quotients_y))) mstore(mPtr, mload(folded_digests)) - mstore(add(mPtr, 0x20), mload(add(folded_digests, 0x20))) - mstore(add(mPtr, 0x40), G2_SRS_0_X_0) // the 4 lines are the canonical G2 point on BN254 - mstore(add(mPtr, 0x60), G2_SRS_0_X_1) - mstore(add(mPtr, 0x80), G2_SRS_0_Y_0) - mstore(add(mPtr, 0xa0), G2_SRS_0_Y_1) - mstore(add(mPtr, 0xc0), mload(folded_quotients)) - mstore(add(mPtr, 0xe0), mload(add(folded_quotients, 0x20))) - mstore(add(mPtr, 0x100), G2_SRS_1_X_0) - mstore(add(mPtr, 0x120), G2_SRS_1_X_1) - mstore(add(mPtr, 0x140), G2_SRS_1_Y_0) - mstore(add(mPtr, 0x160), G2_SRS_1_Y_1) + {{ $offset = 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), mload(add(folded_digests, 0x20))) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_X_0) {{ $offset = add $offset 0x20 }} // the 4 lines are the canonical G2 point on BN254 + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_X_1) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_Y_0) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_Y_1) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), mload(folded_quotients)) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), mload(add(folded_quotients, 0x20))) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_X_0) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_X_1) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_Y_0) {{ $offset = add $offset 0x20 }} + mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_Y_1) {{ $offset = add $offset 0x20 }} check_pairing_kzg(mPtr) } @@ -894,7 +906,7 @@ contract PlonkVerifier { /// @notice Fold the opening proofs at ζ: /// * 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}(ζ) + /// * at state+state_folded_claimed_values we store: Linearised_polynomial(ζ)+γL(ζ) + γ²R(ζ)+ γ³O(ζ) + γ⁴S₁(ζ) +γ⁵S₂(ζ) + ∑ᵢγ⁵⁺ⁱPi_{i}(ζ) /// @param aproof pointer to the proof /// acc_gamma stores the γⁱ function fold_state(aproof) { @@ -908,11 +920,11 @@ 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_LINEARISED_POLYNOMIAL_X))) + mstore(state_folded_digests, 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))) - point_acc_mul_calldata(add(state, STATE_FOLDED_DIGESTS_X), add(aproof, PROOF_L_COM_X), acc_gamma, mPtr) + point_acc_mul_calldata(state_folded_digests, 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) acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD) @@ -1003,9 +1015,9 @@ contract PlonkVerifier { mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA))) let start_input := 0x1b // 00.."gamma" - 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 ζω) + let size_input := add(0x14, mul(VK_NB_CUSTOM_GATES,3)) // number of 32bytes elmts = 0x14 (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) + let check_staticcall := staticcall(gas(), SHA2, add(mPtr,start_input), size_input, add(state, STATE_GAMMA_KZG), 0x20) if iszero(check_staticcall) { error_verify() } @@ -1149,7 +1161,7 @@ contract PlonkVerifier { compute_commitment_linearised_polynomial_ec(aproof, s1, s2) } - /// @notice compute -z_h(ζ)*([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) { @@ -1157,10 +1169,10 @@ contract PlonkVerifier { let n_plus_two := add(VK_DOMAIN_SIZE, 2) let mPtr := add(mload(0x40), STATE_LAST_MEM) let zeta_power_n_plus_two := pow(mload(add(state, STATE_ZETA)), n_plus_two, mPtr) - point_mul_calldata(add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_2_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_1_X), mPtr) + point_mul_calldata(add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_2_COM_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_1_COM_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_add_calldata(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_0_COM_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) @@ -1170,7 +1182,7 @@ contract PlonkVerifier { /// @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_opening_linearised_polynomial(aproof) { + function compute_opening_linearised_polynomial(aproof) { let state := mload(0x40) @@ -1290,7 +1302,6 @@ contract PlonkVerifier { /// @param s scalar /// @mPtr free memory function point_acc_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) @@ -1325,7 +1336,7 @@ contract PlonkVerifier { mstore(add(mPtr, 0xa0), R_MOD) let check_staticcall := staticcall(gas(),MOD_EXP,mPtr,0xc0,mPtr,0x20) if eq(check_staticcall, 0) { - + error_mod_exp() } res := mload(mPtr) } @@ -1334,7 +1345,7 @@ contract PlonkVerifier { } ` -// MarshalSolidity convert s a proof to a byte array that can be used in a +// MarshalSolidity converts a proof to a byte array that can be used in a // Solidity contract. func (proof *Proof) MarshalSolidity() []byte { @@ -1398,16 +1409,14 @@ func (proof *Proof) MarshalSolidity() []byte { // uint256[] selector_commit_api_at_zeta; // uint256[] wire_committed_commitments; - if len(proof.Bsb22Commitments) > 0 { - for i := 0; i < len(proof.Bsb22Commitments); i++ { - tmp32 = proof.BatchedProof.ClaimedValues[6+i].Bytes() - res = append(res, tmp32[:]...) - } - - for _, bc := range proof.Bsb22Commitments { - tmp64 = bc.RawBytes() - res = append(res, tmp64[:]...) - } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + tmp32 = proof.BatchedProof.ClaimedValues[6+i].Bytes() + res = append(res, tmp32[:]...) + } + + for _, bc := range proof.Bsb22Commitments { + tmp64 = bc.RawBytes() + res = append(res, tmp64[:]...) } return res diff --git a/backend/plonk/bn254/unmarshal.go b/backend/plonk/bn254/unmarshal.go index cc3e7b8add..9554884105 100644 --- a/backend/plonk/bn254/unmarshal.go +++ b/backend/plonk/bn254/unmarshal.go @@ -55,10 +55,8 @@ func UnmarshalSolidity(s []byte, nbCommits int) Proof { proof.ZShiftedOpening.ClaimedValue.SetBytes(s[offset : offset+fr_size]) offset += fr_size - // uint256 quotient_polynomial_at_zeta; - // uint256 linearization_polynomial_at_zeta; - proof.BatchedProof.ClaimedValues[0].SetBytes(s[offset : offset+fr_size]) - offset += fr_size + // we skip the claimed value of the linearised polynomial at zeta as + // it is not in the marshal solidity proof // uint256 opening_at_zeta_proof_x; // uint256 opening_at_zeta_proof_y; @@ -73,7 +71,7 @@ func UnmarshalSolidity(s []byte, nbCommits int) Proof { // uint256[] selector_commit_api_at_zeta; // uint256[] wire_committed_commitments; for i := 0; i < nbCommits; i++ { - proof.BatchedProof.ClaimedValues[7+i].SetBytes(s[offset : offset+fr_size]) + proof.BatchedProof.ClaimedValues[6+i].SetBytes(s[offset : offset+fr_size]) offset += fr_size } diff --git a/backend/plonk/bn254/verify.go b/backend/plonk/bn254/verify.go index 8c4fecbca5..2197228b3f 100644 --- a/backend/plonk/bn254/verify.go +++ b/backend/plonk/bn254/verify.go @@ -42,6 +42,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -61,6 +62,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -99,16 +126,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - 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) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1077,7 +1099,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1150,10 +1172,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1193,7 +1215,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1220,6 +1242,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1258,25 +1282,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - 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₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1316,20 +1341,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) - blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + 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/setup.go b/backend/plonk/bw6-633/setup.go index 8aa342a41a..03622060e3 100644 --- a/backend/plonk/bw6-633/setup.go +++ b/backend/plonk/bw6-633/setup.go @@ -18,7 +18,6 @@ package plonk import ( "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/fft" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/iop" @@ -96,7 +95,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -154,9 +153,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) diff --git a/backend/plonk/bw6-633/verify.go b/backend/plonk/bw6-633/verify.go index 831c11ec18..c9e1f1928e 100644 --- a/backend/plonk/bw6-633/verify.go +++ b/backend/plonk/bw6-633/verify.go @@ -42,6 +42,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -61,6 +62,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -99,16 +126,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - 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) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1077,7 +1099,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1150,10 +1172,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1193,7 +1215,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1220,6 +1242,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1258,25 +1282,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) - 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₁(ζ) + den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1316,20 +1341,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) - blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) + blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + 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/setup.go b/backend/plonk/bw6-761/setup.go index 9764e5a796..b9cd13daed 100644 --- a/backend/plonk/bw6-761/setup.go +++ b/backend/plonk/bw6-761/setup.go @@ -18,7 +18,6 @@ package plonk import ( "fmt" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/fft" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/iop" @@ -96,7 +95,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -154,9 +153,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) diff --git a/backend/plonk/bw6-761/verify.go b/backend/plonk/bw6-761/verify.go index c9f25260e5..23522a2dca 100644 --- a/backend/plonk/bw6-761/verify.go +++ b/backend/plonk/bw6-761/verify.go @@ -42,6 +42,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -61,6 +62,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -99,16 +126,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) - 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) + zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1 + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i> 1 + nbBsbGates := len(s.proof.Bsb22Commitments) gateConstraint := func(u ...fr.Element) fr.Element { @@ -1054,7 +1076,7 @@ func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { return pEvaluatedAtZeta } -// /!\ modifies p's underlying array of coefficients, in particular the size changes +// /!\ modifies the size func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { cp := p.Coefficients() cbp := bp.Coefficients() @@ -1127,10 +1149,10 @@ func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKe return g.Wait() } -// divideByXMinusOne +// divideByZH // The input must be in LagrangeCoset. // The result is in Canonical Regular. (in place using a) -func divideByXMinusOne(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { +func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { // check that the basis is LagrangeCoset if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { @@ -1170,7 +1192,7 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { res[0].Exp(domains[1].FrMultiplicativeGen, expo) var t fr.Element - t.Exp(domains[1].Generator, big.NewInt(int64(domains[0].Cardinality))) + t.Exp(domains[1].Generator, expo) one := fr.One() @@ -1197,6 +1219,8 @@ func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { // + α*( (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)) +// +// /!\ blindedZCanonical is modified 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 { // l(ζ)r(ζ) @@ -1235,25 +1259,26 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, s2.Neg(&s2).Mul(&s2, &alpha) // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z - var zhZeta, zetaNPlusTwo, alphaSquareLagrangeOne, one, den, frNbElmt fr.Element + var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element one.SetOne() nbElmt := int64(s.domain0.Cardinality) - 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 + alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ + zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² + alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 + zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 frNbElmt.SetUint64(uint64(nbElmt)) 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₁(ζ) + alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &alpha). + Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) s3canonical := s.trace.S3.Coefficients() s.trace.Qk.ToCanonical(s.domain0).ToRegular() - // the hi are all of the same length + // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated + // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated h1 := s.h1() h2 := s.h2() h3 := s.h3() @@ -1293,20 +1318,29 @@ func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, } } - t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeOne) // α²L₁(ζ)Z(X) + t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) - if i < len(h1) { + // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. + // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 + if i < len(h3) { t.Mul(&h3[i], &zetaNPlusTwo). Add(&t, &h2[i]). Mul(&t, &zetaNPlusTwo). - Add(&t, &h1[i]) - t.Mul(&t, &zhZeta) + Add(&t, &h1[i]). + Mul(&t, &zhZeta) blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } else { + if s.opt.StatisticalZK { + t.Mul(&h2[i], &zetaNPlusTwo). + Add(&t, &h1[i]). + Mul(&t, &zhZeta) + blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) + } } - } }) + return blindedZCanonical } diff --git a/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl index 8401930fc9..e58f9684eb 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/plonk.setup.go.tmpl @@ -5,7 +5,6 @@ import ( {{- template "import_backend_cs" . }} "fmt" "github.com/consensys/gnark-crypto/ecc/{{toLower .Curve}}/fr/iop" - "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/backend/plonk/internal" "github.com/consensys/gnark/constraint" ) @@ -78,7 +77,7 @@ func Setup(spr *cs.SparseR1CS, srs, srsLagrange kzg.SRS) (*ProvingKey, *Verifyin // step 0: set the fft domains domain := initFFTDomain(spr) if domain.Cardinality < 2 { - return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", spr.GetNbConstraints()) + return nil, nil, fmt.Errorf("circuit has only %d constraints; unsupported by the current implementation", len(spr.Public)+spr.GetNbConstraints()) } // check the size of the kzg srs. @@ -136,9 +135,7 @@ func (pk *ProvingKey) VerifyingKey() interface{} { func NewTrace(spr *cs.SparseR1CS, domain *fft.Domain) *Trace { var trace Trace - nbConstraints := spr.GetNbConstraints() - sizeSystem := uint64(nbConstraints + len(spr.Public)) - size := ecc.NextPowerOfTwo(sizeSystem) + size := int(domain.Cardinality) commitmentInfo := spr.CommitmentInfo.(constraint.PlonkCommitments) ql := make([]fr.Element, size) 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 8b06dc097a..7ba4a42a48 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/plonk.verify.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/plonk.verify.go.tmpl @@ -25,6 +25,7 @@ import ( var ( errAlgebraicRelation = errors.New("algebraic relation does not hold") errInvalidWitness = errors.New("witness length is invalid") + errInvalidPoint = errors.New("point is not on the curve") ) func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { @@ -44,6 +45,32 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac return errInvalidWitness } + // check that the points in the proof are on the curve + for i := 0; i < len(proof.LRO); i++ { + if !proof.LRO[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.Z.IsInSubGroup() { + return errInvalidPoint + } + for i := 0; i < len(proof.H); i++ { + if !proof.H[i].IsInSubGroup() { + return errInvalidPoint + } + } + for i := 0; i < len(proof.Bsb22Commitments); i++ { + if !proof.Bsb22Commitments[i].IsInSubGroup() { + return errInvalidPoint + } + } + if !proof.BatchedProof.H.IsInSubGroup() { + return errInvalidPoint + } + if !proof.ZShiftedOpening.H.IsInSubGroup() { + return errInvalidPoint + } + // transcript to derive the challenge fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta") @@ -82,16 +109,16 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac } // evaluation of zhZeta=ζⁿ-1 - var zetaPowerM, zhZeta, lagrangeOne fr.Element + var zetaPowerM, zhZeta, lagrangeZero fr.Element var bExpo big.Int one := fr.One() bExpo.SetUint64(vk.Size) zetaPowerM.Exp(zeta, &bExpo) 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) + lagrangeZero.Sub(&zeta, &one). // ζ-1 + Inverse(&lagrangeZero). // 1/(ζ-1) + Mul(&lagrangeZero, &zhZeta). // (ζ^n-1)/(ζ-1) + Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1) // compute PI = ∑_{i