From c41d12d4945df1e6334c51ec67b075159e462c33 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 27 Nov 2024 16:03:26 -0500 Subject: [PATCH 01/22] perf(sw_bn254): use Eval for E2 --- std/algebra/emulated/fields_bn254/e2.go | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e2.go b/std/algebra/emulated/fields_bn254/e2.go index 5d38c50eaf..fea7225a79 100644 --- a/std/algebra/emulated/fields_bn254/e2.go +++ b/std/algebra/emulated/fields_bn254/e2.go @@ -201,16 +201,10 @@ func (e Ext2) MulByNonResidue3Power5(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, @@ -270,11 +264,10 @@ func (e Ext2) IsZero(z *E2) frontend.Variable { } 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, From 06898d3ab8d29b3e284740a414b08c7a5b092f3f Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Dec 2024 16:41:21 -0500 Subject: [PATCH 02/22] feat: SZ direct Fp12 mul --- std/algebra/emulated/fields_bn254/e12.go | 770 +++++++++++++----- .../emulated/fields_bn254/e12_pairing.go | 542 ------------ std/algebra/emulated/fields_bn254/e12_test.go | 31 + std/algebra/emulated/fields_bn254/e6.go | 495 ----------- std/algebra/emulated/fields_bn254/e6_test.go | 444 ---------- std/algebra/emulated/sw_bn254/pairing.go | 214 +---- 6 files changed, 644 insertions(+), 1852 deletions(-) delete mode 100644 std/algebra/emulated/fields_bn254/e12_pairing.go delete mode 100644 std/algebra/emulated/fields_bn254/e6.go delete mode 100644 std/algebra/emulated/fields_bn254/e6_test.go diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 1cc99fe365..98b6b65a49 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -1,263 +1,653 @@ package fields_bn254 import ( + "math/big" + "github.com/consensys/gnark-crypto/ecc/bn254" + fp_bn "github.com/consensys/gnark-crypto/ecc/bn254/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 + api frontend.API + fp *curveF } func NewExt12(api frontend.API) *Ext12 { - return &Ext12{Ext6: NewExt6(api)} + fp, err := emulated.NewField[emulated.BN254Fp](api) + if err != nil { + panic(err) + } + return &Ext12{ + 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) Reduce(x *E12) *E12 { + var z E12 + z.A0 = *e.fp.Reduce(&x.A0) + z.A1 = *e.fp.Reduce(&x.A1) + z.A2 = *e.fp.Reduce(&x.A2) + z.A3 = *e.fp.Reduce(&x.A3) + z.A4 = *e.fp.Reduce(&x.A4) + z.A5 = *e.fp.Reduce(&x.A5) + z.A6 = *e.fp.Reduce(&x.A6) + z.A7 = *e.fp.Reduce(&x.A7) + z.A8 = *e.fp.Reduce(&x.A8) + z.A9 = *e.fp.Reduce(&x.A9) + z.A10 = *e.fp.Reduce(&x.A10) + z.A11 = *e.fp.Reduce(&x.A11) + + return &z +} + +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) MulByElement(x *E12, y *baseEl) *E12 { + a0 := e.fp.Mul(&x.A0, y) + a1 := e.fp.Mul(&x.A1, y) + a2 := e.fp.Mul(&x.A2, y) + a3 := e.fp.Mul(&x.A3, y) + a4 := e.fp.Mul(&x.A4, y) + a5 := e.fp.Mul(&x.A5, y) + a6 := e.fp.Mul(&x.A6, y) + a7 := e.fp.Mul(&x.A7, y) + a8 := e.fp.Mul(&x.A8, y) + a9 := e.fp.Mul(&x.A9, y) + a10 := e.fp.Mul(&x.A10, y) + a11 := e.fp.Mul(&x.A11, y) -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) 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, } } -// 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) MulByConstElement(x *E12, y *big.Int) *E12 { + a0 := e.fp.MulConst(&x.A0, y) + a1 := e.fp.MulConst(&x.A1, y) + a2 := e.fp.MulConst(&x.A2, y) + a3 := e.fp.MulConst(&x.A3, y) + a4 := e.fp.MulConst(&x.A4, y) + a5 := e.fp.MulConst(&x.A5, y) + a6 := e.fp.MulConst(&x.A6, y) + a7 := e.fp.MulConst(&x.A7, y) + a8 := e.fp.MulConst(&x.A8, y) + a9 := e.fp.MulConst(&x.A9, y) + a10 := e.fp.MulConst(&x.A10, y) + a11 := e.fp.MulConst(&x.A11, y) + return &E12{ - C0: E6{ - B0: *z00, - B1: *z01, - B2: *z02, - }, - C1: E6{ - B0: *z10, - B1: *z11, - B2: *z12, - }, + 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) IsEqual(x, y *E12) frontend.Variable { - isC0Equal := e.Ext6.IsEqual(&x.C0, &y.C0) - isC1Equal := e.Ext6.IsEqual(&x.C1, &y.C1) - return e.api.And(isC0Equal, isC1Equal) +func (e Ext12) Conjugate(x *E12) *E12 { + return &E12{ + 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), + } } -func (e Ext12) AssertIsEqual(x, y *E12) { - e.Ext6.AssertIsEqual(&x.C0, &y.C0) - e.Ext6.AssertIsEqual(&x.C1, &y.C1) +func (e Ext12) Mul(x, y *E12) *E12 { + return e.mulDirect(x, y) } -func FromE12(y *bn254.E12) E12 { - return E12{ - C0: FromE6(&y.C0), - C1: FromE6(&y.C1), - } +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 = 18 w^6 - 82, 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 - 82 * c12 - 1476 * c18 + // d1 = c1 - 82 * c13 - 1476 * c19 + // d2 = c2 - 82 * c14 - 1476 * c20 + // d3 = c3 - 82 * c15 - 1476 * c21 + // d4 = c4 - 82 * c16 - 1476 * c22 + // d5 = c5 - 82 * c17 + // d6 = c6 + 18 * c12 + 242 * c18 + // d7 = c7 + 18 * c13 + 242 * c19 + // d8 = c8 + 18 * c14 + 242 * c20 + // d9 = c9 + 18 * c15 + 242 * c21 + // d10 = c10 + 18 * c16 + 242 * c22 + // d11 = c11 + 18 * 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 -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) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } + // d0 = c0 - 82 * c12 - 1476 * c18 + // = a0 b0 - 82 * (a1 b11 + a2 b10 + a3 b9 + a4 b8 + a5 b7 + a6 b6 + a7 b5 + a8 b4 + a9 b3 + a10 b2 + a11 b1) - 1476 * (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, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 1476, 1476, 1476, 1476, 1476}) - 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]}, - }, - } + // d1 = c1 - 82 * c13 - 1476 * c19 + // = a0 b1 + a1 b0 - 82 * (a2 b11 + a3 b10 + a4 b9 + a5 b8 + a6 b7 + a7 b6 + a8 b5 + a9 b4 + a10 b3 + a11 b2) - 1476 * (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, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, 1476, 1476, 1476, 1476}) + + // d2 = c2 - 82 * c14 - 1476 * c20 + // = a0 b2 + a1 b1 + a2 b0 - 82 * (a3 b11 + a4 b10 + a5 b9 + a6 b8 + a7 b7 + a8 b6 + a9 b5 + a10 b4 + a11 b3) - 1476 * (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, 82, 82, 82, 82, 82, 82, 82, 82, 82, 1476, 1476, 1476}) + + // d3 = c3 - 82 * c15 - 1476 * c21 + // = a0 b3 + a1 b2 + a2 b1 + a3 b0 - 82 * (a4 b11 + a5 b10 + a6 b9 + a7 b8 + a8 b7 + a9 b6 + a10 b5 + a11 b4) - 1476 * (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, 82, 82, 82, 82, 82, 82, 82, 82, 1476, 1476}) + + // d4 = c4 - 82 * c16 - 1476 * c22 + // = a0 b4 + a1 b3 + a2 b2 + a3 b1 + a4 b0 - 82 * (a5 b11 + a6 b10 + a7 b9 + a8 b8 + a9 b7 + a10 b6 + a11 b5) - 1476 * 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, 82, 82, 82, 82, 82, 82, 82, 1476}) + + // d5 = c5 - 82 * c17 + // = a0 b5 + a1 b4 + a2 b3 + a3 b2 + a4 b1 + a5 b0 - 82 * (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, 82, 82, 82, 82, 82, 82}) + + // d6 = c6 + 18 * c12 + 242 * c18 + // = a0 b6 + a1 b5 + a2 b4 + a3 b3 + a4 b2 + a5 b1 + a6 b0 + 18 * (a1 b11 + a2 b10 + a3 b9 + a4 b8 + a5 b7 + a6 b6 + a7 b5 + a8 b4 + a9 b3 + a10 b2 + a11 b1) + 242 * (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, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 242, 242, 242, 242, 242}) + + // d7 = c7 + 18 * c13 + 242 * c19 + // = a0 b7 + a1 b6 + a2 b5 + a3 b4 + a4 b3 + a5 b2 + a6 b1 + a7 b0 + 18 * (a2 b11 + a3 b10 + a4 b9 + a5 b8 + a6 b7 + a7 b6 + a8 b5 + a9 b4 + a10 b3 + a11 b2) + 242 * (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, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 242, 242, 242, 242}) + + // d8 = c8 + 18 * c14 + 242 * c20 + // = a0 b8 + a1 b7 + a2 b6 + a3 b5 + a4 b4 + a5 b3 + a6 b2 + a7 b1 + a8 b0 + 18 * (a3 b11 + a4 b10 + a5 b9 + a6 b8 + a7 b7 + a8 b6 + a9 b5 + a10 b4 + a11 b3) + 242 * (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, 18, 18, 18, 18, 18, 18, 18, 18, 18, 242, 242, 242}) - one := e.One() + // d9 = c9 + 18 * c15 + 242 * c21 + // = a0 b9 + a1 b8 + a2 b7 + a3 b6 + a4 b5 + a5 b4 + a6 b3 + a7 b2 + a8 b1 + a9 b0 + 18 * (a4 b11 + a5 b10 + a6 b9 + a7 b8 + a8 b7 + a9 b6 + a10 b5 + a11 b4) + 242 * (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, 18, 18, 18, 18, 18, 18, 18, 18, 242, 242}) - // 1 == inv * x - _one := e.Mul(&inv, x) - e.AssertIsEqual(one, _one) + // d10 = c10 + 18 * c16 + 242 * c22 + // = a0 b10 + a1 b9 + a2 b8 + a3 b7 + a4 b6 + a5 b5 + a6 b4 + a7 b3 + a8 b2 + a9 b1 + a10 b0 + 18 * (a5 b11 + a6 b10 + a7 b9 + a8 b8 + a9 b7 + a10 b6 + a11 b5) + 242 * (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, 18, 18, 18, 18, 18, 18, 18, 242}) - return &inv + // d11 = c11 + 18 * 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 + 18 * (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, 18, 18, 18, 18, 18, 18}) + 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) 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) +func (e Ext12) Select(selector frontend.Variable, z1, z0 *E12) *E12 { + a0 := e.fp.Select(selector, &z1.A0, &z0.A0) + a1 := e.fp.Select(selector, &z1.A1, &z0.A1) + a2 := e.fp.Select(selector, &z1.A2, &z0.A2) + a3 := e.fp.Select(selector, &z1.A3, &z0.A3) + a4 := e.fp.Select(selector, &z1.A4, &z0.A4) + a5 := e.fp.Select(selector, &z1.A5, &z0.A5) + a6 := e.fp.Select(selector, &z1.A6, &z0.A6) + a7 := e.fp.Select(selector, &z1.A7, &z0.A7) + a8 := e.fp.Select(selector, &z1.A8, &z0.A8) + a9 := e.fp.Select(selector, &z1.A9, &z0.A9) + a10 := e.fp.Select(selector, &z1.A10, &z0.A10) + a11 := e.fp.Select(selector, &z1.A11, &z0.A11) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) + return &E12{ + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } +} - 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]}, - }, +func (e Ext12) Lookup2(s1, s2 frontend.Variable, z0, z1, z2, z3 *E12) *E12 { + a0 := e.fp.Lookup2(s1, s2, &z0.A0, &z1.A0, &z2.A0, &z3.A0) + a1 := e.fp.Lookup2(s1, s2, &z0.A1, &z1.A1, &z2.A1, &z3.A1) + a2 := e.fp.Lookup2(s1, s2, &z0.A2, &z1.A2, &z2.A2, &z3.A2) + a3 := e.fp.Lookup2(s1, s2, &z0.A3, &z1.A3, &z2.A3, &z3.A3) + a4 := e.fp.Lookup2(s1, s2, &z0.A4, &z1.A4, &z2.A4, &z3.A4) + a5 := e.fp.Lookup2(s1, s2, &z0.A5, &z1.A5, &z2.A5, &z3.A5) + a6 := e.fp.Lookup2(s1, s2, &z0.A6, &z1.A6, &z2.A6, &z3.A6) + a7 := e.fp.Lookup2(s1, s2, &z0.A7, &z1.A7, &z2.A7, &z3.A7) + a8 := e.fp.Lookup2(s1, s2, &z0.A8, &z1.A8, &z2.A8, &z3.A8) + a9 := e.fp.Lookup2(s1, s2, &z0.A9, &z1.A9, &z2.A9, &z3.A9) + a10 := e.fp.Lookup2(s1, s2, &z0.A10, &z1.A10, &z2.A10, &z3.A10) + a11 := e.fp.Lookup2(s1, s2, &z0.A11, &z1.A11, &z2.A11, &z3.A11) + + return &E12{ + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } +} - // x == div * y - _x := e.Mul(&div, y) - e.AssertIsEqual(x, _x) +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) +} - return &div +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) 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) 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 *bn254.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 - 9 * a001 + // A1 = a100 - 9 * a101 + // A2 = a010 - 9 * a011 + // A3 = a110 - 9 * a111 + // A4 = a020 - 9 * a021 + // A5 = a120 - 9 * a121 + // A6 = a001 + // A7 = a101 + // A8 = a011 + // A9 = a111 + // A10 = a021 + // A11 = a121 + + var c0, c1, c2, c3, c4, c5, t fp_bn.Element + t.SetUint64(9).Mul(&t, &a.C0.B0.A1) + c0.Sub(&a.C0.B0.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B0.A1) + c1.Sub(&a.C1.B0.A0, &t) + t.SetUint64(9).Mul(&t, &a.C0.B1.A1) + c2.Sub(&a.C0.B1.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B1.A1) + c3.Sub(&a.C1.B1.A0, &t) + t.SetUint64(9).Mul(&t, &a.C0.B2.A1) + c4.Sub(&a.C0.B2.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B2.A1) + c5.Sub(&a.C1.B2.A0, &t) + + return E12{ + A0: emulated.ValueOf[emulated.BN254Fp](c0), + A1: emulated.ValueOf[emulated.BN254Fp](c1), + A2: emulated.ValueOf[emulated.BN254Fp](c2), + A3: emulated.ValueOf[emulated.BN254Fp](c3), + A4: emulated.ValueOf[emulated.BN254Fp](c4), + A5: emulated.ValueOf[emulated.BN254Fp](c5), + A6: emulated.ValueOf[emulated.BN254Fp](a.C0.B0.A1), + A7: emulated.ValueOf[emulated.BN254Fp](a.C1.B0.A1), + A8: emulated.ValueOf[emulated.BN254Fp](a.C0.B1.A1), + A9: emulated.ValueOf[emulated.BN254Fp](a.C1.B1.A1), + A10: emulated.ValueOf[emulated.BN254Fp](a.C0.B2.A1), + A11: emulated.ValueOf[emulated.BN254Fp](a.C1.B2.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} +// direct to tower extension conversion +func (e Ext12) e12RoundTrip(a *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 + + // a000 = A0 + 9 * A6 + // a001 = A6 + // a010 = A2 + 9 * A8 + // a011 = A8 + // a020 = A4 + 9 * A10 + // a021 = A10 + // a100 = A1 + 9 * A7 + // a101 = A7 + // a110 = A3 + 9 * A9 + // a111 = A9 + // a120 = A5 + 9 * A11 + // a121 = A11 + nine := big.NewInt(9) + a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) + a001 := a.A6 + a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) + a011 := a.A8 + a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) + a021 := a.A10 + a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) + a101 := a.A7 + a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) + a111 := a.A9 + a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) + a121 := a.A11 + + // A0 = a000 - 9 * a001 + // A1 = a100 - 9 * a101 + // A2 = a010 - 9 * a011 + // A3 = a110 - 9 * a111 + // A4 = a020 - 9 * a021 + // A5 = a120 - 9 * a121 + // A6 = a001 + // A7 = a101 + // A8 = a011 + // A9 = a111 + // A10 = a021 + // A11 = a121 + A0 := e.fp.Sub(a000, e.fp.MulConst(&a001, nine)) + A1 := e.fp.Sub(a100, e.fp.MulConst(&a101, nine)) + A2 := e.fp.Sub(a010, e.fp.MulConst(&a011, nine)) + A3 := e.fp.Sub(a110, e.fp.MulConst(&a111, nine)) + A4 := e.fp.Sub(a020, e.fp.MulConst(&a021, nine)) + A5 := e.fp.Sub(a120, e.fp.MulConst(&a121, nine)) + A6 := a001 + A7 := a101 + A8 := a011 + A9 := a111 + A10 := a021 + A11 := a121 + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: A6, + A7: A7, + A8: A8, + A9: A9, + A10: A10, + A11: A11, + } } diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go deleted file mode 100644 index f7c93c75e7..0000000000 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ /dev/null @@ -1,542 +0,0 @@ -package fields_bn254 - -import ( - "github.com/consensys/gnark/std/math/emulated" -) - -func (e Ext12) nSquareGS(z *E12, n int) *E12 { - for i := 0; i < n; i++ { - z = e.CyclotomicSquare(z) - } - return z -} - -// Exponentiation by U=6u+2 where t is the seed u=4965661367192848881 -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 - // _1100 = 2*_110 - // _1111 = _11 + _1100 - // _1100000 = _1100 << 3 - // _1100111 = _111 + _1100000 - // i22 = ((_1100111 << 2 + 1) << 5 + _1111) << 3 - // i38 = ((1 + i22) << 4 + _111) << 9 + _111 - // i50 = 2*((i38 << 4 + _11) << 5 + _1111) - // i61 = ((1 + i50) << 5 + _111) << 3 + _11 - // i75 = ((i61 << 6 + _111) << 4 + _111) << 2 - // return ((1 + i75) << 2 + 1) << 3 - // - // Operations: 64 squares 18 multiplies - // - // Generated by github.com/mmcloughlin/addchain v0.4.0. - - z := e.Square(x) - t0 := e.Mul(x, z) - t1 := e.Square(t0) - z = e.Mul(x, t1) - t2 := e.Square(t1) - t1 = e.Mul(t0, t2) - t2 = e.nSquareGS(t2, 3) - t2 = e.Mul(z, t2) - t2 = e.nSquareGS(t2, 2) - t2 = e.Mul(x, t2) - t2 = e.nSquareGS(t2, 5) - t2 = e.Mul(t1, t2) - t2 = e.nSquareGS(t2, 3) - t2 = e.Mul(x, t2) - t2 = e.nSquareGS(t2, 4) - t2 = e.Mul(z, t2) - t2 = e.nSquareGS(t2, 9) - t2 = e.Mul(z, t2) - t2 = e.nSquareGS(t2, 4) - t2 = e.Mul(t0, t2) - t2 = e.nSquareGS(t2, 5) - t1 = e.Mul(t1, t2) - t1 = e.Square(t1) - t1 = e.Mul(x, t1) - t1 = e.nSquareGS(t1, 5) - t1 = e.Mul(z, t1) - t1 = e.nSquareGS(t1, 3) - t0 = e.Mul(t0, t1) - t0 = e.nSquareGS(t0, 6) - t0 = e.Mul(z, t0) - t0 = e.nSquareGS(t0, 4) - z = e.Mul(z, t0) - z = e.nSquareGS(z, 2) - z = e.Mul(x, z) - z = e.nSquareGS(z, 2) - z = e.Mul(x, z) - z = e.nSquareGS(z, 3) - - return z -} - -func (e Ext12) nSquareTorus(z *E6, n int) *E6 { - for i := 0; i < n; i++ { - z = e.SquareTorus(z) - } - return z -} - -// Exponentiation by the seed t=4965661367192848881 -// The computations are performed on E6 compressed form using Torus-based arithmetic. -func (e Ext12) ExptTorus(x *E6) *E6 { - // ExptTorus computation is derived from the addition chain: - // - // _10 = 2*1 - // _100 = 2*_10 - // _1000 = 2*_100 - // _10000 = 2*_1000 - // _10001 = 1 + _10000 - // _10011 = _10 + _10001 - // _10100 = 1 + _10011 - // _11001 = _1000 + _10001 - // _100010 = 2*_10001 - // _100111 = _10011 + _10100 - // _101001 = _10 + _100111 - // i27 = (_100010 << 6 + _100 + _11001) << 7 + _11001 - // i44 = (i27 << 8 + _101001 + _10) << 6 + _10001 - // i70 = ((i44 << 8 + _101001) << 6 + _101001) << 10 - // return (_100111 + i70) << 6 + _101001 + _1000 - // - // Operations: 62 squares 17 multiplies - // - // Generated by github.com/mmcloughlin/addchain v0.4.0. - - t3 := e.SquareTorus(x) - t5 := e.SquareTorus(t3) - result := e.SquareTorus(t5) - t0 := e.SquareTorus(result) - t2 := e.MulTorus(x, t0) - t0 = e.MulTorus(t3, t2) - t1 := e.MulTorus(x, t0) - t4 := e.MulTorus(result, t2) - t6 := e.SquareTorus(t2) - t1 = e.MulTorus(t0, t1) - t0 = e.MulTorus(t3, t1) - t6 = e.nSquareTorus(t6, 6) - t5 = e.MulTorus(t5, t6) - t5 = e.MulTorus(t4, t5) - t5 = e.nSquareTorus(t5, 7) - t4 = e.MulTorus(t4, t5) - t4 = e.nSquareTorus(t4, 8) - t4 = e.MulTorus(t0, t4) - t3 = e.MulTorus(t3, t4) - t3 = e.nSquareTorus(t3, 6) - t2 = e.MulTorus(t2, t3) - t2 = e.nSquareTorus(t2, 8) - t2 = e.MulTorus(t0, t2) - t2 = e.nSquareTorus(t2, 6) - t2 = e.MulTorus(t0, t2) - t2 = e.nSquareTorus(t2, 10) - t1 = e.MulTorus(t1, t2) - t1 = e.nSquareTorus(t1, 6) - t0 = e.MulTorus(t0, t1) - z := e.MulTorus(result, t0) - return z -} - -// Square034 squares an E12 sparse element of the form -// -// E12{ -// C0: E6{B0: 1, B1: 0, B2: 0}, -// C1: E6{B0: c3, B1: c4, B2: 0}, -// } -func (e *Ext12) Square034(x *E12) *E12 { - c0 := E6{ - B0: *e.Ext2.Sub(&x.C0.B0, &x.C1.B0), - B1: *e.Ext2.Neg(&x.C1.B1), - B2: *e.Ext2.Zero(), - } - - c3 := &E6{ - B0: x.C0.B0, - B1: *e.Ext2.Neg(&x.C1.B0), - B2: *e.Ext2.Neg(&x.C1.B1), - } - - c2 := E6{ - B0: x.C1.B0, - B1: x.C1.B1, - B2: *e.Ext2.Zero(), - } - c3 = e.MulBy01(c3, &c0.B0, &c0.B1) - c3 = e.Ext6.Add(c3, &c2) - - var z E12 - z.C1.B0 = *e.Ext2.Add(&c2.B0, &c2.B0) - z.C1.B1 = *e.Ext2.Add(&c2.B1, &c2.B1) - - z.C0.B0 = c3.B0 - z.C0.B1 = *e.Ext2.Add(&c3.B1, &c2.B0) - z.C0.B2 = *e.Ext2.Add(&c3.B2, &c2.B1) - - return &z -} - -// MulBy034 multiplies z by an E12 sparse element of the form -// -// E12{ -// C0: E6{B0: 1, B1: 0, B2: 0}, -// C1: E6{B0: c3, B1: c4, B2: 0}, -// } -func (e *Ext12) MulBy034(z *E12, c3, c4 *E2) *E12 { - - a := z.C0 - b := e.MulBy01(&z.C1, c3, c4) - c3 = e.Ext2.Add(e.Ext2.One(), c3) - d := e.Ext6.Add(&z.C0, &z.C1) - d = e.MulBy01(d, c3, c4) - - zC1 := e.Ext6.Add(&a, b) - zC1 = e.Ext6.Neg(zC1) - zC1 = e.Ext6.Add(zC1, d) - 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: 1, B1: 0, B2: 0}, -// C1: E6{B0: c3, B1: c4, B2: 0}, -// } -// -// and -// -// E12{ -// C0: E6{B0: 1, B1: 0, B2: 0}, -// C1: E6{B0: d3, B1: d4, B2: 0}, -// } -func (e *Ext12) Mul034By034(d3, d4, c3, c4 *E2) [5]*E2 { - x3 := e.Ext2.Mul(c3, d3) - x4 := e.Ext2.Mul(c4, d4) - x04 := e.Ext2.Add(c4, d4) - x03 := e.Ext2.Add(c3, d3) - tmp := e.Ext2.Add(c3, c4) - x34 := e.Ext2.Add(d3, d4) - x34 = e.Ext2.Mul(x34, tmp) - tmp = e.Ext2.Add(x4, x3) - x34 = e.Ext2.Sub(x34, tmp) - - zC0B0 := e.Ext2.MulByNonResidue(x4) - zC0B0 = e.Ext2.Add(zC0B0, e.Ext2.One()) - zC0B1 := x3 - zC0B2 := x34 - zC1B0 := x03 - zC1B1 := x04 - - return [5]*E2{zC0B0, zC0B1, zC0B2, zC1B0, zC1B1} -} - -// MulBy01234 multiplies z by an E12 sparse element of the form -// -// E12{ -// C0: E6{B0: c0, B1: c1, B2: c2}, -// C1: E6{B0: c3, B1: c4, B2: 0}, -// } -func (e *Ext12) MulBy01234(z *E12, x [5]*E2) *E12 { - c0 := &E6{B0: *x[0], B1: *x[1], B2: *x[2]} - c1 := &E6{B0: *x[3], B1: *x[4], B2: *e.Ext2.Zero()} - 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.MulBy01(&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, - } -} - -// multiplies two E12 sparse element of the form: -// -// E12{ -// C0: E6{B0: x0, B1: x1, B2: x2}, -// C1: E6{B0: x3, B1: x4, B2: 0}, -// } -// -// and -// -// E12{ -// C0: E6{B0: 1, B1: 0, B2: 0}, -// C1: E6{B0: z3, B1: z4, B2: 0}, -// } -func (e *Ext12) Mul01234By034(x [5]*E2, z3, z4 *E2) *E12 { - c0 := &E6{B0: *x[0], B1: *x[1], B2: *x[2]} - c1 := &E6{B0: *x[3], B1: *x[4], B2: *e.Ext2.Zero()} - a := e.Ext6.Add(e.Ext6.One(), &E6{B0: *z3, B1: *z4, B2: *e.Ext2.Zero()}) - b := e.Ext6.Add(c0, c1) - a = e.Ext6.Mul(a, b) - c := e.Ext6.Mul01By01(z3, z4, x[3], x[4]) - b = e.Ext6.Add(c0, c) - z1 := e.Ext6.Sub(a, b) - z0 := e.Ext6.MulByNonResidue(c) - z0 = e.Ext6.Add(z0, c0) - 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³-9-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 = &E6{ - B0: n.B0, - B1: *e.Ext2.Add(&n.B1, e.Ext2.One()), - B2: n.B2, - } - d := e.Ext6.Add(y1, y2) - y3 := e.Ext6.DivUnchecked(n, d) - return y3 -} - -// InverseTorus inverses a compressed elements y ∈ E6 -// and returns -y -func (e Ext12) InverseTorus(y *E6) *E6 { - return e.Ext6.Neg(y) -} - -// SquareTorus squares a compressed elements y ∈ E6 -// and returns (y + v/y)/2 -// -// 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) - } - - 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]}, - } - - // v = (2x-y)y - v := e.Ext6.Double(&sq) - v = e.Ext6.Sub(v, y) - v = e.Ext6.Mul(v, y) - - _v := E6{B0: *e.Ext2.Zero(), B1: *e.Ext2.One(), B2: *e.Ext2.Zero()} - e.Ext6.AssertIsEqual(v, &_v) - - return &sq - -} - -// 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) - - v0 := E2{emulated.ValueOf[emulated.BN254Fp]("18566938241244942414004596690298913868373833782006617400804628704885040364344"), emulated.ValueOf[emulated.BN254Fp]("5722266937896532885780051958958348231143373700109372999374820235121374419868")} - res := &E6{B0: *t0, B1: *t1, B2: *t2} - res = e.Ext6.MulBy0(res, &v0) - - return res -} - -// 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.BN254Fp]("2203960485148121921418603742825762020974279258880205651967") - 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) - - return &E6{B0: *t0, B1: *t1, B2: *t2} -} - -// FrobeniusCubeTorus raises a compressed elements y ∈ E6 to the cube modulus p^3 -// and returns y^(p^3) / v^((p^3-1)/2) -func (e Ext12) FrobeniusCubeTorus(y *E6) *E6 { - t0 := e.Ext2.Conjugate(&y.B0) - t1 := e.Ext2.Conjugate(&y.B1) - t2 := e.Ext2.Conjugate(&y.B2) - t1 = e.Ext2.MulByNonResidue3Power2(t1) - t2 = e.Ext2.MulByNonResidue3Power4(t2) - - v0 := E2{emulated.ValueOf[emulated.BN254Fp]("10190819375481120917420622822672549775783927716138318623895010788866272024264"), emulated.ValueOf[emulated.BN254Fp]("303847389135065887422783454877609941456349188919719272345083954437860409601")} - res := &E6{B0: *t0, B1: *t1, B2: *t2} - res = e.Ext6.MulBy0(res, &v0) - - return res -} - -// 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 follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen. -// -// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (e Ext12) AssertFinalExponentiationIsOne(x *E12) { - res, err := e.fp.NewHint(finalExpHint, 24, &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 - cubicNonResiduePower := E12{ - C0: 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]}, - }, - C1: (*e.Ext6.Zero()), - } - - // Check that x * cubicNonResiduePower == residueWitness^λ - // where λ = 6u + 2 + q^3 - q^2 + q, with u the BN254 seed - // and residueWitness, cubicNonResiduePower from the hint. - t2 := e.Mul(&cubicNonResiduePower, x) - - t1 := e.FrobeniusCube(&residueWitness) - t0 := e.FrobeniusSquare(&residueWitness) - t1 = e.DivUnchecked(t1, t0) - t0 = e.Frobenius(&residueWitness) - t1 = e.Mul(t1, t0) - - // exponentiation by U=6u+2 - t0 = e.ExpByU(&residueWitness) - - t0 = e.Mul(t0, t1) - - e.AssertIsEqual(t0, t2) -} - -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, - }, - } -} - -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}, - } -} - -func (e Ext12) FrobeniusCube(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.MulByNonResidue3Power2(t1) - t2 = e.Ext2.MulByNonResidue3Power4(t2) - t3 = e.Ext2.MulByNonResidue3Power1(t3) - t4 = e.Ext2.MulByNonResidue3Power3(t4) - t5 = e.Ext2.MulByNonResidue3Power5(t5) - return &E12{ - C0: E6{ - B0: *t0, - B1: *t1, - B2: *t2, - }, - C1: E6{ - B0: *t3, - B1: *t4, - B2: *t5, - }, - } -} diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index a3289b4698..9e5703504b 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -9,6 +9,33 @@ import ( "github.com/consensys/gnark/test" ) +type e12Convert struct { + A E12 +} + +func (circuit *e12Convert) Define(api frontend.API) error { + e := NewExt12(api) + expected := e.e12RoundTrip(&circuit.A) + e.AssertIsEqual(expected, &circuit.A) + return nil +} + +func TestConvertFp12(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a bn254.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,6 +129,7 @@ func TestMulFp12(t *testing.T) { } +/* type e12Div struct { A, B, C E12 } @@ -161,6 +189,7 @@ func TestSquareFp12(t *testing.T) { assert.NoError(err) } +*/ type e12Conjugate struct { A E12 @@ -192,6 +221,7 @@ func TestConjugateFp12(t *testing.T) { assert.NoError(err) } +/* type e12Inverse struct { A E12 C E12 `gnark:",public"` @@ -618,3 +648,4 @@ func TestTorusSquare(t *testing.T) { err := test.IsSolved(&torusSquare{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } +*/ diff --git a/std/algebra/emulated/fields_bn254/e6.go b/std/algebra/emulated/fields_bn254/e6.go deleted file mode 100644 index feca59a5f7..0000000000 --- a/std/algebra/emulated/fields_bn254/e6.go +++ /dev/null @@ -1,495 +0,0 @@ -package fields_bn254 - -import ( - "math/big" - - "github.com/consensys/gnark-crypto/ecc/bn254" - "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) MulByConstE2(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, - } -} - -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, - } -} - -// 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, - } -} - -// Mul01By01 multiplies two E6 sparse element of the form: -// -// E6{ -// B0: c0, -// B1: c1, -// B2: 0, -// } -// -// and -// -// E6{ -// B0: d0, -// B1: d1, -// B2: 0, -// } -func (e Ext6) Mul01By01(c0, c1, d0, d1 *E2) *E6 { - a := e.Ext2.Mul(d0, c0) - b := e.Ext2.Mul(d1, c1) - t1 := e.Ext2.Add(c0, c1) - tmp := e.Ext2.Add(d0, d1) - t1 = e.Ext2.Mul(t1, tmp) - tmp = e.Ext2.Add(a, b) - t1 = e.Ext2.Sub(t1, tmp) - return &E6{ - B0: *a, - B1: *t1, - B2: *b, - } -} - -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) FrobeniusSquare(x *E6) *E6 { - z01 := e.Ext2.MulByNonResidue2Power2(&x.B1) - z02 := e.Ext2.MulByNonResidue2Power4(&x.B2) - return &E6{B0: x.B0, B1: *z01, B2: *z02} -} - -func (e Ext6) IsEqual(x, y *E6) frontend.Variable { - isB0Equal := e.Ext2.IsEqual(&x.B0, &y.B0) - isB1Equal := e.Ext2.IsEqual(&x.B1, &y.B1) - isB2Equal := e.Ext2.IsEqual(&x.B2, &y.B2) - res := e.api.And(isB0Equal, isB1Equal) - res = e.api.And(res, isB2Equal) - return res -} - -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 *bn254.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_bn254/e6_test.go b/std/algebra/emulated/fields_bn254/e6_test.go deleted file mode 100644 index 4fbfcdea8b..0000000000 --- a/std/algebra/emulated/fields_bn254/e6_test.go +++ /dev/null @@ -1,444 +0,0 @@ -package fields_bn254 - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bn254" - "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 bn254.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 bn254.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 bn254.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 bn254.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 bn254.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 bn254.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 bn254.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 bn254.E6 - var b bn254.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 bn254.E6 - var C0, C1 bn254.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 e6Mul01By01 struct { - A0, A1 E2 - B0, B1 E2 - C E6 `gnark:",public"` -} - -func (circuit *e6Mul01By01) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Mul01By01(&circuit.A0, &circuit.A1, &circuit.B0, &circuit.B1) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestMul01By01(t *testing.T) { - - // we test our new E3.Mul01By01 against E3.MulBy01 - assert := test.NewAssert(t) - // witness values - var a, c bn254.E6 - var A0, A1, B0, B1 bn254.E2 - _, _ = A0.SetRandom() - _, _ = A1.SetRandom() - _, _ = B0.SetRandom() - _, _ = B1.SetRandom() - - // build a 01 sparse E3 with, - // first two elements as A1 and A2, - // and the third as 0 - a.B0 = A0 - a.B1 = A1 - a.B2.SetZero() - c.Set(&a) - c.MulBy01(&B0, &B1) - - witness := e6Mul01By01{ - A0: FromE2(&A0), - A1: FromE2(&A1), - B0: FromE2(&B0), - B1: FromE2(&B1), - C: FromE6(&c), - } - - err := test.IsSolved(&e6Mul01By01{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - -} - -type e6MulBy0 struct { - A E6 - C0 E2 - C E6 `gnark:",public"` -} - -func (circuit *e6MulBy0) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.MulBy0(&circuit.A, &circuit.C0) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestMulFp6By0(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bn254.E6 - var C0, zero bn254.E2 - _, _ = a.SetRandom() - _, _ = C0.SetRandom() - c.Set(&a) - c.MulBy01(&C0, &zero) - - witness := e6MulBy0{ - A: FromE6(&a), - C0: FromE2(&C0), - C: FromE6(&c), - } - - err := test.IsSolved(&e6MulBy0{}, &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 bn254.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 bn254.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/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 569fb72f93..e449301927 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -6,6 +6,7 @@ import ( "math/big" "github.com/consensys/gnark-crypto/ecc/bn254" + fp_bn "github.com/consensys/gnark-crypto/ecc/bn254/fp" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/emulated/fields_bn254" "github.com/consensys/gnark/std/algebra/emulated/sw_emulated" @@ -15,6 +16,7 @@ import ( type Pairing struct { api frontend.API *fields_bn254.Ext12 + *fields_bn254.Ext2 curveF *emulated.Field[BaseField] curve *sw_emulated.Curve[BaseField, ScalarField] g2 *G2 @@ -24,36 +26,34 @@ type Pairing struct { type GTEl = fields_bn254.E12 -func NewGTEl(v bn254.GT) GTEl { +func NewGTEl(a bn254.GT) GTEl { + var c0, c1, c2, c3, c4, c5, t fp_bn.Element + t.SetUint64(9).Mul(&t, &a.C0.B0.A1) + c0.Sub(&a.C0.B0.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B0.A1) + c1.Sub(&a.C1.B0.A0, &t) + t.SetUint64(9).Mul(&t, &a.C0.B1.A1) + c2.Sub(&a.C0.B1.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B1.A1) + c3.Sub(&a.C1.B1.A0, &t) + t.SetUint64(9).Mul(&t, &a.C0.B2.A1) + c4.Sub(&a.C0.B2.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B2.A1) + c5.Sub(&a.C1.B2.A0, &t) + return GTEl{ - C0: fields_bn254.E6{ - B0: fields_bn254.E2{ - A0: emulated.ValueOf[BaseField](v.C0.B0.A0), - A1: emulated.ValueOf[BaseField](v.C0.B0.A1), - }, - B1: fields_bn254.E2{ - A0: emulated.ValueOf[BaseField](v.C0.B1.A0), - A1: emulated.ValueOf[BaseField](v.C0.B1.A1), - }, - B2: fields_bn254.E2{ - A0: emulated.ValueOf[BaseField](v.C0.B2.A0), - A1: emulated.ValueOf[BaseField](v.C0.B2.A1), - }, - }, - C1: fields_bn254.E6{ - B0: fields_bn254.E2{ - A0: emulated.ValueOf[BaseField](v.C1.B0.A0), - A1: emulated.ValueOf[BaseField](v.C1.B0.A1), - }, - B1: fields_bn254.E2{ - A0: emulated.ValueOf[BaseField](v.C1.B1.A0), - A1: emulated.ValueOf[BaseField](v.C1.B1.A1), - }, - B2: fields_bn254.E2{ - A0: emulated.ValueOf[BaseField](v.C1.B2.A0), - A1: emulated.ValueOf[BaseField](v.C1.B2.A1), - }, - }, + A0: emulated.ValueOf[emulated.BN254Fp](c0), + A1: emulated.ValueOf[emulated.BN254Fp](c1), + A2: emulated.ValueOf[emulated.BN254Fp](c2), + A3: emulated.ValueOf[emulated.BN254Fp](c3), + A4: emulated.ValueOf[emulated.BN254Fp](c4), + A5: emulated.ValueOf[emulated.BN254Fp](c5), + A6: emulated.ValueOf[emulated.BN254Fp](a.C0.B0.A1), + A7: emulated.ValueOf[emulated.BN254Fp](a.C1.B0.A1), + A8: emulated.ValueOf[emulated.BN254Fp](a.C0.B1.A1), + A9: emulated.ValueOf[emulated.BN254Fp](a.C1.B1.A1), + A10: emulated.ValueOf[emulated.BN254Fp](a.C0.B2.A1), + A11: emulated.ValueOf[emulated.BN254Fp](a.C1.B2.A1), } } @@ -89,154 +89,6 @@ func (pr Pairing) generators() *G2Affine { return pr.g2gen } -// FinalExponentiation computes the exponentiation eᵈ where -// -// d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r. -// -// We use instead d'= s ⋅ d, where s is the cofactor -// -// 2x₀(6x₀²+3x₀+1) -// -// and r does NOT divide d' -// -// 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 eᵈ where -// -// d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r. -// -// We use instead d'= s ⋅ d, where s is the cofactor -// -// 2x₀(6x₀²+3x₀+1) -// -// and r does NOT divide d' -// -// 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 eᵈ where -// -// d = (p¹²-1)/r = (p¹²-1)/Φ₁₂(p) ⋅ Φ₁₂(p)/r = (p⁶-1)(p²+1)(p⁴ - p² +1)/r. -// -// We use instead d'= s ⋅ d, where s is the cofactor -// -// 2x₀(6x₀²+3x₀+1) -// -// and r does NOT divide d' -// -// 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) - // 2x₀(6x₀²+3x₀+1)(p⁴-p²+1)/r - // Duquesne and Ghammam - // https://eprint.iacr.org/2015/192.pdf - // Fuentes et al. (alg. 6) - // performed in torus compressed form - t0 = pr.ExptTorus(c) - t0 = pr.InverseTorus(t0) - t0 = pr.SquareTorus(t0) - t1 := pr.SquareTorus(t0) - t1 = pr.MulTorus(t0, t1) - t2 := pr.ExptTorus(t1) - t2 = pr.InverseTorus(t2) - t3 := pr.InverseTorus(t1) - t1 = pr.MulTorus(t2, t3) - t3 = pr.SquareTorus(t2) - t4 := pr.ExptTorus(t3) - t4 = pr.MulTorus(t1, t4) - t3 = pr.MulTorus(t0, t4) - t0 = pr.MulTorus(t2, t4) - t0 = pr.MulTorus(c, t0) - t2 = pr.FrobeniusTorus(t3) - t0 = pr.MulTorus(t2, t0) - t2 = pr.FrobeniusSquareTorus(t4) - t0 = pr.MulTorus(t2, t0) - t2 = pr.InverseTorus(c) - t2 = pr.MulTorus(t2, t3) - t2 = pr.FrobeniusCubeTorus(t2) - - var result GTEl - // MulTorus(t0, t2) requires t0 ≠ -t2. When t0 = -t2, 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(t2, t0)) - } else { - // For a product of pairings this might happen when the result is expected to be 1. - // We assign a dummy value (1) to t0 and proceed further. - // Finally we do a select on both edge cases: - // - Only if seletor1=0 and selector2=0, we return MulTorus(t2, t0) decompressed. - // - Otherwise, we return 1. - _sum := pr.Ext6.Add(t0, t2) - selector2 = pr.Ext6.IsZero(_sum) - t0 = pr.Ext6.Select(selector2, _dummy, t0) - selector := pr.api.Mul(pr.api.Sub(1, selector1), pr.api.Sub(1, selector2)) - result = *pr.Select(selector, pr.DecompressTorus(pr.MulTorus(t2, t0)), pr.One()) - } - - return &result -} - -// Pair calculates the reduced pairing for a set of points -// ∏ᵢ e(Pᵢ, Qᵢ). -// -// This function doesn't check that the inputs are in the correct subgroups. See AssertIsOnG1 and AssertIsOnG2. -func (pr Pairing) Pair(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { - res, err := pr.MillerLoop(P, Q) - if err != nil { - return nil, fmt.Errorf("miller loop: %w", err) - } - res = pr.finalExponentiation(res, len(P) == 1) - return res, nil -} - // PairingCheck calculates the reduced pairing for a set of points and asserts if the result is One // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 // @@ -252,10 +104,10 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { // 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) + buf := pr.Ext12.Conjugate(f) + buf = pr.Ext12.DivUnchecked(buf, f) + f = pr.Ext12.FrobeniusSquare(buf) + f = pr.Ext12.Mul(f, buf) pr.AssertFinalExponentiationIsOne(f) From 698d114206cc09ab6a10f4a3942cc83edc6a36d9 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Dec 2024 19:25:23 -0500 Subject: [PATCH 03/22] feat: SZ direct Fp12 sq, inv, dic --- std/algebra/emulated/fields_bn254/e12.go | 94 ++++++++- std/algebra/emulated/fields_bn254/e12_test.go | 81 ++++--- std/algebra/emulated/fields_bn254/hints.go | 198 +++++++++++++----- 3 files changed, 275 insertions(+), 98 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 98b6b65a49..3616c7e4b9 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -400,6 +400,99 @@ func (e Ext12) mulDirect(a, b *E12) *E12 { A11: *d11, } } +func (e Ext12) Square(x *E12) *E12 { + return e.squareDirect(x) +} + +func (e Ext12) squareDirect(a *E12) *E12 { + + mone := e.fp.NewElement(-1) + // d0 = a0 a0 - 82 * (2 a1 a11 + 2 a2 a10 + 2 a3 a9 + 2 a4 a8 + 2 a5 a7 + a6 a6) - 1476 * (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, 164, 164, 164, 164, 164, 82, 2952, 2952, 1476}) + + // d1 = 2 a0 a1 - 164 * (2 a2 a11 + a3 a10 + a4 a9 + a5 a8 + a6 a7) - 2952 * (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, 164, 164, 164, 164, 164, 2952, 2952}) + + // d2 = 2 a0 a2 + a1 a1 - 82 * (2 a3 a11 + 2 a4 a10 + 2 a5 a9 + 2 a6 a8 + a7 a7) - 1476 * (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, 164, 164, 164, 164, 82, 2952, 1476}) + + // d3 = 2 a0 a3 + 2 a1 a2 - 164 * (a4 a11 + a5 a10 + a6 a9 + a7 a8) - 2952 * 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, 164, 164, 164, 164, 2952}) + + // d4 = 2 a0 a4 + 2 a1 a3 + a2 a2 - 82 * (2 a5 a11 + 2 a6 a10 + 2 a7 a9 + a8 a8) - 1476 * 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, 164, 164, 164, 82, 1476}) + + // d5 = 2 (a0 a5 + a1 a4 + a2 a3) - 164 * (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, 164, 164, 164}) + + // d6 = 2 a0 a6 + 2 a1 a5 + 2 a2 a4 + a3 a3 + 18 * (2 a1 a11 + 2 a2 a10 + 2 a3 a9 + 2 a4 a8 + 2 a5 a7 + a6 a6) + 242 * (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, 36, 36, 36, 36, 36, 18, 484, 484, 242}) + + // d7 = 2(a0 a7 + a1 a6 + a2 a5 + a3 a4) + 36 * (a2 a11 + a3 a10 + a4 a9 + a5 a8 + a6 a7) + 484 * (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, 36, 36, 36, 36, 36, 484, 484}) + + // d8 = 2(a0 a8 + a1 a7 + a2 a6 + a3 a5) + a4 a4 + 18 * (2 a3 a11 + 2 a4 a10 + 2 a5 a9 + 2 a6 a8 + a7 a7) + 242 * (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, 36, 36, 36, 36, 18, 484, 242}) + + // d9 = 2(a0 a9 + a1 a8 + a2 a7 + a3 a6 + a4 a5) + 36 * (a4 a11 + a5 a10 + a6 a9 + a7 a8) + 484 * 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, 36, 36, 36, 36, 484}) + + // d10 = 2(a0 a10 + a1 a9 + a2 a8 + a3 a7 + a4 a6) + a5 a5 + 18 * (2 a5 a11 + 2 a6 a10 + 2 a7 a9 + a8 a8) + 242 * 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, 36, 36, 36, 18, 242}) + + // d11 = 2(a0 a11 + a1 a10 + a2 a9 + a3 a8 + a4 a7 + a5 a6) + 36 * (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, 36, 36, 36}) + + 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.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{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 + _one := e.Mul(&inv, x) + e.AssertIsEqual(one, _one) + + return &inv + +} + +func (e Ext12) DivUnchecked(x, y *E12) *E12 { + 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{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 := e.Mul(&div, y) + e.AssertIsEqual(x, _x) + + return &div + +} func (e Ext12) Select(selector frontend.Variable, z1, z0 *E12) *E12 { a0 := e.fp.Select(selector, &z1.A0, &z0.A0) @@ -578,7 +671,6 @@ func FromE12(a *bn254.E12) E12 { } } -// direct to tower extension conversion func (e Ext12) e12RoundTrip(a *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: diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index 9e5703504b..98b4c5e253 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -129,7 +129,6 @@ func TestMulFp12(t *testing.T) { } -/* type e12Div struct { A, B, C E12 } @@ -189,7 +188,6 @@ func TestSquareFp12(t *testing.T) { assert.NoError(err) } -*/ type e12Conjugate struct { A E12 @@ -221,7 +219,6 @@ func TestConjugateFp12(t *testing.T) { assert.NoError(err) } -/* type e12Inverse struct { A E12 C E12 `gnark:",public"` @@ -252,6 +249,46 @@ func TestInverseFp12(t *testing.T) { assert.NoError(err) } +/* +type e12MulBy034 struct { + A E12 `gnark:",public"` + W E12 + B, C E2 +} + +func (circuit *e12MulBy034) Define(api frontend.API) error { + e := NewExt12(api) + res := e.MulBy034(&circuit.A, &circuit.B, &circuit.C) + e.AssertIsEqual(res, &circuit.W) + return nil +} + +func TestFp12MulBy034(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, w bn254.E12 + _, _ = a.SetRandom() + var one, b, c bn254.E2 + one.SetOne() + _, _ = b.SetRandom() + _, _ = c.SetRandom() + w.Set(&a) + w.MulBy034(&one, &b, &c) + + witness := e12MulBy034{ + A: FromE12(&a), + B: FromE2(&b), + C: FromE2(&c), + W: FromE12(&w), + } + + err := test.IsSolved(&e12MulBy034{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + +} + +// Torus-based arithmetic type e12ExptTorus struct { A E6 C E12 `gnark:",public"` @@ -291,45 +328,7 @@ func TestFp12ExptTorus(t *testing.T) { assert.NoError(err) } -type e12MulBy034 struct { - A E12 `gnark:",public"` - W E12 - B, C E2 -} - -func (circuit *e12MulBy034) Define(api frontend.API) error { - e := NewExt12(api) - res := e.MulBy034(&circuit.A, &circuit.B, &circuit.C) - e.AssertIsEqual(res, &circuit.W) - return nil -} - -func TestFp12MulBy034(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, w bn254.E12 - _, _ = a.SetRandom() - var one, b, c bn254.E2 - one.SetOne() - _, _ = b.SetRandom() - _, _ = c.SetRandom() - w.Set(&a) - w.MulBy034(&one, &b, &c) - - witness := e12MulBy034{ - A: FromE12(&a), - B: FromE2(&b), - C: FromE2(&c), - W: FromE12(&w), - } - - err := test.IsSolved(&e12MulBy034{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -// Torus-based arithmetic type torusCompress struct { A E12 C E6 `gnark:",public"` diff --git a/std/algebra/emulated/fields_bn254/hints.go b/std/algebra/emulated/fields_bn254/hints.go index c5d68ed67d..63426e379e 100644 --- a/std/algebra/emulated/fields_bn254/hints.go +++ b/std/algebra/emulated/fields_bn254/hints.go @@ -5,6 +5,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254" "github.com/consensys/gnark-crypto/ecc/bn254/fp" + fp_bn "github.com/consensys/gnark-crypto/ecc/bn254/fp" "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/std/math/emulated" ) @@ -186,34 +187,67 @@ func divE6By6Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er 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 d [12]big.Int + var t1 big.Int + t1.SetUint64(9).Mul(&t1, inputs[6]) + d[0].Add(inputs[0], &t1) + d[1].Set(inputs[6]) + t1.SetUint64(9).Mul(&t1, inputs[8]) + d[2].Add(inputs[2], &t1) + d[3].Set(inputs[8]) + t1.SetUint64(9).Mul(&t1, inputs[10]) + d[4].Add(inputs[4], &t1) + d[5].Set(inputs[10]) + t1.SetUint64(9).Mul(&t1, inputs[7]) + d[6].Add(inputs[1], &t1) + d[7].Set(inputs[7]) + t1.SetUint64(9).Mul(&t1, inputs[9]) + d[8].Add(inputs[3], &t1) + d[9].Set(inputs[9]) + t1.SetUint64(9).Mul(&t1, inputs[11]) + d[10].Add(inputs[5], &t1) + d[11].Set(inputs[11]) var a, c bn254.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]) + 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, t2 fp_bn.Element + t2.SetUint64(9).Mul(&t2, &c.C0.B0.A1) + c0.Sub(&c.C0.B0.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C1.B0.A1) + c1.Sub(&c.C1.B0.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C0.B1.A1) + c2.Sub(&c.C0.B1.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C1.B1.A1) + c3.Sub(&c.C1.B1.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C0.B2.A1) + c4.Sub(&c.C0.B2.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C1.B2.A1) + c5.Sub(&c.C1.B2.A0, &t2) + + 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,45 +259,97 @@ func divE12Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) erro func(mod *big.Int, inputs, outputs []*big.Int) error { var a, b, c bn254.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 + var t1 big.Int + t1.SetUint64(9).Mul(&t1, inputs[6]) + d[0].Add(inputs[0], &t1) + d[1].Set(inputs[6]) + t1.SetUint64(9).Mul(&t1, inputs[8]) + d[2].Add(inputs[2], &t1) + d[3].Set(inputs[8]) + t1.SetUint64(9).Mul(&t1, inputs[10]) + d[4].Add(inputs[4], &t1) + d[5].Set(inputs[10]) + t1.SetUint64(9).Mul(&t1, inputs[7]) + d[6].Add(inputs[1], &t1) + d[7].Set(inputs[7]) + t1.SetUint64(9).Mul(&t1, inputs[9]) + d[8].Add(inputs[3], &t1) + d[9].Set(inputs[9]) + t1.SetUint64(9).Mul(&t1, inputs[11]) + d[10].Add(inputs[5], &t1) + 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]) + + t1.SetUint64(9).Mul(&t1, inputs[18]) + d[0].Add(inputs[12], &t1) + d[1].Set(inputs[18]) + t1.SetUint64(9).Mul(&t1, inputs[20]) + d[2].Add(inputs[14], &t1) + d[3].Set(inputs[20]) + t1.SetUint64(9).Mul(&t1, inputs[22]) + d[4].Add(inputs[16], &t1) + d[5].Set(inputs[22]) + t1.SetUint64(9).Mul(&t1, inputs[19]) + d[6].Add(inputs[13], &t1) + d[7].Set(inputs[19]) + t1.SetUint64(9).Mul(&t1, inputs[21]) + d[8].Add(inputs[15], &t1) + d[9].Set(inputs[21]) + t1.SetUint64(9).Mul(&t1, inputs[23]) + d[10].Add(inputs[17], &t1) + 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, t2 fp_bn.Element + t2.SetUint64(9).Mul(&t2, &c.C0.B0.A1) + c0.Sub(&c.C0.B0.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C1.B0.A1) + c1.Sub(&c.C1.B0.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C0.B1.A1) + c2.Sub(&c.C0.B1.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C1.B1.A1) + c3.Sub(&c.C1.B1.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C0.B2.A1) + c4.Sub(&c.C0.B2.A0, &t2) + t2.SetUint64(9).Mul(&t2, &c.C1.B2.A1) + c5.Sub(&c.C1.B2.A0, &t2) + + 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 From f951982b47cdba6613a08987f85f9bb36fb21626 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Dec 2024 20:35:17 -0500 Subject: [PATCH 04/22] feat: SZ direct Fp12 frobenius 1,2,3 --- std/algebra/emulated/fields_bn254/e12.go | 138 +++++++++++++++++- std/algebra/emulated/fields_bn254/e12_test.go | 90 ++++++++++++ 2 files changed, 226 insertions(+), 2 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 3616c7e4b9..0ac911e8b7 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -14,6 +14,7 @@ type E12 struct { } type Ext12 struct { + *Ext2 api frontend.API fp *curveF } @@ -24,8 +25,9 @@ func NewExt12(api frontend.API) *Ext12 { panic(err) } return &Ext12{ - api: api, - fp: fp, + Ext2: NewExt2(api), + api: api, + fp: fp, } } @@ -743,3 +745,135 @@ func (e Ext12) e12RoundTrip(a *E12) *E12 { A11: A11, } } + +func (e Ext12) Frobenius(a *E12) *E12 { + nine := big.NewInt(9) + a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) + a001 := e.fp.Neg(&a.A6) + a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) + a011 := e.fp.Neg(&a.A8) + a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) + a021 := e.fp.Neg(&a.A10) + a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) + a101 := e.fp.Neg(&a.A7) + a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) + a111 := e.fp.Neg(&a.A9) + a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) + a121 := e.fp.Neg(&a.A11) + + t1 := e.Ext2.MulByNonResidue1Power2(&E2{A0: *a010, A1: *a011}) + t2 := e.Ext2.MulByNonResidue1Power4(&E2{A0: *a020, A1: *a021}) + t3 := e.Ext2.MulByNonResidue1Power1(&E2{A0: *a100, A1: *a101}) + t4 := e.Ext2.MulByNonResidue1Power3(&E2{A0: *a110, A1: *a111}) + t5 := e.Ext2.MulByNonResidue1Power5(&E2{A0: *a120, A1: *a121}) + + A0 := e.fp.Sub(a000, e.fp.MulConst(a001, nine)) + A1 := e.fp.Sub(&t3.A0, e.fp.MulConst(&t3.A1, nine)) + A2 := e.fp.Sub(&t1.A0, e.fp.MulConst(&t1.A1, nine)) + A3 := e.fp.Sub(&t4.A0, e.fp.MulConst(&t4.A1, nine)) + A4 := e.fp.Sub(&t2.A0, e.fp.MulConst(&t2.A1, nine)) + A5 := e.fp.Sub(&t5.A0, e.fp.MulConst(&t5.A1, nine)) + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *a001, + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + A11: t5.A1, + } +} + +func (e Ext12) FrobeniusSquare(a *E12) *E12 { + nine := big.NewInt(9) + a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) + a001 := &a.A6 + a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) + a011 := &a.A8 + a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) + a021 := &a.A10 + a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) + a101 := &a.A7 + a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) + a111 := &a.A9 + a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) + a121 := &a.A11 + + t1 := e.Ext2.MulByNonResidue2Power2(&E2{A0: *a010, A1: *a011}) + t2 := e.Ext2.MulByNonResidue2Power4(&E2{A0: *a020, A1: *a021}) + t3 := e.Ext2.MulByNonResidue2Power1(&E2{A0: *a100, A1: *a101}) + t4 := e.Ext2.MulByNonResidue2Power3(&E2{A0: *a110, A1: *a111}) + t5 := e.Ext2.MulByNonResidue2Power5(&E2{A0: *a120, A1: *a121}) + + A0 := e.fp.Sub(a000, e.fp.MulConst(a001, nine)) + A1 := e.fp.Sub(&t3.A0, e.fp.MulConst(&t3.A1, nine)) + A2 := e.fp.Sub(&t1.A0, e.fp.MulConst(&t1.A1, nine)) + A3 := e.fp.Sub(&t4.A0, e.fp.MulConst(&t4.A1, nine)) + A4 := e.fp.Sub(&t2.A0, e.fp.MulConst(&t2.A1, nine)) + A5 := e.fp.Sub(&t5.A0, e.fp.MulConst(&t5.A1, nine)) + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *a001, + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + A11: t5.A1, + } +} + +func (e Ext12) FrobeniusCube(a *E12) *E12 { + nine := big.NewInt(9) + a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) + a001 := e.fp.Neg(&a.A6) + a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) + a011 := e.fp.Neg(&a.A8) + a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) + a021 := e.fp.Neg(&a.A10) + a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) + a101 := e.fp.Neg(&a.A7) + a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) + a111 := e.fp.Neg(&a.A9) + a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) + a121 := e.fp.Neg(&a.A11) + + t1 := e.Ext2.MulByNonResidue3Power2(&E2{A0: *a010, A1: *a011}) + t2 := e.Ext2.MulByNonResidue3Power4(&E2{A0: *a020, A1: *a021}) + t3 := e.Ext2.MulByNonResidue3Power1(&E2{A0: *a100, A1: *a101}) + t4 := e.Ext2.MulByNonResidue3Power3(&E2{A0: *a110, A1: *a111}) + t5 := e.Ext2.MulByNonResidue3Power5(&E2{A0: *a120, A1: *a121}) + + A0 := e.fp.Sub(a000, e.fp.MulConst(a001, nine)) + A1 := e.fp.Sub(&t3.A0, e.fp.MulConst(&t3.A1, nine)) + A2 := e.fp.Sub(&t1.A0, e.fp.MulConst(&t1.A1, nine)) + A3 := e.fp.Sub(&t4.A0, e.fp.MulConst(&t4.A1, nine)) + A4 := e.fp.Sub(&t2.A0, e.fp.MulConst(&t2.A1, nine)) + A5 := e.fp.Sub(&t5.A0, e.fp.MulConst(&t5.A1, nine)) + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *a001, + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + A11: t5.A1, + } +} diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index 98b4c5e253..bc1655c15d 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -249,6 +249,96 @@ func TestInverseFp12(t *testing.T) { assert.NoError(err) } +type Frobenius struct { + A E12 + C E12 `gnark:",public"` +} + +func (circuit *Frobenius) Define(api frontend.API) error { + e := NewExt12(api) + expected := e.Frobenius(&circuit.A) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestFrobenius(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, c bn254.E12 + _, _ = a.SetRandom() + + c.Frobenius(&a) + + witness := Frobenius{ + A: FromE12(&a), + C: FromE12(&c), + } + + err := test.IsSolved(&Frobenius{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type FrobeniusSquare struct { + A E12 + C E12 `gnark:",public"` +} + +func (circuit *FrobeniusSquare) Define(api frontend.API) error { + e := NewExt12(api) + expected := e.FrobeniusSquare(&circuit.A) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestFrobeniusSquare(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, c bn254.E12 + _, _ = a.SetRandom() + + c.FrobeniusSquare(&a) + + witness := FrobeniusSquare{ + A: FromE12(&a), + C: FromE12(&c), + } + + err := test.IsSolved(&FrobeniusSquare{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type FrobeniusCube struct { + A E12 + C E12 `gnark:",public"` +} + +func (circuit *FrobeniusCube) Define(api frontend.API) error { + e := NewExt12(api) + expected := e.FrobeniusCube(&circuit.A) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestFrobeniusCube(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, c bn254.E12 + _, _ = a.SetRandom() + + c.FrobeniusCube(&a) + + witness := FrobeniusCube{ + A: FromE12(&a), + C: FromE12(&c), + } + + err := test.IsSolved(&FrobeniusCube{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + /* type e12MulBy034 struct { A E12 `gnark:",public"` From f3c25c5fae9aea192941b070f8c793bd37fda0aa Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sat, 7 Dec 2024 02:27:40 -0500 Subject: [PATCH 05/22] feat: add some methods related to pairing using Eval --- std/algebra/emulated/fields_bn254/e12.go | 7 + .../emulated/fields_bn254/e12_pairing.go | 251 ++++++++++++ std/algebra/emulated/fields_bn254/e12_test.go | 374 +----------------- std/algebra/emulated/sw_bn254/doc_test.go | 93 ----- std/algebra/emulated/sw_bn254/pairing.go | 291 ++------------ std/algebra/emulated/sw_bn254/pairing_test.go | 253 ------------ 6 files changed, 288 insertions(+), 981 deletions(-) create mode 100644 std/algebra/emulated/fields_bn254/e12_pairing.go delete mode 100644 std/algebra/emulated/sw_bn254/doc_test.go diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 0ac911e8b7..1af7b9fe34 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -461,6 +461,13 @@ func (e Ext12) squareDirect(a *E12) *E12 { } } +// Granger-Scott's cyclotomic square +// https://eprint.iacr.org/2009/565.pdf, 3.2 +func (e Ext12) CyclotomicSquareGS(x *E12) *E12 { + // TODO: implement GS + return e.squareDirect(x) +} + func (e Ext12) Inverse(x *E12) *E12 { 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 { diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go new file mode 100644 index 0000000000..f9853c4c23 --- /dev/null +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -0,0 +1,251 @@ +package fields_bn254 + +import ( + "math/big" +) + +func (e Ext12) nSquareGS(z *E12, n int) *E12 { + for i := 0; i < n; i++ { + z = e.CyclotomicSquareGS(z) + } + return z +} + +// Exponentiation by U=6u+2 where t is the seed u=4965661367192848881 +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 + // _1100 = 2*_110 + // _1111 = _11 + _1100 + // _1100000 = _1100 << 3 + // _1100111 = _111 + _1100000 + // i22 = ((_1100111 << 2 + 1) << 5 + _1111) << 3 + // i38 = ((1 + i22) << 4 + _111) << 9 + _111 + // i50 = 2*((i38 << 4 + _11) << 5 + _1111) + // i61 = ((1 + i50) << 5 + _111) << 3 + _11 + // i75 = ((i61 << 6 + _111) << 4 + _111) << 2 + // return ((1 + i75) << 2 + 1) << 3 + // + // Operations: 64 squares 18 multiplies + // + // Generated by github.com/mmcloughlin/addchain v0.4.0. + + z := e.Square(x) + t0 := e.Mul(x, z) + t1 := e.Square(t0) + z = e.Mul(x, t1) + t2 := e.Square(t1) + t1 = e.Mul(t0, t2) + t2 = e.nSquareGS(t2, 3) + t2 = e.Mul(z, t2) + t2 = e.nSquareGS(t2, 2) + t2 = e.Mul(x, t2) + t2 = e.nSquareGS(t2, 5) + t2 = e.Mul(t1, t2) + t2 = e.nSquareGS(t2, 3) + t2 = e.Mul(x, t2) + t2 = e.nSquareGS(t2, 4) + t2 = e.Mul(z, t2) + t2 = e.nSquareGS(t2, 9) + t2 = e.Mul(z, t2) + t2 = e.nSquareGS(t2, 4) + t2 = e.Mul(t0, t2) + t2 = e.nSquareGS(t2, 5) + t1 = e.Mul(t1, t2) + t1 = e.Square(t1) + t1 = e.Mul(x, t1) + t1 = e.nSquareGS(t1, 5) + t1 = e.Mul(z, t1) + t1 = e.nSquareGS(t1, 3) + t0 = e.Mul(t0, t1) + t0 = e.nSquareGS(t0, 6) + t0 = e.Mul(z, t0) + t0 = e.nSquareGS(t0, 4) + z = e.Mul(z, t0) + z = e.nSquareGS(z, 2) + z = e.Mul(x, z) + z = e.nSquareGS(z, 2) + z = e.Mul(x, z) + z = e.nSquareGS(z, 3) + + return z +} + +// MulBy01379 multiplies a by an E12 sparse element b of the form +// +// b.A0 = 1 +// b.A1 = c3.A0 - 9 * c3.A1 +// b.A2 = 0 +// b.A3 = c4.A0 - 9 * c4.A1 +// b.A4 = 0 +// b.A5 = 0 +// b.A6 = 0 +// b.A7 = c3.A1 +// b.A8 = 0 +// b.A9 = c4.A1 +// b.A10 = 0 +// b.A11 = 0 +func (e *Ext12) MulBy01379(a *E12, c3, c4 *E2) *E12 { + nine := big.NewInt(9) + b1 := e.fp.Sub(&c3.A0, e.fp.MulConst(&c3.A1, nine)) + b3 := e.fp.Sub(&c4.A0, e.fp.MulConst(&c4.A1, nine)) + b7 := &c3.A1 + b9 := &c4.A1 + // d0 = a0 - 82 * (a3 b9 + a5 b7 + a9 b3 + a11 b1) - 1476 * (a9 b9 + a11 b7) + mone := e.fp.NewElement(-1) + d0 := e.fp.Eval([][]*baseEl{{&a.A0}, {mone, &a.A3, b9}, {mone, &a.A5, b7}, {mone, &a.A9, b3}, {mone, &a.A11, b1}, {mone, &a.A9, b9}, {mone, &a.A11, b7}}, []int{1, 82, 82, 82, 82, 1476, 1476}) + + // d1 = a0 b1 + a1 - 82 * (a4 b9 + a10 b3 + a6 b7) - 1476 * a10 b9 + d1 := e.fp.Eval([][]*baseEl{{&a.A0, b1}, {&a.A1}, {mone, &a.A4, b9}, {mone, &a.A10, b3}, {mone, &a.A6, b7}, {mone, &a.A10, b9}}, []int{1, 1, 82, 82, 82, 1476}) + + // d2 = a1 b1 + a2 - 82 * (a5 b9 + a11 b3 + a7 b7) - 1476 * a11 b9 + d2 := e.fp.Eval([][]*baseEl{{&a.A1, b1}, {&a.A2}, {mone, &a.A5, b9}, {mone, &a.A11, b3}, {mone, &a.A7, b7}, {mone, &a.A11, b9}}, []int{1, 1, 82, 82, 82, 1476}) + + // d3 = a0 b3 + a2 b1 + a3 - 82 * (a6 b9 + a8 b7) + d3 := e.fp.Eval([][]*baseEl{{&a.A0, b3}, {&a.A2, b1}, {&a.A3}, {mone, &a.A6, b9}, {mone, &a.A8, b7}}, []int{1, 1, 1, 82, 82}) + + // d4 = a1 b3 + a3 b1 + a4 - 82 * (a7 b9 + a9 b7) + d4 := e.fp.Eval([][]*baseEl{{&a.A1, b3}, {&a.A3, b1}, {&a.A4}, {mone, &a.A7, b9}, {mone, &a.A9, b7}}, []int{1, 1, 1, 82, 82}) + + // d5 = a2 b3 + a4 b1 + a5 - 82 * (a8 b9 + a10 b7) + d5 := e.fp.Eval([][]*baseEl{{&a.A2, b3}, {&a.A4, b1}, {&a.A5}, {mone, &a.A8, b9}, {mone, &a.A10, b7}}, []int{1, 1, 1, 82, 82}) + + // d6 = a3 b3 + a5 b1 + a6 + 18 * (a3 b9 + a9 b3 + a11 b1 + a5 b7) + 242 * (a9 b9 + a11 b7) + d6 := e.fp.Eval([][]*baseEl{{&a.A3, b3}, {&a.A5, b1}, {&a.A6}, {&a.A3, b9}, {&a.A9, b3}, {&a.A11, b1}, {&a.A5, b7}, {&a.A11, b7}, {&a.A9, b9}}, []int{1, 1, 1, 18, 18, 18, 18, 242, 242}) + + // d7 = a0 b7 + a4 b3 + a6 b1 + a7 + 18 * (a4 b9 + a10 b3 + a6 b7) + 242 * a10 b9 + d7 := e.fp.Eval([][]*baseEl{{&a.A0, b7}, {&a.A4, b3}, {&a.A6, b1}, {&a.A7}, {&a.A4, b9}, {&a.A10, b3}, {&a.A6, b7}, {&a.A10, b9}}, []int{1, 1, 1, 1, 18, 18, 18, 242}) + + // d8 = a1 b7 + a5 b3 + a7 b1 + a8 + 18 * (a5 b9 + a11 b3 + a7 b7) + 242 * a11 b9 + d8 := e.fp.Eval([][]*baseEl{{&a.A1, b7}, {&a.A5, b3}, {&a.A7, b1}, {&a.A8}, {&a.A5, b9}, {&a.A11, b3}, {&a.A7, b7}, {&a.A11, b9}}, []int{1, 1, 1, 1, 18, 18, 18, 242}) + + // d9 = a2 b7 + a0 b9 + a6 b3 + a8 b1 + a9 + 18 * (a6 b9 + a8 b7) + d9 := e.fp.Eval([][]*baseEl{{&a.A2, b7}, {&a.A0, b9}, {&a.A6, b3}, {&a.A8, b1}, {&a.A9}, {&a.A6, b9}, {&a.A8, b7}}, []int{1, 1, 1, 1, 1, 18, 18}) + + // d10 = a3 b7 + a1 b9 + a7 b3 + a9 b1 + a10 + 18 * (a7 b9 + a9 b7) + d10 := e.fp.Eval([][]*baseEl{{&a.A3, b7}, {&a.A1, b9}, {&a.A7, b3}, {&a.A9, b1}, {&a.A10}, {&a.A7, b9}, {&a.A9, b7}}, []int{1, 1, 1, 1, 1, 18, 18}) + + // d11 = a4 b7 + a2 b9 + a8 b3 + a10 b1 + a11 + 18 * (a8 b9 + a10 b7) + d11 := e.fp.Eval([][]*baseEl{{&a.A4, b7}, {&a.A2, b9}, {&a.A8, b3}, {&a.A10, b1}, {&a.A11}, {&a.A8, b9}, {&a.A10, b7}}, []int{1, 1, 1, 1, 1, 18, 18}) + + 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, + } +} + +// 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 follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen. +// +// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf +func (e Ext12) AssertFinalExponentiationIsOne(a *E12) { + nine := big.NewInt(9) + a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) + a001 := &a.A6 + a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) + a011 := &a.A8 + a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) + a021 := &a.A10 + a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) + a101 := &a.A7 + a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) + a111 := &a.A9 + a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) + a121 := &a.A11 + + res, err := e.fp.NewHint(finalExpHint, 24, a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + + A0 := e.fp.Sub(res[0], e.fp.MulConst(res[1], nine)) + A1 := e.fp.Sub(res[6], e.fp.MulConst(res[7], nine)) + A2 := e.fp.Sub(res[2], e.fp.MulConst(res[3], nine)) + A3 := e.fp.Sub(res[8], e.fp.MulConst(res[9], nine)) + A4 := e.fp.Sub(res[4], e.fp.MulConst(res[5], nine)) + A5 := e.fp.Sub(res[10], e.fp.MulConst(res[11], nine)) + A6 := res[1] + A7 := res[7] + A8 := res[3] + A9 := res[9] + A10 := res[5] + A11 := res[11] + + residueWitness := E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *A6, + A7: *A7, + A8: *A8, + A9: *A9, + A10: *A10, + A11: *A11, + } + + A0 = e.fp.Sub(res[12+0], e.fp.MulConst(res[12+1], nine)) + A1 = e.fp.Sub(res[12+6], e.fp.MulConst(res[12+7], nine)) + A2 = e.fp.Sub(res[12+2], e.fp.MulConst(res[12+3], nine)) + A3 = e.fp.Sub(res[12+8], e.fp.MulConst(res[12+9], nine)) + A4 = e.fp.Sub(res[12+4], e.fp.MulConst(res[12+5], nine)) + A5 = e.fp.Sub(res[12+10], e.fp.MulConst(res[12+11], nine)) + A6 = e.fp.Zero() + A7 = e.fp.Zero() + A8 = e.fp.Zero() + A9 = e.fp.Zero() + A10 = e.fp.Zero() + A11 = e.fp.Zero() + + // constrain cubicNonResiduePower to be in Fp6 + cubicNonResiduePower := E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *A6, + A7: *A7, + A8: *A8, + A9: *A9, + A10: *A10, + A11: *A11, + } + + // Check that x * cubicNonResiduePower == residueWitness^λ + // where λ = 6u + 2 + q^3 - q^2 + q, with u the BN254 seed + // and residueWitness, cubicNonResiduePower from the hint. + t2 := e.Mul(&cubicNonResiduePower, a) + + t1 := e.FrobeniusCube(&residueWitness) + t0 := e.FrobeniusSquare(&residueWitness) + t1 = e.DivUnchecked(t1, t0) + t0 = e.Frobenius(&residueWitness) + t1 = e.Mul(t1, t0) + + // exponentiation by U=6u+2 + t0 = e.ExpByU(&residueWitness) + + t0 = e.Mul(t0, t1) + + e.AssertIsEqual(t0, t2) +} diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index bc1655c15d..9bda6a0ed9 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -339,21 +339,20 @@ func TestFrobeniusCube(t *testing.T) { assert.NoError(err) } -/* -type e12MulBy034 struct { +type e12MulBy01379 struct { A E12 `gnark:",public"` W E12 B, C E2 } -func (circuit *e12MulBy034) Define(api frontend.API) error { +func (circuit *e12MulBy01379) Define(api frontend.API) error { e := NewExt12(api) - res := e.MulBy034(&circuit.A, &circuit.B, &circuit.C) + res := e.MulBy01379(&circuit.A, &circuit.B, &circuit.C) e.AssertIsEqual(res, &circuit.W) return nil } -func TestFp12MulBy034(t *testing.T) { +func TestFp12MulBy01379(t *testing.T) { assert := test.NewAssert(t) // witness values @@ -366,375 +365,14 @@ func TestFp12MulBy034(t *testing.T) { w.Set(&a) w.MulBy034(&one, &b, &c) - witness := e12MulBy034{ + witness := e12MulBy01379{ A: FromE12(&a), B: FromE2(&b), C: FromE2(&c), W: FromE12(&w), } - err := test.IsSolved(&e12MulBy034{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12MulBy01379{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } - -// Torus-based arithmetic -type e12ExptTorus struct { - A E6 - C E12 `gnark:",public"` -} - -func (circuit *e12ExptTorus) Define(api frontend.API) error { - e := NewExt12(api) - z := e.ExptTorus(&circuit.A) - expected := e.DecompressTorus(z) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestFp12ExptTorus(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bn254.E12 - _, _ = a.SetRandom() - - // put a in the cyclotomic subgroup - var tmp bn254.E12 - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - - c.Expt(&a) - _a, _ := a.CompressTorus() - witness := e12ExptTorus{ - A: FromE6(&_a), - C: FromE12(&c), - } - - err := test.IsSolved(&e12ExptTorus{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - - -type torusCompress struct { - A E12 - C E6 `gnark:",public"` -} - -func (circuit *torusCompress) Define(api frontend.API) error { - e := NewExt12(api) - expected := e.CompressTorus(&circuit.A) - e.Ext6.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestTorusCompress(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a bn254.E12 - _, _ = a.SetRandom() - - // put a in the cyclotomic subgroup - var tmp bn254.E12 - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - - c, _ := a.CompressTorus() - - witness := torusCompress{ - A: FromE12(&a), - C: FromE6(&c), - } - - err := test.IsSolved(&torusCompress{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type torusDecompress struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *torusDecompress) Define(api frontend.API) error { - e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - expected := e.DecompressTorus(compressed) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestTorusDecompress(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a bn254.E12 - _, _ = a.SetRandom() - - // put a in the cyclotomic subgroup - var tmp bn254.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{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&torusDecompress{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type torusMul struct { - A E12 - B E12 - C E12 `gnark:",public"` -} - -func (circuit *torusMul) 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) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestTorusMul(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, b, c, tmp bn254.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) - - witness := torusMul{ - A: FromE12(&a), - B: FromE12(&b), - C: FromE12(&c), - } - - err := test.IsSolved(&torusMul{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type torusInverse struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *torusInverse) Define(api frontend.API) error { - e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.InverseTorus(compressed) - expected := e.DecompressTorus(compressed) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestTorusInverse(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c, tmp bn254.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) - - witness := torusInverse{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&torusInverse{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type torusFrobenius struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *torusFrobenius) Define(api frontend.API) error { - e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.FrobeniusTorus(compressed) - expected := e.DecompressTorus(compressed) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestTorusFrobenius(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c, tmp bn254.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{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&torusFrobenius{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type torusFrobeniusSquare struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *torusFrobeniusSquare) Define(api frontend.API) error { - e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.FrobeniusSquareTorus(compressed) - expected := e.DecompressTorus(compressed) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestTorusFrobeniusSquare(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c, tmp bn254.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 frobeniusSquare - c.FrobeniusSquare(&a) - - witness := torusFrobeniusSquare{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&torusFrobeniusSquare{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type torusFrobeniusCube struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *torusFrobeniusCube) Define(api frontend.API) error { - e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.FrobeniusCubeTorus(compressed) - expected := e.DecompressTorus(compressed) - e.AssertIsEqual(expected, &circuit.C) - return nil -} - -func TestTorusFrobeniusCube(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c, tmp bn254.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 frobeniusCube - c.FrobeniusCube(&a) - - witness := torusFrobeniusCube{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&torusFrobeniusCube{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type torusSquare struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *torusSquare) 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) - return nil -} - -func TestTorusSquare(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c, tmp bn254.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 square - c.Square(&a) - - witness := torusSquare{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&torusSquare{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} -*/ diff --git a/std/algebra/emulated/sw_bn254/doc_test.go b/std/algebra/emulated/sw_bn254/doc_test.go deleted file mode 100644 index 36f7445dac..0000000000 --- a/std/algebra/emulated/sw_bn254/doc_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package sw_bn254_test - -import ( - "crypto/rand" - "fmt" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bn254" - "github.com/consensys/gnark/backend/groth16" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/r1cs" - "github.com/consensys/gnark/std/algebra/emulated/sw_bn254" -) - -type PairCircuit struct { - InG1 sw_bn254.G1Affine - InG2 sw_bn254.G2Affine - Res sw_bn254.GTEl -} - -func (c *PairCircuit) Define(api frontend.API) error { - pairing, err := sw_bn254.NewPairing(api) - if err != nil { - return fmt.Errorf("new pairing: %w", err) - } - // Pair method does not check that the points are in the proper groups. - pairing.AssertIsOnG1(&c.InG1) - pairing.AssertIsOnG2(&c.InG2) - // Compute the pairing - res, err := pairing.Pair([]*sw_bn254.G1Affine{&c.InG1}, []*sw_bn254.G2Affine{&c.InG2}) - if err != nil { - return fmt.Errorf("pair: %w", err) - } - pairing.AssertIsEqual(res, &c.Res) - return nil -} - -func ExamplePairing() { - p, q, err := randomG1G2Affines() - if err != nil { - panic(err) - } - res, err := bn254.Pair([]bn254.G1Affine{p}, []bn254.G2Affine{q}) - if err != nil { - panic(err) - } - circuit := PairCircuit{} - witness := PairCircuit{ - InG1: sw_bn254.NewG1Affine(p), - InG2: sw_bn254.NewG2Affine(q), - Res: sw_bn254.NewGTEl(res), - } - ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) - if err != nil { - panic(err) - } - pk, vk, err := groth16.Setup(ccs) - if err != nil { - panic(err) - } - secretWitness, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) - if err != nil { - panic(err) - } - publicWitness, err := secretWitness.Public() - if err != nil { - panic(err) - } - proof, err := groth16.Prove(ccs, pk, secretWitness) - if err != nil { - panic(err) - } - err = groth16.Verify(proof, vk, publicWitness) - if err != nil { - panic(err) - } -} - -func randomG1G2Affines() (p bn254.G1Affine, q bn254.G2Affine, err error) { - _, _, G1AffGen, G2AffGen := bn254.Generators() - mod := bn254.ID.ScalarField() - s1, err := rand.Int(rand.Reader, mod) - if err != nil { - return p, q, err - } - s2, err := rand.Int(rand.Reader, mod) - if err != nil { - return p, q, err - } - p.ScalarMultiplication(&G1AffGen, s1) - q.ScalarMultiplication(&G2AffGen, s2) - return -} diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index e449301927..13335a8d34 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -73,6 +73,7 @@ func NewPairing(api frontend.API) (*Pairing, error) { return &Pairing{ api: api, Ext12: fields_bn254.NewExt12(api), + Ext2: fields_bn254.NewExt2(api), curveF: ba, curve: curve, g2: NewG2(api), @@ -109,7 +110,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { f = pr.Ext12.FrobeniusSquare(buf) f = pr.Ext12.Mul(f, buf) - pr.AssertFinalExponentiationIsOne(f) + // pr.AssertFinalExponentiationIsOne(f) return nil } @@ -254,114 +255,26 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl xNegOverY[k] = pr.curveF.Neg(xNegOverY[k]) } - var prodLines [5]*fields_bn254.E2 res := pr.Ext12.One() // Compute f_{6x₀+2,Q}(P) - // i = 64, separately to avoid an E12 Square - // (Square(res) = 1² = 1) - - // k = 0, separately to avoid MulBy034 (res × ℓ) - // (assign line to res) - // line evaluation at P[0] - res = &fields_bn254.E12{ - C0: res.C0, - C1: fields_bn254.E6{ - B0: *pr.MulByElement(&lines[0][0][64].R0, xNegOverY[0]), - B1: *pr.MulByElement(&lines[0][0][64].R1, yInv[0]), - B2: res.C1.B2, - }, - } - - if n >= 2 { - // k = 1, separately to avoid MulBy034 (res × ℓ) - // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) - // line evaluation at P[1] - // ℓ × res - prodLines = pr.Mul034By034( - pr.MulByElement(&lines[1][0][64].R0, xNegOverY[1]), - pr.MulByElement(&lines[1][0][64].R1, yInv[1]), - &res.C1.B0, - &res.C1.B1, - ) - res = &fields_bn254.E12{ - C0: fields_bn254.E6{ - B0: *prodLines[0], - B1: *prodLines[1], - B2: *prodLines[2], - }, - C1: fields_bn254.E6{ - B0: *prodLines[3], - B1: *prodLines[4], - B2: res.C1.B2, - }, - } - } - - if n >= 3 { - // k = 2, separately to avoid MulBy034 (res × ℓ) - // (res has a zero E2 element, so we use Mul01234By034) - // line evaluation at P[1] - // ℓ × res - res = pr.Mul01234By034( - prodLines, - pr.MulByElement(&lines[2][0][64].R0, xNegOverY[2]), - pr.MulByElement(&lines[2][0][64].R1, yInv[2]), - ) + for i := 64; i >= 0; i-- { + res = pr.Ext12.Square(res) - // k >= 3 - for k := 3; k < n; k++ { - // line evaluation at P[k] - // ℓ × res - res = pr.MulBy034( + for k := 0; k < n; k++ { + res = pr.MulBy01379( res, - pr.MulByElement(&lines[k][0][64].R0, xNegOverY[k]), - pr.MulByElement(&lines[k][0][64].R1, yInv[k]), + pr.Ext2.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][i].R1, yInv[k]), ) - } - } - - for i := 63; i >= 0; i-- { - res = pr.Square(res) - - if loopCounter[i] == 0 { - // if number of lines is odd, mul last line by res - // works for n=1 as well - if n%2 != 0 { - // ℓ × res - res = pr.MulBy034( + if loopCounter[i] == 0 { + continue + } else { + res = pr.MulBy01379( res, - pr.MulByElement(&lines[n-1][0][i].R0, xNegOverY[n-1]), - pr.MulByElement(&lines[n-1][0][i].R1, yInv[n-1]), - ) - } - - // mul lines 2-by-2 - for k := 1; k < n; k += 2 { - // ℓ × ℓ - prodLines = pr.Mul034By034( - pr.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), - pr.MulByElement(&lines[k][0][i].R1, yInv[k]), - pr.MulByElement(&lines[k-1][0][i].R0, xNegOverY[k-1]), - pr.MulByElement(&lines[k-1][0][i].R1, yInv[k-1]), + pr.Ext2.MulByElement(&lines[k][1][i].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][1][i].R1, yInv[k]), ) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) - } - - } else { - for k := 0; k < n; k++ { - // lines evaluations at P - // and ℓ × ℓ - prodLines := pr.Mul034By034( - pr.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), - pr.MulByElement(&lines[k][0][i].R1, yInv[k]), - pr.MulByElement(&lines[k][1][i].R0, xNegOverY[k]), - pr.MulByElement(&lines[k][1][i].R1, yInv[k]), - ) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) - } } } @@ -370,14 +283,16 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl // lines evaluations at P // and ℓ × ℓ for k := 0; k < n; k++ { - prodLines := pr.Mul034By034( - pr.MulByElement(&lines[k][0][65].R0, xNegOverY[k]), - pr.MulByElement(&lines[k][0][65].R1, yInv[k]), - pr.MulByElement(&lines[k][1][65].R0, xNegOverY[k]), - pr.MulByElement(&lines[k][1][65].R1, yInv[k]), + res = pr.MulBy01379( + res, + pr.Ext2.MulByElement(&lines[k][0][65].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][65].R1, yInv[k]), + ) + res = pr.MulBy01379( + res, + pr.Ext2.MulByElement(&lines[k][1][65].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][1][65].R1, yInv[k]), ) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) } return res, nil @@ -529,164 +444,6 @@ func (pr Pairing) MillerLoopAndMul(P *G1Affine, Q *G2Affine, previous *GTEl) (*G if err != nil { return nil, fmt.Errorf("miller loop: %w", err) } - res = pr.Mul(res, previous) + res = pr.Ext12.Mul(res, previous) return res, err } - -// millerLoopAndFinalExpResult computes the Miller loop between P and Q, -// multiplies it in 𝔽p¹² by previous and returns the result. -func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous *GTEl) *GTEl { - - // hint the non-residue witness - hint, err := pr.curveF.NewHint(millerLoopAndCheckFinalExpHint, 18, &P.X, &P.Y, &Q.P.X.A0, &Q.P.X.A1, &Q.P.Y.A0, &Q.P.Y.A1, &previous.C0.B0.A0, &previous.C0.B0.A1, &previous.C0.B1.A0, &previous.C0.B1.A1, &previous.C0.B2.A0, &previous.C0.B2.A1, &previous.C1.B0.A0, &previous.C1.B0.A1, &previous.C1.B1.A0, &previous.C1.B1.A1, &previous.C1.B2.A0, &previous.C1.B2.A1) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } - - residueWitness := fields_bn254.E12{ - C0: fields_bn254.E6{ - B0: fields_bn254.E2{A0: *hint[0], A1: *hint[1]}, - B1: fields_bn254.E2{A0: *hint[2], A1: *hint[3]}, - B2: fields_bn254.E2{A0: *hint[4], A1: *hint[5]}, - }, - C1: fields_bn254.E6{ - B0: fields_bn254.E2{A0: *hint[6], A1: *hint[7]}, - B1: fields_bn254.E2{A0: *hint[8], A1: *hint[9]}, - B2: fields_bn254.E2{A0: *hint[10], A1: *hint[11]}, - }, - } - // constrain cubicNonResiduePower to be in Fp6 - cubicNonResiduePower := fields_bn254.E12{ - C0: fields_bn254.E6{ - B0: fields_bn254.E2{A0: *hint[12], A1: *hint[13]}, - B1: fields_bn254.E2{A0: *hint[14], A1: *hint[15]}, - B2: fields_bn254.E2{A0: *hint[16], A1: *hint[17]}, - }, - C1: (*pr.Ext6.Zero()), - } - - // residueWitnessInv = 1 / residueWitness - residueWitnessInv := pr.Inverse(&residueWitness) - - if Q.Lines == nil { - Qlines := pr.computeLines(&Q.P) - Q.Lines = &Qlines - } - lines := *Q.Lines - - // precomputations - yInv := pr.curveF.Inverse(&P.Y) - xNegOverY := pr.curveF.Mul(&P.X, yInv) - xNegOverY = pr.curveF.Neg(xNegOverY) - - // init Miller loop accumulator to residueWitnessInv to share the squarings - // of residueWitnessInv^{6x₀+2} - res := residueWitnessInv - - // Compute f_{6x₀+2,Q}(P) - for i := 64; i >= 0; i-- { - res = pr.Square(res) - - switch loopCounter[i] { - case 0: - // ℓ × res - res = pr.MulBy034( - res, - pr.MulByElement(&lines[0][i].R0, xNegOverY), - pr.MulByElement(&lines[0][i].R1, yInv), - ) - case 1: - // multiply by residueWitnessInv when bit=1 - res = pr.Mul(res, residueWitnessInv) - // lines evaluations at P - // and ℓ × ℓ - prodLines := pr.Mul034By034( - pr.MulByElement(&lines[0][i].R0, xNegOverY), - pr.MulByElement(&lines[0][i].R1, yInv), - pr.MulByElement(&lines[1][i].R0, xNegOverY), - pr.MulByElement(&lines[1][i].R1, yInv), - ) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) - case -1: - // multiply by residueWitness when bit=-1 - res = pr.Mul(res, &residueWitness) - // lines evaluations at P - // and ℓ × ℓ - prodLines := pr.Mul034By034( - pr.MulByElement(&lines[0][i].R0, xNegOverY), - pr.MulByElement(&lines[0][i].R1, yInv), - pr.MulByElement(&lines[1][i].R0, xNegOverY), - pr.MulByElement(&lines[1][i].R1, yInv), - ) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) - default: - panic(fmt.Sprintf("invalid loop counter value %d", loopCounter[i])) - } - } - - // Compute ℓ_{[6x₀+2]Q,π(Q)}(P) · ℓ_{[6x₀+2]Q+π(Q),-π²(Q)}(P) - // lines evaluations at P - // and ℓ × ℓ - prodLines := pr.Mul034By034( - pr.MulByElement(&lines[0][65].R0, xNegOverY), - pr.MulByElement(&lines[0][65].R1, yInv), - pr.MulByElement(&lines[1][65].R0, xNegOverY), - pr.MulByElement(&lines[1][65].R1, yInv), - ) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) - - // multiply by previous multi-Miller function - res = pr.Mul(res, previous) - - // Check that res * cubicNonResiduePower * residueWitnessInv^λ' == 1 - // where λ' = q^3 - q^2 + q, with u the BN254 seed - // and residueWitnessInv, cubicNonResiduePower from the hint. - // Note that res is already MillerLoop(P,Q) * residueWitnessInv^{6x₀+2} since - // we initialized the Miller loop accumulator with residueWitnessInv. - t2 := pr.Mul(&cubicNonResiduePower, res) - - t1 := pr.FrobeniusCube(residueWitnessInv) - t0 := pr.FrobeniusSquare(residueWitnessInv) - t1 = pr.DivUnchecked(t1, t0) - t0 = pr.Frobenius(residueWitnessInv) - t1 = pr.Mul(t1, t0) - - t2 = pr.Mul(t2, t1) - - return t2 -} - -// IsMillerLoopAndFinalExpOne computes the Miller loop between P and Q, -// multiplies it in 𝔽p¹² by previous and and returns a boolean indicating if -// the result lies in the same equivalence class as the reduced pairing -// purported to be 1. This check replaces the final exponentiation step -// in-circuit and follows Section 4 of [On Proving Pairings] paper by A. -// Novakovic and L. Eagen. -// -// This method is needed for evmprecompiles/ecpair. -// -// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (pr Pairing) IsMillerLoopAndFinalExpOne(P *G1Affine, Q *G2Affine, previous *GTEl) frontend.Variable { - t2 := pr.millerLoopAndFinalExpResult(P, Q, previous) - - res := pr.IsEqual(t2, pr.One()) - return res -} - -// AssertMillerLoopAndFinalExpIsOne computes the Miller loop between P and Q, -// multiplies it in 𝔽p¹² by previous and checks that the result lies in the -// same equivalence class as the reduced pairing purported to be 1. This check -// replaces the final exponentiation step in-circuit and follows Section 4 of -// [On Proving Pairings] paper by A. Novakovic and L. Eagen. -// -// This method is needed for evmprecompiles/ecpair. -// -// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (pr Pairing) AssertMillerLoopAndFinalExpIsOne(P *G1Affine, Q *G2Affine, previous *GTEl) { - t2 := pr.millerLoopAndFinalExpResult(P, Q, previous) - pr.AssertIsEqual(t2, pr.One()) -} diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index eb84bf9b98..9bf399069b 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -33,36 +33,6 @@ func randomG1G2Affines() (bn254.G1Affine, bn254.G2Affine) { return p, q } -type FinalExponentiationCircuit struct { - InGt GTEl - Res GTEl -} - -func (c *FinalExponentiationCircuit) Define(api frontend.API) error { - pairing, err := NewPairing(api) - 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) - return nil -} - -func TestFinalExponentiationTestSolve(t *testing.T) { - assert := test.NewAssert(t) - var gt bn254.GT - gt.SetRandom() - res := bn254.FinalExponentiation(>) - witness := FinalExponentiationCircuit{ - InGt: NewGTEl(gt), - Res: NewGTEl(res), - } - err := test.IsSolved(&FinalExponentiationCircuit{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - type MillerLoopCircuit struct { InG1 G1Affine InG2 G2Affine @@ -145,108 +115,6 @@ func TestMillerLoopAndMulTestSolve(t *testing.T) { assert.NoError(err) } -type PairCircuit struct { - InG1 G1Affine - InG2 G2Affine - Res GTEl -} - -func (c *PairCircuit) Define(api frontend.API) error { - pairing, err := NewPairing(api) - if err != nil { - return fmt.Errorf("new pairing: %w", err) - } - pairing.AssertIsOnG1(&c.InG1) - pairing.AssertIsOnG2(&c.InG2) - res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) - if err != nil { - return fmt.Errorf("pair: %w", err) - } - pairing.AssertIsEqual(res, &c.Res) - return nil -} - -func TestPairTestSolve(t *testing.T) { - assert := test.NewAssert(t) - p, q := randomG1G2Affines() - res, err := bn254.Pair([]bn254.G1Affine{p}, []bn254.G2Affine{q}) - assert.NoError(err) - witness := PairCircuit{ - InG1: NewG1Affine(p), - InG2: NewG2Affine(q), - Res: NewGTEl(res), - } - err = test.IsSolved(&PairCircuit{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -func TestPairFixedTestSolve(t *testing.T) { - assert := test.NewAssert(t) - p, q := randomG1G2Affines() - res, err := bn254.Pair([]bn254.G1Affine{p}, []bn254.G2Affine{q}) - assert.NoError(err) - witness := PairCircuit{ - InG1: NewG1Affine(p), - InG2: NewG2AffineFixed(q), - Res: NewGTEl(res), - } - err = test.IsSolved(&PairCircuit{InG2: NewG2AffineFixedPlaceholder()}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type MultiPairCircuit struct { - InG1 G1Affine - InG2 G2Affine - Res GTEl - n int -} - -func (c *MultiPairCircuit) Define(api frontend.API) error { - pairing, err := NewPairing(api) - if err != nil { - return fmt.Errorf("new pairing: %w", err) - } - pairing.AssertIsOnG1(&c.InG1) - pairing.AssertIsOnG2(&c.InG2) - P, Q := []*G1Affine{}, []*G2Affine{} - for i := 0; i < c.n; i++ { - P = append(P, &c.InG1) - Q = append(Q, &c.InG2) - } - res, err := pairing.Pair(P, Q) - if err != nil { - return fmt.Errorf("pair: %w", err) - } - pairing.AssertIsEqual(res, &c.Res) - return nil -} - -func TestMultiPairTestSolve(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - assert := test.NewAssert(t) - p1, q1 := randomG1G2Affines() - p := make([]bn254.G1Affine, 4) - q := make([]bn254.G2Affine, 4) - for i := 0; i < 4; i++ { - p[i] = p1 - q[i] = q1 - } - - for i := 2; i < 4; i++ { - res, err := bn254.Pair(p[:i], q[:i]) - assert.NoError(err) - witness := MultiPairCircuit{ - InG1: NewG1Affine(p1), - InG2: NewG2Affine(q1), - Res: NewGTEl(res), - } - err = test.IsSolved(&MultiPairCircuit{n: i}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - } -} - type PairingCheckCircuit struct { In1G1 G1Affine In2G1 G1Affine @@ -419,65 +287,6 @@ func TestIsOnG2Solve(t *testing.T) { assert.NoError(err) } -type IsMillerLoopAndFinalExpCircuit struct { - Prev GTEl - P G1Affine - Q G2Affine - Expected frontend.Variable -} - -func (c *IsMillerLoopAndFinalExpCircuit) Define(api frontend.API) error { - pairing, err := NewPairing(api) - if err != nil { - return fmt.Errorf("new pairing: %w", err) - } - res := pairing.IsMillerLoopAndFinalExpOne(&c.P, &c.Q, &c.Prev) - api.AssertIsEqual(res, c.Expected) - return nil - -} - -func TestIsMillerLoopAndFinalExpCircuitTestSolve(t *testing.T) { - assert := test.NewAssert(t) - p, q := randomG1G2Affines() - - var np bn254.G1Affine - np.Neg(&p) - - ok, err := bn254.PairingCheck([]bn254.G1Affine{p, np}, []bn254.G2Affine{q, q}) - assert.NoError(err) - assert.True(ok) - - lines := bn254.PrecomputeLines(q) - // need to use ML with precomputed lines. Otherwise, the result will be different - mlres, err := bn254.MillerLoopFixedQ( - []bn254.G1Affine{p}, - [][2][len(bn254.LoopCounter)]bn254.LineEvaluationAff{lines}, - ) - assert.NoError(err) - - witness := IsMillerLoopAndFinalExpCircuit{ - Prev: NewGTEl(mlres), - P: NewG1Affine(np), - Q: NewG2Affine(q), - Expected: 1, - } - err = test.IsSolved(&IsMillerLoopAndFinalExpCircuit{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - - var randPrev bn254.GT - randPrev.SetRandom() - - witness = IsMillerLoopAndFinalExpCircuit{ - Prev: NewGTEl(randPrev), - P: NewG1Affine(np), - Q: NewG2Affine(q), - Expected: 0, - } - err = test.IsSolved(&IsMillerLoopAndFinalExpCircuit{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - // bench func BenchmarkPairing(b *testing.B) { // e(a,2b) * e(-2a,b) == 1 @@ -544,65 +353,3 @@ func BenchmarkPairing(b *testing.B) { } }) } - -// bench -func BenchmarkFinalExponentiation(b *testing.B) { - // e(a,2b) * e(-2a,b) == 1 - var gt bn254.GT - gt.SetRandom() - res := bn254.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) - } - } - }) -} From 19fea1d09217498452f4dc23a24651592db00d12 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 9 Dec 2024 11:31:52 -0500 Subject: [PATCH 06/22] fix(finalexp): constrain nonresidue to be in direct Fp6 --- .../emulated/fields_bn254/e12_pairing.go | 88 ++++++++----------- std/algebra/emulated/sw_bn254/pairing_test.go | 33 +++++++ 2 files changed, 71 insertions(+), 50 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index f9853c4c23..357d514791 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -174,61 +174,49 @@ func (e Ext12) AssertFinalExponentiationIsOne(a *E12) { panic(err) } - A0 := e.fp.Sub(res[0], e.fp.MulConst(res[1], nine)) - A1 := e.fp.Sub(res[6], e.fp.MulConst(res[7], nine)) - A2 := e.fp.Sub(res[2], e.fp.MulConst(res[3], nine)) - A3 := e.fp.Sub(res[8], e.fp.MulConst(res[9], nine)) - A4 := e.fp.Sub(res[4], e.fp.MulConst(res[5], nine)) - A5 := e.fp.Sub(res[10], e.fp.MulConst(res[11], nine)) - A6 := res[1] - A7 := res[7] - A8 := res[3] - A9 := res[9] - A10 := res[5] - A11 := res[11] - residueWitness := E12{ - A0: *A0, - A1: *A1, - A2: *A2, - A3: *A3, - A4: *A4, - A5: *A5, - A6: *A6, - A7: *A7, - A8: *A8, - A9: *A9, - A10: *A10, - A11: *A11, + A0: *e.fp.Sub(res[0], e.fp.MulConst(res[1], nine)), + A1: *e.fp.Sub(res[6], e.fp.MulConst(res[7], nine)), + A2: *e.fp.Sub(res[2], e.fp.MulConst(res[3], nine)), + A3: *e.fp.Sub(res[8], e.fp.MulConst(res[9], nine)), + A4: *e.fp.Sub(res[4], e.fp.MulConst(res[5], nine)), + A5: *e.fp.Sub(res[10], e.fp.MulConst(res[11], nine)), + A6: *res[1], + A7: *res[7], + A8: *res[3], + A9: *res[9], + A10: *res[5], + A11: *res[11], } - A0 = e.fp.Sub(res[12+0], e.fp.MulConst(res[12+1], nine)) - A1 = e.fp.Sub(res[12+6], e.fp.MulConst(res[12+7], nine)) - A2 = e.fp.Sub(res[12+2], e.fp.MulConst(res[12+3], nine)) - A3 = e.fp.Sub(res[12+8], e.fp.MulConst(res[12+9], nine)) - A4 = e.fp.Sub(res[12+4], e.fp.MulConst(res[12+5], nine)) - A5 = e.fp.Sub(res[12+10], e.fp.MulConst(res[12+11], nine)) - A6 = e.fp.Zero() - A7 = e.fp.Zero() - A8 = e.fp.Zero() - A9 = e.fp.Zero() - A10 = e.fp.Zero() - A11 = e.fp.Zero() - // constrain cubicNonResiduePower to be in Fp6 + // that is: a100=a101=a110=a111=a120=a121=0 + // or + // A0 = a000 - 9 * a001 + // A1 = 0 + // A2 = a010 - 9 * a011 + // A3 = 0 + // A4 = a020 - 9 * a021 + // A5 = 0 + // A6 = a001 + // A7 = 0 + // A8 = a011 + // A9 = 0 + // A10 = a021 + // A11 = 0 cubicNonResiduePower := E12{ - A0: *A0, - A1: *A1, - A2: *A2, - A3: *A3, - A4: *A4, - A5: *A5, - A6: *A6, - A7: *A7, - A8: *A8, - A9: *A9, - A10: *A10, - A11: *A11, + A0: *e.fp.Sub(res[12], e.fp.MulConst(res[13], nine)), + A1: *e.fp.Zero(), + A2: *e.fp.Sub(res[14], e.fp.MulConst(res[15], nine)), + A3: *e.fp.Zero(), + A4: *e.fp.Sub(res[16], e.fp.MulConst(res[17], nine)), + A5: *e.fp.Zero(), + A6: *res[13], + A7: *e.fp.Zero(), + A8: *res[15], + A9: *e.fp.Zero(), + A10: *res[17], + A11: *e.fp.Zero(), } // Check that x * cubicNonResiduePower == residueWitness^λ diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index 9bf399069b..5fda8c109e 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -115,6 +115,39 @@ func TestMillerLoopAndMulTestSolve(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 bn254.G1Affine + p2.Double(&p1).Neg(&p2) + var q2 bn254.G2Affine + q2.Set(&q1) + q1.Double(&q1) + ml, err := bn254.MillerLoop( + []bn254.G1Affine{p1, p2}, + []bn254.G2Affine{q1, q2}, + ) + witness := FinalExponentiationIsOne{ + InGt: NewGTEl(ml), + } + err = test.IsSolved(&FinalExponentiationIsOne{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + type PairingCheckCircuit struct { In1G1 G1Affine In2G1 G1Affine From a0956debf29d672d1375c63699004f032c1ae351 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 9 Dec 2024 12:40:16 -0500 Subject: [PATCH 07/22] feat: add Mul01379By01379 --- .../emulated/fields_bn254/e12_pairing.go | 78 +++++++++++++++++++ std/algebra/emulated/fields_bn254/e12_test.go | 74 ++++++++++++++++++ std/algebra/emulated/sw_bn254/pairing.go | 11 ++- 3 files changed, 162 insertions(+), 1 deletion(-) diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index 357d514791..62537fd84f 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -147,6 +147,84 @@ func (e *Ext12) MulBy01379(a *E12, c3, c4 *E2) *E12 { } } +// Mul01379By01379 multiplies two E12 sparse element of the form: +// +// A0 = 1 +// A1 = c3.A0 - 9 * c3.A1 +// A2 = 0 +// A3 = c4.A0 - 9 * c4.A1 +// A4 = 0 +// A5 = 0 +// A6 = 0 +// A7 = c3.A1 +// A8 = 0 +// A9 = c4.A1 +// A10 = 0 +// A11 = 0 +func (e *Ext12) Mul01379By01379(e3, e4, c3, c4 *E2) *E12 { + nine := big.NewInt(9) + a1 := e.fp.Sub(&e3.A0, e.fp.MulConst(&e3.A1, nine)) + a3 := e.fp.Sub(&e4.A0, e.fp.MulConst(&e4.A1, nine)) + a7 := &e3.A1 + a9 := &e4.A1 + b1 := e.fp.Sub(&c3.A0, e.fp.MulConst(&c3.A1, nine)) + b3 := e.fp.Sub(&c4.A0, e.fp.MulConst(&c4.A1, nine)) + b7 := &c3.A1 + b9 := &c4.A1 + + // d0 = 1 - 82 * (a3 b9 + a9 b3) - 1476 * a9 b9 + mone := e.fp.NewElement(-1) + d0 := e.fp.Eval([][]*baseEl{{e.fp.One()}, {mone, a3, b9}, {mone, a9, b3}, {mone, a9, b9}}, []int{1, 82, 82, 1476}) + + // d1 = b1 + a1 + d1 := e.fp.Add(a1, b1) + + // d2 = a1 b1 - 82 * a7 b7 + d2 := e.fp.Eval([][]*baseEl{{a1, b1}, {mone, a7, b7}}, []int{1, 82}) + + // d3 = b3 + a3 + d3 := e.fp.Add(a3, b3) + + // d4 = a1 b3 + a3 b1 - 82 * (a7 b9 + a9 b7) + d4 := e.fp.Eval([][]*baseEl{{a1, b3}, {a3, b1}, {mone, a7, b9}, {mone, a9, b7}}, []int{1, 1, 82, 82}) + + // d5 = 0 + d5 := e.fp.Zero() + + // d6 = a3 b3 + 18 * (a3 b9 + a9 b3) + 242 * a9 b9 + d6 := e.fp.Eval([][]*baseEl{{a3, b3}, {a3, b9}, {a9, b3}, {a9, b9}}, []int{1, 18, 18, 242}) + + // d7 = b7 + a7 + d7 := e.fp.Add(a7, b7) + + // d8 = a1 b7 + a7 b1 + 18 * a7 b7 + d8 := e.fp.Eval([][]*baseEl{{a1, b7}, {a7, b1}, {a7, b7}}, []int{1, 1, 18}) + + // d9 = b9 + a9 + d9 := e.fp.Add(a9, b9) + + // d10 = a3 b7 + a1 b9 + a7 b3 + a9 b1 + 18 * (a7 b9 + a9 b7) + d10 := e.fp.Eval([][]*baseEl{{a3, b7}, {a1, b9}, {a7, b3}, {a9, b1}, {a7, b9}, {a9, b7}}, []int{1, 1, 1, 1, 18, 18}) + + // d11 = 0 + d11 := e.fp.Zero() + + 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, + } +} + // 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. diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index 9bda6a0ed9..aa5e0a5cc6 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -376,3 +376,77 @@ func TestFp12MulBy01379(t *testing.T) { assert.NoError(err) } + +type e12Mul01379By01379 struct { + A, B E2 `gnark:",public"` + C, D E2 `gnark:",public"` + W E12 +} + +func (circuit *e12Mul01379By01379) Define(api frontend.API) error { + e := NewExt12(api) + res := e.Mul01379By01379(&circuit.A, &circuit.B, &circuit.C, &circuit.D) + e.AssertIsEqual(res, &circuit.W) + return nil +} + +func TestFp12Mul01379By01379(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var one, a, b, c, d bn254.E2 + one.SetOne() + _, _ = a.SetRandom() + _, _ = b.SetRandom() + _, _ = c.SetRandom() + _, _ = d.SetRandom() + prod := Mul034By034(&one, &a, &b, &one, &c, &d) + var w bn254.E12 + w.C0.B0.Set(&prod[0]) + w.C0.B1.Set(&prod[1]) + w.C0.B2.Set(&prod[2]) + w.C1.B0.Set(&prod[3]) + w.C1.B1.Set(&prod[4]) + w.C1.B2.SetZero() + + witness := e12Mul01379By01379{ + A: FromE2(&a), + B: FromE2(&b), + C: FromE2(&c), + D: FromE2(&d), + W: FromE12(&w), + } + + err := test.IsSolved(&e12Mul01379By01379{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + +} + +// utils +// Mul034By034 multiplication of sparse element (c0,0,0,c3,c4,0) by sparse element (d0,0,0,d3,d4,0) +func Mul034By034(d0, d3, d4, c0, c3, c4 *bn254.E2) [5]bn254.E2 { + var z00, tmp, x0, x3, x4, x04, x03, x34 bn254.E2 + x0.Mul(c0, d0) + x3.Mul(c3, d3) + x4.Mul(c4, d4) + tmp.Add(c0, c4) + x04.Add(d0, d4). + Mul(&x04, &tmp). + Sub(&x04, &x0). + Sub(&x04, &x4) + tmp.Add(c0, c3) + x03.Add(d0, d3). + Mul(&x03, &tmp). + Sub(&x03, &x0). + Sub(&x03, &x3) + tmp.Add(c3, c4) + x34.Add(d3, d4). + Mul(&x34, &tmp). + Sub(&x34, &x3). + Sub(&x34, &x4) + + z00.MulByNonResidue(&x4). + Add(&z00, &x0) + + return [5]bn254.E2{z00, x3, x34, x03, x04} +} diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 13335a8d34..1cb3bb861e 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -258,7 +258,16 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl res := pr.Ext12.One() // Compute f_{6x₀+2,Q}(P) - for i := 64; i >= 0; i-- { + // i = 64 + for k := 0; k < n; k++ { + res = pr.MulBy01379( + res, + pr.Ext2.MulByElement(&lines[k][0][0].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][0].R1, yInv[k]), + ) + } + + for i := 63; i >= 0; i-- { res = pr.Ext12.Square(res) for k := 0; k < n; k++ { From 7a19812f3d744f086b2ccd0bada8486f0dd5f5f8 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 9 Dec 2024 14:37:49 -0500 Subject: [PATCH 08/22] feat(bn254): pairing using direct Fp12 w/ Eval --- .../emulated/fields_bn254/e12_pairing.go | 64 +++++++++++++-- std/algebra/emulated/fields_bn254/e12_test.go | 5 +- std/algebra/emulated/sw_bn254/pairing.go | 55 +++++++++---- std/algebra/emulated/sw_bn254/pairing_test.go | 82 ------------------- 4 files changed, 99 insertions(+), 107 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index 62537fd84f..a99a1add81 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -161,7 +161,7 @@ func (e *Ext12) MulBy01379(a *E12, c3, c4 *E2) *E12 { // A9 = c4.A1 // A10 = 0 // A11 = 0 -func (e *Ext12) Mul01379By01379(e3, e4, c3, c4 *E2) *E12 { +func (e *Ext12) Mul01379By01379(e3, e4, c3, c4 *E2) [10]*baseEl { nine := big.NewInt(9) a1 := e.fp.Sub(&e3.A0, e.fp.MulConst(&e3.A1, nine)) a3 := e.fp.Sub(&e4.A0, e.fp.MulConst(&e4.A1, nine)) @@ -174,7 +174,8 @@ func (e *Ext12) Mul01379By01379(e3, e4, c3, c4 *E2) *E12 { // d0 = 1 - 82 * (a3 b9 + a9 b3) - 1476 * a9 b9 mone := e.fp.NewElement(-1) - d0 := e.fp.Eval([][]*baseEl{{e.fp.One()}, {mone, a3, b9}, {mone, a9, b3}, {mone, a9, b9}}, []int{1, 82, 82, 1476}) + d0 := e.fp.Eval([][]*baseEl{{a3, b9}, {a9, b3}, {a9, b9}, {mone}}, []int{82, 82, 1476, 1}) + d0 = e.fp.Neg(d0) // d1 = b1 + a1 d1 := e.fp.Add(a1, b1) @@ -188,9 +189,6 @@ func (e *Ext12) Mul01379By01379(e3, e4, c3, c4 *E2) *E12 { // d4 = a1 b3 + a3 b1 - 82 * (a7 b9 + a9 b7) d4 := e.fp.Eval([][]*baseEl{{a1, b3}, {a3, b1}, {mone, a7, b9}, {mone, a9, b7}}, []int{1, 1, 82, 82}) - // d5 = 0 - d5 := e.fp.Zero() - // d6 = a3 b3 + 18 * (a3 b9 + a9 b3) + 242 * a9 b9 d6 := e.fp.Eval([][]*baseEl{{a3, b3}, {a3, b9}, {a9, b3}, {a9, b9}}, []int{1, 18, 18, 242}) @@ -206,8 +204,60 @@ func (e *Ext12) Mul01379By01379(e3, e4, c3, c4 *E2) *E12 { // d10 = a3 b7 + a1 b9 + a7 b3 + a9 b1 + 18 * (a7 b9 + a9 b7) d10 := e.fp.Eval([][]*baseEl{{a3, b7}, {a1, b9}, {a7, b3}, {a9, b1}, {a7, b9}, {a9, b7}}, []int{1, 1, 1, 1, 18, 18}) - // d11 = 0 - d11 := e.fp.Zero() + return [10]*baseEl{d0, d1, d2, d3, d4, d6, d7, d8, d9, d10} +} + +// MulBy01379 multiplies a by an E12 sparse element b of the form +// +// b.A0 = b[0] +// b.A1 = b[1] +// b.A2 = b[2] +// b.A3 = b[3] +// b.A4 = b[4] +// b.A5 = 0 +// b.A6 = b[5] +// b.A7 = b[6] +// b.A8 = b[7] +// b.A9 = b[8] +// b.A10 = b[9] +// b.A11 = 0 +func (e *Ext12) MulBy012346789(a *E12, b [10]*baseEl) *E12 { + // d0 = a0 b0 - 82 * (a2 b10 + a3 b9 + a4 b8 + a5 b7 + a6 b6 + a8 b4 + a9 b3 + a10 b2 + a11 b1) - 1476 * (a8 b10 + a9 b9 + a10 b8 + a11 b7) + mone := e.fp.NewElement(-1) + d0 := e.fp.Eval([][]*baseEl{{&a.A0, b[0]}, {mone, &a.A2, b[9]}, {mone, &a.A3, b[8]}, {mone, &a.A4, b[7]}, {mone, &a.A5, b[6]}, {mone, &a.A6, b[5]}, {mone, &a.A8, b[4]}, {mone, &a.A9, b[3]}, {mone, &a.A10, b[2]}, {mone, &a.A11, b[1]}, {mone, &a.A8, b[9]}, {mone, &a.A9, b[8]}, {mone, &a.A10, b[7]}, {mone, &a.A11, b[6]}}, []int{1, 82, 82, 82, 82, 82, 82, 82, 82, 82, 1476, 1476, 1476, 1476}) + + // d1 = a0 b1 + a1 b0 - 82 * (a3 b10 + a4 b9 + a5 b8 + a6 b7 + a7 b6 + a9 b4 + a10 b3 + a11 b2) - 1476 * (a9 b10 + a10 b9 + a11 b8) + d1 := e.fp.Eval([][]*baseEl{{&a.A0, b[1]}, {&a.A1, b[0]}, {mone, &a.A3, b[9]}, {mone, &a.A4, b[8]}, {mone, &a.A5, b[7]}, {mone, &a.A6, b[6]}, {mone, &a.A7, b[5]}, {mone, &a.A9, b[4]}, {mone, &a.A10, b[3]}, {mone, &a.A11, b[2]}, {mone, &a.A9, b[9]}, {mone, &a.A10, b[8]}, {mone, &a.A11, b[7]}}, []int{1, 1, 82, 82, 82, 82, 82, 82, 82, 82, 1476, 1476, 1476}) + + // d2 = a0 b2 + a1 b1 + a2 b0 - 82 * (a4 b10 + a5 b9 + a6 b8 + a7 b7 + a8 b6 + a10 b4 + a11 b3) - 1476 * (a10 b10 + a11 b9) + d2 := e.fp.Eval([][]*baseEl{{&a.A0, b[2]}, {&a.A1, b[1]}, {&a.A2, b[0]}, {mone, &a.A4, b[9]}, {mone, &a.A5, b[8]}, {mone, &a.A6, b[7]}, {mone, &a.A7, b[6]}, {mone, &a.A8, b[5]}, {mone, &a.A10, b[4]}, {mone, &a.A11, b[3]}, {mone, &a.A10, b[9]}, {mone, &a.A11, b[8]}}, []int{1, 1, 1, 82, 82, 82, 82, 82, 82, 82, 1476, 1476}) + + // d3 = a0 b3 + a1 b2 + a2 b1 + a3 b0 - 82 * (a5 b10 + a6 b9 + a7 b8 + a8 b7 + a9 b6 + a11 b4) - 1476 * a11 b10 + d3 := e.fp.Eval([][]*baseEl{{&a.A0, b[3]}, {&a.A1, b[2]}, {&a.A2, b[1]}, {&a.A3, b[0]}, {mone, &a.A5, b[9]}, {mone, &a.A6, b[8]}, {mone, &a.A7, b[7]}, {mone, &a.A8, b[6]}, {mone, &a.A9, b[5]}, {mone, &a.A11, b[4]}, {mone, &a.A11, b[9]}}, []int{1, 1, 1, 1, 82, 82, 82, 82, 82, 82, 1476}) + + // d4 = a0 b4 + a1 b3 + a2 b2 + a3 b1 + a4 b0 - 82 * (a6 b10 + a7 b9 + a8 b8 + a9 b7 + a10 b6) + d4 := e.fp.Eval([][]*baseEl{{&a.A0, b[4]}, {&a.A1, b[3]}, {&a.A2, b[2]}, {&a.A3, b[1]}, {&a.A4, b[0]}, {mone, &a.A6, b[9]}, {mone, &a.A7, b[8]}, {mone, &a.A8, b[7]}, {mone, &a.A9, b[6]}, {mone, &a.A10, b[5]}}, []int{1, 1, 1, 1, 1, 82, 82, 82, 82, 82}) + + // d5 = a1 b4 + a2 b3 + a3 b2 + a4 b1 + a5 b0 - 82 * (a7 b10 + a8 b9 + a9 b8 + a10 b7 + a11 b6) + d5 := e.fp.Eval([][]*baseEl{{&a.A1, b[4]}, {&a.A2, b[3]}, {&a.A3, b[2]}, {&a.A4, b[1]}, {&a.A5, b[0]}, {mone, &a.A7, b[9]}, {mone, &a.A8, b[8]}, {mone, &a.A9, b[7]}, {mone, &a.A10, b[6]}, {mone, &a.A11, b[5]}}, []int{1, 1, 1, 1, 1, 82, 82, 82, 82, 82}) + + // d6 = a0 b6 + a2 b4 + a3 b3 + a4 b2 + a5 b1 + a6 b0 + 18 * (a2 b10 + a3 b9 + a4 b8 + a5 b7 + a6 b6 + a8 b4 + a9 b3 + a10 b2 + a11 b1) + 242 * (a8 b10 + a9 b9 + a10 b8 + a11 b7) + d6 := e.fp.Eval([][]*baseEl{{&a.A0, b[5]}, {&a.A2, b[4]}, {&a.A3, b[3]}, {&a.A4, b[2]}, {&a.A5, b[1]}, {&a.A6, b[0]}, {&a.A2, b[9]}, {&a.A3, b[8]}, {&a.A4, b[7]}, {&a.A5, b[6]}, {&a.A6, b[5]}, {&a.A8, b[4]}, {&a.A9, b[3]}, {&a.A10, b[2]}, {&a.A11, b[1]}, {&a.A8, b[9]}, {&a.A9, b[8]}, {&a.A10, b[7]}, {&a.A11, b[6]}}, []int{1, 1, 1, 1, 1, 1, 18, 18, 18, 18, 18, 18, 18, 18, 18, 242, 242, 242, 242}) + + // d7 == a0 b7 + a1 b6 + a3 b4 + a4 b3 + a5 b2 + a6 b1 + a7 b0 + 18 * (a3 b10 + a4 b9 + a5 b8 + a6 b7 + a7 b6 + a9 b4 + a10 b3 + a11 b2) + 242 * (a9 b10 + a10 b9 + a11 b8) + d7 := e.fp.Eval([][]*baseEl{{&a.A0, b[6]}, {&a.A1, b[5]}, {&a.A3, b[4]}, {&a.A4, b[3]}, {&a.A5, b[2]}, {&a.A6, b[1]}, {&a.A7, b[0]}, {&a.A3, b[9]}, {&a.A4, b[8]}, {&a.A5, b[7]}, {&a.A6, b[6]}, {&a.A7, b[5]}, {&a.A9, b[4]}, {&a.A10, b[3]}, {&a.A11, b[2]}, {&a.A9, b[9]}, {&a.A10, b[8]}, {&a.A11, b[7]}}, []int{1, 1, 1, 1, 1, 1, 1, 18, 18, 18, 18, 18, 18, 18, 18, 242, 242, 242}) + + // d8 = a0 b8 + a1 b7 + a2 b6 + a4 b4 + a5 b3 + a6 b2 + a7 b1 + a8 b0 + 18 * (a4 b10 + a5 b9 + a6 b8 + a7 b7 + a8 b6 + a10 b4 + a11 b3) + 242 * (a10 b10 + a11 b9) + d8 := e.fp.Eval([][]*baseEl{{&a.A0, b[7]}, {&a.A1, b[6]}, {&a.A2, b[5]}, {&a.A4, b[4]}, {&a.A5, b[3]}, {&a.A6, b[2]}, {&a.A7, b[1]}, {&a.A8, b[0]}, {&a.A4, b[9]}, {&a.A5, b[8]}, {&a.A6, b[7]}, {&a.A7, b[6]}, {&a.A8, b[5]}, {&a.A10, b[4]}, {&a.A11, b[3]}, {&a.A10, b[9]}, {&a.A11, b[8]}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 18, 18, 18, 18, 18, 18, 18, 242, 242}) + + // d9 = a0 b9 + a1 b8 + a2 b7 + a3 b6 + a5 b4 + a6 b3 + a7 b2 + a8 b1 + a9 b0 + 18 * (a5 b10 + a6 b9 + a7 b8 + a8 b7 + a9 b6 + a11 b4) + 242 * a11 b10 + d9 := e.fp.Eval([][]*baseEl{{&a.A0, b[8]}, {&a.A1, b[7]}, {&a.A2, b[6]}, {&a.A3, b[5]}, {&a.A5, b[4]}, {&a.A6, b[3]}, {&a.A7, b[2]}, {&a.A8, b[1]}, {&a.A9, b[0]}, {&a.A5, b[9]}, {&a.A6, b[8]}, {&a.A7, b[7]}, {&a.A8, b[6]}, {&a.A9, b[5]}, {&a.A11, b[4]}, {&a.A11, b[9]}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 18, 18, 18, 18, 18, 18, 242}) + + // d10 = a0 b10 + a1 b9 + a2 b8 + a3 b7 + a4 b6 + a6 b4 + a7 b3 + a8 b2 + a9 b1 + a10 b0 + 18 * (a6 b10 + a7 b9 + a8 b8 + a9 b7 + a10 b6) + d10 := e.fp.Eval([][]*baseEl{{&a.A0, b[9]}, {&a.A1, b[8]}, {&a.A2, b[7]}, {&a.A3, b[6]}, {&a.A4, b[5]}, {&a.A6, b[4]}, {&a.A7, b[3]}, {&a.A8, b[2]}, {&a.A9, b[1]}, {&a.A10, b[0]}, {&a.A6, b[9]}, {&a.A7, b[8]}, {&a.A8, b[7]}, {&a.A9, b[6]}, {&a.A10, b[5]}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 18, 18, 18, 18, 18}) + + // d11 = a1 b10 + a2 b9 + a3 b8 + a4 b7 + a5 b6 + a7 b4 + a8 b3 + a9 b2 + a10 b1 + a11 b0 + 18 * (a7 b10 + a8 b9 + a9 b8 + a10 b7 + a11 b6) + d11 := e.fp.Eval([][]*baseEl{{&a.A1, b[9]}, {&a.A2, b[8]}, {&a.A3, b[7]}, {&a.A4, b[6]}, {&a.A5, b[5]}, {&a.A7, b[4]}, {&a.A8, b[3]}, {&a.A9, b[2]}, {&a.A10, b[1]}, {&a.A11, b[0]}, {&a.A7, b[9]}, {&a.A8, b[8]}, {&a.A9, b[7]}, {&a.A10, b[6]}, {&a.A11, b[5]}}, []int{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 18, 18, 18, 18, 18}) return &E12{ A0: *d0, diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index aa5e0a5cc6..52610304fa 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -386,7 +386,10 @@ type e12Mul01379By01379 struct { func (circuit *e12Mul01379By01379) Define(api frontend.API) error { e := NewExt12(api) res := e.Mul01379By01379(&circuit.A, &circuit.B, &circuit.C, &circuit.D) - e.AssertIsEqual(res, &circuit.W) + e.AssertIsEqual( + &E12{*res[0], *res[1], *res[2], *res[3], *res[4], *e.fp.Zero(), *res[5], *res[6], *res[7], *res[8], *res[9], *e.fp.Zero()}, + &circuit.W, + ) return nil } diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 1cb3bb861e..b982946a92 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -13,6 +13,9 @@ import ( "github.com/consensys/gnark/std/math/emulated" ) +type baseEl = emulated.Element[BaseField] +type GTEl = fields_bn254.E12 + type Pairing struct { api frontend.API *fields_bn254.Ext12 @@ -24,8 +27,6 @@ type Pairing struct { g2gen *G2Affine } -type GTEl = fields_bn254.E12 - func NewGTEl(a bn254.GT) GTEl { var c0, c1, c2, c3, c4, c5, t fp_bn.Element t.SetUint64(9).Mul(&t, &a.C0.B0.A1) @@ -243,8 +244,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. @@ -256,6 +257,7 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl } res := pr.Ext12.One() + var prodLines [10]*baseEl // Compute f_{6x₀+2,Q}(P) // i = 64 @@ -271,19 +273,41 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl res = pr.Ext12.Square(res) for k := 0; k < n; k++ { - res = pr.MulBy01379( - res, - pr.Ext2.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), - pr.Ext2.MulByElement(&lines[k][0][i].R1, yInv[k]), - ) if loopCounter[i] == 0 { - continue + // if number of lines is odd, mul last line by res + // works for n=1 as well + if n%2 != 0 { + // ℓ × res + res = pr.MulBy01379( + res, + pr.Ext2.MulByElement(&lines[n-1][0][i].R0, xNegOverY[n-1]), + pr.Ext2.MulByElement(&lines[n-1][0][i].R1, yInv[n-1]), + ) + } + + // mul lines 2-by-2 + for k := 1; k < n; k += 2 { + // ℓ × ℓ + prodLines = pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][i].R1, yInv[k]), + pr.Ext2.MulByElement(&lines[k-1][0][i].R0, xNegOverY[k-1]), + pr.Ext2.MulByElement(&lines[k-1][0][i].R1, yInv[k-1]), + ) + // (ℓ × ℓ) × res + res = pr.Ext12.MulBy012346789(res, prodLines) + } + } else { - res = pr.MulBy01379( - res, + // ℓ × ℓ + prodLines = pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][i].R1, yInv[k]), pr.Ext2.MulByElement(&lines[k][1][i].R0, xNegOverY[k]), pr.Ext2.MulByElement(&lines[k][1][i].R1, yInv[k]), ) + // (ℓ × ℓ) × res + res = pr.Ext12.MulBy012346789(res, prodLines) } } } @@ -292,16 +316,13 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl // lines evaluations at P // and ℓ × ℓ for k := 0; k < n; k++ { - res = pr.MulBy01379( - res, + prodLines = pr.Mul01379By01379( pr.Ext2.MulByElement(&lines[k][0][65].R0, xNegOverY[k]), pr.Ext2.MulByElement(&lines[k][0][65].R1, yInv[k]), - ) - res = pr.MulBy01379( - res, pr.Ext2.MulByElement(&lines[k][1][65].R0, xNegOverY[k]), pr.Ext2.MulByElement(&lines[k][1][65].R1, yInv[k]), ) + res = pr.Ext12.MulBy012346789(res, prodLines) } return res, nil diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index 5fda8c109e..87039e7f9f 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -33,88 +33,6 @@ func randomG1G2Affines() (bn254.G1Affine, bn254.G2Affine) { return p, q } -type MillerLoopCircuit struct { - InG1 G1Affine - InG2 G2Affine - Res GTEl -} - -func (c *MillerLoopCircuit) Define(api frontend.API) error { - pairing, err := NewPairing(api) - if err != nil { - return fmt.Errorf("new pairing: %w", err) - } - res, err := pairing.MillerLoop([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) - if err != nil { - return fmt.Errorf("pair: %w", err) - } - pairing.AssertIsEqual(res, &c.Res) - return nil -} - -func TestMillerLoopTestSolve(t *testing.T) { - assert := test.NewAssert(t) - p, q := randomG1G2Affines() - lines := bn254.PrecomputeLines(q) - res, err := bn254.MillerLoopFixedQ( - []bn254.G1Affine{p}, - [][2][len(bn254.LoopCounter)]bn254.LineEvaluationAff{lines}, - ) - assert.NoError(err) - witness := MillerLoopCircuit{ - InG1: NewG1Affine(p), - InG2: NewG2Affine(q), - Res: NewGTEl(res), - } - err = test.IsSolved(&MillerLoopCircuit{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type MillerLoopAndMulCircuit struct { - Prev GTEl - P G1Affine - Q G2Affine - Current GTEl -} - -func (c *MillerLoopAndMulCircuit) Define(api frontend.API) error { - pairing, err := NewPairing(api) - if err != nil { - return fmt.Errorf("new pairing: %w", err) - } - res, err := pairing.MillerLoopAndMul(&c.P, &c.Q, &c.Prev) - if err != nil { - return fmt.Errorf("pair: %w", err) - } - pairing.AssertIsEqual(res, &c.Current) - return nil - -} - -func TestMillerLoopAndMulTestSolve(t *testing.T) { - assert := test.NewAssert(t) - var prev, curr bn254.GT - prev.SetRandom() - p, q := randomG1G2Affines() - lines := bn254.PrecomputeLines(q) - // need to use ML with precomputed lines. Otherwise, the result will be different - mlres, err := bn254.MillerLoopFixedQ( - []bn254.G1Affine{p}, - [][2][len(bn254.LoopCounter)]bn254.LineEvaluationAff{lines}, - ) - assert.NoError(err) - curr.Mul(&prev, &mlres) - - witness := MillerLoopAndMulCircuit{ - Prev: NewGTEl(prev), - P: NewG1Affine(p), - Q: NewG2Affine(q), - Current: NewGTEl(curr), - } - err = test.IsSolved(&MillerLoopAndMulCircuit{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - type FinalExponentiationIsOne struct { InGt GTEl } From 5203e1de3d57311ac05a8b4c2245ef214c844fc5 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 9 Dec 2024 15:25:57 -0500 Subject: [PATCH 09/22] perf(bn254): some more optims in ML --- std/algebra/emulated/sw_bn254/pairing.go | 86 +++++++++++++++--------- 1 file changed, 56 insertions(+), 30 deletions(-) diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index b982946a92..6235a6b7ce 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -256,17 +256,62 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl xNegOverY[k] = pr.curveF.Neg(xNegOverY[k]) } - res := pr.Ext12.One() var prodLines [10]*baseEl // Compute f_{6x₀+2,Q}(P) // i = 64 - for k := 0; k < n; k++ { - res = pr.MulBy01379( - res, - pr.Ext2.MulByElement(&lines[k][0][0].R0, xNegOverY[k]), - pr.Ext2.MulByElement(&lines[k][0][0].R1, yInv[k]), + // + // k = 0 + c3 := pr.Ext2.MulByElement(&lines[0][0][0].R0, xNegOverY[0]) + c4 := pr.Ext2.MulByElement(&lines[0][0][0].R1, yInv[0]) + nine := big.NewInt(9) + res := >El{ + A0: *pr.curveF.One(), + A1: *pr.curveF.Sub(&c3.A0, pr.curveF.MulConst(&c3.A1, nine)), + A2: *pr.curveF.Zero(), + A3: *pr.curveF.Sub(&c4.A0, pr.curveF.MulConst(&c4.A1, nine)), + A4: *pr.curveF.Zero(), + A5: *pr.curveF.Zero(), + A6: *pr.curveF.Zero(), + A7: c3.A1, + A8: *pr.curveF.Zero(), + A9: c4.A1, + A10: *pr.curveF.Zero(), + A11: *pr.curveF.Zero(), + } + + if n >= 2 { + // k = 1, separately to avoid MulBy034 (res × ℓ) + // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) + // line evaluation at P[1] + // ℓ × res + prodLines = pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[1][0][64].R0, xNegOverY[1]), + pr.Ext2.MulByElement(&lines[1][0][64].R1, yInv[1]), + &fields_bn254.E2{ + A0: *pr.curveF.Add(&res.A1, pr.curveF.MulConst(&res.A7, nine)), + A1: res.A7, + }, + &fields_bn254.E2{ + A0: *pr.curveF.Add(&res.A3, pr.curveF.MulConst(&res.A9, nine)), + A1: res.A9, + }, ) + res = pr.Ext12.MulBy012346789(res, prodLines) + } + + if n >= 3 { + // k >= 3 + for k := 3; k < n; k++ { + // line evaluation at P[k] + // ℓ × res + res = pr.MulBy01379( + res, + pr.Ext2.MulByElement(&lines[k][0][0].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][0].R1, yInv[k]), + ) + + } } for i := 63; i >= 0; i-- { @@ -274,30 +319,11 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl for k := 0; k < n; k++ { if loopCounter[i] == 0 { - // if number of lines is odd, mul last line by res - // works for n=1 as well - if n%2 != 0 { - // ℓ × res - res = pr.MulBy01379( - res, - pr.Ext2.MulByElement(&lines[n-1][0][i].R0, xNegOverY[n-1]), - pr.Ext2.MulByElement(&lines[n-1][0][i].R1, yInv[n-1]), - ) - } - - // mul lines 2-by-2 - for k := 1; k < n; k += 2 { - // ℓ × ℓ - prodLines = pr.Mul01379By01379( - pr.Ext2.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), - pr.Ext2.MulByElement(&lines[k][0][i].R1, yInv[k]), - pr.Ext2.MulByElement(&lines[k-1][0][i].R0, xNegOverY[k-1]), - pr.Ext2.MulByElement(&lines[k-1][0][i].R1, yInv[k-1]), - ) - // (ℓ × ℓ) × res - res = pr.Ext12.MulBy012346789(res, prodLines) - } - + res = pr.MulBy01379( + res, + pr.Ext2.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][i].R1, yInv[k]), + ) } else { // ℓ × ℓ prodLines = pr.Mul01379By01379( From edc115b7b5494c776bb842a55d1dbd0e2b546252 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 9 Dec 2024 16:30:10 -0500 Subject: [PATCH 10/22] feat(sw_bn254): add finalexp --- internal/stats/latest_stats.csv | 4 +- .../emulated/fields_bn254/e12_pairing.go | 57 ++++++++++++ std/algebra/emulated/fields_bn254/e12_test.go | 37 ++++++++ std/algebra/emulated/sw_bn254/pairing.go | 91 +++++++++++++------ std/algebra/emulated/sw_bn254/pairing_test.go | 61 +++++++++++++ 5 files changed, 221 insertions(+), 29 deletions(-) diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index 18197f8970..e134f0c3b0 100644 --- a/internal/stats/latest_stats.csv +++ b/internal/stats/latest_stats.csv @@ -181,14 +181,14 @@ pairing_bls24315,bls24_315,plonk,0,0 pairing_bls24315,bls24_317,plonk,0,0 pairing_bls24315,bw6_761,plonk,0,0 pairing_bls24315,bw6_633,plonk,141249,141249 -pairing_bn254,bn254,groth16,963003,1603091 +pairing_bn254,bn254,groth16,604413,979937 pairing_bn254,bls12_377,groth16,0,0 pairing_bn254,bls12_381,groth16,0,0 pairing_bn254,bls24_315,groth16,0,0 pairing_bn254,bls24_317,groth16,0,0 pairing_bn254,bw6_761,groth16,0,0 pairing_bn254,bw6_633,groth16,0,0 -pairing_bn254,bn254,plonk,3771397,3534755 +pairing_bn254,bn254,plonk,2340019,1988493 pairing_bn254,bls12_377,plonk,0,0 pairing_bn254,bls12_381,plonk,0,0 pairing_bn254,bls24_315,plonk,0,0 diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index a99a1add81..d7f4f45a76 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -11,6 +11,63 @@ func (e Ext12) nSquareGS(z *E12, n int) *E12 { return z } +// Exponentiation by the seed t=4965661367192848881 +func (e Ext12) Expt(x *E12) *E12 { + // ExptTorus computation is derived from the addition chain: + // + // _10 = 2*1 + // _100 = 2*_10 + // _1000 = 2*_100 + // _10000 = 2*_1000 + // _10001 = 1 + _10000 + // _10011 = _10 + _10001 + // _10100 = 1 + _10011 + // _11001 = _1000 + _10001 + // _100010 = 2*_10001 + // _100111 = _10011 + _10100 + // _101001 = _10 + _100111 + // i27 = (_100010 << 6 + _100 + _11001) << 7 + _11001 + // i44 = (i27 << 8 + _101001 + _10) << 6 + _10001 + // i70 = ((i44 << 8 + _101001) << 6 + _101001) << 10 + // return (_100111 + i70) << 6 + _101001 + _1000 + // + // Operations: 62 squares 17 multiplies + // + // Generated by github.com/mmcloughlin/addchain v0.4.0. + + t3 := e.CyclotomicSquareGS(x) + t5 := e.CyclotomicSquareGS(t3) + result := e.CyclotomicSquareGS(t5) + t0 := e.CyclotomicSquareGS(result) + t2 := e.Mul(x, t0) + t0 = e.Mul(t3, t2) + t1 := e.Mul(x, t0) + t4 := e.Mul(result, t2) + t6 := e.CyclotomicSquareGS(t2) + t1 = e.Mul(t0, t1) + t0 = e.Mul(t3, t1) + t6 = e.nSquareGS(t6, 6) + t5 = e.Mul(t5, t6) + t5 = e.Mul(t4, t5) + t5 = e.nSquareGS(t5, 7) + t4 = e.Mul(t4, t5) + t4 = e.nSquareGS(t4, 8) + t4 = e.Mul(t0, t4) + t3 = e.Mul(t3, t4) + t3 = e.nSquareGS(t3, 6) + t2 = e.Mul(t2, t3) + t2 = e.nSquareGS(t2, 8) + t2 = e.Mul(t0, t2) + t2 = e.nSquareGS(t2, 6) + t2 = e.Mul(t0, t2) + t2 = e.nSquareGS(t2, 10) + t1 = e.Mul(t1, t2) + t1 = e.nSquareGS(t1, 6) + t0 = e.Mul(t0, t1) + z := e.Mul(result, t0) + return z +} + // Exponentiation by U=6u+2 where t is the seed u=4965661367192848881 func (e Ext12) ExpByU(x *E12) *E12 { // ExpByU computation is derived from the addition chain: diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index 52610304fa..5cfd3dde74 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -425,6 +425,43 @@ func TestFp12Mul01379By01379(t *testing.T) { } +type e12Expt struct { + A E12 + C E12 `gnark:",public"` +} + +func (circuit *e12Expt) Define(api frontend.API) error { + e := NewExt12(api) + expected := e.Expt(&circuit.A) + e.AssertIsEqual(expected, &circuit.C) + + return nil +} + +func TestFp12Expt(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, c bn254.E12 + _, _ = a.SetRandom() + + // put a in the cyclotomic subgroup + var tmp bn254.E12 + tmp.Conjugate(&a) + a.Inverse(&a) + tmp.Mul(&tmp, &a) + a.FrobeniusSquare(&tmp).Mul(&a, &tmp) + + c.Expt(&a) + witness := e12Expt{ + A: FromE12(&a), + C: FromE12(&c), + } + + err := test.IsSolved(&e12Expt{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + // utils // Mul034By034 multiplication of sparse element (c0,0,0,c3,c4,0) by sparse element (d0,0,0,d3,d4,0) func Mul034By034(d0, d3, d4, c0, c3, c4 *bn254.E2) [5]bn254.E2 { diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 6235a6b7ce..d92f71be79 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -91,6 +91,64 @@ func (pr Pairing) generators() *G2Affine { return pr.g2gen } +// Pair calculates the reduced pairing for a set of points +// ∏ᵢ e(Pᵢ, Qᵢ). +// +// This function doesn't check that the inputs are in the correct subgroups. See AssertIsOnG1 and AssertIsOnG2. +func (pr Pairing) Pair(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { + res, err := pr.MillerLoop(P, Q) + if err != nil { + return nil, fmt.Errorf("miller loop: %w", err) + } + res = pr.FinalExponentiation(res) + 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 2x₀(6x₀²+3x₀+1) +func (pr Pairing) FinalExponentiation(e *GTEl) *GTEl { + // Easy part + // (p⁶-1)(p²+1) + t0 := pr.Ext12.Conjugate(e) + e = pr.Ext12.Inverse(e) + t0 = pr.Ext12.Mul(t0, e) + e = pr.Ext12.FrobeniusSquare(t0) + e = pr.Ext12.Mul(e, t0) + + // Hard part (up to permutation) + // 2x₀(6x₀²+3x₀+1)(p⁴-p²+1)/r + // Duquesne and Ghammam + // https://eprint.iacr.org/2015/192.pdf + // Fuentes et al. (alg. 6) + t0 = pr.Ext12.Expt(e) + t0 = pr.Ext12.Conjugate(t0) + t0 = pr.Ext12.CyclotomicSquareGS(t0) + t1 := pr.Ext12.CyclotomicSquareGS(t0) + t1 = pr.Ext12.Mul(t0, t1) + t2 := pr.Ext12.Expt(t1) + t2 = pr.Ext12.Conjugate(t2) + t3 := pr.Ext12.Conjugate(t1) + t1 = pr.Ext12.Mul(t2, t3) + t3 = pr.Ext12.CyclotomicSquareGS(t2) + t4 := pr.Ext12.Expt(t3) + t4 = pr.Ext12.Mul(t1, t4) + t3 = pr.Ext12.Mul(t0, t4) + t0 = pr.Ext12.Mul(t2, t4) + t0 = pr.Ext12.Mul(e, t0) + t2 = pr.Ext12.Frobenius(t3) + t0 = pr.Ext12.Mul(t2, t0) + t2 = pr.Ext12.FrobeniusSquare(t4) + t0 = pr.Ext12.Mul(t2, t0) + t2 = pr.Ext12.Conjugate(e) + t2 = pr.Ext12.Mul(t2, t3) + t2 = pr.Ext12.FrobeniusCube(t2) + t0 = pr.Ext12.Mul(t2, t0) + + return t0 +} + // PairingCheck calculates the reduced pairing for a set of points and asserts if the result is One // ∏ᵢ e(Pᵢ, Qᵢ) =? 1 // @@ -111,7 +169,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { f = pr.Ext12.FrobeniusSquare(buf) f = pr.Ext12.Mul(f, buf) - // pr.AssertFinalExponentiationIsOne(f) + pr.AssertFinalExponentiationIsOne(f) return nil } @@ -262,8 +320,8 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl // i = 64 // // k = 0 - c3 := pr.Ext2.MulByElement(&lines[0][0][0].R0, xNegOverY[0]) - c4 := pr.Ext2.MulByElement(&lines[0][0][0].R1, yInv[0]) + c3 := pr.Ext2.MulByElement(&lines[0][0][64].R0, xNegOverY[0]) + c4 := pr.Ext2.MulByElement(&lines[0][0][64].R1, yInv[0]) nine := big.NewInt(9) res := >El{ A0: *pr.curveF.One(), @@ -281,34 +339,13 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl } if n >= 2 { - // k = 1, separately to avoid MulBy034 (res × ℓ) - // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) - // line evaluation at P[1] - // ℓ × res - prodLines = pr.Mul01379By01379( - pr.Ext2.MulByElement(&lines[1][0][64].R0, xNegOverY[1]), - pr.Ext2.MulByElement(&lines[1][0][64].R1, yInv[1]), - &fields_bn254.E2{ - A0: *pr.curveF.Add(&res.A1, pr.curveF.MulConst(&res.A7, nine)), - A1: res.A7, - }, - &fields_bn254.E2{ - A0: *pr.curveF.Add(&res.A3, pr.curveF.MulConst(&res.A9, nine)), - A1: res.A9, - }, - ) - res = pr.Ext12.MulBy012346789(res, prodLines) - } - - if n >= 3 { - // k >= 3 - for k := 3; k < n; k++ { + for k := 1; k < n; k++ { // line evaluation at P[k] // ℓ × res res = pr.MulBy01379( res, - pr.Ext2.MulByElement(&lines[k][0][0].R0, xNegOverY[k]), - pr.Ext2.MulByElement(&lines[k][0][0].R1, yInv[k]), + pr.Ext2.MulByElement(&lines[k][0][64].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][64].R1, yInv[k]), ) } diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index 87039e7f9f..476cecf445 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -33,6 +33,34 @@ func randomG1G2Affines() (bn254.G1Affine, bn254.G2Affine) { return p, q } +type FinalExponentiation struct { + InGt GTEl + Res GTEl +} + +func (c *FinalExponentiation) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + expected := pairing.FinalExponentiation(&c.InGt) + pairing.AssertIsEqual(expected, &c.Res) + return nil +} + +func TestFinalExponentiationTestSolve(t *testing.T) { + assert := test.NewAssert(t) + var gt bn254.GT + gt.SetRandom() + res := bn254.FinalExponentiation(>) + witness := FinalExponentiation{ + InGt: NewGTEl(gt), + Res: NewGTEl(res), + } + err := test.IsSolved(&FinalExponentiation{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + type FinalExponentiationIsOne struct { InGt GTEl } @@ -66,6 +94,39 @@ func TestFinalExponentiationIsOneTestSolve(t *testing.T) { assert.NoError(err) } +type PairCircuit struct { + InG1 G1Affine + InG2 G2Affine + Res GTEl +} + +func (c *PairCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) + if err != nil { + return fmt.Errorf("pair: %w", err) + } + pairing.AssertIsEqual(res, &c.Res) + return nil +} + +func TestPairTestSolve(t *testing.T) { + assert := test.NewAssert(t) + p, q := randomG1G2Affines() + res, err := bn254.Pair([]bn254.G1Affine{p}, []bn254.G2Affine{q}) + assert.NoError(err) + witness := PairCircuit{ + InG1: NewG1Affine(p), + InG2: NewG2Affine(q), + Res: NewGTEl(res), + } + err = test.IsSolved(&PairCircuit{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + type PairingCheckCircuit struct { In1G1 G1Affine In2G1 G1Affine From 1eade4a42edb707e7212916a8c3935ca7f8835d9 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 9 Dec 2024 16:52:36 -0500 Subject: [PATCH 11/22] fix: golinter --- std/evmprecompiles/08-bnpairing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/evmprecompiles/08-bnpairing.go b/std/evmprecompiles/08-bnpairing.go index 4a31e1f058..25ab396b0c 100644 --- a/std/evmprecompiles/08-bnpairing.go +++ b/std/evmprecompiles/08-bnpairing.go @@ -47,7 +47,7 @@ func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { } // 3- Check that ∏ᵢ e(Pᵢ, Qᵢ) == 1 - ml := pair.One() + ml := pair.Ext12.One() for i := 0; i < n-1; i++ { // fixed circuit 1 ml, err = pair.MillerLoopAndMul(P[i], Q[i], ml) From 4fdc8efc0f33847cc4e9cc03e2b3e71bd1279f55 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 9 Dec 2024 17:03:45 -0500 Subject: [PATCH 12/22] fix: golinter --- std/algebra/emulated/sw_bn254/pairing_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index 476cecf445..9d0afe4229 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -87,6 +87,7 @@ func TestFinalExponentiationIsOneTestSolve(t *testing.T) { []bn254.G1Affine{p1, p2}, []bn254.G2Affine{q1, q2}, ) + assert.NoError(err) witness := FinalExponentiationIsOne{ InGt: NewGTEl(ml), } From dcd078edeb6aeb7d453b50f352cc09f22af10951 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Dec 2024 10:39:51 -0500 Subject: [PATCH 13/22] perf: implement GS cyclo sq --- std/algebra/emulated/fields_bn254/e12.go | 334 +++++++++++------- .../emulated/fields_bn254/e12_pairing.go | 37 +- std/algebra/emulated/sw_bn254/pairing.go | 9 - 3 files changed, 231 insertions(+), 149 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 1af7b9fe34..54a2548479 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -464,8 +464,92 @@ func (e Ext12) squareDirect(a *E12) *E12 { // Granger-Scott's cyclotomic square // https://eprint.iacr.org/2009/565.pdf, 3.2 func (e Ext12) CyclotomicSquareGS(x *E12) *E12 { - // TODO: implement GS - return e.squareDirect(x) + // x=(x0,x2,x4,x1,x3,x5) in E6 + // cyclosquare(x) = 3*x4²*u + 3*x0² - 2*x0, + // 6*x1*x5*u + 2*x3, + // 3*x2²*u + 3*x3² - 2*x1, + // 6*x0*x4 + 2*x4, + // 3*x5²*u + 3*x1² - 2*x2, + // 6*x2*x3 + 2*x5, + nine := big.NewInt(9) + x000 := e.fp.Add(&x.A0, e.fp.MulConst(&x.A6, nine)) + x00 := E2{A0: *x000, A1: x.A6} + x010 := e.fp.Add(&x.A2, e.fp.MulConst(&x.A8, nine)) + x01 := E2{A0: *x010, A1: x.A8} + x020 := e.fp.Add(&x.A4, e.fp.MulConst(&x.A10, nine)) + x02 := E2{A0: *x020, A1: x.A10} + x100 := e.fp.Add(&x.A1, e.fp.MulConst(&x.A7, nine)) + x10 := E2{A0: *x100, A1: x.A7} + x110 := e.fp.Add(&x.A3, e.fp.MulConst(&x.A9, nine)) + x11 := E2{A0: *x110, A1: x.A9} + x120 := e.fp.Add(&x.A5, e.fp.MulConst(&x.A11, nine)) + x12 := E2{A0: *x120, A1: x.A11} + + t0 := e.Ext2.Square(&x11) + t1 := e.Ext2.Square(&x00) + t6 := e.Ext2.Add(&x11, &x00) + t6 = e.Ext2.Square(t6) + t6 = e.Ext2.Sub(t6, t0) + t6 = e.Ext2.Sub(t6, t1) + t2 := e.Ext2.Square(&x02) + t3 := e.Ext2.Square(&x10) + t7 := e.Ext2.Add(&x02, &x10) + t7 = e.Ext2.Square(t7) + t7 = e.Ext2.Sub(t7, t2) + t7 = e.Ext2.Sub(t7, t3) + t4 := e.Ext2.Square(&x12) + t5 := e.Ext2.Square(&x01) + t8 := e.Ext2.Add(&x12, &x01) + 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, &x00) + z00 = e.Ext2.Double(z00) + z00 = e.Ext2.Add(z00, t0) + z01 := e.Ext2.Sub(t2, &x01) + z01 = e.Ext2.Double(z01) + z01 = e.Ext2.Add(z01, t2) + z02 := e.Ext2.Sub(t4, &x02) + z02 = e.Ext2.Double(z02) + z02 = e.Ext2.Add(z02, t4) + z10 := e.Ext2.Add(t8, &x10) + z10 = e.Ext2.Double(z10) + z10 = e.Ext2.Add(z10, t8) + z11 := e.Ext2.Add(t6, &x11) + z11 = e.Ext2.Double(z11) + z11 = e.Ext2.Add(z11, t6) + z12 := e.Ext2.Add(t7, &x12) + z12 = e.Ext2.Double(z12) + z12 = e.Ext2.Add(z12, t7) + + A0 := e.fp.Sub(&z00.A0, e.fp.MulConst(&z00.A1, nine)) + A1 := e.fp.Sub(&z10.A0, e.fp.MulConst(&z10.A1, nine)) + A2 := e.fp.Sub(&z01.A0, e.fp.MulConst(&z01.A1, nine)) + A3 := e.fp.Sub(&z11.A0, e.fp.MulConst(&z11.A1, nine)) + A4 := e.fp.Sub(&z02.A0, e.fp.MulConst(&z02.A1, nine)) + A5 := e.fp.Sub(&z12.A0, e.fp.MulConst(&z12.A1, nine)) + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: z00.A1, + A7: z10.A1, + A8: z01.A1, + A9: z11.A1, + A10: z02.A1, + A11: z12.A1, + } } func (e Ext12) Inverse(x *E12) *E12 { @@ -630,129 +714,6 @@ func (e Ext12) Copy(x *E12) *E12 { } } -// tower to direct extension conversion -func FromE12(a *bn254.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 - 9 * a001 - // A1 = a100 - 9 * a101 - // A2 = a010 - 9 * a011 - // A3 = a110 - 9 * a111 - // A4 = a020 - 9 * a021 - // A5 = a120 - 9 * a121 - // A6 = a001 - // A7 = a101 - // A8 = a011 - // A9 = a111 - // A10 = a021 - // A11 = a121 - - var c0, c1, c2, c3, c4, c5, t fp_bn.Element - t.SetUint64(9).Mul(&t, &a.C0.B0.A1) - c0.Sub(&a.C0.B0.A0, &t) - t.SetUint64(9).Mul(&t, &a.C1.B0.A1) - c1.Sub(&a.C1.B0.A0, &t) - t.SetUint64(9).Mul(&t, &a.C0.B1.A1) - c2.Sub(&a.C0.B1.A0, &t) - t.SetUint64(9).Mul(&t, &a.C1.B1.A1) - c3.Sub(&a.C1.B1.A0, &t) - t.SetUint64(9).Mul(&t, &a.C0.B2.A1) - c4.Sub(&a.C0.B2.A0, &t) - t.SetUint64(9).Mul(&t, &a.C1.B2.A1) - c5.Sub(&a.C1.B2.A0, &t) - - return E12{ - A0: emulated.ValueOf[emulated.BN254Fp](c0), - A1: emulated.ValueOf[emulated.BN254Fp](c1), - A2: emulated.ValueOf[emulated.BN254Fp](c2), - A3: emulated.ValueOf[emulated.BN254Fp](c3), - A4: emulated.ValueOf[emulated.BN254Fp](c4), - A5: emulated.ValueOf[emulated.BN254Fp](c5), - A6: emulated.ValueOf[emulated.BN254Fp](a.C0.B0.A1), - A7: emulated.ValueOf[emulated.BN254Fp](a.C1.B0.A1), - A8: emulated.ValueOf[emulated.BN254Fp](a.C0.B1.A1), - A9: emulated.ValueOf[emulated.BN254Fp](a.C1.B1.A1), - A10: emulated.ValueOf[emulated.BN254Fp](a.C0.B2.A1), - A11: emulated.ValueOf[emulated.BN254Fp](a.C1.B2.A1), - } -} - -func (e Ext12) e12RoundTrip(a *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 - - // a000 = A0 + 9 * A6 - // a001 = A6 - // a010 = A2 + 9 * A8 - // a011 = A8 - // a020 = A4 + 9 * A10 - // a021 = A10 - // a100 = A1 + 9 * A7 - // a101 = A7 - // a110 = A3 + 9 * A9 - // a111 = A9 - // a120 = A5 + 9 * A11 - // a121 = A11 - nine := big.NewInt(9) - a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) - a001 := a.A6 - a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) - a011 := a.A8 - a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) - a021 := a.A10 - a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) - a101 := a.A7 - a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) - a111 := a.A9 - a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) - a121 := a.A11 - - // A0 = a000 - 9 * a001 - // A1 = a100 - 9 * a101 - // A2 = a010 - 9 * a011 - // A3 = a110 - 9 * a111 - // A4 = a020 - 9 * a021 - // A5 = a120 - 9 * a121 - // A6 = a001 - // A7 = a101 - // A8 = a011 - // A9 = a111 - // A10 = a021 - // A11 = a121 - A0 := e.fp.Sub(a000, e.fp.MulConst(&a001, nine)) - A1 := e.fp.Sub(a100, e.fp.MulConst(&a101, nine)) - A2 := e.fp.Sub(a010, e.fp.MulConst(&a011, nine)) - A3 := e.fp.Sub(a110, e.fp.MulConst(&a111, nine)) - A4 := e.fp.Sub(a020, e.fp.MulConst(&a021, nine)) - A5 := e.fp.Sub(a120, e.fp.MulConst(&a121, nine)) - A6 := a001 - A7 := a101 - A8 := a011 - A9 := a111 - A10 := a021 - A11 := a121 - - return &E12{ - 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) Frobenius(a *E12) *E12 { nine := big.NewInt(9) a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) @@ -884,3 +845,126 @@ func (e Ext12) FrobeniusCube(a *E12) *E12 { A11: t5.A1, } } + +// tower to direct extension conversion +func FromE12(a *bn254.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 - 9 * a001 + // A1 = a100 - 9 * a101 + // A2 = a010 - 9 * a011 + // A3 = a110 - 9 * a111 + // A4 = a020 - 9 * a021 + // A5 = a120 - 9 * a121 + // A6 = a001 + // A7 = a101 + // A8 = a011 + // A9 = a111 + // A10 = a021 + // A11 = a121 + + var c0, c1, c2, c3, c4, c5, t fp_bn.Element + t.SetUint64(9).Mul(&t, &a.C0.B0.A1) + c0.Sub(&a.C0.B0.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B0.A1) + c1.Sub(&a.C1.B0.A0, &t) + t.SetUint64(9).Mul(&t, &a.C0.B1.A1) + c2.Sub(&a.C0.B1.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B1.A1) + c3.Sub(&a.C1.B1.A0, &t) + t.SetUint64(9).Mul(&t, &a.C0.B2.A1) + c4.Sub(&a.C0.B2.A0, &t) + t.SetUint64(9).Mul(&t, &a.C1.B2.A1) + c5.Sub(&a.C1.B2.A0, &t) + + return E12{ + A0: emulated.ValueOf[emulated.BN254Fp](c0), + A1: emulated.ValueOf[emulated.BN254Fp](c1), + A2: emulated.ValueOf[emulated.BN254Fp](c2), + A3: emulated.ValueOf[emulated.BN254Fp](c3), + A4: emulated.ValueOf[emulated.BN254Fp](c4), + A5: emulated.ValueOf[emulated.BN254Fp](c5), + A6: emulated.ValueOf[emulated.BN254Fp](a.C0.B0.A1), + A7: emulated.ValueOf[emulated.BN254Fp](a.C1.B0.A1), + A8: emulated.ValueOf[emulated.BN254Fp](a.C0.B1.A1), + A9: emulated.ValueOf[emulated.BN254Fp](a.C1.B1.A1), + A10: emulated.ValueOf[emulated.BN254Fp](a.C0.B2.A1), + A11: emulated.ValueOf[emulated.BN254Fp](a.C1.B2.A1), + } +} + +func (e Ext12) e12RoundTrip(a *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 + + // a000 = A0 + 9 * A6 + // a001 = A6 + // a010 = A2 + 9 * A8 + // a011 = A8 + // a020 = A4 + 9 * A10 + // a021 = A10 + // a100 = A1 + 9 * A7 + // a101 = A7 + // a110 = A3 + 9 * A9 + // a111 = A9 + // a120 = A5 + 9 * A11 + // a121 = A11 + nine := big.NewInt(9) + a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) + a001 := a.A6 + a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) + a011 := a.A8 + a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) + a021 := a.A10 + a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) + a101 := a.A7 + a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) + a111 := a.A9 + a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) + a121 := a.A11 + + // A0 = a000 - 9 * a001 + // A1 = a100 - 9 * a101 + // A2 = a010 - 9 * a011 + // A3 = a110 - 9 * a111 + // A4 = a020 - 9 * a021 + // A5 = a120 - 9 * a121 + // A6 = a001 + // A7 = a101 + // A8 = a011 + // A9 = a111 + // A10 = a021 + // A11 = a121 + A0 := e.fp.Sub(a000, e.fp.MulConst(&a001, nine)) + A1 := e.fp.Sub(a100, e.fp.MulConst(&a101, nine)) + A2 := e.fp.Sub(a010, e.fp.MulConst(&a011, nine)) + A3 := e.fp.Sub(a110, e.fp.MulConst(&a111, nine)) + A4 := e.fp.Sub(a020, e.fp.MulConst(&a021, nine)) + A5 := e.fp.Sub(a120, e.fp.MulConst(&a121, nine)) + A6 := a001 + A7 := a101 + A8 := a011 + A9 := a111 + A10 := a021 + A11 := a121 + + return &E12{ + A0: *A0, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: A6, + A7: A7, + A8: A8, + A9: A9, + A10: A10, + A11: A11, + } +} diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index d7f4f45a76..f00ff92156 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -4,6 +4,13 @@ import ( "math/big" ) +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.CyclotomicSquareGS(z) @@ -96,37 +103,37 @@ func (e Ext12) ExpByU(x *E12) *E12 { z = e.Mul(x, t1) t2 := e.Square(t1) t1 = e.Mul(t0, t2) - t2 = e.nSquareGS(t2, 3) + t2 = e.nSquare(t2, 3) t2 = e.Mul(z, t2) - t2 = e.nSquareGS(t2, 2) + t2 = e.nSquare(t2, 2) t2 = e.Mul(x, t2) - t2 = e.nSquareGS(t2, 5) + t2 = e.nSquare(t2, 5) t2 = e.Mul(t1, t2) - t2 = e.nSquareGS(t2, 3) + t2 = e.nSquare(t2, 3) t2 = e.Mul(x, t2) - t2 = e.nSquareGS(t2, 4) + t2 = e.nSquare(t2, 4) t2 = e.Mul(z, t2) - t2 = e.nSquareGS(t2, 9) + t2 = e.nSquare(t2, 9) t2 = e.Mul(z, t2) - t2 = e.nSquareGS(t2, 4) + t2 = e.nSquare(t2, 4) t2 = e.Mul(t0, t2) - t2 = e.nSquareGS(t2, 5) + t2 = e.nSquare(t2, 5) t1 = e.Mul(t1, t2) t1 = e.Square(t1) t1 = e.Mul(x, t1) - t1 = e.nSquareGS(t1, 5) + t1 = e.nSquare(t1, 5) t1 = e.Mul(z, t1) - t1 = e.nSquareGS(t1, 3) + t1 = e.nSquare(t1, 3) t0 = e.Mul(t0, t1) - t0 = e.nSquareGS(t0, 6) + t0 = e.nSquare(t0, 6) t0 = e.Mul(z, t0) - t0 = e.nSquareGS(t0, 4) + t0 = e.nSquare(t0, 4) z = e.Mul(z, t0) - z = e.nSquareGS(z, 2) + z = e.nSquare(z, 2) 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) return z } diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 9f2f5b4909..fdfedafea3 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -159,15 +159,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.Ext12.Conjugate(f) - buf = pr.Ext12.DivUnchecked(buf, f) - f = pr.Ext12.FrobeniusSquare(buf) - f = pr.Ext12.Mul(f, buf) pr.AssertFinalExponentiationIsOne(f) From b5563692adb278067751be3c6ea0883afedc3736 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Dec 2024 17:45:13 -0500 Subject: [PATCH 14/22] perf: optimize Granger-Scott square with Eval --- internal/stats/latest_stats.csv | 4 +- std/algebra/emulated/fields_bn254/e12.go | 103 ++++++------------ std/algebra/emulated/fields_bn254/e12_test.go | 37 +++++++ 3 files changed, 72 insertions(+), 72 deletions(-) diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index e134f0c3b0..b46def2697 100644 --- a/internal/stats/latest_stats.csv +++ b/internal/stats/latest_stats.csv @@ -181,14 +181,14 @@ pairing_bls24315,bls24_315,plonk,0,0 pairing_bls24315,bls24_317,plonk,0,0 pairing_bls24315,bw6_761,plonk,0,0 pairing_bls24315,bw6_633,plonk,141249,141249 -pairing_bn254,bn254,groth16,604413,979937 +pairing_bn254,bn254,groth16,610241,1000885 pairing_bn254,bls12_377,groth16,0,0 pairing_bn254,bls12_381,groth16,0,0 pairing_bn254,bls24_315,groth16,0,0 pairing_bn254,bls24_317,groth16,0,0 pairing_bn254,bw6_761,groth16,0,0 pairing_bn254,bw6_633,groth16,0,0 -pairing_bn254,bn254,plonk,2340019,1988493 +pairing_bn254,bn254,plonk,2347160,2059138 pairing_bn254,bls12_377,plonk,0,0 pairing_bn254,bls12_381,plonk,0,0 pairing_bn254,bls24_315,plonk,0,0 diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 54a2548479..3ca531f492 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -464,77 +464,40 @@ func (e Ext12) squareDirect(a *E12) *E12 { // Granger-Scott's cyclotomic square // https://eprint.iacr.org/2009/565.pdf, 3.2 func (e Ext12) CyclotomicSquareGS(x *E12) *E12 { - // x=(x0,x2,x4,x1,x3,x5) in E6 - // cyclosquare(x) = 3*x4²*u + 3*x0² - 2*x0, - // 6*x1*x5*u + 2*x3, - // 3*x2²*u + 3*x3² - 2*x1, - // 6*x0*x4 + 2*x4, - // 3*x5²*u + 3*x1² - 2*x2, - // 6*x2*x3 + 2*x5, nine := big.NewInt(9) x000 := e.fp.Add(&x.A0, e.fp.MulConst(&x.A6, nine)) - x00 := E2{A0: *x000, A1: x.A6} + x001 := &x.A6 x010 := e.fp.Add(&x.A2, e.fp.MulConst(&x.A8, nine)) - x01 := E2{A0: *x010, A1: x.A8} + x011 := &x.A8 x020 := e.fp.Add(&x.A4, e.fp.MulConst(&x.A10, nine)) - x02 := E2{A0: *x020, A1: x.A10} + x021 := &x.A10 x100 := e.fp.Add(&x.A1, e.fp.MulConst(&x.A7, nine)) - x10 := E2{A0: *x100, A1: x.A7} + x101 := &x.A7 x110 := e.fp.Add(&x.A3, e.fp.MulConst(&x.A9, nine)) - x11 := E2{A0: *x110, A1: x.A9} + x111 := &x.A9 x120 := e.fp.Add(&x.A5, e.fp.MulConst(&x.A11, nine)) - x12 := E2{A0: *x120, A1: x.A11} - - t0 := e.Ext2.Square(&x11) - t1 := e.Ext2.Square(&x00) - t6 := e.Ext2.Add(&x11, &x00) - t6 = e.Ext2.Square(t6) - t6 = e.Ext2.Sub(t6, t0) - t6 = e.Ext2.Sub(t6, t1) - t2 := e.Ext2.Square(&x02) - t3 := e.Ext2.Square(&x10) - t7 := e.Ext2.Add(&x02, &x10) - t7 = e.Ext2.Square(t7) - t7 = e.Ext2.Sub(t7, t2) - t7 = e.Ext2.Sub(t7, t3) - t4 := e.Ext2.Square(&x12) - t5 := e.Ext2.Square(&x01) - t8 := e.Ext2.Add(&x12, &x01) - 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, &x00) - z00 = e.Ext2.Double(z00) - z00 = e.Ext2.Add(z00, t0) - z01 := e.Ext2.Sub(t2, &x01) - z01 = e.Ext2.Double(z01) - z01 = e.Ext2.Add(z01, t2) - z02 := e.Ext2.Sub(t4, &x02) - z02 = e.Ext2.Double(z02) - z02 = e.Ext2.Add(z02, t4) - z10 := e.Ext2.Add(t8, &x10) - z10 = e.Ext2.Double(z10) - z10 = e.Ext2.Add(z10, t8) - z11 := e.Ext2.Add(t6, &x11) - z11 = e.Ext2.Double(z11) - z11 = e.Ext2.Add(z11, t6) - z12 := e.Ext2.Add(t7, &x12) - z12 = e.Ext2.Double(z12) - z12 = e.Ext2.Add(z12, t7) - - A0 := e.fp.Sub(&z00.A0, e.fp.MulConst(&z00.A1, nine)) - A1 := e.fp.Sub(&z10.A0, e.fp.MulConst(&z10.A1, nine)) - A2 := e.fp.Sub(&z01.A0, e.fp.MulConst(&z01.A1, nine)) - A3 := e.fp.Sub(&z11.A0, e.fp.MulConst(&z11.A1, nine)) - A4 := e.fp.Sub(&z02.A0, e.fp.MulConst(&z02.A1, nine)) - A5 := e.fp.Sub(&z12.A0, e.fp.MulConst(&z12.A1, nine)) + x121 := &x.A11 + + mone := e.fp.NewElement(-1) + z000 := e.fp.Eval([][]*baseEl{{x110, x110}, {mone, x111, x111}, {mone, x110, x111}, {x000, x000}, {mone, x001, x001}, {mone, x000}}, []int{27, 27, 6, 3, 3, 2}) + z001 := e.fp.Eval([][]*baseEl{{x110, x110}, {mone, x111, x111}, {x110, x111}, {x000, x001}, {mone, x001}}, []int{3, 3, 54, 6, 2}) + z020 := e.fp.Eval([][]*baseEl{{x020, x020}, {mone, x021, x021}, {mone, x020, x021}, {x100, x100}, {mone, x101, x101}, {mone, x010}}, []int{27, 27, 6, 3, 3, 2}) + z021 := e.fp.Eval([][]*baseEl{{x020, x020}, {mone, x021, x021}, {x020, x021}, {x100, x101}, {mone, x011}}, []int{3, 3, 54, 6, 2}) + z110 := e.fp.Eval([][]*baseEl{{x120, x120}, {mone, x121, x121}, {mone, x120, x121}, {x010, x010}, {mone, x011, x011}, {mone, x020}}, []int{27, 27, 6, 3, 3, 2}) + z111 := e.fp.Eval([][]*baseEl{{x120, x120}, {mone, x121, x121}, {x120, x121}, {x010, x011}, {mone, x021}}, []int{3, 3, 54, 6, 2}) + z010 := e.fp.Eval([][]*baseEl{{x010, x120}, {mone, x011, x121}, {mone, x010, x121}, {mone, x011, x120}, {x100}}, []int{54, 54, 6, 6, 2}) + z011 := e.fp.Eval([][]*baseEl{{x010, x120}, {mone, x011, x121}, {x010, x121}, {x011, x120}, {x101}}, []int{6, 6, 54, 54, 2}) + z100 := e.fp.Eval([][]*baseEl{{x000, x110}, {mone, x001, x111}, {x110}}, []int{6, 6, 2}) + z101 := e.fp.Eval([][]*baseEl{{x000, x111}, {x001, x110}, {x111}}, []int{6, 6, 2}) + z120 := e.fp.Eval([][]*baseEl{{x020, x100}, {mone, x021, x101}, {x120}}, []int{6, 6, 2}) + z121 := e.fp.Eval([][]*baseEl{{x020, x101}, {x021, x100}, {x121}}, []int{6, 6, 2}) + + A0 := e.fp.Sub(z000, e.fp.MulConst(z001, nine)) + A1 := e.fp.Sub(z010, e.fp.MulConst(z011, nine)) + A2 := e.fp.Sub(z020, e.fp.MulConst(z021, nine)) + A3 := e.fp.Sub(z100, e.fp.MulConst(z101, nine)) + A4 := e.fp.Sub(z110, e.fp.MulConst(z111, nine)) + A5 := e.fp.Sub(z120, e.fp.MulConst(z121, nine)) return &E12{ A0: *A0, @@ -543,12 +506,12 @@ func (e Ext12) CyclotomicSquareGS(x *E12) *E12 { A3: *A3, A4: *A4, A5: *A5, - A6: z00.A1, - A7: z10.A1, - A8: z01.A1, - A9: z11.A1, - A10: z02.A1, - A11: z12.A1, + A6: *z001, + A7: *z011, + A8: *z021, + A9: *z101, + A10: *z111, + A11: *z121, } } diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index 5cfd3dde74..c0e890dafd 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -189,6 +189,43 @@ func TestSquareFp12(t *testing.T) { } +type e12SquareGS struct { + A, C E12 +} + +func (circuit *e12SquareGS) Define(api frontend.API) error { + e := NewExt12(api) + expected := e.CyclotomicSquareGS(&circuit.A) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestSquareGSFp12(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, c bn254.E12 + _, _ = a.SetRandom() + + // put a in the cyclotomic subgroup + var tmp bn254.E12 + tmp.Conjugate(&a) + a.Inverse(&a) + tmp.Mul(&tmp, &a) + a.FrobeniusSquare(&tmp).Mul(&a, &tmp) + + c.Square(&a) + + witness := e12SquareGS{ + A: FromE12(&a), + C: FromE12(&c), + } + + err := test.IsSolved(&e12SquareGS{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + +} + type e12Conjugate struct { A E12 C E12 `gnark:",public"` From ee0a3ecb1ba8b77a032a5b331480c7f3565714b2 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Dec 2024 20:47:20 -0500 Subject: [PATCH 15/22] refactor: use ToTower and FromTower methods --- std/algebra/emulated/fields_bn254/e12.go | 127 +++++++----------- .../emulated/fields_bn254/e12_pairing.go | 42 ++---- std/algebra/emulated/fields_bn254/e12_test.go | 3 +- std/algebra/emulated/sw_bn254/pairing.go | 46 +------ 4 files changed, 69 insertions(+), 149 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 3ca531f492..efb80aecac 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -464,55 +464,25 @@ func (e Ext12) squareDirect(a *E12) *E12 { // Granger-Scott's cyclotomic square // https://eprint.iacr.org/2009/565.pdf, 3.2 func (e Ext12) CyclotomicSquareGS(x *E12) *E12 { - nine := big.NewInt(9) - x000 := e.fp.Add(&x.A0, e.fp.MulConst(&x.A6, nine)) - x001 := &x.A6 - x010 := e.fp.Add(&x.A2, e.fp.MulConst(&x.A8, nine)) - x011 := &x.A8 - x020 := e.fp.Add(&x.A4, e.fp.MulConst(&x.A10, nine)) - x021 := &x.A10 - x100 := e.fp.Add(&x.A1, e.fp.MulConst(&x.A7, nine)) - x101 := &x.A7 - x110 := e.fp.Add(&x.A3, e.fp.MulConst(&x.A9, nine)) - x111 := &x.A9 - x120 := e.fp.Add(&x.A5, e.fp.MulConst(&x.A11, nine)) - x121 := &x.A11 + tower := e.ToTower(x) mone := e.fp.NewElement(-1) - z000 := e.fp.Eval([][]*baseEl{{x110, x110}, {mone, x111, x111}, {mone, x110, x111}, {x000, x000}, {mone, x001, x001}, {mone, x000}}, []int{27, 27, 6, 3, 3, 2}) - z001 := e.fp.Eval([][]*baseEl{{x110, x110}, {mone, x111, x111}, {x110, x111}, {x000, x001}, {mone, x001}}, []int{3, 3, 54, 6, 2}) - z020 := e.fp.Eval([][]*baseEl{{x020, x020}, {mone, x021, x021}, {mone, x020, x021}, {x100, x100}, {mone, x101, x101}, {mone, x010}}, []int{27, 27, 6, 3, 3, 2}) - z021 := e.fp.Eval([][]*baseEl{{x020, x020}, {mone, x021, x021}, {x020, x021}, {x100, x101}, {mone, x011}}, []int{3, 3, 54, 6, 2}) - z110 := e.fp.Eval([][]*baseEl{{x120, x120}, {mone, x121, x121}, {mone, x120, x121}, {x010, x010}, {mone, x011, x011}, {mone, x020}}, []int{27, 27, 6, 3, 3, 2}) - z111 := e.fp.Eval([][]*baseEl{{x120, x120}, {mone, x121, x121}, {x120, x121}, {x010, x011}, {mone, x021}}, []int{3, 3, 54, 6, 2}) - z010 := e.fp.Eval([][]*baseEl{{x010, x120}, {mone, x011, x121}, {mone, x010, x121}, {mone, x011, x120}, {x100}}, []int{54, 54, 6, 6, 2}) - z011 := e.fp.Eval([][]*baseEl{{x010, x120}, {mone, x011, x121}, {x010, x121}, {x011, x120}, {x101}}, []int{6, 6, 54, 54, 2}) - z100 := e.fp.Eval([][]*baseEl{{x000, x110}, {mone, x001, x111}, {x110}}, []int{6, 6, 2}) - z101 := e.fp.Eval([][]*baseEl{{x000, x111}, {x001, x110}, {x111}}, []int{6, 6, 2}) - z120 := e.fp.Eval([][]*baseEl{{x020, x100}, {mone, x021, x101}, {x120}}, []int{6, 6, 2}) - z121 := e.fp.Eval([][]*baseEl{{x020, x101}, {x021, x100}, {x121}}, []int{6, 6, 2}) - - A0 := e.fp.Sub(z000, e.fp.MulConst(z001, nine)) - A1 := e.fp.Sub(z010, e.fp.MulConst(z011, nine)) - A2 := e.fp.Sub(z020, e.fp.MulConst(z021, nine)) - A3 := e.fp.Sub(z100, e.fp.MulConst(z101, nine)) - A4 := e.fp.Sub(z110, e.fp.MulConst(z111, nine)) - A5 := e.fp.Sub(z120, e.fp.MulConst(z121, nine)) - - return &E12{ - A0: *A0, - A1: *A1, - A2: *A2, - A3: *A3, - A4: *A4, - A5: *A5, - A6: *z001, - A7: *z011, - A8: *z021, - A9: *z101, - A10: *z111, - A11: *z121, - } + 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{27, 27, 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, 54, 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{27, 27, 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, 54, 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{27, 27, 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, 54, 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{54, 54, 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, 54, 54, 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) Inverse(x *E12) *E12 { @@ -859,12 +829,13 @@ func FromE12(a *bn254.E12) E12 { } } -func (e Ext12) e12RoundTrip(a *E12) *E12 { +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: - // a000 a001 a010 a011 a020 a021 a100 a101 a110 a111 a120 a121 - // a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 - + // + // 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 + 9 * A6 // a001 = A6 // a010 = A2 + 9 * A8 @@ -879,18 +850,29 @@ func (e Ext12) e12RoundTrip(a *E12) *E12 { // a121 = A11 nine := big.NewInt(9) a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) - a001 := a.A6 + a001 := &a.A6 a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) - a011 := a.A8 + a011 := &a.A8 a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) - a021 := a.A10 + a021 := &a.A10 a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) - a101 := a.A7 + a101 := &a.A7 a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) - a111 := a.A9 + a111 := &a.A9 a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) - a121 := 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 - 9 * a001 // A1 = a100 - 9 * a101 // A2 = a010 - 9 * a011 @@ -903,18 +885,13 @@ func (e Ext12) e12RoundTrip(a *E12) *E12 { // A9 = a111 // A10 = a021 // A11 = a121 - A0 := e.fp.Sub(a000, e.fp.MulConst(&a001, nine)) - A1 := e.fp.Sub(a100, e.fp.MulConst(&a101, nine)) - A2 := e.fp.Sub(a010, e.fp.MulConst(&a011, nine)) - A3 := e.fp.Sub(a110, e.fp.MulConst(&a111, nine)) - A4 := e.fp.Sub(a020, e.fp.MulConst(&a021, nine)) - A5 := e.fp.Sub(a120, e.fp.MulConst(&a121, nine)) - A6 := a001 - A7 := a101 - A8 := a011 - A9 := a111 - A10 := a021 - A11 := a121 + nine := big.NewInt(9) + A0 := e.fp.Sub(tower[0], e.fp.MulConst(tower[1], nine)) + A1 := e.fp.Sub(tower[6], e.fp.MulConst(tower[7], nine)) + A2 := e.fp.Sub(tower[2], e.fp.MulConst(tower[3], nine)) + A3 := e.fp.Sub(tower[8], e.fp.MulConst(tower[9], nine)) + A4 := e.fp.Sub(tower[4], e.fp.MulConst(tower[5], nine)) + A5 := e.fp.Sub(tower[10], e.fp.MulConst(tower[11], nine)) return &E12{ A0: *A0, @@ -923,11 +900,11 @@ func (e Ext12) e12RoundTrip(a *E12) *E12 { A3: *A3, A4: *A4, A5: *A5, - A6: A6, - A7: A7, - A8: A8, - A9: A9, - A10: A10, - A11: A11, + A6: *tower[1], + A7: *tower[7], + A8: *tower[3], + A9: *tower[9], + A10: *tower[5], + A11: *tower[11], } } diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index f00ff92156..2bdf669275 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -346,40 +346,16 @@ func (e *Ext12) MulBy012346789(a *E12, b [10]*baseEl) *E12 { // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf func (e Ext12) AssertFinalExponentiationIsOne(a *E12) { - nine := big.NewInt(9) - a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) - a001 := &a.A6 - a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) - a011 := &a.A8 - a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) - a021 := &a.A10 - a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) - a101 := &a.A7 - a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) - a111 := &a.A9 - a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) - a121 := &a.A11 - - res, err := e.fp.NewHint(finalExpHint, 24, a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) + tower := e.ToTower(a) + + res, err := e.fp.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) } - residueWitness := E12{ - A0: *e.fp.Sub(res[0], e.fp.MulConst(res[1], nine)), - A1: *e.fp.Sub(res[6], e.fp.MulConst(res[7], nine)), - A2: *e.fp.Sub(res[2], e.fp.MulConst(res[3], nine)), - A3: *e.fp.Sub(res[8], e.fp.MulConst(res[9], nine)), - A4: *e.fp.Sub(res[4], e.fp.MulConst(res[5], nine)), - A5: *e.fp.Sub(res[10], e.fp.MulConst(res[11], nine)), - A6: *res[1], - A7: *res[7], - A8: *res[3], - A9: *res[9], - A10: *res[5], - A11: *res[11], - } + nine := big.NewInt(9) + residueWitness := e.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 @@ -416,14 +392,14 @@ func (e Ext12) AssertFinalExponentiationIsOne(a *E12) { // and residueWitness, cubicNonResiduePower from the hint. t2 := e.Mul(&cubicNonResiduePower, a) - t1 := e.FrobeniusCube(&residueWitness) - t0 := e.FrobeniusSquare(&residueWitness) + t1 := e.FrobeniusCube(residueWitness) + t0 := e.FrobeniusSquare(residueWitness) t1 = e.DivUnchecked(t1, t0) - t0 = e.Frobenius(&residueWitness) + t0 = e.Frobenius(residueWitness) t1 = e.Mul(t1, t0) // exponentiation by U=6u+2 - t0 = e.ExpByU(&residueWitness) + t0 = e.ExpByU(residueWitness) t0 = e.Mul(t0, t1) diff --git a/std/algebra/emulated/fields_bn254/e12_test.go b/std/algebra/emulated/fields_bn254/e12_test.go index c0e890dafd..b2f0ae68a5 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -15,7 +15,8 @@ type e12Convert struct { func (circuit *e12Convert) Define(api frontend.API) error { e := NewExt12(api) - expected := e.e12RoundTrip(&circuit.A) + tower := e.ToTower(&circuit.A) + expected := e.FromTower(tower) e.AssertIsEqual(expected, &circuit.A) return nil } diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index fdfedafea3..22c836f501 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -24,7 +24,6 @@ type Pairing struct { curve *sw_emulated.Curve[BaseField, ScalarField] g2 *G2 bTwist *fields_bn254.E2 - g2gen *G2Affine } func NewGTEl(a bn254.GT) GTEl { @@ -82,15 +81,6 @@ func NewPairing(api frontend.API) (*Pairing, error) { }, nil } -func (pr Pairing) generators() *G2Affine { - if pr.g2gen == nil { - _, _, _, g2gen := bn254.Generators() - cg2gen := NewG2AffineFixed(g2gen) - pr.g2gen = &cg2gen - } - return pr.g2gen -} - // Pair calculates the reduced pairing for a set of points // ∏ᵢ e(Pᵢ, Qᵢ). // @@ -535,40 +525,15 @@ func (pr Pairing) MillerLoopAndMul(P *G1Affine, Q *G2Affine, previous *GTEl) (*G // millerLoopAndFinalExpResult computes the Miller loop between P and Q, // multiplies it in 𝔽p¹² by previous and returns the result. func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous *GTEl) *GTEl { - nine := big.NewInt(9) - a000 := pr.curveF.Add(&previous.A0, pr.curveF.MulConst(&previous.A6, nine)) - a001 := &previous.A6 - a010 := pr.curveF.Add(&previous.A2, pr.curveF.MulConst(&previous.A8, nine)) - a011 := &previous.A8 - a020 := pr.curveF.Add(&previous.A4, pr.curveF.MulConst(&previous.A10, nine)) - a021 := &previous.A10 - a100 := pr.curveF.Add(&previous.A1, pr.curveF.MulConst(&previous.A7, nine)) - a101 := &previous.A7 - a110 := pr.curveF.Add(&previous.A3, pr.curveF.MulConst(&previous.A9, nine)) - a111 := &previous.A9 - a120 := pr.curveF.Add(&previous.A5, pr.curveF.MulConst(&previous.A11, nine)) - a121 := &previous.A11 + tower := pr.ToTower(previous) // hint the non-residue witness - hint, err := pr.curveF.NewHint(millerLoopAndCheckFinalExpHint, 18, &P.X, &P.Y, &Q.P.X.A0, &Q.P.X.A1, &Q.P.Y.A0, &Q.P.Y.A1, a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) + hint, err := pr.curveF.NewHint(millerLoopAndCheckFinalExpHint, 18, &P.X, &P.Y, &Q.P.X.A0, &Q.P.X.A1, &Q.P.Y.A0, &Q.P.Y.A1, 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) } - residueWitness := GTEl{ - A0: *pr.curveF.Sub(hint[0], pr.curveF.MulConst(hint[1], nine)), - A1: *pr.curveF.Sub(hint[6], pr.curveF.MulConst(hint[7], nine)), - A2: *pr.curveF.Sub(hint[2], pr.curveF.MulConst(hint[3], nine)), - A3: *pr.curveF.Sub(hint[8], pr.curveF.MulConst(hint[9], nine)), - A4: *pr.curveF.Sub(hint[4], pr.curveF.MulConst(hint[5], nine)), - A5: *pr.curveF.Sub(hint[10], pr.curveF.MulConst(hint[11], nine)), - A6: *hint[1], - A7: *hint[7], - A8: *hint[3], - A9: *hint[9], - A10: *hint[5], - A11: *hint[11], - } + residueWitness := pr.FromTower([12]*baseEl{hint[0], hint[1], hint[2], hint[3], hint[4], hint[5], hint[6], hint[7], hint[8], hint[9], hint[10], hint[11]}) // constrain cubicNonResiduePower to be in Fp6 // that is: a100=a101=a110=a111=a120=a121=0 @@ -585,6 +550,7 @@ func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous // A9 = 0 // A10 = a021 // A11 = 0 + nine := big.NewInt(9) cubicNonResiduePower := GTEl{ A0: *pr.curveF.Sub(hint[12], pr.curveF.MulConst(hint[13], nine)), A1: *pr.curveF.Zero(), @@ -601,7 +567,7 @@ func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous } // residueWitnessInv = 1 / residueWitness - residueWitnessInv := pr.Ext12.Inverse(&residueWitness) + residueWitnessInv := pr.Ext12.Inverse(residueWitness) if Q.Lines == nil { Qlines := pr.computeLines(&Q.P) @@ -645,7 +611,7 @@ func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous res = pr.Ext12.MulBy012346789(res, prodLines) case -1: // multiply by residueWitness when bit=-1 - res = pr.Ext12.Mul(res, &residueWitness) + res = pr.Ext12.Mul(res, residueWitness) // lines evaluations at P // and ℓ × ℓ prodLines := pr.Mul01379By01379( From 33d154121b0662b1dc03986c6b3654622338dfef Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Dec 2024 21:06:22 -0500 Subject: [PATCH 16/22] refactor: clean code --- std/algebra/emulated/fields_bn254/e12.go | 240 ++++----------------- std/algebra/emulated/fields_bn254/hints.go | 125 +---------- 2 files changed, 44 insertions(+), 321 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index efb80aecac..6d97690b0b 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -31,24 +31,6 @@ func NewExt12(api frontend.API) *Ext12 { } } -func (e Ext12) Reduce(x *E12) *E12 { - var z E12 - z.A0 = *e.fp.Reduce(&x.A0) - z.A1 = *e.fp.Reduce(&x.A1) - z.A2 = *e.fp.Reduce(&x.A2) - z.A3 = *e.fp.Reduce(&x.A3) - z.A4 = *e.fp.Reduce(&x.A4) - z.A5 = *e.fp.Reduce(&x.A5) - z.A6 = *e.fp.Reduce(&x.A6) - z.A7 = *e.fp.Reduce(&x.A7) - z.A8 = *e.fp.Reduce(&x.A8) - z.A9 = *e.fp.Reduce(&x.A9) - z.A10 = *e.fp.Reduce(&x.A10) - z.A11 = *e.fp.Reduce(&x.A11) - - return &z -} - func (e Ext12) Zero() *E12 { zero := e.fp.Zero() return &E12{ @@ -207,66 +189,6 @@ func (e Ext12) Double(x *E12) *E12 { } } -func (e Ext12) MulByElement(x *E12, y *baseEl) *E12 { - a0 := e.fp.Mul(&x.A0, y) - a1 := e.fp.Mul(&x.A1, y) - a2 := e.fp.Mul(&x.A2, y) - a3 := e.fp.Mul(&x.A3, y) - a4 := e.fp.Mul(&x.A4, y) - a5 := e.fp.Mul(&x.A5, y) - a6 := e.fp.Mul(&x.A6, y) - a7 := e.fp.Mul(&x.A7, y) - a8 := e.fp.Mul(&x.A8, y) - a9 := e.fp.Mul(&x.A9, y) - a10 := e.fp.Mul(&x.A10, y) - a11 := e.fp.Mul(&x.A11, y) - - return &E12{ - 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) MulByConstElement(x *E12, y *big.Int) *E12 { - a0 := e.fp.MulConst(&x.A0, y) - a1 := e.fp.MulConst(&x.A1, y) - a2 := e.fp.MulConst(&x.A2, y) - a3 := e.fp.MulConst(&x.A3, y) - a4 := e.fp.MulConst(&x.A4, y) - a5 := e.fp.MulConst(&x.A5, y) - a6 := e.fp.MulConst(&x.A6, y) - a7 := e.fp.MulConst(&x.A7, y) - a8 := e.fp.MulConst(&x.A8, y) - a9 := e.fp.MulConst(&x.A9, y) - a10 := e.fp.MulConst(&x.A10, y) - a11 := e.fp.MulConst(&x.A11, y) - - return &E12{ - 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) Conjugate(x *E12) *E12 { return &E12{ A0: x.A0, @@ -402,6 +324,7 @@ func (e Ext12) mulDirect(a, b *E12) *E12 { A11: *d11, } } + func (e Ext12) Square(x *E12) *E12 { return e.squareDirect(x) } @@ -520,66 +443,6 @@ func (e Ext12) DivUnchecked(x, y *E12) *E12 { } -func (e Ext12) Select(selector frontend.Variable, z1, z0 *E12) *E12 { - a0 := e.fp.Select(selector, &z1.A0, &z0.A0) - a1 := e.fp.Select(selector, &z1.A1, &z0.A1) - a2 := e.fp.Select(selector, &z1.A2, &z0.A2) - a3 := e.fp.Select(selector, &z1.A3, &z0.A3) - a4 := e.fp.Select(selector, &z1.A4, &z0.A4) - a5 := e.fp.Select(selector, &z1.A5, &z0.A5) - a6 := e.fp.Select(selector, &z1.A6, &z0.A6) - a7 := e.fp.Select(selector, &z1.A7, &z0.A7) - a8 := e.fp.Select(selector, &z1.A8, &z0.A8) - a9 := e.fp.Select(selector, &z1.A9, &z0.A9) - a10 := e.fp.Select(selector, &z1.A10, &z0.A10) - a11 := e.fp.Select(selector, &z1.A11, &z0.A11) - - return &E12{ - 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) Lookup2(s1, s2 frontend.Variable, z0, z1, z2, z3 *E12) *E12 { - a0 := e.fp.Lookup2(s1, s2, &z0.A0, &z1.A0, &z2.A0, &z3.A0) - a1 := e.fp.Lookup2(s1, s2, &z0.A1, &z1.A1, &z2.A1, &z3.A1) - a2 := e.fp.Lookup2(s1, s2, &z0.A2, &z1.A2, &z2.A2, &z3.A2) - a3 := e.fp.Lookup2(s1, s2, &z0.A3, &z1.A3, &z2.A3, &z3.A3) - a4 := e.fp.Lookup2(s1, s2, &z0.A4, &z1.A4, &z2.A4, &z3.A4) - a5 := e.fp.Lookup2(s1, s2, &z0.A5, &z1.A5, &z2.A5, &z3.A5) - a6 := e.fp.Lookup2(s1, s2, &z0.A6, &z1.A6, &z2.A6, &z3.A6) - a7 := e.fp.Lookup2(s1, s2, &z0.A7, &z1.A7, &z2.A7, &z3.A7) - a8 := e.fp.Lookup2(s1, s2, &z0.A8, &z1.A8, &z2.A8, &z3.A8) - a9 := e.fp.Lookup2(s1, s2, &z0.A9, &z1.A9, &z2.A9, &z3.A9) - a10 := e.fp.Lookup2(s1, s2, &z0.A10, &z1.A10, &z2.A10, &z3.A10) - a11 := e.fp.Lookup2(s1, s2, &z0.A11, &z1.A11, &z2.A11, &z3.A11) - - return &E12{ - 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) AssertIsEqual(a, b *E12) { e.fp.AssertIsEqual(&a.A0, &b.A0) e.fp.AssertIsEqual(&a.A1, &b.A1) @@ -648,27 +511,23 @@ func (e Ext12) Copy(x *E12) *E12 { } func (e Ext12) Frobenius(a *E12) *E12 { - nine := big.NewInt(9) - a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) - a001 := e.fp.Neg(&a.A6) - a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) - a011 := e.fp.Neg(&a.A8) - a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) - a021 := e.fp.Neg(&a.A10) - a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) - a101 := e.fp.Neg(&a.A7) - a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) - a111 := e.fp.Neg(&a.A9) - a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) - a121 := e.fp.Neg(&a.A11) + 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: *a010, A1: *a011}) - t2 := e.Ext2.MulByNonResidue1Power4(&E2{A0: *a020, A1: *a021}) - t3 := e.Ext2.MulByNonResidue1Power1(&E2{A0: *a100, A1: *a101}) - t4 := e.Ext2.MulByNonResidue1Power3(&E2{A0: *a110, A1: *a111}) - t5 := e.Ext2.MulByNonResidue1Power5(&E2{A0: *a120, A1: *a121}) + 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(a000, e.fp.MulConst(a001, nine)) + nine := big.NewInt(9) + A0 := e.fp.Sub(tower[0], e.fp.MulConst(tower[1], nine)) A1 := e.fp.Sub(&t3.A0, e.fp.MulConst(&t3.A1, nine)) A2 := e.fp.Sub(&t1.A0, e.fp.MulConst(&t1.A1, nine)) A3 := e.fp.Sub(&t4.A0, e.fp.MulConst(&t4.A1, nine)) @@ -682,7 +541,7 @@ func (e Ext12) Frobenius(a *E12) *E12 { A3: *A3, A4: *A4, A5: *A5, - A6: *a001, + A6: *tower[1], A7: t3.A1, A8: t1.A1, A9: t4.A1, @@ -692,27 +551,16 @@ func (e Ext12) Frobenius(a *E12) *E12 { } func (e Ext12) FrobeniusSquare(a *E12) *E12 { - nine := big.NewInt(9) - a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) - a001 := &a.A6 - a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) - a011 := &a.A8 - a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) - a021 := &a.A10 - a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) - a101 := &a.A7 - a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) - a111 := &a.A9 - a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) - a121 := &a.A11 + tower := e.ToTower(a) - t1 := e.Ext2.MulByNonResidue2Power2(&E2{A0: *a010, A1: *a011}) - t2 := e.Ext2.MulByNonResidue2Power4(&E2{A0: *a020, A1: *a021}) - t3 := e.Ext2.MulByNonResidue2Power1(&E2{A0: *a100, A1: *a101}) - t4 := e.Ext2.MulByNonResidue2Power3(&E2{A0: *a110, A1: *a111}) - t5 := e.Ext2.MulByNonResidue2Power5(&E2{A0: *a120, A1: *a121}) + 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(a000, e.fp.MulConst(a001, nine)) + nine := big.NewInt(9) + A0 := e.fp.Sub(tower[0], e.fp.MulConst(tower[1], nine)) A1 := e.fp.Sub(&t3.A0, e.fp.MulConst(&t3.A1, nine)) A2 := e.fp.Sub(&t1.A0, e.fp.MulConst(&t1.A1, nine)) A3 := e.fp.Sub(&t4.A0, e.fp.MulConst(&t4.A1, nine)) @@ -726,7 +574,7 @@ func (e Ext12) FrobeniusSquare(a *E12) *E12 { A3: *A3, A4: *A4, A5: *A5, - A6: *a001, + A6: *tower[1], A7: t3.A1, A8: t1.A1, A9: t4.A1, @@ -736,27 +584,23 @@ func (e Ext12) FrobeniusSquare(a *E12) *E12 { } func (e Ext12) FrobeniusCube(a *E12) *E12 { - nine := big.NewInt(9) - a000 := e.fp.Add(&a.A0, e.fp.MulConst(&a.A6, nine)) - a001 := e.fp.Neg(&a.A6) - a010 := e.fp.Add(&a.A2, e.fp.MulConst(&a.A8, nine)) - a011 := e.fp.Neg(&a.A8) - a020 := e.fp.Add(&a.A4, e.fp.MulConst(&a.A10, nine)) - a021 := e.fp.Neg(&a.A10) - a100 := e.fp.Add(&a.A1, e.fp.MulConst(&a.A7, nine)) - a101 := e.fp.Neg(&a.A7) - a110 := e.fp.Add(&a.A3, e.fp.MulConst(&a.A9, nine)) - a111 := e.fp.Neg(&a.A9) - a120 := e.fp.Add(&a.A5, e.fp.MulConst(&a.A11, nine)) - a121 := e.fp.Neg(&a.A11) + 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.MulByNonResidue3Power2(&E2{A0: *a010, A1: *a011}) - t2 := e.Ext2.MulByNonResidue3Power4(&E2{A0: *a020, A1: *a021}) - t3 := e.Ext2.MulByNonResidue3Power1(&E2{A0: *a100, A1: *a101}) - t4 := e.Ext2.MulByNonResidue3Power3(&E2{A0: *a110, A1: *a111}) - t5 := e.Ext2.MulByNonResidue3Power5(&E2{A0: *a120, A1: *a121}) + t1 := e.Ext2.MulByNonResidue3Power2(&E2{A0: *tower[2], A1: *tower[3]}) + t2 := e.Ext2.MulByNonResidue3Power4(&E2{A0: *tower[4], A1: *tower[5]}) + t3 := e.Ext2.MulByNonResidue3Power1(&E2{A0: *tower[6], A1: *tower[7]}) + t4 := e.Ext2.MulByNonResidue3Power3(&E2{A0: *tower[8], A1: *tower[9]}) + t5 := e.Ext2.MulByNonResidue3Power5(&E2{A0: *tower[10], A1: *tower[11]}) - A0 := e.fp.Sub(a000, e.fp.MulConst(a001, nine)) + nine := big.NewInt(9) + A0 := e.fp.Sub(tower[0], e.fp.MulConst(tower[1], nine)) A1 := e.fp.Sub(&t3.A0, e.fp.MulConst(&t3.A1, nine)) A2 := e.fp.Sub(&t1.A0, e.fp.MulConst(&t1.A1, nine)) A3 := e.fp.Sub(&t4.A0, e.fp.MulConst(&t4.A1, nine)) @@ -770,7 +614,7 @@ func (e Ext12) FrobeniusCube(a *E12) *E12 { A3: *A3, A4: *A4, A5: *A5, - A6: *a001, + A6: *tower[1], A7: t3.A1, A8: t1.A1, A9: t4.A1, diff --git a/std/algebra/emulated/fields_bn254/hints.go b/std/algebra/emulated/fields_bn254/hints.go index 63426e379e..0cdb2fa4fc 100644 --- a/std/algebra/emulated/fields_bn254/hints.go +++ b/std/algebra/emulated/fields_bn254/hints.go @@ -5,7 +5,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254" "github.com/consensys/gnark-crypto/ecc/bn254/fp" - fp_bn "github.com/consensys/gnark-crypto/ecc/bn254/fp" "github.com/consensys/gnark/constraint/solver" "github.com/consensys/gnark/std/math/emulated" ) @@ -20,11 +19,6 @@ func GetHints() []solver.Hint { // E2 divE2Hint, inverseE2Hint, - // E6 - divE6Hint, - inverseE6Hint, - squareTorusHint, - divE6By6Hint, // E12 divE12Hint, inverseE12Hint, @@ -68,121 +62,6 @@ 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 bn254.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 bn254.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 bn254.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 bn254.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, @@ -223,7 +102,7 @@ func inverseE12Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) c.Inverse(&a) - var c0, c1, c2, c3, c4, c5, t2 fp_bn.Element + var c0, c1, c2, c3, c4, c5, t2 fp.Element t2.SetUint64(9).Mul(&t2, &c.C0.B0.A1) c0.Sub(&c.C0.B0.A0, &t2) t2.SetUint64(9).Mul(&t2, &c.C1.B0.A1) @@ -325,7 +204,7 @@ func divE12Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) erro c.Inverse(&b).Mul(&c, &a) - var c0, c1, c2, c3, c4, c5, t2 fp_bn.Element + var c0, c1, c2, c3, c4, c5, t2 fp.Element t2.SetUint64(9).Mul(&t2, &c.C0.B0.A1) c0.Sub(&c.C0.B0.A0, &t2) t2.SetUint64(9).Mul(&t2, &c.C1.B0.A1) From 8db24a1adc15c736b703f5a2b518b46f63742353 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Dec 2024 21:10:01 -0500 Subject: [PATCH 17/22] docs: add doc_test.go --- std/algebra/emulated/sw_bn254/doc_test.go | 93 +++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 std/algebra/emulated/sw_bn254/doc_test.go diff --git a/std/algebra/emulated/sw_bn254/doc_test.go b/std/algebra/emulated/sw_bn254/doc_test.go new file mode 100644 index 0000000000..36f7445dac --- /dev/null +++ b/std/algebra/emulated/sw_bn254/doc_test.go @@ -0,0 +1,93 @@ +package sw_bn254_test + +import ( + "crypto/rand" + "fmt" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/std/algebra/emulated/sw_bn254" +) + +type PairCircuit struct { + InG1 sw_bn254.G1Affine + InG2 sw_bn254.G2Affine + Res sw_bn254.GTEl +} + +func (c *PairCircuit) Define(api frontend.API) error { + pairing, err := sw_bn254.NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + // Pair method does not check that the points are in the proper groups. + pairing.AssertIsOnG1(&c.InG1) + pairing.AssertIsOnG2(&c.InG2) + // Compute the pairing + res, err := pairing.Pair([]*sw_bn254.G1Affine{&c.InG1}, []*sw_bn254.G2Affine{&c.InG2}) + if err != nil { + return fmt.Errorf("pair: %w", err) + } + pairing.AssertIsEqual(res, &c.Res) + return nil +} + +func ExamplePairing() { + p, q, err := randomG1G2Affines() + if err != nil { + panic(err) + } + res, err := bn254.Pair([]bn254.G1Affine{p}, []bn254.G2Affine{q}) + if err != nil { + panic(err) + } + circuit := PairCircuit{} + witness := PairCircuit{ + InG1: sw_bn254.NewG1Affine(p), + InG2: sw_bn254.NewG2Affine(q), + Res: sw_bn254.NewGTEl(res), + } + ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + if err != nil { + panic(err) + } + pk, vk, err := groth16.Setup(ccs) + if err != nil { + panic(err) + } + secretWitness, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) + if err != nil { + panic(err) + } + publicWitness, err := secretWitness.Public() + if err != nil { + panic(err) + } + proof, err := groth16.Prove(ccs, pk, secretWitness) + if err != nil { + panic(err) + } + err = groth16.Verify(proof, vk, publicWitness) + if err != nil { + panic(err) + } +} + +func randomG1G2Affines() (p bn254.G1Affine, q bn254.G2Affine, err error) { + _, _, G1AffGen, G2AffGen := bn254.Generators() + mod := bn254.ID.ScalarField() + s1, err := rand.Int(rand.Reader, mod) + if err != nil { + return p, q, err + } + s2, err := rand.Int(rand.Reader, mod) + if err != nil { + return p, q, err + } + p.ScalarMultiplication(&G1AffGen, s1) + q.ScalarMultiplication(&G2AffGen, s2) + return +} From 028bb162fab6e9125a8777b802d61811a0397490 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Dec 2024 21:13:31 -0500 Subject: [PATCH 18/22] test: TestPairFixed and IsMillerLoopAndFinalExpCircui --- std/algebra/emulated/sw_bn254/pairing_test.go | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index 9d0afe4229..ae2fe87e80 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -128,6 +128,73 @@ func TestPairTestSolve(t *testing.T) { assert.NoError(err) } +func TestPairFixedTestSolve(t *testing.T) { + assert := test.NewAssert(t) + p, q := randomG1G2Affines() + res, err := bn254.Pair([]bn254.G1Affine{p}, []bn254.G2Affine{q}) + assert.NoError(err) + witness := PairCircuit{ + InG1: NewG1Affine(p), + InG2: NewG2AffineFixed(q), + Res: NewGTEl(res), + } + err = test.IsSolved(&PairCircuit{InG2: NewG2AffineFixedPlaceholder()}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type MultiPairCircuit struct { + InG1 G1Affine + InG2 G2Affine + Res GTEl + n int +} + +func (c *MultiPairCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + pairing.AssertIsOnG1(&c.InG1) + pairing.AssertIsOnG2(&c.InG2) + P, Q := []*G1Affine{}, []*G2Affine{} + for i := 0; i < c.n; i++ { + P = append(P, &c.InG1) + Q = append(Q, &c.InG2) + } + res, err := pairing.Pair(P, Q) + if err != nil { + return fmt.Errorf("pair: %w", err) + } + pairing.AssertIsEqual(res, &c.Res) + return nil +} + +func TestMultiPairTestSolve(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + assert := test.NewAssert(t) + p1, q1 := randomG1G2Affines() + p := make([]bn254.G1Affine, 4) + q := make([]bn254.G2Affine, 4) + for i := 0; i < 4; i++ { + p[i] = p1 + q[i] = q1 + } + + for i := 2; i < 4; i++ { + res, err := bn254.Pair(p[:i], q[:i]) + assert.NoError(err) + witness := MultiPairCircuit{ + InG1: NewG1Affine(p1), + InG2: NewG2Affine(q1), + Res: NewGTEl(res), + } + err = test.IsSolved(&MultiPairCircuit{n: i}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + } +} + type PairingCheckCircuit struct { In1G1 G1Affine In2G1 G1Affine @@ -300,6 +367,65 @@ func TestIsOnG2Solve(t *testing.T) { assert.NoError(err) } +type IsMillerLoopAndFinalExpCircuit struct { + Prev GTEl + P G1Affine + Q G2Affine + Expected frontend.Variable +} + +func (c *IsMillerLoopAndFinalExpCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + res := pairing.IsMillerLoopAndFinalExpOne(&c.P, &c.Q, &c.Prev) + api.AssertIsEqual(res, c.Expected) + return nil + +} + +func TestIsMillerLoopAndFinalExpCircuitTestSolve(t *testing.T) { + assert := test.NewAssert(t) + p, q := randomG1G2Affines() + + var np bn254.G1Affine + np.Neg(&p) + + ok, err := bn254.PairingCheck([]bn254.G1Affine{p, np}, []bn254.G2Affine{q, q}) + assert.NoError(err) + assert.True(ok) + + lines := bn254.PrecomputeLines(q) + // need to use ML with precomputed lines. Otherwise, the result will be different + mlres, err := bn254.MillerLoopFixedQ( + []bn254.G1Affine{p}, + [][2][len(bn254.LoopCounter)]bn254.LineEvaluationAff{lines}, + ) + assert.NoError(err) + + witness := IsMillerLoopAndFinalExpCircuit{ + Prev: NewGTEl(mlres), + P: NewG1Affine(np), + Q: NewG2Affine(q), + Expected: 1, + } + err = test.IsSolved(&IsMillerLoopAndFinalExpCircuit{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + + var randPrev bn254.GT + randPrev.SetRandom() + + witness = IsMillerLoopAndFinalExpCircuit{ + Prev: NewGTEl(randPrev), + P: NewG1Affine(np), + Q: NewG2Affine(q), + Expected: 0, + } + err = test.IsSolved(&IsMillerLoopAndFinalExpCircuit{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + // bench func BenchmarkPairing(b *testing.B) { // e(a,2b) * e(-2a,b) == 1 From dc4ea7fcce36f661de9c265f2d46a3b0db83fd67 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 11 Dec 2024 13:34:49 -0500 Subject: [PATCH 19/22] perf(bn254/pairing): some more optims ~56k scs --- .../emulated/fields_bn254/e12_pairing.go | 2 +- std/algebra/emulated/sw_bn254/pairing.go | 93 +++++++++++++------ 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index 2bdf669275..b2e77cf67c 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -271,7 +271,7 @@ func (e *Ext12) Mul01379By01379(e3, e4, c3, c4 *E2) [10]*baseEl { return [10]*baseEl{d0, d1, d2, d3, d4, d6, d7, d8, d9, d10} } -// MulBy01379 multiplies a by an E12 sparse element b of the form +// MulBy012346789 multiplies a by an E12 sparse element b of the form // // b.A0 = b[0] // b.A1 = b[1] diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 22c836f501..e425f8754d 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -320,7 +320,34 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl } if n >= 2 { - for k := 1; k < n; k++ { + // k = 1, separately to avoid MulBy01379 (res × ℓ) + // (res is also a line at this point, so we use Mul01379By01379 ℓ × ℓ) + // line evaluation at P[1] + prodLines = pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[1][0][64].R0, xNegOverY[1]), + pr.Ext2.MulByElement(&lines[1][0][64].R1, yInv[1]), + c3, + c4, + ) + res = >El{ + A0: *prodLines[0], + A1: *prodLines[1], + A2: *prodLines[2], + A3: *prodLines[3], + A4: *prodLines[4], + A5: *pr.curveF.Zero(), + A6: *prodLines[5], + A7: *prodLines[6], + A8: *prodLines[7], + A9: *prodLines[8], + A10: *prodLines[9], + A11: *pr.curveF.Zero(), + } + } + + if n >= 3 { + // k >= 2 + for k := 2; k < n; k++ { // line evaluation at P[k] // ℓ × res res = pr.MulBy01379( @@ -328,7 +355,6 @@ func (pr Pairing) millerLoopLines(P []*G1Affine, lines []lineEvaluations) (*GTEl pr.Ext2.MulByElement(&lines[k][0][64].R0, xNegOverY[k]), pr.Ext2.MulByElement(&lines[k][0][64].R1, yInv[k]), ) - } } @@ -379,6 +405,7 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *g2AffP, isSub bool) (*g2AffP, *lineEv var line1, line2 lineEvaluation var p g2AffP + mone := pr.curveF.NewElement(-1) // compute λ1 = (y1-y2)/(x1-x2) or λ1 = (y1+y2)/(x1-x2) if isSub is true var n *fields_bn254.E2 @@ -391,15 +418,16 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *g2AffP, isSub bool) (*g2AffP, *lineEv λ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_bn254.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.MulByConstElement(&p1.Y, big.NewInt(2)) @@ -409,21 +437,23 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *g2AffP, isSub bool) (*g2AffP, *lineEv λ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_bn254.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_bn254.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 } @@ -434,6 +464,7 @@ 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) @@ -442,20 +473,22 @@ func (pr Pairing) doubleStep(p1 *g2AffP) (*g2AffP, *lineEvaluation) { λ := 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_bn254.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_bn254.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 @@ -465,19 +498,23 @@ func (pr Pairing) doubleStep(p1 *g2AffP) (*g2AffP, *lineEvaluation) { // https://eprint.iacr.org/2022/1162 (Section 6.1) func (pr Pairing) addStep(p1, p2 *g2AffP) (*g2AffP, *lineEvaluation) { + mone := pr.curveF.NewElement(-1) + // compute λ = (y2-y1)/(x2-x1) p2ypy := pr.Ext2.Sub(&p2.Y, &p1.Y) p2xpx := pr.Ext2.Sub(&p2.X, &p1.X) λ := pr.Ext2.DivUnchecked(p2ypy, p2xpx) // xr = λ²-x1-x2 - xr := pr.Ext2.Square(λ) - xr = pr.Ext2.Sub(xr, pr.Ext2.Add(&p1.X, &p2.X)) + xr0 := pr.curveF.Eval([][]*baseEl{{&λ.A0, &λ.A0}, {mone, &λ.A1, &λ.A1}, {mone, &p1.X.A0}, {mone, &p2.X.A0}}, []int{1, 1, 1, 1}) + xr1 := pr.curveF.Eval([][]*baseEl{{&λ.A0, &λ.A1}, {mone, &p1.X.A1}, {mone, &p2.X.A1}}, []int{2, 1, 1}) + xr := &fields_bn254.E2{A0: *xr0, A1: *xr1} // yr = λ(x1-xr) - y1 - pxrx := pr.Ext2.Sub(&p1.X, xr) - λpxrx := pr.Ext2.Mul(λ, pxrx) - yr := pr.Ext2.Sub(λpxrx, &p1.Y) + yr := pr.Ext2.Sub(&p1.X, xr) + 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_bn254.E2{A0: *yr0, A1: *yr1} var res g2AffP res.X = *xr @@ -485,8 +522,8 @@ func (pr Pairing) addStep(p1, p2 *g2AffP) (*g2AffP, *lineEvaluation) { var line lineEvaluation 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 &res, &line @@ -495,6 +532,8 @@ func (pr Pairing) addStep(p1, p2 *g2AffP) (*g2AffP, *lineEvaluation) { // lineCompute computes the line through p1 and p2, but does not compute p1+p2. func (pr Pairing) lineCompute(p1, p2 *g2AffP) *lineEvaluation { + mone := pr.curveF.NewElement(-1) + // compute λ = (y2+y1)/(x2-x1) qypy := pr.Ext2.Add(&p1.Y, &p2.Y) qxpx := pr.Ext2.Sub(&p1.X, &p2.X) @@ -502,8 +541,8 @@ func (pr Pairing) lineCompute(p1, p2 *g2AffP) *lineEvaluation { var line lineEvaluation 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 From 3eff1cd4ba5cc9f037680f1511861e6fcf0c1ba6 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 11 Dec 2024 13:35:52 -0500 Subject: [PATCH 20/22] chore: update stats --- internal/stats/latest_stats.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index b46def2697..5b272ec86b 100644 --- a/internal/stats/latest_stats.csv +++ b/internal/stats/latest_stats.csv @@ -181,14 +181,14 @@ pairing_bls24315,bls24_315,plonk,0,0 pairing_bls24315,bls24_317,plonk,0,0 pairing_bls24315,bw6_761,plonk,0,0 pairing_bls24315,bw6_633,plonk,141249,141249 -pairing_bn254,bn254,groth16,610241,1000885 +pairing_bn254,bn254,groth16,604783,990919 pairing_bn254,bls12_377,groth16,0,0 pairing_bn254,bls12_381,groth16,0,0 pairing_bn254,bls24_315,groth16,0,0 pairing_bn254,bls24_317,groth16,0,0 pairing_bn254,bw6_761,groth16,0,0 pairing_bn254,bw6_633,groth16,0,0 -pairing_bn254,bn254,plonk,2347160,2059138 +pairing_bn254,bn254,plonk,2319665,2030447 pairing_bn254,bls12_377,plonk,0,0 pairing_bn254,bls12_381,plonk,0,0 pairing_bn254,bls24_315,plonk,0,0 From 2cc6cfab03dcd84f04d36ff8cf7eb3d3b9e090a9 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 11 Dec 2024 15:36:10 -0500 Subject: [PATCH 21/22] perf(bn254): use Eval for G2 subgroup membership --- std/algebra/emulated/fields_bn254/e2.go | 12 +++++ std/algebra/emulated/sw_bn254/g2.go | 57 +++++++++++++++--------- std/algebra/emulated/sw_bn254/pairing.go | 3 +- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/std/algebra/emulated/fields_bn254/e2.go b/std/algebra/emulated/fields_bn254/e2.go index fea7225a79..7af33cbd86 100644 --- a/std/algebra/emulated/fields_bn254/e2.go +++ b/std/algebra/emulated/fields_bn254/e2.go @@ -274,6 +274,18 @@ func (e Ext2) Square(x *E2) *E2 { } } +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/sw_bn254/g2.go b/std/algebra/emulated/sw_bn254/g2.go index 8edb7953c5..6058f6e6c3 100644 --- a/std/algebra/emulated/sw_bn254/g2.go +++ b/std/algebra/emulated/sw_bn254/g2.go @@ -11,6 +11,7 @@ import ( type G2 struct { api frontend.API + fp *emulated.Field[BaseField] *fields_bn254.Ext2 w *emulated.Element[BaseField] u, v *fields_bn254.E2 @@ -40,6 +41,11 @@ func newG2AffP(v bn254.G2Affine) g2AffP { } func NewG2(api frontend.API) *G2 { + fp, err := emulated.NewField[emulated.BN254Fp](api) + if err != nil { + // TODO: we start returning errors when generifying + panic(err) + } w := emulated.ValueOf[BaseField]("21888242871839275220042445260109153167277707414472061641714758635765020556616") u := fields_bn254.E2{ A0: emulated.ValueOf[BaseField]("21575463638280843010398324269430826099269044274347216827212613867836435027261"), @@ -51,6 +57,7 @@ func NewG2(api frontend.API) *G2 { } return &G2{ api: api, + fp: fp, Ext2: fields_bn254.NewExt2(api), w: &w, u: &u, @@ -149,20 +156,23 @@ func (g2 *G2) scalarMulBySeed(q *G2Affine) *G2Affine { } func (g2 G2) add(p, q *G2Affine) *G2Affine { + mone := g2.fp.NewElement(-1) + // compute λ = (q.y-p.y)/(q.x-p.x) qypy := g2.Ext2.Sub(&q.P.Y, &p.P.Y) qxpx := g2.Ext2.Sub(&q.P.X, &p.P.X) λ := g2.Ext2.DivUnchecked(qypy, qxpx) // xr = λ²-p.x-q.x - λλ := g2.Ext2.Square(λ) - qxpx = g2.Ext2.Add(&p.P.X, &q.P.X) - xr := g2.Ext2.Sub(λλ, qxpx) + xr0 := g2.fp.Eval([][]*baseEl{{&λ.A0, &λ.A0}, {mone, &λ.A1, &λ.A1}, {mone, &p.P.X.A0}, {mone, &q.P.X.A0}}, []int{1, 1, 1, 1}) + xr1 := g2.fp.Eval([][]*baseEl{{&λ.A0, &λ.A1}, {mone, &p.P.X.A1}, {mone, &q.P.X.A1}}, []int{2, 1, 1}) + xr := &fields_bn254.E2{A0: *xr0, A1: *xr1} // p.y = λ(p.x-r.x) - p.y - pxrx := g2.Ext2.Sub(&p.P.X, xr) - λpxrx := g2.Ext2.Mul(λ, pxrx) - yr := g2.Ext2.Sub(λpxrx, &p.P.Y) + yr := g2.Ext2.Sub(&p.P.X, xr) + yr0 := g2.fp.Eval([][]*baseEl{{&λ.A0, &yr.A0}, {mone, &λ.A1, &yr.A1}, {mone, &p.P.Y.A0}}, []int{1, 1, 1}) + yr1 := g2.fp.Eval([][]*baseEl{{&λ.A0, &yr.A1}, {&λ.A1, &yr.A0}, {mone, &p.P.Y.A1}}, []int{1, 1, 1}) + yr = &fields_bn254.E2{A0: *yr0, A1: *yr1} return &G2Affine{ P: g2AffP{ @@ -189,6 +199,8 @@ func (g2 G2) sub(p, q *G2Affine) *G2Affine { } func (g2 *G2) double(p *G2Affine) *G2Affine { + mone := g2.fp.NewElement(-1) + // compute λ = (3p.x²)/2*p.y xx3a := g2.Square(&p.P.X) xx3a = g2.MulByConstElement(xx3a, big.NewInt(3)) @@ -196,14 +208,15 @@ func (g2 *G2) double(p *G2Affine) *G2Affine { λ := g2.DivUnchecked(xx3a, y2) // xr = λ²-2p.x - x2 := g2.Double(&p.P.X) - λλ := g2.Square(λ) - xr := g2.Sub(λλ, x2) + xr0 := g2.fp.Eval([][]*baseEl{{&λ.A0, &λ.A0}, {mone, &λ.A1, &λ.A1}, {mone, &p.P.X.A0}}, []int{1, 1, 2}) + xr1 := g2.fp.Eval([][]*baseEl{{&λ.A0, &λ.A1}, {mone, &p.P.X.A1}}, []int{2, 2}) + xr := &fields_bn254.E2{A0: *xr0, A1: *xr1} // yr = λ(p-xr) - p.y - pxrx := g2.Sub(&p.P.X, xr) - λpxrx := g2.Mul(λ, pxrx) - yr := g2.Sub(λpxrx, &p.P.Y) + yr := g2.Ext2.Sub(&p.P.X, xr) + yr0 := g2.fp.Eval([][]*baseEl{{&λ.A0, &yr.A0}, {mone, &λ.A1, &yr.A1}, {mone, &p.P.Y.A0}}, []int{1, 1, 1}) + yr1 := g2.fp.Eval([][]*baseEl{{&λ.A0, &yr.A1}, {&λ.A1, &yr.A0}, {mone, &p.P.Y.A1}}, []int{1, 1, 1}) + yr = &fields_bn254.E2{A0: *yr0, A1: *yr1} return &G2Affine{ P: g2AffP{ @@ -222,6 +235,7 @@ func (g2 *G2) doubleN(p *G2Affine, n int) *G2Affine { } func (g2 G2) doubleAndAdd(p, q *G2Affine) *G2Affine { + mone := g2.fp.NewElement(-1) // compute λ1 = (q.y-p.y)/(q.x-p.x) yqyp := g2.Ext2.Sub(&q.P.Y, &p.P.Y) @@ -229,9 +243,9 @@ func (g2 G2) doubleAndAdd(p, q *G2Affine) *G2Affine { λ1 := g2.Ext2.DivUnchecked(yqyp, xqxp) // compute x2 = λ1²-p.x-q.x - λ1λ1 := g2.Ext2.Square(λ1) - xqxp = g2.Ext2.Add(&p.P.X, &q.P.X) - x2 := g2.Ext2.Sub(λ1λ1, xqxp) + x20 := g2.fp.Eval([][]*baseEl{{&λ1.A0, &λ1.A0}, {mone, &λ1.A1, &λ1.A1}, {mone, &p.P.X.A0}, {mone, &q.P.X.A0}}, []int{1, 1, 1, 1}) + x21 := g2.fp.Eval([][]*baseEl{{&λ1.A0, &λ1.A1}, {mone, &p.P.X.A1}, {mone, &q.P.X.A1}}, []int{2, 1, 1}) + x2 := &fields_bn254.E2{A0: *x20, A1: *x21} // omit y2 computation // compute λ2 = -λ1-2*p.y/(x2-p.x) @@ -241,15 +255,16 @@ func (g2 G2) doubleAndAdd(p, q *G2Affine) *G2Affine { λ2 = g2.Ext2.Add(λ1, λ2) λ2 = g2.Ext2.Neg(λ2) - // compute x3 =λ2²-p.x-x3 - λ2λ2 := g2.Ext2.Square(λ2) - x3 := g2.Ext2.Sub(λ2λ2, &p.P.X) - x3 = g2.Ext2.Sub(x3, x2) + // compute x3 =λ2²-p.x-x2 + x30 := g2.fp.Eval([][]*baseEl{{&λ2.A0, &λ2.A0}, {mone, &λ2.A1, &λ2.A1}, {mone, &p.P.X.A0}, {mone, x20}}, []int{1, 1, 1, 1}) + x31 := g2.fp.Eval([][]*baseEl{{&λ2.A0, &λ2.A1}, {mone, &p.P.X.A1}, {mone, x21}}, []int{2, 1, 1}) + x3 := &fields_bn254.E2{A0: *x30, A1: *x31} // compute y3 = λ2*(p.x - x3)-p.y y3 := g2.Ext2.Sub(&p.P.X, x3) - y3 = g2.Ext2.Mul(λ2, y3) - y3 = g2.Ext2.Sub(y3, &p.P.Y) + y30 := g2.fp.Eval([][]*baseEl{{&λ2.A0, &y3.A0}, {mone, &λ2.A1, &y3.A1}, {mone, &p.P.Y.A0}}, []int{1, 1, 1}) + y31 := g2.fp.Eval([][]*baseEl{{&λ2.A0, &y3.A1}, {&λ2.A1, &y3.A0}, {mone, &p.P.Y.A1}}, []int{1, 1, 1}) + y3 = &fields_bn254.E2{A0: *y30, A1: *y31} return &G2Affine{ P: g2AffP{ diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index e425f8754d..10cc8e0297 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -176,8 +176,7 @@ func (pr Pairing) computeTwistEquation(Q *G2Affine) (left, right *fields_bn254.E b := pr.Ext2.Select(selector, pr.Ext2.Zero(), pr.bTwist) left = pr.Ext2.Square(&Q.P.Y) - right = pr.Ext2.Square(&Q.P.X) - right = pr.Ext2.Mul(right, &Q.P.X) + right = pr.Ext2.Cube(&Q.P.X) right = pr.Ext2.Add(right, b) return left, right } From c6230d3ecf21f3f93fa067419fa0d8ea71a0488b Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 11 Dec 2024 17:14:05 -0500 Subject: [PATCH 22/22] perf(bn254): use Eval for G1 subgroup membership --- std/algebra/emulated/sw_emulated/point.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/std/algebra/emulated/sw_emulated/point.go b/std/algebra/emulated/sw_emulated/point.go index 2cd9e71a6c..10b27de351 100644 --- a/std/algebra/emulated/sw_emulated/point.go +++ b/std/algebra/emulated/sw_emulated/point.go @@ -215,8 +215,7 @@ func (c *Curve[B, S]) AssertIsOnCurve(p *AffinePoint[B]) { b := c.baseApi.Select(selector, c.baseApi.Zero(), &c.b) left := c.baseApi.Mul(&p.Y, &p.Y) - right := c.baseApi.Mul(&p.X, c.baseApi.Mul(&p.X, &p.X)) - right = c.baseApi.Add(right, b) + right := c.baseApi.Eval([][]*emulated.Element[B]{{&p.X, &p.X, &p.X}, {b}}, []int{1, 1}) if c.addA { ax := c.baseApi.Mul(&c.a, &p.X) right = c.baseApi.Add(right, ax)