diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index 18197f8970..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,963003,1603091 +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,3771397,3534755 +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 diff --git a/std/algebra/emulated/fields_bn254/e12.go b/std/algebra/emulated/fields_bn254/e12.go index 1cc99fe365..6d97690b0b 100644 --- a/std/algebra/emulated/fields_bn254/e12.go +++ b/std/algebra/emulated/fields_bn254/e12.go @@ -1,217 +1,421 @@ 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 + *Ext2 + 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{ + Ext2: NewExt2(api), + api: api, + fp: fp, + } } -func (e Ext12) Add(x, y *E12) *E12 { - z0 := e.Ext6.Add(&x.C0, &y.C0) - z1 := e.Ext6.Add(&x.C1, &y.C1) +func (e Ext12) Zero() *E12 { + zero := e.fp.Zero() return &E12{ - C0: *z0, - C1: *z1, + A0: *zero, + A1: *zero, + A2: *zero, + A3: *zero, + A4: *zero, + A5: *zero, + A6: *zero, + A7: *zero, + A8: *zero, + A9: *zero, + A10: *zero, + A11: *zero, } } -func (e Ext12) Sub(x, y *E12) *E12 { - z0 := e.Ext6.Sub(&x.C0, &y.C0) - z1 := e.Ext6.Sub(&x.C1, &y.C1) +func (e Ext12) One() *E12 { + one := e.fp.One() + zero := e.fp.Zero() return &E12{ - C0: *z0, - C1: *z1, + A0: *one, + A1: *zero, + A2: *zero, + A3: *zero, + A4: *zero, + A5: *zero, + A6: *zero, + A7: *zero, + A8: *zero, + A9: *zero, + A10: *zero, + A11: *zero, } } -func (e Ext12) Conjugate(x *E12) *E12 { - z1 := e.Ext6.Neg(&x.C1) +func (e Ext12) Neg(x *E12) *E12 { + a0 := e.fp.Neg(&x.A0) + a1 := e.fp.Neg(&x.A1) + a2 := e.fp.Neg(&x.A2) + a3 := e.fp.Neg(&x.A3) + a4 := e.fp.Neg(&x.A4) + a5 := e.fp.Neg(&x.A5) + a6 := e.fp.Neg(&x.A6) + a7 := e.fp.Neg(&x.A7) + a8 := e.fp.Neg(&x.A8) + a9 := e.fp.Neg(&x.A9) + a10 := e.fp.Neg(&x.A10) + a11 := e.fp.Neg(&x.A11) + return &E12{ - C0: x.C0, - C1: *z1, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) Mul(x, y *E12) *E12 { - a := e.Ext6.Add(&x.C0, &x.C1) - b := e.Ext6.Add(&y.C0, &y.C1) - a = e.Ext6.Mul(a, b) - b = e.Ext6.Mul(&x.C0, &y.C0) - c := e.Ext6.Mul(&x.C1, &y.C1) - d := e.Ext6.Add(c, b) - z1 := e.Ext6.Sub(a, d) - z0 := e.Ext6.MulByNonResidue(c) - z0 = e.Ext6.Add(z0, b) +func (e Ext12) Add(x, y *E12) *E12 { + a0 := e.fp.Add(&x.A0, &y.A0) + a1 := e.fp.Add(&x.A1, &y.A1) + a2 := e.fp.Add(&x.A2, &y.A2) + a3 := e.fp.Add(&x.A3, &y.A3) + a4 := e.fp.Add(&x.A4, &y.A4) + a5 := e.fp.Add(&x.A5, &y.A5) + a6 := e.fp.Add(&x.A6, &y.A6) + a7 := e.fp.Add(&x.A7, &y.A7) + a8 := e.fp.Add(&x.A8, &y.A8) + a9 := e.fp.Add(&x.A9, &y.A9) + a10 := e.fp.Add(&x.A10, &y.A10) + a11 := e.fp.Add(&x.A11, &y.A11) + return &E12{ - C0: *z0, - C1: *z1, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) Zero() *E12 { - zero := e.fp.Zero() +func (e Ext12) Sub(x, y *E12) *E12 { + a0 := e.fp.Sub(&x.A0, &y.A0) + a1 := e.fp.Sub(&x.A1, &y.A1) + a2 := e.fp.Sub(&x.A2, &y.A2) + a3 := e.fp.Sub(&x.A3, &y.A3) + a4 := e.fp.Sub(&x.A4, &y.A4) + a5 := e.fp.Sub(&x.A5, &y.A5) + a6 := e.fp.Sub(&x.A6, &y.A6) + a7 := e.fp.Sub(&x.A7, &y.A7) + a8 := e.fp.Sub(&x.A8, &y.A8) + a9 := e.fp.Sub(&x.A9, &y.A9) + a10 := e.fp.Sub(&x.A10, &y.A10) + a11 := e.fp.Sub(&x.A11, &y.A11) + return &E12{ - C0: E6{ - B0: E2{A0: *zero, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, - C1: E6{ - B0: E2{A0: *zero, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) One() *E12 { - z000 := e.fp.One() - zero := e.fp.Zero() +func (e Ext12) Double(x *E12) *E12 { + two := big.NewInt(2) + a0 := e.fp.MulConst(&x.A0, two) + a1 := e.fp.MulConst(&x.A1, two) + a2 := e.fp.MulConst(&x.A2, two) + a3 := e.fp.MulConst(&x.A3, two) + a4 := e.fp.MulConst(&x.A4, two) + a5 := e.fp.MulConst(&x.A5, two) + a6 := e.fp.MulConst(&x.A6, two) + a7 := e.fp.MulConst(&x.A7, two) + a8 := e.fp.MulConst(&x.A8, two) + a9 := e.fp.MulConst(&x.A9, two) + a10 := e.fp.MulConst(&x.A10, two) + a11 := e.fp.MulConst(&x.A11, two) + return &E12{ - C0: E6{ - B0: E2{A0: *z000, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, - C1: E6{ - B0: E2{A0: *zero, A1: *zero}, - B1: E2{A0: *zero, A1: *zero}, - B2: E2{A0: *zero, A1: *zero}, - }, + A0: *a0, + A1: *a1, + A2: *a2, + A3: *a3, + A4: *a4, + A5: *a5, + A6: *a6, + A7: *a7, + A8: *a8, + A9: *a9, + A10: *a10, + A11: *a11, } } -func (e Ext12) IsZero(z *E12) frontend.Variable { - c0 := e.Ext6.IsZero(&z.C0) - c1 := e.Ext6.IsZero(&z.C1) - return e.api.And(c0, c1) -} - -func (e Ext12) Square(x *E12) *E12 { - c0 := e.Ext6.Sub(&x.C0, &x.C1) - c3 := e.Ext6.MulByNonResidue(&x.C1) - c3 = e.Ext6.Sub(&x.C0, c3) - c2 := e.Ext6.Mul(&x.C0, &x.C1) - c0 = e.Ext6.Mul(c0, c3) - c0 = e.Ext6.Add(c0, c2) - z1 := e.Ext6.Double(c2) - c2 = e.Ext6.MulByNonResidue(c2) - z0 := e.Ext6.Add(c0, c2) +func (e Ext12) Conjugate(x *E12) *E12 { return &E12{ - C0: *z0, - C1: *z1, + A0: x.A0, + A1: *e.fp.Neg(&x.A1), + A2: x.A2, + A3: *e.fp.Neg(&x.A3), + A4: x.A4, + A5: *e.fp.Neg(&x.A5), + A6: x.A6, + A7: *e.fp.Neg(&x.A7), + A8: x.A8, + A9: *e.fp.Neg(&x.A9), + A10: x.A10, + A11: *e.fp.Neg(&x.A11), } } -// Granger--Scott cyclotomic square -func (e Ext12) CyclotomicSquare(x *E12) *E12 { - t0 := e.Ext2.Square(&x.C1.B1) - t1 := e.Ext2.Square(&x.C0.B0) - t6 := e.Ext2.Add(&x.C1.B1, &x.C0.B0) - t6 = e.Ext2.Square(t6) - t6 = e.Ext2.Sub(t6, t0) - t6 = e.Ext2.Sub(t6, t1) - t2 := e.Ext2.Square(&x.C0.B2) - t3 := e.Ext2.Square(&x.C1.B0) - t7 := e.Ext2.Add(&x.C0.B2, &x.C1.B0) - t7 = e.Ext2.Square(t7) - t7 = e.Ext2.Sub(t7, t2) - t7 = e.Ext2.Sub(t7, t3) - t4 := e.Ext2.Square(&x.C1.B2) - t5 := e.Ext2.Square(&x.C0.B1) - t8 := e.Ext2.Add(&x.C1.B2, &x.C0.B1) - t8 = e.Ext2.Square(t8) - t8 = e.Ext2.Sub(t8, t4) - t8 = e.Ext2.Sub(t8, t5) - t8 = e.Ext2.MulByNonResidue(t8) - t0 = e.Ext2.MulByNonResidue(t0) - t0 = e.Ext2.Add(t0, t1) - t2 = e.Ext2.MulByNonResidue(t2) - t2 = e.Ext2.Add(t2, t3) - t4 = e.Ext2.MulByNonResidue(t4) - t4 = e.Ext2.Add(t4, t5) - z00 := e.Ext2.Sub(t0, &x.C0.B0) - z00 = e.Ext2.Double(z00) - z00 = e.Ext2.Add(z00, t0) - z01 := e.Ext2.Sub(t2, &x.C0.B1) - z01 = e.Ext2.Double(z01) - z01 = e.Ext2.Add(z01, t2) - z02 := e.Ext2.Sub(t4, &x.C0.B2) - z02 = e.Ext2.Double(z02) - z02 = e.Ext2.Add(z02, t4) - z10 := e.Ext2.Add(t8, &x.C1.B0) - z10 = e.Ext2.Double(z10) - z10 = e.Ext2.Add(z10, t8) - z11 := e.Ext2.Add(t6, &x.C1.B1) - z11 = e.Ext2.Double(z11) - z11 = e.Ext2.Add(z11, t6) - z12 := e.Ext2.Add(t7, &x.C1.B2) - z12 = e.Ext2.Double(z12) - z12 = e.Ext2.Add(z12, t7) +func (e Ext12) Mul(x, y *E12) *E12 { + return e.mulDirect(x, y) +} + +func (e Ext12) mulDirect(a, b *E12) *E12 { + + // a = a11 w^11 + a10 w^10 + a9 w^9 + a8 w^8 + a7 w^7 + a6 w^6 + a5 w^5 + a4 w^4 + a3 w^3 + a2 w^2 + a1 w + a0 + // b = b11 w^11 + b10 w^10 + b9 w^9 + b8 w^8 + b7 w^7 + b6 w^6 + b5 w^5 + b4 w^4 + b3 w^3 + b2 w^2 + b1 w + b0 + // + // Given that w^12 = 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 + + // 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}) + + // 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}) + + // 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}) + + // 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}) + + // 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{ - C0: E6{ - B0: *z00, - B1: *z01, - B2: *z02, - }, - C1: E6{ - B0: *z10, - B1: *z11, - B2: *z12, - }, + A0: *d0, + A1: *d1, + A2: *d2, + A3: *d3, + A4: *d4, + A5: *d5, + A6: *d6, + A7: *d7, + A8: *d8, + A9: *d9, + A10: *d10, + A11: *d11, } } -func (e Ext12) 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) Square(x *E12) *E12 { + return e.squareDirect(x) } -func (e Ext12) AssertIsEqual(x, y *E12) { - e.Ext6.AssertIsEqual(&x.C0, &y.C0) - e.Ext6.AssertIsEqual(&x.C1, &y.C1) -} +func (e Ext12) squareDirect(a *E12) *E12 { -func FromE12(y *bn254.E12) E12 { - return E12{ - C0: FromE6(&y.C0), - C1: FromE6(&y.C1), + 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, } +} + +// Granger-Scott's cyclotomic square +// https://eprint.iacr.org/2009/565.pdf, 3.2 +func (e Ext12) CyclotomicSquareGS(x *E12) *E12 { + tower := e.ToTower(x) + + mone := e.fp.NewElement(-1) + z000 := e.fp.Eval([][]*baseEl{{tower[8], tower[8]}, {mone, tower[9], tower[9]}, {mone, tower[8], tower[9]}, {tower[0], tower[0]}, {mone, tower[1], tower[1]}, {mone, tower[0]}}, []int{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 { - res, err := e.fp.NewHint(inverseE12Hint, 12, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) + res, err := e.fp.NewHint(inverseE12Hint, 12, &x.A0, &x.A1, &x.A2, &x.A3, &x.A4, &x.A5, &x.A6, &x.A7, &x.A8, &x.A9, &x.A10, &x.A11) if err != nil { // err is non-nil only for invalid number of inputs panic(err) } - inv := E12{ - C0: E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - }, - C1: E6{ - B0: E2{A0: *res[6], A1: *res[7]}, - B1: E2{A0: *res[8], A1: *res[9]}, - B2: E2{A0: *res[10], A1: *res[11]}, - }, - } - + inv := E12{A0: *res[0], A1: *res[1], A2: *res[2], A3: *res[3], A4: *res[4], A5: *res[5], A6: *res[6], A7: *res[7], A8: *res[8], A9: *res[9], A10: *res[10], A11: *res[11]} one := e.One() // 1 == inv * x @@ -223,41 +427,328 @@ func (e Ext12) Inverse(x *E12) *E12 { } func (e Ext12) DivUnchecked(x, y *E12) *E12 { - res, err := e.fp.NewHint(divE12Hint, 12, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1, &y.C0.B0.A0, &y.C0.B0.A1, &y.C0.B1.A0, &y.C0.B1.A1, &y.C0.B2.A0, &y.C0.B2.A1, &y.C1.B0.A0, &y.C1.B0.A1, &y.C1.B1.A0, &y.C1.B1.A1, &y.C1.B2.A0, &y.C1.B2.A1) - + res, err := e.fp.NewHint(divE12Hint, 12, &x.A0, &x.A1, &x.A2, &x.A3, &x.A4, &x.A5, &x.A6, &x.A7, &x.A8, &x.A9, &x.A10, &x.A11, &y.A0, &y.A1, &y.A2, &y.A3, &y.A4, &y.A5, &y.A6, &y.A7, &y.A8, &y.A9, &y.A10, &y.A11) if err != nil { // err is non-nil only for invalid number of inputs panic(err) } - div := E12{ - C0: E6{ - B0: E2{A0: *res[0], A1: *res[1]}, - B1: E2{A0: *res[2], A1: *res[3]}, - B2: E2{A0: *res[4], A1: *res[5]}, - }, - C1: E6{ - B0: E2{A0: *res[6], A1: *res[7]}, - B1: E2{A0: *res[8], A1: *res[9]}, - B2: E2{A0: *res[10], A1: *res[11]}, - }, - } + div := E12{A0: *res[0], A1: *res[1], A2: *res[2], A3: *res[3], A4: *res[4], A5: *res[5], A6: *res[6], A7: *res[7], A8: *res[8], A9: *res[9], A10: *res[10], A11: *res[11]} - // x == div * y + // x = div * y _x := e.Mul(&div, y) e.AssertIsEqual(x, _x) return &div + +} + +func (e Ext12) AssertIsEqual(a, b *E12) { + e.fp.AssertIsEqual(&a.A0, &b.A0) + e.fp.AssertIsEqual(&a.A1, &b.A1) + e.fp.AssertIsEqual(&a.A2, &b.A2) + e.fp.AssertIsEqual(&a.A3, &b.A3) + e.fp.AssertIsEqual(&a.A4, &b.A4) + e.fp.AssertIsEqual(&a.A5, &b.A5) + e.fp.AssertIsEqual(&a.A6, &b.A6) + e.fp.AssertIsEqual(&a.A7, &b.A7) + e.fp.AssertIsEqual(&a.A8, &b.A8) + e.fp.AssertIsEqual(&a.A9, &b.A9) + e.fp.AssertIsEqual(&a.A10, &b.A10) + e.fp.AssertIsEqual(&a.A11, &b.A11) +} + +func (e Ext12) IsEqual(x, y *E12) frontend.Variable { + diff0 := e.fp.Sub(&x.A0, &y.A0) + diff1 := e.fp.Sub(&x.A1, &y.A1) + diff2 := e.fp.Sub(&x.A2, &y.A2) + diff3 := e.fp.Sub(&x.A3, &y.A3) + diff4 := e.fp.Sub(&x.A4, &y.A4) + diff5 := e.fp.Sub(&x.A5, &y.A5) + diff6 := e.fp.Sub(&x.A6, &y.A6) + diff7 := e.fp.Sub(&x.A7, &y.A7) + diff8 := e.fp.Sub(&x.A8, &y.A8) + diff9 := e.fp.Sub(&x.A9, &y.A9) + diff10 := e.fp.Sub(&x.A10, &y.A10) + diff11 := e.fp.Sub(&x.A11, &y.A11) + isZero0 := e.fp.IsZero(diff0) + isZero1 := e.fp.IsZero(diff1) + isZero2 := e.fp.IsZero(diff2) + isZero3 := e.fp.IsZero(diff3) + isZero4 := e.fp.IsZero(diff4) + isZero5 := e.fp.IsZero(diff5) + isZero6 := e.fp.IsZero(diff6) + isZero7 := e.fp.IsZero(diff7) + isZero8 := e.fp.IsZero(diff8) + isZero9 := e.fp.IsZero(diff9) + isZero10 := e.fp.IsZero(diff10) + isZero11 := e.fp.IsZero(diff11) + + return e.api.And( + e.api.And( + e.api.And(e.api.And(isZero0, isZero1), e.api.And(isZero2, isZero3)), + e.api.And(e.api.And(isZero4, isZero5), e.api.And(isZero6, isZero7)), + ), + e.api.And(e.api.And(isZero8, isZero9), e.api.And(isZero10, isZero11)), + ) +} + +func (e Ext12) Copy(x *E12) *E12 { + return &E12{ + A0: x.A0, + A1: x.A1, + A2: x.A2, + A3: x.A3, + A4: x.A4, + A5: x.A5, + A6: x.A6, + A7: x.A7, + A8: x.A8, + A9: x.A9, + A10: x.A10, + A11: x.A11, + } +} + +func (e Ext12) Frobenius(a *E12) *E12 { + tower := e.ToTower(a) + + tower[1] = e.fp.Neg(tower[1]) + tower[3] = e.fp.Neg(tower[3]) + tower[5] = e.fp.Neg(tower[5]) + tower[7] = e.fp.Neg(tower[7]) + tower[9] = e.fp.Neg(tower[9]) + tower[11] = e.fp.Neg(tower[11]) + + t1 := e.Ext2.MulByNonResidue1Power2(&E2{A0: *tower[2], A1: *tower[3]}) + t2 := e.Ext2.MulByNonResidue1Power4(&E2{A0: *tower[4], A1: *tower[5]}) + t3 := e.Ext2.MulByNonResidue1Power1(&E2{A0: *tower[6], A1: *tower[7]}) + t4 := e.Ext2.MulByNonResidue1Power3(&E2{A0: *tower[8], A1: *tower[9]}) + t5 := e.Ext2.MulByNonResidue1Power5(&E2{A0: *tower[10], A1: *tower[11]}) + + 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)) + 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: *tower[1], + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + A11: t5.A1, + } +} + +func (e Ext12) FrobeniusSquare(a *E12) *E12 { + tower := e.ToTower(a) + + t1 := e.Ext2.MulByNonResidue2Power2(&E2{A0: *tower[2], A1: *tower[3]}) + t2 := e.Ext2.MulByNonResidue2Power4(&E2{A0: *tower[4], A1: *tower[5]}) + t3 := e.Ext2.MulByNonResidue2Power1(&E2{A0: *tower[6], A1: *tower[7]}) + t4 := e.Ext2.MulByNonResidue2Power3(&E2{A0: *tower[8], A1: *tower[9]}) + t5 := e.Ext2.MulByNonResidue2Power5(&E2{A0: *tower[10], A1: *tower[11]}) + + 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)) + 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: *tower[1], + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + A11: t5.A1, + } +} + +func (e Ext12) FrobeniusCube(a *E12) *E12 { + tower := e.ToTower(a) + + tower[1] = e.fp.Neg(tower[1]) + tower[3] = e.fp.Neg(tower[3]) + tower[5] = e.fp.Neg(tower[5]) + tower[7] = e.fp.Neg(tower[7]) + tower[9] = e.fp.Neg(tower[9]) + tower[11] = e.fp.Neg(tower[11]) + + t1 := e.Ext2.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]}) + + 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)) + 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: *tower[1], + A7: t3.A1, + A8: t1.A1, + A9: t4.A1, + A10: t2.A1, + 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) 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) ToTower(a *E12) [12]*baseEl { + // gnark-crypto uses a quadratic over cubic over quadratic 12th extension of Fp. + // The two towers are isomorphic and the coefficients are permuted as follows: + // + // tower = a000 a001 a010 a011 a020 a021 a100 a101 a110 a111 a120 a121 + // direct = a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 + // + // a000 = A0 + 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 + + tower := [12]*baseEl{a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121} + return tower } -func (e Ext12) Lookup2(s1, s2 frontend.Variable, a, b, c, d *E12) *E12 { - c0 := e.Ext6.Lookup2(s1, s2, &a.C0, &b.C0, &c.C0, &d.C0) - c1 := e.Ext6.Lookup2(s1, s2, &a.C1, &b.C1, &c.C1, &d.C1) - return &E12{C0: *c0, C1: *c1} +func (e Ext12) 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 + // A3 = a110 - 9 * a111 + // A4 = a020 - 9 * a021 + // A5 = a120 - 9 * a121 + // 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, + A1: *A1, + A2: *A2, + A3: *A3, + A4: *A4, + A5: *A5, + A6: *tower[1], + A7: *tower[7], + A8: *tower[3], + A9: *tower[9], + A10: *tower[5], + A11: *tower[11], + } } diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index 2e0411afed..b2e77cf67c 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -1,16 +1,80 @@ package fields_bn254 import ( - "github.com/consensys/gnark/std/math/emulated" + "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.CyclotomicSquare(z) + z = e.CyclotomicSquareGS(z) } 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: @@ -39,387 +103,240 @@ 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) - - return z -} + z = e.nSquare(z, 3) -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 +// MulBy01379 multiplies a by an E12 sparse element b of the form // -// 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, - } -} +// 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}) -// 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, + A0: *d0, + A1: *d1, + A2: *d2, + A3: *d3, + A4: *d4, + A5: *d5, + A6: *d6, + A7: *d7, + A8: *d8, + A9: *d9, + A10: *d10, + A11: *d11, } } -// 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. +// Mul01379By01379 multiplies two E12 sparse element of the form: // -// 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 +// 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) [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)) + 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{{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) + + // 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}) + + // 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}) + + return [10]*baseEl{d0, d1, d2, d3, d4, d6, d7, d8, d9, d10} } -// 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 +// MulBy012346789 multiplies a by an E12 sparse element b of the form // -// It uses a hint to verify that (2x-y)y = v saving one E6 AssertIsEqual. -func (e Ext12) SquareTorus(y *E6) *E6 { - res, err := e.fp.NewHint(squareTorusHint, 6, &y.B0.A0, &y.B0.A1, &y.B1.A0, &y.B1.A1, &y.B2.A0, &y.B2.A1) - if err != nil { - // err is non-nil only for invalid number of inputs - panic(err) - } +// b.A0 = 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}) - 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]}, - } + // 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}) - // v = (2x-y)y - v := e.Ext6.Double(&sq) - v = e.Ext6.Sub(v, y) - v = e.Ext6.Mul(v, y) + // 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}) - _v := E6{B0: *e.Ext2.Zero(), B1: *e.Ext2.One(), B2: *e.Ext2.Zero()} - e.Ext6.AssertIsEqual(v, &_v) + // 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}) - return &sq + // 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}) -// 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) + // 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}) - 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) + // 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}) - return res -} + // 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}) -// 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} -} + // 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}) -// 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) + // 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}) - 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) + // 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 res + 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 @@ -428,115 +345,63 @@ func (e Ext12) FrobeniusCubeTorus(y *E6) *E6 { // 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) +func (e Ext12) AssertFinalExponentiationIsOne(a *E12) { + 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{ - 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]}, - }, - } + 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 - cubicNonResiduePower := 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]}, + // 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: *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^λ // where λ = 6u + 2 + q^3 - q^2 + q, with u the BN254 seed // and residueWitness, cubicNonResiduePower from the hint. - t2 := &E12{ - C0: *e.Ext6.Mul(&x.C0, &cubicNonResiduePower), - C1: *e.Ext6.Mul(&x.C1, &cubicNonResiduePower), - } + 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) 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..b2f0ae68a5 100644 --- a/std/algebra/emulated/fields_bn254/e12_test.go +++ b/std/algebra/emulated/fields_bn254/e12_test.go @@ -9,6 +9,34 @@ import ( "github.com/consensys/gnark/test" ) +type e12Convert struct { + A E12 +} + +func (circuit *e12Convert) Define(api frontend.API) error { + e := NewExt12(api) + tower := e.ToTower(&circuit.A) + expected := e.FromTower(tower) + e.AssertIsEqual(expected, &circuit.A) + return nil +} + +func TestConvertFp12(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a 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 } @@ -162,81 +190,18 @@ func TestSquareFp12(t *testing.T) { } -type e12Conjugate struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *e12Conjugate) Define(api frontend.API) error { - e := NewExt12(api) - expected := e.Conjugate(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestConjugateFp12(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bn254.E12 - _, _ = a.SetRandom() - c.Conjugate(&a) - - witness := e12Conjugate{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&e12Conjugate{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e12Inverse struct { - A E12 - C E12 `gnark:",public"` -} - -func (circuit *e12Inverse) Define(api frontend.API) error { - e := NewExt12(api) - expected := e.Inverse(&circuit.A) - e.AssertIsEqual(expected, &circuit.C) - - return nil -} - -func TestInverseFp12(t *testing.T) { - - assert := test.NewAssert(t) - // witness values - var a, c bn254.E12 - _, _ = a.SetRandom() - c.Inverse(&a) - - witness := e12Inverse{ - A: FromE12(&a), - C: FromE12(&c), - } - - err := test.IsSolved(&e12Inverse{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e12ExptTorus struct { - A E6 - C E12 `gnark:",public"` +type e12SquareGS struct { + A, C E12 } -func (circuit *e12ExptTorus) Define(api frontend.API) error { +func (circuit *e12SquareGS) Define(api frontend.API) error { e := NewExt12(api) - z := e.ExptTorus(&circuit.A) - expected := e.DecompressTorus(z) + expected := e.CyclotomicSquareGS(&circuit.A) e.AssertIsEqual(expected, &circuit.C) - return nil } -func TestFp12ExptTorus(t *testing.T) { +func TestSquareGSFp12(t *testing.T) { assert := test.NewAssert(t) // witness values @@ -250,371 +215,316 @@ func TestFp12ExptTorus(t *testing.T) { tmp.Mul(&tmp, &a) a.FrobeniusSquare(&tmp).Mul(&a, &tmp) - c.Expt(&a) - _a, _ := a.CompressTorus() - witness := e12ExptTorus{ - A: FromE6(&_a), - C: FromE12(&c), - } - - err := test.IsSolved(&e12ExptTorus{}, &witness, ecc.BN254.ScalarField()) - 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) + c.Square(&a) - witness := e12MulBy034{ + witness := e12SquareGS{ A: FromE12(&a), - B: FromE2(&b), - C: FromE2(&c), - W: FromE12(&w), + C: FromE12(&c), } - err := test.IsSolved(&e12MulBy034{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12SquareGS{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -// Torus-based arithmetic -type torusCompress struct { +type e12Conjugate struct { A E12 - C E6 `gnark:",public"` + C E12 `gnark:",public"` } -func (circuit *torusCompress) Define(api frontend.API) error { +func (circuit *e12Conjugate) Define(api frontend.API) error { e := NewExt12(api) - expected := e.CompressTorus(&circuit.A) - e.Ext6.AssertIsEqual(expected, &circuit.C) + expected := e.Conjugate(&circuit.A) + e.AssertIsEqual(expected, &circuit.C) + return nil } -func TestTorusCompress(t *testing.T) { +func TestConjugateFp12(t *testing.T) { assert := test.NewAssert(t) // witness values - var a bn254.E12 + var a, c bn254.E12 _, _ = a.SetRandom() + c.Conjugate(&a) - // 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{ + witness := e12Conjugate{ A: FromE12(&a), - C: FromE6(&c), + C: FromE12(&c), } - err := test.IsSolved(&torusCompress{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12Conjugate{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusDecompress struct { +type e12Inverse struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusDecompress) Define(api frontend.API) error { +func (circuit *e12Inverse) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - expected := e.DecompressTorus(compressed) + expected := e.Inverse(&circuit.A) e.AssertIsEqual(expected, &circuit.C) + return nil } -func TestTorusDecompress(t *testing.T) { +func TestInverseFp12(t *testing.T) { assert := test.NewAssert(t) // witness values - var a bn254.E12 + var a, c bn254.E12 _, _ = a.SetRandom() + c.Inverse(&a) - // 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{ + witness := e12Inverse{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusDecompress{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12Inverse{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusMul struct { +type Frobenius struct { A E12 - B E12 C E12 `gnark:",public"` } -func (circuit *torusMul) Define(api frontend.API) error { +func (circuit *Frobenius) Define(api frontend.API) error { e := NewExt12(api) - compressedA := e.CompressTorus(&circuit.A) - compressedB := e.CompressTorus(&circuit.B) - compressedAB := e.MulTorus(compressedA, compressedB) - expected := e.DecompressTorus(compressedAB) + expected := e.Frobenius(&circuit.A) e.AssertIsEqual(expected, &circuit.C) return nil } -func TestTorusMul(t *testing.T) { +func TestFrobenius(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, b, c, tmp bn254.E12 + var a, c 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) + c.Frobenius(&a) - witness := torusMul{ + witness := Frobenius{ A: FromE12(&a), - B: FromE12(&b), C: FromE12(&c), } - err := test.IsSolved(&torusMul{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&Frobenius{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusInverse struct { +type FrobeniusSquare struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusInverse) Define(api frontend.API) error { +func (circuit *FrobeniusSquare) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.InverseTorus(compressed) - expected := e.DecompressTorus(compressed) + expected := e.FrobeniusSquare(&circuit.A) e.AssertIsEqual(expected, &circuit.C) return nil } -func TestTorusInverse(t *testing.T) { +func TestFrobeniusSquare(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bn254.E12 + var a, c 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) + c.FrobeniusSquare(&a) - witness := torusInverse{ + witness := FrobeniusSquare{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusInverse{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&FrobeniusSquare{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusFrobenius struct { +type FrobeniusCube struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusFrobenius) Define(api frontend.API) error { +func (circuit *FrobeniusCube) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.FrobeniusTorus(compressed) - expected := e.DecompressTorus(compressed) + expected := e.FrobeniusCube(&circuit.A) e.AssertIsEqual(expected, &circuit.C) return nil } -func TestTorusFrobenius(t *testing.T) { +func TestFrobeniusCube(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bn254.E12 + var a, c 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) + c.FrobeniusCube(&a) - witness := torusFrobenius{ + witness := FrobeniusCube{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusFrobenius{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&FrobeniusCube{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -type torusFrobeniusSquare struct { - A E12 - C E12 `gnark:",public"` +type e12MulBy01379 struct { + A E12 `gnark:",public"` + W E12 + B, C E2 } -func (circuit *torusFrobeniusSquare) Define(api frontend.API) error { +func (circuit *e12MulBy01379) 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) + res := e.MulBy01379(&circuit.A, &circuit.B, &circuit.C) + e.AssertIsEqual(res, &circuit.W) return nil } -func TestTorusFrobeniusSquare(t *testing.T) { +func TestFp12MulBy01379(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bn254.E12 + 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) - // 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{ + witness := e12MulBy01379{ A: FromE12(&a), - C: FromE12(&c), + B: FromE2(&b), + C: FromE2(&c), + W: FromE12(&w), } - err := test.IsSolved(&torusFrobeniusSquare{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12MulBy01379{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) + } -type torusFrobeniusCube struct { - A E12 - C E12 `gnark:",public"` +type e12Mul01379By01379 struct { + A, B E2 `gnark:",public"` + C, D E2 `gnark:",public"` + W E12 } -func (circuit *torusFrobeniusCube) Define(api frontend.API) error { +func (circuit *e12Mul01379By01379) 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) + res := e.Mul01379By01379(&circuit.A, &circuit.B, &circuit.C, &circuit.D) + 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 } -func TestTorusFrobeniusCube(t *testing.T) { +func TestFp12Mul01379By01379(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bn254.E12 + var one, a, b, c, d bn254.E2 + one.SetOne() _, _ = 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), + _, _ = 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(&torusFrobeniusCube{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e12Mul01379By01379{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) + } -type torusSquare struct { +type e12Expt struct { A E12 C E12 `gnark:",public"` } -func (circuit *torusSquare) Define(api frontend.API) error { +func (circuit *e12Expt) Define(api frontend.API) error { e := NewExt12(api) - compressed := e.CompressTorus(&circuit.A) - compressed = e.SquareTorus(compressed) - expected := e.DecompressTorus(compressed) + expected := e.Expt(&circuit.A) e.AssertIsEqual(expected, &circuit.C) + return nil } -func TestTorusSquare(t *testing.T) { +func TestFp12Expt(t *testing.T) { assert := test.NewAssert(t) // witness values - var a, c, tmp bn254.E12 + 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) - // uncompressed square - c.Square(&a) - - witness := torusSquare{ + c.Expt(&a) + witness := e12Expt{ A: FromE12(&a), C: FromE12(&c), } - err := test.IsSolved(&torusSquare{}, &witness, ecc.BN254.ScalarField()) + 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 { + 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/fields_bn254/e2.go b/std/algebra/emulated/fields_bn254/e2.go index 5d38c50eaf..7af33cbd86 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,22 @@ 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, + } +} + +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, 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/fields_bn254/hints.go b/std/algebra/emulated/fields_bn254/hints.go index c5d68ed67d..0cdb2fa4fc 100644 --- a/std/algebra/emulated/fields_bn254/hints.go +++ b/std/algebra/emulated/fields_bn254/hints.go @@ -19,11 +19,6 @@ func GetHints() []solver.Hint { // E2 divE2Hint, inverseE2Hint, - // E6 - divE6Hint, - inverseE6Hint, - squareTorusHint, - divE6By6Hint, // E12 divE12Hint, inverseE12Hint, @@ -67,153 +62,71 @@ 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, 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.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 +138,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.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 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 def19d2654..10cc8e0297 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -6,54 +6,54 @@ 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" "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 + *fields_bn254.Ext2 curveF *emulated.Field[BaseField] curve *sw_emulated.Curve[BaseField, ScalarField] g2 *G2 bTwist *fields_bn254.E2 - 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) + 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) -func NewGTEl(v bn254.GT) GTEl { 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), } } @@ -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), @@ -80,150 +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 -} - -// 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ᵢ). // @@ -233,10 +90,55 @@ func (pr Pairing) Pair(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { if err != nil { return nil, fmt.Errorf("miller loop: %w", err) } - res = pr.finalExponentiation(res, len(P) == 1) + res = pr.FinalExponentiation(res) return res, nil } +// 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 // @@ -247,15 +149,6 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { return err } - // We perform the easy part of the final exp to push f to the cyclotomic - // subgroup so that AssertFinalExponentiationIsOne is carried with optimized - // cyclotomic squaring (e.g. Karabina12345). - // - // f = f^(p⁶-1)(p²+1) - buf := pr.Conjugate(f) - buf = pr.DivUnchecked(buf, f) - f = pr.FrobeniusSquare(buf) - f = pr.Mul(f, buf) pr.AssertFinalExponentiationIsOne(f) @@ -283,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 } @@ -390,8 +282,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. @@ -402,114 +294,89 @@ 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() + var prodLines [10]*baseEl // 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, - }, + // i = 64 + // + // k = 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(), + 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 ℓ × ℓ) + // k = 1, separately to avoid MulBy01379 (res × ℓ) + // (res is also a line at this point, so we use Mul01379By01379 ℓ × ℓ) // 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, + 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 = &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, - }, + 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, 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]), - ) - - // k >= 3 - for k := 3; k < n; k++ { + // k >= 2 + for k := 2; k < n; k++ { // line evaluation at P[k] // ℓ × res - res = pr.MulBy034( + 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][64].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][64].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( + res = pr.Ext12.Square(res) + + for k := 0; k < n; k++ { + if loopCounter[i] == 0 { + 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]), + pr.Ext2.MulByElement(&lines[k][0][i].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][i].R1, yInv[k]), ) - } - - // mul lines 2-by-2 - for k := 1; k < n; k += 2 { + } else { // ℓ × ℓ - 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]), - ) - // (ℓ × ℓ) × 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]), + 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.MulBy01234(res, prodLines) - + res = pr.Ext12.MulBy012346789(res, prodLines) } } } @@ -518,14 +385,13 @@ 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]), + prodLines = pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[k][0][65].R0, xNegOverY[k]), + pr.Ext2.MulByElement(&lines[k][0][65].R1, yInv[k]), + 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) + res = pr.Ext12.MulBy012346789(res, prodLines) } return res, nil @@ -538,6 +404,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 @@ -550,15 +417,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)) @@ -568,21 +436,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 } @@ -593,6 +463,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) @@ -601,20 +472,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 @@ -624,19 +497,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 @@ -644,8 +521,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 @@ -654,6 +531,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) @@ -661,8 +540,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 @@ -677,42 +556,56 @@ 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 { + 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, &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) + 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 := 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]}) - 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.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]}, + // 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 + nine := big.NewInt(9) + cubicNonResiduePower := GTEl{ + A0: *pr.curveF.Sub(hint[12], pr.curveF.MulConst(hint[13], nine)), + A1: *pr.curveF.Zero(), + A2: *pr.curveF.Sub(hint[14], pr.curveF.MulConst(hint[15], nine)), + A3: *pr.curveF.Zero(), + A4: *pr.curveF.Sub(hint[16], pr.curveF.MulConst(hint[17], nine)), + A5: *pr.curveF.Zero(), + A6: *hint[13], + A7: *pr.curveF.Zero(), + A8: *hint[15], + A9: *pr.curveF.Zero(), + A10: *hint[17], + A11: *pr.curveF.Zero(), } // residueWitnessInv = 1 / residueWitness - residueWitnessInv := pr.Inverse(&residueWitness) + residueWitnessInv := pr.Ext12.Inverse(residueWitness) if Q.Lines == nil { Qlines := pr.computeLines(&Q.P) @@ -731,42 +624,42 @@ func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous // Compute f_{6x₀+2,Q}(P) for i := 64; i >= 0; i-- { - res = pr.Square(res) + res = pr.Ext12.Square(res) switch loopCounter[i] { case 0: // ℓ × res - res = pr.MulBy034( + res = pr.MulBy01379( res, - pr.MulByElement(&lines[0][i].R0, xNegOverY), - pr.MulByElement(&lines[0][i].R1, yInv), + pr.Ext2.MulByElement(&lines[0][i].R0, xNegOverY), + pr.Ext2.MulByElement(&lines[0][i].R1, yInv), ) case 1: // multiply by residueWitnessInv when bit=1 - res = pr.Mul(res, residueWitnessInv) + res = pr.Ext12.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), + prodLines := pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[0][i].R0, xNegOverY), + pr.Ext2.MulByElement(&lines[0][i].R1, yInv), + pr.Ext2.MulByElement(&lines[1][i].R0, xNegOverY), + pr.Ext2.MulByElement(&lines[1][i].R1, yInv), ) // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) + res = pr.Ext12.MulBy012346789(res, prodLines) case -1: // multiply by residueWitness when bit=-1 - res = pr.Mul(res, &residueWitness) + res = pr.Ext12.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), + prodLines := pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[0][i].R0, xNegOverY), + pr.Ext2.MulByElement(&lines[0][i].R1, yInv), + pr.Ext2.MulByElement(&lines[1][i].R0, xNegOverY), + pr.Ext2.MulByElement(&lines[1][i].R1, yInv), ) // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) + res = pr.Ext12.MulBy012346789(res, prodLines) default: panic(fmt.Sprintf("invalid loop counter value %d", loopCounter[i])) } @@ -774,36 +667,31 @@ func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous // 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), + prodLines := pr.Mul01379By01379( + pr.Ext2.MulByElement(&lines[0][65].R0, xNegOverY), + pr.Ext2.MulByElement(&lines[0][65].R1, yInv), + pr.Ext2.MulByElement(&lines[1][65].R0, xNegOverY), + pr.Ext2.MulByElement(&lines[1][65].R1, yInv), ) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, prodLines) + res = pr.Ext12.MulBy012346789(res, prodLines) // multiply by previous multi-Miller function - res = pr.Mul(res, previous) + res = pr.Ext12.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 := &fields_bn254.E12{ - C0: *pr.Ext6.Mul(&res.C0, &cubicNonResiduePower), - C1: *pr.Ext6.Mul(&res.C1, &cubicNonResiduePower), - } + t2 := pr.Ext12.Mul(&cubicNonResiduePower, res) t1 := pr.FrobeniusCube(residueWitnessInv) t0 := pr.FrobeniusSquare(residueWitnessInv) - t1 = pr.DivUnchecked(t1, t0) + t1 = pr.Ext12.DivUnchecked(t1, t0) t0 = pr.Frobenius(residueWitnessInv) - t1 = pr.Mul(t1, t0) + t1 = pr.Ext12.Mul(t1, t0) - t2 = pr.Mul(t2, t1) + t2 = pr.Ext12.Mul(t2, t1) return t2 } @@ -821,7 +709,7 @@ func (pr Pairing) millerLoopAndFinalExpResult(P *G1Affine, Q *G2Affine, previous func (pr Pairing) IsMillerLoopAndFinalExpOne(P *G1Affine, Q *G2Affine, previous *GTEl) frontend.Variable { t2 := pr.millerLoopAndFinalExpResult(P, Q, previous) - res := pr.IsEqual(t2, pr.One()) + res := pr.IsEqual(t2, pr.Ext12.One()) return res } @@ -836,5 +724,5 @@ func (pr Pairing) IsMillerLoopAndFinalExpOne(P *G1Affine, Q *G2Affine, previous // [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()) + pr.AssertIsEqual(t2, pr.Ext12.One()) } diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index eb84bf9b98..ae2fe87e80 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -33,20 +33,18 @@ func randomG1G2Affines() (bn254.G1Affine, bn254.G2Affine) { return p, q } -type FinalExponentiationCircuit struct { +type FinalExponentiation struct { InGt GTEl Res GTEl } -func (c *FinalExponentiationCircuit) Define(api frontend.API) error { +func (c *FinalExponentiation) 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) + expected := pairing.FinalExponentiation(&c.InGt) + pairing.AssertIsEqual(expected, &c.Res) return nil } @@ -55,93 +53,45 @@ func TestFinalExponentiationTestSolve(t *testing.T) { var gt bn254.GT gt.SetRandom() res := bn254.FinalExponentiation(>) - witness := FinalExponentiationCircuit{ + witness := FinalExponentiation{ InGt: NewGTEl(gt), Res: NewGTEl(res), } - err := test.IsSolved(&FinalExponentiationCircuit{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&FinalExponentiation{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -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 +type FinalExponentiationIsOne struct { + InGt GTEl } -func (c *MillerLoopAndMulCircuit) Define(api frontend.API) error { +func (c *FinalExponentiationIsOne) 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) + pairing.AssertFinalExponentiationIsOne(&c.InGt) return nil - } -func TestMillerLoopAndMulTestSolve(t *testing.T) { +func TestFinalExponentiationIsOneTestSolve(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}, + // 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}, ) assert.NoError(err) - curr.Mul(&prev, &mlres) - - witness := MillerLoopAndMulCircuit{ - Prev: NewGTEl(prev), - P: NewG1Affine(p), - Q: NewG2Affine(q), - Current: NewGTEl(curr), + witness := FinalExponentiationIsOne{ + InGt: NewGTEl(ml), } - err = test.IsSolved(&MillerLoopAndMulCircuit{}, &witness, ecc.BN254.ScalarField()) + err = test.IsSolved(&FinalExponentiationIsOne{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -156,8 +106,6 @@ func (c *PairCircuit) Define(api frontend.API) error { 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) @@ -544,65 +492,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) - } - } - }) -} 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) 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)