github.com/consensys/gnark@v0.11.0/internal/generator/backend/template/zkpschemes/plonkfri/plonk.prove.go.tmpl (about)

     1  import (
     2  	"math/big"
     3  	"math/bits"
     4  	"runtime"
     5  
     6  	"github.com/consensys/gnark/backend/witness"
     7  
     8  	{{ template "import_fr" . }}
     9  	{{ template "import_fft" . }}
    10  	{{ template "import_backend_cs" . }}
    11      {{ template "import_fri" . }}
    12  	
    13      "github.com/consensys/gnark/internal/utils"
    14  	"github.com/consensys/gnark/backend"
    15  	fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir"
    16  )
    17  
    18  type Proof struct {
    19  	// commitments to the solution vectors
    20  	LROpp [3]fri.ProofOfProximity
    21  
    22  	// commitment to Z (permutation polynomial)
    23  	// Z   Commitment
    24  	Zpp fri.ProofOfProximity
    25  
    26  	// commitment to h1,h2,h3 such that h = h1 + X**n*h2 + X**2nh3 the quotient polynomial
    27  	Hpp [3]fri.ProofOfProximity
    28  
    29  	// opening proofs for L, R, O
    30  	OpeningsLROmp [3]fri.OpeningProof
    31  
    32  	// opening proofs for Z, Zu
    33  	OpeningsZmp [2]fri.OpeningProof
    34  
    35  	// opening proof for H
    36  	OpeningsHmp [3]fri.OpeningProof
    37  
    38  	// opening proofs for ql, qr, qm, qo, qk
    39  	OpeningsQlQrQmQoQkincompletemp [5]fri.OpeningProof
    40  
    41  	// openings of S1, S2, S3
    42  	// OpeningsS1S2S3   [3]OpeningProof
    43  	OpeningsS1S2S3mp [3]fri.OpeningProof
    44  
    45  	// openings of Id1, Id2, Id3
    46  	OpeningsId1Id2Id3mp [3]fri.OpeningProof
    47  }
    48  
    49  func Prove(spr *cs.SparseR1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...backend.ProverOption) (*Proof, error) {
    50  	opt, err := backend.NewProverConfig(opts...)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	var proof Proof
    56  
    57  	// 0 - Fiat Shamir
    58  	fs := fiatshamir.NewTranscript(opt.ChallengeHash, "gamma", "beta", "alpha", "zeta")
    59  
    60  	// 1 - solve the system
    61  	_solution, err := spr.Solve(fullWitness, opt.SolverOpts...)
    62  	if err != nil {
    63  		return nil, err
    64  	} 
    65  	
    66  	solution := _solution.(*cs.SparseR1CSSolution)
    67  	evaluationLDomainSmall := []fr.Element(solution.L)
    68  	evaluationRDomainSmall := []fr.Element(solution.R)
    69  	evaluationODomainSmall := []fr.Element(solution.O)
    70  
    71  	// 2 - commit to lro
    72  	blindedLCanonical, blindedRCanonical, blindedOCanonical, err := computeBlindedLROCanonical(
    73  		evaluationLDomainSmall,
    74  		evaluationRDomainSmall,
    75  		evaluationODomainSmall,
    76  		&pk.Domain[0])
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	proof.LROpp[0], err = pk.Vk.Iopp.BuildProofOfProximity(blindedLCanonical)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	proof.LROpp[1], err = pk.Vk.Iopp.BuildProofOfProximity(blindedRCanonical)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	proof.LROpp[2], err = pk.Vk.Iopp.BuildProofOfProximity(blindedOCanonical)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	// 3 - compute Z, challenges are derived using L, R, O + public inputs
    94  	fw, ok := fullWitness.Vector().(fr.Vector)
    95  	if !ok {
    96  		return nil, witness.ErrInvalidWitness
    97  	}
    98  	dataFiatShamir := make([][fr.Bytes]byte, len(spr.Public)+3)
    99  	for i := 0; i < len(spr.Public); i++ {
   100  		copy(dataFiatShamir[i][:], fw[i].Marshal())
   101  	}
   102  	copy(dataFiatShamir[len(spr.Public)][:], proof.LROpp[0].ID)
   103  	copy(dataFiatShamir[len(spr.Public)+1][:], proof.LROpp[1].ID)
   104  	copy(dataFiatShamir[len(spr.Public)+2][:], proof.LROpp[2].ID)
   105  
   106  	beta, err := deriveRandomnessFixedSize(fs, "gamma", dataFiatShamir...)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	gamma, err := deriveRandomness(fs, "beta", nil)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	//var beta, gamma fr.Element
   117  	//beta.SetUint64(9)
   118  	// gamma.SetString("10")
   119  	blindedZCanonical, err := computeBlindedZCanonical(
   120  		evaluationLDomainSmall,
   121  		evaluationRDomainSmall,
   122  		evaluationODomainSmall,
   123  		pk, beta, gamma)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	// 4 - commit Z
   129  	proof.Zpp, err = pk.Vk.Iopp.BuildProofOfProximity(blindedZCanonical)
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  
   134  	// 5 - compute H
   135  	// var alpha fr.Element
   136  	alpha, err := deriveRandomness(fs, "alpha", proof.Zpp.ID)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	// alpha.SetUint64(11)
   141  
   142  	evaluationQkCompleteDomainBigBitReversed := make([]fr.Element, pk.Domain[1].Cardinality)
   143  	copy(evaluationQkCompleteDomainBigBitReversed, fw[:len(spr.Public)])
   144  	copy(evaluationQkCompleteDomainBigBitReversed[len(spr.Public):], pk.LQkIncompleteDomainSmall[len(spr.Public):])
   145  	pk.Domain[0].FFTInverse(evaluationQkCompleteDomainBigBitReversed[:pk.Domain[0].Cardinality], fft.DIF)
   146  	fft.BitReverse(evaluationQkCompleteDomainBigBitReversed[:pk.Domain[0].Cardinality])
   147  
   148  	evaluationQkCompleteDomainBigBitReversed = fftBigCosetWOBitReverse(evaluationQkCompleteDomainBigBitReversed, &pk.Domain[1])
   149  
   150  	evaluationBlindedLDomainBigBitReversed := fftBigCosetWOBitReverse(blindedLCanonical, &pk.Domain[1])
   151  	evaluationBlindedRDomainBigBitReversed := fftBigCosetWOBitReverse(blindedRCanonical, &pk.Domain[1])
   152  	evaluationBlindedODomainBigBitReversed := fftBigCosetWOBitReverse(blindedOCanonical, &pk.Domain[1])
   153  
   154  	evaluationConstraintsDomainBigBitReversed := evalConstraintsInd(
   155  		pk,
   156  		evaluationBlindedLDomainBigBitReversed,
   157  		evaluationBlindedRDomainBigBitReversed,
   158  		evaluationBlindedODomainBigBitReversed,
   159  		evaluationQkCompleteDomainBigBitReversed)
   160  
   161  	evaluationBlindedZDomainBigBitReversed := fftBigCosetWOBitReverse(blindedZCanonical, &pk.Domain[1])
   162  
   163  	evaluationOrderingDomainBigBitReversed := evaluateOrderingDomainBigBitReversed(
   164  		pk,
   165  		evaluationBlindedZDomainBigBitReversed,
   166  		evaluationBlindedLDomainBigBitReversed,
   167  		evaluationBlindedRDomainBigBitReversed,
   168  		evaluationBlindedODomainBigBitReversed,
   169  		beta, gamma)
   170  
   171  	h1Canonical, h2Canonical, h3Canonical := computeQuotientCanonical(
   172  		pk,
   173  		evaluationConstraintsDomainBigBitReversed,
   174  		evaluationOrderingDomainBigBitReversed,
   175  		evaluationBlindedZDomainBigBitReversed,
   176  		alpha)
   177  
   178  	// 6 - commit to H
   179  	proof.Hpp[0], err = pk.Vk.Iopp.BuildProofOfProximity(h1Canonical)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  	proof.Hpp[1], err = pk.Vk.Iopp.BuildProofOfProximity(h2Canonical)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	proof.Hpp[2], err = pk.Vk.Iopp.BuildProofOfProximity(h3Canonical)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	// 7 - build the opening proofs
   193  	// compute the size of the domain of evaluation of the committed polynomial,
   194  	// the opening position. The challenge zeta will be g^{i} where i is the opening
   195  	// position, and g is the generator of the fri domain.
   196  	rho := uint64(fri.GetRho())
   197  	friSize := 2 * rho * pk.Vk.Size
   198  	var bFriSize big.Int
   199  	bFriSize.SetInt64(int64(friSize))
   200  	frOpeningPosition, err := deriveRandomness(fs, "zeta", proof.Hpp[0].ID, proof.Hpp[1].ID, proof.Hpp[2].ID)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	var bOpeningPosition big.Int
   205  	bOpeningPosition.SetBytes(frOpeningPosition.Marshal()).Mod(&bOpeningPosition, &bFriSize)
   206  	openingPosition := bOpeningPosition.Uint64()
   207  
   208  	// ql, qr, qm, qo, qkIncomplete
   209  	proof.OpeningsQlQrQmQoQkincompletemp[0], err = pk.Vk.Iopp.Open(pk.CQl, openingPosition)
   210  	if err != nil {
   211  		return &proof, err
   212  	}
   213  	proof.OpeningsQlQrQmQoQkincompletemp[1], err = pk.Vk.Iopp.Open(pk.CQr, openingPosition)
   214  	if err != nil {
   215  		return &proof, err
   216  	}
   217  	proof.OpeningsQlQrQmQoQkincompletemp[2], err = pk.Vk.Iopp.Open(pk.CQm, openingPosition)
   218  	if err != nil {
   219  		return &proof, err
   220  	}
   221  	proof.OpeningsQlQrQmQoQkincompletemp[3], err = pk.Vk.Iopp.Open(pk.CQo, openingPosition)
   222  	if err != nil {
   223  		return &proof, err
   224  	}
   225  	proof.OpeningsQlQrQmQoQkincompletemp[4], err = pk.Vk.Iopp.Open(pk.CQkIncomplete, openingPosition)
   226  	if err != nil {
   227  		return &proof, err
   228  	}
   229  
   230  	// l, r, o
   231  	proof.OpeningsLROmp[0], err = pk.Vk.Iopp.Open(blindedLCanonical, openingPosition)
   232  	if err != nil {
   233  		return &proof, err
   234  	}
   235  	proof.OpeningsLROmp[1], err = pk.Vk.Iopp.Open(blindedRCanonical, openingPosition)
   236  	if err != nil {
   237  		return &proof, err
   238  	}
   239  	proof.OpeningsLROmp[2], err = pk.Vk.Iopp.Open(blindedOCanonical, openingPosition)
   240  	if err != nil {
   241  		return &proof, err
   242  	}
   243  
   244  	// h0, h1, h2
   245  	proof.OpeningsHmp[0], err = pk.Vk.Iopp.Open(h1Canonical, openingPosition)
   246  	if err != nil {
   247  		return &proof, err
   248  	}
   249  	proof.OpeningsHmp[1], err = pk.Vk.Iopp.Open(h2Canonical, openingPosition)
   250  	if err != nil {
   251  		return &proof, err
   252  	}
   253  	proof.OpeningsHmp[2], err = pk.Vk.Iopp.Open(h3Canonical, openingPosition)
   254  	if err != nil {
   255  		return &proof, err
   256  	}
   257  
   258  	// s0, s1, s2
   259  	proof.OpeningsS1S2S3mp[0], err = pk.Vk.Iopp.Open(pk.Vk.SCanonical[0], openingPosition)
   260  	if err != nil {
   261  		return &proof, err
   262  	}
   263  	proof.OpeningsS1S2S3mp[1], err = pk.Vk.Iopp.Open(pk.Vk.SCanonical[1], openingPosition)
   264  	if err != nil {
   265  		return &proof, err
   266  	}
   267  	proof.OpeningsS1S2S3mp[2], err = pk.Vk.Iopp.Open(pk.Vk.SCanonical[2], openingPosition)
   268  	if err != nil {
   269  		return &proof, err
   270  	}
   271  
   272  	// id0, id1, id2
   273  	proof.OpeningsId1Id2Id3mp[0], err = pk.Vk.Iopp.Open(pk.Vk.IdCanonical[0], openingPosition)
   274  	if err != nil {
   275  		return &proof, err
   276  	}
   277  	proof.OpeningsId1Id2Id3mp[1], err = pk.Vk.Iopp.Open(pk.Vk.IdCanonical[1], openingPosition)
   278  	if err != nil {
   279  		return &proof, err
   280  	}
   281  	proof.OpeningsId1Id2Id3mp[2], err = pk.Vk.Iopp.Open(pk.Vk.IdCanonical[2], openingPosition)
   282  	if err != nil {
   283  		return &proof, err
   284  	}
   285  
   286  	// zeta is shifted by g, the generator of Z/nZ where n is the number of constraints. We need
   287  	// to query the "rho" factor from FRI to know by what should be shifted the opening position.
   288  	// We multiply by 2 because FRI is instantiated with pk.Domain[0].Cardinality+2, which makes
   289  	// the iop's domain of size rho*(2*pk.Domain[0].Cardinality).
   290  	shiftedOpeningPosition := (openingPosition + uint64(2*rho)) % friSize
   291  	proof.OpeningsZmp[0], err = pk.Vk.Iopp.Open(blindedZCanonical, openingPosition)
   292  	if err != nil {
   293  		return &proof, err
   294  	}
   295  	proof.OpeningsZmp[1], err = pk.Vk.Iopp.Open(blindedZCanonical, shiftedOpeningPosition)
   296  	if err != nil {
   297  		return &proof, err
   298  	}
   299  
   300  	return &proof, nil
   301  }
   302  
   303  // evaluateOrderingDomainBigBitReversed computes the evaluation of Z(uX)g1g2g3-Z(X)f1f2f3 on the odd
   304  // cosets of the big domain.
   305  //
   306  // * z evaluation of the blinded permutation accumulator polynomial on odd cosets
   307  // * l, r, o evaluation of the blinded solution vectors on odd cosets
   308  // * gamma randomization
   309  func evaluateOrderingDomainBigBitReversed(pk *ProvingKey, z, l, r, o []fr.Element, beta, gamma fr.Element) []fr.Element {
   310  
   311  	nbElmts := int(pk.Domain[1].Cardinality)
   312  
   313  	// computes  z_(uX)*(l(X)+s₁(X)*β+γ)*(r(X))+s₂(gⁱ)*β+γ)*(o(X))+s₃(X)*β+γ) - z(X)*(l(X)+X*β+γ)*(r(X)+u*X*β+γ)*(o(X)+u²*X*β+γ)
   314  	// on the big domain (coset).
   315  	res := make([]fr.Element, pk.Domain[1].Cardinality) // re use allocated memory for EvaluationS1BigDomain
   316  
   317  	// utils variables useful for using bit reversed indices
   318  	nn := uint64(64 - bits.TrailingZeros64(uint64(nbElmts)))
   319  
   320  	// needed to shift LsZ
   321  	toShift := int(pk.Domain[1].Cardinality / pk.Domain[0].Cardinality)
   322  
   323  	var cosetShift, cosetShiftSquare fr.Element
   324  	cosetShift.Set(&pk.Vk.CosetShift)
   325  	cosetShiftSquare.Square(&pk.Vk.CosetShift)
   326  
   327  	utils.Parallelize(int(pk.Domain[1].Cardinality), func(start, end int) {
   328  
   329  		var evaluationIDBigDomain fr.Element
   330  		evaluationIDBigDomain.Exp(pk.Domain[1].Generator, big.NewInt(int64(start))).
   331  			Mul(&evaluationIDBigDomain, &pk.Domain[1].FrMultiplicativeGen)
   332  
   333  		var f [3]fr.Element
   334  		var g [3]fr.Element
   335  
   336  		for i := start; i < end; i++ {
   337  
   338  			_i := bits.Reverse64(uint64(i)) >> nn
   339  			_is := bits.Reverse64(uint64((i+toShift)%nbElmts)) >> nn
   340  
   341  			// in what follows gⁱ is understood as the generator of the chosen coset of domainBig
   342  			f[0].Mul(&evaluationIDBigDomain, &beta).Add(&f[0], &l[_i]).Add(&f[0], &gamma)                               //l(gⁱ)+gⁱ*β+γ
   343  			f[1].Mul(&evaluationIDBigDomain, &cosetShift).Mul(&f[1], &beta).Add(&f[1], &r[_i]).Add(&f[1], &gamma)       //r(gⁱ)+u*gⁱ*β+γ
   344  			f[2].Mul(&evaluationIDBigDomain, &cosetShiftSquare).Mul(&f[2], &beta).Add(&f[2], &o[_i]).Add(&f[2], &gamma) //o(gⁱ)+u²*gⁱ*β+γ
   345  
   346  			g[0].Mul(&pk.EvaluationS1BigDomain[_i], &beta).Add(&g[0], &l[_i]).Add(&g[0], &gamma) //l(gⁱ))+s1(gⁱ)*β+γ
   347  			g[1].Mul(&pk.EvaluationS2BigDomain[_i], &beta).Add(&g[1], &r[_i]).Add(&g[1], &gamma) //r(gⁱ))+s2(gⁱ)*β+γ
   348  			g[2].Mul(&pk.EvaluationS3BigDomain[_i], &beta).Add(&g[2], &o[_i]).Add(&g[2], &gamma) //o(gⁱ))+s3(gⁱ)*β+γ
   349  
   350  			f[0].Mul(&f[0], &f[1]).Mul(&f[0], &f[2]).Mul(&f[0], &z[_i])  // z(gⁱ)*(l(gⁱ)+g^i*β+γ)*(r(g^i)+u*g^i*β+γ)*(o(g^i)+u²*g^i*β+γ)
   351  			g[0].Mul(&g[0], &g[1]).Mul(&g[0], &g[2]).Mul(&g[0], &z[_is]) //  z_(ugⁱ)*(l(gⁱ))+s₁(gⁱ)*β+γ)*(r(gⁱ))+s₂(gⁱ)*β+γ)*(o(gⁱ))+s₃(gⁱ)*β+γ)
   352  
   353  			res[_i].Sub(&g[0], &f[0]) // z_(ugⁱ)*(l(gⁱ))+s₁(gⁱ)*β+γ)*(r(gⁱ))+s₂(gⁱ)*β+γ)*(o(gⁱ))+s₃(gⁱ)*β+γ) - z(gⁱ)*(l(gⁱ)+g^i*β+γ)*(r(g^i)+u*g^i*β+γ)*(o(g^i)+u²*g^i*β+γ)
   354  
   355  			evaluationIDBigDomain.Mul(&evaluationIDBigDomain, &pk.Domain[1].Generator) // gⁱ*g
   356  		}
   357  	})
   358  
   359  	return res
   360  }
   361  
   362  // evalConstraintsInd computes the evaluation of lL+qrR+qqmL.R+qoO+k on
   363  // the odd coset of (Z/8mZ)/(Z/4mZ), where m=nbConstraints+nbAssertions.
   364  //
   365  // * lsL, lsR, lsO are the evaluation of the blinded solution vectors on odd cosets
   366  // * lsQk is the completed version of qk, in canonical version
   367  //
   368  // lsL, lsR, lsO are in bit reversed order, lsQk is in the correct order.
   369  func evalConstraintsInd(pk *ProvingKey, lsL, lsR, lsO, lsQk []fr.Element) []fr.Element {
   370  
   371  	res := make([]fr.Element, pk.Domain[1].Cardinality)
   372  	// nn := uint64(64 - bits.TrailingZeros64(pk.Domain[1].Cardinality))
   373  
   374  	utils.Parallelize(len(res), func(start, end int) {
   375  
   376  		var t0, t1 fr.Element
   377  
   378  		for i := start; i < end; i++ {
   379  
   380  			// irev := bits.Reverse64(uint64(i)) >> nn
   381  
   382  			t1.Mul(&pk.EvaluationQmDomainBigBitReversed[i], &lsR[i]) // qm.r
   383  			t1.Add(&t1, &pk.EvaluationQlDomainBigBitReversed[i])     // qm.r + ql
   384  			t1.Mul(&t1, &lsL[i])                                     //  qm.l.r + ql.l
   385  
   386  			t0.Mul(&pk.EvaluationQrDomainBigBitReversed[i], &lsR[i])
   387  			t0.Add(&t0, &t1) // qm.l.r + ql.l + qr.r
   388  
   389  			t1.Mul(&pk.EvaluationQoDomainBigBitReversed[i], &lsO[i])
   390  			t0.Add(&t0, &t1)          // ql.l + qr.r + qm.l.r + qo.o
   391  			res[i].Add(&t0, &lsQk[i]) // ql.l + qr.r + qm.l.r + qo.o + k
   392  
   393  		}
   394  	})
   395  
   396  	return res
   397  }
   398  
   399  // fftBigCosetWOBitReverse evaluates poly (canonical form) of degree m<n where n=domainBig.Cardinality
   400  // on the odd coset of (Z/2nZ)/(Z/nZ).
   401  //
   402  // Puts the result in res of size n.
   403  // Warning: result is in bit reversed order, we do a bit reverse operation only once in computeQuotientCanonical
   404  func fftBigCosetWOBitReverse(poly []fr.Element, domainBig *fft.Domain) []fr.Element {
   405  
   406  	res := make([]fr.Element, domainBig.Cardinality)
   407  
   408  	// we copy poly in res and scale by coset here
   409  	// to avoid FFT scaling on domainBig.Cardinality (res is very sparse)
   410  	cosetTable, err := domainBig.CosetTable()
   411  	if err != nil {
   412  		panic(err)
   413  	}
   414  	utils.Parallelize(len(poly), func(start, end int) {
   415  		for i := start; i < end; i++ {
   416  			res[i].Mul(&poly[i], &cosetTable[i])
   417  		}
   418  	}, runtime.NumCPU()/2)
   419  	domainBig.FFT(res, fft.DIF)
   420  	return res
   421  }
   422  
   423  // evaluateXnMinusOneDomainBigCoset evalutes Xᵐ-1 on DomainBig coset
   424  func evaluateXnMinusOneDomainBigCoset(domainBig, domainSmall *fft.Domain) []fr.Element {
   425  
   426  	ratio := domainBig.Cardinality / domainSmall.Cardinality
   427  
   428  	res := make([]fr.Element, ratio)
   429  
   430  	expo := big.NewInt(int64(domainSmall.Cardinality))
   431  	res[0].Exp(domainBig.FrMultiplicativeGen, expo)
   432  
   433  	var t fr.Element
   434  	t.Exp(domainBig.Generator, big.NewInt(int64(domainSmall.Cardinality)))
   435  
   436  	for i := 1; i < int(ratio); i++ {
   437  		res[i].Mul(&res[i-1], &t)
   438  	}
   439  
   440  	var one fr.Element
   441  	one.SetOne()
   442  	for i := 0; i < int(ratio); i++ {
   443  		res[i].Sub(&res[i], &one)
   444  	}
   445  
   446  	return res
   447  }
   448  
   449  // computeQuotientCanonical computes h in canonical form, split as h1+X^mh2+X²mh3 such that
   450  //
   451  // qlL+qrR+qmL.R+qoO+k + alpha.(zu*g1*g2*g3-z*f1*f2*f3) + alpha**2*L1*(z-1)= h.Z
   452  // \------------------/         \------------------------/             \-----/
   453  //    constraintsInd			    constraintOrdering					startsAtOne
   454  //
   455  // constraintInd, constraintOrdering are evaluated on the odd cosets of (Z/8mZ)/(Z/mZ)
   456  func computeQuotientCanonical(pk *ProvingKey, evaluationConstraintsIndBitReversed, evaluationConstraintOrderingBitReversed, evaluationBlindedZDomainBigBitReversed []fr.Element, alpha fr.Element) ([]fr.Element, []fr.Element, []fr.Element) {
   457  
   458  	h := make([]fr.Element, pk.Domain[1].Cardinality)
   459  
   460  	// evaluate Z = Xᵐ-1 on a coset of the big domain
   461  	evaluationXnMinusOneInverse := evaluateXnMinusOneDomainBigCoset(&pk.Domain[1], &pk.Domain[0])
   462  	evaluationXnMinusOneInverse = fr.BatchInvert(evaluationXnMinusOneInverse)
   463  
   464  	// computes L₁ (canonical form)
   465  	startsAtOne := make([]fr.Element, pk.Domain[1].Cardinality)
   466  	for i := 0; i < int(pk.Domain[0].Cardinality); i++ {
   467  		startsAtOne[i].Set(&pk.Domain[0].CardinalityInv)
   468  	}
   469  	pk.Domain[1].FFT(startsAtOne, fft.DIF, fft.OnCoset())
   470  
   471  	// ql(X)L(X)+qr(X)R(X)+qm(X)L(X)R(X)+qo(X)O(X)+k(X) + α.(z(μX)*g₁(X)*g₂(X)*g₃(X)-z(X)*f₁(X)*f₂(X)*f₃(X)) + α**2*L₁(X)(Z(X)-1)
   472  	// on a coset of the big domain
   473  	nn := uint64(64 - bits.TrailingZeros64(pk.Domain[1].Cardinality))
   474  
   475  	var one fr.Element
   476  	one.SetOne()
   477  
   478  	ratio := pk.Domain[1].Cardinality / pk.Domain[0].Cardinality
   479  
   480  	utils.Parallelize(int(pk.Domain[1].Cardinality), func(start, end int) {
   481  		var t fr.Element
   482  		for i := uint64(start); i < uint64(end); i++ {
   483  
   484  			_i := bits.Reverse64(i) >> nn
   485  
   486  			t.Sub(&evaluationBlindedZDomainBigBitReversed[_i], &one) // evaluates L₁(X)*(Z(X)-1) on a coset of the big domain
   487  			h[_i].Mul(&startsAtOne[_i], &t).Mul(&h[_i], &alpha).
   488  				Add(&h[_i], &evaluationConstraintOrderingBitReversed[_i]).
   489  				Mul(&h[_i], &alpha).
   490  				Add(&h[_i], &evaluationConstraintsIndBitReversed[_i]).
   491  				Mul(&h[_i], &evaluationXnMinusOneInverse[i%ratio])
   492  		}
   493  	})
   494  
   495  	// put h in canonical form. h is of degree 3*(n+1)+2.
   496  	// using fft.DIT put h revert bit reverse
   497  	pk.Domain[1].FFTInverse(h, fft.DIT, fft.OnCoset())
   498  
   499  	// degree of hi is n+2 because of the blinding
   500  	h1 := h[:pk.Domain[0].Cardinality+2]
   501  	h2 := h[pk.Domain[0].Cardinality+2 : 2*(pk.Domain[0].Cardinality+2)]
   502  	h3 := h[2*(pk.Domain[0].Cardinality+2) : 3*(pk.Domain[0].Cardinality+2)]
   503  
   504  	return h1, h2, h3
   505  
   506  }
   507  
   508  // computeZ computes Z, in canonical basis, where:
   509  //
   510  // * Z of degree n (domainNum.Cardinality)
   511  // * Z(1)=1
   512  // 								   (l_i+z**i+gamma)*(r_i+u*z**i+gamma)*(o_i+u**2z**i+gamma)
   513  // * for i>0: Z(u**i) = Pi_{k<i} -------------------------------------------------------
   514  //								     (l_i+s1+gamma)*(r_i+s2+gamma)*(o_i+s3+gamma)
   515  //
   516  //	* l, r, o are the solution in Lagrange basis
   517  func computeBlindedZCanonical(l, r, o []fr.Element, pk *ProvingKey, beta, gamma fr.Element) ([]fr.Element, error) {
   518  
   519  	// note that z has more capacity has its memory is reused for blinded z later on
   520  	z := make([]fr.Element, pk.Domain[0].Cardinality, pk.Domain[0].Cardinality+3)
   521  	nbElmts := int(pk.Domain[0].Cardinality)
   522  	gInv := make([]fr.Element, pk.Domain[0].Cardinality)
   523  
   524  	z[0].SetOne()
   525  	gInv[0].SetOne()
   526  
   527  	evaluationIDSmallDomain := getIDSmallDomain(&pk.Domain[0])
   528  
   529  	utils.Parallelize(nbElmts-1, func(start, end int) {
   530  
   531  		var f [3]fr.Element
   532  		var g [3]fr.Element
   533  
   534  		for i := start; i < end; i++ {
   535  
   536  			f[0].Mul(&evaluationIDSmallDomain[i], &beta).Add(&f[0], &l[i]).Add(&f[0], &gamma)           //lᵢ+g^i*β+γ
   537  			f[1].Mul(&evaluationIDSmallDomain[i+nbElmts], &beta).Add(&f[1], &r[i]).Add(&f[1], &gamma)   //rᵢ+u*g^i*β+γ
   538  			f[2].Mul(&evaluationIDSmallDomain[i+2*nbElmts], &beta).Add(&f[2], &o[i]).Add(&f[2], &gamma) //oᵢ+u²*g^i*β+γ
   539  
   540  			g[0].Mul(&evaluationIDSmallDomain[pk.Permutation[i]], &beta).Add(&g[0], &l[i]).Add(&g[0], &gamma)           //lᵢ+s₁(g^i)*β+γ
   541  			g[1].Mul(&evaluationIDSmallDomain[pk.Permutation[i+nbElmts]], &beta).Add(&g[1], &r[i]).Add(&g[1], &gamma)   //rᵢ+s₂(g^i)*β+γ
   542  			g[2].Mul(&evaluationIDSmallDomain[pk.Permutation[i+2*nbElmts]], &beta).Add(&g[2], &o[i]).Add(&g[2], &gamma) //oᵢ+s₃(g^i)*β+γ
   543  
   544  			f[0].Mul(&f[0], &f[1]).Mul(&f[0], &f[2]) // (lᵢ+g^i*β+γ)*(rᵢ+u*g^i*β+γ)*(oᵢ+u²*g^i*β+γ)
   545  			g[0].Mul(&g[0], &g[1]).Mul(&g[0], &g[2]) //  (lᵢ+s₁(g^i)*β+γ)*(rᵢ+s₂(g^i)*β+γ)*(oᵢ+s₃(g^i)*β+γ)
   546  
   547  			gInv[i+1] = g[0]
   548  			z[i+1] = f[0]
   549  
   550  		}
   551  	})
   552  
   553  	gInv = fr.BatchInvert(gInv)
   554  	for i := 1; i < nbElmts; i++ {
   555  		z[i].Mul(&z[i], &z[i-1]).
   556  			Mul(&z[i], &gInv[i])
   557  	}
   558  
   559  	pk.Domain[0].FFTInverse(z, fft.DIF)
   560  	fft.BitReverse(z)
   561  
   562  	return blindPoly(z, pk.Domain[0].Cardinality, 2)
   563  
   564  }
   565  
   566  // computeBlindedLROCanonical
   567  // l, r, o in canonical basis with blinding
   568  func computeBlindedLROCanonical(
   569  	ll, lr, lo []fr.Element, domain *fft.Domain) (bcl, bcr, bco []fr.Element, err error) {
   570  
   571  	// note that bcl, bcr and bco reuses cl, cr and co memory
   572  	cl := make([]fr.Element, domain.Cardinality, domain.Cardinality+2)
   573  	cr := make([]fr.Element, domain.Cardinality, domain.Cardinality+2)
   574  	co := make([]fr.Element, domain.Cardinality, domain.Cardinality+2)
   575  
   576  	chDone := make(chan error, 2)
   577  
   578  	go func() {
   579  		var err error
   580  		copy(cl, ll)
   581  		domain.FFTInverse(cl, fft.DIF)
   582  		fft.BitReverse(cl)
   583  		bcl, err = blindPoly(cl, domain.Cardinality, 1)
   584  		chDone <- err
   585  	}()
   586  	go func() {
   587  		var err error
   588  		copy(cr, lr)
   589  		domain.FFTInverse(cr, fft.DIF)
   590  		fft.BitReverse(cr)
   591  		bcr, err = blindPoly(cr, domain.Cardinality, 1)
   592  		chDone <- err
   593  	}()
   594  	copy(co, lo)
   595  	domain.FFTInverse(co, fft.DIF)
   596  	fft.BitReverse(co)
   597  	if bco, err = blindPoly(co, domain.Cardinality, 1); err != nil {
   598  		return
   599  	}
   600  	err = <-chDone
   601  	if err != nil {
   602  		return
   603  	}
   604  	err = <-chDone
   605  	return
   606  }
   607  
   608  // blindPoly blinds a polynomial by adding a Q(X)*(X**degree-1), where deg Q = order.
   609  //
   610  // * cp polynomial in canonical form
   611  // * rou root of unity, meaning the blinding factor is multiple of X**rou-1
   612  // * bo blinding order,  it's the degree of Q, where the blinding is Q(X)*(X**degree-1)
   613  //
   614  // WARNING:
   615  // pre condition degree(cp) ⩽ rou + bo
   616  // pre condition cap(cp) ⩾ int(totalDegree + 1)
   617  func blindPoly(cp []fr.Element, rou, bo uint64) ([]fr.Element, error) {
   618  
   619  	// degree of the blinded polynomial is max(rou+order, cp.Degree)
   620  	totalDegree := rou + bo
   621  
   622  	// re-use cp
   623  	res := cp[:totalDegree+1]
   624  
   625  	// random polynomial
   626  	blindingPoly := make([]fr.Element, bo+1)
   627  
   628  	for i := uint64(0); i < bo+1; i++ {
   629  		if _, err := blindingPoly[i].SetRandom(); err != nil {
   630  			return nil, err
   631  		}
   632  	}
   633  
   634  	// blinding
   635  	for i := uint64(0); i < bo+1; i++ {
   636  		res[i].Sub(&res[i], &blindingPoly[i])
   637  		res[rou+i].Add(&res[rou+i], &blindingPoly[i])
   638  	}
   639  
   640  	return res, nil
   641  }
   642  
   643  func deriveRandomnessFixedSize(fs *fiatshamir.Transcript, challenge string, data ...[fr.Bytes]byte) (fr.Element, error) {
   644  
   645  	var r fr.Element
   646  	for _, d := range data {
   647  		if err := fs.Bind(challenge, d[:]); err != nil {
   648  			return r, err
   649  		}
   650  	}
   651  
   652  	b, err := fs.ComputeChallenge(challenge)
   653  	if err != nil {
   654  		return r, err
   655  	}
   656  	r.SetBytes(b)
   657  	return r, nil
   658  
   659  }
   660  
   661  func deriveRandomness(fs *fiatshamir.Transcript, challenge string, data ...[]byte) (fr.Element, error) {
   662  
   663  	var r fr.Element
   664  	for _, d := range data {
   665  		if err := fs.Bind(challenge, d); err != nil {
   666  			return r, err
   667  		}
   668  	}
   669  
   670  	b, err := fs.ComputeChallenge(challenge)
   671  	if err != nil {
   672  		return r, err
   673  	}
   674  	r.SetBytes(b)
   675  	return r, nil
   676  
   677  }