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")