From 148d5ec1025395291ce51d64d1d7a02809e463ab Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sat, 14 Dec 2024 11:04:02 -0500 Subject: [PATCH] feat(bls12-381): pairing using direct Fp12 + non-native `Eval()` (#1349) --- internal/stats/latest_stats.csv | 4 +- std/algebra/emulated/fields_bls12381/doc.go | 6 +- std/algebra/emulated/fields_bls12381/e12.go | 791 ++++++++++++++---- .../emulated/fields_bls12381/e12_pairing.go | 496 ++--------- .../emulated/fields_bls12381/e12_test.go | 387 +++------ std/algebra/emulated/fields_bls12381/e2.go | 34 +- std/algebra/emulated/fields_bls12381/e6.go | 465 ---------- .../emulated/fields_bls12381/e6_test.go | 360 -------- std/algebra/emulated/fields_bls12381/hints.go | 395 +++------ std/algebra/emulated/sw_bls12381/hints.go | 128 +++ std/algebra/emulated/sw_bls12381/pairing.go | 436 +++++----- .../emulated/sw_bls12381/pairing_test.go | 101 +-- 12 files changed, 1327 insertions(+), 2276 deletions(-) delete mode 100644 std/algebra/emulated/fields_bls12381/e6.go delete mode 100644 std/algebra/emulated/fields_bls12381/e6_test.go create mode 100644 std/algebra/emulated/sw_bls12381/hints.go diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index 5b272ec86b..3062542cb6 100644 --- a/internal/stats/latest_stats.csv +++ b/internal/stats/latest_stats.csv @@ -153,14 +153,14 @@ pairing_bls12377,bls24_315,plonk,0,0 pairing_bls12377,bls24_317,plonk,0,0 pairing_bls12377,bw6_761,plonk,51280,51280 pairing_bls12377,bw6_633,plonk,0,0 -pairing_bls12381,bn254,groth16,1419904,2366999 +pairing_bls12381,bn254,groth16,947528,1567714 pairing_bls12381,bls12_377,groth16,0,0 pairing_bls12381,bls12_381,groth16,0,0 pairing_bls12381,bls24_315,groth16,0,0 pairing_bls12381,bls24_317,groth16,0,0 pairing_bls12381,bw6_761,groth16,0,0 pairing_bls12381,bw6_633,groth16,0,0 -pairing_bls12381,bn254,plonk,5593770,5250897 +pairing_bls12381,bn254,plonk,3642638,3233378 pairing_bls12381,bls12_377,plonk,0,0 pairing_bls12381,bls12_381,plonk,0,0 pairing_bls12381,bls24_315,plonk,0,0 diff --git a/std/algebra/emulated/fields_bls12381/doc.go b/std/algebra/emulated/fields_bls12381/doc.go index c94c08b683..f61b20877f 100644 --- a/std/algebra/emulated/fields_bls12381/doc.go +++ b/std/algebra/emulated/fields_bls12381/doc.go @@ -1,6 +1,10 @@ -// Package fields_bls12381 implements the fields arithmetic of the Fp12 tower +// Package fields_bls12381 implements the fields arithmetic of the direct 𝔽p¹² extension // used to compute the pairing over the BLS12-381 curve. // +// 𝔽p¹²[i] = 𝔽p/i¹²-2i⁶+2 +// +// This direct tower is isomorphic to the 2-3-2 tower: +// // 𝔽p²[u] = 𝔽p/u²+1 // 𝔽p⁶[v] = 𝔽p²/v³-1-u // 𝔽p¹²[w] = 𝔽p⁶/w²-v diff --git a/std/algebra/emulated/fields_bls12381/e12.go b/std/algebra/emulated/fields_bls12381/e12.go index dbc394db1a..07edffc9e5 100644 --- a/std/algebra/emulated/fields_bls12381/e12.go +++ b/std/algebra/emulated/fields_bls12381/e12.go @@ -1,211 +1,397 @@ package fields_bls12381 import ( + "math/big" + bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/emulated" ) type E12 struct { - C0, C1 E6 + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11 baseEl } type Ext12 struct { - *Ext6 + *Ext2 + api frontend.API + fp *curveF } func NewExt12(api frontend.API) *Ext12 { - return &Ext12{Ext6: NewExt6(api)} + fp, err := emulated.NewField[emulated.BLS12381Fp](api) + if err != nil { + panic(err) + } + return &Ext12{ + Ext2: NewExt2(api), + api: api, + fp: fp, + } } -func (e Ext12) Add(x, y *E12) *E12 { - z0 := e.Ext6.Add(&x.C0, &y.C0) - z1 := e.Ext6.Add(&x.C1, &y.C1) +func (e Ext12) Zero() *E12 { + zero := e.fp.Zero() return &E12{ - C0: *z0, - C1: *z1, + A0: *zero, + A1: *zero, + A2: *zero, + A3: *zero, + A4: *zero, + A5: *zero, + A6: *zero, + A7: *zero, + A8: *zero, + A9: *zero, + A10: *zero, + A11: *zero, } } -func (e Ext12) Sub(x, y *E12) *E12 { - z0 := e.Ext6.Sub(&x.C0, &y.C0) - z1 := e.Ext6.Sub(&x.C1, &y.C1) +func (e Ext12) One() *E12 { + one := e.fp.One() + zero := e.fp.Zero() return &E12{ - C0: *z0, - C1: *z1, + A0: *one, + A1: *zero, + A2: *zero, + A3: *zero, + A4: *zero, + A5: *zero, + A6: *zero, + A7: *zero, + A8: *zero, + A9: *zero, + A10: *zero, + A11: *zero, } } -func (e Ext12) Conjugate(x *E12) *E12 { - z1 := e.Ext6.Neg(&x.C1) +func (e Ext12) Neg(x *E12) *E12 { + a0 := e.fp.Neg(&x.A0) + a1 := e.fp.Neg(&x.A1) + a2 := e.fp.Neg(&x.A2) + a3 := e.fp.Neg(&x.A3) + a4 := e.fp.Neg(&x.A4) + a5 := e.fp.Neg(&x.A5) + a6 := e.fp.Neg(&x.A6) + a7 := e.fp.Neg(&x.A7) + a8 := e.fp.Neg(&x.A8) + a9 := e.fp.Neg(&x.A9) + a10 := e.fp.Neg(&x.A10) + a11 := e.fp.Neg(&x.A11) + return &E12{ - C0: x.C0, - C1: *z1, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) Mul(x, y *E12) *E12 { - a := e.Ext6.Add(&x.C0, &x.C1) - b := e.Ext6.Add(&y.C0, &y.C1) - a = e.Ext6.Mul(a, b) - b = e.Ext6.Mul(&x.C0, &y.C0) - c := e.Ext6.Mul(&x.C1, &y.C1) - d := e.Ext6.Add(c, b) - z1 := e.Ext6.Sub(a, d) - z0 := e.Ext6.MulByNonResidue(c) - z0 = e.Ext6.Add(z0, b) +func (e Ext12) Add(x, y *E12) *E12 { + a0 := e.fp.Add(&x.A0, &y.A0) + a1 := e.fp.Add(&x.A1, &y.A1) + a2 := e.fp.Add(&x.A2, &y.A2) + a3 := e.fp.Add(&x.A3, &y.A3) + a4 := e.fp.Add(&x.A4, &y.A4) + a5 := e.fp.Add(&x.A5, &y.A5) + a6 := e.fp.Add(&x.A6, &y.A6) + a7 := e.fp.Add(&x.A7, &y.A7) + a8 := e.fp.Add(&x.A8, &y.A8) + a9 := e.fp.Add(&x.A9, &y.A9) + a10 := e.fp.Add(&x.A10, &y.A10) + a11 := e.fp.Add(&x.A11, &y.A11) + return &E12{ - C0: *z0, - C1: *z1, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) Zero() *E12 { - zero := e.fp.Zero() +func (e Ext12) Sub(x, y *E12) *E12 { + a0 := e.fp.Sub(&x.A0, &y.A0) + a1 := e.fp.Sub(&x.A1, &y.A1) + a2 := e.fp.Sub(&x.A2, &y.A2) + a3 := e.fp.Sub(&x.A3, &y.A3) + a4 := e.fp.Sub(&x.A4, &y.A4) + a5 := e.fp.Sub(&x.A5, &y.A5) + a6 := e.fp.Sub(&x.A6, &y.A6) + a7 := e.fp.Sub(&x.A7, &y.A7) + a8 := e.fp.Sub(&x.A8, &y.A8) + a9 := e.fp.Sub(&x.A9, &y.A9) + a10 := e.fp.Sub(&x.A10, &y.A10) + a11 := e.fp.Sub(&x.A11, &y.A11) + return &E12{ - C0: E6{ - B0: E2{A0: *zero, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, - C1: E6{ - B0: E2{A0: *zero, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) One() *E12 { - z000 := e.fp.One() - zero := e.fp.Zero() +func (e Ext12) Double(x *E12) *E12 { + two := big.NewInt(2) + a0 := e.fp.MulConst(&x.A0, two) + a1 := e.fp.MulConst(&x.A1, two) + a2 := e.fp.MulConst(&x.A2, two) + a3 := e.fp.MulConst(&x.A3, two) + a4 := e.fp.MulConst(&x.A4, two) + a5 := e.fp.MulConst(&x.A5, two) + a6 := e.fp.MulConst(&x.A6, two) + a7 := e.fp.MulConst(&x.A7, two) + a8 := e.fp.MulConst(&x.A8, two) + a9 := e.fp.MulConst(&x.A9, two) + a10 := e.fp.MulConst(&x.A10, two) + a11 := e.fp.MulConst(&x.A11, two) + return &E12{ - C0: E6{ - B0: E2{A0: *z000, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, - C1: E6{ - B0: E2{A0: *zero, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) IsZero(z *E12) frontend.Variable { - c0 := e.Ext6.IsZero(&z.C0) - c1 := e.Ext6.IsZero(&z.C1) - return e.api.And(c0, c1) -} - -func (e Ext12) Square(x *E12) *E12 { - c0 := e.Ext6.Sub(&x.C0, &x.C1) - c3 := e.Ext6.MulByNonResidue(&x.C1) - c3 = e.Ext6.Sub(&x.C0, c3) - c2 := e.Ext6.Mul(&x.C0, &x.C1) - c0 = e.Ext6.Mul(c0, c3) - c0 = e.Ext6.Add(c0, c2) - z1 := e.Ext6.Double(c2) - c2 = e.Ext6.MulByNonResidue(c2) - z0 := e.Ext6.Add(c0, c2) +func (e Ext12) Conjugate(x *E12) *E12 { return &E12{ - C0: *z0, - C1: *z1, + A0: x.A0, + A1: *e.fp.Neg(&x.A1), + A2: x.A2, + A3: *e.fp.Neg(&x.A3), + A4: x.A4, + A5: *e.fp.Neg(&x.A5), + A6: x.A6, + A7: *e.fp.Neg(&x.A7), + A8: x.A8, + A9: *e.fp.Neg(&x.A9), + A10: x.A10, + A11: *e.fp.Neg(&x.A11), } } -// Granger--Scott cyclotomic square -func (e Ext12) CyclotomicSquare(x *E12) *E12 { - t0 := e.Ext2.Square(&x.C1.B1) - t1 := e.Ext2.Square(&x.C0.B0) - t6 := e.Ext2.Add(&x.C1.B1, &x.C0.B0) - t6 = e.Ext2.Square(t6) - t6 = e.Ext2.Sub(t6, t0) - t6 = e.Ext2.Sub(t6, t1) - t2 := e.Ext2.Square(&x.C0.B2) - t3 := e.Ext2.Square(&x.C1.B0) - t7 := e.Ext2.Add(&x.C0.B2, &x.C1.B0) - t7 = e.Ext2.Square(t7) - t7 = e.Ext2.Sub(t7, t2) - t7 = e.Ext2.Sub(t7, t3) - t4 := e.Ext2.Square(&x.C1.B2) - t5 := e.Ext2.Square(&x.C0.B1) - t8 := e.Ext2.Add(&x.C1.B2, &x.C0.B1) - t8 = e.Ext2.Square(t8) - t8 = e.Ext2.Sub(t8, t4) - t8 = e.Ext2.Sub(t8, t5) - t8 = e.Ext2.MulByNonResidue(t8) - t0 = e.Ext2.MulByNonResidue(t0) - t0 = e.Ext2.Add(t0, t1) - t2 = e.Ext2.MulByNonResidue(t2) - t2 = e.Ext2.Add(t2, t3) - t4 = e.Ext2.MulByNonResidue(t4) - t4 = e.Ext2.Add(t4, t5) - z00 := e.Ext2.Sub(t0, &x.C0.B0) - z00 = e.Ext2.Double(z00) - z00 = e.Ext2.Add(z00, t0) - z01 := e.Ext2.Sub(t2, &x.C0.B1) - z01 = e.Ext2.Double(z01) - z01 = e.Ext2.Add(z01, t2) - z02 := e.Ext2.Sub(t4, &x.C0.B2) - z02 = e.Ext2.Double(z02) - z02 = e.Ext2.Add(z02, t4) - z10 := e.Ext2.Add(t8, &x.C1.B0) - z10 = e.Ext2.Double(z10) - z10 = e.Ext2.Add(z10, t8) - z11 := e.Ext2.Add(t6, &x.C1.B1) - z11 = e.Ext2.Double(z11) - z11 = e.Ext2.Add(z11, t6) - z12 := e.Ext2.Add(t7, &x.C1.B2) - z12 = e.Ext2.Double(z12) - z12 = e.Ext2.Add(z12, t7) +func (e Ext12) Mul(x, y *E12) *E12 { + return e.mulDirect(x, y) +} + +func (e Ext12) mulDirect(a, b *E12) *E12 { + + // a = a11 w^11 + a10 w^10 + a9 w^9 + a8 w^8 + a7 w^7 + a6 w^6 + a5 w^5 + a4 w^4 + a3 w^3 + a2 w^2 + a1 w + a0 + // b = b11 w^11 + b10 w^10 + b9 w^9 + b8 w^8 + b7 w^7 + b6 w^6 + b5 w^5 + b4 w^4 + b3 w^3 + b2 w^2 + b1 w + b0 + // + // Given that w^12 = 2 w^6 - 2, we can compute the product a * b as follows: + // + // a * b = d11 w^11 + d10 w^10 + d9 w^9 + d8 w^8 + d7 w^7 + d6 w^6 + d5 w^5 + d4 w^4 + d3 w^3 + d2 w^2 + d1 w + d0 + // + // where: + // + // d0 = c0 - 2 * c12 - 4 * c18 + // d1 = c1 - 2 * c13 - 4 * c19 + // d2 = c2 - 2 * c14 - 4 * c20 + // d3 = c3 - 2 * c15 - 4 * c21 + // d4 = c4 - 2 * c16 - 4 * c22 + // d5 = c5 - 2 * c17 + // d6 = c6 + 2 * c12 + 2 * c18 + // d7 = c7 + 2 * c13 + 2 * c19 + // d8 = c8 + 2 * c14 + 2 * c20 + // d9 = c9 + 2 * c15 + 2 * c21 + // d10 = c10 + 2 * c16 + 2 * c22 + // d11 = c11 + 2 * c17 + // + // and: + // + // c0 = a0 b0 + // c1 = a0 b1 + a1 b0 + // c2 = a0 b2 + a1 b1 + a2 b0 + // c3 = a0 b3 + a1 b2 + a2 b1 + a3 b0 + // c4 = a0 b4 + a1 b3 + a2 b2 + a3 b1 + a4 b0 + // c5 = a0 b5 + a1 b4 + a2 b3 + a3 b2 + a4 b1 + a5 b0 + // c6 = a0 b6 + a1 b5 + a2 b4 + a3 b3 + a4 b2 + a5 b1 + a6 b0 + // c7 = a0 b7 + a1 b6 + a2 b5 + a3 b4 + a4 b3 + a5 b2 + a6 b1 + a7 b0 + // c8 = a0 b8 + a1 b7 + a2 b6 + a3 b5 + a4 b4 + a5 b3 + a6 b2 + a7 b1 + a8 b0 + // c9 = a0 b9 + a1 b8 + a2 b7 + a3 b6 + a4 b5 + a5 b4 + a6 b3 + a7 b2 + a8 b1 + a9 b0 + // c10 = a0 b10 + a1 b9 + a2 b8 + a3 b7 + a4 b6 + a5 b5 + a6 b4 + a7 b3 + a8 b2 + a9 b1 + a10 b0 + // c11 = a0 b11 + a1 b10 + a2 b9 + a3 b8 + a4 b7 + a5 b6 + a6 b5 + a7 b4 + a8 b3 + a9 b2 + a10 b1 + a11 b0 + // c12 = a1 b11 + a2 b10 + a3 b9 + a4 b8 + a5 b7 + a6 b6 + a7 b5 + a8 b4 + a9 b3 + a10 b2 + a11 b1 + // c13 = a2 b11 + a3 b10 + a4 b9 + a5 b8 + a6 b7 + a7 b6 + a8 b5 + a9 b4 + a10 b3 + a11 b2 + // c14 = a3 b11 + a4 b10 + a5 b9 + a6 b8 + a7 b7 + a8 b6 + a9 b5 + a10 b4 + a11 b3 + // c15 = a4 b11 + a5 b10 + a6 b9 + a7 b8 + a8 b7 + a9 b6 + a10 b5 + a11 b4 + // c16 = a5 b11 + a6 b10 + a7 b9 + a8 b8 + a9 b7 + a10 b6 + a11 b5 + // c17 = a6 b11 + a7 b10 + a8 b9 + a9 b8 + a10 b7 + a11 b6 + // c18 = a7 b11 + a8 b10 + a9 b9 + a10 b8 + a11 b7 + // c19 = a8 b11 + a9 b10 + a10 b9 + a11 b8 + // c20 = a9 b11 + a10 b10 + a11 b9 + // c21 = a10 b11 + a11 b10 + // c22 = a11 b11 + + // d0 = c0 - 2 * c12 - 4 * c18 + // = a0 b0 - 2 * (a1 b11 + a2 b10 + a3 b9 + a4 b8 + a5 b7 + a6 b6 + a7 b5 + a8 b4 + a9 b3 + a10 b2 + a11 b1) - 4 * (a7 b11 + a8 b10 + a9 b9 + a10 b8 + a11 b7) + mone := e.fp.NewElement(-1) + d0 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A0}, {mone, &a.A1, &b.A11}, {mone, &a.A2, &b.A10}, {mone, &a.A3, &b.A9}, {mone, &a.A4, &b.A8}, {mone, &a.A5, &b.A7}, {mone, &a.A6, &b.A6}, {mone, &a.A7, &b.A5}, {mone, &a.A8, &b.A4}, {mone, &a.A9, &b.A3}, {mone, &a.A10, &b.A2}, {mone, &a.A11, &b.A1}, {mone, &a.A7, &b.A11}, {mone, &a.A8, &b.A10}, {mone, &a.A9, &b.A9}, {mone, &a.A10, &b.A8}, {mone, &a.A11, &b.A7}}, []int{1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4}) + + // d1 = c1 - 2 * c13 - 4 * c19 + // = a0 b1 + a1 b0 - 2 * (a2 b11 + a3 b10 + a4 b9 + a5 b8 + a6 b7 + a7 b6 + a8 b5 + a9 b4 + a10 b3 + a11 b2) - 4 * (a8 b11 + a9 b10 + a10 b9 + a11 b8) + d1 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A1}, {&a.A1, &b.A0}, {mone, &a.A2, &b.A11}, {mone, &a.A3, &b.A10}, {mone, &a.A4, &b.A9}, {mone, &a.A5, &b.A8}, {mone, &a.A6, &b.A7}, {mone, &a.A7, &b.A6}, {mone, &a.A8, &b.A5}, {mone, &a.A9, &b.A4}, {mone, &a.A10, &b.A3}, {mone, &a.A11, &b.A2}, {mone, &a.A8, &b.A11}, {mone, &a.A9, &b.A10}, {mone, &a.A10, &b.A9}, {mone, &a.A11, &b.A8}}, []int{1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4}) + + // d2 = c2 - 2 * c14 - 4 * c20 + // = a0 b2 + a1 b1 + a2 b0 - 2 * (a3 b11 + a4 b10 + a5 b9 + a6 b8 + a7 b7 + a8 b6 + a9 b5 + a10 b4 + a11 b3) - 4 * (a9 b11 + a10 b10 + a11 b9) + d2 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A2}, {&a.A1, &b.A1}, {&a.A2, &b.A0}, {mone, &a.A3, &b.A11}, {mone, &a.A4, &b.A10}, {mone, &a.A5, &b.A9}, {mone, &a.A6, &b.A8}, {mone, &a.A7, &b.A7}, {mone, &a.A8, &b.A6}, {mone, &a.A9, &b.A5}, {mone, &a.A10, &b.A4}, {mone, &a.A11, &b.A3}, {mone, &a.A9, &b.A11}, {mone, &a.A10, &b.A10}, {mone, &a.A11, &b.A9}}, []int{1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4}) + + // d3 = c3 - 2 * c15 - 4 * c21 + // = a0 b3 + a1 b2 + a2 b1 + a3 b0 - 2 * (a4 b11 + a5 b10 + a6 b9 + a7 b8 + a8 b7 + a9 b6 + a10 b5 + a11 b4) - 4 * (a10 b11 + a11 b10) + d3 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A3}, {&a.A1, &b.A2}, {&a.A2, &b.A1}, {&a.A3, &b.A0}, {mone, &a.A4, &b.A11}, {mone, &a.A5, &b.A10}, {mone, &a.A6, &b.A9}, {mone, &a.A7, &b.A8}, {mone, &a.A8, &b.A7}, {mone, &a.A9, &b.A6}, {mone, &a.A10, &b.A5}, {mone, &a.A11, &b.A4}, {mone, &a.A10, &b.A11}, {mone, &a.A11, &b.A10}}, []int{1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4}) + + // d4 = c4 - 2 * c16 - 4 * c22 + // = a0 b4 + a1 b3 + a2 b2 + a3 b1 + a4 b0 - 2 * (a5 b11 + a6 b10 + a7 b9 + a8 b8 + a9 b7 + a10 b6 + a11 b5) - 4 * a11 b11 + d4 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A4}, {&a.A1, &b.A3}, {&a.A2, &b.A2}, {&a.A3, &b.A1}, {&a.A4, &b.A0}, {mone, &a.A5, &b.A11}, {mone, &a.A6, &b.A10}, {mone, &a.A7, &b.A9}, {mone, &a.A8, &b.A8}, {mone, &a.A9, &b.A7}, {mone, &a.A10, &b.A6}, {mone, &a.A11, &b.A5}, {mone, &a.A11, &b.A11}}, []int{1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 4}) + + // d5 = c5 - 2 * c17 + // = a0 b5 + a1 b4 + a2 b3 + a3 b2 + a4 b1 + a5 b0 - 2 * (a6 b11 + a7 b10 + a8 b9 + a9 b8 + a10 b7 + a11 b6) + d5 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A5}, {&a.A1, &b.A4}, {&a.A2, &b.A3}, {&a.A3, &b.A2}, {&a.A4, &b.A1}, {&a.A5, &b.A0}, {mone, &a.A6, &b.A11}, {mone, &a.A7, &b.A10}, {mone, &a.A8, &b.A9}, {mone, &a.A9, &b.A8}, {mone, &a.A10, &b.A7}, {mone, &a.A11, &b.A6}}, []int{1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2}) + + // d6 = c6 + 2 * c12 + 2 * c2 + // = a0 b6 + a1 b5 + a2 b4 + a3 b3 + a4 b2 + a5 b1 + a6 b0 + 2 * (a1 b11 + a2 b10 + a3 b9 + a4 b8 + a5 b7 + a6 b6 + a7 b5 + a8 b4 + a9 b3 + a10 b2 + a11 b1) + 2 * (a7 b11 + a8 b10 + a9 b9 + a10 b8 + a11 b7) + d6 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A6}, {&a.A1, &b.A5}, {&a.A2, &b.A4}, {&a.A3, &b.A3}, {&a.A4, &b.A2}, {&a.A5, &b.A1}, {&a.A6, &b.A0}, {&a.A1, &b.A11}, {&a.A2, &b.A10}, {&a.A3, &b.A9}, {&a.A4, &b.A8}, {&a.A5, &b.A7}, {&a.A6, &b.A6}, {&a.A7, &b.A5}, {&a.A8, &b.A4}, {&a.A9, &b.A3}, {&a.A10, &b.A2}, {&a.A11, &b.A1}, {&a.A7, &b.A11}, {&a.A8, &b.A10}, {&a.A9, &b.A9}, {&a.A10, &b.A8}, {&a.A11, &b.A7}}, []int{1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) + + // d7 = c7 + 2 * c13 + 2 * c19 + // = a0 b7 + a1 b6 + a2 b5 + a3 b4 + a4 b3 + a5 b2 + a6 b1 + a7 b0 + 2 * (a2 b11 + a3 b10 + a4 b9 + a5 b8 + a6 b7 + a7 b6 + a8 b5 + a9 b4 + a10 b3 + a11 b2) + 2 * (a8 b11 + a9 b10 + a10 b9 + a11 b8) + d7 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A7}, {&a.A1, &b.A6}, {&a.A2, &b.A5}, {&a.A3, &b.A4}, {&a.A4, &b.A3}, {&a.A5, &b.A2}, {&a.A6, &b.A1}, {&a.A7, &b.A0}, {&a.A2, &b.A11}, {&a.A3, &b.A10}, {&a.A4, &b.A9}, {&a.A5, &b.A8}, {&a.A6, &b.A7}, {&a.A7, &b.A6}, {&a.A8, &b.A5}, {&a.A9, &b.A4}, {&a.A10, &b.A3}, {&a.A11, &b.A2}, {&a.A8, &b.A11}, {&a.A9, &b.A10}, {&a.A10, &b.A9}, {&a.A11, &b.A8}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) + + // d8 = c8 + 2 * c14 + 2 * c20 + // = a0 b8 + a1 b7 + a2 b6 + a3 b5 + a4 b4 + a5 b3 + a6 b2 + a7 b1 + a8 b0 + 2 * (a3 b11 + a4 b10 + a5 b9 + a6 b8 + a7 b7 + a8 b6 + a9 b5 + a10 b4 + a11 b3) + 2 * (a9 b11 + a10 b10 + a11 b9) + d8 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A8}, {&a.A1, &b.A7}, {&a.A2, &b.A6}, {&a.A3, &b.A5}, {&a.A4, &b.A4}, {&a.A5, &b.A3}, {&a.A6, &b.A2}, {&a.A7, &b.A1}, {&a.A8, &b.A0}, {&a.A3, &b.A11}, {&a.A4, &b.A10}, {&a.A5, &b.A9}, {&a.A6, &b.A8}, {&a.A7, &b.A7}, {&a.A8, &b.A6}, {&a.A9, &b.A5}, {&a.A10, &b.A4}, {&a.A11, &b.A3}, {&a.A9, &b.A11}, {&a.A10, &b.A10}, {&a.A11, &b.A9}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) + + // d9 = c9 + 2 * c15 + 2 * c21 + // = a0 b9 + a1 b8 + a2 b7 + a3 b6 + a4 b5 + a5 b4 + a6 b3 + a7 b2 + a8 b1 + a9 b0 + 2 * (a4 b11 + a5 b10 + a6 b9 + a7 b8 + a8 b7 + a9 b6 + a10 b5 + a11 b4) + 2 * (a10 b11 + a11 b10) + d9 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A9}, {&a.A1, &b.A8}, {&a.A2, &b.A7}, {&a.A3, &b.A6}, {&a.A4, &b.A5}, {&a.A5, &b.A4}, {&a.A6, &b.A3}, {&a.A7, &b.A2}, {&a.A8, &b.A1}, {&a.A9, &b.A0}, {&a.A4, &b.A11}, {&a.A5, &b.A10}, {&a.A6, &b.A9}, {&a.A7, &b.A8}, {&a.A8, &b.A7}, {&a.A9, &b.A6}, {&a.A10, &b.A5}, {&a.A11, &b.A4}, {&a.A10, &b.A11}, {&a.A11, &b.A10}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}) + + // d10 = c10 + 2 * c16 + 2 * c22 + // = a0 b10 + a1 b9 + a2 b8 + a3 b7 + a4 b6 + a5 b5 + a6 b4 + a7 b3 + a8 b2 + a9 b1 + a10 b0 + 2 * (a5 b11 + a6 b10 + a7 b9 + a8 b8 + a9 b7 + a10 b6 + a11 b5) + 2 * (a11 b11) + d10 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A10}, {&a.A1, &b.A9}, {&a.A2, &b.A8}, {&a.A3, &b.A7}, {&a.A4, &b.A6}, {&a.A5, &b.A5}, {&a.A6, &b.A4}, {&a.A7, &b.A3}, {&a.A8, &b.A2}, {&a.A9, &b.A1}, {&a.A10, &b.A0}, {&a.A5, &b.A11}, {&a.A6, &b.A10}, {&a.A7, &b.A9}, {&a.A8, &b.A8}, {&a.A9, &b.A7}, {&a.A10, &b.A6}, {&a.A11, &b.A5}, {&a.A11, &b.A11}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2}) + + // d11 = c11 + 2 * c17 + // = a0 b11 + a1 b10 + a2 b9 + a3 b8 + a4 b7 + a5 b6 + a6 b5 + a7 b4 + a8 b3 + a9 b2 + a10 b1 + a11 b0 + 2 * (a6 b11 + a7 b10 + a8 b9 + a9 b8 + a10 b7 + a11 b6) + d11 := e.fp.Eval([][]*baseEl{{&a.A0, &b.A11}, {&a.A1, &b.A10}, {&a.A2, &b.A9}, {&a.A3, &b.A8}, {&a.A4, &b.A7}, {&a.A5, &b.A6}, {&a.A6, &b.A5}, {&a.A7, &b.A4}, {&a.A8, &b.A3}, {&a.A9, &b.A2}, {&a.A10, &b.A1}, {&a.A11, &b.A0}, {&a.A6, &b.A11}, {&a.A7, &b.A10}, {&a.A8, &b.A9}, {&a.A9, &b.A8}, {&a.A10, &b.A7}, {&a.A11, &b.A6}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2}) + return &E12{ - C0: E6{ - B0: *z00, - B1: *z01, - B2: *z02, - }, - C1: E6{ - B0: *z10, - B1: *z11, - B2: *z12, - }, + A0: *d0, + A1: *d1, + A2: *d2, + A3: *d3, + A4: *d4, + A5: *d5, + A6: *d6, + A7: *d7, + A8: *d8, + A9: *d9, + A10: *d10, + A11: *d11, } } -func (e Ext12) AssertIsEqual(x, y *E12) { - e.Ext6.AssertIsEqual(&x.C0, &y.C0) - e.Ext6.AssertIsEqual(&x.C1, &y.C1) +func (e Ext12) Square(x *E12) *E12 { + return e.squareDirect(x) } -func FromE12(y *bls12381.E12) E12 { - return E12{ - C0: FromE6(&y.C0), - C1: FromE6(&y.C1), - } +func (e Ext12) squareDirect(a *E12) *E12 { + + mone := e.fp.NewElement(-1) + // d0 = a0 a0 - 2 * (2 a1 a11 + 2 a2 a10 + 2 a3 a9 + 2 a4 a8 + 2 a5 a7 + a6 a6) - 4 * (2 a7 a11 + 2 a8 a10 + a9 a9) + d0 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A0}, {mone, &a.A1, &a.A11}, {mone, &a.A2, &a.A10}, {mone, &a.A3, &a.A9}, {mone, &a.A4, &a.A8}, {mone, &a.A5, &a.A7}, {mone, &a.A6, &a.A6}, {mone, &a.A7, &a.A11}, {mone, &a.A8, &a.A10}, {mone, &a.A9, &a.A9}}, []int{1, 4, 4, 4, 4, 4, 2, 8, 8, 4}) + + // d1 = 2 a0 a1 - 4 * (2 a2 a11 + a3 a10 + a4 a9 + a5 a8 + a6 a7) - 8 * (a8 a11 + a9 a10) + d1 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A1}, {mone, &a.A2, &a.A11}, {mone, &a.A3, &a.A10}, {mone, &a.A4, &a.A9}, {mone, &a.A5, &a.A8}, {mone, &a.A6, &a.A7}, {mone, &a.A8, &a.A11}, {mone, &a.A9, &a.A10}}, []int{2, 4, 4, 4, 4, 4, 8, 8}) + + // d2 = 2 a0 a2 + a1 a1 - 2 * (2 a3 a11 + 2 a4 a10 + 2 a5 a9 + 2 a6 a8 + a7 a7) - 4 * (2 a9 a11 + a10 a10) + d2 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A2}, {&a.A1, &a.A1}, {mone, &a.A3, &a.A11}, {mone, &a.A4, &a.A10}, {mone, &a.A5, &a.A9}, {mone, &a.A6, &a.A8}, {mone, &a.A7, &a.A7}, {mone, &a.A9, &a.A11}, {mone, &a.A10, &a.A10}}, []int{2, 1, 4, 4, 4, 4, 2, 8, 4}) + + // d3 = 2 a0 a3 + 2 a1 a2 - 4 * (a4 a11 + a5 a10 + a6 a9 + a7 a8) - 8 * a10 a11 + d3 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A3}, {&a.A1, &a.A2}, {mone, &a.A4, &a.A11}, {mone, &a.A5, &a.A10}, {mone, &a.A6, &a.A9}, {mone, &a.A7, &a.A8}, {mone, &a.A10, &a.A11}}, []int{2, 2, 4, 4, 4, 4, 8}) + + // d4 = 2 a0 a4 + 2 a1 a3 + a2 a2 - 2 * (2 a5 a11 + 2 a6 a10 + 2 a7 a9 + a8 a8) - 4 * a11 a11 + d4 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A4}, {&a.A1, &a.A3}, {&a.A2, &a.A2}, {mone, &a.A5, &a.A11}, {mone, &a.A6, &a.A10}, {mone, &a.A7, &a.A9}, {mone, &a.A8, &a.A8}, {mone, &a.A11, &a.A11}}, []int{2, 2, 1, 4, 4, 4, 2, 4}) + + // d5 = 2 (a0 a5 + a1 a4 + a2 a3) - 4 * (a6 a11 + a7 a10 + a8 a9) + d5 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A5}, {&a.A1, &a.A4}, {&a.A2, &a.A3}, {mone, &a.A6, &a.A11}, {mone, &a.A7, &a.A10}, {mone, &a.A8, &a.A9}}, []int{2, 2, 2, 4, 4, 4}) + // d6 = 2 a0 a6 + 2 a1 a5 + 2 a2 a4 + a3 a3 + 2 * (2 a1 a11 + 2 a2 a10 + 2 a3 a9 + 2 a4 a8 + 2 a5 a7 + a6 a6) + 2 * (2 a7 a11 + 2 a8 a10 + a9 a9) + d6 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A6}, {&a.A1, &a.A5}, {&a.A2, &a.A4}, {&a.A3, &a.A3}, {&a.A1, &a.A11}, {&a.A2, &a.A10}, {&a.A3, &a.A9}, {&a.A4, &a.A8}, {&a.A5, &a.A7}, {&a.A6, &a.A6}, {&a.A7, &a.A11}, {&a.A8, &a.A10}, {&a.A9, &a.A9}}, []int{2, 2, 2, 1, 4, 4, 4, 4, 4, 2, 4, 4, 2}) + + // d7 = 2(a0 a7 + a1 a6 + a2 a5 + a3 a4) + 4 * (a2 a11 + a3 a10 + a4 a9 + a5 a8 + a6 a7) + 4 * (a8 a11 + a9 a10) + d7 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A7}, {&a.A1, &a.A6}, {&a.A2, &a.A5}, {&a.A3, &a.A4}, {&a.A2, &a.A11}, {&a.A3, &a.A10}, {&a.A4, &a.A9}, {&a.A5, &a.A8}, {&a.A6, &a.A7}, {&a.A8, &a.A11}, {&a.A9, &a.A10}}, []int{2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4}) + + // d8 = 2(a0 a8 + a1 a7 + a2 a6 + a3 a5) + a4 a4 + 2 * (2 a3 a11 + 2 a4 a10 + 2 a5 a9 + 2 a6 a8 + a7 a7) + 2 * (2 a9 a11 + a10 a10) + d8 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A8}, {&a.A1, &a.A7}, {&a.A2, &a.A6}, {&a.A3, &a.A5}, {&a.A4, &a.A4}, {&a.A3, &a.A11}, {&a.A4, &a.A10}, {&a.A5, &a.A9}, {&a.A6, &a.A8}, {&a.A7, &a.A7}, {&a.A9, &a.A11}, {&a.A10, &a.A10}}, []int{2, 2, 2, 2, 1, 4, 4, 4, 4, 2, 4, 2}) + + // d9 = 2(a0 a9 + a1 a8 + a2 a7 + a3 a6 + a4 a5) + 4 * (a4 a11 + a5 a10 + a6 a9 + a7 a8) + 4 * a10 a11 + d9 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A9}, {&a.A1, &a.A8}, {&a.A2, &a.A7}, {&a.A3, &a.A6}, {&a.A4, &a.A5}, {&a.A4, &a.A11}, {&a.A5, &a.A10}, {&a.A6, &a.A9}, {&a.A7, &a.A8}, {&a.A10, &a.A11}}, []int{2, 2, 2, 2, 2, 4, 4, 4, 4, 4}) + + // d10 = 2(a0 a10 + a1 a9 + a2 a8 + a3 a7 + a4 a6) + a5 a5 + 2 * (2 a5 a11 + 2 a6 a10 + 2 a7 a9 + a8 a8) + 2 * a11 a11 + d10 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A10}, {&a.A1, &a.A9}, {&a.A2, &a.A8}, {&a.A3, &a.A7}, {&a.A4, &a.A6}, {&a.A5, &a.A5}, {&a.A5, &a.A11}, {&a.A6, &a.A10}, {&a.A7, &a.A9}, {&a.A8, &a.A8}, {&a.A11, &a.A11}}, []int{2, 2, 2, 2, 2, 1, 4, 4, 4, 2, 2}) + + // d11 = 2(a0 a11 + a1 a10 + a2 a9 + a3 a8 + a4 a7 + a5 a6) + 4 * (a6 a11 + a7 a10 + a8 a9) + d11 := e.fp.Eval([][]*baseEl{{&a.A0, &a.A11}, {&a.A1, &a.A10}, {&a.A2, &a.A9}, {&a.A3, &a.A8}, {&a.A4, &a.A7}, {&a.A5, &a.A6}, {&a.A6, &a.A11}, {&a.A7, &a.A10}, {&a.A8, &a.A9}}, []int{2, 2, 2, 2, 2, 2, 4, 4, 4}) + + return &E12{ + A0: *d0, + A1: *d1, + A2: *d2, + A3: *d3, + A4: *d4, + A5: *d5, + A6: *d6, + A7: *d7, + A8: *d8, + A9: *d9, + A10: *d10, + A11: *d11, + } } func (e Ext12) Inverse(x *E12) *E12 { - res, err := e.fp.NewHint(inverseE12Hint, 12, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) + res, err := e.fp.NewHint(inverseE12Hint, 12, &x.A0, &x.A1, &x.A2, &x.A3, &x.A4, &x.A5, &x.A6, &x.A7, &x.A8, &x.A9, &x.A10, &x.A11) if err != nil { // err is non-nil only for invalid number of inputs panic(err) } - inv := E12{ - C0: E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - }, - C1: E6{ - B0: E2{A0: *res[6], A1: *res[7]}, - B1: E2{A0: *res[8], A1: *res[9]}, - B2: E2{A0: *res[10], A1: *res[11]}, - }, - } - + inv := E12{A0: *res[0], A1: *res[1], A2: *res[2], A3: *res[3], A4: *res[4], A5: *res[5], A6: *res[6], A7: *res[7], A8: *res[8], A9: *res[9], A10: *res[10], A11: *res[11]} one := e.One() // 1 == inv * x @@ -217,41 +403,302 @@ func (e Ext12) Inverse(x *E12) *E12 { } func (e Ext12) DivUnchecked(x, y *E12) *E12 { - res, err := e.fp.NewHint(divE12Hint, 12, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1, &y.C0.B0.A0, &y.C0.B0.A1, &y.C0.B1.A0, &y.C0.B1.A1, &y.C0.B2.A0, &y.C0.B2.A1, &y.C1.B0.A0, &y.C1.B0.A1, &y.C1.B1.A0, &y.C1.B1.A1, &y.C1.B2.A0, &y.C1.B2.A1) - + res, err := e.fp.NewHint(divE12Hint, 12, &x.A0, &x.A1, &x.A2, &x.A3, &x.A4, &x.A5, &x.A6, &x.A7, &x.A8, &x.A9, &x.A10, &x.A11, &y.A0, &y.A1, &y.A2, &y.A3, &y.A4, &y.A5, &y.A6, &y.A7, &y.A8, &y.A9, &y.A10, &y.A11) if err != nil { // err is non-nil only for invalid number of inputs panic(err) } - div := E12{ - C0: E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - }, - C1: E6{ - B0: E2{A0: *res[6], A1: *res[7]}, - B1: E2{A0: *res[8], A1: *res[9]}, - B2: E2{A0: *res[10], A1: *res[11]}, - }, - } + div := E12{A0: *res[0], A1: *res[1], A2: *res[2], A3: *res[3], A4: *res[4], A5: *res[5], A6: *res[6], A7: *res[7], A8: *res[8], A9: *res[9], A10: *res[10], A11: *res[11]} - // x == div * y + // x = div * y _x := e.Mul(&div, y) e.AssertIsEqual(x, _x) return &div + +} + +func (e Ext12) AssertIsEqual(a, b *E12) { + e.fp.AssertIsEqual(&a.A0, &b.A0) + e.fp.AssertIsEqual(&a.A1, &b.A1) + e.fp.AssertIsEqual(&a.A2, &b.A2) + e.fp.AssertIsEqual(&a.A3, &b.A3) + e.fp.AssertIsEqual(&a.A4, &b.A4) + e.fp.AssertIsEqual(&a.A5, &b.A5) + e.fp.AssertIsEqual(&a.A6, &b.A6) + e.fp.AssertIsEqual(&a.A7, &b.A7) + e.fp.AssertIsEqual(&a.A8, &b.A8) + e.fp.AssertIsEqual(&a.A9, &b.A9) + e.fp.AssertIsEqual(&a.A10, &b.A10) + e.fp.AssertIsEqual(&a.A11, &b.A11) +} + +func (e Ext12) IsEqual(x, y *E12) frontend.Variable { + diff0 := e.fp.Sub(&x.A0, &y.A0) + diff1 := e.fp.Sub(&x.A1, &y.A1) + diff2 := e.fp.Sub(&x.A2, &y.A2) + diff3 := e.fp.Sub(&x.A3, &y.A3) + diff4 := e.fp.Sub(&x.A4, &y.A4) + diff5 := e.fp.Sub(&x.A5, &y.A5) + diff6 := e.fp.Sub(&x.A6, &y.A6) + diff7 := e.fp.Sub(&x.A7, &y.A7) + diff8 := e.fp.Sub(&x.A8, &y.A8) + diff9 := e.fp.Sub(&x.A9, &y.A9) + diff10 := e.fp.Sub(&x.A10, &y.A10) + diff11 := e.fp.Sub(&x.A11, &y.A11) + isZero0 := e.fp.IsZero(diff0) + isZero1 := e.fp.IsZero(diff1) + isZero2 := e.fp.IsZero(diff2) + isZero3 := e.fp.IsZero(diff3) + isZero4 := e.fp.IsZero(diff4) + isZero5 := e.fp.IsZero(diff5) + isZero6 := e.fp.IsZero(diff6) + isZero7 := e.fp.IsZero(diff7) + isZero8 := e.fp.IsZero(diff8) + isZero9 := e.fp.IsZero(diff9) + isZero10 := e.fp.IsZero(diff10) + isZero11 := e.fp.IsZero(diff11) + + return e.api.And( + e.api.And( + e.api.And(e.api.And(isZero0, isZero1), e.api.And(isZero2, isZero3)), + e.api.And(e.api.And(isZero4, isZero5), e.api.And(isZero6, isZero7)), + ), + e.api.And(e.api.And(isZero8, isZero9), e.api.And(isZero10, isZero11)), + ) +} + +func (e Ext12) Copy(x *E12) *E12 { + return &E12{ + A0: x.A0, + A1: x.A1, + A2: x.A2, + A3: x.A3, + A4: x.A4, + A5: x.A5, + A6: x.A6, + A7: x.A7, + A8: x.A8, + A9: x.A9, + A10: x.A10, + A11: x.A11, + } +} + +// tower to direct extension conversion +func FromE12(a *bls12381.E12) E12 { + // gnark-crypto uses a quadratic over cubic over quadratic 12th extension of Fp. + // The two towers are isomorphic and the coefficients are permuted as follows: + // a000 a001 a010 a011 a020 a021 a100 a101 a110 a111 a120 a121 + // a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 + // + // A0 = a000 - a001 + // A1 = a100 - a101 + // A2 = a010 - a011 + // A3 = a110 - a111 + // A4 = a020 - a021 + // A5 = a120 - a121 + // A6 = a001 + // A7 = a101 + // A8 = a011 + // A9 = a111 + // A10 = a021 + // A11 = a121 + + var c0, c1, c2, c3, c4, c5 fp.Element + c0.Sub(&a.C0.B0.A0, &a.C0.B0.A1) + c1.Sub(&a.C1.B0.A0, &a.C1.B0.A1) + c2.Sub(&a.C0.B1.A0, &a.C0.B1.A1) + c3.Sub(&a.C1.B1.A0, &a.C1.B1.A1) + c4.Sub(&a.C0.B2.A0, &a.C0.B2.A1) + c5.Sub(&a.C1.B2.A0, &a.C1.B2.A1) + + return E12{ + A0: emulated.ValueOf[emulated.BLS12381Fp](c0), + A1: emulated.ValueOf[emulated.BLS12381Fp](c1), + A2: emulated.ValueOf[emulated.BLS12381Fp](c2), + A3: emulated.ValueOf[emulated.BLS12381Fp](c3), + A4: emulated.ValueOf[emulated.BLS12381Fp](c4), + A5: emulated.ValueOf[emulated.BLS12381Fp](c5), + A6: emulated.ValueOf[emulated.BLS12381Fp](a.C0.B0.A1), + A7: emulated.ValueOf[emulated.BLS12381Fp](a.C1.B0.A1), + A8: emulated.ValueOf[emulated.BLS12381Fp](a.C0.B1.A1), + A9: emulated.ValueOf[emulated.BLS12381Fp](a.C1.B1.A1), + A10: emulated.ValueOf[emulated.BLS12381Fp](a.C0.B2.A1), + A11: emulated.ValueOf[emulated.BLS12381Fp](a.C1.B2.A1), + } +} + +func (e Ext12) ToTower(a *E12) [12]*baseEl { + // gnark-crypto uses a quadratic over cubic over quadratic 12th extension of Fp. + // The two towers are isomorphic and the coefficients are permuted as follows: + // + // tower = a000 a001 a010 a011 a020 a021 a100 a101 a110 a111 a120 a121 + // direct = a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 + // + // a000 = A0 + A6 + // a001 = A6 + // a010 = A2 + A8 + // a011 = A8 + // a020 = A4 + A10 + // a021 = A10 + // a100 = A1 + A7 + // a101 = A7 + // a110 = A3 + A9 + // a111 = A9 + // a120 = A5 + A11 + // a121 = A11 + a000 := e.fp.Add(&a.A0, &a.A6) + a001 := &a.A6 + a010 := e.fp.Add(&a.A2, &a.A8) + a011 := &a.A8 + a020 := e.fp.Add(&a.A4, &a.A10) + a021 := &a.A10 + a100 := e.fp.Add(&a.A1, &a.A7) + a101 := &a.A7 + a110 := e.fp.Add(&a.A3, &a.A9) + a111 := &a.A9 + a120 := e.fp.Add(&a.A5, &a.A11) + a121 := &a.A11 + + tower := [12]*baseEl{a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121} + return tower +} + +func (e Ext12) FromTower(tower [12]*baseEl) *E12 { + // gnark-crypto uses a quadratic over cubic over quadratic 12th extension of Fp. + // The two towers are isomorphic and the coefficients are permuted as follows: + // + // tower = a000 a001 a010 a011 a020 a021 a100 a101 a110 a111 a120 a121 + // direct = a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 + // + // A0 = a000 - a001 + // A1 = a100 - a101 + // A2 = a010 - a011 + // A3 = a110 - a111 + // A4 = a020 - a021 + // A5 = a120 - a121 + // A6 = a001 + // A7 = a101 + // A8 = a011 + // A9 = a111 + // A10 = a021 + // A11 = a121 + A0 := e.fp.Sub(tower[0], tower[1]) + A1 := e.fp.Sub(tower[6], tower[7]) + A2 := e.fp.Sub(tower[2], tower[3]) + A3 := e.fp.Sub(tower[8], tower[9]) + A4 := e.fp.Sub(tower[4], tower[5]) + A5 := e.fp.Sub(tower[10], tower[11]) + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *tower[1], + A7: *tower[7], + A8: *tower[3], + A9: *tower[9], + A10: *tower[5], + A11: *tower[11], + } +} + +// Granger-Scott's cyclotomic square +// https://eprint.iacr.org/2009/565.pdf, 3.2 +func (e Ext12) CyclotomicSquareGS(x *E12) *E12 { + tower := e.ToTower(x) + + mone := e.fp.NewElement(-1) + z000 := e.fp.Eval([][]*baseEl{{tower[8], tower[8]}, {mone, tower[9], tower[9]}, {mone, tower[8], tower[9]}, {tower[0], tower[0]}, {mone, tower[1], tower[1]}, {mone, tower[0]}}, []int{3, 3, 6, 3, 3, 2}) + z001 := e.fp.Eval([][]*baseEl{{tower[8], tower[8]}, {mone, tower[9], tower[9]}, {tower[8], tower[9]}, {tower[0], tower[1]}, {mone, tower[1]}}, []int{3, 3, 6, 6, 2}) + z010 := e.fp.Eval([][]*baseEl{{tower[4], tower[4]}, {mone, tower[5], tower[5]}, {mone, tower[4], tower[5]}, {tower[6], tower[6]}, {mone, tower[7], tower[7]}, {mone, tower[2]}}, []int{3, 3, 6, 3, 3, 2}) + z011 := e.fp.Eval([][]*baseEl{{tower[4], tower[4]}, {mone, tower[5], tower[5]}, {tower[4], tower[5]}, {tower[6], tower[7]}, {mone, tower[3]}}, []int{3, 3, 6, 6, 2}) + z020 := e.fp.Eval([][]*baseEl{{tower[10], tower[10]}, {mone, tower[11], tower[11]}, {mone, tower[10], tower[11]}, {tower[2], tower[2]}, {mone, tower[3], tower[3]}, {mone, tower[4]}}, []int{3, 3, 6, 3, 3, 2}) + z021 := e.fp.Eval([][]*baseEl{{tower[10], tower[10]}, {mone, tower[11], tower[11]}, {tower[10], tower[11]}, {tower[2], tower[3]}, {mone, tower[5]}}, []int{3, 3, 6, 6, 2}) + z100 := e.fp.Eval([][]*baseEl{{tower[2], tower[10]}, {mone, tower[3], tower[11]}, {mone, tower[2], tower[11]}, {mone, tower[3], tower[10]}, {tower[6]}}, []int{6, 6, 6, 6, 2}) + z101 := e.fp.Eval([][]*baseEl{{tower[2], tower[10]}, {mone, tower[3], tower[11]}, {tower[2], tower[11]}, {tower[3], tower[10]}, {tower[7]}}, []int{6, 6, 6, 6, 2}) + z110 := e.fp.Eval([][]*baseEl{{tower[0], tower[8]}, {mone, tower[1], tower[9]}, {tower[8]}}, []int{6, 6, 2}) + z111 := e.fp.Eval([][]*baseEl{{tower[0], tower[9]}, {tower[1], tower[8]}, {tower[9]}}, []int{6, 6, 2}) + z120 := e.fp.Eval([][]*baseEl{{tower[4], tower[6]}, {mone, tower[5], tower[7]}, {tower[10]}}, []int{6, 6, 2}) + z121 := e.fp.Eval([][]*baseEl{{tower[4], tower[7]}, {tower[5], tower[6]}, {tower[11]}}, []int{6, 6, 2}) + + direct := e.FromTower([12]*baseEl{z000, z001, z010, z011, z020, z021, z100, z101, z110, z111, z120, z121}) + + return direct } -func (e Ext12) Select(selector frontend.Variable, z1, z0 *E12) *E12 { - c0 := e.Ext6.Select(selector, &z1.C0, &z0.C0) - c1 := e.Ext6.Select(selector, &z1.C1, &z0.C1) - return &E12{C0: *c0, C1: *c1} +func (e Ext12) Frobenius(a *E12) *E12 { + tower := e.ToTower(a) + + tower[1] = e.fp.Neg(tower[1]) + tower[3] = e.fp.Neg(tower[3]) + tower[5] = e.fp.Neg(tower[5]) + tower[7] = e.fp.Neg(tower[7]) + tower[9] = e.fp.Neg(tower[9]) + tower[11] = e.fp.Neg(tower[11]) + + t1 := e.Ext2.MulByNonResidue1Power2(&E2{A0: *tower[2], A1: *tower[3]}) + t2 := e.Ext2.MulByNonResidue1Power4(&E2{A0: *tower[4], A1: *tower[5]}) + t3 := e.Ext2.MulByNonResidue1Power1(&E2{A0: *tower[6], A1: *tower[7]}) + t4 := e.Ext2.MulByNonResidue1Power3(&E2{A0: *tower[8], A1: *tower[9]}) + t5 := e.Ext2.MulByNonResidue1Power5(&E2{A0: *tower[10], A1: *tower[11]}) + + A0 := e.fp.Sub(tower[0], tower[1]) + A1 := e.fp.Sub(&t3.A0, &t3.A1) + A2 := e.fp.Sub(&t1.A0, &t1.A1) + A3 := e.fp.Sub(&t4.A0, &t4.A1) + A4 := e.fp.Sub(&t2.A0, &t2.A1) + A5 := e.fp.Sub(&t5.A0, &t5.A1) + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *tower[1], + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + A11: t5.A1, + } } -func (e Ext12) Lookup2(s1, s2 frontend.Variable, a, b, c, d *E12) *E12 { - c0 := e.Ext6.Lookup2(s1, s2, &a.C0, &b.C0, &c.C0, &d.C0) - c1 := e.Ext6.Lookup2(s1, s2, &a.C1, &b.C1, &c.C1, &d.C1) - return &E12{C0: *c0, C1: *c1} +func (e Ext12) FrobeniusSquare(a *E12) *E12 { + tower := e.ToTower(a) + + t1 := e.Ext2.MulByNonResidue2Power2(&E2{A0: *tower[2], A1: *tower[3]}) + t2 := e.Ext2.MulByNonResidue2Power4(&E2{A0: *tower[4], A1: *tower[5]}) + t3 := e.Ext2.MulByNonResidue2Power1(&E2{A0: *tower[6], A1: *tower[7]}) + t4 := e.Ext2.MulByNonResidue2Power3(&E2{A0: *tower[8], A1: *tower[9]}) + t5 := e.Ext2.MulByNonResidue2Power5(&E2{A0: *tower[10], A1: *tower[11]}) + + A0 := e.fp.Sub(tower[0], tower[1]) + A1 := e.fp.Sub(&t3.A0, &t3.A1) + A2 := e.fp.Sub(&t1.A0, &t1.A1) + A3 := e.fp.Sub(&t4.A0, &t4.A1) + A4 := e.fp.Sub(&t2.A0, &t2.A1) + A5 := e.fp.Sub(&t5.A0, &t5.A1) + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *tower[1], + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + A11: t5.A1, + } } diff --git a/std/algebra/emulated/fields_bls12381/e12_pairing.go b/std/algebra/emulated/fields_bls12381/e12_pairing.go index 7e4f26d9b4..1ae94d3dfa 100644 --- a/std/algebra/emulated/fields_bls12381/e12_pairing.go +++ b/std/algebra/emulated/fields_bls12381/e12_pairing.go @@ -1,17 +1,22 @@ package fields_bls12381 -import "github.com/consensys/gnark/std/math/emulated" +func (e Ext12) nSquare(z *E12, n int) *E12 { + for i := 0; i < n; i++ { + z = e.Square(z) + } + return z +} func (e Ext12) nSquareGS(z *E12, n int) *E12 { for i := 0; i < n; i++ { - z = e.CyclotomicSquare(z) + z = e.CyclotomicSquareGS(z) } return z } -// Expt sets z to x^t in E12 and return z +// ExptNeg sets z to x^t in E12 and return z // where t = -u = 15132376222941642752 -func (e Ext12) Expt(x *E12) *E12 { +func (e Ext12) ExptNeg(x *E12) *E12 { // Expt computation is derived from the addition chain: // // _10 = 2*1 @@ -26,108 +31,26 @@ func (e Ext12) Expt(x *E12) *E12 { // // Generated by github.com/mmcloughlin/addchain v0.4.0. - z := e.CyclotomicSquare(x) + z := e.Square(x) z = e.Mul(x, z) - z = e.nSquareGS(z, 2) + z = e.nSquare(z, 2) z = e.Mul(x, z) - z = e.nSquareGS(z, 3) + z = e.nSquare(z, 3) z = e.Mul(x, z) - z = e.nSquareGS(z, 9) + z = e.nSquare(z, 9) z = e.Mul(x, z) - z = e.nSquareGS(z, 32) - z = e.Mul(x, z) - z = e.nSquareGS(z, 15) - z = e.CyclotomicSquare(z) - - return z -} - -// ExpByU sets z to x^U in E12 and return z -// where U = (u-1)^2/3 = 76329603384216526031706109802092473003 -func (e Ext12) ExpByU(x *E12) *E12 { - // ExpByU computation is derived from the addition chain: - // - // _10 = 2*1 - // _11 = 1 + _10 - // _110 = 2*_11 - // _111 = 1 + _110 - // _1000 = 1 + _111 - // _100000 = _1000 << 2 - // _100011 = _11 + _100000 - // _101010 = _111 + _100011 - // _1010100 = 2*_101010 - // _1010101 = 1 + _1010100 - // _1010110 = 1 + _1010101 - // _1011001 = _11 + _1010110 - // _1100001 = _1000 + _1011001 - // _10101011 = _1010101 + _1010110 - // _11000010 = 2*_1100001 - // _11100101 = _100011 + _11000010 - // i49 = ((_11100101 << 7 + _1011001) << 5 + _11) << 18 - // i68 = ((_1010101 + i49) << 9 + _10101011) << 7 + _1100001 - // i85 = (i68 << 9 + _10 + _10101011) << 5 + _11 - // i136 = ((i85 << 17 + _1010101) << 9 + _10101011) << 23 - // return (_1010101 + i136) << 9 + _10101011 - // - // Operations: 124 squares 23 multiplies - // - // Generated by github.com/mmcloughlin/addchain v0.4.0. - - t2 := e.CyclotomicSquare(x) - t1 := e.Mul(x, t2) - z := e.CyclotomicSquare(t1) + z = e.nSquare(z, 32) z = e.Mul(x, z) - t3 := e.Mul(x, z) - t0 := e.CyclotomicSquare(t3) - t0 = e.CyclotomicSquare(t0) - t5 := e.Mul(t1, t0) - z = e.Mul(z, t5) - z = e.CyclotomicSquare(z) - t0 = e.Mul(x, z) - z = e.Mul(x, t0) - t4 := e.Mul(t1, z) - t3 = e.Mul(t3, t4) - z = e.Mul(t0, z) - t6 := e.CyclotomicSquare(t3) - t5 = e.Mul(t5, t6) - t5 = e.nSquareGS(t5, 7) - t4 = e.Mul(t4, t5) - t4 = e.nSquareGS(t4, 5) - t4 = e.Mul(t1, t4) - t4 = e.nSquareGS(t4, 18) - t4 = e.Mul(t0, t4) - t4 = e.nSquareGS(t4, 9) - t4 = e.Mul(z, t4) - t4 = e.nSquareGS(t4, 7) - t3 = e.Mul(t3, t4) - t3 = e.nSquareGS(t3, 9) - t2 = e.Mul(t2, t3) - t2 = e.Mul(z, t2) - t2 = e.nSquareGS(t2, 5) - t1 = e.Mul(t1, t2) - t1 = e.nSquareGS(t1, 17) - t1 = e.Mul(t0, t1) - t1 = e.nSquareGS(t1, 9) - t1 = e.Mul(z, t1) - t1 = e.nSquareGS(t1, 23) - t0 = e.Mul(t0, t1) - t0 = e.nSquareGS(t0, 9) - z = e.Mul(z, t0) + z = e.nSquare(z, 15) + z = e.Square(z) return z } -func (e Ext12) nSquareTorus(z *E6, n int) *E6 { - for i := 0; i < n; i++ { - z = e.SquareTorus(z) - } - return z -} - -// ExptHalfTorus set z to x^(t/2) in E6 and return z -// const t/2 uint64 = 7566188111470821376 // negative -func (e Ext12) ExptHalfTorus(x *E6) *E6 { - // FixedExp computation is derived from the addition chain: +// ExptGS sets z to x^t in E12 and return z +// where t = u = -15132376222941642752 +func (e Ext12) ExptGS(x *E12) *E12 { + // ExptGS computation is derived from the addition chain: // // _10 = 2*1 // _11 = 1 + _10 @@ -140,334 +63,97 @@ func (e Ext12) ExptHalfTorus(x *E6) *E6 { // Operations: 62 squares 5 multiplies // // Generated by github.com/mmcloughlin/addchain v0.4.0. - - // Step 1: z = x^0x2 - z := e.SquareTorus(x) - - // Step 2: z = x^0x3 - z = e.MulTorus(x, z) - - z = e.SquareTorus(z) - z = e.SquareTorus(z) - - // Step 5: z = x^0xd - z = e.MulTorus(x, z) - - // Step 8: z = x^0x68 - z = e.nSquareTorus(z, 3) - - // Step 9: z = x^0x69 - z = e.MulTorus(x, z) - - // Step 18: z = x^0xd200 - z = e.nSquareTorus(z, 9) - - // Step 19: z = x^0xd201 - z = e.MulTorus(x, z) - - // Step 51: z = x^0xd20100000000 - z = e.nSquareTorus(z, 32) - - // Step 52: z = x^0xd20100000001 - z = e.MulTorus(x, z) - - // Step 67: z = x^0x6900800000008000 - z = e.nSquareTorus(z, 15) - - z = e.InverseTorus(z) // because tAbsVal is negative - + z := e.ExptHalfGS(x) + z = e.CyclotomicSquareGS(z) return z } -// ExptTorus set z to xᵗ in E6 and return z -// const t uint64 = 15132376222941642752 // negative -func (e Ext12) ExptTorus(x *E6) *E6 { - z := e.ExptHalfTorus(x) - z = e.SquareTorus(z) - return z -} - -// MulBy014 multiplies z by an E12 sparse element of the form -// -// E12{ -// C0: E6{B0: c0, B1: c1, B2: 0}, -// C1: E6{B0: 0, B1: 1, B2: 0}, -// } -func (e *Ext12) MulBy014(z *E12, c0, c1 *E2) *E12 { - - a := e.MulBy01(&z.C0, c0, c1) - - var b E6 - // Mul by E6{0, 1, 0} - b.B0 = *e.Ext2.MulByNonResidue(&z.C1.B2) - b.B2 = z.C1.B1 - b.B1 = z.C1.B0 - - one := e.Ext2.One() - d := e.Ext2.Add(c1, one) - - zC1 := e.Ext6.Add(&z.C1, &z.C0) - zC1 = e.Ext6.MulBy01(zC1, c0, d) - tmp := e.Ext6.Add(&b, a) - zC1 = e.Ext6.Sub(zC1, tmp) - zC0 := e.Ext6.MulByNonResidue(&b) - zC0 = e.Ext6.Add(zC0, a) - - return &E12{ - C0: *zC0, - C1: *zC1, - } -} - -// multiplies two E12 sparse element of the form: -// -// E12{ -// C0: E6{B0: c0, B1: c1, B2: 0}, -// C1: E6{B0: 0, B1: 1, B2: 0}, -// } -// -// and -// -// E12{ -// C0: E6{B0: d0, B1: d1, B2: 0}, -// C1: E6{B0: 0, B1: 1, B2: 0}, -// } -func (e Ext12) Mul014By014(d0, d1, c0, c1 *E2) [5]*E2 { - x0 := e.Ext2.Mul(c0, d0) - x1 := e.Ext2.Mul(c1, d1) - x04 := e.Ext2.Add(c0, d0) - tmp := e.Ext2.Add(c0, c1) - x01 := e.Ext2.Add(d0, d1) - x01 = e.Ext2.Mul(x01, tmp) - tmp = e.Ext2.Add(x1, x0) - x01 = e.Ext2.Sub(x01, tmp) - x14 := e.Ext2.Add(c1, d1) - - zC0B0 := e.Ext2.NonResidue() - zC0B0 = e.Ext2.Add(zC0B0, x0) - - return [5]*E2{zC0B0, x01, x1, x04, x14} -} - -// MulBy01245 multiplies z by an E12 sparse element of the form -// -// E12{ -// C0: E6{B0: c0, B1: c1, B2: c2}, -// C1: E6{B0: 0, B1: c4, B2: c5}, -// } -func (e *Ext12) MulBy01245(z *E12, x [5]*E2) *E12 { - c0 := &E6{B0: *x[0], B1: *x[1], B2: *x[2]} - c1 := &E6{B0: *e.Ext2.Zero(), B1: *x[3], B2: *x[4]} - a := e.Ext6.Add(&z.C0, &z.C1) - b := e.Ext6.Add(c0, c1) - a = e.Ext6.Mul(a, b) - b = e.Ext6.Mul(&z.C0, c0) - c := e.Ext6.MulBy12(&z.C1, x[3], x[4]) - d := e.Ext6.Add(c, b) - z1 := e.Ext6.Sub(a, d) - z0 := e.Ext6.MulByNonResidue(c) - z0 = e.Ext6.Add(z0, b) - return &E12{ - C0: *z0, - C1: *z1, - } -} - -// Torus-based arithmetic: -// -// After the easy part of the final exponentiation the elements are in a proper -// subgroup of Fpk (E12) that coincides with some algebraic tori. The elements -// are in the torus Tk(Fp) and thus in each torus Tk/d(Fp^d) for d|k, d≠k. We -// take d=6. So the elements are in T2(Fp6). -// Let G_{q,2} = {m ∈ Fq^2 | m^(q+1) = 1} where q = p^6. -// When m.C1 = 0, then m.C0 must be 1 or −1. -// -// We recall the tower construction: -// -// 𝔽p²[u] = 𝔽p/u²+1 -// 𝔽p⁶[v] = 𝔽p²/v³-1-u -// 𝔽p¹²[w] = 𝔽p⁶/w²-v - -// CompressTorus compresses x ∈ E12 to (x.C0 + 1)/x.C1 ∈ E6 -func (e Ext12) CompressTorus(x *E12) *E6 { - // x ∈ G_{q,2} \ {-1,1} - y := e.Ext6.Add(&x.C0, e.Ext6.One()) - y = e.Ext6.DivUnchecked(y, &x.C1) - return y -} - -// DecompressTorus decompresses y ∈ E6 to (y+w)/(y-w) ∈ E12 -func (e Ext12) DecompressTorus(y *E6) *E12 { - var n, d E12 - one := e.Ext6.One() - n.C0 = *y - n.C1 = *one - d.C0 = *y - d.C1 = *e.Ext6.Neg(one) - - x := e.DivUnchecked(&n, &d) - return x -} - -// MulTorus multiplies two compressed elements y1, y2 ∈ E6 -// and returns (y1 * y2 + v)/(y1 + y2) -// N.B.: we use MulTorus in the final exponentiation throughout y1 ≠ -y2 always. -func (e Ext12) MulTorus(y1, y2 *E6) *E6 { - n := e.Ext6.Mul(y1, y2) - n.B1 = *e.Ext2.Add(&n.B1, e.Ext2.One()) - d := e.Ext6.Add(y1, y2) - y3 := e.Ext6.DivUnchecked(n, d) - return y3 -} +func (e Ext12) ExptHalfGS(x *E12) *E12 { + z := e.CyclotomicSquareGS(x) + z = e.Mul(x, z) + z = e.nSquareGS(z, 2) + z = e.Mul(x, z) + z = e.nSquareGS(z, 3) + z = e.Mul(x, z) + z = e.nSquareGS(z, 9) + z = e.Mul(x, z) + z = e.nSquareGS(z, 32) + z = e.Mul(x, z) + z = e.nSquareGS(z, 15) + z = e.Conjugate(z) -// InverseTorus inverses a compressed elements y ∈ E6 -// and returns -y -func (e Ext12) InverseTorus(y *E6) *E6 { - return e.Ext6.Neg(y) + return z } -// SquareTorus squares a compressed elements y ∈ E6 -// and returns (y + v/y)/2 +// MulBy02368 multiplies a by an E12 sparse element b of the form // -// It uses a hint to verify that (2x-y)y = v saving one E6 AssertIsEqual. -func (e Ext12) SquareTorus(y *E6) *E6 { - res, err := e.fp.NewHint(squareTorusHint, 6, &y.B0.A0, &y.B0.A1, &y.B1.A0, &y.B1.A1, &y.B2.A0, &y.B2.A1) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } +// b.A0 = c00 - c01 +// b.A1 = 0 +// b.A2 = c10 - c11 +// b.A3 = 1 +// b.A4 = 0 +// b.A5 = 0 +// b.A6 = c01 +// b.A7 = 0 +// b.A8 = c11 +// b.A9 = 0 +// b.A10 = 0 +// b.A11 = 0 +func (e *Ext12) MulBy02368(a *E12, c0, c1 *E2) *E12 { + b0 := e.fp.Sub(&c0.A0, &c0.A1) + b2 := e.fp.Sub(&c1.A0, &c1.A1) + b6 := &c0.A1 + b8 := &c1.A1 + mone := e.fp.NewElement(-1) - sq := E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - } + // d0 = a0 b0 - 2 * (a4 b8 + a6 b6 + a10 b2 + a9) - 4 * a10 b8 + d0 := e.fp.Eval([][]*baseEl{{&a.A0, b0}, {mone, &a.A4, b8}, {mone, &a.A6, b6}, {mone, &a.A9}, {mone, &a.A10, b2}, {mone, &a.A10, b8}}, []int{1, 2, 2, 2, 2, 4}) - // v = (2x-y)y - v := e.Ext6.Double(&sq) - v = e.Ext6.Sub(v, y) - v = e.Ext6.Mul(v, y) + // d1 = a1 b0 - 2 * (a5 b8 + a7 b6 + a11 b2 + a10) - 4 * a11 b8 + d1 := e.fp.Eval([][]*baseEl{{&a.A1, b0}, {mone, &a.A5, b8}, {mone, &a.A7, b6}, {mone, &a.A10}, {mone, &a.A11, b2}, {mone, &a.A11, b8}}, []int{1, 2, 2, 2, 2, 4}) - _v := E6{B0: *e.Ext2.Zero(), B1: *e.Ext2.One(), B2: *e.Ext2.Zero()} - e.Ext6.AssertIsEqual(v, &_v) + // d2 = a0 b2 + a2 b0 - 2 * (a6 b8 + a8 b6 + a11) + d2 := e.fp.Eval([][]*baseEl{{&a.A0, b2}, {&a.A2, b0}, {mone, &a.A6, b8}, {mone, &a.A8, b6}, {mone, &a.A11}}, []int{1, 1, 2, 2, 2}) - return &sq + // d3 = a0 + a1 b2 + a3 b0 - 2 * (a7 b8 + a9 b6) + d3 := e.fp.Eval([][]*baseEl{{&a.A0}, {&a.A1, b2}, {&a.A3, b0}, {mone, &a.A7, b8}, {mone, &a.A9, b6}}, []int{1, 1, 1, 2, 2}) -} + // d4 = a1 + a2 b2 + a4 b0 - 2 * (a8 b8 + a10 b6) + d4 := e.fp.Eval([][]*baseEl{{&a.A1}, {&a.A2, b2}, {&a.A4, b0}, {mone, &a.A8, b8}, {mone, &a.A10, b6}}, []int{1, 1, 1, 2, 2}) -// FrobeniusTorus raises a compressed elements y ∈ E6 to the modulus p -// and returns y^p / v^((p-1)/2) -func (e Ext12) FrobeniusTorus(y *E6) *E6 { - t0 := e.Ext2.Conjugate(&y.B0) - t1 := e.Ext2.Conjugate(&y.B1) - t2 := e.Ext2.Conjugate(&y.B2) - t1 = e.Ext2.MulByNonResidue1Power2(t1) - t2 = e.Ext2.MulByNonResidue1Power4(t2) + // d5 = a2 + a3 b2 + a5 b0 - 2 * (a9 b8 + a11 b6) + d5 := e.fp.Eval([][]*baseEl{{&a.A2}, {&a.A3, b2}, {&a.A5, b0}, {mone, &a.A9, b8}, {mone, &a.A11, b6}}, []int{1, 1, 1, 2, 2}) - v0 := E2{emulated.ValueOf[emulated.BLS12381Fp]("877076961050607968509681729531255177986764537961432449499635504522207616027455086505066378536590128544573588734230"), emulated.ValueOf[emulated.BLS12381Fp]("877076961050607968509681729531255177986764537961432449499635504522207616027455086505066378536590128544573588734230")} - res := &E6{B0: *t0, B1: *t1, B2: *t2} - res = e.Ext6.MulBy0(res, &v0) + // d6 = a0 b6 + a3 + a4 b2 + a6 b0 + 2 * (a4 b8 + a6 b6 + a9 + a10 b2 + a10 b8) + d6 := e.fp.Eval([][]*baseEl{{&a.A0, b6}, {&a.A3}, {&a.A4, b2}, {&a.A6, b0}, {&a.A4, b8}, {&a.A6, b6}, {&a.A9}, {&a.A10, b2}, {&a.A10, b8}}, []int{1, 1, 1, 1, 2, 2, 2, 2, 2}) - return res -} + // d7 = a1 b6 + a4 + a5 b2 + a7 b0 + 2 * (a5 b8 + a7 b6 + a10 + a11 b2 + a11 b8) + d7 := e.fp.Eval([][]*baseEl{{&a.A1, b6}, {&a.A4}, {&a.A5, b2}, {&a.A7, b0}, {&a.A5, b8}, {&a.A7, b6}, {&a.A10}, {&a.A11, b2}, {&a.A11, b8}}, []int{1, 1, 1, 1, 2, 2, 2, 2, 2}) -// FrobeniusSquareTorus raises a compressed elements y ∈ E6 to the square modulus p^2 -// and returns y^(p^2) / v^((p^2-1)/2) -func (e Ext12) FrobeniusSquareTorus(y *E6) *E6 { - v0 := emulated.ValueOf[emulated.BLS12381Fp]("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437") - t0 := e.Ext2.MulByElement(&y.B0, &v0) - t1 := e.Ext2.MulByNonResidue2Power2(&y.B1) - t1 = e.Ext2.MulByElement(t1, &v0) - t2 := e.Ext2.MulByNonResidue2Power4(&y.B2) - t2 = e.Ext2.MulByElement(t2, &v0) + // d8 = a0 b8 + a2 b6 + a5 + a6 b2 + a8 b0 + 2 * (a6 b8 + a8 b6 + a11) + d8 := e.fp.Eval([][]*baseEl{{&a.A0, b8}, {&a.A2, b6}, {&a.A5}, {&a.A6, b2}, {&a.A8, b0}, {&a.A6, b8}, {&a.A8, b6}, {&a.A11}}, []int{1, 1, 1, 1, 1, 2, 2, 2}) - return &E6{B0: *t0, B1: *t1, B2: *t2} -} + // d9 = a1 b8 + a3 b6 + a6 + a7 b2 + a9 b0 + 2 * (a7 b8 + a9 b6) + d9 := e.fp.Eval([][]*baseEl{{&a.A1, b8}, {&a.A3, b6}, {&a.A6}, {&a.A7, b2}, {&a.A9, b0}, {&a.A7, b8}, {&a.A9, b6}}, []int{1, 1, 1, 1, 1, 2, 2}) -// AssertFinalExponentiationIsOne checks that a Miller function output x lies in the -// same equivalence class as the reduced pairing. This replaces the final -// exponentiation step in-circuit. -// The method is inspired from [On Proving Pairings] paper by A. Novakovic and -// L. Eagen, and is based on a personal communication with A. Novakovic. -// -// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (e Ext12) AssertFinalExponentiationIsOne(x *E12) { - res, err := e.fp.NewHint(finalExpHint, 18, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } - - residueWitness := E12{ - C0: E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - }, - C1: E6{ - B0: E2{A0: *res[6], A1: *res[7]}, - B1: E2{A0: *res[8], A1: *res[9]}, - B2: E2{A0: *res[10], A1: *res[11]}, - }, - } - // constrain cubicNonResiduePower to be in Fp6 - scalingFactor := E6{ - B0: E2{A0: *res[12], A1: *res[13]}, - B1: E2{A0: *res[14], A1: *res[15]}, - B2: E2{A0: *res[16], A1: *res[17]}, - } - - // Check that x * scalingFactor == residueWitness^(q-u) - // where u=-0xd201000000010000 is the BLS12-381 seed, - // and residueWitness, scalingFactor from the hint. - t0 := e.Frobenius(&residueWitness) - // exponentiation by -u - t1 := e.Expt(&residueWitness) - t0 = e.Mul(t0, t1) - - t1 = &E12{ - C0: *e.Ext6.Mul(&x.C0, &scalingFactor), - C1: *e.Ext6.Mul(&x.C1, &scalingFactor), - } - - e.AssertIsEqual(t0, t1) -} + // d10 = a2 b8 + a4 b6 + a7 + a8 b2 + a10 b0 + 2 * (a8 b8 + a10 b6) + d10 := e.fp.Eval([][]*baseEl{{&a.A2, b8}, {&a.A4, b6}, {&a.A7}, {&a.A8, b2}, {&a.A10, b0}, {&a.A8, b8}, {&a.A10, b6}}, []int{1, 1, 1, 1, 1, 2, 2}) -func (e Ext12) Frobenius(x *E12) *E12 { - t0 := e.Ext2.Conjugate(&x.C0.B0) - t1 := e.Ext2.Conjugate(&x.C0.B1) - t2 := e.Ext2.Conjugate(&x.C0.B2) - t3 := e.Ext2.Conjugate(&x.C1.B0) - t4 := e.Ext2.Conjugate(&x.C1.B1) - t5 := e.Ext2.Conjugate(&x.C1.B2) - t1 = e.Ext2.MulByNonResidue1Power2(t1) - t2 = e.Ext2.MulByNonResidue1Power4(t2) - t3 = e.Ext2.MulByNonResidue1Power1(t3) - t4 = e.Ext2.MulByNonResidue1Power3(t4) - t5 = e.Ext2.MulByNonResidue1Power5(t5) - return &E12{ - C0: E6{ - B0: *t0, - B1: *t1, - B2: *t2, - }, - C1: E6{ - B0: *t3, - B1: *t4, - B2: *t5, - }, - } -} + // d11 = a3 b8 + a5 b6 + a8 + a9 b2 + a11 b0 + 2 * (a9 b8 + a11 b6) + d11 := e.fp.Eval([][]*baseEl{{&a.A3, b8}, {&a.A5, b6}, {&a.A8}, {&a.A9, b2}, {&a.A11, b0}, {&a.A9, b8}, {&a.A11, b6}}, []int{1, 1, 1, 1, 1, 2, 2}) -func (e Ext12) FrobeniusSquare(x *E12) *E12 { - z00 := &x.C0.B0 - z01 := e.Ext2.MulByNonResidue2Power2(&x.C0.B1) - z02 := e.Ext2.MulByNonResidue2Power4(&x.C0.B2) - z10 := e.Ext2.MulByNonResidue2Power1(&x.C1.B0) - z11 := e.Ext2.MulByNonResidue2Power3(&x.C1.B1) - z12 := e.Ext2.MulByNonResidue2Power5(&x.C1.B2) return &E12{ - C0: E6{B0: *z00, B1: *z01, B2: *z02}, - C1: E6{B0: *z10, B1: *z11, B2: *z12}, + A0: *d0, + A1: *d1, + A2: *d2, + A3: *d3, + A4: *d4, + A5: *d5, + A6: *d6, + A7: *d7, + A8: *d8, + A9: *d9, + A10: *d10, + A11: *d11, } } diff --git a/std/algebra/emulated/fields_bls12381/e12_test.go b/std/algebra/emulated/fields_bls12381/e12_test.go index cba62ef2be..5a75b1f807 100644 --- a/std/algebra/emulated/fields_bls12381/e12_test.go +++ b/std/algebra/emulated/fields_bls12381/e12_test.go @@ -1,6 +1,7 @@ package fields_bls12381 import ( + "math/big" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -9,6 +10,34 @@ import ( "github.com/consensys/gnark/test" ) +type e12Convert struct { + A E12 +} + +func (circuit *e12Convert) Define(api frontend.API) error { + e := NewExt12(api) + tower := e.ToTower(&circuit.A) + expected := e.FromTower(tower) + e.AssertIsEqual(expected, &circuit.A) + return nil +} + +func TestConvertFp12(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a bls12381.E12 + _, _ = a.SetRandom() + + witness := e12Convert{ + A: FromE12(&a), + } + + err := test.IsSolved(&e12Convert{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + +} + type e12Add struct { A, B, C E12 } @@ -102,37 +131,6 @@ func TestMulFp12(t *testing.T) { } -type e12Div struct { - A, B, C E12 -} - -func (circuit *e12Div) Define(api frontend.API) error { - e := NewExt12(api) - expected := e.DivUnchecked(&circuit.A, &circuit.B) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestDivFp12(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, b, c bls12381.E12 - _, _ = a.SetRandom() - _, _ = b.SetRandom() - c.Div(&a, &b) - - witness := e12Div{ - A: FromE12(&a), - B: FromE12(&b), - C: FromE12(&c), - } - - err := test.IsSolved(&e12Div{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - type e12Square struct { A, C E12 } @@ -162,81 +160,18 @@ func TestSquareFp12(t *testing.T) { } -type e12Conjugate struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *e12Conjugate) Define(api frontend.API) error { - e := NewExt12(api) - expected := e.Conjugate(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestConjugateFp12(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E12 - _, _ = a.SetRandom() - c.Conjugate(&a) - - witness := e12Conjugate{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&e12Conjugate{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e12Inverse struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *e12Inverse) Define(api frontend.API) error { - e := NewExt12(api) - expected := e.Inverse(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestInverseFp12(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E12 - _, _ = a.SetRandom() - c.Inverse(&a) - - witness := e12Inverse{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&e12Inverse{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e12ExptTorus struct { - A E6 - C E12 `gnark:",public"` +type e12SquareGS struct { + A, C E12 } -func (circuit *e12ExptTorus) Define(api frontend.API) error { +func (circuit *e12SquareGS) Define(api frontend.API) error { e := NewExt12(api) - z := e.ExptTorus(&circuit.A) - expected := e.DecompressTorus(z) + expected := e.CyclotomicSquareGS(&circuit.A) e.AssertIsEqual(expected, &circuit.C) - return nil } -func TestFp12ExptTorus(t *testing.T) { +func TestSquareGSFp12(t *testing.T) { assert := test.NewAssert(t) // witness values @@ -250,332 +185,272 @@ func TestFp12ExptTorus(t *testing.T) { tmp.Mul(&tmp, &a) a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - c.Expt(&a) - _a, _ := a.CompressTorus() - witness := e12ExptTorus{ - A: FromE6(&_a), + c.CyclotomicSquare(&a) + + witness := e12SquareGS{ + A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&e12ExptTorus{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12SquareGS{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) + } -type e12MulBy014 struct { - A E12 `gnark:",public"` - W E12 - B, C E2 +type e12Div struct { + A, B, C E12 } -func (circuit *e12MulBy014) Define(api frontend.API) error { +func (circuit *e12Div) Define(api frontend.API) error { e := NewExt12(api) - res := e.MulBy014(&circuit.A, &circuit.B, &circuit.C) - e.AssertIsEqual(res, &circuit.W) + expected := e.DivUnchecked(&circuit.A, &circuit.B) + e.AssertIsEqual(expected, &circuit.C) return nil } -func TestFp12MulBy014(t *testing.T) { +func TestDivFp12(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, w bls12381.E12 + var a, b, c bls12381.E12 _, _ = a.SetRandom() - var one, b, c bls12381.E2 - one.SetOne() _, _ = b.SetRandom() - _, _ = c.SetRandom() - w.Set(&a) - w.MulBy014(&b, &c, &one) + c.Div(&a, &b) - witness := e12MulBy014{ + witness := e12Div{ A: FromE12(&a), - B: FromE2(&b), - C: FromE2(&c), - W: FromE12(&w), + B: FromE12(&b), + C: FromE12(&c), } - err := test.IsSolved(&e12MulBy014{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12Div{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -// Torus-based arithmetic -type torusCompress struct { +type e12Conjugate struct { A E12 - C E6 `gnark:",public"` + C E12 `gnark:",public"` } -func (circuit *torusCompress) Define(api frontend.API) error { +func (circuit *e12Conjugate) Define(api frontend.API) error { e := NewExt12(api) - expected := e.CompressTorus(&circuit.A) - e.Ext6.AssertIsEqual(expected, &circuit.C) + expected := e.Conjugate(&circuit.A) + e.AssertIsEqual(expected, &circuit.C) + return nil } -func TestTorusCompress(t *testing.T) { +func TestConjugateFp12(t *testing.T) { assert := test.NewAssert(t) // witness values - var a bls12381.E12 + var a, c bls12381.E12 _, _ = a.SetRandom() + c.Conjugate(&a) - // put a in the cyclotomic subgroup - var tmp bls12381.E12 - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - - c, _ := a.CompressTorus() - - witness := torusCompress{ + witness := e12Conjugate{ A: FromE12(&a), - C: FromE6(&c), + C: FromE12(&c), } - err := test.IsSolved(&torusCompress{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12Conjugate{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusDecompress struct { +type e12Inverse struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusDecompress) Define(api frontend.API) error { +func (circuit *e12Inverse) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - expected := e.DecompressTorus(compressed) + expected := e.Inverse(&circuit.A) e.AssertIsEqual(expected, &circuit.C) + return nil } -func TestTorusDecompress(t *testing.T) { +func TestInverseFp12(t *testing.T) { assert := test.NewAssert(t) // witness values - var a bls12381.E12 + var a, c bls12381.E12 _, _ = a.SetRandom() + c.Inverse(&a) - // put a in the cyclotomic subgroup - var tmp bls12381.E12 - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - - d, _ := a.CompressTorus() - c := d.DecompressTorus() - - witness := torusDecompress{ + witness := e12Inverse{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusDecompress{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12Inverse{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusMul struct { +type Frobenius struct { A E12 - B E12 C E12 `gnark:",public"` } -func (circuit *torusMul) Define(api frontend.API) error { +func (circuit *Frobenius) Define(api frontend.API) error { e := NewExt12(api) - compressedA := e.CompressTorus(&circuit.A) - compressedB := e.CompressTorus(&circuit.B) - compressedAB := e.MulTorus(compressedA, compressedB) - expected := e.DecompressTorus(compressedAB) + expected := e.Frobenius(&circuit.A) e.AssertIsEqual(expected, &circuit.C) return nil } -func TestTorusMul(t *testing.T) { +func TestFrobenius(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, b, c, tmp bls12381.E12 + var a, c bls12381.E12 _, _ = a.SetRandom() - _, _ = b.SetRandom() - // put a in the cyclotomic subgroup - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - // put b in the cyclotomic subgroup - tmp.Conjugate(&b) - b.Inverse(&b) - tmp.Mul(&tmp, &b) - b.FrobeniusSquare(&tmp).Mul(&b, &tmp) - - // uncompressed mul - c.Mul(&a, &b) + c.Frobenius(&a) - witness := torusMul{ + witness := Frobenius{ A: FromE12(&a), - B: FromE12(&b), C: FromE12(&c), } - err := test.IsSolved(&torusMul{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&Frobenius{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusInverse struct { +type FrobeniusSquare struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusInverse) Define(api frontend.API) error { +func (circuit *FrobeniusSquare) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.InverseTorus(compressed) - expected := e.DecompressTorus(compressed) + expected := e.FrobeniusSquare(&circuit.A) e.AssertIsEqual(expected, &circuit.C) return nil } -func TestTorusInverse(t *testing.T) { +func TestFrobeniusSquare(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bls12381.E12 + var a, c bls12381.E12 _, _ = a.SetRandom() - // put a in the cyclotomic subgroup - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - - // uncompressed inverse - c.Inverse(&a) + c.FrobeniusSquare(&a) - witness := torusInverse{ + witness := FrobeniusSquare{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusInverse{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&FrobeniusSquare{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusFrobenius struct { +type e12Expt struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusFrobenius) Define(api frontend.API) error { +func (circuit *e12Expt) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.FrobeniusTorus(compressed) - expected := e.DecompressTorus(compressed) + expected := e.ExptNeg(&circuit.A) e.AssertIsEqual(expected, &circuit.C) + return nil } -func TestTorusFrobenius(t *testing.T) { +func TestFp12Expt(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bls12381.E12 + var a, c bls12381.E12 _, _ = a.SetRandom() - // put a in the cyclotomic subgroup - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - - // uncompressed frobenius - c.Frobenius(&a) - - witness := torusFrobenius{ + var xGen big.Int + xGen.SetString("15132376222941642752", 10) + c.Exp(a, &xGen) + witness := e12Expt{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusFrobenius{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12Expt{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusFrobeniusSquare struct { +type e12ExptGS struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusFrobeniusSquare) Define(api frontend.API) error { +func (circuit *e12ExptGS) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.FrobeniusSquareTorus(compressed) - expected := e.DecompressTorus(compressed) + expected := e.ExptGS(&circuit.A) e.AssertIsEqual(expected, &circuit.C) + return nil } -func TestTorusFrobeniusSquare(t *testing.T) { +func TestFp12ExptGS(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bls12381.E12 + var a, c bls12381.E12 _, _ = a.SetRandom() // put a in the cyclotomic subgroup + var tmp bls12381.E12 tmp.Conjugate(&a) a.Inverse(&a) tmp.Mul(&tmp, &a) a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - // uncompressed frobeniusSquare - c.FrobeniusSquare(&a) - - witness := torusFrobeniusSquare{ + c.Expt(&a) + witness := e12ExptGS{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusFrobeniusSquare{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12ExptGS{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusSquare struct { - A E12 - C E12 `gnark:",public"` +type e12MulBy02368 struct { + A E12 `gnark:",public"` + W E12 + B, C E2 } -func (circuit *torusSquare) Define(api frontend.API) error { +func (circuit *e12MulBy02368) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.SquareTorus(compressed) - expected := e.DecompressTorus(compressed) - e.AssertIsEqual(expected, &circuit.C) + res := e.MulBy02368(&circuit.A, &circuit.B, &circuit.C) + e.AssertIsEqual(res, &circuit.W) return nil } -func TestTorusSquare(t *testing.T) { +func TestFp12MulBy02368(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bls12381.E12 + var a, w bls12381.E12 _, _ = a.SetRandom() + var one, b, c bls12381.E2 + one.SetOne() + _, _ = b.SetRandom() + _, _ = c.SetRandom() + w.Set(&a) + w.MulBy014(&b, &c, &one) - // put a in the cyclotomic subgroup - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - - // uncompressed square - c.Square(&a) - - witness := torusSquare{ + witness := e12MulBy02368{ A: FromE12(&a), - C: FromE12(&c), + B: FromE2(&b), + C: FromE2(&c), + W: FromE12(&w), } - err := test.IsSolved(&torusSquare{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12MulBy02368{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) + } diff --git a/std/algebra/emulated/fields_bls12381/e2.go b/std/algebra/emulated/fields_bls12381/e2.go index 30b5ebda49..ba378f6796 100644 --- a/std/algebra/emulated/fields_bls12381/e2.go +++ b/std/algebra/emulated/fields_bls12381/e2.go @@ -184,16 +184,10 @@ func (e Ext2) MulByNonResidue2Power5(x *E2) *E2 { } func (e Ext2) Mul(x, y *E2) *E2 { - - v0 := e.fp.Mul(&x.A0, &y.A0) - v1 := e.fp.Mul(&x.A1, &y.A1) - - b0 := e.fp.Sub(v0, v1) - b1 := e.fp.Add(&x.A0, &x.A1) - tmp := e.fp.Add(&y.A0, &y.A1) - b1 = e.fp.Mul(b1, tmp) - tmp = e.fp.Add(v0, v1) - b1 = e.fp.Sub(b1, tmp) + // b0 = x0*y0 - x1*y1 + b0 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A0}, {e.fp.NewElement(-1), &x.A1, &y.A1}}, []int{1, 1}) + // b1 = x0*y1 + x1*y0 + b1 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A1}, {&x.A1, &y.A0}}, []int{1, 1}) return &E2{ A0: *b0, @@ -262,17 +256,27 @@ func (e Ext2) NonResidue() *E2 { } func (e Ext2) Square(x *E2) *E2 { - a := e.fp.Add(&x.A0, &x.A1) - b := e.fp.Sub(&x.A0, &x.A1) - a = e.fp.Mul(a, b) - b = e.fp.Mul(&x.A0, &x.A1) - b = e.fp.MulConst(b, big.NewInt(2)) + // a = (x0+x1)(x0-x1) = x0^2 - x1^2 + a := e.fp.Eval([][]*baseEl{{&x.A0, &x.A0}, {e.fp.NewElement(-1), &x.A1, &x.A1}}, []int{1, 1}) + // b = 2*x0*x1 + b := e.fp.Eval([][]*baseEl{{&x.A0, &x.A1}}, []int{2}) return &E2{ A0: *a, A1: *b, } } +func (e Ext2) Cube(x *E2) *E2 { + mone := e.fp.NewElement(-1) + // a = x0^3 - 3*x0*x1^2 + a := e.fp.Eval([][]*baseEl{{&x.A0, &x.A0, &x.A0}, {mone, &x.A0, &x.A1, &x.A1}}, []int{1, 3}) + // b = 3*x1*x0^2 - x1^3 + b := e.fp.Eval([][]*baseEl{{&x.A1, &x.A0, &x.A0}, {mone, &x.A1, &x.A1, &x.A1}}, []int{3, 1}) + return &E2{ + A0: *a, + A1: *b, + } +} func (e Ext2) Double(x *E2) *E2 { two := big.NewInt(2) z0 := e.fp.MulConst(&x.A0, two) diff --git a/std/algebra/emulated/fields_bls12381/e6.go b/std/algebra/emulated/fields_bls12381/e6.go deleted file mode 100644 index 22a3596324..0000000000 --- a/std/algebra/emulated/fields_bls12381/e6.go +++ /dev/null @@ -1,465 +0,0 @@ -package fields_bls12381 - -import ( - "math/big" - - bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/internal/frontendtype" -) - -type E6 struct { - B0, B1, B2 E2 -} - -type Ext6 struct { - *Ext2 -} - -func NewExt6(api frontend.API) *Ext6 { - return &Ext6{Ext2: NewExt2(api)} -} - -func (e Ext6) One() *E6 { - z0 := e.Ext2.One() - z1 := e.Ext2.Zero() - z2 := e.Ext2.Zero() - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -func (e Ext6) Zero() *E6 { - z0 := e.Ext2.Zero() - z1 := e.Ext2.Zero() - z2 := e.Ext2.Zero() - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -func (e Ext6) IsZero(z *E6) frontend.Variable { - b0 := e.Ext2.IsZero(&z.B0) - b1 := e.Ext2.IsZero(&z.B1) - b2 := e.Ext2.IsZero(&z.B2) - return e.api.And(e.api.And(b0, b1), b2) -} - -func (e Ext6) Add(x, y *E6) *E6 { - z0 := e.Ext2.Add(&x.B0, &y.B0) - z1 := e.Ext2.Add(&x.B1, &y.B1) - z2 := e.Ext2.Add(&x.B2, &y.B2) - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -func (e Ext6) Neg(x *E6) *E6 { - z0 := e.Ext2.Neg(&x.B0) - z1 := e.Ext2.Neg(&x.B1) - z2 := e.Ext2.Neg(&x.B2) - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -func (e Ext6) Sub(x, y *E6) *E6 { - z0 := e.Ext2.Sub(&x.B0, &y.B0) - z1 := e.Ext2.Sub(&x.B1, &y.B1) - z2 := e.Ext2.Sub(&x.B2, &y.B2) - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -// Mul multiplies two E6 elmts -func (e Ext6) Mul(x, y *E6) *E6 { - if ft, ok := e.api.(frontendtype.FrontendTyper); ok { - switch ft.FrontendType() { - case frontendtype.R1CS: - return e.mulToom3OverKaratsuba(x, y) - case frontendtype.SCS: - return e.mulKaratsubaOverKaratsuba(x, y) - } - } - return e.mulKaratsubaOverKaratsuba(x, y) -} - -func (e Ext6) mulToom3OverKaratsuba(x, y *E6) *E6 { - // Toom-Cook-3x over Karatsuba: - // We start by computing five interpolation points – these are evaluations of - // the product x(u)y(u) with u ∈ {0, ±1, 2, ∞}: - // - // v0 = x(0)y(0) = x.A0 * y.A0 - // v1 = x(1)y(1) = (x.A0 + x.A1 + x.A2)(y.A0 + y.A1 + y.A2) - // v2 = x(−1)y(−1) = (x.A0 − x.A1 + x.A2)(y.A0 − y.A1 + y.A2) - // v3 = x(2)y(2) = (x.A0 + 2x.A1 + 4x.A2)(y.A0 + 2y.A1 + 4y.A2) - // v4 = x(∞)y(∞) = x.A2 * y.A2 - - v0 := e.Ext2.Mul(&x.B0, &y.B0) - - t1 := e.Ext2.Add(&x.B0, &x.B2) - t2 := e.Ext2.Add(&y.B0, &y.B2) - t3 := e.Ext2.Add(t2, &y.B1) - v1 := e.Ext2.Add(t1, &x.B1) - v1 = e.Ext2.Mul(v1, t3) - - t3 = e.Ext2.Sub(t2, &y.B1) - v2 := e.Ext2.Sub(t1, &x.B1) - v2 = e.Ext2.Mul(v2, t3) - - t1 = e.Ext2.MulByConstElement(&x.B1, big.NewInt(2)) - t2 = e.Ext2.MulByConstElement(&x.B2, big.NewInt(4)) - v3 := e.Ext2.Add(t1, t2) - v3 = e.Ext2.Add(v3, &x.B0) - t1 = e.Ext2.MulByConstElement(&y.B1, big.NewInt(2)) - t2 = e.Ext2.MulByConstElement(&y.B2, big.NewInt(4)) - t3 = e.Ext2.Add(t1, t2) - t3 = e.Ext2.Add(t3, &y.B0) - v3 = e.Ext2.Mul(v3, t3) - - v4 := e.Ext2.Mul(&x.B2, &y.B2) - - // Then the interpolation is performed as: - // - // a0 = v0 + β((1/2)v0 − (1/2)v1 − (1/6)v2 + (1/6)v3 − 2v4) - // a1 = −(1/2)v0 + v1 − (1/3)v2 − (1/6)v3 + 2v4 + βv4 - // a2 = −v0 + (1/2)v1 + (1/2)v2 − v4 - // - // where β is the cubic non-residue. - // - // In-circuit, we compute 6*x*y as - // c0 = 6v0 + β(3v0 − 3v1 − v2 + v3 − 12v4) - // a1 = -(3v0 + 2v2 + v3) + 6(v1 + 2v4 + βv4) - // a2 = 3(v1 + v2 - 2(v0 + v4)) - // - // and then divide a0, a1 and a2 by 6 using a hint. - - a0 := e.Ext2.MulByConstElement(v0, big.NewInt(6)) - t1 = e.Ext2.Sub(v0, v1) - t1 = e.Ext2.MulByConstElement(t1, big.NewInt(3)) - t1 = e.Ext2.Sub(t1, v2) - t1 = e.Ext2.Add(t1, v3) - t2 = e.Ext2.MulByConstElement(v4, big.NewInt(12)) - t1 = e.Ext2.Sub(t1, t2) - t1 = e.Ext2.MulByNonResidue(t1) - a0 = e.Ext2.Add(a0, t1) - - a1 := e.Ext2.MulByConstElement(v0, big.NewInt(3)) - t1 = e.Ext2.MulByConstElement(v2, big.NewInt(2)) - a1 = e.Ext2.Add(a1, t1) - a1 = e.Ext2.Add(a1, v3) - t1 = e.Ext2.MulByConstElement(v4, big.NewInt(2)) - t1 = e.Ext2.Add(t1, v1) - t2 = e.Ext2.MulByNonResidue(v4) - t1 = e.Ext2.Add(t1, t2) - t1 = e.Ext2.MulByConstElement(t1, big.NewInt(6)) - a1 = e.Ext2.Sub(t1, a1) - - a2 := e.Ext2.Add(v1, v2) - a2 = e.Ext2.MulByConstElement(a2, big.NewInt(3)) - t1 = e.Ext2.Add(v0, v4) - t1 = e.Ext2.MulByConstElement(t1, big.NewInt(6)) - a2 = e.Ext2.Sub(a2, t1) - - res := e.divE6By6([6]*baseEl{&a0.A0, &a0.A1, &a1.A0, &a1.A1, &a2.A0, &a2.A1}) - return &E6{ - B0: E2{ - A0: *res[0], - A1: *res[1], - }, - B1: E2{ - A0: *res[2], - A1: *res[3], - }, - B2: E2{ - A0: *res[4], - A1: *res[5], - }, - } -} - -func (e Ext6) mulKaratsubaOverKaratsuba(x, y *E6) *E6 { - // Karatsuba over Karatsuba: - // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf - t0 := e.Ext2.Mul(&x.B0, &y.B0) - t1 := e.Ext2.Mul(&x.B1, &y.B1) - t2 := e.Ext2.Mul(&x.B2, &y.B2) - c0 := e.Ext2.Add(&x.B1, &x.B2) - tmp := e.Ext2.Add(&y.B1, &y.B2) - c0 = e.Ext2.Mul(c0, tmp) - tmp = e.Ext2.Add(t2, t1) - c0 = e.Ext2.Sub(c0, tmp) - c0 = e.Ext2.MulByNonResidue(c0) - c0 = e.Ext2.Add(c0, t0) - c1 := e.Ext2.Add(&x.B0, &x.B1) - tmp = e.Ext2.Add(&y.B0, &y.B1) - c1 = e.Ext2.Mul(c1, tmp) - tmp = e.Ext2.Add(t0, t1) - c1 = e.Ext2.Sub(c1, tmp) - tmp = e.Ext2.MulByNonResidue(t2) - c1 = e.Ext2.Add(c1, tmp) - tmp = e.Ext2.Add(&x.B0, &x.B2) - c2 := e.Ext2.Add(&y.B0, &y.B2) - c2 = e.Ext2.Mul(c2, tmp) - tmp = e.Ext2.Add(t0, t2) - c2 = e.Ext2.Sub(c2, tmp) - c2 = e.Ext2.Add(c2, t1) - return &E6{ - B0: *c0, - B1: *c1, - B2: *c2, - } -} - -func (e Ext6) Double(x *E6) *E6 { - z0 := e.Ext2.Double(&x.B0) - z1 := e.Ext2.Double(&x.B1) - z2 := e.Ext2.Double(&x.B2) - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -func (e Ext6) Square(x *E6) *E6 { - c4 := e.Ext2.Mul(&x.B0, &x.B1) - c4 = e.Ext2.Double(c4) - c5 := e.Ext2.Square(&x.B2) - c1 := e.Ext2.MulByNonResidue(c5) - c1 = e.Ext2.Add(c1, c4) - c2 := e.Ext2.Sub(c4, c5) - c3 := e.Ext2.Square(&x.B0) - c4 = e.Ext2.Sub(&x.B0, &x.B1) - c4 = e.Ext2.Add(c4, &x.B2) - c5 = e.Ext2.Mul(&x.B1, &x.B2) - c5 = e.Ext2.Double(c5) - c4 = e.Ext2.Square(c4) - c0 := e.Ext2.MulByNonResidue(c5) - c0 = e.Ext2.Add(c0, c3) - z2 := e.Ext2.Add(c2, c4) - z2 = e.Ext2.Add(z2, c5) - z2 = e.Ext2.Sub(z2, c3) - z0 := c0 - z1 := c1 - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -func (e Ext6) MulByE2(x *E6, y *E2) *E6 { - z0 := e.Ext2.Mul(&x.B0, y) - z1 := e.Ext2.Mul(&x.B1, y) - z2 := e.Ext2.Mul(&x.B2, y) - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -// MulBy12 multiplication by sparse element (0,b1,b2) -func (e Ext6) MulBy12(x *E6, b1, b2 *E2) *E6 { - t1 := e.Ext2.Mul(&x.B1, b1) - t2 := e.Ext2.Mul(&x.B2, b2) - c0 := e.Ext2.Add(&x.B1, &x.B2) - tmp := e.Ext2.Add(b1, b2) - c0 = e.Ext2.Mul(c0, tmp) - tmp = e.Ext2.Add(t1, t2) - c0 = e.Ext2.Sub(c0, tmp) - c0 = e.Ext2.MulByNonResidue(c0) - c1 := e.Ext2.Add(&x.B0, &x.B1) - c1 = e.Ext2.Mul(c1, b1) - c1 = e.Ext2.Sub(c1, t1) - tmp = e.Ext2.MulByNonResidue(t2) - c1 = e.Ext2.Add(c1, tmp) - tmp = e.Ext2.Add(&x.B0, &x.B2) - c2 := e.Ext2.Mul(b2, tmp) - c2 = e.Ext2.Sub(c2, t2) - c2 = e.Ext2.Add(c2, t1) - return &E6{ - B0: *c0, - B1: *c1, - B2: *c2, - } -} - -// MulBy0 multiplies z by an E6 sparse element of the form -// -// E6{ -// B0: c0, -// B1: 0, -// B2: 0, -// } -func (e Ext6) MulBy0(z *E6, c0 *E2) *E6 { - a := e.Ext2.Mul(&z.B0, c0) - tmp := e.Ext2.Add(&z.B0, &z.B2) - t2 := e.Ext2.Mul(c0, tmp) - t2 = e.Ext2.Sub(t2, a) - tmp = e.Ext2.Add(&z.B0, &z.B1) - t1 := e.Ext2.Mul(c0, tmp) - t1 = e.Ext2.Sub(t1, a) - return &E6{ - B0: *a, - B1: *t1, - B2: *t2, - } -} - -// MulBy01 multiplies z by an E6 sparse element of the form -// -// E6{ -// B0: c0, -// B1: c1, -// B2: 0, -// } -func (e Ext6) MulBy01(z *E6, c0, c1 *E2) *E6 { - a := e.Ext2.Mul(&z.B0, c0) - b := e.Ext2.Mul(&z.B1, c1) - tmp := e.Ext2.Add(&z.B1, &z.B2) - t0 := e.Ext2.Mul(c1, tmp) - t0 = e.Ext2.Sub(t0, b) - t0 = e.Ext2.MulByNonResidue(t0) - t0 = e.Ext2.Add(t0, a) - // for t2, schoolbook is faster than karatsuba - // c2 = a0b2 + a1b1 + a2b0, - // c2 = a2b0 + b ∵ b2 = 0, b = a1b1 - t2 := e.Ext2.Mul(&z.B2, c0) - t2 = e.Ext2.Add(t2, b) - t1 := e.Ext2.Add(c0, c1) - tmp = e.Ext2.Add(&z.B0, &z.B1) - t1 = e.Ext2.Mul(t1, tmp) - tmp = e.Ext2.Add(a, b) - t1 = e.Ext2.Sub(t1, tmp) - return &E6{ - B0: *t0, - B1: *t1, - B2: *t2, - } -} - -func (e Ext6) MulByNonResidue(x *E6) *E6 { - z2, z1, z0 := &x.B1, &x.B0, &x.B2 - z0 = e.Ext2.MulByNonResidue(z0) - return &E6{ - B0: *z0, - B1: *z1, - B2: *z2, - } -} - -func (e Ext6) AssertIsEqual(x, y *E6) { - e.Ext2.AssertIsEqual(&x.B0, &y.B0) - e.Ext2.AssertIsEqual(&x.B1, &y.B1) - e.Ext2.AssertIsEqual(&x.B2, &y.B2) -} - -func FromE6(y *bls12381.E6) E6 { - return E6{ - B0: FromE2(&y.B0), - B1: FromE2(&y.B1), - B2: FromE2(&y.B2), - } - -} - -func (e Ext6) Inverse(x *E6) *E6 { - res, err := e.fp.NewHint(inverseE6Hint, 6, &x.B0.A0, &x.B0.A1, &x.B1.A0, &x.B1.A1, &x.B2.A0, &x.B2.A1) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } - - inv := E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - } - - one := e.One() - - // 1 == inv * x - _one := e.Mul(&inv, x) - e.AssertIsEqual(one, _one) - - return &inv - -} - -func (e Ext6) DivUnchecked(x, y *E6) *E6 { - res, err := e.fp.NewHint(divE6Hint, 6, &x.B0.A0, &x.B0.A1, &x.B1.A0, &x.B1.A1, &x.B2.A0, &x.B2.A1, &y.B0.A0, &y.B0.A1, &y.B1.A0, &y.B1.A1, &y.B2.A0, &y.B2.A1) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } - - div := E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - } - - // x == div * y - _x := e.Mul(&div, y) - e.AssertIsEqual(x, _x) - - return &div -} - -func (e Ext6) divE6By6(x [6]*baseEl) [6]*baseEl { - res, err := e.fp.NewHint(divE6By6Hint, 6, x[0], x[1], x[2], x[3], x[4], x[5]) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } - - y0 := *res[0] - y1 := *res[1] - y2 := *res[2] - y3 := *res[3] - y4 := *res[4] - y5 := *res[5] - - // xi == 6 * yi - x0 := e.fp.MulConst(&y0, big.NewInt(6)) - x1 := e.fp.MulConst(&y1, big.NewInt(6)) - x2 := e.fp.MulConst(&y2, big.NewInt(6)) - x3 := e.fp.MulConst(&y3, big.NewInt(6)) - x4 := e.fp.MulConst(&y4, big.NewInt(6)) - x5 := e.fp.MulConst(&y5, big.NewInt(6)) - e.fp.AssertIsEqual(x[0], x0) - e.fp.AssertIsEqual(x[1], x1) - e.fp.AssertIsEqual(x[2], x2) - e.fp.AssertIsEqual(x[3], x3) - e.fp.AssertIsEqual(x[4], x4) - e.fp.AssertIsEqual(x[5], x5) - - return [6]*baseEl{&y0, &y1, &y2, &y3, &y4, &y5} -} - -func (e Ext6) Select(selector frontend.Variable, z1, z0 *E6) *E6 { - b0 := e.Ext2.Select(selector, &z1.B0, &z0.B0) - b1 := e.Ext2.Select(selector, &z1.B1, &z0.B1) - b2 := e.Ext2.Select(selector, &z1.B2, &z0.B2) - return &E6{B0: *b0, B1: *b1, B2: *b2} -} - -func (e Ext6) Lookup2(s1, s2 frontend.Variable, a, b, c, d *E6) *E6 { - b0 := e.Ext2.Lookup2(s1, s2, &a.B0, &b.B0, &c.B0, &d.B0) - b1 := e.Ext2.Lookup2(s1, s2, &a.B1, &b.B1, &c.B1, &d.B1) - b2 := e.Ext2.Lookup2(s1, s2, &a.B2, &b.B2, &c.B2, &d.B2) - return &E6{B0: *b0, B1: *b1, B2: *b2} -} diff --git a/std/algebra/emulated/fields_bls12381/e6_test.go b/std/algebra/emulated/fields_bls12381/e6_test.go deleted file mode 100644 index 9ed08d3b51..0000000000 --- a/std/algebra/emulated/fields_bls12381/e6_test.go +++ /dev/null @@ -1,360 +0,0 @@ -package fields_bls12381 - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc" - bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/test" -) - -type e6Add struct { - A, B, C E6 -} - -func (circuit *e6Add) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Add(&circuit.A, &circuit.B) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestAddFp6(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, b, c bls12381.E6 - _, _ = a.SetRandom() - _, _ = b.SetRandom() - c.Add(&a, &b) - - witness := e6Add{ - A: FromE6(&a), - B: FromE6(&b), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Add{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6Sub struct { - A, B, C E6 -} - -func (circuit *e6Sub) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Sub(&circuit.A, &circuit.B) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestSubFp6(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, b, c bls12381.E6 - _, _ = a.SetRandom() - _, _ = b.SetRandom() - c.Sub(&a, &b) - - witness := e6Sub{ - A: FromE6(&a), - B: FromE6(&b), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Sub{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6Mul struct { - A, B, C E6 -} - -func (circuit *e6Mul) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Mul(&circuit.A, &circuit.B) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestMulFp6(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, b, c bls12381.E6 - _, _ = a.SetRandom() - _, _ = b.SetRandom() - c.Mul(&a, &b) - - witness := e6Mul{ - A: FromE6(&a), - B: FromE6(&b), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Mul{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6MulVariant struct { - A, B, C E6 -} - -func (circuit *e6MulVariant) Define(api frontend.API) error { - e := NewExt6(api) - expected1 := e.mulKaratsubaOverKaratsuba(&circuit.A, &circuit.B) - expected2 := e.mulToom3OverKaratsuba(&circuit.A, &circuit.B) - e.AssertIsEqual(expected1, &circuit.C) - e.AssertIsEqual(expected2, &circuit.C) - return nil -} - -func TestMulFp6Variants(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, b, c bls12381.E6 - _, _ = a.SetRandom() - _, _ = b.SetRandom() - c.Mul(&a, &b) - - witness := e6Mul{ - A: FromE6(&a), - B: FromE6(&b), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Mul{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6Square struct { - A, C E6 -} - -func (circuit *e6Square) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Square(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestSquareFp6(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E6 - _, _ = a.SetRandom() - c.Square(&a) - - witness := e6Square{ - A: FromE6(&a), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Square{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6Div struct { - A, B, C E6 -} - -func (circuit *e6Div) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.DivUnchecked(&circuit.A, &circuit.B) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestDivFp6(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, b, c bls12381.E6 - _, _ = a.SetRandom() - _, _ = b.SetRandom() - c.Div(&a, &b) - - witness := e6Div{ - A: FromE6(&a), - B: FromE6(&b), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Div{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6MulByNonResidue struct { - A E6 - C E6 `gnark:",public"` -} - -func (circuit *e6MulByNonResidue) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.MulByNonResidue(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestMulFp6ByNonResidue(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E6 - _, _ = a.SetRandom() - c.MulByNonResidue(&a) - - witness := e6MulByNonResidue{ - A: FromE6(&a), - C: FromE6(&c), - } - - err := test.IsSolved(&e6MulByNonResidue{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6MulByE2 struct { - A E6 - B E2 - C E6 `gnark:",public"` -} - -func (circuit *e6MulByE2) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.MulByE2(&circuit.A, &circuit.B) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestMulFp6ByE2(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E6 - var b bls12381.E2 - _, _ = a.SetRandom() - _, _ = b.SetRandom() - c.MulByE2(&a, &b) - - witness := e6MulByE2{ - A: FromE6(&a), - B: FromE2(&b), - C: FromE6(&c), - } - - err := test.IsSolved(&e6MulByE2{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6MulBy01 struct { - A E6 - C0, C1 E2 - C E6 `gnark:",public"` -} - -func (circuit *e6MulBy01) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.MulBy01(&circuit.A, &circuit.C0, &circuit.C1) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestMulFp6By01(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E6 - var C0, C1 bls12381.E2 - _, _ = a.SetRandom() - _, _ = C0.SetRandom() - _, _ = C1.SetRandom() - c.Set(&a) - c.MulBy01(&C0, &C1) - - witness := e6MulBy01{ - A: FromE6(&a), - C0: FromE2(&C0), - C1: FromE2(&C1), - C: FromE6(&c), - } - - err := test.IsSolved(&e6MulBy01{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6Neg struct { - A E6 - C E6 `gnark:",public"` -} - -func (circuit *e6Neg) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Neg(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestNegFp6(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E6 - _, _ = a.SetRandom() - c.Neg(&a) - - witness := e6Neg{ - A: FromE6(&a), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Neg{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e6Inverse struct { - A E6 - C E6 `gnark:",public"` -} - -func (circuit *e6Inverse) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Inverse(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestInverseFp6(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bls12381.E6 - _, _ = a.SetRandom() - c.Inverse(&a) - - witness := e6Inverse{ - A: FromE6(&a), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Inverse{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} diff --git a/std/algebra/emulated/fields_bls12381/hints.go b/std/algebra/emulated/fields_bls12381/hints.go index 77863d3ffa..24473129f2 100644 --- a/std/algebra/emulated/fields_bls12381/hints.go +++ b/std/algebra/emulated/fields_bls12381/hints.go @@ -19,15 +19,9 @@ func GetHints() []solver.Hint { // E2 divE2Hint, inverseE2Hint, - // E6 - divE6Hint, - inverseE6Hint, - squareTorusHint, - divE6By6Hint, // E12 divE12Hint, inverseE12Hint, - finalExpHint, } } @@ -67,153 +61,59 @@ func divE2Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error }) } -// E6 hints -func inverseE6Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { - return emulated.UnwrapHint(nativeInputs, nativeOutputs, - func(mod *big.Int, inputs, outputs []*big.Int) error { - var a, c bls12381.E6 - - a.B0.A0.SetBigInt(inputs[0]) - a.B0.A1.SetBigInt(inputs[1]) - a.B1.A0.SetBigInt(inputs[2]) - a.B1.A1.SetBigInt(inputs[3]) - a.B2.A0.SetBigInt(inputs[4]) - a.B2.A1.SetBigInt(inputs[5]) - - c.Inverse(&a) - - c.B0.A0.BigInt(outputs[0]) - c.B0.A1.BigInt(outputs[1]) - c.B1.A0.BigInt(outputs[2]) - c.B1.A1.BigInt(outputs[3]) - c.B2.A0.BigInt(outputs[4]) - c.B2.A1.BigInt(outputs[5]) - - return nil - }) -} - -func divE6Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { - return emulated.UnwrapHint(nativeInputs, nativeOutputs, - func(mod *big.Int, inputs, outputs []*big.Int) error { - var a, b, c bls12381.E6 - - a.B0.A0.SetBigInt(inputs[0]) - a.B0.A1.SetBigInt(inputs[1]) - a.B1.A0.SetBigInt(inputs[2]) - a.B1.A1.SetBigInt(inputs[3]) - a.B2.A0.SetBigInt(inputs[4]) - a.B2.A1.SetBigInt(inputs[5]) - - b.B0.A0.SetBigInt(inputs[6]) - b.B0.A1.SetBigInt(inputs[7]) - b.B1.A0.SetBigInt(inputs[8]) - b.B1.A1.SetBigInt(inputs[9]) - b.B2.A0.SetBigInt(inputs[10]) - b.B2.A1.SetBigInt(inputs[11]) - - c.Inverse(&b).Mul(&c, &a) - - c.B0.A0.BigInt(outputs[0]) - c.B0.A1.BigInt(outputs[1]) - c.B1.A0.BigInt(outputs[2]) - c.B1.A1.BigInt(outputs[3]) - c.B2.A0.BigInt(outputs[4]) - c.B2.A1.BigInt(outputs[5]) - - return nil - }) -} - -func squareTorusHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { - return emulated.UnwrapHint(nativeInputs, nativeOutputs, - func(mod *big.Int, inputs, outputs []*big.Int) error { - var a, c bls12381.E6 - - a.B0.A0.SetBigInt(inputs[0]) - a.B0.A1.SetBigInt(inputs[1]) - a.B1.A0.SetBigInt(inputs[2]) - a.B1.A1.SetBigInt(inputs[3]) - a.B2.A0.SetBigInt(inputs[4]) - a.B2.A1.SetBigInt(inputs[5]) - - _c := a.DecompressTorus() - _c.CyclotomicSquare(&_c) - c, _ = _c.CompressTorus() - - c.B0.A0.BigInt(outputs[0]) - c.B0.A1.BigInt(outputs[1]) - c.B1.A0.BigInt(outputs[2]) - c.B1.A1.BigInt(outputs[3]) - c.B2.A0.BigInt(outputs[4]) - c.B2.A1.BigInt(outputs[5]) - - return nil - }) -} - -func divE6By6Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { - return emulated.UnwrapHint(nativeInputs, nativeOutputs, - func(mod *big.Int, inputs, outputs []*big.Int) error { - var a, c bls12381.E6 - - a.B0.A0.SetBigInt(inputs[0]) - a.B0.A1.SetBigInt(inputs[1]) - a.B1.A0.SetBigInt(inputs[2]) - a.B1.A1.SetBigInt(inputs[3]) - a.B2.A0.SetBigInt(inputs[4]) - a.B2.A1.SetBigInt(inputs[5]) - - var sixInv fp.Element - sixInv.SetString("6") - sixInv.Inverse(&sixInv) - c.B0.MulByElement(&a.B0, &sixInv) - c.B1.MulByElement(&a.B1, &sixInv) - c.B2.MulByElement(&a.B2, &sixInv) - - c.B0.A0.BigInt(outputs[0]) - c.B0.A1.BigInt(outputs[1]) - c.B1.A0.BigInt(outputs[2]) - c.B1.A1.BigInt(outputs[3]) - c.B2.A0.BigInt(outputs[4]) - c.B2.A1.BigInt(outputs[5]) - - return nil - }) -} - // E12 hints func inverseE12Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { return emulated.UnwrapHint(nativeInputs, nativeOutputs, func(mod *big.Int, inputs, outputs []*big.Int) error { var a, c bls12381.E12 - a.C0.B0.A0.SetBigInt(inputs[0]) - a.C0.B0.A1.SetBigInt(inputs[1]) - a.C0.B1.A0.SetBigInt(inputs[2]) - a.C0.B1.A1.SetBigInt(inputs[3]) - a.C0.B2.A0.SetBigInt(inputs[4]) - a.C0.B2.A1.SetBigInt(inputs[5]) - a.C1.B0.A0.SetBigInt(inputs[6]) - a.C1.B0.A1.SetBigInt(inputs[7]) - a.C1.B1.A0.SetBigInt(inputs[8]) - a.C1.B1.A1.SetBigInt(inputs[9]) - a.C1.B2.A0.SetBigInt(inputs[10]) - a.C1.B2.A1.SetBigInt(inputs[11]) + var d [12]big.Int + d[0].Add(inputs[0], inputs[6]) + d[1].Set(inputs[6]) + d[2].Add(inputs[2], inputs[8]) + d[3].Set(inputs[8]) + d[4].Add(inputs[4], inputs[10]) + d[5].Set(inputs[10]) + d[6].Add(inputs[1], inputs[7]) + d[7].Set(inputs[7]) + d[8].Add(inputs[3], inputs[9]) + d[9].Set(inputs[9]) + d[10].Add(inputs[5], inputs[11]) + d[11].Set(inputs[11]) + a.C0.B0.A0.SetBigInt(&d[0]) + a.C0.B0.A1.SetBigInt(&d[1]) + a.C0.B1.A0.SetBigInt(&d[2]) + a.C0.B1.A1.SetBigInt(&d[3]) + a.C0.B2.A0.SetBigInt(&d[4]) + a.C0.B2.A1.SetBigInt(&d[5]) + a.C1.B0.A0.SetBigInt(&d[6]) + a.C1.B0.A1.SetBigInt(&d[7]) + a.C1.B1.A0.SetBigInt(&d[8]) + a.C1.B1.A1.SetBigInt(&d[9]) + a.C1.B2.A0.SetBigInt(&d[10]) + a.C1.B2.A1.SetBigInt(&d[11]) c.Inverse(&a) - c.C0.B0.A0.BigInt(outputs[0]) - c.C0.B0.A1.BigInt(outputs[1]) - c.C0.B1.A0.BigInt(outputs[2]) - c.C0.B1.A1.BigInt(outputs[3]) - c.C0.B2.A0.BigInt(outputs[4]) - c.C0.B2.A1.BigInt(outputs[5]) - c.C1.B0.A0.BigInt(outputs[6]) + var c0, c1, c2, c3, c4, c5 fp.Element + c0.Sub(&c.C0.B0.A0, &c.C0.B0.A1) + c1.Sub(&c.C1.B0.A0, &c.C1.B0.A1) + c2.Sub(&c.C0.B1.A0, &c.C0.B1.A1) + c3.Sub(&c.C1.B1.A0, &c.C1.B1.A1) + c4.Sub(&c.C0.B2.A0, &c.C0.B2.A1) + c5.Sub(&c.C1.B2.A0, &c.C1.B2.A1) + + c0.BigInt(outputs[0]) + c1.BigInt(outputs[1]) + c2.BigInt(outputs[2]) + c3.BigInt(outputs[3]) + c4.BigInt(outputs[4]) + c5.BigInt(outputs[5]) + c.C0.B0.A1.BigInt(outputs[6]) c.C1.B0.A1.BigInt(outputs[7]) - c.C1.B1.A0.BigInt(outputs[8]) + c.C0.B1.A1.BigInt(outputs[8]) c.C1.B1.A1.BigInt(outputs[9]) - c.C1.B2.A0.BigInt(outputs[10]) + c.C0.B2.A1.BigInt(outputs[10]) c.C1.B2.A1.BigInt(outputs[11]) return nil @@ -225,157 +125,80 @@ func divE12Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) erro func(mod *big.Int, inputs, outputs []*big.Int) error { var a, b, c bls12381.E12 - a.C0.B0.A0.SetBigInt(inputs[0]) - a.C0.B0.A1.SetBigInt(inputs[1]) - a.C0.B1.A0.SetBigInt(inputs[2]) - a.C0.B1.A1.SetBigInt(inputs[3]) - a.C0.B2.A0.SetBigInt(inputs[4]) - a.C0.B2.A1.SetBigInt(inputs[5]) - a.C1.B0.A0.SetBigInt(inputs[6]) - a.C1.B0.A1.SetBigInt(inputs[7]) - a.C1.B1.A0.SetBigInt(inputs[8]) - a.C1.B1.A1.SetBigInt(inputs[9]) - a.C1.B2.A0.SetBigInt(inputs[10]) - a.C1.B2.A1.SetBigInt(inputs[11]) - - b.C0.B0.A0.SetBigInt(inputs[12]) - b.C0.B0.A1.SetBigInt(inputs[13]) - b.C0.B1.A0.SetBigInt(inputs[14]) - b.C0.B1.A1.SetBigInt(inputs[15]) - b.C0.B2.A0.SetBigInt(inputs[16]) - b.C0.B2.A1.SetBigInt(inputs[17]) - b.C1.B0.A0.SetBigInt(inputs[18]) - b.C1.B0.A1.SetBigInt(inputs[19]) - b.C1.B1.A0.SetBigInt(inputs[20]) - b.C1.B1.A1.SetBigInt(inputs[21]) - b.C1.B2.A0.SetBigInt(inputs[22]) - b.C1.B2.A1.SetBigInt(inputs[23]) + var d [12]big.Int + d[0].Add(inputs[0], inputs[6]) + d[1].Set(inputs[6]) + d[2].Add(inputs[2], inputs[8]) + d[3].Set(inputs[8]) + d[4].Add(inputs[4], inputs[10]) + d[5].Set(inputs[10]) + d[6].Add(inputs[1], inputs[7]) + d[7].Set(inputs[7]) + d[8].Add(inputs[3], inputs[9]) + d[9].Set(inputs[9]) + d[10].Add(inputs[5], inputs[11]) + d[11].Set(inputs[11]) + a.C0.B0.A0.SetBigInt(&d[0]) + a.C0.B0.A1.SetBigInt(&d[1]) + a.C0.B1.A0.SetBigInt(&d[2]) + a.C0.B1.A1.SetBigInt(&d[3]) + a.C0.B2.A0.SetBigInt(&d[4]) + a.C0.B2.A1.SetBigInt(&d[5]) + a.C1.B0.A0.SetBigInt(&d[6]) + a.C1.B0.A1.SetBigInt(&d[7]) + a.C1.B1.A0.SetBigInt(&d[8]) + a.C1.B1.A1.SetBigInt(&d[9]) + a.C1.B2.A0.SetBigInt(&d[10]) + a.C1.B2.A1.SetBigInt(&d[11]) + + d[0].Add(inputs[12], inputs[18]) + d[1].Set(inputs[18]) + d[2].Add(inputs[14], inputs[20]) + d[3].Set(inputs[20]) + d[4].Add(inputs[16], inputs[22]) + d[5].Set(inputs[22]) + d[6].Add(inputs[13], inputs[19]) + d[7].Set(inputs[19]) + d[8].Add(inputs[15], inputs[21]) + d[9].Set(inputs[21]) + d[10].Add(inputs[17], inputs[23]) + d[11].Set(inputs[23]) + b.C0.B0.A0.SetBigInt(&d[0]) + b.C0.B0.A1.SetBigInt(&d[1]) + b.C0.B1.A0.SetBigInt(&d[2]) + b.C0.B1.A1.SetBigInt(&d[3]) + b.C0.B2.A0.SetBigInt(&d[4]) + b.C0.B2.A1.SetBigInt(&d[5]) + b.C1.B0.A0.SetBigInt(&d[6]) + b.C1.B0.A1.SetBigInt(&d[7]) + b.C1.B1.A0.SetBigInt(&d[8]) + b.C1.B1.A1.SetBigInt(&d[9]) + b.C1.B2.A0.SetBigInt(&d[10]) + b.C1.B2.A1.SetBigInt(&d[11]) c.Inverse(&b).Mul(&c, &a) - c.C0.B0.A0.BigInt(outputs[0]) - c.C0.B0.A1.BigInt(outputs[1]) - c.C0.B1.A0.BigInt(outputs[2]) - c.C0.B1.A1.BigInt(outputs[3]) - c.C0.B2.A0.BigInt(outputs[4]) - c.C0.B2.A1.BigInt(outputs[5]) - c.C1.B0.A0.BigInt(outputs[6]) + var c0, c1, c2, c3, c4, c5 fp.Element + c0.Sub(&c.C0.B0.A0, &c.C0.B0.A1) + c1.Sub(&c.C1.B0.A0, &c.C1.B0.A1) + c2.Sub(&c.C0.B1.A0, &c.C0.B1.A1) + c3.Sub(&c.C1.B1.A0, &c.C1.B1.A1) + c4.Sub(&c.C0.B2.A0, &c.C0.B2.A1) + c5.Sub(&c.C1.B2.A0, &c.C1.B2.A1) + + c0.BigInt(outputs[0]) + c1.BigInt(outputs[1]) + c2.BigInt(outputs[2]) + c3.BigInt(outputs[3]) + c4.BigInt(outputs[4]) + c5.BigInt(outputs[5]) + c.C0.B0.A1.BigInt(outputs[6]) c.C1.B0.A1.BigInt(outputs[7]) - c.C1.B1.A0.BigInt(outputs[8]) + c.C0.B1.A1.BigInt(outputs[8]) c.C1.B1.A1.BigInt(outputs[9]) - c.C1.B2.A0.BigInt(outputs[10]) + c.C0.B2.A1.BigInt(outputs[10]) c.C1.B2.A1.BigInt(outputs[11]) return nil }) } - -func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { - // This is inspired from https://eprint.iacr.org/2024/640.pdf - // and based on a personal communication with the author Andrija Novakovic. - return emulated.UnwrapHint(nativeInputs, nativeOutputs, - func(mod *big.Int, inputs, outputs []*big.Int) error { - var millerLoop bls12381.E12 - - millerLoop.C0.B0.A0.SetBigInt(inputs[0]) - millerLoop.C0.B0.A1.SetBigInt(inputs[1]) - millerLoop.C0.B1.A0.SetBigInt(inputs[2]) - millerLoop.C0.B1.A1.SetBigInt(inputs[3]) - millerLoop.C0.B2.A0.SetBigInt(inputs[4]) - millerLoop.C0.B2.A1.SetBigInt(inputs[5]) - millerLoop.C1.B0.A0.SetBigInt(inputs[6]) - millerLoop.C1.B0.A1.SetBigInt(inputs[7]) - millerLoop.C1.B1.A0.SetBigInt(inputs[8]) - millerLoop.C1.B1.A1.SetBigInt(inputs[9]) - millerLoop.C1.B2.A0.SetBigInt(inputs[10]) - millerLoop.C1.B2.A1.SetBigInt(inputs[11]) - - var root, rootPthInverse, root27thInverse, residueWitness, scalingFactor bls12381.E12 - var order3rd, order3rdPower, exponent, exponentInv, finalExpFactor, polyFactor big.Int - // polyFactor = (1-x)/3 - polyFactor.SetString("5044125407647214251", 10) - // finalExpFactor = ((q^12 - 1) / r) / (27 * polyFactor) - finalExpFactor.SetString("2366356426548243601069753987687709088104621721678962410379583120840019275952471579477684846670499039076873213559162845121989217658133790336552276567078487633052653005423051750848782286407340332979263075575489766963251914185767058009683318020965829271737924625612375201545022326908440428522712877494557944965298566001441468676802477524234094954960009227631543471415676620753242466901942121887152806837594306028649150255258504417829961387165043999299071444887652375514277477719817175923289019181393803729926249507024121957184340179467502106891835144220611408665090353102353194448552304429530104218473070114105759487413726485729058069746063140422361472585604626055492939586602274983146215294625774144156395553405525711143696689756441298365274341189385646499074862712688473936093315628166094221735056483459332831845007196600723053356837526749543765815988577005929923802636375670820616189737737304893769679803809426304143627363860243558537831172903494450556755190448279875942974830469855835666815454271389438587399739607656399812689280234103023464545891697941661992848552456326290792224091557256350095392859243101357349751064730561345062266850238821755009430903520645523345000326783803935359711318798844368754833295302563158150573540616830138810935344206231367357992991289265295323280", 10) - - // 1. get pth-root inverse - exponent.Mul(&finalExpFactor, big.NewInt(27)) - root.Exp(millerLoop, &exponent) - if root.IsOne() { - rootPthInverse.SetOne() - } else { - exponentInv.ModInverse(&exponent, &polyFactor) - exponent.Neg(&exponentInv).Mod(&exponent, &polyFactor) - rootPthInverse.Exp(root, &exponent) - } - - // 2.1. get order of 3rd primitive root - var three big.Int - three.SetUint64(3) - exponent.Mul(&polyFactor, &finalExpFactor) - root.Exp(millerLoop, &exponent) - if root.IsOne() { - order3rdPower.SetUint64(0) - } - root.Exp(root, &three) - if root.IsOne() { - order3rdPower.SetUint64(1) - } - root.Exp(root, &three) - if root.IsOne() { - order3rdPower.SetUint64(2) - } - root.Exp(root, &three) - if root.IsOne() { - order3rdPower.SetUint64(3) - } - - // 2.2. get 27th root inverse - if order3rdPower.Uint64() == 0 { - root27thInverse.SetOne() - } else { - order3rd.Exp(&three, &order3rdPower, nil) - exponent.Mul(&polyFactor, &finalExpFactor) - root.Exp(millerLoop, &exponent) - exponentInv.ModInverse(&exponent, &order3rd) - exponent.Neg(&exponentInv).Mod(&exponent, &order3rd) - root27thInverse.Exp(root, &exponent) - } - - // 2.3. shift the Miller loop result so that millerLoop * scalingFactor - // is of order finalExpFactor - scalingFactor.Mul(&rootPthInverse, &root27thInverse) - millerLoop.Mul(&millerLoop, &scalingFactor) - - // 3. get the witness residue - // - // lambda = q - u, the optimal exponent - var lambda big.Int - lambda.SetString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539", 10) - exponent.ModInverse(&lambda, &finalExpFactor) - residueWitness.Exp(millerLoop, &exponent) - - // return the witness residue - residueWitness.C0.B0.A0.BigInt(outputs[0]) - residueWitness.C0.B0.A1.BigInt(outputs[1]) - residueWitness.C0.B1.A0.BigInt(outputs[2]) - residueWitness.C0.B1.A1.BigInt(outputs[3]) - residueWitness.C0.B2.A0.BigInt(outputs[4]) - residueWitness.C0.B2.A1.BigInt(outputs[5]) - residueWitness.C1.B0.A0.BigInt(outputs[6]) - residueWitness.C1.B0.A1.BigInt(outputs[7]) - residueWitness.C1.B1.A0.BigInt(outputs[8]) - residueWitness.C1.B1.A1.BigInt(outputs[9]) - residueWitness.C1.B2.A0.BigInt(outputs[10]) - residueWitness.C1.B2.A1.BigInt(outputs[11]) - - // return the scaling factor - scalingFactor.C0.B0.A0.BigInt(outputs[12]) - scalingFactor.C0.B0.A1.BigInt(outputs[13]) - scalingFactor.C0.B1.A0.BigInt(outputs[14]) - scalingFactor.C0.B1.A1.BigInt(outputs[15]) - scalingFactor.C0.B2.A0.BigInt(outputs[16]) - scalingFactor.C0.B2.A1.BigInt(outputs[17]) - - return nil - }) -} diff --git a/std/algebra/emulated/sw_bls12381/hints.go b/std/algebra/emulated/sw_bls12381/hints.go new file mode 100644 index 0000000000..676ed3c225 --- /dev/null +++ b/std/algebra/emulated/sw_bls12381/hints.go @@ -0,0 +1,128 @@ +package sw_bls12381 + +import ( + "math/big" + + bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark/constraint/solver" + "github.com/consensys/gnark/std/math/emulated" +) + +func init() { + solver.RegisterHint(GetHints()...) +} + +// GetHints returns all hint functions used in the package. +func GetHints() []solver.Hint { + return []solver.Hint{finalExpHint} +} + +func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { + // This is inspired from https://eprint.iacr.org/2024/640.pdf + // and based on a personal communication with the author Andrija Novakovic. + return emulated.UnwrapHint(nativeInputs, nativeOutputs, + func(mod *big.Int, inputs, outputs []*big.Int) error { + var millerLoop bls12381.E12 + + millerLoop.C0.B0.A0.SetBigInt(inputs[0]) + millerLoop.C0.B0.A1.SetBigInt(inputs[1]) + millerLoop.C0.B1.A0.SetBigInt(inputs[2]) + millerLoop.C0.B1.A1.SetBigInt(inputs[3]) + millerLoop.C0.B2.A0.SetBigInt(inputs[4]) + millerLoop.C0.B2.A1.SetBigInt(inputs[5]) + millerLoop.C1.B0.A0.SetBigInt(inputs[6]) + millerLoop.C1.B0.A1.SetBigInt(inputs[7]) + millerLoop.C1.B1.A0.SetBigInt(inputs[8]) + millerLoop.C1.B1.A1.SetBigInt(inputs[9]) + millerLoop.C1.B2.A0.SetBigInt(inputs[10]) + millerLoop.C1.B2.A1.SetBigInt(inputs[11]) + + var root, rootPthInverse, root27thInverse, residueWitness, scalingFactor bls12381.E12 + var order3rd, order3rdPower, exponent, exponentInv, finalExpFactor, polyFactor big.Int + // polyFactor = (1-x)/3 + polyFactor.SetString("5044125407647214251", 10) + // finalExpFactor = ((q^12 - 1) / r) / (27 * polyFactor) + finalExpFactor.SetString("2366356426548243601069753987687709088104621721678962410379583120840019275952471579477684846670499039076873213559162845121989217658133790336552276567078487633052653005423051750848782286407340332979263075575489766963251914185767058009683318020965829271737924625612375201545022326908440428522712877494557944965298566001441468676802477524234094954960009227631543471415676620753242466901942121887152806837594306028649150255258504417829961387165043999299071444887652375514277477719817175923289019181393803729926249507024121957184340179467502106891835144220611408665090353102353194448552304429530104218473070114105759487413726485729058069746063140422361472585604626055492939586602274983146215294625774144156395553405525711143696689756441298365274341189385646499074862712688473936093315628166094221735056483459332831845007196600723053356837526749543765815988577005929923802636375670820616189737737304893769679803809426304143627363860243558537831172903494450556755190448279875942974830469855835666815454271389438587399739607656399812689280234103023464545891697941661992848552456326290792224091557256350095392859243101357349751064730561345062266850238821755009430903520645523345000326783803935359711318798844368754833295302563158150573540616830138810935344206231367357992991289265295323280", 10) + + // 1. get pth-root inverse + exponent.Mul(&finalExpFactor, big.NewInt(27)) + root.Exp(millerLoop, &exponent) + if root.IsOne() { + rootPthInverse.SetOne() + } else { + exponentInv.ModInverse(&exponent, &polyFactor) + exponent.Neg(&exponentInv).Mod(&exponent, &polyFactor) + rootPthInverse.Exp(root, &exponent) + } + + // 2.1. get order of 3rd primitive root + var three big.Int + three.SetUint64(3) + exponent.Mul(&polyFactor, &finalExpFactor) + root.Exp(millerLoop, &exponent) + if root.IsOne() { + order3rdPower.SetUint64(0) + } + root.Exp(root, &three) + if root.IsOne() { + order3rdPower.SetUint64(1) + } + root.Exp(root, &three) + if root.IsOne() { + order3rdPower.SetUint64(2) + } + root.Exp(root, &three) + if root.IsOne() { + order3rdPower.SetUint64(3) + } + + // 2.2. get 27th root inverse + if order3rdPower.Uint64() == 0 { + root27thInverse.SetOne() + } else { + order3rd.Exp(&three, &order3rdPower, nil) + exponent.Mul(&polyFactor, &finalExpFactor) + root.Exp(millerLoop, &exponent) + exponentInv.ModInverse(&exponent, &order3rd) + exponent.Neg(&exponentInv).Mod(&exponent, &order3rd) + root27thInverse.Exp(root, &exponent) + } + + // 2.3. shift the Miller loop result so that millerLoop * scalingFactor + // is of order finalExpFactor + scalingFactor.Mul(&rootPthInverse, &root27thInverse) + millerLoop.Mul(&millerLoop, &scalingFactor) + + // 3. get the witness residue + // + // lambda = q - u, the optimal exponent + var lambda big.Int + lambda.SetString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539", 10) + exponent.ModInverse(&lambda, &finalExpFactor) + residueWitness.Exp(millerLoop, &exponent) + + // return the witness residue + residueWitness.C0.B0.A0.BigInt(outputs[0]) + residueWitness.C0.B0.A1.BigInt(outputs[1]) + residueWitness.C0.B1.A0.BigInt(outputs[2]) + residueWitness.C0.B1.A1.BigInt(outputs[3]) + residueWitness.C0.B2.A0.BigInt(outputs[4]) + residueWitness.C0.B2.A1.BigInt(outputs[5]) + residueWitness.C1.B0.A0.BigInt(outputs[6]) + residueWitness.C1.B0.A1.BigInt(outputs[7]) + residueWitness.C1.B1.A0.BigInt(outputs[8]) + residueWitness.C1.B1.A1.BigInt(outputs[9]) + residueWitness.C1.B2.A0.BigInt(outputs[10]) + residueWitness.C1.B2.A1.BigInt(outputs[11]) + + // return the scaling factor + scalingFactor.C0.B0.A0.BigInt(outputs[12]) + scalingFactor.C0.B0.A1.BigInt(outputs[13]) + scalingFactor.C0.B1.A0.BigInt(outputs[14]) + scalingFactor.C0.B1.A1.BigInt(outputs[15]) + scalingFactor.C0.B2.A0.BigInt(outputs[16]) + scalingFactor.C0.B2.A1.BigInt(outputs[17]) + + return nil + }) +} diff --git a/std/algebra/emulated/sw_bls12381/pairing.go b/std/algebra/emulated/sw_bls12381/pairing.go index b46a3d2c22..8f60180901 100644 --- a/std/algebra/emulated/sw_bls12381/pairing.go +++ b/std/algebra/emulated/sw_bls12381/pairing.go @@ -6,6 +6,7 @@ import ( "math/big" bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381" + "github.com/consensys/gnark-crypto/ecc/bls12-381/fp" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/emulated/fields_bls12381" "github.com/consensys/gnark/std/algebra/emulated/sw_emulated" @@ -15,46 +16,40 @@ import ( type Pairing struct { api frontend.API *fields_bls12381.Ext12 + *fields_bls12381.Ext2 curveF *emulated.Field[BaseField] curve *sw_emulated.Curve[BaseField, ScalarField] g2 *G2 g1 *G1 bTwist *fields_bls12381.E2 - g2gen *G2Affine } +type baseEl = emulated.Element[BaseField] type GTEl = fields_bls12381.E12 -func NewGTEl(v bls12381.GT) GTEl { +func NewGTEl(a bls12381.GT) GTEl { + + var c0, c1, c2, c3, c4, c5 fp.Element + c0.Sub(&a.C0.B0.A0, &a.C0.B0.A1) + c1.Sub(&a.C1.B0.A0, &a.C1.B0.A1) + c2.Sub(&a.C0.B1.A0, &a.C0.B1.A1) + c3.Sub(&a.C1.B1.A0, &a.C1.B1.A1) + c4.Sub(&a.C0.B2.A0, &a.C0.B2.A1) + c5.Sub(&a.C1.B2.A0, &a.C1.B2.A1) + return GTEl{ - C0: fields_bls12381.E6{ - B0: fields_bls12381.E2{ - A0: emulated.ValueOf[BaseField](v.C0.B0.A0), - A1: emulated.ValueOf[BaseField](v.C0.B0.A1), - }, - B1: fields_bls12381.E2{ - A0: emulated.ValueOf[BaseField](v.C0.B1.A0), - A1: emulated.ValueOf[BaseField](v.C0.B1.A1), - }, - B2: fields_bls12381.E2{ - A0: emulated.ValueOf[BaseField](v.C0.B2.A0), - A1: emulated.ValueOf[BaseField](v.C0.B2.A1), - }, - }, - C1: fields_bls12381.E6{ - B0: fields_bls12381.E2{ - A0: emulated.ValueOf[BaseField](v.C1.B0.A0), - A1: emulated.ValueOf[BaseField](v.C1.B0.A1), - }, - B1: fields_bls12381.E2{ - A0: emulated.ValueOf[BaseField](v.C1.B1.A0), - A1: emulated.ValueOf[BaseField](v.C1.B1.A1), - }, - B2: fields_bls12381.E2{ - A0: emulated.ValueOf[BaseField](v.C1.B2.A0), - A1: emulated.ValueOf[BaseField](v.C1.B2.A1), - }, - }, + A0: emulated.ValueOf[BaseField](c0), + A1: emulated.ValueOf[BaseField](c1), + A2: emulated.ValueOf[BaseField](c2), + A3: emulated.ValueOf[BaseField](c3), + A4: emulated.ValueOf[BaseField](c4), + A5: emulated.ValueOf[BaseField](c5), + A6: emulated.ValueOf[BaseField](a.C0.B0.A1), + A7: emulated.ValueOf[BaseField](a.C1.B0.A1), + A8: emulated.ValueOf[BaseField](a.C0.B1.A1), + A9: emulated.ValueOf[BaseField](a.C1.B1.A1), + A10: emulated.ValueOf[BaseField](a.C0.B2.A1), + A11: emulated.ValueOf[BaseField](a.C1.B2.A1), } } @@ -78,6 +73,7 @@ func NewPairing(api frontend.API) (*Pairing, error) { return &Pairing{ api: api, Ext12: fields_bls12381.NewExt12(api), + Ext2: fields_bls12381.NewExt2(api), curveF: ba, curve: curve, g1: g1, @@ -86,144 +82,6 @@ func NewPairing(api frontend.API) (*Pairing, error) { }, nil } -func (pr Pairing) generators() *G2Affine { - if pr.g2gen == nil { - _, _, _, g2gen := bls12381.Generators() - cg2gen := NewG2AffineFixed(g2gen) - pr.g2gen = &cg2gen - } - return pr.g2gen -} - -// FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ where -// -// d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// -// we use instead -// -// d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// -// where s is the cofactor 3 (Hayashida et al.). -// -// FinalExponentiation returns a decompressed element in E12. -// -// This is the safe version of the method where e may be {-1,1}. If it is known -// that e ≠ {-1,1} then using the unsafe version of the method saves -// considerable amount of constraints. When called with the result of -// [MillerLoop], then current method is applicable when length of the inputs to -// Miller loop is 1. -func (pr Pairing) FinalExponentiation(e *GTEl) *GTEl { - return pr.finalExponentiation(e, false) -} - -// FinalExponentiationUnsafe computes the exponentiation (∏ᵢ zᵢ)ᵈ where -// -// d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// -// we use instead -// -// d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// -// where s is the cofactor 3 (Hayashida et al.). -// -// FinalExponentiationUnsafe returns a decompressed element in E12. -// -// This is the unsafe version of the method where e may NOT be {-1,1}. If e ∈ -// {-1, 1}, then there exists no valid solution to the circuit. This method is -// applicable when called with the result of [MillerLoop] method when the length -// of the inputs to Miller loop is 1. -func (pr Pairing) FinalExponentiationUnsafe(e *GTEl) *GTEl { - return pr.finalExponentiation(e, true) -} - -// finalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ where -// -// d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// -// we use instead -// -// d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r -// -// where s is the cofactor 3 (Hayashida et al.). -// -// finalExponentiation returns a decompressed element in E12 -func (pr Pairing) finalExponentiation(e *GTEl, unsafe bool) *GTEl { - - // 1. Easy part - // (p⁶-1)(p²+1) - var selector1, selector2 frontend.Variable - _dummy := pr.Ext6.One() - - if unsafe { - // The Miller loop result is ≠ {-1,1}, otherwise this means P and Q are - // linearly dependent and not from G1 and G2 respectively. - // So e ∈ G_{q,2} \ {-1,1} and hence e.C1 ≠ 0. - // Nothing to do. - - } else { - // However, for a product of Miller loops (n>=2) this might happen. If this is - // the case, the result is 1 in the torus. We assign a dummy value (1) to e.C1 - // and proceed further. - selector1 = pr.Ext6.IsZero(&e.C1) - e.C1.B0.A0 = *pr.curveF.Select(selector1, pr.curveF.One(), &e.C1.B0.A0) - } - - // Torus compression absorbed: - // Raising e to (p⁶-1) is - // e^(p⁶) / e = (e.C0 - w*e.C1) / (e.C0 + w*e.C1) - // = (-e.C0/e.C1 + w) / (-e.C0/e.C1 - w) - // So the fraction -e.C0/e.C1 is already in the torus. - // This absorbs the torus compression in the easy part. - c := pr.Ext6.DivUnchecked(&e.C0, &e.C1) - c = pr.Ext6.Neg(c) - t0 := pr.FrobeniusSquareTorus(c) - c = pr.MulTorus(t0, c) - - // 2. Hard part (up to permutation) - // 3(p⁴-p²+1)/r - // Daiki Hayashida, Kenichiro Hayasaka and Tadanori Teruya - // https://eprint.iacr.org/2020/875.pdf - // performed in torus compressed form - t0 = pr.SquareTorus(c) - t1 := pr.ExptHalfTorus(t0) - t2 := pr.InverseTorus(c) - t1 = pr.MulTorus(t1, t2) - t2 = pr.ExptTorus(t1) - t1 = pr.InverseTorus(t1) - t1 = pr.MulTorus(t1, t2) - t2 = pr.ExptTorus(t1) - t1 = pr.FrobeniusTorus(t1) - t1 = pr.MulTorus(t1, t2) - c = pr.MulTorus(c, t0) - t0 = pr.ExptTorus(t1) - t2 = pr.ExptTorus(t0) - t0 = pr.FrobeniusSquareTorus(t1) - t1 = pr.InverseTorus(t1) - t1 = pr.MulTorus(t1, t2) - t1 = pr.MulTorus(t1, t0) - - var result GTEl - // MulTorus(c, t1) requires c ≠ -t1. When c = -t1, it means the - // product is 1 in the torus. - if unsafe { - // For a single pairing, this does not happen because the pairing is non-degenerate. - result = *pr.DecompressTorus(pr.MulTorus(c, t1)) - } else { - // For a product of pairings this might happen when the result is expected to be 1. - // We assign a dummy value (1) to t1 and proceed further. - // Finally we do a select on both edge cases: - // - Only if seletor1=0 and selector2=0, we return MulTorus(c, t1) decompressed. - // - Otherwise, we return 1. - _sum := pr.Ext6.Add(c, t1) - selector2 = pr.Ext6.IsZero(_sum) - t1 = pr.Ext6.Select(selector2, _dummy, t1) - selector := pr.api.Mul(pr.api.Sub(1, selector1), pr.api.Sub(1, selector2)) - result = *pr.Select(selector, pr.DecompressTorus(pr.MulTorus(c, t1)), pr.One()) - } - - return &result -} - // Pair calculates the reduced pairing for a set of points // ∏ᵢ e(Pᵢ, Qᵢ). // @@ -233,7 +91,7 @@ func (pr Pairing) Pair(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { if err != nil { return nil, fmt.Errorf("miller loop: %w", err) } - res = pr.finalExponentiation(res, len(P) == 1) + res = pr.FinalExponentiation(res) return res, nil } @@ -247,15 +105,6 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { return err } - // We perform the easy part of the final exp to push f to the cyclotomic - // subgroup so that AssertFinalExponentiationIsOne is carried with optimized - // cyclotomic squaring (e.g. Karabina12345). - // - // f = f^(p⁶-1)(p²+1) - buf := pr.Conjugate(f) - buf = pr.DivUnchecked(buf, f) - f = pr.FrobeniusSquare(buf) - f = pr.Mul(f, buf) pr.AssertFinalExponentiationIsOne(f) @@ -356,8 +205,8 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl } // precomputations - yInv := make([]*emulated.Element[BaseField], n) - xNegOverY := make([]*emulated.Element[BaseField], n) + yInv := make([]*baseEl, n) + xNegOverY := make([]*baseEl, n) for k := 0; k < n; k++ { // P are supposed to be on G1 respectively of prime order r. @@ -371,39 +220,14 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl res := pr.Ext12.One() // Compute ∏ᵢ { fᵢ_{x₀,Q}(P) } - // i = 62, separately to avoid an E12 Square // (Square(res) = 1² = 1) - // k = 0, separately to avoid MulBy014 (res × ℓ) - res.C0.B0 = *pr.MulByElement(&lines[0][0][62].R1, yInv[0]) - res.C0.B1 = *pr.MulByElement(&lines[0][0][62].R0, xNegOverY[0]) - res.C1.B1 = *pr.Ext2.One() - - prodLines := pr.Mul014By014( - pr.MulByElement(&lines[0][1][62].R1, yInv[0]), - pr.MulByElement(&lines[0][1][62].R0, xNegOverY[0]), - &res.C0.B0, - &res.C0.B1, - ) - res = &fields_bls12381.E12{ - C0: fields_bls12381.E6{ - B0: *prodLines[0], - B1: *prodLines[1], - B2: *prodLines[2], - }, - C1: fields_bls12381.E6{ - B0: res.C1.B0, - B1: *prodLines[3], - B2: *prodLines[4], - }, - } - - for k := 1; k < n; k++ { - res = pr.MulBy014(res, + 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]), ) - res = pr.MulBy014(res, + res = pr.MulBy02368(res, pr.MulByElement(&lines[k][1][62].R1, yInv[k]), pr.MulByElement(&lines[k][1][62].R0, xNegOverY[k]), ) @@ -412,20 +236,20 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl for i := 61; i >= 0; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² - res = pr.Square(res) + res = pr.Ext12.Square(res) for k := 0; k < n; k++ { if loopCounter[i] == 0 { - res = pr.MulBy014(res, + res = pr.MulBy02368(res, pr.MulByElement(&lines[k][0][i].R1, yInv[k]), pr.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), ) } else { - res = pr.MulBy014(res, + res = pr.MulBy02368(res, pr.MulByElement(&lines[k][0][i].R1, yInv[k]), pr.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), ) - res = pr.MulBy014(res, + res = pr.MulBy02368(res, pr.MulByElement(&lines[k][1][i].R1, yInv[k]), pr.MulByElement(&lines[k][1][i].R0, xNegOverY[k]), ) @@ -439,13 +263,118 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl return res, nil } +// FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ +// where d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r +// we use instead d=s ⋅ (p⁶-1)(p²+1)(p⁴ - p² +1)/r +// where s is the cofactor 3 (Hayashida et al.) +func (pr Pairing) FinalExponentiation(e *GTEl) *GTEl { + z := pr.Copy(e) + + // Easy part + // (p⁶-1)(p²+1) + t0 := pr.Ext12.Conjugate(z) + t0 = pr.Ext12.DivUnchecked(t0, z) + z = pr.Ext12.FrobeniusSquare(t0) + z = pr.Ext12.Mul(z, t0) + + // Hard part (up to permutation) + // Daiki Hayashida, Kenichiro Hayasaka and Tadanori Teruya + // https://eprint.iacr.org/2020/875.pdf + t0 = pr.Ext12.CyclotomicSquareGS(z) + t1 := pr.Ext12.ExptHalfGS(t0) + t2 := pr.Ext12.Conjugate(z) + t1 = pr.Ext12.Mul(t1, t2) + t2 = pr.Ext12.ExptGS(t1) + t1 = pr.Ext12.Conjugate(t1) + t1 = pr.Ext12.Mul(t1, t2) + t2 = pr.Ext12.ExptGS(t1) + t1 = pr.Ext12.Frobenius(t1) + t1 = pr.Ext12.Mul(t1, t2) + z = pr.Ext12.Mul(z, t0) + t0 = pr.Ext12.ExptGS(t1) + t2 = pr.Ext12.ExptGS(t0) + t0 = pr.Ext12.FrobeniusSquare(t1) + t1 = pr.Ext12.Conjugate(t1) + t1 = pr.Ext12.Mul(t1, t2) + t1 = pr.Ext12.Mul(t1, t0) + z = pr.Ext12.Mul(z, t1) + + return z +} + +// AssertFinalExponentiationIsOne checks that a Miller function output x lies in the +// same equivalence class as the reduced pairing. This replaces the final +// exponentiation step in-circuit. +// The method is inspired from [On Proving Pairings] paper by A. Novakovic and +// L. Eagen, and is based on a personal communication with A. Novakovic. +// +// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf +func (pr Pairing) AssertFinalExponentiationIsOne(x *GTEl) { + tower := pr.ToTower(x) + + res, err := pr.curveF.NewHint(finalExpHint, 24, tower[0], tower[1], tower[2], tower[3], tower[4], tower[5], tower[6], tower[7], tower[8], tower[9], tower[10], tower[11]) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + + residueWitness := pr.FromTower([12]*baseEl{res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], res[8], res[9], res[10], res[11]}) + // constrain cubicNonResiduePower to be in Fp6 + // that is: a100=a101=a110=a111=a120=a121=0 + // or + // A0 = a000 - a001 + // A1 = 0 + // A2 = a010 - a011 + // A3 = 0 + // A4 = a020 - a021 + // A5 = 0 + // A6 = a001 + // A7 = 0 + // A8 = a011 + // A9 = 0 + // A10 = a021 + // A11 = 0 + scalingFactor := GTEl{ + A0: *pr.curveF.Sub(res[12], res[13]), + A1: *pr.curveF.Zero(), + A2: *pr.curveF.Sub(res[14], res[15]), + A3: *pr.curveF.Zero(), + A4: *pr.curveF.Sub(res[16], res[17]), + A5: *pr.curveF.Zero(), + A6: *res[13], + A7: *pr.curveF.Zero(), + A8: *res[15], + A9: *pr.curveF.Zero(), + A10: *res[17], + A11: *pr.curveF.Zero(), + } + + // Check that x * scalingFactor == residueWitness^(q-u) + // where u=-0xd201000000010000 is the BLS12-381 seed, + // and residueWitness, scalingFactor from the hint. + t0 := pr.Frobenius(residueWitness) + // exponentiation by -u + t1 := pr.ExptNeg(residueWitness) + t0 = pr.Ext12.Mul(t0, t1) + + t1 = pr.Ext12.Mul(x, &scalingFactor) + + pr.AssertIsEqual(t0, t1) +} + // doubleAndAddStep doubles p1 and adds p2 to the result in affine coordinates. -// Then evaluates the lines going through p1 and p2 (line1) and p1 and p1+p2 (line2). +// Then evaluates the lines going through p1 and p2 or -p2 (line1) and p1 and p1+p2 (line2). // https://eprint.iacr.org/2022/1162 (Section 6.1) func (pr Pairing) doubleAndAddStep(p1, p2 *g2AffP) (*g2AffP, *lineEvaluation, *lineEvaluation) { var line1, line2 lineEvaluation var p g2AffP + mone := pr.curveF.NewElement(-1) // compute λ1 = (y2-y1)/(x2-x1) n := pr.Ext2.Sub(&p1.Y, &p2.Y) @@ -453,39 +382,42 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *g2AffP) (*g2AffP, *lineEvaluation, *l λ1 := pr.Ext2.DivUnchecked(n, d) // compute x3 =λ1²-x1-x2 - x3 := pr.Ext2.Square(λ1) - x3 = pr.Ext2.Sub(x3, pr.Ext2.Add(&p1.X, &p2.X)) + x30 := pr.curveF.Eval([][]*baseEl{{&λ1.A0, &λ1.A0}, {mone, &λ1.A1, &λ1.A1}, {mone, &p1.X.A0}, {mone, &p2.X.A0}}, []int{1, 1, 1, 1}) + x31 := pr.curveF.Eval([][]*baseEl{{&λ1.A0, &λ1.A1}, {mone, &p1.X.A1}, {mone, &p2.X.A1}}, []int{2, 1, 1}) + x3 := &fields_bls12381.E2{A0: *x30, A1: *x31} // omit y3 computation // compute line1 line1.R0 = *λ1 - line1.R1 = *pr.Ext2.Mul(λ1, &p1.X) - line1.R1 = *pr.Ext2.Sub(&line1.R1, &p1.Y) + line1.R1.A0 = *pr.curveF.Eval([][]*baseEl{{&λ1.A0, &p1.X.A0}, {mone, &λ1.A1, &p1.X.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + line1.R1.A1 = *pr.curveF.Eval([][]*baseEl{{&λ1.A0, &p1.X.A1}, {&λ1.A1, &p1.X.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) // compute λ2 = -λ1-2y1/(x3-x1) - n = pr.Ext2.Double(&p1.Y) + n = pr.Ext2.MulByConstElement(&p1.Y, big.NewInt(2)) d = pr.Ext2.Sub(x3, &p1.X) λ2 := pr.Ext2.DivUnchecked(n, d) λ2 = pr.Ext2.Add(λ2, λ1) λ2 = pr.Ext2.Neg(λ2) // compute x4 = λ2²-x1-x3 - x4 := pr.Ext2.Square(λ2) - x4 = pr.Ext2.Sub(x4, pr.Ext2.Add(&p1.X, x3)) + x40 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &λ2.A0}, {mone, &λ2.A1, &λ2.A1}, {mone, &p1.X.A0}, {mone, x30}}, []int{1, 1, 1, 1}) + x41 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &λ2.A1}, {mone, &p1.X.A1}, {mone, x31}}, []int{2, 1, 1}) + x4 := &fields_bls12381.E2{A0: *x40, A1: *x41} // compute y4 = λ2(x1 - x4)-y1 y4 := pr.Ext2.Sub(&p1.X, x4) - y4 = pr.Ext2.Mul(λ2, y4) - y4 = pr.Ext2.Sub(y4, &p1.Y) + y40 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &y4.A0}, {mone, &λ2.A1, &y4.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + y41 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &y4.A1}, {&λ2.A1, &y4.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) + y4 = &fields_bls12381.E2{A0: *y40, A1: *y41} p.X = *x4 p.Y = *y4 // compute line2 line2.R0 = *λ2 - line2.R1 = *pr.Ext2.Mul(λ2, &p1.X) - line2.R1 = *pr.Ext2.Sub(&line2.R1, &p1.Y) + line2.R1.A0 = *pr.curveF.Eval([][]*baseEl{{&λ2.A0, &p1.X.A0}, {mone, &λ2.A1, &p1.X.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + line2.R1.A1 = *pr.curveF.Eval([][]*baseEl{{&λ2.A0, &p1.X.A1}, {&λ2.A1, &p1.X.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) return &p, &line1, &line2 } @@ -496,29 +428,31 @@ func (pr Pairing) doubleStep(p1 *g2AffP) (*g2AffP, *lineEvaluation) { var p g2AffP var line lineEvaluation + mone := pr.curveF.NewElement(-1) // λ = 3x²/2y n := pr.Ext2.Square(&p1.X) - three := big.NewInt(3) - n = pr.Ext2.MulByConstElement(n, three) - d := pr.Ext2.Double(&p1.Y) + n = pr.Ext2.MulByConstElement(n, big.NewInt(3)) + d := pr.Ext2.MulByConstElement(&p1.Y, big.NewInt(2)) λ := pr.Ext2.DivUnchecked(n, d) // xr = λ²-2x - xr := pr.Ext2.Square(λ) - xr = pr.Ext2.Sub(xr, pr.Ext2.MulByConstElement(&p1.X, big.NewInt(2))) + xr0 := pr.curveF.Eval([][]*baseEl{{&λ.A0, &λ.A0}, {mone, &λ.A1, &λ.A1}, {mone, &p1.X.A0}}, []int{1, 1, 2}) + xr1 := pr.curveF.Eval([][]*baseEl{{&λ.A0, &λ.A1}, {mone, &p1.X.A1}}, []int{2, 2}) + xr := &fields_bls12381.E2{A0: *xr0, A1: *xr1} // yr = λ(x-xr)-y yr := pr.Ext2.Sub(&p1.X, xr) - yr = pr.Ext2.Mul(λ, yr) - yr = pr.Ext2.Sub(yr, &p1.Y) + yr0 := pr.curveF.Eval([][]*baseEl{{&λ.A0, &yr.A0}, {mone, &λ.A1, &yr.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + yr1 := pr.curveF.Eval([][]*baseEl{{&λ.A0, &yr.A1}, {&λ.A1, &yr.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) + yr = &fields_bls12381.E2{A0: *yr0, A1: *yr1} p.X = *xr p.Y = *yr line.R0 = *λ - line.R1 = *pr.Ext2.Mul(λ, &p1.X) - line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + line.R1.A0 = *pr.curveF.Eval([][]*baseEl{{&λ.A0, &p1.X.A0}, {mone, &λ.A1, &p1.X.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + line.R1.A1 = *pr.curveF.Eval([][]*baseEl{{&λ.A0, &p1.X.A1}, {&λ.A1, &p1.X.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) return &p, &line @@ -529,6 +463,7 @@ func (pr Pairing) tripleStep(p1 *g2AffP) (*g2AffP, *lineEvaluation, *lineEvaluat var line1, line2 lineEvaluation var res g2AffP + mone := pr.curveF.NewElement(-1) // λ1 = 3x²/2y n := pr.Ext2.Square(&p1.X) @@ -539,12 +474,13 @@ func (pr Pairing) tripleStep(p1 *g2AffP) (*g2AffP, *lineEvaluation, *lineEvaluat // compute line1 line1.R0 = *λ1 - line1.R1 = *pr.Ext2.Mul(λ1, &p1.X) - line1.R1 = *pr.Ext2.Sub(&line1.R1, &p1.Y) + line1.R1.A0 = *pr.curveF.Eval([][]*baseEl{{&λ1.A0, &p1.X.A0}, {mone, &λ1.A1, &p1.X.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + line1.R1.A1 = *pr.curveF.Eval([][]*baseEl{{&λ1.A0, &p1.X.A1}, {&λ1.A1, &p1.X.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) // x2 = λ1²-2x - x2 := pr.Ext2.Square(λ1) - x2 = pr.Ext2.Sub(x2, pr.Ext2.MulByConstElement(&p1.X, big.NewInt(2))) + x20 := pr.curveF.Eval([][]*baseEl{{&λ1.A0, &λ1.A0}, {mone, &λ1.A1, &λ1.A1}, {mone, &p1.X.A0}}, []int{1, 1, 2}) + x21 := pr.curveF.Eval([][]*baseEl{{&λ1.A0, &λ1.A1}, {mone, &p1.X.A1}}, []int{2, 2}) + x2 := &fields_bls12381.E2{A0: *x20, A1: *x21} // omit yr computation, and // compute λ2 = 2y/(x2 − x) − λ1. @@ -554,18 +490,19 @@ func (pr Pairing) tripleStep(p1 *g2AffP) (*g2AffP, *lineEvaluation, *lineEvaluat // compute line2 line2.R0 = *λ2 - line2.R1 = *pr.Ext2.Mul(λ2, &p1.X) - line2.R1 = *pr.Ext2.Sub(&line2.R1, &p1.Y) + line2.R1.A0 = *pr.curveF.Eval([][]*baseEl{{&λ2.A0, &p1.X.A0}, {mone, &λ2.A1, &p1.X.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + line2.R1.A1 = *pr.curveF.Eval([][]*baseEl{{&λ2.A0, &p1.X.A1}, {&λ2.A1, &p1.X.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) - // xr = λ²-p.x-x2 - λ2λ2 := pr.Ext2.Mul(λ2, λ2) - qxrx := pr.Ext2.Add(x2, &p1.X) - xr := pr.Ext2.Sub(λ2λ2, qxrx) + // xr = λ²-x1-x2 + xr0 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &λ2.A0}, {mone, &λ2.A1, &λ2.A1}, {mone, &p1.X.A0}, {mone, x20}}, []int{1, 1, 1, 1}) + xr1 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &λ2.A1}, {mone, &p1.X.A1}, {mone, x21}}, []int{2, 1, 1}) + xr := &fields_bls12381.E2{A0: *xr0, A1: *xr1} - // yr = λ(p.x-xr) - p.y - pxrx := pr.Ext2.Sub(&p1.X, xr) - λ2pxrx := pr.Ext2.Mul(λ2, pxrx) - yr := pr.Ext2.Sub(λ2pxrx, &p1.Y) + // yr = λ(x1-xr) - y1 + yr := pr.Ext2.Sub(&p1.X, xr) + yr0 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &yr.A0}, {mone, &λ2.A1, &yr.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + yr1 := pr.curveF.Eval([][]*baseEl{{&λ2.A0, &yr.A1}, {&λ2.A1, &yr.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) + yr = &fields_bls12381.E2{A0: *yr0, A1: *yr1} res.X = *xr res.Y = *yr @@ -584,9 +521,10 @@ func (pr Pairing) tangentCompute(p1 *g2AffP) *lineEvaluation { λ := pr.Ext2.DivUnchecked(n, d) var line lineEvaluation + mone := pr.curveF.NewElement(-1) line.R0 = *λ - line.R1 = *pr.Ext2.Mul(λ, &p1.X) - line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + line.R1.A0 = *pr.curveF.Eval([][]*baseEl{{&λ.A0, &p1.X.A0}, {mone, &λ.A1, &p1.X.A1}, {mone, &p1.Y.A0}}, []int{1, 1, 1}) + line.R1.A1 = *pr.curveF.Eval([][]*baseEl{{&λ.A0, &p1.X.A1}, {&λ.A1, &p1.X.A0}, {mone, &p1.Y.A1}}, []int{1, 1, 1}) return &line diff --git a/std/algebra/emulated/sw_bls12381/pairing_test.go b/std/algebra/emulated/sw_bls12381/pairing_test.go index 80490e280a..30cbb14a0d 100644 --- a/std/algebra/emulated/sw_bls12381/pairing_test.go +++ b/std/algebra/emulated/sw_bls12381/pairing_test.go @@ -43,10 +43,8 @@ func (c *FinalExponentiationCircuit) Define(api frontend.API) error { if err != nil { return fmt.Errorf("new pairing: %w", err) } - res1 := pairing.FinalExponentiation(&c.InGt) - pairing.AssertIsEqual(res1, &c.Res) - res2 := pairing.FinalExponentiationUnsafe(&c.InGt) - pairing.AssertIsEqual(res2, &c.Res) + res := pairing.FinalExponentiation(&c.InGt) + pairing.AssertIsEqual(res, &c.Res) return nil } @@ -63,6 +61,40 @@ func TestFinalExponentiationTestSolve(t *testing.T) { assert.NoError(err) } +type FinalExponentiationIsOne struct { + InGt GTEl +} + +func (c *FinalExponentiationIsOne) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + pairing.AssertFinalExponentiationIsOne(&c.InGt) + return nil +} + +func TestFinalExponentiationIsOneTestSolve(t *testing.T) { + assert := test.NewAssert(t) + // e(a,2b) * e(-2a,b) == 1 + p1, q1 := randomG1G2Affines() + var p2 bls12381.G1Affine + p2.Double(&p1).Neg(&p2) + var q2 bls12381.G2Affine + q2.Set(&q1) + q1.Double(&q1) + ml, err := bls12381.MillerLoop( + []bls12381.G1Affine{p1, p2}, + []bls12381.G2Affine{q1, q2}, + ) + assert.NoError(err) + witness := FinalExponentiationIsOne{ + InGt: NewGTEl(ml), + } + err = test.IsSolved(&FinalExponentiationIsOne{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + type PairCircuit struct { InG1 G1Affine InG2 G2Affine @@ -295,64 +327,3 @@ func BenchmarkPairing(b *testing.B) { } }) } - -func BenchmarkFinalExponentiation(b *testing.B) { - // e(a,2b) * e(-2a,b) == 1 - var gt bls12381.GT - gt.SetRandom() - res := bls12381.FinalExponentiation(>) - witness := FinalExponentiationCircuit{ - InGt: NewGTEl(gt), - Res: NewGTEl(res), - } - w, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) - if err != nil { - b.Fatal(err) - } - var ccs constraint.ConstraintSystem - b.Run("compile scs", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - if ccs, err = frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &FinalExponentiationCircuit{}); err != nil { - b.Fatal(err) - } - } - }) - var buf bytes.Buffer - _, err = ccs.WriteTo(&buf) - if err != nil { - b.Fatal(err) - } - b.Logf("scs size: %d (bytes), nb constraints %d, nbInstructions: %d", buf.Len(), ccs.GetNbConstraints(), ccs.GetNbInstructions()) - b.Run("solve scs", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := ccs.Solve(w); err != nil { - b.Fatal(err) - } - } - }) - b.Run("compile r1cs", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - if ccs, err = frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &FinalExponentiationCircuit{}); err != nil { - b.Fatal(err) - } - } - }) - buf.Reset() - _, err = ccs.WriteTo(&buf) - if err != nil { - b.Fatal(err) - } - b.Logf("r1cs size: %d (bytes), nb constraints %d, nbInstructions: %d", buf.Len(), ccs.GetNbConstraints(), ccs.GetNbInstructions()) - - b.Run("solve r1cs", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := ccs.Solve(w); err != nil { - b.Fatal(err) - } - } - }) -}