From e44e1c81969244b32c53bbc94a3dee04504f367b Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 29 Oct 2021 11:16:08 +0200 Subject: [PATCH 1/6] perf(std/sw): reduce constraints for pairing circuit --- std/algebra/fields/e12.go | 105 +++++++++++++-------------------- std/algebra/fields/e12_test.go | 10 +++- std/algebra/fields/e6.go | 42 +++++++++++++ 3 files changed, 92 insertions(+), 65 deletions(-) diff --git a/std/algebra/fields/e12.go b/std/algebra/fields/e12.go index 5653a9a9e8..31c1225f65 100644 --- a/std/algebra/fields/e12.go +++ b/std/algebra/fields/e12.go @@ -208,50 +208,19 @@ func (e *E12) Conjugate(api frontend.API, e1 E12) *E12 { // MulBy034 multiplication by sparse element func (e *E12) MulBy034(api frontend.API, c0, c3, c4 E2, ext Extension) *E12 { - var z0, z1, z2, z3, z4, z5, tmp1, tmp2 E2 - var t [12]E2 - - z0 = e.C0.B0 - z1 = e.C0.B1 - z2 = e.C0.B2 - z3 = e.C1.B0 - z4 = e.C1.B1 - z5 = e.C1.B2 - - tmp1.MulByIm(api, c3, ext) // MulByNonResidue - tmp2.MulByIm(api, c4, ext) // MulByNonResidue - - t[0].Mul(api, tmp1, z5, ext) - t[1].Mul(api, tmp2, z4, ext) - t[2].Mul(api, c3, z3, ext) - t[3].Mul(api, tmp2, z5, ext) - t[4].Mul(api, c3, z4, ext) - t[5].Mul(api, c4, z3, ext) - t[6].Mul(api, c3, z0, ext) - t[7].Mul(api, tmp2, z2, ext) - t[8].Mul(api, c3, z1, ext) - t[9].Mul(api, c4, z0, ext) - t[10].Mul(api, c3, z2, ext) - t[11].Mul(api, c4, z1, ext) - - e.C0.B0.Mul(api, c0, z0, ext). - Add(api, e.C0.B0, t[0]). - Add(api, e.C0.B0, t[1]) - e.C0.B1.Mul(api, c0, z1, ext). - Add(api, e.C0.B1, t[2]). - Add(api, e.C0.B1, t[3]) - e.C0.B2.Mul(api, c0, z2, ext). - Add(api, e.C0.B2, t[4]). - Add(api, e.C0.B2, t[5]) - e.C1.B0.Mul(api, c0, z3, ext). - Add(api, e.C1.B0, t[6]). - Add(api, e.C1.B0, t[7]) - e.C1.B1.Mul(api, c0, z4, ext). - Add(api, e.C1.B1, t[8]). - Add(api, e.C1.B1, t[9]) - e.C1.B2.Mul(api, c0, z5, ext). - Add(api, e.C1.B2, t[10]). - Add(api, e.C1.B2, t[11]) + var a, b, d E6 + + a.MulByE2(api, e.C0, c0, ext) + + b = e.C1 + b.MulBy01(api, c3, c4, ext) + + c0.Add(api, c0, c3) + d.Add(api, e.C0, e.C1) + d.MulBy01(api, c0, c4, ext) + + e.C1.Add(api, a, b).Neg(api, e.C1).Add(api, e.C1, d) + e.C0.MulByNonResidue(api, b, ext).Add(api, e.C0, a) return e } @@ -341,28 +310,38 @@ func (e *E12) Select(api frontend.API, b frontend.Variable, r1, r2 E12) *E12 { return e } -// FixedExponentiation compute e1**exponent, where the exponent is hardcoded +// nSquare repeated cyclotmic square +func (e *E12) nSquare(api frontend.API, n int, ext Extension) { + for i := 0; i < n; i++ { + e.CyclotomicSquare(api, *e, ext) + } +} + +// Expt compute e1**exponent, where the exponent is hardcoded // This function is only used for the final expo of the pairing for bls12377, so the exponent is supposed to be hardcoded // and on 64 bits. -func (e *E12) FixedExponentiation(api frontend.API, e1 E12, exponent uint64, ext Extension) *E12 { - - var expoBin [64]uint8 - for i := 0; i < 64; i++ { - expoBin[i] = uint8((exponent >> (63 - i))) & 1 - } +func (e *E12) Expt(api frontend.API, e1 E12, exponent uint64, ext Extension) *E12 { res := E12{} - res.SetOne(api) + x33 := E12{} + res = e1 + + res.nSquare(api, 5, ext) + res.Mul(api, res, e1, ext) + x33 = res + res.nSquare(api, 7, ext) + res.Mul(api, res, x33, ext) + res.nSquare(api, 4, ext) + res.Mul(api, res, e1, ext) + res.CyclotomicSquare(api, res, ext) + res.Mul(api, res, e1, ext) + res.nSquare(api, 46, ext) + res.Mul(api, res, e1, ext) - for i := 0; i < 64; i++ { - res.Mul(api, res, res, ext) - if expoBin[i] == 1 { - res.Mul(api, res, e1, ext) - } - } *e = res return e + } // FinalExponentiation computes the final expo x**(p**6-1)(p**2+1)(p**4 - p**2 +1)/r @@ -385,18 +364,18 @@ func (e *E12) FinalExponentiation(api frontend.API, e1 E12, genT uint64, ext Ext // and Tadanori Teruya // https://eprint.iacr.org/2020/875.pdf t[0].CyclotomicSquare(api, result, ext) - t[1].FixedExponentiation(api, result, genT, ext) + t[1].Expt(api, result, genT, ext) t[2].Conjugate(api, result) t[1].Mul(api, t[1], t[2], ext) - t[2].FixedExponentiation(api, t[1], genT, ext) + t[2].Expt(api, t[1], genT, ext) t[1].Conjugate(api, t[1]) t[1].Mul(api, t[1], t[2], ext) - t[2].FixedExponentiation(api, t[1], genT, ext) + t[2].Expt(api, t[1], genT, ext) t[1].Frobenius(api, t[1], ext) t[1].Mul(api, t[1], t[2], ext) result.Mul(api, result, t[0], ext) - t[0].FixedExponentiation(api, t[1], genT, ext) - t[2].FixedExponentiation(api, t[0], genT, ext) + t[0].Expt(api, t[1], genT, ext) + t[2].Expt(api, t[0], genT, ext) t[0].FrobeniusSquare(api, t[1], ext) t[1].Conjugate(api, t[1]) t[1].Mul(api, t[1], t[2], ext) diff --git a/std/algebra/fields/e12_test.go b/std/algebra/fields/e12_test.go index 88c188b042..1d5315c2e4 100644 --- a/std/algebra/fields/e12_test.go +++ b/std/algebra/fields/e12_test.go @@ -303,7 +303,7 @@ func (circuit *fp12FixedExpo) Define(curveID ecc.ID, api frontend.API) error { expected := E12{} ext := GetBLS377ExtensionFp12(api) expo := uint64(9586122913090633729) - expected.FixedExponentiation(api, circuit.A, expo, ext) + expected.Expt(api, circuit.A, expo, ext) expected.MustBeEqual(api, circuit.C) return nil } @@ -312,10 +312,16 @@ func TestExpFixedExpoFp12(t *testing.T) { var circuit, witness fp12FixedExpo // witness values - var a, c bls12377.E12 + var a, b, c bls12377.E12 expo := uint64(9586122913090633729) + // put a in the cyclotomic subgroup (we assume the group is Fp12, field of definition of bls277) a.SetRandom() + b.Conjugate(&a) + a.Inverse(&a) + b.Mul(&b, &a) + a.FrobeniusSquare(&b).Mul(&a, &b) + c.Exp(&a, *new(big.Int).SetUint64(expo)) witness.A.Assign(&a) diff --git a/std/algebra/fields/e6.go b/std/algebra/fields/e6.go index feb1cfdcfd..33d7bc3e3f 100644 --- a/std/algebra/fields/e6.go +++ b/std/algebra/fields/e6.go @@ -169,3 +169,45 @@ func (e *E6) MustBeEqual(api frontend.API, other E6) { e.B1.MustBeEqual(api, other.B1) e.B2.MustBeEqual(api, other.B2) } + +// MulByE2 multiplies an element in E6 by an element in E2 +func (e *E6) MulByE2(api frontend.API, e1 E6, e2 E2, ext Extension) *E6 { + e2Copy := E2{} + e2Copy = e2 + e.B0.Mul(api, e1.B0, e2Copy, ext) + e.B1.Mul(api, e1.B1, e2Copy, ext) + e.B2.Mul(api, e1.B2, e2Copy, ext) + return e +} + +// MulBy01 multiplication by sparse element (c0,c1,0) +func (e *E6) MulBy01(api frontend.API, c0, c1 E2, ext Extension) *E6 { + + var a, b, tmp, t0, t1, t2 E2 + + a.Mul(api, e.B0, c0, ext) + b.Mul(api, e.B1, c1, ext) + + tmp.Add(api, e.B1, e.B2) + t0.Mul(api, c1, tmp, ext) + t0.Sub(api, t0, b) + t0.MulByIm(api, t0, ext) + t0.Add(api, t0, a) + + tmp.Add(api, e.B0, e.B2) + t2.Mul(api, c0, tmp, ext) + t2.Sub(api, t2, a) + t2.Add(api, t2, b) + + t1.Add(api, c0, c1) + tmp.Add(api, e.B0, e.B1) + t1.Mul(api, t1, tmp, ext) + t1.Sub(api, t1, a) + t1.Sub(api, t1, b) + + e.B0 = t0 + e.B1 = t1 + e.B2 = t2 + + return e +} From 8c4de863557f02567ccd045eeacfafd2440e76cb Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 29 Oct 2021 11:44:43 +0200 Subject: [PATCH 2/6] perf(std/sw): square(a) instead of mul(a,a) --- std/algebra/sw/g2.go | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/std/algebra/sw/g2.go b/std/algebra/sw/g2.go index 4d570f8ae8..b903eb758c 100644 --- a/std/algebra/sw/g2.go +++ b/std/algebra/sw/g2.go @@ -41,7 +41,7 @@ func (p *G2Jac) ToProj(api frontend.API, p1 *G2Jac, ext fields.Extension) *G2Jac p.X.Mul(api, p1.X, p1.Z, ext) p.Y = p1.Y var t fields.E2 - t.Mul(api, p1.Z, p1.Z, ext) + t.Square(api, p1.Z, ext) p.Z.Mul(api, p.Z, t, ext) return p } @@ -67,9 +67,9 @@ func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac, ext fields.Extension) *G2 var Z1Z1, Z2Z2, U1, U2, S1, S2, H, I, J, r, V fields.E2 - Z1Z1.Mul(api, p1.Z, p1.Z, ext) + Z1Z1.Square(api, p1.Z, ext) - Z2Z2.Mul(api, p.Z, p.Z, ext) + Z2Z2.Square(api, p.Z, ext) U1.Mul(api, p1.X, Z2Z2, ext) @@ -84,7 +84,7 @@ func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac, ext fields.Extension) *G2 H.Sub(api, U2, U1) I.Add(api, H, H) - I.Mul(api, I, I, ext) + I.Square(api, I, ext) J.Mul(api, H, I, ext) @@ -93,7 +93,7 @@ func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac, ext fields.Extension) *G2 V.Mul(api, U1, I, ext) - p.X.Mul(api, r, r, ext) + p.X.Square(api, r, ext) p.X.Sub(api, p.X, J) p.X.Sub(api, p.X, V) p.X.Sub(api, p.X, V) @@ -107,7 +107,7 @@ func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac, ext fields.Extension) *G2 p.Y.Sub(api, p.Y, S1) p.Z.Add(api, p.Z, p1.Z) - p.Z.Mul(api, p.Z, p.Z, ext) + p.Z.Square(api, p.Z, ext) p.Z.Sub(api, p.Z, Z1Z1) p.Z.Sub(api, p.Z, Z2Z2) p.Z.Mul(api, p.Z, H, ext) @@ -126,7 +126,7 @@ func (p *G2Affine) AddAssign(api frontend.API, p1 *G2Affine, ext fields.Extensio l.Inverse(api, d, ext).Mul(api, l, n, ext) // xr =lambda**2-p1.x-p.x - xr.Mul(api, l, l, ext). + xr.Square(api, l, ext). Sub(api, xr, p1.X). Sub(api, xr, p.X) @@ -137,6 +137,7 @@ func (p *G2Affine) AddAssign(api frontend.API, p1 *G2Affine, ext fields.Extensio p.X = xr p.Y = yr + return p } @@ -147,12 +148,12 @@ func (p *G2Affine) Double(api frontend.API, p1 *G2Affine, ext fields.Extension) var n, d, l, xr, yr fields.E2 // lambda = 3*p1.x**2/2*p.y - n.Mul(api, p1.X, p1.X, ext).MulByFp(api, n, 3) + n.Square(api, p1.X, ext).MulByFp(api, n, 3) d.MulByFp(api, p1.Y, 2) l.Inverse(api, d, ext).Mul(api, l, n, ext) // xr = lambda**2-2*p1.x - xr.Mul(api, l, l, ext). + xr.Square(api, l, ext). Sub(api, xr, p1.X). Sub(api, xr, p1.X) @@ -173,21 +174,21 @@ func (p *G2Jac) Double(api frontend.API, p1 *G2Jac, ext fields.Extension) *G2Jac var XX, YY, YYYY, ZZ, S, M, T fields.E2 - XX.Mul(api, p.X, p.X, ext) - YY.Mul(api, p.Y, p.Y, ext) - YYYY.Mul(api, YY, YY, ext) - ZZ.Mul(api, p.Z, p.Z, ext) + XX.Square(api, p.X, ext) + YY.Square(api, p.Y, ext) + YYYY.Square(api, YY, ext) + ZZ.Square(api, p.Z, ext) S.Add(api, p.X, YY) - S.Mul(api, S, S, ext) + S.Square(api, S, ext) S.Sub(api, S, XX) S.Sub(api, S, YYYY) S.Add(api, S, S) M.MulByFp(api, XX, 3) // M = 3*XX+a*ZZ^2, here a=0 (we suppose sw has j invariant 0) p.Z.Add(api, p.Z, p.Y) - p.Z.Mul(api, p.Z, p.Z, ext) + p.Z.Square(api, p.Z, ext) p.Z.Sub(api, p.Z, YY) p.Z.Sub(api, p.Z, ZZ) - p.X.Mul(api, M, M, ext) + p.X.Square(api, M, ext) T.Add(api, S, S) p.X.Sub(api, p.X, T) p.Y.Sub(api, S, p.X) From 03b71b2640575709d6a1ceabf8968a71deeda4f7 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sat, 30 Oct 2021 03:08:15 +0100 Subject: [PATCH 3/6] feat(std/pairing): karabina cyclotomic square --- std/algebra/fields/e12.go | 145 +++++++++++++++++++++++++++++++-- std/algebra/fields/e2.go | 14 ++++ std/algebra/sw/pairing.go | 2 +- std/algebra/sw/pairing_test.go | 6 ++ 4 files changed, 161 insertions(+), 6 deletions(-) diff --git a/std/algebra/fields/e12.go b/std/algebra/fields/e12.go index 31c1225f65..4534998bbb 100644 --- a/std/algebra/fields/e12.go +++ b/std/algebra/fields/e12.go @@ -166,7 +166,131 @@ func (e *E12) Square(api frontend.API, x E12, ext Extension) *E12 { return e } -// CyclotomicSquare squares a Fp12 elt in the cyclotomic group +// Karabina's compressed cyclotomic square +// https://eprint.iacr.org/2010/542.pdf +// Th. 3.2 with minor modifications to fit our tower +func (e *E12) CyclotomicSquareCompressed(api frontend.API, x E12, ext Extension) *E12 { + + var t [7]E2 + + // t0 = g1^2 + t[0].Square(api, x.C0.B1, ext) + // t1 = g5^2 + t[1].Square(api, x.C1.B2, ext) + // t5 = g1 + g5 + t[5].Add(api, x.C0.B1, x.C1.B2) + // t2 = (g1 + g5)^2 + t[2].Square(api, t[5], ext) + + // t3 = g1^2 + g5^2 + t[3].Add(api, t[0], t[1]) + // t5 = 2 * g1 * g5 + t[5].Sub(api, t[2], t[3]) + + // t6 = g3 + g2 + t[6].Add(api, x.C1.B0, x.C0.B2) + // t3 = (g3 + g2)^2 + t[3].Square(api, t[6], ext) + // t2 = g3^2 + t[2].Square(api, x.C1.B0, ext) + + // t6 = 2 * nr * g1 * g5 + t[6].MulByIm(api, t[5], ext) + // t5 = 4 * nr * g1 * g5 + 2 * g3 + t[5].Add(api, t[6], x.C1.B0). + Double(api, t[5]) + // z3 = 6 * nr * g1 * g5 + 2 * g3 + e.C1.B0.Add(api, t[5], t[6]) + + // t4 = nr * g5^2 + t[4].MulByIm(api, t[1], ext) + // t5 = nr * g5^2 + g1^2 + t[5].Add(api, t[0], t[4]) + // t6 = nr * g5^2 + g1^2 - g2 + t[6].Sub(api, t[5], x.C0.B2) + + // t1 = g2^2 + t[1].Square(api, x.C0.B2, ext) + + // t6 = 2 * nr * g5^2 + 2 * g1^2 - 2*g2 + t[6].Double(api, t[6]) + // z2 = 3 * nr * g5^2 + 3 * g1^2 - 2*g2 + e.C0.B2.Add(api, t[6], t[5]) + + // t4 = nr * g2^2 + t[4].MulByIm(api, t[1], ext) + // t5 = g3^2 + nr * g2^2 + t[5].Add(api, t[2], t[4]) + // t6 = g3^2 + nr * g2^2 - g1 + t[6].Sub(api, t[5], x.C0.B1) + // t6 = 2 * g3^2 + 2 * nr * g2^2 - 2 * g1 + t[6].Double(api, t[6]) + // z1 = 3 * g3^2 + 3 * nr * g2^2 - 2 * g1 + e.C0.B1.Add(api, t[6], t[5]) + + // t0 = g2^2 + g3^2 + t[0].Add(api, t[2], t[1]) + // t5 = 2 * g3 * g2 + t[5].Sub(api, t[3], t[0]) + // t6 = 2 * g3 * g2 + g5 + t[6].Add(api, t[5], x.C1.B2) + // t6 = 4 * g3 * g2 + 2 * g5 + t[6].Double(api, t[6]) + // z5 = 6 * g3 * g2 + 2 * g5 + e.C1.B2.Add(api, t[5], t[6]) + + return e +} + +// Decompress Karabina's cyclotomic square result +func (e *E12) Decompress(api frontend.API, x E12, ext Extension) *E12 { + + var t [3]E2 + var one E2 + one.SetOne(api) + + // t0 = g1^2 + t[0].Square(api, x.C0.B1, ext) + // t1 = 3 * g1^2 - 2 * g2 + t[1].Sub(api, t[0], x.C0.B2). + Double(api, t[1]). + Add(api, t[1], t[0]) + // t0 = E * g5^2 + t1 + t[2].Square(api, x.C1.B2, ext) + t[0].MulByIm(api, t[2], ext). + Add(api, t[0], t[1]) + // t1 = 1/(4 * g3) + t[1].Double(api, x.C1.B0). + Double(api, t[1]). + Inverse(api, t[1], ext) + // z4 = g4 + e.C1.B1.Mul(api, t[0], t[1], ext) + + // t1 = g2 * g1 + t[1].Mul(api, x.C0.B2, x.C0.B1, ext) + // t2 = 2 * g4^2 - 3 * g2 * g1 + t[2].Square(api, x.C1.B1, ext). + Sub(api, t[2], t[1]). + Double(api, t[2]). + Sub(api, t[2], t[1]) + // t1 = g3 * g5 + t[1].Mul(api, x.C1.B0, x.C1.B2, ext) + // c_0 = E * (2 * g4^2 + g3 * g5 - 3 * g2 * g1) + 1 + t[2].Add(api, t[2], t[1]) + e.C0.B0.MulByIm(api, t[2], ext). + Add(api, e.C0.B0, one) + + e.C0.B1 = x.C0.B1 + e.C0.B2 = x.C0.B2 + e.C1.B0 = x.C1.B0 + e.C1.B2 = x.C1.B2 + + return e +} + +// Granger-Scott's cyclotomic square +// squares a Fp12 elt in the cyclotomic group +// https://eprint.iacr.org/2009/565.pdf, 3.2 func (e *E12) CyclotomicSquare(api frontend.API, x E12, ext Extension) *E12 { // https://eprint.iacr.org/2009/565.pdf, 3.2 @@ -317,6 +441,13 @@ func (e *E12) nSquare(api frontend.API, n int, ext Extension) { } } +// nSquareCompressed repeated compressed cyclotmic square +func (e *E12) nSquareCompressed(api frontend.API, n int, ext Extension) { + for i := 0; i < n; i++ { + e.CyclotomicSquareCompressed(api, *e, ext) + } +} + // Expt compute e1**exponent, where the exponent is hardcoded // This function is only used for the final expo of the pairing for bls12377, so the exponent is supposed to be hardcoded // and on 64 bits. @@ -326,16 +457,20 @@ func (e *E12) Expt(api frontend.API, e1 E12, exponent uint64, ext Extension) *E1 x33 := E12{} res = e1 - res.nSquare(api, 5, ext) + res.nSquareCompressed(api, 5, ext) + res.Decompress(api, res, ext) res.Mul(api, res, e1, ext) x33 = res - res.nSquare(api, 7, ext) + res.nSquareCompressed(api, 7, ext) + res.Decompress(api, res, ext) res.Mul(api, res, x33, ext) - res.nSquare(api, 4, ext) + res.nSquareCompressed(api, 4, ext) + res.Decompress(api, res, ext) res.Mul(api, res, e1, ext) res.CyclotomicSquare(api, res, ext) res.Mul(api, res, e1, ext) - res.nSquare(api, 46, ext) + res.nSquareCompressed(api, 46, ext) + res.Decompress(api, res, ext) res.Mul(api, res, e1, ext) *e = res diff --git a/std/algebra/fields/e2.go b/std/algebra/fields/e2.go index 4e96d2eb85..6c553774aa 100644 --- a/std/algebra/fields/e2.go +++ b/std/algebra/fields/e2.go @@ -28,6 +28,13 @@ type E2 struct { A0, A1 frontend.Variable } +// SetOne returns a newly allocated element equal to 1 +func (e *E2) SetOne(api frontend.API) *E2 { + e.A0 = api.Constant(1) + e.A1 = api.Constant(0) + return e +} + // Neg negates a e2 elmt func (e *E2) Neg(api frontend.API, e1 E2) *E2 { e.A0 = api.Sub(0, e1.A0) @@ -42,6 +49,13 @@ func (e *E2) Add(api frontend.API, e1, e2 E2) *E2 { return e } +// Double e2 elmt +func (e *E2) Double(api frontend.API, e1 E2) *E2 { + e.A0 = api.Add(e1.A0, e1.A0) + e.A1 = api.Add(e1.A1, e1.A1) + return e +} + // Sub e2 elmts func (e *E2) Sub(api frontend.API, e1, e2 E2) *E2 { e.A0 = api.Sub(e1.A0, e2.A0) diff --git a/std/algebra/sw/pairing.go b/std/algebra/sw/pairing.go index 134908e830..a897f9354d 100644 --- a/std/algebra/sw/pairing.go +++ b/std/algebra/sw/pairing.go @@ -81,7 +81,7 @@ func MillerLoop(api frontend.API, P G1Affine, Q G2Affine, res *fields.E12, pairi } squareStep := func() { - res.Mul(api, *res, *res, pairingInfo.Extension) + res.Square(api, *res, pairingInfo.Extension) QNext.Double(api, &QCur, pairingInfo.Extension).Neg(api, &QNext) l = computeLineCoef(api, QCur, QNext, pairingInfo.Extension) l.R0.MulByFp(api, l.R0, P.X) diff --git a/std/algebra/sw/pairing_test.go b/std/algebra/sw/pairing_test.go index ee0c8fc5c4..a086225925 100644 --- a/std/algebra/sw/pairing_test.go +++ b/std/algebra/sw/pairing_test.go @@ -127,3 +127,9 @@ func mustbeEq(api frontend.API, fp12 fields.E12, e12 *bls12377.GT) { api.AssertIsEqual(fp12.C1.B2.A0, e12.C1.B2.A0) api.AssertIsEqual(fp12.C1.B2.A1, e12.C1.B2.A1) } + +func BenchmarkPairing(b *testing.B) { + var c pairingBLS377 + ccsBench, _ = frontend.Compile(ecc.BW6_761, backend.GROTH16, &c) + b.Log("groth16", ccsBench.GetNbConstraints()) +} From 74f05dad91041bd84dcba4aa67509ad742b70edd Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sat, 30 Oct 2021 22:34:21 +0100 Subject: [PATCH 4/6] perf(std/pairing): reduce constraints in tower arithmetic --- std/algebra/fields/e12.go | 22 ++++++------ std/algebra/fields/e2.go | 14 +++----- std/algebra/fields/e6.go | 70 ++++++++++++++++++++++++++------------- 3 files changed, 63 insertions(+), 43 deletions(-) diff --git a/std/algebra/fields/e12.go b/std/algebra/fields/e12.go index 4534998bbb..9fc6f68fb2 100644 --- a/std/algebra/fields/e12.go +++ b/std/algebra/fields/e12.go @@ -135,16 +135,16 @@ func (e *E12) Neg(api frontend.API, e1 E12) *E12 { func (e *E12) Mul(api frontend.API, e1, e2 E12, ext Extension) *E12 { var u, v, ac, bd E6 - u.Add(api, e1.C0, e1.C1) // 6C - v.Add(api, e2.C0, e2.C1) // 6C - v.Mul(api, u, v, ext) // 61C + u.Add(api, e1.C0, e1.C1) + v.Add(api, e2.C0, e2.C1) + v.Mul(api, u, v, ext) - ac.Mul(api, e1.C0, e2.C0, ext) // 61C - bd.Mul(api, e1.C1, e2.C1, ext) // 61C - e.C1.Sub(api, v, ac).Sub(api, e.C1, bd) // 12C + ac.Mul(api, e1.C0, e2.C0, ext) + bd.Mul(api, e1.C1, e2.C1, ext) + e.C1.Sub(api, v, ac).Sub(api, e.C1, bd) - bd.Mul(api, bd, ext.wSquare, ext) // 6C - e.C0.Add(api, ac, bd) // 6C + bd.Mul(api, bd, ext.wSquare, ext) + e.C0.Add(api, ac, bd) return e } @@ -269,7 +269,7 @@ func (e *E12) Decompress(api frontend.API, x E12, ext Extension) *E12 { // t1 = g2 * g1 t[1].Mul(api, x.C0.B2, x.C0.B1, ext) // t2 = 2 * g4^2 - 3 * g2 * g1 - t[2].Square(api, x.C1.B1, ext). + t[2].Square(api, e.C1.B1, ext). Sub(api, t[2], t[1]). Double(api, t[2]). Sub(api, t[2], t[1]) @@ -395,8 +395,8 @@ func (e *E12) Inverse(api frontend.API, e1 E12, ext Extension) *E12 { var t [2]E6 var buf E6 - t[0].Mul(api, e1.C0, e1.C0, ext) - t[1].Mul(api, e1.C1, e1.C1, ext) + t[0].Square(api, e1.C0, ext) + t[1].Square(api, e1.C1, ext) buf.MulByNonResidue(api, t[1], ext) t[0].Sub(api, t[0], buf) diff --git a/std/algebra/fields/e2.go b/std/algebra/fields/e2.go index 6c553774aa..94dcf1ba6c 100644 --- a/std/algebra/fields/e2.go +++ b/std/algebra/fields/e2.go @@ -78,15 +78,12 @@ func (e *E2) Mul(api frontend.API, e1, e2 E2, ext Extension) *E2 { // 1C l31 := api.Add(ac, bd) - l3 := api.Sub(u, l31) - - e.A1 = api.Mul(l3, 1) + e.A1 = api.Sub(u, l31) // 1C buSquare := frontend.FromInterface(ext.uSquare) l41 := api.Mul(bd, buSquare) - l4 := api.Add(ac, l41) - e.A0 = api.Mul(l4, 1) + e.A0 = api.Add(ac, l41) return e } @@ -145,10 +142,9 @@ func (e *E2) Inverse(api frontend.API, e1 E2, ext Extension) *E2 { t1beta = api.Mul(t1, ext.uSquare) t0 = api.Sub(t0, t1beta) - t1 = api.Inverse(t0) - e.A0 = api.Mul(a0, t1) - e.A1 = api.Sub(0, a1) - e.A1 = api.Mul(e.A1, t1) + e.A0 = api.DivUnchecked(a0, t0) + e.A1 = api.DivUnchecked(a1, t0) + e.A1 = api.Sub(0, e.A1) return e } diff --git a/std/algebra/fields/e6.go b/std/algebra/fields/e6.go index 33d7bc3e3f..d7f1176409 100644 --- a/std/algebra/fields/e6.go +++ b/std/algebra/fields/e6.go @@ -67,25 +67,28 @@ func (e *E6) Neg(api frontend.API, e1 E6) *E6 { // icube is the imaginary elmt to the cube func (e *E6) Mul(api frontend.API, e1, e2 E6, ext Extension) *E6 { - // notations: (a+bv+cv2)*(d+ev+fe2) - var ad, bf, ce E2 - ad.Mul(api, e1.B0, e2.B0, ext) // 5C - bf.Mul(api, e1.B1, e2.B2, ext).MulByIm(api, bf, ext) // 6C - ce.Mul(api, e1.B2, e2.B1, ext).MulByIm(api, ce, ext) // 6C - - var cf, ae, bd E2 - cf.Mul(api, e1.B2, e2.B2, ext).MulByIm(api, cf, ext) // 6C - ae.Mul(api, e1.B0, e2.B1, ext) // 5C - bd.Mul(api, e1.B1, e2.B0, ext) // 5C - - var af, be, cd E2 - af.Mul(api, e1.B0, e2.B2, ext) // 5C - be.Mul(api, e1.B1, e2.B1, ext) // 5C - cd.Mul(api, e1.B2, e2.B0, ext) // 5C - - e.B0.Add(api, ad, bf).Add(api, e.B0, ce) // 4C - e.B1.Add(api, cf, ae).Add(api, e.B1, bd) // 4C - e.B2.Add(api, af, be).Add(api, e.B2, cd) // 4C + // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf + var t0, t1, t2, c0, c1, c2, tmp E2 + t0.Mul(api, e1.B0, e2.B0, ext) + t1.Mul(api, e1.B1, e2.B1, ext) + t2.Mul(api, e1.B2, e2.B2, ext) + + c0.Add(api, e1.B1, e1.B2) + tmp.Add(api, e2.B1, e2.B2) + c0.Mul(api, c0, tmp, ext).Sub(api, c0, t1).Sub(api, c0, t2).MulByIm(api, c0, ext).Add(api, c0, t0) + + c1.Add(api, e1.B0, e1.B1) + tmp.Add(api, e2.B0, e2.B1) + c1.Mul(api, c1, tmp, ext).Sub(api, c1, t0).Sub(api, c1, t1) + tmp.MulByIm(api, t2, ext) + c1.Add(api, c1, tmp) + + tmp.Add(api, e1.B0, e1.B2) + c2.Add(api, e2.B0, e2.B2).Mul(api, c2, tmp, ext).Sub(api, c2, t0).Sub(api, c2, t2).Add(api, c2, t1) + + e.B0 = c0 + e.B1 = c1 + e.B2 = c2 return e } @@ -116,16 +119,37 @@ func (e *E6) MulByNonResidue(api frontend.API, e1 E6, ext Extension) *E6 { return e } -// Inverse inverses an Fp2 elmt +// Square sets z to the E6 product of x,x, returns e +func (e *E6) Square(api frontend.API, x E6, ext Extension) *E6 { + + // Algorithm 16 from https://eprint.iacr.org/2010/354.pdf + var c4, c5, c1, c2, c3, c0 E2 + c4.Mul(api, x.B0, x.B1, ext).Double(api, c4) + c5.Square(api, x.B2, ext) + c1.MulByIm(api, c5, ext).Add(api, c1, c4) + c2.Sub(api, c4, c5) + c3.Square(api, x.B0, ext) + c4.Sub(api, x.B0, x.B1).Add(api, c4, x.B2) + c5.Mul(api, x.B1, x.B2, ext).Double(api, c5) + c4.Square(api, c4, ext) + c0.MulByIm(api, c5, ext).Add(api, c0, c3) + e.B2.Add(api, c2, c4).Add(api, e.B2, c5).Sub(api, e.B2, c3) + e.B0 = c0 + e.B1 = c1 + + return e +} + +// Inverse inverses an Fp6 elmt func (e *E6) Inverse(api frontend.API, e1 E6, ext Extension) *E6 { var t [7]E2 var c [3]E2 var buf E2 - t[0].Mul(api, e1.B0, e1.B0, ext) - t[1].Mul(api, e1.B1, e1.B1, ext) - t[2].Mul(api, e1.B2, e1.B2, ext) + t[0].Square(api, e1.B0, ext) + t[1].Square(api, e1.B1, ext) + t[2].Square(api, e1.B2, ext) t[3].Mul(api, e1.B0, e1.B1, ext) t[4].Mul(api, e1.B0, e1.B2, ext) t[5].Mul(api, e1.B1, e1.B2, ext) From 01e0bbbdfa68ed09e6ce5eb8c88f0f94a958d770 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sun, 31 Oct 2021 00:20:48 +0100 Subject: [PATCH 5/6] style: remove unused code (nSquare) --- std/algebra/fields/e12.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/std/algebra/fields/e12.go b/std/algebra/fields/e12.go index 9fc6f68fb2..d9676a50c8 100644 --- a/std/algebra/fields/e12.go +++ b/std/algebra/fields/e12.go @@ -434,13 +434,6 @@ func (e *E12) Select(api frontend.API, b frontend.Variable, r1, r2 E12) *E12 { return e } -// nSquare repeated cyclotmic square -func (e *E12) nSquare(api frontend.API, n int, ext Extension) { - for i := 0; i < n; i++ { - e.CyclotomicSquare(api, *e, ext) - } -} - // nSquareCompressed repeated compressed cyclotmic square func (e *E12) nSquareCompressed(api frontend.API, n int, ext Extension) { for i := 0; i < n; i++ { From 611b4b8ea30c635b2b1e34f93ca5f184faa9b12b Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sun, 31 Oct 2021 23:49:09 +0100 Subject: [PATCH 6/6] perf(std/pairing): Miller loop add/double steps --- std/algebra/sw/pairing.go | 76 +++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/std/algebra/sw/pairing.go b/std/algebra/sw/pairing.go index a897f9354d..360c805eba 100644 --- a/std/algebra/sw/pairing.go +++ b/std/algebra/sw/pairing.go @@ -72,8 +72,8 @@ func MillerLoop(api frontend.API, P G1Affine, Q G2Affine, res *fields.E12, pairi QCur = Q fsquareStep := func() { - QNext.Double(api, &QCur, pairingInfo.Extension).Neg(api, &QNext) - l = computeLineCoef(api, QCur, QNext, pairingInfo.Extension) + QNext, l = DoubleStep(api, &QCur, pairingInfo.Extension) + QNext.Neg(api, &QNext) l.R0.MulByFp(api, l.R0, P.X) l.R1.MulByFp(api, l.R1, P.Y) res.MulBy034(api, l.R1, l.R0, l.R2, pairingInfo.Extension) @@ -82,8 +82,8 @@ func MillerLoop(api frontend.API, P G1Affine, Q G2Affine, res *fields.E12, pairi squareStep := func() { res.Square(api, *res, pairingInfo.Extension) - QNext.Double(api, &QCur, pairingInfo.Extension).Neg(api, &QNext) - l = computeLineCoef(api, QCur, QNext, pairingInfo.Extension) + QNext, l = DoubleStep(api, &QCur, pairingInfo.Extension) + QNext.Neg(api, &QNext) l.R0.MulByFp(api, l.R0, P.X) l.R1.MulByFp(api, l.R1, P.Y) res.MulBy034(api, l.R1, l.R0, l.R2, pairingInfo.Extension) @@ -91,8 +91,8 @@ func MillerLoop(api frontend.API, P G1Affine, Q G2Affine, res *fields.E12, pairi } mulStep := func() { - QNext.Neg(api, &QNext).AddAssign(api, &Q, pairingInfo.Extension) - l = computeLineCoef(api, QCur, Q, pairingInfo.Extension) + QNext.Neg(api, &QNext) + QNext, l = AddStep(api, &QNext, &Q, pairingInfo.Extension) l.R0.MulByFp(api, l.R0, P.X) l.R1.MulByFp(api, l.R1, P.Y) res.MulBy034(api, l.R1, l.R0, l.R2, pairingInfo.Extension) @@ -131,3 +131,67 @@ func MillerLoop(api frontend.API, P G1Affine, Q G2Affine, res *fields.E12, pairi return res } + +// AddStep +func AddStep(api frontend.API, p1, p2 *G2Affine, ext fields.Extension) (G2Affine, LineEvaluation) { + + var n, d, l, xr, yr fields.E2 + var line LineEvaluation + var p G2Affine + + // compute lambda = (p1.y-p2.y)/(p1.x-p2.x) + n.Sub(api, p1.Y, p2.Y) + d.Sub(api, p1.X, p2.X) + l.Inverse(api, d, ext).Mul(api, l, n, ext) + + // xr =lambda**2-p1.x-p2.x + xr.Square(api, l, ext). + Sub(api, xr, p1.X). + Sub(api, xr, p2.X) + + // yr = lambda(p2.x - xr)-p2.y + yr.Sub(api, p2.X, xr). + Mul(api, l, yr, ext). + Sub(api, yr, p2.Y) + + p.X = xr + p.Y = yr + + line.R0.Neg(api, l) + line.R1.SetOne(api) + line.R2.Mul(api, l, p1.X, ext).Sub(api, line.R2, p1.Y) + + return p, line +} + +func DoubleStep(api frontend.API, p1 *G2Affine, ext fields.Extension) (G2Affine, LineEvaluation) { + + var n, d, l, xr, yr fields.E2 + var p G2Affine + var line LineEvaluation + + // lambda = 3*p1.x**2/2*p.y + n.Square(api, p1.X, ext).MulByFp(api, n, 3) + d.MulByFp(api, p1.Y, 2) + l.Inverse(api, d, ext).Mul(api, l, n, ext) + + // xr = lambda**2-2*p1.x + xr.Square(api, l, ext). + Sub(api, xr, p1.X). + Sub(api, xr, p1.X) + + // yr = lambda*(p.x-xr)-p.y + yr.Sub(api, p1.X, xr). + Mul(api, l, yr, ext). + Sub(api, yr, p1.Y) + + p.X = xr + p.Y = yr + + line.R0.Neg(api, l) + line.R1.SetOne(api) + line.R2.Mul(api, l, p1.X, ext).Sub(api, line.R2, p1.Y) + + return p, line + +}