github.com/consensys/gnark@v0.11.0/backend/plonk/bn254/verify.go (about)

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