github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-378/fr/plookup/vector.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 consensys/gnark-crypto DO NOT EDIT
    16  
    17  package plookup
    18  
    19  import (
    20  	"crypto/sha256"
    21  	"errors"
    22  	"math/big"
    23  	"math/bits"
    24  	"sort"
    25  
    26  	"github.com/consensys/gnark-crypto/ecc/bls12-378/fr"
    27  	"github.com/consensys/gnark-crypto/ecc/bls12-378/fr/fft"
    28  	"github.com/consensys/gnark-crypto/ecc/bls12-378/kzg"
    29  	fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir"
    30  )
    31  
    32  var (
    33  	ErrNotInTable          = errors.New("some value in the vector is not in the lookup table")
    34  	ErrPlookupVerification = errors.New("plookup verification failed")
    35  	ErrGenerator           = errors.New("wrong generator")
    36  )
    37  
    38  // Proof Plookup proof, containing opening proofs
    39  type ProofLookupVector struct {
    40  
    41  	// size of the system
    42  	size uint64
    43  
    44  	// generator of the fft domain, used for shifting the evaluation point
    45  	g fr.Element
    46  
    47  	// Commitments to h1, h2, t, z, f, h
    48  	h1, h2, t, z, f, h kzg.Digest
    49  
    50  	// Batch opening proof of h1, h2, z, t
    51  	BatchedProof kzg.BatchOpeningProof
    52  
    53  	// Batch opening proof of h1, h2, z shifted by g
    54  	BatchedProofShifted kzg.BatchOpeningProof
    55  }
    56  
    57  // evaluateAccumulationPolynomial computes Z, in Lagrange basis. Z is the accumulation of the partial
    58  // ratios of 2 fully split polynomials (cf https://eprint.iacr.org/2020/315.pdf)
    59  // * lf is the list of values that should be in lt
    60  // * lt is the lookup table
    61  // * lh1, lh2 is lf sorted by lt split in 2 overlapping slices
    62  // * beta, gamma are challenges (Schwartz-zippel: they are the random evaluations point)
    63  func evaluateAccumulationPolynomial(lf, lt, lh1, lh2 []fr.Element, beta, gamma fr.Element) []fr.Element {
    64  
    65  	z := make([]fr.Element, len(lt))
    66  
    67  	n := len(lt)
    68  	d := make([]fr.Element, n-1)
    69  	var u, c fr.Element
    70  	c.SetOne().
    71  		Add(&c, &beta).
    72  		Mul(&c, &gamma)
    73  	for i := 0; i < n-1; i++ {
    74  
    75  		d[i].Mul(&beta, &lh1[i+1]).
    76  			Add(&d[i], &lh1[i]).
    77  			Add(&d[i], &c)
    78  
    79  		u.Mul(&beta, &lh2[i+1]).
    80  			Add(&u, &lh2[i]).
    81  			Add(&u, &c)
    82  
    83  		d[i].Mul(&d[i], &u)
    84  	}
    85  	d = fr.BatchInvert(d)
    86  
    87  	z[0].SetOne()
    88  	var a, b, e fr.Element
    89  	e.SetOne().Add(&e, &beta)
    90  	for i := 0; i < n-1; i++ {
    91  
    92  		a.Add(&gamma, &lf[i])
    93  
    94  		b.Mul(&beta, &lt[i+1]).
    95  			Add(&b, &lt[i]).
    96  			Add(&b, &c)
    97  
    98  		a.Mul(&a, &b).
    99  			Mul(&a, &e)
   100  
   101  		z[i+1].Mul(&z[i], &a).
   102  			Mul(&z[i+1], &d[i])
   103  	}
   104  
   105  	return z
   106  }
   107  
   108  // evaluateNumBitReversed computes the evaluation (shifted, bit reversed) of h where
   109  // h = (x-1)*z*(1+\beta)*(\gamma+f)*(\gamma(1+\beta) + t+ \beta*t(gX)) -
   110  //
   111  //	(x-1)*z(gX)*(\gamma(1+\beta) + h_{1} + \beta*h_{1}(gX))*(\gamma(1+\beta) + h_{2} + \beta*h_{2}(gX) )
   112  //
   113  // * cz, ch1, ch2, ct, cf are the polynomials z, h1, h2, t, f in canonical basis
   114  // * _lz, _lh1, _lh2, _lt, _lf are the polynomials z, h1, h2, t, f in shifted Lagrange basis (domainBig)
   115  // * beta, gamma are the challenges
   116  // * it returns h in canonical basis
   117  func evaluateNumBitReversed(_lz, _lh1, _lh2, _lt, _lf []fr.Element, beta, gamma fr.Element, domainBig *fft.Domain) []fr.Element {
   118  
   119  	// result
   120  	s := int(domainBig.Cardinality)
   121  	num := make([]fr.Element, domainBig.Cardinality)
   122  
   123  	var u, onePlusBeta, GammaTimesOnePlusBeta, m, n, one fr.Element
   124  
   125  	one.SetOne()
   126  	onePlusBeta.Add(&one, &beta)
   127  	GammaTimesOnePlusBeta.Mul(&onePlusBeta, &gamma)
   128  
   129  	g := make([]fr.Element, s)
   130  	g[0].Set(&domainBig.FrMultiplicativeGen)
   131  	for i := 1; i < s; i++ {
   132  		g[i].Mul(&g[i-1], &domainBig.Generator)
   133  	}
   134  
   135  	var gg fr.Element
   136  	expo := big.NewInt(int64(domainBig.Cardinality>>1 - 1))
   137  	gg.Square(&domainBig.Generator).Exp(gg, expo)
   138  
   139  	nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality))
   140  
   141  	for i := 0; i < s; i++ {
   142  
   143  		_i := int(bits.Reverse64(uint64(i)) >> nn)
   144  		_is := int(bits.Reverse64(uint64((i+2)%s)) >> nn)
   145  
   146  		// m = z*(1+\beta)*(\gamma+f)*(\gamma(1+\beta) + t+ \beta*t(gX))
   147  		m.Mul(&onePlusBeta, &_lz[_i])
   148  		u.Add(&gamma, &_lf[_i])
   149  		m.Mul(&m, &u)
   150  		u.Mul(&beta, &_lt[_is]).
   151  			Add(&u, &_lt[_i]).
   152  			Add(&u, &GammaTimesOnePlusBeta)
   153  		m.Mul(&m, &u)
   154  
   155  		// n = z(gX)*(\gamma(1+\beta) + h_{1} + \beta*h_{1}(gX))*(\gamma(1+\beta) + h_{2} + \beta*h_{2}(gX)
   156  		n.Mul(&beta, &_lh1[_is]).
   157  			Add(&n, &_lh1[_i]).
   158  			Add(&n, &GammaTimesOnePlusBeta)
   159  		u.Mul(&beta, &_lh2[_is]).
   160  			Add(&u, &_lh2[_i]).
   161  			Add(&u, &GammaTimesOnePlusBeta)
   162  		n.Mul(&n, &u).
   163  			Mul(&n, &_lz[_is])
   164  
   165  		// (x-gg**(n-1))*(m-n)
   166  		num[_i].Sub(&m, &n)
   167  		u.Sub(&g[i], &gg)
   168  		num[_i].Mul(&num[_i], &u)
   169  
   170  	}
   171  
   172  	return num
   173  }
   174  
   175  // evaluateXnMinusOneDomainBig returns the evaluation of (x^{n}-1) on FrMultiplicativeGen*< g  >
   176  func evaluateXnMinusOneDomainBig(domainBig *fft.Domain) [2]fr.Element {
   177  
   178  	sizeDomainSmall := domainBig.Cardinality / 2
   179  
   180  	var one fr.Element
   181  	one.SetOne()
   182  
   183  	// x^{n}-1 on FrMultiplicativeGen*< g  >
   184  	var res [2]fr.Element
   185  	var shift fr.Element
   186  	shift.Exp(domainBig.FrMultiplicativeGen, big.NewInt(int64(sizeDomainSmall)))
   187  	res[0].Sub(&shift, &one)
   188  	res[1].Add(&shift, &one).Neg(&res[1])
   189  
   190  	return res
   191  
   192  }
   193  
   194  // evaluateL0DomainBig returns the evaluation of (x^{n}-1)/(x-1) on
   195  // x^{n}-1 on FrMultiplicativeGen*< g  >
   196  func evaluateL0DomainBig(domainBig *fft.Domain) ([2]fr.Element, []fr.Element) {
   197  
   198  	var one fr.Element
   199  	one.SetOne()
   200  
   201  	// x^{n}-1 on FrMultiplicativeGen*< g  >
   202  	xnMinusOne := evaluateXnMinusOneDomainBig(domainBig)
   203  
   204  	// 1/(x-1) on FrMultiplicativeGen*< g  >
   205  	var acc fr.Element
   206  	denL0 := make([]fr.Element, domainBig.Cardinality)
   207  	acc.Set(&domainBig.FrMultiplicativeGen)
   208  	for i := 0; i < int(domainBig.Cardinality); i++ {
   209  		denL0[i].Sub(&acc, &one)
   210  		acc.Mul(&acc, &domainBig.Generator)
   211  	}
   212  	denL0 = fr.BatchInvert(denL0)
   213  
   214  	return xnMinusOne, denL0
   215  }
   216  
   217  // evaluationLnDomainBig returns the evaluation of (x^{n}-1)/(x-g^{n-1}) on
   218  // x^{n}-1 on FrMultiplicativeGen*< g  >
   219  func evaluationLnDomainBig(domainBig *fft.Domain) ([2]fr.Element, []fr.Element) {
   220  
   221  	sizeDomainSmall := domainBig.Cardinality / 2
   222  
   223  	var one fr.Element
   224  	one.SetOne()
   225  
   226  	// x^{n}-1 on FrMultiplicativeGen*< g  >
   227  	numLn := evaluateXnMinusOneDomainBig(domainBig)
   228  
   229  	// 1/(x-g^{n-1}) on FrMultiplicativeGen*< g  >
   230  	var gg, acc fr.Element
   231  	gg.Square(&domainBig.Generator).Exp(gg, big.NewInt(int64(sizeDomainSmall-1)))
   232  	denLn := make([]fr.Element, domainBig.Cardinality)
   233  	acc.Set(&domainBig.FrMultiplicativeGen)
   234  	for i := 0; i < int(domainBig.Cardinality); i++ {
   235  		denLn[i].Sub(&acc, &gg)
   236  		acc.Mul(&acc, &domainBig.Generator)
   237  	}
   238  	denLn = fr.BatchInvert(denLn)
   239  
   240  	return numLn, denLn
   241  
   242  }
   243  
   244  // evaluateZStartsByOneBitReversed returns l0 * (z-1), in Lagrange basis and bit reversed order
   245  func evaluateZStartsByOneBitReversed(lsZBitReversed []fr.Element, domainBig *fft.Domain) []fr.Element {
   246  
   247  	var one fr.Element
   248  	one.SetOne()
   249  
   250  	res := make([]fr.Element, domainBig.Cardinality)
   251  
   252  	nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality))
   253  
   254  	xnMinusOne, denL0 := evaluateL0DomainBig(domainBig)
   255  
   256  	for i := 0; i < len(lsZBitReversed); i++ {
   257  		_i := int(bits.Reverse64(uint64(i)) >> nn)
   258  		res[_i].Sub(&lsZBitReversed[_i], &one).
   259  			Mul(&res[_i], &xnMinusOne[i%2]).
   260  			Mul(&res[_i], &denL0[i])
   261  	}
   262  
   263  	return res
   264  }
   265  
   266  // evaluateZEndsByOneBitReversed returns ln * (z-1), in Lagrange basis and bit reversed order
   267  func evaluateZEndsByOneBitReversed(lsZBitReversed []fr.Element, domainBig *fft.Domain) []fr.Element {
   268  
   269  	var one fr.Element
   270  	one.SetOne()
   271  
   272  	numLn, denLn := evaluationLnDomainBig(domainBig)
   273  
   274  	res := make([]fr.Element, len(lsZBitReversed))
   275  	nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality))
   276  
   277  	for i := 0; i < len(lsZBitReversed); i++ {
   278  		_i := int(bits.Reverse64(uint64(i)) >> nn)
   279  		res[_i].Sub(&lsZBitReversed[_i], &one).
   280  			Mul(&res[_i], &numLn[i%2]).
   281  			Mul(&res[_i], &denLn[i])
   282  	}
   283  
   284  	return res
   285  }
   286  
   287  // evaluateOverlapH1h2BitReversed returns ln * (h1 - h2(g.x)), in Lagrange basis and bit reversed order
   288  func evaluateOverlapH1h2BitReversed(_lh1, _lh2 []fr.Element, domainBig *fft.Domain) []fr.Element {
   289  
   290  	var one fr.Element
   291  	one.SetOne()
   292  
   293  	numLn, denLn := evaluationLnDomainBig(domainBig)
   294  
   295  	res := make([]fr.Element, len(_lh1))
   296  	nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality))
   297  
   298  	s := len(_lh1)
   299  	for i := 0; i < s; i++ {
   300  
   301  		_i := int(bits.Reverse64(uint64(i)) >> nn)
   302  		_is := int(bits.Reverse64(uint64((i+2)%s)) >> nn)
   303  
   304  		res[_i].Sub(&_lh1[_i], &_lh2[_is]).
   305  			Mul(&res[_i], &numLn[i%2]).
   306  			Mul(&res[_i], &denLn[i])
   307  	}
   308  
   309  	return res
   310  }
   311  
   312  // computeQuotientCanonical computes the full quotient of the plookup protocol.
   313  // * alpha is the challenge to fold the numerator
   314  // * lh, lh0, lhn, lh1h2 are the various pieces of the numerator (Lagrange shifted form, bit reversed order)
   315  // * domainBig fft domain
   316  // It returns the quotient, in canonical basis
   317  func computeQuotientCanonical(alpha fr.Element, lh, lh0, lhn, lh1h2 []fr.Element, domainBig *fft.Domain) []fr.Element {
   318  
   319  	sizeDomainBig := int(domainBig.Cardinality)
   320  	res := make([]fr.Element, sizeDomainBig)
   321  
   322  	var one fr.Element
   323  	one.SetOne()
   324  
   325  	numLn := evaluateXnMinusOneDomainBig(domainBig)
   326  	numLn[0].Inverse(&numLn[0])
   327  	numLn[1].Inverse(&numLn[1])
   328  	nn := uint64(64 - bits.TrailingZeros64(domainBig.Cardinality))
   329  
   330  	for i := 0; i < sizeDomainBig; i++ {
   331  
   332  		_i := int(bits.Reverse64(uint64(i)) >> nn)
   333  
   334  		res[_i].Mul(&lh1h2[_i], &alpha).
   335  			Add(&res[_i], &lhn[_i]).
   336  			Mul(&res[_i], &alpha).
   337  			Add(&res[_i], &lh0[_i]).
   338  			Mul(&res[_i], &alpha).
   339  			Add(&res[_i], &lh[_i]).
   340  			Mul(&res[_i], &numLn[i%2])
   341  	}
   342  
   343  	domainBig.FFTInverse(res, fft.DIT, fft.OnCoset())
   344  
   345  	return res
   346  }
   347  
   348  // ProveLookupVector returns proof that the values in f are in t.
   349  //
   350  // /!\IMPORTANT/!\
   351  //
   352  // If the table t is already committed somewhere (which is the normal workflow
   353  // before generating a lookup proof), the commitment needs to be done on the
   354  // table sorted. Otherwise the commitment in proof.t will not be the same as
   355  // the public commitment: it will contain the same values, but permuted.
   356  func ProveLookupVector(pk kzg.ProvingKey, f, t fr.Vector) (ProofLookupVector, error) {
   357  
   358  	// res
   359  	var proof ProofLookupVector
   360  	var err error
   361  
   362  	// hash function used for Fiat Shamir
   363  	hFunc := sha256.New()
   364  
   365  	// transcript to derive the challenge
   366  	fs := fiatshamir.NewTranscript(hFunc, "beta", "gamma", "alpha", "nu")
   367  
   368  	// create domains
   369  	var domainSmall *fft.Domain
   370  	if len(t) <= len(f) {
   371  		domainSmall = fft.NewDomain(uint64(len(f) + 1))
   372  	} else {
   373  		domainSmall = fft.NewDomain(uint64(len(t)))
   374  	}
   375  	sizeDomainSmall := int(domainSmall.Cardinality)
   376  
   377  	// set the size
   378  	proof.size = domainSmall.Cardinality
   379  
   380  	// set the generator
   381  	proof.g.Set(&domainSmall.Generator)
   382  
   383  	// resize f and t
   384  	// note: the last element of lf does not matter
   385  	lf := make([]fr.Element, sizeDomainSmall)
   386  	lt := make([]fr.Element, sizeDomainSmall)
   387  	cf := make([]fr.Element, sizeDomainSmall)
   388  	ct := make([]fr.Element, sizeDomainSmall)
   389  	copy(lt, t)
   390  	copy(lf, f)
   391  	for i := len(f); i < sizeDomainSmall; i++ {
   392  		lf[i] = f[len(f)-1]
   393  	}
   394  	for i := len(t); i < sizeDomainSmall; i++ {
   395  		lt[i] = t[len(t)-1]
   396  	}
   397  	sort.Sort(fr.Vector(lt))
   398  	copy(ct, lt)
   399  	copy(cf, lf)
   400  	domainSmall.FFTInverse(ct, fft.DIF)
   401  	domainSmall.FFTInverse(cf, fft.DIF)
   402  	fft.BitReverse(ct)
   403  	fft.BitReverse(cf)
   404  	proof.t, err = kzg.Commit(ct, pk)
   405  	if err != nil {
   406  		return proof, err
   407  	}
   408  	proof.f, err = kzg.Commit(cf, pk)
   409  	if err != nil {
   410  		return proof, err
   411  	}
   412  
   413  	// write f sorted by t
   414  	lfSortedByt := make(fr.Vector, 2*domainSmall.Cardinality-1)
   415  	copy(lfSortedByt, lt)
   416  	copy(lfSortedByt[domainSmall.Cardinality:], lf)
   417  	sort.Sort(lfSortedByt)
   418  
   419  	// compute h1, h2, commit to them
   420  	lh1 := make([]fr.Element, sizeDomainSmall)
   421  	lh2 := make([]fr.Element, sizeDomainSmall)
   422  	ch1 := make([]fr.Element, sizeDomainSmall)
   423  	ch2 := make([]fr.Element, sizeDomainSmall)
   424  	copy(lh1, lfSortedByt[:sizeDomainSmall])
   425  	copy(lh2, lfSortedByt[sizeDomainSmall-1:])
   426  
   427  	copy(ch1, lfSortedByt[:sizeDomainSmall])
   428  	copy(ch2, lfSortedByt[sizeDomainSmall-1:])
   429  	domainSmall.FFTInverse(ch1, fft.DIF)
   430  	domainSmall.FFTInverse(ch2, fft.DIF)
   431  	fft.BitReverse(ch1)
   432  	fft.BitReverse(ch2)
   433  
   434  	proof.h1, err = kzg.Commit(ch1, pk)
   435  	if err != nil {
   436  		return proof, err
   437  	}
   438  	proof.h2, err = kzg.Commit(ch2, pk)
   439  	if err != nil {
   440  		return proof, err
   441  	}
   442  
   443  	// derive beta, gamma
   444  	beta, err := deriveRandomness(fs, "beta", &proof.t, &proof.f, &proof.h1, &proof.h2)
   445  	if err != nil {
   446  		return proof, err
   447  	}
   448  	gamma, err := deriveRandomness(fs, "gamma")
   449  	if err != nil {
   450  		return proof, err
   451  	}
   452  
   453  	// Compute to Z
   454  	lz := evaluateAccumulationPolynomial(lf, lt, lh1, lh2, beta, gamma)
   455  	cz := make([]fr.Element, len(lz))
   456  	copy(cz, lz)
   457  	domainSmall.FFTInverse(cz, fft.DIF)
   458  	fft.BitReverse(cz)
   459  	proof.z, err = kzg.Commit(cz, pk)
   460  	if err != nil {
   461  		return proof, err
   462  	}
   463  
   464  	// prepare data for computing the quotient
   465  	// compute the numerator
   466  	s := domainSmall.Cardinality
   467  	domainBig := fft.NewDomain(uint64(2 * s))
   468  
   469  	_lz := make([]fr.Element, 2*s)
   470  	_lh1 := make([]fr.Element, 2*s)
   471  	_lh2 := make([]fr.Element, 2*s)
   472  	_lt := make([]fr.Element, 2*s)
   473  	_lf := make([]fr.Element, 2*s)
   474  	copy(_lz, cz)
   475  	copy(_lh1, ch1)
   476  	copy(_lh2, ch2)
   477  	copy(_lt, ct)
   478  	copy(_lf, cf)
   479  	domainBig.FFT(_lz, fft.DIF, fft.OnCoset())
   480  	domainBig.FFT(_lh1, fft.DIF, fft.OnCoset())
   481  	domainBig.FFT(_lh2, fft.DIF, fft.OnCoset())
   482  	domainBig.FFT(_lt, fft.DIF, fft.OnCoset())
   483  	domainBig.FFT(_lf, fft.DIF, fft.OnCoset())
   484  
   485  	// compute h
   486  	lh := evaluateNumBitReversed(_lz, _lh1, _lh2, _lt, _lf, beta, gamma, domainBig)
   487  
   488  	// compute l0*(z-1)
   489  	lh0 := evaluateZStartsByOneBitReversed(_lz, domainBig)
   490  
   491  	// compute ln(z-1)
   492  	lhn := evaluateZEndsByOneBitReversed(_lz, domainBig)
   493  
   494  	// compute ln*(h1-h2(g*X))
   495  	lh1h2 := evaluateOverlapH1h2BitReversed(_lh1, _lh2, domainBig)
   496  
   497  	// compute the quotient
   498  	alpha, err := deriveRandomness(fs, "alpha", &proof.z)
   499  	if err != nil {
   500  		return proof, err
   501  	}
   502  	ch := computeQuotientCanonical(alpha, lh, lh0, lhn, lh1h2, domainBig)
   503  	proof.h, err = kzg.Commit(ch, pk)
   504  	if err != nil {
   505  		return proof, err
   506  	}
   507  
   508  	// build the opening proofs
   509  	nu, err := deriveRandomness(fs, "nu", &proof.h)
   510  	if err != nil {
   511  		return proof, err
   512  	}
   513  	proof.BatchedProof, err = kzg.BatchOpenSinglePoint(
   514  		[][]fr.Element{
   515  			ch1,
   516  			ch2,
   517  			ct,
   518  			cz,
   519  			cf,
   520  			ch,
   521  		},
   522  		[]kzg.Digest{
   523  			proof.h1,
   524  			proof.h2,
   525  			proof.t,
   526  			proof.z,
   527  			proof.f,
   528  			proof.h,
   529  		},
   530  		nu,
   531  		hFunc,
   532  		pk,
   533  	)
   534  	if err != nil {
   535  		return proof, err
   536  	}
   537  
   538  	nu.Mul(&nu, &domainSmall.Generator)
   539  	proof.BatchedProofShifted, err = kzg.BatchOpenSinglePoint(
   540  		[][]fr.Element{
   541  			ch1,
   542  			ch2,
   543  			ct,
   544  			cz,
   545  		},
   546  		[]kzg.Digest{
   547  			proof.h1,
   548  			proof.h2,
   549  			proof.t,
   550  			proof.z,
   551  		},
   552  		nu,
   553  		hFunc,
   554  		pk,
   555  	)
   556  	if err != nil {
   557  		return proof, err
   558  	}
   559  
   560  	return proof, nil
   561  }
   562  
   563  // VerifyLookupVector verifies that a ProofLookupVector proof is correct
   564  func VerifyLookupVector(vk kzg.VerifyingKey, proof ProofLookupVector) error {
   565  
   566  	// hash function that is used for Fiat Shamir
   567  	hFunc := sha256.New()
   568  
   569  	// transcript to derive the challenge
   570  	fs := fiatshamir.NewTranscript(hFunc, "beta", "gamma", "alpha", "nu")
   571  
   572  	// derive the various challenges
   573  	beta, err := deriveRandomness(fs, "beta", &proof.t, &proof.f, &proof.h1, &proof.h2)
   574  	if err != nil {
   575  		return err
   576  	}
   577  
   578  	gamma, err := deriveRandomness(fs, "gamma")
   579  	if err != nil {
   580  		return err
   581  	}
   582  
   583  	alpha, err := deriveRandomness(fs, "alpha", &proof.z)
   584  	if err != nil {
   585  		return err
   586  	}
   587  
   588  	nu, err := deriveRandomness(fs, "nu", &proof.h)
   589  	if err != nil {
   590  		return err
   591  	}
   592  
   593  	// check opening proofs
   594  	err = kzg.BatchVerifySinglePoint(
   595  		[]kzg.Digest{
   596  			proof.h1,
   597  			proof.h2,
   598  			proof.t,
   599  			proof.z,
   600  			proof.f,
   601  			proof.h,
   602  		},
   603  		&proof.BatchedProof,
   604  		nu,
   605  		hFunc,
   606  		vk,
   607  	)
   608  	if err != nil {
   609  		return err
   610  	}
   611  
   612  	// shift the point and verify shifted proof
   613  	var shiftedNu fr.Element
   614  	shiftedNu.Mul(&nu, &proof.g)
   615  	err = kzg.BatchVerifySinglePoint(
   616  		[]kzg.Digest{
   617  			proof.h1,
   618  			proof.h2,
   619  			proof.t,
   620  			proof.z,
   621  		},
   622  		&proof.BatchedProofShifted,
   623  		shiftedNu,
   624  		hFunc,
   625  		vk,
   626  	)
   627  	if err != nil {
   628  		return err
   629  	}
   630  
   631  	// check the generator is correct
   632  	var checkOrder, one fr.Element
   633  	one.SetOne()
   634  	checkOrder.Exp(proof.g, big.NewInt(int64(proof.size/2)))
   635  	if checkOrder.Equal(&one) {
   636  		return ErrGenerator
   637  	}
   638  	checkOrder.Square(&checkOrder)
   639  	if !checkOrder.Equal(&one) {
   640  		return ErrGenerator
   641  	}
   642  
   643  	// check polynomial relation using Schwartz Zippel
   644  	var lhs, rhs, nun, g, _g, a, v, w fr.Element
   645  	g.Exp(proof.g, big.NewInt(int64(proof.size-1)))
   646  
   647  	v.Add(&one, &beta)
   648  	w.Mul(&v, &gamma)
   649  
   650  	// h(ν) where
   651  	// h = (xⁿ⁻¹-1)*z*(1+β)*(γ+f)*(γ(1+β) + t+ β*t(gX)) -
   652  	//		(xⁿ⁻¹-1)*z(gX)*(γ(1+β) + h₁ + β*h₁(gX))*(γ(1+β) + h₂ + β*h₂(gX) )
   653  	lhs.Sub(&nu, &g). // (ν-gⁿ⁻¹)
   654  				Mul(&lhs, &proof.BatchedProof.ClaimedValues[3]).
   655  				Mul(&lhs, &v)
   656  	a.Add(&gamma, &proof.BatchedProof.ClaimedValues[4])
   657  	lhs.Mul(&lhs, &a)
   658  	a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[2]).
   659  		Add(&a, &proof.BatchedProof.ClaimedValues[2]).
   660  		Add(&a, &w)
   661  	lhs.Mul(&lhs, &a)
   662  
   663  	rhs.Sub(&nu, &g).
   664  		Mul(&rhs, &proof.BatchedProofShifted.ClaimedValues[3])
   665  	a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[0]).
   666  		Add(&a, &proof.BatchedProof.ClaimedValues[0]).
   667  		Add(&a, &w)
   668  	rhs.Mul(&rhs, &a)
   669  	a.Mul(&beta, &proof.BatchedProofShifted.ClaimedValues[1]).
   670  		Add(&a, &proof.BatchedProof.ClaimedValues[1]).
   671  		Add(&a, &w)
   672  	rhs.Mul(&rhs, &a)
   673  
   674  	lhs.Sub(&lhs, &rhs)
   675  
   676  	// check consistency of bounds
   677  	var l0, ln, d1, d2 fr.Element
   678  	l0.Exp(nu, big.NewInt(int64(proof.size))).Sub(&l0, &one)
   679  	ln.Set(&l0)
   680  	d1.Sub(&nu, &one)
   681  	d2.Sub(&nu, &g)
   682  	l0.Div(&l0, &d1) // (νⁿ-1)/(ν-1)
   683  	ln.Div(&ln, &d2) // (νⁿ-1)/(ν-gⁿ⁻¹)
   684  
   685  	// l₀*(z-1) = (νⁿ-1)/(ν-1)*(z-1)
   686  	var l0z fr.Element
   687  	l0z.Sub(&proof.BatchedProof.ClaimedValues[3], &one).
   688  		Mul(&l0z, &l0)
   689  
   690  	// lₙ*(z-1) = (νⁿ-1)/(ν-gⁿ⁻¹)*(z-1)
   691  	var lnz fr.Element
   692  	lnz.Sub(&proof.BatchedProof.ClaimedValues[3], &one).
   693  		Mul(&ln, &lnz)
   694  
   695  	// lₙ*(h1 - h₂(g.x))
   696  	var lnh1h2 fr.Element
   697  	lnh1h2.Sub(&proof.BatchedProof.ClaimedValues[0], &proof.BatchedProofShifted.ClaimedValues[1]).
   698  		Mul(&lnh1h2, &ln)
   699  
   700  	// fold the numerator
   701  	lnh1h2.Mul(&lnh1h2, &alpha).
   702  		Add(&lnh1h2, &lnz).
   703  		Mul(&lnh1h2, &alpha).
   704  		Add(&lnh1h2, &l0z).
   705  		Mul(&lnh1h2, &alpha).
   706  		Add(&lnh1h2, &lhs)
   707  
   708  	// (xⁿ-1) * h(x) evaluated at ν
   709  	nun.Exp(nu, big.NewInt(int64(proof.size)))
   710  	_g.Sub(&nun, &one)
   711  	_g.Mul(&proof.BatchedProof.ClaimedValues[5], &_g)
   712  	if !lnh1h2.Equal(&_g) {
   713  		return ErrPlookupVerification
   714  	}
   715  
   716  	return nil
   717  }