github.com/consensys/gnark@v0.11.0/backend/plonk/bn254/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/bn254"
    35  
    36  	"github.com/consensys/gnark-crypto/ecc/bn254/fr"
    37  
    38  	"github.com/consensys/gnark-crypto/ecc/bn254/fr/fft"
    39  	"github.com/consensys/gnark-crypto/ecc/bn254/fr/hash_to_field"
    40  	"github.com/consensys/gnark-crypto/ecc/bn254/fr/iop"
    41  
    42  	"github.com/consensys/gnark-crypto/ecc/bn254/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/bn254"
    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")