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