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