github.com/consensys/gnark@v0.11.0/internal/generator/backend/template/zkpschemes/plonk/plonk.verify.go.tmpl (about)

     1  import (
     2  	"errors"
     3  	"fmt"
     4      "io"
     5  	"math/big"
     6      {{ if eq .Curve "BN254" -}}
     7      "text/template"
     8      {{- end }}
     9  	"time"
    10  	"github.com/consensys/gnark/backend/solidity"
    11  
    12  	"github.com/consensys/gnark-crypto/ecc"
    13  	{{ template "import_curve" . }}
    14  	{{ template "import_fr" . }}
    15  	{{ if eq .Curve "BN254" -}}
    16  	"github.com/consensys/gnark-crypto/ecc/bn254/fp"
    17  	{{- end }}
    18  	{{- template "import_hash_to_field" . }}
    19  	{{ template "import_kzg" . }}
    20  	fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir"
    21  	"github.com/consensys/gnark/backend"
    22  	"github.com/consensys/gnark/logger"
    23  )
    24  
    25  var (
    26  	errAlgebraicRelation = errors.New("algebraic relation does not hold")
    27  	errInvalidWitness    = errors.New("witness length is invalid")
    28  	errInvalidPoint      = errors.New("point is not on the curve")
    29  )
    30  
    31  func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error {
    32  
    33  	log := logger.Logger().With().Str("curve", "{{ toLower .Curve }}").Str("backend", "plonk").Logger()
    34  	start := time.Now()
    35  	cfg, err := backend.NewVerifierConfig(opts...)
    36  	if err != nil {
    37  		return fmt.Errorf("create backend config: %w", err)
    38  	}
    39  
    40  	if len(proof.Bsb22Commitments) != len(vk.Qcp) {
    41  		return errors.New("BSB22 Commitment number mismatch")
    42  	}
    43  
    44  	if len(publicWitness) != int(vk.NbPublicVariables) {
    45  		return errInvalidWitness
    46  	}
    47  
    48  	// check that the points in the proof are on the curve
    49  	for i := 0; i < len(proof.LRO); i++ {
    50  		if !proof.LRO[i].IsInSubGroup() {
    51  			return errInvalidPoint
    52  		}
    53  	}
    54  	if !proof.Z.IsInSubGroup() {
    55  		return errInvalidPoint
    56  	}
    57  	for i := 0; i < len(proof.H); i++ {
    58  		if !proof.H[i].IsInSubGroup() {
    59  			return errInvalidPoint
    60  		}
    61  	}
    62  	for i := 0; i < len(proof.Bsb22Commitments); i++ {
    63  		if !proof.Bsb22Commitments[i].IsInSubGroup() {
    64  			return errInvalidPoint
    65  		}
    66  	}
    67  	if !proof.BatchedProof.H.IsInSubGroup() {
    68  		return errInvalidPoint
    69  	}
    70  	if !proof.ZShiftedOpening.H.IsInSubGroup() {
    71  		return errInvalidPoint
    72  	}
    73  
    74  	// transcript to derive the challenge
    75  	fs := fiatshamir.NewTranscript(cfg.ChallengeHash, "gamma", "beta", "alpha", "zeta")
    76  
    77  	// The first challenge is derived using the public data: the commitments to the permutation,
    78  	// the coefficients of the circuit, and the public inputs.
    79  	// derive gamma from the Comm(blinded cl), Comm(blinded cr), Comm(blinded co)
    80  	if err := bindPublicData(fs, "gamma", vk, publicWitness); err != nil {
    81  		return err
    82  	}
    83  	gamma, err := deriveRandomness(fs, "gamma", &proof.LRO[0], &proof.LRO[1], &proof.LRO[2])
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	// derive beta from Comm(l), Comm(r), Comm(o)
    89  	beta, err := deriveRandomness(fs, "beta")
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	// derive alpha from Com(Z), Bsb22Commitments
    95  	alphaDeps := make([]*curve.G1Affine, len(proof.Bsb22Commitments)+1)
    96  	for i := range proof.Bsb22Commitments {
    97  		alphaDeps[i] = &proof.Bsb22Commitments[i]
    98  	}
    99  	alphaDeps[len(alphaDeps)-1] = &proof.Z
   100  	alpha, err := deriveRandomness(fs, "alpha", alphaDeps...)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	// derive zeta, the point of evaluation
   106  	zeta, err := deriveRandomness(fs, "zeta", &proof.H[0], &proof.H[1], &proof.H[2])
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	// evaluation of zhZeta=ζⁿ-1
   112  	var zetaPowerM, zhZeta, lagrangeZero fr.Element
   113  	var bExpo big.Int
   114  	one := fr.One()
   115  	bExpo.SetUint64(vk.Size)
   116  	zetaPowerM.Exp(zeta, &bExpo)
   117  	zhZeta.Sub(&zetaPowerM, &one) // ζⁿ-1
   118  	lagrangeZero.Sub(&zeta, &one). // ζ-1
   119  					Inverse(&lagrangeZero).         // 1/(ζ-1)
   120  					Mul(&lagrangeZero, &zhZeta).    // (ζ^n-1)/(ζ-1)
   121  					Mul(&lagrangeZero, &vk.SizeInv) // 1/n * (ζ^n-1)/(ζ-1)
   122  
   123  	// compute PI = ∑_{i<n} Lᵢ*wᵢ
   124  	var pi fr.Element
   125  	var accw fr.Element
   126  	{
   127  		// [ζ-1,ζ-ω,ζ-ω²,..]
   128  		dens := make([]fr.Element, len(publicWitness))
   129  		accw.SetOne()
   130  		for i := 0; i < len(publicWitness); i++ {
   131  			dens[i].Sub(&zeta, &accw)
   132  			accw.Mul(&accw, &vk.Generator)
   133  		}
   134  
   135  		// [1/(ζ-1),1/(ζ-ω),1/(ζ-ω²),..]
   136  		invDens := fr.BatchInvert(dens)
   137  
   138  		accw.SetOne()
   139  		var xiLi fr.Element
   140  		for i := 0; i < len(publicWitness); i++ {
   141  			xiLi.Mul(&zhZeta, &invDens[i]).
   142  				Mul(&xiLi, &vk.SizeInv).
   143  				Mul(&xiLi, &accw).
   144  				Mul(&xiLi, &publicWitness[i]) // Pi[i]*(ωⁱ/n)(ζ^n-1)/(ζ-ω^i)
   145  			accw.Mul(&accw, &vk.Generator)
   146  			pi.Add(&pi, &xiLi)
   147  		}
   148  
   149  		if cfg.HashToFieldFn == nil {
   150  			cfg.HashToFieldFn = hash_to_field.New([]byte("BSB22-Plonk"))
   151  		}
   152  		var hashedCmt fr.Element
   153  		nbBuf := fr.Bytes
   154  		if cfg.HashToFieldFn.Size() < fr.Bytes {
   155  			nbBuf = cfg.HashToFieldFn.Size()
   156  		}
   157  		var wPowI, den, lagrange fr.Element
   158  		for i, cci := range vk.CommitmentConstraintIndexes {
   159  			cfg.HashToFieldFn.Write(proof.Bsb22Commitments[i].Marshal())
   160  			hashBts := cfg.HashToFieldFn.Sum(nil)
   161  			cfg.HashToFieldFn.Reset()
   162  			hashedCmt.SetBytes(hashBts[:nbBuf])
   163  
   164  			// Computing Lᵢ(ζ) where i=CommitmentIndex
   165  			wPowI.Exp(vk.Generator, big.NewInt(int64(vk.NbPublicVariables)+int64(cci)))
   166  			den.Sub(&zeta, &wPowI) // ζ-wⁱ
   167  			lagrange.SetOne().
   168  				Sub(&zetaPowerM, &lagrange). // ζⁿ-1
   169  				Mul(&lagrange, &wPowI).      // wⁱ(ζⁿ-1)
   170  				Div(&lagrange, &den).        // wⁱ(ζⁿ-1)/(ζ-wⁱ)
   171  				Mul(&lagrange, &vk.SizeInv)  // wⁱ/n (ζⁿ-1)/(ζ-wⁱ)
   172  
   173  			xiLi.Mul(&lagrange, &hashedCmt)
   174  			pi.Add(&pi, &xiLi)
   175  		}
   176  	}
   177  
   178  	var _s1, _s2, tmp fr.Element
   179  	l := proof.BatchedProof.ClaimedValues[1]
   180  	r := proof.BatchedProof.ClaimedValues[2]
   181  	o := proof.BatchedProof.ClaimedValues[3]
   182  	s1 := proof.BatchedProof.ClaimedValues[4]
   183  	s2 := proof.BatchedProof.ClaimedValues[5]
   184  
   185  	// Z(ωζ)
   186  	zu := proof.ZShiftedOpening.ClaimedValue
   187  
   188  	// α²*L₁(ζ)
   189  	var alphaSquarelagrangeZero fr.Element
   190  	alphaSquarelagrangeZero.Mul(&lagrangeZero, &alpha).
   191  		Mul(&alphaSquarelagrangeZero, &alpha) // α²*L₁(ζ)
   192  
   193  	// computing the constant coefficient of the full algebraic relation
   194  	// , corresponding to the value of the linearisation polynomiat at ζ
   195  	// PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ)
   196  	var constLin fr.Element
   197  	constLin.Mul(&beta, &s1).Add(&constLin, &gamma).Add(&constLin, &l)       // (l(ζ)+β*s1(ζ)+γ)
   198  	tmp.Mul(&s2, &beta).Add(&tmp, &gamma).Add(&tmp, &r)                      // (r(ζ)+β*s2(ζ)+γ)
   199  	constLin.Mul(&constLin, &tmp)                                            // (l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)
   200  	tmp.Add(&o, &gamma)                                                      // (o(ζ)+γ)
   201  	constLin.Mul(&tmp, &constLin).Mul(&constLin, &alpha).Mul(&constLin, &zu) // α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ)
   202  
   203  	constLin.Sub(&constLin, &alphaSquarelagrangeZero).Add(&constLin, &pi) // PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ)
   204  	constLin.Neg(&constLin)                                              // -[PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ)]
   205  
   206  	// check that the opening of the linearised polynomial is equal to -constLin
   207  	openingLinPol := proof.BatchedProof.ClaimedValues[0]
   208  	if !constLin.Equal(&openingLinPol) {
   209  		return errAlgebraicRelation
   210  	}
   211  
   212  	// computing the linearised polynomial digest
   213  	// α²*L₁(ζ)*[Z] +
   214  	// _s1*[s3]+_s2*[Z] + l(ζ)*[Ql] +
   215  	// l(ζ)r(ζ)*[Qm] + r(ζ)*[Qr] + o(ζ)*[Qo] + [Qk] + ∑ᵢQcp_(ζ)[Pi_i] -
   216  	// Z_{H}(ζ)*(([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾*[H₂])
   217  	// where
   218  	// _s1 =  α*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)
   219  	// _s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)
   220  
   221  	// _s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)
   222  	_s1.Mul(&beta, &s1).Add(&_s1, &l).Add(&_s1, &gamma)                   // (l(ζ)+β*s1(β)+γ)
   223  	tmp.Mul(&beta, &s2).Add(&tmp, &r).Add(&tmp, &gamma)                   // (r(ζ)+β*s2(β)+γ)
   224  	_s1.Mul(&_s1, &tmp).Mul(&_s1, &beta).Mul(&_s1, &alpha).Mul(&_s1, &zu) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)
   225  
   226  	// _s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)
   227  	_s2.Mul(&beta, &zeta).Add(&_s2, &gamma).Add(&_s2, &l)                                                     // (l(ζ)+β*ζ+γ)
   228  	tmp.Mul(&beta, &vk.CosetShift).Mul(&tmp, &zeta).Add(&tmp, &gamma).Add(&tmp, &r)                           // (r(ζ)+β*u*ζ+γ)
   229  	_s2.Mul(&_s2, &tmp)                                                                                       // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)
   230  	tmp.Mul(&beta, &vk.CosetShift).Mul(&tmp, &vk.CosetShift).Mul(&tmp, &zeta).Add(&tmp, &o).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ)
   231  	_s2.Mul(&_s2, &tmp).Mul(&_s2, &alpha).Neg(&_s2)                                                           // -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)
   232  
   233  	// α²*L₁(ζ) - α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ)
   234  	var coeffZ fr.Element
   235  	coeffZ.Add(&alphaSquarelagrangeZero, &_s2)
   236  
   237  	// l(ζ)*r(ζ)
   238  	var rl fr.Element
   239  	rl.Mul(&l, &r)
   240  
   241  	// -ζⁿ⁺²*(ζⁿ-1), -ζ²⁽ⁿ⁺²⁾*(ζⁿ-1), -(ζⁿ-1)
   242  	nPlusTwo := big.NewInt(int64(vk.Size) + 2)
   243  	var zetaNPlusTwoZh, zetaNPlusTwoSquareZh, zh fr.Element
   244  	zetaNPlusTwoZh.Exp(zeta, nPlusTwo)
   245  	zetaNPlusTwoSquareZh.Mul(&zetaNPlusTwoZh, &zetaNPlusTwoZh)                          // ζ²⁽ⁿ⁺²⁾
   246  	zetaNPlusTwoZh.Mul(&zetaNPlusTwoZh, &zhZeta).Neg(&zetaNPlusTwoZh)                   // -ζⁿ⁺²*(ζⁿ-1)
   247  	zetaNPlusTwoSquareZh.Mul(&zetaNPlusTwoSquareZh, &zhZeta).Neg(&zetaNPlusTwoSquareZh) // -ζ²⁽ⁿ⁺²⁾*(ζⁿ-1)
   248  	zh.Neg(&zhZeta)
   249  
   250  	var linearizedPolynomialDigest curve.G1Affine
   251  	points := append(proof.Bsb22Commitments,
   252  		vk.Ql, vk.Qr, vk.Qm, vk.Qo, vk.Qk,
   253  		vk.S[2], proof.Z,
   254  		proof.H[0], proof.H[1], proof.H[2],
   255  	)
   256  
   257  	qC := make([]fr.Element, len(proof.Bsb22Commitments))
   258  	copy(qC, proof.BatchedProof.ClaimedValues[6:])
   259  
   260  	scalars := append(qC,
   261  		l, r, rl, o, one,
   262  		_s1, coeffZ,
   263  		zh, zetaNPlusTwoZh, zetaNPlusTwoSquareZh,
   264  	)
   265  	if _, err := linearizedPolynomialDigest.MultiExp(points, scalars, ecc.MultiExpConfig{}); err != nil {
   266  		return err
   267  	}
   268  
   269  	// Fold the first proof
   270  	digestsToFold := make([]curve.G1Affine, len(vk.Qcp)+6)
   271  	copy(digestsToFold[6:], vk.Qcp)
   272  	digestsToFold[0] = linearizedPolynomialDigest
   273  	digestsToFold[1] = proof.LRO[0]
   274  	digestsToFold[2] = proof.LRO[1]
   275  	digestsToFold[3] = proof.LRO[2]
   276  	digestsToFold[4] = vk.S[0]
   277  	digestsToFold[5] = vk.S[1]
   278  	foldedProof, foldedDigest, err := kzg.FoldProof(
   279  		digestsToFold,
   280  		&proof.BatchedProof,
   281  		zeta,
   282  		cfg.KZGFoldingHash,
   283  		zu.Marshal(),
   284  	)
   285  	if err != nil {
   286  		return err
   287  	}
   288  
   289  	// Batch verify
   290  	var shiftedZeta fr.Element
   291  	shiftedZeta.Mul(&zeta, &vk.Generator)
   292  	err = kzg.BatchVerifyMultiPoints([]kzg.Digest{
   293  		foldedDigest,
   294  		proof.Z,
   295  	},
   296  		[]kzg.OpeningProof{
   297  			foldedProof,
   298  			proof.ZShiftedOpening,
   299  		},
   300  		[]fr.Element{
   301  			zeta,
   302  			shiftedZeta,
   303  		},
   304  		vk.Kzg,
   305  	)
   306  
   307  	log.Debug().Dur("took", time.Since(start)).Msg("verifier done")
   308  
   309  	return err
   310  }
   311  
   312  func bindPublicData(fs *fiatshamir.Transcript, challenge string, vk *VerifyingKey, publicInputs []fr.Element) error {
   313  
   314  	// permutation
   315  	if err := fs.Bind(challenge, vk.S[0].Marshal()); err != nil {
   316  		return err
   317  	}
   318  	if err := fs.Bind(challenge, vk.S[1].Marshal()); err != nil {
   319  		return err
   320  	}
   321  	if err := fs.Bind(challenge, vk.S[2].Marshal()); err != nil {
   322  		return err
   323  	}
   324  
   325  	// coefficients
   326  	if err := fs.Bind(challenge, vk.Ql.Marshal()); err != nil {
   327  		return err
   328  	}
   329  	if err := fs.Bind(challenge, vk.Qr.Marshal()); err != nil {
   330  		return err
   331  	}
   332  	if err := fs.Bind(challenge, vk.Qm.Marshal()); err != nil {
   333  		return err
   334  	}
   335  	if err := fs.Bind(challenge, vk.Qo.Marshal()); err != nil {
   336  		return err
   337  	}
   338  	if err := fs.Bind(challenge, vk.Qk.Marshal()); err != nil {
   339  		return err
   340  	}
   341  	for i := range vk.Qcp {
   342  		if err := fs.Bind(challenge, vk.Qcp[i].Marshal()); err != nil {
   343  			return err
   344  		}
   345  	}
   346  
   347  	// public inputs
   348  	for i := 0; i < len(publicInputs); i++ {
   349  		if err := fs.Bind(challenge, publicInputs[i].Marshal()); err != nil {
   350  			return err
   351  		}
   352  	}
   353  
   354  	return nil
   355  
   356  }
   357  
   358  func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*curve.G1Affine) (fr.Element, error) {
   359  
   360  	var buf [curve.SizeOfG1AffineUncompressed]byte
   361  	var r fr.Element
   362  
   363  	for _, p := range points {
   364  		buf = p.RawBytes()
   365  		if err := fs.Bind(challenge, buf[:]); err != nil {
   366  			return r, err
   367  		}
   368  	}
   369  
   370  	b, err := fs.ComputeChallenge(challenge)
   371  	if err != nil {
   372  		return r, err
   373  	}
   374  	r.SetBytes(b)
   375  	return r, nil
   376  }
   377  
   378  
   379  {{if eq .Curve "BN254"}}
   380  // ExportSolidity exports the verifying key to a solidity smart contract.
   381  //
   382  // See https://github.com/ConsenSys/gnark-tests for example usage.
   383  //
   384  // Code has not been audited and is provided as-is, we make no guarantees or warranties to its safety and reliability.
   385  func (vk *VerifyingKey) ExportSolidity(w io.Writer, exportOpts ...solidity.ExportOption) error {
   386  	funcMap := template.FuncMap{
   387  		"hex": func(i int) string {
   388  			return fmt.Sprintf("0x%x", i)
   389  		},
   390  		"mul": func(a, b int) int {
   391  			return a * b
   392  		},
   393  		"inc": func(i int) int {
   394  			return i + 1
   395  		},
   396  		"frstr": func(x fr.Element) string {
   397  			// we use big.Int to always get a positive string.
   398  			// not the most efficient hack, but it works better for .sol generation.
   399  			bv := new(big.Int)
   400  			x.BigInt(bv)
   401  			return bv.String()
   402  		},
   403  		"fpstr": func(x fp.Element) string {
   404  			bv := new(big.Int)
   405  			x.BigInt(bv)
   406  			return bv.String()
   407  		},
   408  		"add": func(i, j int) int {
   409  			return i + j
   410  		},
   411  	}
   412  
   413  	t, err := template.New("t").Funcs(funcMap).Parse(tmplSolidityVerifier)
   414  	if err != nil {
   415  		return err
   416  	}
   417  
   418  	cfg, err := solidity.NewExportConfig(exportOpts...)
   419  	if err != nil {
   420  		return err
   421  	}
   422  	if cfg.HashToFieldFn != nil {
   423  		return fmt.Errorf("setting hash to field function is not supported for PLONK Solidity export. Hash function is hardcoded to RFC9380")
   424  	}
   425  
   426  	return t.Execute(w, struct {
   427  		Cfg solidity.ExportConfig
   428  		Vk VerifyingKey
   429  	}{
   430  		Cfg: cfg,
   431  		Vk:  *vk,
   432  	})
   433  }
   434  
   435  {{else}}
   436  // ExportSolidity not implemented for {{.Curve}}
   437  func (vk *VerifyingKey) ExportSolidity(w io.Writer, exportOpts ...solidity.ExportOption) error {
   438  	return errors.New("not implemented")
   439  }
   440  {{end}}