github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-377/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-377/fr" 27 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/fft" 28 "github.com/consensys/gnark-crypto/ecc/bls12-377/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, <[i+1]). 95 Add(&b, <[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 }