github.com/consensys/gnark@v0.11.0/backend/plonk/bls12-381/prove.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 "context" 21 "errors" 22 "fmt" 23 "hash" 24 "math/big" 25 "math/bits" 26 "runtime" 27 "sync" 28 "time" 29 30 "golang.org/x/sync/errgroup" 31 32 "github.com/consensys/gnark-crypto/ecc" 33 34 curve "github.com/consensys/gnark-crypto/ecc/bls12-381" 35 36 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" 37 38 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft" 39 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/hash_to_field" 40 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/iop" 41 42 "github.com/consensys/gnark-crypto/ecc/bls12-381/kzg" 43 fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" 44 "github.com/consensys/gnark/backend" 45 "github.com/consensys/gnark/backend/witness" 46 47 "github.com/consensys/gnark/constraint" 48 cs "github.com/consensys/gnark/constraint/bls12-381" 49 "github.com/consensys/gnark/constraint/solver" 50 fcs "github.com/consensys/gnark/frontend/cs" 51 "github.com/consensys/gnark/internal/utils" 52 "github.com/consensys/gnark/logger" 53 ) 54 55 const ( 56 id_L int = iota 57 id_R 58 id_O 59 id_Z 60 id_ZS 61 id_Ql 62 id_Qr 63 id_Qm 64 id_Qo 65 id_Qk 66 id_S1 67 id_S2 68 id_S3 69 id_ID 70 id_LOne 71 id_Qci // [ .. , Qc_i, Pi_i, ...] 72 ) 73 74 // blinding factors 75 const ( 76 id_Bl int = iota 77 id_Br 78 id_Bo 79 id_Bz 80 nb_blinding_polynomials 81 ) 82 83 // blinding orders (-1 to deactivate) 84 const ( 85 order_blinding_L = 1 86 order_blinding_R = 1 87 order_blinding_O = 1 88 order_blinding_Z = 2 89 ) 90 91 type Proof struct { 92 93 // Commitments to the solution vectors 94 LRO [3]kzg.Digest 95 96 // Commitment to Z, the permutation polynomial 97 Z kzg.Digest 98 99 // Commitments to h1, h2, h3 such that h = h1 + Xⁿ⁺²*h2 + X²⁽ⁿ⁺²⁾*h3 is the quotient polynomial 100 H [3]kzg.Digest 101 102 Bsb22Commitments []kzg.Digest 103 104 // Batch opening proof of linearizedPolynomial, l, r, o, s1, s2, qCPrime 105 BatchedProof kzg.BatchOpeningProof 106 107 // Opening proof of Z at zeta*mu 108 ZShiftedOpening kzg.OpeningProof 109 } 110 111 func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...backend.ProverOption) (*Proof, error) { 112 113 log := logger.Logger().With(). 114 Str("curve", spr.CurveID().String()). 115 Int("nbConstraints", spr.GetNbConstraints()). 116 Str("backend", "plonk").Logger() 117 118 // parse the options 119 opt, err := backend.NewProverConfig(opts...) 120 if err != nil { 121 return nil, fmt.Errorf("get prover options: %w", err) 122 } 123 124 start := time.Now() 125 126 // init instance 127 g, ctx := errgroup.WithContext(context.Background()) 128 instance, err := newInstance(ctx, spr, pk, fullWitness, &opt) 129 if err != nil { 130 return nil, fmt.Errorf("new instance: %w", err) 131 } 132 133 // solve constraints 134 g.Go(instance.solveConstraints) 135 136 // complete qk 137 g.Go(instance.completeQk) 138 139 // init blinding polynomials 140 g.Go(instance.initBlindingPolynomials) 141 142 // derive gamma, beta (copy constraint) 143 g.Go(instance.deriveGammaAndBeta) 144 145 // compute accumulating ratio for the copy constraint 146 g.Go(instance.buildRatioCopyConstraint) 147 148 // compute h 149 g.Go(instance.computeQuotient) 150 151 // open Z (blinded) at ωζ (proof.ZShiftedOpening) 152 g.Go(instance.openZ) 153 154 // linearized polynomial 155 g.Go(instance.computeLinearizedPolynomial) 156 157 // Batch opening 158 g.Go(instance.batchOpening) 159 160 if err := g.Wait(); err != nil { 161 return nil, err 162 } 163 164 log.Debug().Dur("took", time.Since(start)).Msg("prover done") 165 return instance.proof, nil 166 } 167 168 // represents a Prover instance 169 type instance struct { 170 ctx context.Context 171 172 pk *ProvingKey 173 proof *Proof 174 spr *cs.SparseR1CS 175 opt *backend.ProverConfig 176 177 fs *fiatshamir.Transcript 178 kzgFoldingHash hash.Hash // for KZG folding 179 htfFunc hash.Hash // hash to field function 180 181 // polynomials 182 x []*iop.Polynomial // x stores tracks the polynomial we need 183 bp []*iop.Polynomial // blinding polynomials 184 h *iop.Polynomial // h is the quotient polynomial 185 blindedZ []fr.Element // blindedZ is the blinded version of Z 186 quotientShardsRandomizers [2]fr.Element // random elements for blinding the shards of the quotient 187 188 linearizedPolynomial []fr.Element 189 linearizedPolynomialDigest kzg.Digest 190 191 fullWitness witness.Witness 192 193 // bsb22 commitment stuff 194 commitmentInfo constraint.PlonkCommitments 195 commitmentVal []fr.Element 196 cCommitments []*iop.Polynomial 197 198 // challenges 199 gamma, beta, alpha, zeta fr.Element 200 201 // channel to wait for the steps 202 chLRO, 203 chQk, 204 chbp, 205 chZ, 206 chH, 207 chRestoreLRO, 208 chZOpening, 209 chLinearizedPolynomial, 210 chGammaBeta chan struct{} 211 212 domain0, domain1 *fft.Domain 213 214 trace *Trace 215 } 216 217 func newInstance(ctx context.Context, spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts *backend.ProverConfig) (*instance, error) { 218 if opts.HashToFieldFn == nil { 219 opts.HashToFieldFn = hash_to_field.New([]byte("BSB22-Plonk")) 220 } 221 s := instance{ 222 ctx: ctx, 223 pk: pk, 224 proof: &Proof{}, 225 spr: spr, 226 opt: opts, 227 fullWitness: fullWitness, 228 bp: make([]*iop.Polynomial, nb_blinding_polynomials), 229 fs: fiatshamir.NewTranscript(opts.ChallengeHash, "gamma", "beta", "alpha", "zeta"), 230 kzgFoldingHash: opts.KZGFoldingHash, 231 htfFunc: opts.HashToFieldFn, 232 chLRO: make(chan struct{}, 1), 233 chQk: make(chan struct{}, 1), 234 chbp: make(chan struct{}, 1), 235 chGammaBeta: make(chan struct{}, 1), 236 chZ: make(chan struct{}, 1), 237 chH: make(chan struct{}, 1), 238 chZOpening: make(chan struct{}, 1), 239 chLinearizedPolynomial: make(chan struct{}, 1), 240 chRestoreLRO: make(chan struct{}, 1), 241 } 242 s.initBSB22Commitments() 243 s.x = make([]*iop.Polynomial, id_Qci+2*len(s.commitmentInfo)) 244 245 // init fft domains 246 nbConstraints := spr.GetNbConstraints() 247 sizeSystem := uint64(nbConstraints + len(spr.Public)) // len(spr.Public) is for the placeholder constraints 248 s.domain0 = fft.NewDomain(sizeSystem) 249 250 // sampling random numbers for blinding the quotient 251 if opts.StatisticalZK { 252 s.quotientShardsRandomizers[0].SetRandom() 253 s.quotientShardsRandomizers[1].SetRandom() 254 } 255 256 // h, the quotient polynomial is of degree 3(n+1)+2, so it's in a 3(n+2) dim vector space, 257 // the domain is the next power of 2 superior to 3(n+2). 4*domainNum is enough in all cases 258 // except when n<6. 259 if sizeSystem < 6 { 260 s.domain1 = fft.NewDomain(8*sizeSystem, fft.WithoutPrecompute()) 261 } else { 262 s.domain1 = fft.NewDomain(4*sizeSystem, fft.WithoutPrecompute()) 263 } 264 265 // build trace 266 s.trace = NewTrace(spr, s.domain0) 267 268 return &s, nil 269 } 270 271 func (s *instance) initBlindingPolynomials() error { 272 s.bp[id_Bl] = getRandomPolynomial(order_blinding_L) 273 s.bp[id_Br] = getRandomPolynomial(order_blinding_R) 274 s.bp[id_Bo] = getRandomPolynomial(order_blinding_O) 275 s.bp[id_Bz] = getRandomPolynomial(order_blinding_Z) 276 close(s.chbp) 277 return nil 278 } 279 280 func (s *instance) initBSB22Commitments() { 281 s.commitmentInfo = s.spr.CommitmentInfo.(constraint.PlonkCommitments) 282 s.commitmentVal = make([]fr.Element, len(s.commitmentInfo)) // TODO @Tabaie get rid of this 283 s.cCommitments = make([]*iop.Polynomial, len(s.commitmentInfo)) 284 s.proof.Bsb22Commitments = make([]kzg.Digest, len(s.commitmentInfo)) 285 286 // override the hint for the commitment constraints 287 bsb22ID := solver.GetHintID(fcs.Bsb22CommitmentComputePlaceholder) 288 s.opt.SolverOpts = append(s.opt.SolverOpts, solver.OverrideHint(bsb22ID, s.bsb22Hint)) 289 } 290 291 // Computing and verifying Bsb22 multi-commits explained in https://hackmd.io/x8KsadW3RRyX7YTCFJIkHg 292 func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { 293 var err error 294 commDepth := int(ins[0].Int64()) 295 ins = ins[1:] 296 297 res := &s.commitmentVal[commDepth] 298 299 commitmentInfo := s.spr.CommitmentInfo.(constraint.PlonkCommitments)[commDepth] 300 committedValues := make([]fr.Element, s.domain0.Cardinality) 301 offset := s.spr.GetNbPublicVariables() 302 for i := range ins { 303 committedValues[offset+commitmentInfo.Committed[i]].SetBigInt(ins[i]) 304 } 305 if _, err = committedValues[offset+commitmentInfo.CommitmentIndex].SetRandom(); err != nil { // Commitment injection constraint has qcp = 0. Safe to use for blinding. 306 return err 307 } 308 if _, err = committedValues[offset+s.spr.GetNbConstraints()-1].SetRandom(); err != nil { // Last constraint has qcp = 0. Safe to use for blinding 309 return err 310 } 311 s.cCommitments[commDepth] = iop.NewPolynomial(&committedValues, iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}) 312 if s.proof.Bsb22Commitments[commDepth], err = kzg.Commit(s.cCommitments[commDepth].Coefficients(), s.pk.KzgLagrange); err != nil { 313 return err 314 } 315 316 s.htfFunc.Write(s.proof.Bsb22Commitments[commDepth].Marshal()) 317 hashBts := s.htfFunc.Sum(nil) 318 s.htfFunc.Reset() 319 nbBuf := fr.Bytes 320 if s.htfFunc.Size() < fr.Bytes { 321 nbBuf = s.htfFunc.Size() 322 } 323 res.SetBytes(hashBts[:nbBuf]) // TODO @Tabaie use CommitmentIndex for this; create a new variable CommitmentConstraintIndex for other uses 324 res.BigInt(outs[0]) 325 326 return nil 327 } 328 329 // solveConstraints computes the evaluation of the polynomials L, R, O 330 // and sets x[id_L], x[id_R], x[id_O] in Lagrange form 331 func (s *instance) solveConstraints() error { 332 _solution, err := s.spr.Solve(s.fullWitness, s.opt.SolverOpts...) 333 if err != nil { 334 return err 335 } 336 solution := _solution.(*cs.SparseR1CSSolution) 337 evaluationLDomainSmall := []fr.Element(solution.L) 338 evaluationRDomainSmall := []fr.Element(solution.R) 339 evaluationODomainSmall := []fr.Element(solution.O) 340 var wg sync.WaitGroup 341 wg.Add(2) 342 go func() { 343 s.x[id_L] = iop.NewPolynomial(&evaluationLDomainSmall, iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}) 344 wg.Done() 345 }() 346 go func() { 347 s.x[id_R] = iop.NewPolynomial(&evaluationRDomainSmall, iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}) 348 wg.Done() 349 }() 350 351 s.x[id_O] = iop.NewPolynomial(&evaluationODomainSmall, iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}) 352 353 wg.Wait() 354 355 // commit to l, r, o and add blinding factors 356 if err := s.commitToLRO(); err != nil { 357 return err 358 } 359 close(s.chLRO) 360 return nil 361 } 362 363 func (s *instance) completeQk() error { 364 qk := s.trace.Qk.Clone() 365 qkCoeffs := qk.Coefficients() 366 367 wWitness, ok := s.fullWitness.Vector().(fr.Vector) 368 if !ok { 369 return witness.ErrInvalidWitness 370 } 371 372 copy(qkCoeffs, wWitness[:len(s.spr.Public)]) 373 374 // wait for solver to be done 375 select { 376 case <-s.ctx.Done(): 377 return errContextDone 378 case <-s.chLRO: 379 } 380 381 for i := range s.commitmentInfo { 382 qkCoeffs[s.spr.GetNbPublicVariables()+s.commitmentInfo[i].CommitmentIndex] = s.commitmentVal[i] 383 } 384 385 s.x[id_Qk] = qk 386 close(s.chQk) 387 388 return nil 389 } 390 391 func (s *instance) commitToLRO() error { 392 // wait for blinding polynomials to be initialized or context to be done 393 select { 394 case <-s.ctx.Done(): 395 return errContextDone 396 case <-s.chbp: 397 } 398 399 g := new(errgroup.Group) 400 401 g.Go(func() (err error) { 402 s.proof.LRO[0], err = s.commitToPolyAndBlinding(s.x[id_L], s.bp[id_Bl]) 403 return 404 }) 405 406 g.Go(func() (err error) { 407 s.proof.LRO[1], err = s.commitToPolyAndBlinding(s.x[id_R], s.bp[id_Br]) 408 return 409 }) 410 411 g.Go(func() (err error) { 412 s.proof.LRO[2], err = s.commitToPolyAndBlinding(s.x[id_O], s.bp[id_Bo]) 413 return 414 }) 415 416 return g.Wait() 417 } 418 419 // deriveGammaAndBeta (copy constraint) 420 func (s *instance) deriveGammaAndBeta() error { 421 wWitness, ok := s.fullWitness.Vector().(fr.Vector) 422 if !ok { 423 return witness.ErrInvalidWitness 424 } 425 426 if err := bindPublicData(s.fs, "gamma", s.pk.Vk, wWitness[:len(s.spr.Public)]); err != nil { 427 return err 428 } 429 430 // wait for LRO to be committed 431 select { 432 case <-s.ctx.Done(): 433 return errContextDone 434 case <-s.chLRO: 435 } 436 437 gamma, err := deriveRandomness(s.fs, "gamma", &s.proof.LRO[0], &s.proof.LRO[1], &s.proof.LRO[2]) 438 if err != nil { 439 return err 440 } 441 442 bbeta, err := s.fs.ComputeChallenge("beta") 443 if err != nil { 444 return err 445 } 446 s.gamma = gamma 447 s.beta.SetBytes(bbeta) 448 449 close(s.chGammaBeta) 450 451 return nil 452 } 453 454 // commitToPolyAndBlinding computes the KZG commitment of a polynomial p 455 // in Lagrange form (large degree) 456 // and add the contribution of a blinding polynomial b (small degree) 457 // /!\ The polynomial p is supposed to be in Lagrange form. 458 func (s *instance) commitToPolyAndBlinding(p, b *iop.Polynomial) (commit curve.G1Affine, err error) { 459 460 commit, err = kzg.Commit(p.Coefficients(), s.pk.KzgLagrange) 461 462 // we add in the blinding contribution 463 n := int(s.domain0.Cardinality) 464 cb := commitBlindingFactor(n, b, s.pk.Kzg) 465 commit.Add(&commit, &cb) 466 467 return 468 } 469 470 func (s *instance) deriveAlpha() (err error) { 471 alphaDeps := make([]*curve.G1Affine, len(s.proof.Bsb22Commitments)+1) 472 for i := range s.proof.Bsb22Commitments { 473 alphaDeps[i] = &s.proof.Bsb22Commitments[i] 474 } 475 alphaDeps[len(alphaDeps)-1] = &s.proof.Z 476 s.alpha, err = deriveRandomness(s.fs, "alpha", alphaDeps...) 477 return err 478 } 479 480 func (s *instance) deriveZeta() (err error) { 481 s.zeta, err = deriveRandomness(s.fs, "zeta", &s.proof.H[0], &s.proof.H[1], &s.proof.H[2]) 482 return 483 } 484 485 // computeQuotient computes H 486 func (s *instance) computeQuotient() (err error) { 487 s.x[id_Ql] = s.trace.Ql 488 s.x[id_Qr] = s.trace.Qr 489 s.x[id_Qm] = s.trace.Qm 490 s.x[id_Qo] = s.trace.Qo 491 s.x[id_S1] = s.trace.S1 492 s.x[id_S2] = s.trace.S2 493 s.x[id_S3] = s.trace.S3 494 495 for i := 0; i < len(s.commitmentInfo); i++ { 496 s.x[id_Qci+2*i] = s.trace.Qcp[i] 497 } 498 499 n := s.domain0.Cardinality 500 lone := make([]fr.Element, n) 501 lone[0].SetOne() 502 503 // wait for solver to be done 504 select { 505 case <-s.ctx.Done(): 506 return errContextDone 507 case <-s.chLRO: 508 } 509 510 for i := 0; i < len(s.commitmentInfo); i++ { 511 s.x[id_Qci+2*i+1] = s.cCommitments[i] 512 } 513 514 // wait for Z to be committed or context done 515 select { 516 case <-s.ctx.Done(): 517 return errContextDone 518 case <-s.chZ: 519 } 520 521 // derive alpha 522 if err = s.deriveAlpha(); err != nil { 523 return err 524 } 525 526 // TODO complete waste of memory find another way to do that 527 identity := make([]fr.Element, n) 528 identity[1].Set(&s.beta) 529 530 s.x[id_ID] = iop.NewPolynomial(&identity, iop.Form{Basis: iop.Canonical, Layout: iop.Regular}) 531 s.x[id_LOne] = iop.NewPolynomial(&lone, iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}) 532 s.x[id_ZS] = s.x[id_Z].ShallowClone().Shift(1) 533 534 numerator, err := s.computeNumerator() 535 if err != nil { 536 return err 537 } 538 539 s.h, err = divideByZH(numerator, [2]*fft.Domain{s.domain0, s.domain1}) 540 if err != nil { 541 return err 542 } 543 544 // commit to h 545 if err := commitToQuotient(s.h1(), s.h2(), s.h3(), s.proof, s.pk.Kzg); err != nil { 546 return err 547 } 548 549 if err := s.deriveZeta(); err != nil { 550 return err 551 } 552 553 // wait for clean up tasks to be done 554 select { 555 case <-s.ctx.Done(): 556 return errContextDone 557 case <-s.chRestoreLRO: 558 } 559 560 close(s.chH) 561 562 return nil 563 } 564 565 func (s *instance) buildRatioCopyConstraint() (err error) { 566 // wait for gamma and beta to be derived (or ctx.Done()) 567 select { 568 case <-s.ctx.Done(): 569 return errContextDone 570 case <-s.chGammaBeta: 571 } 572 573 // TODO @gbotrel having iop.BuildRatioCopyConstraint return something 574 // with capacity = len() + 4 would avoid extra alloc / copy during openZ 575 s.x[id_Z], err = iop.BuildRatioCopyConstraint( 576 []*iop.Polynomial{ 577 s.x[id_L], 578 s.x[id_R], 579 s.x[id_O], 580 }, 581 s.trace.S, 582 s.beta, 583 s.gamma, 584 iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}, 585 s.domain0, 586 ) 587 if err != nil { 588 return err 589 } 590 591 // commit to the blinded version of z 592 s.proof.Z, err = s.commitToPolyAndBlinding(s.x[id_Z], s.bp[id_Bz]) 593 594 close(s.chZ) 595 596 return 597 } 598 599 // open Z (blinded) at ωζ 600 func (s *instance) openZ() (err error) { 601 // wait for H to be committed and zeta to be derived (or ctx.Done()) 602 select { 603 case <-s.ctx.Done(): 604 return errContextDone 605 case <-s.chH: 606 } 607 var zetaShifted fr.Element 608 zetaShifted.Mul(&s.zeta, &s.pk.Vk.Generator) 609 s.blindedZ = getBlindedCoefficients(s.x[id_Z], s.bp[id_Bz]) 610 // open z at zeta 611 s.proof.ZShiftedOpening, err = kzg.Open(s.blindedZ, zetaShifted, s.pk.Kzg) 612 if err != nil { 613 return err 614 } 615 close(s.chZOpening) 616 return nil 617 } 618 619 func (s *instance) h1() []fr.Element { 620 var h1 []fr.Element 621 if !s.opt.StatisticalZK { 622 h1 = s.h.Coefficients()[:s.domain0.Cardinality+2] 623 } else { 624 h1 = make([]fr.Element, s.domain0.Cardinality+3) 625 copy(h1, s.h.Coefficients()[:s.domain0.Cardinality+2]) 626 h1[s.domain0.Cardinality+2].Set(&s.quotientShardsRandomizers[0]) 627 } 628 return h1 629 } 630 631 func (s *instance) h2() []fr.Element { 632 var h2 []fr.Element 633 if !s.opt.StatisticalZK { 634 h2 = s.h.Coefficients()[s.domain0.Cardinality+2 : 2*(s.domain0.Cardinality+2)] 635 } else { 636 h2 = make([]fr.Element, s.domain0.Cardinality+3) 637 copy(h2, s.h.Coefficients()[s.domain0.Cardinality+2:2*(s.domain0.Cardinality+2)]) 638 h2[0].Sub(&h2[0], &s.quotientShardsRandomizers[0]) 639 h2[s.domain0.Cardinality+2].Set(&s.quotientShardsRandomizers[1]) 640 } 641 return h2 642 } 643 644 func (s *instance) h3() []fr.Element { 645 var h3 []fr.Element 646 if !s.opt.StatisticalZK { 647 h3 = s.h.Coefficients()[2*(s.domain0.Cardinality+2) : 3*(s.domain0.Cardinality+2)] 648 } else { 649 h3 = make([]fr.Element, s.domain0.Cardinality+2) 650 copy(h3, s.h.Coefficients()[2*(s.domain0.Cardinality+2):3*(s.domain0.Cardinality+2)]) 651 h3[0].Sub(&h3[0], &s.quotientShardsRandomizers[1]) 652 } 653 return h3 654 } 655 656 func (s *instance) computeLinearizedPolynomial() error { 657 658 // wait for H to be committed and zeta to be derived (or ctx.Done()) 659 select { 660 case <-s.ctx.Done(): 661 return errContextDone 662 case <-s.chH: 663 } 664 665 qcpzeta := make([]fr.Element, len(s.commitmentInfo)) 666 var blzeta, brzeta, bozeta fr.Element 667 var wg sync.WaitGroup 668 wg.Add(3 + len(s.commitmentInfo)) 669 670 for i := 0; i < len(s.commitmentInfo); i++ { 671 go func(i int) { 672 qcpzeta[i] = s.trace.Qcp[i].Evaluate(s.zeta) 673 wg.Done() 674 }(i) 675 } 676 677 go func() { 678 blzeta = evaluateBlinded(s.x[id_L], s.bp[id_Bl], s.zeta) 679 wg.Done() 680 }() 681 682 go func() { 683 brzeta = evaluateBlinded(s.x[id_R], s.bp[id_Br], s.zeta) 684 wg.Done() 685 }() 686 687 go func() { 688 bozeta = evaluateBlinded(s.x[id_O], s.bp[id_Bo], s.zeta) 689 wg.Done() 690 }() 691 692 // wait for Z to be opened at zeta (or ctx.Done()) 693 select { 694 case <-s.ctx.Done(): 695 return errContextDone 696 case <-s.chZOpening: 697 } 698 bzuzeta := s.proof.ZShiftedOpening.ClaimedValue 699 700 wg.Wait() 701 702 s.linearizedPolynomial = s.innerComputeLinearizedPoly( 703 blzeta, 704 brzeta, 705 bozeta, 706 s.alpha, 707 s.beta, 708 s.gamma, 709 s.zeta, 710 bzuzeta, 711 qcpzeta, 712 s.blindedZ, 713 coefficients(s.cCommitments), 714 s.pk, 715 ) 716 717 var err error 718 s.linearizedPolynomialDigest, err = kzg.Commit(s.linearizedPolynomial, s.pk.Kzg, runtime.NumCPU()*2) 719 if err != nil { 720 return err 721 } 722 close(s.chLinearizedPolynomial) 723 return nil 724 } 725 726 func (s *instance) batchOpening() error { 727 728 // wait for linearizedPolynomial to be computed (or ctx.Done()) 729 select { 730 case <-s.ctx.Done(): 731 return errContextDone 732 case <-s.chLinearizedPolynomial: 733 } 734 735 polysQcp := coefficients(s.trace.Qcp) 736 polysToOpen := make([][]fr.Element, 6+len(polysQcp)) 737 copy(polysToOpen[6:], polysQcp) 738 739 polysToOpen[0] = s.linearizedPolynomial 740 polysToOpen[1] = getBlindedCoefficients(s.x[id_L], s.bp[id_Bl]) 741 polysToOpen[2] = getBlindedCoefficients(s.x[id_R], s.bp[id_Br]) 742 polysToOpen[3] = getBlindedCoefficients(s.x[id_O], s.bp[id_Bo]) 743 polysToOpen[4] = s.trace.S1.Coefficients() 744 polysToOpen[5] = s.trace.S2.Coefficients() 745 746 digestsToOpen := make([]curve.G1Affine, len(s.pk.Vk.Qcp)+6) 747 copy(digestsToOpen[6:], s.pk.Vk.Qcp) 748 749 digestsToOpen[0] = s.linearizedPolynomialDigest 750 digestsToOpen[1] = s.proof.LRO[0] 751 digestsToOpen[2] = s.proof.LRO[1] 752 digestsToOpen[3] = s.proof.LRO[2] 753 digestsToOpen[4] = s.pk.Vk.S[0] 754 digestsToOpen[5] = s.pk.Vk.S[1] 755 756 var err error 757 s.proof.BatchedProof, err = kzg.BatchOpenSinglePoint( 758 polysToOpen, 759 digestsToOpen, 760 s.zeta, 761 s.kzgFoldingHash, 762 s.pk.Kzg, 763 s.proof.ZShiftedOpening.ClaimedValue.Marshal(), 764 ) 765 766 return err 767 } 768 769 // evaluate the full set of constraints, all polynomials in x are back in 770 // canonical regular form at the end 771 func (s *instance) computeNumerator() (*iop.Polynomial, error) { 772 // init vectors that are used multiple times throughout the computation 773 n := s.domain0.Cardinality 774 twiddles0 := make([]fr.Element, n) 775 if n == 1 { 776 // edge case 777 twiddles0[0].SetOne() 778 } else { 779 twiddles, err := s.domain0.Twiddles() 780 if err != nil { 781 return nil, err 782 } 783 copy(twiddles0, twiddles[0]) 784 w := twiddles0[1] 785 for i := len(twiddles[0]); i < len(twiddles0); i++ { 786 twiddles0[i].Mul(&twiddles0[i-1], &w) 787 } 788 } 789 790 // wait for chQk to be closed (or ctx.Done()) 791 select { 792 case <-s.ctx.Done(): 793 return nil, errContextDone 794 case <-s.chQk: 795 } 796 797 nbBsbGates := len(s.proof.Bsb22Commitments) 798 799 gateConstraint := func(u ...fr.Element) fr.Element { 800 801 var ic, tmp fr.Element 802 803 ic.Mul(&u[id_Ql], &u[id_L]) 804 tmp.Mul(&u[id_Qr], &u[id_R]) 805 ic.Add(&ic, &tmp) 806 tmp.Mul(&u[id_Qm], &u[id_L]).Mul(&tmp, &u[id_R]) 807 ic.Add(&ic, &tmp) 808 tmp.Mul(&u[id_Qo], &u[id_O]) 809 ic.Add(&ic, &tmp).Add(&ic, &u[id_Qk]) 810 for i := 0; i < nbBsbGates; i++ { 811 tmp.Mul(&u[id_Qci+2*i], &u[id_Qci+2*i+1]) 812 ic.Add(&ic, &tmp) 813 } 814 815 return ic 816 } 817 818 var cs, css fr.Element 819 cs.Set(&s.domain1.FrMultiplicativeGen) 820 css.Square(&cs) 821 822 orderingConstraint := func(u ...fr.Element) fr.Element { 823 gamma := s.gamma 824 825 var a, b, c, r, l fr.Element 826 827 a.Add(&gamma, &u[id_L]).Add(&a, &u[id_ID]) 828 b.Mul(&u[id_ID], &cs).Add(&b, &u[id_R]).Add(&b, &gamma) 829 c.Mul(&u[id_ID], &css).Add(&c, &u[id_O]).Add(&c, &gamma) 830 r.Mul(&a, &b).Mul(&r, &c).Mul(&r, &u[id_Z]) 831 832 a.Add(&u[id_S1], &u[id_L]).Add(&a, &gamma) 833 b.Add(&u[id_S2], &u[id_R]).Add(&b, &gamma) 834 c.Add(&u[id_S3], &u[id_O]).Add(&c, &gamma) 835 l.Mul(&a, &b).Mul(&l, &c).Mul(&l, &u[id_ZS]) 836 837 l.Sub(&l, &r) 838 839 return l 840 } 841 842 ratioLocalConstraint := func(u ...fr.Element) fr.Element { 843 844 var res fr.Element 845 res.SetOne() 846 res.Sub(&u[id_Z], &res).Mul(&res, &u[id_LOne]) 847 848 return res 849 } 850 851 rho := int(s.domain1.Cardinality / n) 852 shifters := make([]fr.Element, rho) 853 shifters[0].Set(&s.domain1.FrMultiplicativeGen) 854 for i := 1; i < rho; i++ { 855 shifters[i].Set(&s.domain1.Generator) 856 } 857 858 // stores the current coset shifter 859 var coset fr.Element 860 coset.SetOne() 861 862 var tmp, one fr.Element 863 one.SetOne() 864 bn := big.NewInt(int64(n)) 865 866 cosetTable, err := s.domain0.CosetTable() 867 if err != nil { 868 return nil, err 869 } 870 871 // init the result polynomial & buffer 872 cres := make([]fr.Element, s.domain1.Cardinality) 873 buf := make([]fr.Element, n) 874 var wgBuf sync.WaitGroup 875 876 allConstraints := func(i int, u ...fr.Element) fr.Element { 877 // scale S1, S2, S3 by β 878 u[id_S1].Mul(&u[id_S1], &s.beta) 879 u[id_S2].Mul(&u[id_S2], &s.beta) 880 u[id_S3].Mul(&u[id_S3], &s.beta) 881 882 // blind L, R, O, Z, ZS 883 var y fr.Element 884 y = s.bp[id_Bl].Evaluate(twiddles0[i]) 885 u[id_L].Add(&u[id_L], &y) 886 y = s.bp[id_Br].Evaluate(twiddles0[i]) 887 u[id_R].Add(&u[id_R], &y) 888 y = s.bp[id_Bo].Evaluate(twiddles0[i]) 889 u[id_O].Add(&u[id_O], &y) 890 y = s.bp[id_Bz].Evaluate(twiddles0[i]) 891 u[id_Z].Add(&u[id_Z], &y) 892 893 // ZS is shifted by 1; need to get correct twiddle 894 y = s.bp[id_Bz].Evaluate(twiddles0[(i+1)%int(n)]) 895 u[id_ZS].Add(&u[id_ZS], &y) 896 897 a := gateConstraint(u...) 898 b := orderingConstraint(u...) 899 c := ratioLocalConstraint(u...) 900 c.Mul(&c, &s.alpha).Add(&c, &b).Mul(&c, &s.alpha).Add(&c, &a) 901 return c 902 } 903 904 // for the first iteration, the scalingVector is the coset table 905 scalingVector := cosetTable 906 scalingVectorRev := make([]fr.Element, len(cosetTable)) 907 copy(scalingVectorRev, cosetTable) 908 fft.BitReverse(scalingVectorRev) 909 910 // pre-computed to compute the bit reverse index 911 // of the result polynomial 912 m := uint64(s.domain1.Cardinality) 913 mm := uint64(64 - bits.TrailingZeros64(m)) 914 915 for i := 0; i < rho; i++ { 916 917 coset.Mul(&coset, &shifters[i]) 918 tmp.Exp(coset, bn).Sub(&tmp, &one) 919 920 // bl <- bl *( (s*ωⁱ)ⁿ-1 )s 921 for _, q := range s.bp { 922 cq := q.Coefficients() 923 acc := tmp 924 for j := 0; j < len(cq); j++ { 925 cq[j].Mul(&cq[j], &acc) 926 acc.Mul(&acc, &shifters[i]) 927 } 928 } 929 if i == 1 { 930 // we have to update the scalingVector; instead of scaling by 931 // cosets we scale by the twiddles of the large domain. 932 w := s.domain1.Generator 933 scalingVector = make([]fr.Element, n) 934 fft.BuildExpTable(w, scalingVector) 935 936 // reuse memory 937 copy(scalingVectorRev, scalingVector) 938 fft.BitReverse(scalingVectorRev) 939 } 940 941 // we do **a lot** of FFT here, but on the small domain. 942 // note that for all the polynomials in the proving key 943 // (Ql, Qr, Qm, Qo, S1, S2, S3, Qcp, Qc) and ID, LOne 944 // we could pre-compute these rho*2 FFTs and store them 945 // at the cost of a huge memory footprint. 946 batchApply(s.x, func(p *iop.Polynomial) { 947 nbTasks := calculateNbTasks(len(s.x)-1) * 2 948 // shift polynomials to be in the correct coset 949 p.ToCanonical(s.domain0, nbTasks) 950 951 // scale by shifter[i] 952 var w []fr.Element 953 if p.Layout == iop.Regular { 954 w = scalingVector 955 } else { 956 w = scalingVectorRev 957 } 958 959 cp := p.Coefficients() 960 utils.Parallelize(len(cp), func(start, end int) { 961 for j := start; j < end; j++ { 962 cp[j].Mul(&cp[j], &w[j]) 963 } 964 }, nbTasks) 965 966 // fft in the correct coset 967 p.ToLagrange(s.domain0, nbTasks).ToRegular() 968 }) 969 970 wgBuf.Wait() 971 if _, err := iop.Evaluate( 972 allConstraints, 973 buf, 974 iop.Form{Basis: iop.Lagrange, Layout: iop.Regular}, 975 s.x..., 976 ); err != nil { 977 return nil, err 978 } 979 wgBuf.Add(1) 980 go func(i int) { 981 for j := 0; j < int(n); j++ { 982 // we build the polynomial in bit reverse order 983 cres[bits.Reverse64(uint64(rho*j+i))>>mm] = buf[j] 984 } 985 wgBuf.Done() 986 }(i) 987 988 tmp.Inverse(&tmp) 989 // bl <- bl *( (s*ωⁱ)ⁿ-1 )s 990 for _, q := range s.bp { 991 cq := q.Coefficients() 992 for j := 0; j < len(cq); j++ { 993 cq[j].Mul(&cq[j], &tmp) 994 } 995 } 996 } 997 998 // scale everything back 999 go func() { 1000 s.x[id_ID] = nil 1001 s.x[id_LOne] = nil 1002 s.x[id_ZS] = nil 1003 s.x[id_Qk] = nil 1004 1005 var cs fr.Element 1006 cs.Set(&shifters[0]) 1007 for i := 1; i < len(shifters); i++ { 1008 cs.Mul(&cs, &shifters[i]) 1009 } 1010 cs.Inverse(&cs) 1011 1012 batchApply(s.x, func(p *iop.Polynomial) { 1013 if p == nil { 1014 return 1015 } 1016 p.ToCanonical(s.domain0, 8).ToRegular() 1017 scalePowers(p, cs) 1018 }) 1019 1020 for _, q := range s.bp { 1021 scalePowers(q, cs) 1022 } 1023 1024 close(s.chRestoreLRO) 1025 }() 1026 1027 // ensure all the goroutines are done 1028 wgBuf.Wait() 1029 1030 res := iop.NewPolynomial(&cres, iop.Form{Basis: iop.LagrangeCoset, Layout: iop.BitReverse}) 1031 1032 return res, nil 1033 1034 } 1035 1036 func calculateNbTasks(n int) int { 1037 nbAvailableCPU := runtime.NumCPU() - n 1038 if nbAvailableCPU < 0 { 1039 nbAvailableCPU = 1 1040 } 1041 nbTasks := 1 + (nbAvailableCPU / n) 1042 return nbTasks 1043 } 1044 1045 // batchApply executes fn on all polynomials in x except x[id_ZS] in parallel. 1046 func batchApply(x []*iop.Polynomial, fn func(*iop.Polynomial)) { 1047 var wg sync.WaitGroup 1048 for i := 0; i < len(x); i++ { 1049 if i == id_ZS { 1050 continue 1051 } 1052 wg.Add(1) 1053 go func(i int) { 1054 fn(x[i]) 1055 wg.Done() 1056 }(i) 1057 } 1058 wg.Wait() 1059 } 1060 1061 // p <- <p, (1, w, .., wⁿ) > 1062 // p is supposed to be in canonical form 1063 func scalePowers(p *iop.Polynomial, w fr.Element) { 1064 var acc fr.Element 1065 acc.SetOne() 1066 cp := p.Coefficients() 1067 for i := 0; i < p.Size(); i++ { 1068 cp[i].Mul(&cp[i], &acc) 1069 acc.Mul(&acc, &w) 1070 } 1071 } 1072 1073 func evaluateBlinded(p, bp *iop.Polynomial, zeta fr.Element) fr.Element { 1074 // Get the size of the polynomial 1075 n := big.NewInt(int64(p.Size())) 1076 1077 var pEvaluatedAtZeta fr.Element 1078 1079 // Evaluate the polynomial and blinded polynomial at zeta 1080 chP := make(chan struct{}, 1) 1081 go func() { 1082 pEvaluatedAtZeta = p.Evaluate(zeta) 1083 close(chP) 1084 }() 1085 1086 bpEvaluatedAtZeta := bp.Evaluate(zeta) 1087 1088 // Multiply the evaluated blinded polynomial by tempElement 1089 var t fr.Element 1090 one := fr.One() 1091 t.Exp(zeta, n).Sub(&t, &one) 1092 bpEvaluatedAtZeta.Mul(&bpEvaluatedAtZeta, &t) 1093 1094 // Add the evaluated polynomial and the evaluated blinded polynomial 1095 <-chP 1096 pEvaluatedAtZeta.Add(&pEvaluatedAtZeta, &bpEvaluatedAtZeta) 1097 1098 // Return the result 1099 return pEvaluatedAtZeta 1100 } 1101 1102 // /!\ modifies the size 1103 func getBlindedCoefficients(p, bp *iop.Polynomial) []fr.Element { 1104 cp := p.Coefficients() 1105 cbp := bp.Coefficients() 1106 cp = append(cp, cbp...) 1107 for i := 0; i < len(cbp); i++ { 1108 cp[i].Sub(&cp[i], &cbp[i]) 1109 } 1110 return cp 1111 } 1112 1113 // commits to a polynomial of the form b*(Xⁿ-1) where b is of small degree 1114 func commitBlindingFactor(n int, b *iop.Polynomial, key kzg.ProvingKey) curve.G1Affine { 1115 cp := b.Coefficients() 1116 np := b.Size() 1117 1118 // lo 1119 var tmp curve.G1Affine 1120 tmp.MultiExp(key.G1[:np], cp, ecc.MultiExpConfig{}) 1121 1122 // hi 1123 var res curve.G1Affine 1124 res.MultiExp(key.G1[n:n+np], cp, ecc.MultiExpConfig{}) 1125 res.Sub(&res, &tmp) 1126 return res 1127 } 1128 1129 // return a random polynomial of degree n, if n==-1 cancel the blinding 1130 func getRandomPolynomial(n int) *iop.Polynomial { 1131 var a []fr.Element 1132 if n == -1 { 1133 a := make([]fr.Element, 1) 1134 a[0].SetZero() 1135 } else { 1136 a = make([]fr.Element, n+1) 1137 for i := 0; i <= n; i++ { 1138 a[i].SetRandom() 1139 } 1140 } 1141 res := iop.NewPolynomial(&a, iop.Form{ 1142 Basis: iop.Canonical, Layout: iop.Regular}) 1143 return res 1144 } 1145 1146 func coefficients(p []*iop.Polynomial) [][]fr.Element { 1147 res := make([][]fr.Element, len(p)) 1148 for i, pI := range p { 1149 res[i] = pI.Coefficients() 1150 } 1151 return res 1152 } 1153 1154 func commitToQuotient(h1, h2, h3 []fr.Element, proof *Proof, kzgPk kzg.ProvingKey) error { 1155 g := new(errgroup.Group) 1156 1157 g.Go(func() (err error) { 1158 proof.H[0], err = kzg.Commit(h1, kzgPk) 1159 return 1160 }) 1161 1162 g.Go(func() (err error) { 1163 proof.H[1], err = kzg.Commit(h2, kzgPk) 1164 return 1165 }) 1166 1167 g.Go(func() (err error) { 1168 proof.H[2], err = kzg.Commit(h3, kzgPk) 1169 return 1170 }) 1171 1172 return g.Wait() 1173 } 1174 1175 // divideByZH 1176 // The input must be in LagrangeCoset. 1177 // The result is in Canonical Regular. (in place using a) 1178 func divideByZH(a *iop.Polynomial, domains [2]*fft.Domain) (*iop.Polynomial, error) { 1179 1180 // check that the basis is LagrangeCoset 1181 if a.Basis != iop.LagrangeCoset || a.Layout != iop.BitReverse { 1182 return nil, errors.New("invalid form") 1183 } 1184 1185 // prepare the evaluations of x^n-1 on the big domain's coset 1186 xnMinusOneInverseLagrangeCoset := evaluateXnMinusOneDomainBigCoset(domains) 1187 rho := int(domains[1].Cardinality / domains[0].Cardinality) 1188 1189 r := a.Coefficients() 1190 n := uint64(len(r)) 1191 nn := uint64(64 - bits.TrailingZeros64(n)) 1192 1193 utils.Parallelize(len(r), func(start, end int) { 1194 for i := start; i < end; i++ { 1195 iRev := bits.Reverse64(uint64(i)) >> nn 1196 r[i].Mul(&r[i], &xnMinusOneInverseLagrangeCoset[int(iRev)%rho]) 1197 } 1198 }) 1199 1200 // since a is in bit reverse order, ToRegular shouldn't do anything 1201 a.ToCanonical(domains[1]).ToRegular() 1202 1203 return a, nil 1204 1205 } 1206 1207 // evaluateXnMinusOneDomainBigCoset evaluates Xᵐ-1 on DomainBig coset 1208 func evaluateXnMinusOneDomainBigCoset(domains [2]*fft.Domain) []fr.Element { 1209 1210 rho := domains[1].Cardinality / domains[0].Cardinality 1211 1212 res := make([]fr.Element, rho) 1213 1214 expo := big.NewInt(int64(domains[0].Cardinality)) 1215 res[0].Exp(domains[1].FrMultiplicativeGen, expo) 1216 1217 var t fr.Element 1218 t.Exp(domains[1].Generator, expo) 1219 1220 one := fr.One() 1221 1222 for i := 1; i < int(rho); i++ { 1223 res[i].Mul(&res[i-1], &t) 1224 res[i-1].Sub(&res[i-1], &one) 1225 } 1226 res[len(res)-1].Sub(&res[len(res)-1], &one) 1227 1228 res = fr.BatchInvert(res) 1229 1230 return res 1231 } 1232 1233 // innerComputeLinearizedPoly computes the linearized polynomial in canonical basis. 1234 // The purpose is to commit and open all in one ql, qr, qm, qo, qk. 1235 // * lZeta, rZeta, oZeta are the evaluation of l, r, o at zeta 1236 // * z is the permutation polynomial, zu is Z(μX), the shifted version of Z 1237 // * pk is the proving key: the linearized polynomial is a linear combination of ql, qr, qm, qo, qk. 1238 // 1239 // The Linearized polynomial is: 1240 // 1241 // α²*L₁(ζ)*Z(X) 1242 // + α*( (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(β*s3(X))*Z(μζ) - Z(X)*(l(ζ)+β*id1(ζ)+γ)*(r(ζ)+β*id2(ζ)+γ)*(o(ζ)+β*id3(ζ)+γ)) 1243 // + l(ζ)*Ql(X) + l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) 1244 // - Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) 1245 // 1246 // /!\ blindedZCanonical is modified 1247 func (s *instance) innerComputeLinearizedPoly(lZeta, rZeta, oZeta, alpha, beta, gamma, zeta, zu fr.Element, qcpZeta, blindedZCanonical []fr.Element, pi2Canonical [][]fr.Element, pk *ProvingKey) []fr.Element { 1248 1249 // l(ζ)r(ζ) 1250 var rl fr.Element 1251 rl.Mul(&rZeta, &lZeta) 1252 1253 // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) 1254 // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) 1255 // the linearised polynomial is 1256 // α²*L₁(ζ)*Z(X) + 1257 // s1*s3(X)+s2*Z(X) + l(ζ)*Ql(X) + 1258 // l(ζ)r(ζ)*Qm(X) + r(ζ)*Qr(X) + o(ζ)*Qo(X) + Qk(X) + ∑ᵢQcp_(ζ)Pi_(X) - 1259 // Z_{H}(ζ)*((H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) 1260 var s1, s2 fr.Element 1261 chS1 := make(chan struct{}, 1) 1262 go func() { 1263 s1 = s.trace.S1.Evaluate(zeta) // s1(ζ) 1264 s1.Mul(&s1, &beta).Add(&s1, &lZeta).Add(&s1, &gamma) // (l(ζ)+β*s1(ζ)+γ) 1265 close(chS1) 1266 }() 1267 1268 tmp := s.trace.S2.Evaluate(zeta) // s2(ζ) 1269 tmp.Mul(&tmp, &beta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*s2(ζ)+γ) 1270 <-chS1 1271 s1.Mul(&s1, &tmp).Mul(&s1, &zu).Mul(&s1, &beta).Mul(&s1, &alpha) // (l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*β*Z(μζ)*α 1272 1273 var uzeta, uuzeta fr.Element 1274 uzeta.Mul(&zeta, &pk.Vk.CosetShift) 1275 uuzeta.Mul(&uzeta, &pk.Vk.CosetShift) 1276 1277 s2.Mul(&beta, &zeta).Add(&s2, &lZeta).Add(&s2, &gamma) // (l(ζ)+β*ζ+γ) 1278 tmp.Mul(&beta, &uzeta).Add(&tmp, &rZeta).Add(&tmp, &gamma) // (r(ζ)+β*u*ζ+γ) 1279 s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ) 1280 tmp.Mul(&beta, &uuzeta).Add(&tmp, &oZeta).Add(&tmp, &gamma) // (o(ζ)+β*u²*ζ+γ) 1281 s2.Mul(&s2, &tmp) // (l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) 1282 s2.Neg(&s2).Mul(&s2, &alpha) 1283 1284 // Z_h(ζ), ζⁿ⁺², L₁(ζ)*α²*Z 1285 var zhZeta, zetaNPlusTwo, alphaSquareLagrangeZero, one, den, frNbElmt fr.Element 1286 one.SetOne() 1287 nbElmt := int64(s.domain0.Cardinality) 1288 alphaSquareLagrangeZero.Set(&zeta).Exp(alphaSquareLagrangeZero, big.NewInt(nbElmt)) // ζⁿ 1289 zetaNPlusTwo.Mul(&alphaSquareLagrangeZero, &zeta).Mul(&zetaNPlusTwo, &zeta) // ζⁿ⁺² 1290 alphaSquareLagrangeZero.Sub(&alphaSquareLagrangeZero, &one) // ζⁿ - 1 1291 zhZeta.Set(&alphaSquareLagrangeZero) // Z_h(ζ) = ζⁿ - 1 1292 frNbElmt.SetUint64(uint64(nbElmt)) 1293 den.Sub(&zeta, &one).Inverse(&den) // 1/(ζ-1) 1294 alphaSquareLagrangeZero.Mul(&alphaSquareLagrangeZero, &den). // L₁ = (ζⁿ - 1)/(ζ-1) 1295 Mul(&alphaSquareLagrangeZero, &alpha). 1296 Mul(&alphaSquareLagrangeZero, &alpha). 1297 Mul(&alphaSquareLagrangeZero, &s.domain0.CardinalityInv) // α²*L₁(ζ) 1298 1299 s3canonical := s.trace.S3.Coefficients() 1300 1301 s.trace.Qk.ToCanonical(s.domain0).ToRegular() 1302 1303 // len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 when Statistical ZK is activated 1304 // len(h1)=len(h2)=len(h3)=len(blindedZCanonical)-1 when Statistical ZK is deactivated 1305 h1 := s.h1() 1306 h2 := s.h2() 1307 h3 := s.h3() 1308 1309 // at this stage we have 1310 // s1 = α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ) 1311 // s2 = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) 1312 utils.Parallelize(len(blindedZCanonical), func(start, end int) { 1313 1314 cql := s.trace.Ql.Coefficients() 1315 cqr := s.trace.Qr.Coefficients() 1316 cqm := s.trace.Qm.Coefficients() 1317 cqo := s.trace.Qo.Coefficients() 1318 cqk := s.trace.Qk.Coefficients() 1319 1320 var t, t0, t1 fr.Element 1321 1322 for i := start; i < end; i++ { 1323 t.Mul(&blindedZCanonical[i], &s2) // -Z(X)*α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) 1324 if i < len(s3canonical) { 1325 t0.Mul(&s3canonical[i], &s1) // α*(l(ζ)+β*s1(β)+γ)*(r(ζ)+β*s2(β)+γ)*β*Z(μζ)*β*s3(X) 1326 t.Add(&t, &t0) 1327 } 1328 if i < len(cqm) { 1329 t1.Mul(&cqm[i], &rl) // l(ζ)r(ζ)*Qm(X) 1330 t.Add(&t, &t1) // linPol += l(ζ)r(ζ)*Qm(X) 1331 t0.Mul(&cql[i], &lZeta) // l(ζ)Q_l(X) 1332 t.Add(&t, &t0) // linPol += l(ζ)*Ql(X) 1333 t0.Mul(&cqr[i], &rZeta) //r(ζ)*Qr(X) 1334 t.Add(&t, &t0) // linPol += r(ζ)*Qr(X) 1335 t0.Mul(&cqo[i], &oZeta) // o(ζ)*Qo(X) 1336 t.Add(&t, &t0) // linPol += o(ζ)*Qo(X) 1337 t.Add(&t, &cqk[i]) // linPol += Qk(X) 1338 for j := range qcpZeta { // linPol += ∑ᵢQcp_(ζ)Pi_(X) 1339 t0.Mul(&pi2Canonical[j][i], &qcpZeta[j]) 1340 t.Add(&t, &t0) 1341 } 1342 } 1343 1344 t0.Mul(&blindedZCanonical[i], &alphaSquareLagrangeZero) // α²L₁(ζ)Z(X) 1345 blindedZCanonical[i].Add(&t, &t0) // linPol += α²L₁(ζ)Z(X) 1346 1347 // if statistical zeroknowledge is deactivated, len(h1)=len(h2)=len(h3)=len(blindedZ)-1. 1348 // Else len(h1)=len(h2)=len(blindedZCanonical)=len(h3)+1 1349 if i < len(h3) { 1350 t.Mul(&h3[i], &zetaNPlusTwo). 1351 Add(&t, &h2[i]). 1352 Mul(&t, &zetaNPlusTwo). 1353 Add(&t, &h1[i]). 1354 Mul(&t, &zhZeta) 1355 blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) 1356 } else { 1357 if s.opt.StatisticalZK { 1358 t.Mul(&h2[i], &zetaNPlusTwo). 1359 Add(&t, &h1[i]). 1360 Mul(&t, &zhZeta) 1361 blindedZCanonical[i].Sub(&blindedZCanonical[i], &t) // linPol -= Z_h(ζ)*(H₀(X) + ζᵐ⁺²*H₁(X) + ζ²⁽ᵐ⁺²⁾*H₂(X)) 1362 } 1363 } 1364 } 1365 }) 1366 1367 return blindedZCanonical 1368 } 1369 1370 var errContextDone = errors.New("context done")