github.com/consensys/gnark-crypto@v0.14.0/ecc/bn254/pairing.go (about) 1 // Copyright 2020 ConsenSys AG 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bn254 16 17 import ( 18 "errors" 19 20 "github.com/consensys/gnark-crypto/ecc/bn254/fp" 21 "github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower" 22 ) 23 24 // GT target group of the pairing 25 type GT = fptower.E12 26 27 type lineEvaluation struct { 28 r0 fptower.E2 29 r1 fptower.E2 30 r2 fptower.E2 31 } 32 33 // Pair calculates the reduced pairing for a set of points 34 // ∏ᵢ e(Pᵢ, Qᵢ). 35 // 36 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 37 func Pair(P []G1Affine, Q []G2Affine) (GT, error) { 38 f, err := MillerLoop(P, Q) 39 if err != nil { 40 return GT{}, err 41 } 42 return FinalExponentiation(&f), nil 43 } 44 45 // PairingCheck calculates the reduced pairing for a set of points and returns True if the result is One 46 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 47 // 48 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 49 func PairingCheck(P []G1Affine, Q []G2Affine) (bool, error) { 50 f, err := Pair(P, Q) 51 if err != nil { 52 return false, err 53 } 54 var one GT 55 one.SetOne() 56 return f.Equal(&one), nil 57 } 58 59 // FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ 60 // where d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r 61 // we use instead d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r 62 // where s is the cofactor 2x₀(6x₀²+3x₀+1) 63 func FinalExponentiation(z *GT, _z ...*GT) GT { 64 65 var result GT 66 result.Set(z) 67 68 for _, e := range _z { 69 result.Mul(&result, e) 70 } 71 72 var t [5]GT 73 74 // Easy part 75 // (p⁶-1)(p²+1) 76 t[0].Conjugate(&result) 77 result.Inverse(&result) 78 t[0].Mul(&t[0], &result) 79 result.FrobeniusSquare(&t[0]).Mul(&result, &t[0]) 80 81 var one GT 82 one.SetOne() 83 if result.Equal(&one) { 84 return result 85 } 86 87 // Hard part (up to permutation) 88 // 2x₀(6x₀²+3x₀+1)(p⁴-p²+1)/r 89 // Duquesne and Ghammam 90 // https://eprint.iacr.org/2015/192.pdf 91 // Fuentes et al. (alg. 6) 92 t[0].Expt(&result). 93 Conjugate(&t[0]) 94 t[0].CyclotomicSquare(&t[0]) 95 t[1].CyclotomicSquare(&t[0]) 96 t[1].Mul(&t[0], &t[1]) 97 t[2].Expt(&t[1]) 98 t[2].Conjugate(&t[2]) 99 t[3].Conjugate(&t[1]) 100 t[1].Mul(&t[2], &t[3]) 101 t[3].CyclotomicSquare(&t[2]) 102 t[4].Expt(&t[3]) 103 t[4].Mul(&t[1], &t[4]) 104 t[3].Mul(&t[0], &t[4]) 105 t[0].Mul(&t[2], &t[4]) 106 t[0].Mul(&result, &t[0]) 107 t[2].Frobenius(&t[3]) 108 t[0].Mul(&t[2], &t[0]) 109 t[2].FrobeniusSquare(&t[4]) 110 t[0].Mul(&t[2], &t[0]) 111 t[2].Conjugate(&result) 112 t[2].Mul(&t[2], &t[3]) 113 t[2].FrobeniusCube(&t[2]) 114 t[0].Mul(&t[2], &t[0]) 115 116 return t[0] 117 } 118 119 // MillerLoop computes the multi-Miller loop 120 // ∏ᵢ MillerLoop(Pᵢ, Qᵢ) = 121 // ∏ᵢ { fᵢ_{6x₀+2,Qᵢ}(Pᵢ) · ℓᵢ_{[6x₀+2]Qᵢ,π(Qᵢ)}(Pᵢ) · ℓᵢ_{[6x₀+2]Qᵢ+π(Qᵢ),-π²(Qᵢ)}(Pᵢ) } 122 func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { 123 n := len(P) 124 if n == 0 || n != len(Q) { 125 return GT{}, errors.New("invalid inputs sizes") 126 } 127 128 // filter infinity points 129 p := make([]G1Affine, 0, n) 130 q := make([]G2Affine, 0, n) 131 132 for k := 0; k < n; k++ { 133 if P[k].IsInfinity() || Q[k].IsInfinity() { 134 continue 135 } 136 p = append(p, P[k]) 137 q = append(q, Q[k]) 138 } 139 n = len(p) 140 141 // projective points for Q 142 qProj := make([]g2Proj, n) 143 qNeg := make([]G2Affine, n) 144 for k := 0; k < n; k++ { 145 qProj[k].FromAffine(&q[k]) 146 qNeg[k].Neg(&q[k]) 147 } 148 149 var result GT 150 result.SetOne() 151 var l2, l1 lineEvaluation 152 var prodLines [5]E2 153 154 // Compute ∏ᵢ { fᵢ_{6x₀+2,Q}(P) } 155 if n >= 1 { 156 // i = 64, separately to avoid an E12 Square 157 // (Square(res) = 1² = 1) 158 // LoopCounter[64] = 0 159 // k = 0, separately to avoid MulBy034 (res × ℓ) 160 // (assign line to res) 161 162 // qProj[0] ← 2qProj[0] and l1 the tangent ℓ passing 2qProj[0] 163 qProj[0].doubleStep(&l1) 164 // line evaluation at P[0] (assign) 165 result.C0.B0.MulByElement(&l1.r0, &p[0].Y) 166 result.C1.B0.MulByElement(&l1.r1, &p[0].X) 167 result.C1.B1.Set(&l1.r2) 168 } 169 170 if n >= 2 { 171 // k = 1, separately to avoid MulBy034 (res × ℓ) 172 // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) 173 174 // qProj[1] ← 2qProj[1] and l1 the tangent ℓ passing 2qProj[1] 175 qProj[1].doubleStep(&l1) 176 // line evaluation at P[1] 177 l1.r0.MulByElement(&l1.r0, &p[1].Y) 178 l1.r1.MulByElement(&l1.r1, &p[1].X) 179 // ℓ × res 180 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &result.C0.B0, &result.C1.B0, &result.C1.B1) 181 result.C0.B0 = prodLines[0] 182 result.C0.B1 = prodLines[1] 183 result.C0.B2 = prodLines[2] 184 result.C1.B0 = prodLines[3] 185 result.C1.B1 = prodLines[4] 186 } 187 188 // k >= 2 189 for k := 2; k < n; k++ { 190 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 191 qProj[k].doubleStep(&l1) 192 // line evaluation at P[k] 193 l1.r0.MulByElement(&l1.r0, &p[k].Y) 194 l1.r1.MulByElement(&l1.r1, &p[k].X) 195 // ℓ × res 196 result.MulBy034(&l1.r0, &l1.r1, &l1.r2) 197 } 198 199 // i = 63, separately to avoid a doubleStep (LoopCounter[63]=-1) 200 // (at this point qProj = 2Q, so 2qProj-Q=3Q is equivalent to qProj+Q=3Q 201 // this means doubleStep followed by an addMixedStep is equivalent to an 202 // addMixedStep here) 203 204 result.Square(&result) 205 for k := 0; k < n; k++ { 206 // l2 the line passing qProj[k] and -Q 207 // (avoids a point addition: qProj[k]-Q) 208 qProj[k].lineCompute(&l2, &qNeg[k]) 209 // line evaluation at P[k] 210 l2.r0.MulByElement(&l2.r0, &p[k].Y) 211 l2.r1.MulByElement(&l2.r1, &p[k].X) 212 // qProj[k] ← qProj[k]+Q[k] and 213 // l1 the line ℓ passing qProj[k] and Q[k] 214 qProj[k].addMixedStep(&l1, &q[k]) 215 // line evaluation at P[k] 216 l1.r0.MulByElement(&l1.r0, &p[k].Y) 217 l1.r1.MulByElement(&l1.r1, &p[k].X) 218 // ℓ × ℓ 219 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 220 // (ℓ × ℓ) × res 221 result.MulBy01234(&prodLines) 222 } 223 224 // i <= 62 225 for i := len(LoopCounter) - 4; i >= 0; i-- { 226 // mutualize the square among n Miller loops 227 // (∏ᵢfᵢ)² 228 result.Square(&result) 229 230 for k := 0; k < n; k++ { 231 // qProj[k] ← 2qProj[k] and l1 the tangent ℓ passing 2qProj[k] 232 qProj[k].doubleStep(&l1) 233 // line evaluation at P[k] 234 l1.r0.MulByElement(&l1.r0, &p[k].Y) 235 l1.r1.MulByElement(&l1.r1, &p[k].X) 236 237 if LoopCounter[i] == 1 { 238 // qProj[k] ← qProj[k]+Q[k] and 239 // l2 the line ℓ passing qProj[k] and Q[k] 240 qProj[k].addMixedStep(&l2, &q[k]) 241 // line evaluation at P[k] 242 l2.r0.MulByElement(&l2.r0, &p[k].Y) 243 l2.r1.MulByElement(&l2.r1, &p[k].X) 244 // ℓ × ℓ 245 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 246 // (ℓ × ℓ) × res 247 result.MulBy01234(&prodLines) 248 249 } else if LoopCounter[i] == -1 { 250 // qProj[k] ← qProj[k]-Q[k] and 251 // l2 the line ℓ passing qProj[k] and -Q[k] 252 qProj[k].addMixedStep(&l2, &qNeg[k]) 253 // line evaluation at P[k] 254 l2.r0.MulByElement(&l2.r0, &p[k].Y) 255 l2.r1.MulByElement(&l2.r1, &p[k].X) 256 // ℓ × ℓ 257 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 258 // (ℓ × ℓ) × res 259 result.MulBy01234(&prodLines) 260 } else { 261 // ℓ × res 262 result.MulBy034(&l1.r0, &l1.r1, &l1.r2) 263 } 264 } 265 } 266 267 // Compute ∏ᵢ { ℓᵢ_{[6x₀+2]Q,π(Q)}(P) · ℓᵢ_{[6x₀+2]Q+π(Q),-π²(Q)}(P) } 268 var Q1, Q2 G2Affine 269 for k := 0; k < n; k++ { 270 //Q1 = π(Q) 271 Q1.X.Conjugate(&q[k].X).MulByNonResidue1Power2(&Q1.X) 272 Q1.Y.Conjugate(&q[k].Y).MulByNonResidue1Power3(&Q1.Y) 273 274 // Q2 = -π²(Q) 275 Q2.X.MulByNonResidue2Power2(&q[k].X) 276 Q2.Y.MulByNonResidue2Power3(&q[k].Y).Neg(&Q2.Y) 277 278 // qProj[k] ← qProj[k]+π(Q) and 279 // l1 the line passing qProj[k] and π(Q) 280 qProj[k].addMixedStep(&l2, &Q1) 281 // line evaluation at P[k] 282 l2.r0.MulByElement(&l2.r0, &p[k].Y) 283 l2.r1.MulByElement(&l2.r1, &p[k].X) 284 285 // l2 the line passing qProj[k] and -π²(Q) 286 // (avoids a point addition: qProj[k]-π²(Q)) 287 qProj[k].lineCompute(&l1, &Q2) 288 // line evaluation at P[k] 289 l1.r0.MulByElement(&l1.r0, &p[k].Y) 290 l1.r1.MulByElement(&l1.r1, &p[k].X) 291 292 // ℓ × ℓ 293 prodLines = fptower.Mul034By034(&l1.r0, &l1.r1, &l1.r2, &l2.r0, &l2.r1, &l2.r2) 294 // (ℓ × ℓ) × res 295 result.MulBy01234(&prodLines) 296 } 297 298 return result, nil 299 } 300 301 // doubleStep doubles a point in Homogenous projective coordinates, and evaluates the line in Miller loop 302 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 303 func (p *g2Proj) doubleStep(evaluations *lineEvaluation) { 304 305 // get some Element from our pool 306 var t1, A, B, C, D, E, EE, F, G, H, I, J, K fptower.E2 307 A.Mul(&p.x, &p.y) 308 A.Halve() 309 B.Square(&p.y) 310 C.Square(&p.z) 311 D.Double(&C). 312 Add(&D, &C) 313 E.MulBybTwistCurveCoeff(&D) 314 F.Double(&E). 315 Add(&F, &E) 316 G.Add(&B, &F) 317 G.Halve() 318 H.Add(&p.y, &p.z). 319 Square(&H) 320 t1.Add(&B, &C) 321 H.Sub(&H, &t1) 322 I.Sub(&E, &B) 323 J.Square(&p.x) 324 EE.Square(&E) 325 K.Double(&EE). 326 Add(&K, &EE) 327 328 // X, Y, Z 329 p.x.Sub(&B, &F). 330 Mul(&p.x, &A) 331 p.y.Square(&G). 332 Sub(&p.y, &K) 333 p.z.Mul(&B, &H) 334 335 // Line evaluation 336 evaluations.r0.Neg(&H) 337 evaluations.r1.Double(&J). 338 Add(&evaluations.r1, &J) 339 evaluations.r2.Set(&I) 340 } 341 342 // addMixedStep point addition in Mixed Homogenous projective and Affine coordinates 343 // https://eprint.iacr.org/2013/722.pdf (Section 4.3) 344 func (p *g2Proj) addMixedStep(evaluations *lineEvaluation, a *G2Affine) { 345 346 // get some Element from our pool 347 var Y2Z1, X2Z1, O, L, C, D, E, F, G, H, t0, t1, t2, J fptower.E2 348 Y2Z1.Mul(&a.Y, &p.z) 349 O.Sub(&p.y, &Y2Z1) 350 X2Z1.Mul(&a.X, &p.z) 351 L.Sub(&p.x, &X2Z1) 352 C.Square(&O) 353 D.Square(&L) 354 E.Mul(&L, &D) 355 F.Mul(&p.z, &C) 356 G.Mul(&p.x, &D) 357 t0.Double(&G) 358 H.Add(&E, &F). 359 Sub(&H, &t0) 360 t1.Mul(&p.y, &E) 361 362 // X, Y, Z 363 p.x.Mul(&L, &H) 364 p.y.Sub(&G, &H). 365 Mul(&p.y, &O). 366 Sub(&p.y, &t1) 367 p.z.Mul(&E, &p.z) 368 369 t2.Mul(&L, &a.Y) 370 J.Mul(&a.X, &O). 371 Sub(&J, &t2) 372 373 // Line evaluation 374 evaluations.r0.Set(&L) 375 evaluations.r1.Neg(&O) 376 evaluations.r2.Set(&J) 377 } 378 379 // lineCompute computes the line through p in Homogenous projective coordinates 380 // and a in affine coordinates. It does not compute the resulting point p+a. 381 func (p *g2Proj) lineCompute(evaluations *lineEvaluation, a *G2Affine) { 382 383 // get some Element from our pool 384 var Y2Z1, X2Z1, O, L, t2, J fptower.E2 385 Y2Z1.Mul(&a.Y, &p.z) 386 O.Sub(&p.y, &Y2Z1) 387 X2Z1.Mul(&a.X, &p.z) 388 L.Sub(&p.x, &X2Z1) 389 t2.Mul(&L, &a.Y) 390 J.Mul(&a.X, &O). 391 Sub(&J, &t2) 392 393 // Line evaluation 394 evaluations.r0.Set(&L) 395 evaluations.r1.Neg(&O) 396 evaluations.r2.Set(&J) 397 } 398 399 // ---------------------- 400 // Fixed-argument pairing 401 // ---------------------- 402 403 type LineEvaluationAff struct { 404 R0 fptower.E2 405 R1 fptower.E2 406 } 407 408 // PairFixedQ calculates the reduced pairing for a set of points 409 // ∏ᵢ e(Pᵢ, Qᵢ) where Q are fixed points in G2. 410 // 411 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 412 func PairFixedQ(P []G1Affine, lines [][2][len(LoopCounter)]LineEvaluationAff) (GT, error) { 413 f, err := MillerLoopFixedQ(P, lines) 414 if err != nil { 415 return GT{}, err 416 } 417 return FinalExponentiation(&f), nil 418 } 419 420 // PairingCheckFixedQ calculates the reduced pairing for a set of points and returns True if the result is One 421 // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 where Q are fixed points in G2. 422 // 423 // This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. 424 func PairingCheckFixedQ(P []G1Affine, lines [][2][len(LoopCounter)]LineEvaluationAff) (bool, error) { 425 f, err := PairFixedQ(P, lines) 426 if err != nil { 427 return false, err 428 } 429 var one GT 430 one.SetOne() 431 return f.Equal(&one), nil 432 } 433 434 // PrecomputeLines precomputes the lines for the fixed-argument Miller loop 435 func PrecomputeLines(Q G2Affine) (PrecomputedLines [2][len(LoopCounter)]LineEvaluationAff) { 436 var accQ, negQ G2Affine 437 accQ.Set(&Q) 438 negQ.Neg(&Q) 439 440 n := len(LoopCounter) 441 for i := n - 2; i >= 0; i-- { 442 switch LoopCounter[i] { 443 case 0: 444 accQ.doubleStep(&PrecomputedLines[0][i]) 445 case 1: 446 accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &Q) 447 case -1: 448 accQ.doubleAndAddStep(&PrecomputedLines[0][i], &PrecomputedLines[1][i], &negQ) 449 default: 450 return [2][len(LoopCounter)]LineEvaluationAff{} 451 } 452 } 453 454 var psiQ, phiQ G2Affine 455 phiQ.X.Conjugate(&Q.X).MulByNonResidue1Power2(&phiQ.X) 456 phiQ.Y.Conjugate(&Q.Y).MulByNonResidue1Power3(&phiQ.Y) 457 psiQ.X.MulByNonResidue2Power2(&Q.X) 458 psiQ.Y.MulByNonResidue2Power3(&Q.Y).Neg(&psiQ.Y) 459 460 accQ.addStep(&PrecomputedLines[1][n-1], &phiQ) 461 accQ.addStep(&PrecomputedLines[0][n-1], &psiQ) 462 463 return PrecomputedLines 464 } 465 466 // MillerLoopFixedQ computes the multi-Miller loop as in MillerLoop 467 // but Qᵢ are fixed points in G2 known in advance. 468 func MillerLoopFixedQ(P []G1Affine, lines [][2][len(LoopCounter)]LineEvaluationAff) (GT, error) { 469 n := len(P) 470 if n == 0 || n != len(lines) { 471 return GT{}, errors.New("invalid inputs sizes") 472 } 473 474 // no need to filter infinity points: 475 // 1. if Pᵢ=(0,0) then -x/y=1/y=0 by gnark-crypto convention and so 476 // lines R0 and R1 are 0. At the end it happens that result will stay 477 // 1 through the Miller loop because MulBy34(1,0,0)==1 478 // Mul34By34(1,0,0,1,0,0)==1 and MulBy01234(1,0,0,0,0)==1. 479 // 480 // 2. if Qᵢ=(0,0) then PrecomputeLines(Qᵢ) will return lines R0 and R1 481 // that are 0 because of gnark-convention (*/0==0) in doubleStep and 482 // addStep. Similarly to Pᵢ=(0,0) it happens that result stays 1 483 // throughout the MillerLoop. 484 485 // precomputations 486 yInv := make([]fp.Element, n) 487 xNegOverY := make([]fp.Element, n) 488 for k := 0; k < n; k++ { 489 yInv[k].Set(&P[k].Y) 490 } 491 yInv = fp.BatchInvert(yInv) 492 for k := 0; k < n; k++ { 493 xNegOverY[k].Mul(&P[k].X, &yInv[k]). 494 Neg(&xNegOverY[k]) 495 } 496 497 var result GT 498 result.SetOne() 499 var prodLines [5]E2 500 501 // Compute ∏ᵢ { fᵢ_{6x₀+2,Q}(P) } 502 if n >= 1 { 503 // i = 64, separately to avoid an E12 Square 504 // (Square(res) = 1² = 1) 505 // LoopCounter[64] = 0 506 // k = 0, separately to avoid MulBy34 (res × ℓ) 507 // (assign line to res) 508 509 // line evaluation at P[0] (assign) 510 result.C1.B0.MulByElement(&lines[0][0][64].R0, &xNegOverY[0]) 511 result.C1.B1.MulByElement(&lines[0][0][64].R1, &yInv[0]) 512 // the coefficient which MulBy34 sets to 1 happens to be already 1 (result = 1) 513 } 514 515 if n >= 2 { 516 // k = 1, separately to avoid MulBy34 (res × ℓ) 517 // (res is also a line at this point, so we use Mul34By34 ℓ × ℓ) 518 // line evaluation at P[1] 519 lines[1][0][64].R0.MulByElement(&lines[1][0][64].R0, &xNegOverY[1]) 520 lines[1][0][64].R1.MulByElement(&lines[1][0][64].R1, &yInv[1]) 521 // ℓ × res 522 prodLines = fptower.Mul34By34(&lines[1][0][64].R0, &lines[1][0][64].R1, &result.C1.B0, &result.C1.B1) 523 result.C0.B0 = prodLines[0] 524 result.C0.B1 = prodLines[1] 525 result.C0.B2 = prodLines[2] 526 result.C1.B0 = prodLines[3] 527 result.C1.B1 = prodLines[4] 528 } 529 530 // k >= 2 531 for k := 2; k < n; k++ { 532 // line evaluation at P[k] 533 lines[k][0][64].R0.MulByElement(&lines[k][0][64].R0, &xNegOverY[k]) 534 lines[k][0][64].R1.MulByElement(&lines[k][0][64].R1, &yInv[k]) 535 // ℓ × res 536 result.MulBy34( 537 &lines[k][0][64].R0, 538 &lines[k][0][64].R1, 539 ) 540 } 541 542 for i := len(LoopCounter) - 3; i >= 0; i-- { 543 // mutualize the square among n Miller loops 544 // (∏ᵢfᵢ)² 545 result.Square(&result) 546 547 for k := 0; k < n; k++ { 548 // line evaluation at P[k] 549 lines[k][0][i].R0. 550 MulByElement( 551 &lines[k][0][i].R0, 552 &xNegOverY[k], 553 ) 554 lines[k][0][i].R1. 555 MulByElement( 556 &lines[k][0][i].R1, 557 &yInv[k], 558 ) 559 560 if LoopCounter[i] == 0 { 561 // ℓ × res 562 result.MulBy34( 563 &lines[k][0][i].R0, 564 &lines[k][0][i].R1, 565 ) 566 } else { 567 // line evaluation at P[k] 568 lines[k][1][i].R0. 569 MulByElement( 570 &lines[k][1][i].R0, 571 &xNegOverY[k], 572 ) 573 lines[k][1][i].R1. 574 MulByElement( 575 &lines[k][1][i].R1, 576 &yInv[k], 577 ) 578 // ℓ × ℓ 579 prodLines = fptower.Mul34By34( 580 &lines[k][0][i].R0, &lines[k][0][i].R1, 581 &lines[k][1][i].R0, &lines[k][1][i].R1, 582 ) 583 // (ℓ × ℓ) × res 584 result.MulBy01234(&prodLines) 585 } 586 } 587 } 588 589 // Compute ∏ᵢ { ℓᵢ_{[6x₀+2]Q,π(Q)}(P) · ℓᵢ_{[6x₀+2]Q+π(Q),-π²(Q)}(P) } 590 for k := 0; k < n; k++ { 591 // line evaluation at P[k] 592 lines[k][1][65].R0. 593 MulByElement( 594 &lines[k][1][65].R0, 595 &xNegOverY[k], 596 ) 597 lines[k][1][65].R1. 598 MulByElement( 599 &lines[k][1][65].R1, 600 &yInv[k], 601 ) 602 // line evaluation at P[k] 603 lines[k][0][65].R0. 604 MulByElement( 605 &lines[k][0][65].R0, 606 &xNegOverY[k], 607 ) 608 lines[k][0][65].R1. 609 MulByElement( 610 &lines[k][0][65].R1, 611 &yInv[k], 612 ) 613 // ℓ × ℓ 614 prodLines = fptower.Mul34By34( 615 &lines[k][1][65].R0, &lines[k][1][65].R1, 616 &lines[k][0][65].R0, &lines[k][0][65].R1, 617 ) 618 // (ℓ × ℓ) × res 619 result.MulBy01234(&prodLines) 620 } 621 622 return result, nil 623 } 624 625 func (p *G2Affine) doubleStep(evaluations *LineEvaluationAff) { 626 627 var n, d, λ, xr, yr fptower.E2 628 // λ = 3x²/2y 629 n.Square(&p.X) 630 λ.Double(&n). 631 Add(&λ, &n) 632 d.Double(&p.Y) 633 λ.Div(&λ, &d) 634 635 // xr = λ²-2x 636 xr.Square(&λ). 637 Sub(&xr, &p.X). 638 Sub(&xr, &p.X) 639 640 // yr = λ(x-xr)-y 641 yr.Sub(&p.X, &xr). 642 Mul(&yr, &λ). 643 Sub(&yr, &p.Y) 644 645 evaluations.R0.Set(&λ) 646 evaluations.R1.Mul(&λ, &p.X). 647 Sub(&evaluations.R1, &p.Y) 648 649 p.X.Set(&xr) 650 p.Y.Set(&yr) 651 } 652 653 func (p *G2Affine) addStep(evaluations *LineEvaluationAff, a *G2Affine) { 654 var n, d, λ, λλ, xr, yr fptower.E2 655 656 // compute λ = (y2-y1)/(x2-x1) 657 n.Sub(&a.Y, &p.Y) 658 d.Sub(&a.X, &p.X) 659 λ.Div(&n, &d) 660 661 // xr = λ²-x1-x2 662 λλ.Square(&λ) 663 n.Add(&p.X, &a.X) 664 xr.Sub(&λλ, &n) 665 666 // yr = λ(x1-xr) - y1 667 yr.Sub(&p.X, &xr). 668 Mul(&yr, &λ). 669 Sub(&yr, &p.Y) 670 671 evaluations.R0.Set(&λ) 672 evaluations.R1.Mul(&λ, &p.X). 673 Sub(&evaluations.R1, &p.Y) 674 675 p.X.Set(&xr) 676 p.Y.Set(&yr) 677 } 678 679 func (p *G2Affine) doubleAndAddStep(evaluations1, evaluations2 *LineEvaluationAff, a *G2Affine) { 680 var n, d, l1, x3, l2, x4, y4 fptower.E2 681 682 // compute λ1 = (y2-y1)/(x2-x1) 683 n.Sub(&p.Y, &a.Y) 684 d.Sub(&p.X, &a.X) 685 l1.Div(&n, &d) 686 687 // compute x3 =λ1²-x1-x2 688 x3.Square(&l1) 689 x3.Sub(&x3, &p.X) 690 x3.Sub(&x3, &a.X) 691 692 // omit y3 computation 693 694 // compute line1 695 evaluations1.R0.Set(&l1) 696 evaluations1.R1.Mul(&l1, &p.X) 697 evaluations1.R1.Sub(&evaluations1.R1, &p.Y) 698 699 // compute λ2 = -λ1-2y1/(x3-x1) 700 n.Double(&p.Y) 701 d.Sub(&x3, &p.X) 702 l2.Div(&n, &d) 703 l2.Add(&l2, &l1) 704 l2.Neg(&l2) 705 706 // compute x4 = λ2²-x1-x3 707 x4.Square(&l2) 708 x4.Sub(&x4, &p.X) 709 x4.Sub(&x4, &x3) 710 711 // compute y4 = λ2(x1 - x4)-y1 712 y4.Sub(&p.X, &x4) 713 y4.Mul(&l2, &y4) 714 y4.Sub(&y4, &p.Y) 715 716 // compute line2 717 evaluations2.R0.Set(&l2) 718 evaluations2.R1.Mul(&l2, &p.X) 719 evaluations2.R1.Sub(&evaluations2.R1, &p.Y) 720 721 p.X.Set(&x4) 722 p.Y.Set(&y4) 723 }