From b4069bd140d8f0ff903b3399caae2424583a2e2c Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 17 Jan 2025 13:21:41 -0500 Subject: [PATCH] refactor(bw6-761): PairingCheck uses millerLoopLines --- std/algebra/emulated/sw_bls12381/pairing.go | 12 +- std/algebra/emulated/sw_bn254/pairing.go | 5 +- std/algebra/emulated/sw_bw6761/hints.go | 17 +- std/algebra/emulated/sw_bw6761/pairing.go | 313 +++++++++----------- 4 files changed, 157 insertions(+), 190 deletions(-) diff --git a/std/algebra/emulated/sw_bls12381/pairing.go b/std/algebra/emulated/sw_bls12381/pairing.go index 1e26fcece..183bde4f3 100644 --- a/std/algebra/emulated/sw_bls12381/pairing.go +++ b/std/algebra/emulated/sw_bls12381/pairing.go @@ -289,23 +289,23 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations, init * // Compute ∏ᵢ { fᵢ_{x₀,Q}(P) } res := pr.Ext12.One() - j := len(loopCounter) - 2 if init != nil { res = init } + j := len(loopCounter) - 2 if first { - // i = 62, separately to avoid an E12 Square + // i = j, separately to avoid an E12 Square // (Square(res) = 1² = 1) for k := 0; k < n; k++ { res = pr.MulBy02368(res, - pr.MulByElement(&lines[k][0][62].R1, yInv[k]), - pr.MulByElement(&lines[k][0][62].R0, xNegOverY[k]), + pr.MulByElement(&lines[k][0][j].R1, yInv[k]), + pr.MulByElement(&lines[k][0][j].R0, xNegOverY[k]), ) res = pr.MulBy02368(res, - pr.MulByElement(&lines[k][1][62].R1, yInv[k]), - pr.MulByElement(&lines[k][1][62].R0, xNegOverY[k]), + pr.MulByElement(&lines[k][1][j].R1, yInv[k]), + pr.MulByElement(&lines[k][1][j].R0, xNegOverY[k]), ) } j-- diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index c2209cfe4..729ec52b2 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -446,7 +446,6 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations, init * // Compute f_{6x₀+2,Q}(P) var prodLines [10]*baseEl res := pr.Ext12.One() - j := len(loopCounter) - 2 var initInv GTEl if init != nil { @@ -454,9 +453,9 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations, init * initInv = *pr.Ext12.Inverse(init) } + j := len(loopCounter) - 2 if first { - // i = 64 - // + // i = j // k = 0 c3 := pr.Ext2.MulByElement(&lines[0][0][j].R0, xNegOverY[0]) c4 := pr.Ext2.MulByElement(&lines[0][0][j].R1, yInv[0]) diff --git a/std/algebra/emulated/sw_bw6761/hints.go b/std/algebra/emulated/sw_bw6761/hints.go index e7215a7ca..796651934 100644 --- a/std/algebra/emulated/sw_bw6761/hints.go +++ b/std/algebra/emulated/sw_bw6761/hints.go @@ -81,14 +81,15 @@ func pairingCheckHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int var mInv big.Int mInv.SetString("105300887666978464659709343582542432109497460559010677145223399327335567156593762277982229043678237863242655241846768823344862796112034076814141083092751207576412334798103601349742476585775877619451019850167305863473223932142842098178714149254582966792063312581807532675011404956270444910983750120675327025908192761069674135173328190635728173483753211505851991073745950587829640934449952514784880889959559541546684726344944253403018397996950965921029567425987659358091464001225755716260618839676545930683009926269854751616319103606509390667378268460666742713527948268373325914395974070631687649214144656759247037859773349886114399692016935966157297580328600396352321897692663748248168657388300690175586203114387947411720168269584172401784701771662759756974275902513788431327670950496435721956320875507468132703494465092748348925165286946843554008708392819919707156205920861214337368776935547492934209453494196115576830279851512338758088097719490141268227027970070242059962020992385206924254152017997017283665944910844784993588814611604460594039341562723060932582754994971346320340801549001828241339646153773031765187339622798156846331769418880530957782348437016822638577491500694745694281480857816937650066502281171825041093314285283892479458782481150957342407", 10) - residueWitness := finalExpWitness(&millerLoop, &mInv) - - residueWitness.B0.A0.BigInt(outputs[0]) - residueWitness.B0.A1.BigInt(outputs[2]) - residueWitness.B0.A2.BigInt(outputs[4]) - residueWitness.B1.A0.BigInt(outputs[1]) - residueWitness.B1.A1.BigInt(outputs[3]) - residueWitness.B1.A2.BigInt(outputs[5]) + residueWitnessInv := finalExpWitness(&millerLoop, &mInv) + residueWitnessInv.Inverse(&residueWitnessInv) + + residueWitnessInv.B0.A0.BigInt(outputs[0]) + residueWitnessInv.B0.A1.BigInt(outputs[2]) + residueWitnessInv.B0.A2.BigInt(outputs[4]) + residueWitnessInv.B1.A0.BigInt(outputs[1]) + residueWitnessInv.B1.A1.BigInt(outputs[3]) + residueWitnessInv.B1.A2.BigInt(outputs[5]) return nil }) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 1c38c37fb..7ca01b1b7 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -187,7 +187,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { panic(err) } - residueWitness := >El{ + residueWitnessInv := >El{ A0: *hint[0], A1: *hint[1], A2: *hint[2], @@ -205,136 +205,9 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { lines[i] = *Q[i].Lines } - // precomputations - yInv := make([]*baseEl, nP) - xNegOverY := make([]*baseEl, nP) - - for k := 0; k < nP; k++ { - // P are supposed to be on G1 respectively of prime order r. - // The point (x,0) is of order 2. But this function does not check - // subgroup membership. - yInv[k] = pr.curveF.Inverse(&P[k].Y) - xNegOverY[k] = pr.curveF.Mul(&P[k].X, yInv[k]) - xNegOverY[k] = pr.curveF.Neg(xNegOverY[k]) - } - - // f_{x₀+1+λ(x₀³-x₀²-x₀),Q}(P), Q is known in advance - var prodLines [5]*baseEl - // init Miller loop accumulator to residueWitnessInv^p to share the squarings - // of residueWitnessInv^{x₀+1+p(x₀³-x₀²-x₀)} - residueWitnessInv := pr.Ext6.Inverse(residueWitness) - frobResidueWitness := pr.Ext6.Frobenius(residueWitness) - frobResidueWitnessInv := pr.Ext6.Frobenius(residueWitnessInv) - result := frobResidueWitnessInv - - for i := 188; i > 0; i-- { - // mutualize the square among nP Miller loops - // (∏ᵢfᵢ)² - result = pr.Square(result) - - j := loopCounter1[i] + 3*loopCounter2[i] - switch j { - // cases -4, -2, 2, 4 do not occur, given the static LoopCounters - case -3: - // mul by frobResidueWitness to capture -1's in x₀³-x₀²-x₀ - result = pr.Ext6.Mul(result, frobResidueWitness) - // mul by tangent and line - for k := 0; k < nP; k++ { - prodLines = pr.Mul023By023( - pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), - pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), - ) - result = pr.MulBy02345(result, prodLines) - } - case -1: - // mul by residueWitness to capture -1's in x₀+1 - result = pr.Ext6.Mul(result, residueWitness) - // mul by tangent and line - for k := 0; k < nP; k++ { - prodLines = pr.Mul023By023( - pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), - pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), - ) - result = pr.MulBy02345(result, prodLines) - } - case 0: - // mul tangents 2-by-2 and then by accumulator - for k := 1; k < nP; k += 2 { - prodLines = pr.Mul023By023( - pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), - pr.curveF.Mul(&lines[k-1][0][i].R1, yInv[k-1]), - pr.curveF.Mul(&lines[k-1][0][i].R0, xNegOverY[k-1]), - ) - result = pr.MulBy02345(result, prodLines) - } - // if number of tangents is odd, mul last line by res - // works for nP=1 as well - if nP%2 != 0 { - // ℓ × res - result = pr.MulBy023(result, - pr.curveF.Mul(&lines[nP-1][0][i].R1, yInv[nP-1]), - pr.curveF.Mul(&lines[nP-1][0][i].R0, xNegOverY[nP-1]), - ) - } - case 1: - // mul by residueWitnessInv to capture 1's in x₀+1 - result = pr.Ext6.Mul(result, residueWitnessInv) - // mul by line and tangent - for k := 0; k < nP; k++ { - prodLines = pr.Mul023By023( - pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), - pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), - ) - result = pr.MulBy02345(result, prodLines) - } - case 3: - // mul by frobResidueWitnessInv to capture 1's in x₀³-x₀²-x₀ - result = pr.Ext6.Mul(result, frobResidueWitnessInv) - for k := 0; k < nP; k++ { - prodLines = pr.Mul023By023( - pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), - pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), - pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), - ) - result = pr.MulBy02345(result, prodLines) - } - default: - panic("unknown case for loopCounter") - } - } - - // i = 0 (j = -3) - result = pr.Square(result) - // mul by frobResidueWitness to capture -1's in x₀³-x₀²-x₀ - result = pr.Ext6.Mul(result, frobResidueWitness) - // x₀+1+λ(x₀³-x₀²-x₀) = 0 mod r so accQ = ∞ at the last iteration, - // we only mul by tangent. - // mul tangents 2-by-2 and then by accumulator - for k := 1; k < nP; k += 2 { - prodLines = pr.Mul023By023( - pr.curveF.Mul(&lines[k][0][0].R1, yInv[k]), - pr.curveF.Mul(&lines[k][0][0].R0, xNegOverY[k]), - pr.curveF.Mul(&lines[k-1][0][0].R1, yInv[k-1]), - pr.curveF.Mul(&lines[k-1][0][0].R0, xNegOverY[k-1]), - ) - result = pr.MulBy02345(result, prodLines) - } - // if number of tangents is odd, mul last line by res - // works for nP=1 as well - if nP%2 != 0 { - // ℓ × res - result = pr.MulBy023(result, - pr.curveF.Mul(&lines[nP-1][0][0].R1, yInv[nP-1]), - pr.curveF.Mul(&lines[nP-1][0][0].R0, xNegOverY[nP-1]), - ) + res, err := pr.millerLoopLines(P, lines, residueWitnessInv, false) + if err != nil { + return fmt.Errorf("miller loop: %w", err) } // Check that: MillerLoop(P,Q) == residueWitness^Λ @@ -345,7 +218,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { // since we initialized the Miller loop accumulator with residueWitnessInv^{p}. // So we only need to check that: // result == 1. - pr.AssertIsEqual(result, pr.Ext6.One()) + pr.AssertIsEqual(res, pr.Ext6.One()) return nil } @@ -463,12 +336,12 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } lines[i] = *Q[i].Lines } - return pr.millerLoopLines(P, lines) + return pr.millerLoopLines(P, lines, nil, true) } // millerLoopLines computes the multi-Miller loop from points in G1 and precomputed lines in G2 -func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl, error) { +func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations, init *GTEl, first bool) (*GTEl, error) { // check input size match n := len(P) @@ -489,53 +362,88 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl xNegOverY[k] = pr.curveF.Neg(xNegOverY[k]) } - // f_{x₀+1+λ(x₀³-x₀²-x₀),Q}(P), Q is known in advance + // Compute f_{x₀+1+λ(x₀³-x₀²-x₀),Q}(P) var prodLines [5]*baseEl result := pr.Ext6.One() - // i = 188 - // k = 0 - result = &fields_bw6761.E6{ - A0: *pr.curveF.Mul(&lines[0][0][188].R1, yInv[0]), - A1: result.A1, - A2: *pr.curveF.Mul(&lines[0][0][188].R0, xNegOverY[0]), - A3: *pr.curveF.One(), - A4: result.A4, - A5: result.A5, + var initInv, frobInit, frobInitInv GTEl + if init != nil { + initInv = *pr.Ext6.Inverse(init) + frobInit = *pr.Ext6.Frobenius(init) + frobInitInv = *pr.Ext6.Frobenius(&initInv) + result = &frobInit } - if n >= 2 { - // k = 1, separately to avoid MulBy023 (res × ℓ) - // (res is also a line at this point, so we use Mul023By023 ℓ × ℓ) - prodLines = pr.Mul023By023( - pr.curveF.Mul(&lines[1][0][188].R1, yInv[1]), - pr.curveF.Mul(&lines[1][0][188].R0, xNegOverY[1]), - &result.A0, - &result.A2, - ) - result = &fields_bw6761.E6{ - A0: *prodLines[0], + j := len(loopCounter2) - 2 + if first { + // i = j + // k = 0 + result = >El{ + A0: *pr.curveF.Mul(&lines[0][0][j].R1, yInv[0]), A1: result.A1, - A2: *prodLines[1], - A3: *prodLines[2], - A4: *prodLines[3], - A5: *prodLines[4], + A2: *pr.curveF.Mul(&lines[0][0][j].R0, xNegOverY[0]), + A3: *pr.curveF.One(), + A4: result.A4, + A5: result.A5, } - } - for k := 2; k < n; k++ { - result = pr.MulBy023(result, - pr.curveF.Mul(&lines[k][0][188].R1, yInv[k]), - pr.curveF.Mul(&lines[k][0][188].R0, xNegOverY[k]), - ) + if n >= 2 { + // k = 1, separately to avoid MulBy023 (res × ℓ) + // (res is also a line at this point, so we use Mul023By023 ℓ × ℓ) + prodLines = pr.Mul023By023( + pr.curveF.Mul(&lines[1][0][j].R1, yInv[1]), + pr.curveF.Mul(&lines[1][0][j].R0, xNegOverY[1]), + &result.A0, + &result.A2, + ) + result = >El{ + A0: *prodLines[0], + A1: result.A1, + A2: *prodLines[1], + A3: *prodLines[2], + A4: *prodLines[3], + A5: *prodLines[4], + } + } + + for k := 2; k < n; k++ { + result = pr.MulBy023(result, + pr.curveF.Mul(&lines[k][0][j].R1, yInv[k]), + pr.curveF.Mul(&lines[k][0][j].R0, xNegOverY[k]), + ) + } + j-- } - for i := 187; i >= 0; i-- { + for i := j; i > 0; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² result = pr.Square(result) - if i > 0 && loopCounter2[i]*3+loopCounter1[i] != 0 { + j := loopCounter1[i] + 3*loopCounter2[i] + switch j { + // cases -4, -2, 2, 4 do not occur, given the static LoopCounters + case -3: + if init != nil { + // mul by frobInitInv to capture -1's in x₀³-x₀²-x₀ + result = pr.Ext6.Mul(result, &frobInitInv) + } + // mul by tangent and line + for k := 0; k < n; k++ { + prodLines = pr.Mul023By023( + pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), + pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), + ) + result = pr.MulBy02345(result, prodLines) + } + case -1: + if init != nil { + // mul by initInv to capture -1's in x₀+1 + result = pr.Ext6.Mul(result, &initInv) + } + // mul by tangent and line for k := 0; k < n; k++ { prodLines = pr.Mul023By023( pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), @@ -545,8 +453,18 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl ) result = pr.MulBy02345(result, prodLines) } - } else { - // if number of lines is odd, mul last line by res + case 0: + // mul tangents 2-by-2 and then by accumulator + for k := 1; k < n; k += 2 { + prodLines = pr.Mul023By023( + pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), + pr.curveF.Mul(&lines[k-1][0][i].R1, yInv[k-1]), + pr.curveF.Mul(&lines[k-1][0][i].R0, xNegOverY[k-1]), + ) + result = pr.MulBy02345(result, prodLines) + } + // if number of tangents is odd, mul last line by res // works for n=1 as well if n%2 != 0 { // ℓ × res @@ -555,19 +473,68 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl pr.curveF.Mul(&lines[n-1][0][i].R0, xNegOverY[n-1]), ) } - // mul lines 2-by-2 - for k := 1; k < n; k += 2 { + case 1: + if init != nil { + // mul by init to capture 1's in x₀+1 + result = pr.Ext6.Mul(result, init) + } + // mul by line and tangent + for k := 0; k < n; k++ { prodLines = pr.Mul023By023( pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), - pr.curveF.Mul(&lines[k-1][0][i].R1, yInv[k-1]), - pr.curveF.Mul(&lines[k-1][0][i].R0, xNegOverY[k-1]), + pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), + ) + result = pr.MulBy02345(result, prodLines) + } + case 3: + if init != nil { + // mul by frobInit to capture 1's in x₀³-x₀²-x₀ + result = pr.Ext6.Mul(result, &frobInit) + } + for k := 0; k < n; k++ { + prodLines = pr.Mul023By023( + pr.curveF.Mul(&lines[k][0][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][0][i].R0, xNegOverY[k]), + pr.curveF.Mul(&lines[k][1][i].R1, yInv[k]), + pr.curveF.Mul(&lines[k][1][i].R0, xNegOverY[k]), ) result = pr.MulBy02345(result, prodLines) } + default: + panic("unknown case for loopCounter") } } + // i = 0 (j = -3) + result = pr.Square(result) + if init != nil { + // mul by frobInitInv to capture -1's in x₀³-x₀²-x₀ + result = pr.Ext6.Mul(result, &frobInitInv) + } + // x₀+1+λ(x₀³-x₀²-x₀) = 0 mod r so accQ = ∞ at the last iteration, + // we only mul by tangent. + // mul tangents 2-by-2 and then by accumulator + for k := 1; k < n; k += 2 { + prodLines = pr.Mul023By023( + pr.curveF.Mul(&lines[k][0][0].R1, yInv[k]), + pr.curveF.Mul(&lines[k][0][0].R0, xNegOverY[k]), + pr.curveF.Mul(&lines[k-1][0][0].R1, yInv[k-1]), + pr.curveF.Mul(&lines[k-1][0][0].R0, xNegOverY[k-1]), + ) + result = pr.MulBy02345(result, prodLines) + } + // if number of tangents is odd, mul last line by res + // works for n=1 as well + if n%2 != 0 { + // ℓ × res + result = pr.MulBy023(result, + pr.curveF.Mul(&lines[n-1][0][0].R1, yInv[n-1]), + pr.curveF.Mul(&lines[n-1][0][0].R0, xNegOverY[n-1]), + ) + } + return result, nil }