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 }