diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index f5be61615f..94706f3c86 100644 --- a/internal/stats/latest_stats.csv +++ b/internal/stats/latest_stats.csv @@ -195,14 +195,14 @@ pairing_bn254,bls24_315,plonk,0,0 pairing_bn254,bls24_317,plonk,0,0 pairing_bn254,bw6_761,plonk,0,0 pairing_bn254,bw6_633,plonk,0,0 -pairing_bw6761,bn254,groth16,2592181,4256159 +pairing_bw6761,bn254,groth16,1794795,3003881 pairing_bw6761,bls12_377,groth16,0,0 pairing_bw6761,bls12_381,groth16,0,0 pairing_bw6761,bls24_315,groth16,0,0 pairing_bw6761,bls24_317,groth16,0,0 pairing_bw6761,bw6_761,groth16,0,0 pairing_bw6761,bw6_633,groth16,0,0 -pairing_bw6761,bn254,plonk,9920293,9270827 +pairing_bw6761,bn254,plonk,6779434,6155114 pairing_bw6761,bls12_377,plonk,0,0 pairing_bw6761,bls12_381,plonk,0,0 pairing_bw6761,bls24_315,plonk,0,0 diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 433b92cc5a..f43cdd28e3 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -190,9 +190,7 @@ func (e Ext6) mulFpByNonResidue(fp *curveF, x *baseEl) *baseEl { } func (e Ext6) Mul(x, y *E6) *E6 { - x = e.Reduce(x) - y = e.Reduce(y) - return e.mulToomCook6(x, y) + return e.mulDirect(x, y) } func (e Ext6) mulMontgomery6(x, y *E6) *E6 { @@ -426,6 +424,37 @@ func (e Ext6) mulMontgomery6(x, y *E6) *E6 { } } +func (e Ext6) mulDirect(x, y *E6) *E6 { + nonResidue := e.fp.NewElement(-4) + // c0 = a0b0 + β(a1b5 + a2b4 + a3b3 + a4b2 + a5b1) + c0 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A0}, {nonResidue, &x.A1, &y.A5}, {nonResidue, &x.A2, &y.A4}, {nonResidue, &x.A3, &y.A3}, {nonResidue, &x.A4, &y.A2}, {nonResidue, &x.A5, &y.A1}}, + []int{1, 1, 1, 1, 1, 1}) + // c1 = a0b1 + a1b0 + β(a2b5 + a3b4 + a4b3 + a5b2) + c1 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A1}, {&x.A1, &y.A0}, {nonResidue, &x.A2, &y.A5}, {nonResidue, &x.A3, &y.A4}, {nonResidue, &x.A4, &y.A3}, {nonResidue, &x.A5, &y.A2}}, + []int{1, 1, 1, 1, 1, 1}) + // c2 = a0b2 + a1b1 + a2b0 + β(a3b5 + a4b4 + a5b3) + c2 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A2}, {&x.A1, &y.A1}, {&x.A2, &y.A0}, {nonResidue, &x.A3, &y.A5}, {nonResidue, &x.A4, &y.A4}, {nonResidue, &x.A5, &y.A3}}, + []int{1, 1, 1, 1, 1, 1}) + // c3 = a0b3 + a1b2 + a2b1 + a3b0 + β(a4b5 + a5b4) + c3 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A3}, {&x.A1, &y.A2}, {&x.A2, &y.A1}, {&x.A3, &y.A0}, {nonResidue, &x.A4, &y.A5}, {nonResidue, &x.A5, &y.A4}}, + []int{1, 1, 1, 1, 1, 1}) + // c4 = a0b4 + a1b3 + a2b2 + a3b1 + a4b0 + βa5b5 + c4 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A4}, {&x.A1, &y.A3}, {&x.A2, &y.A2}, {&x.A3, &y.A1}, {&x.A4, &y.A0}, {nonResidue, &x.A5, &y.A5}}, + []int{1, 1, 1, 1, 1, 1}) + // c5 = a0b5 + a1b4 + a2b3 + a3b2 + a4b1 + a5b0, + c5 := e.fp.Eval([][]*baseEl{{&x.A0, &y.A5}, {&x.A1, &y.A4}, {&x.A2, &y.A3}, {&x.A3, &y.A2}, {&x.A4, &y.A1}, {&x.A5, &y.A0}}, + []int{1, 1, 1, 1, 1, 1}) + + return &E6{ + A0: *c0, + A1: *c1, + A2: *c2, + A3: *c3, + A4: *c4, + A5: *c5, + } +} + func (e Ext6) mulToomCook6(x, y *E6) *E6 { // Toom-Cook 6-way multiplication: // @@ -704,6 +733,42 @@ func (e Ext6) mulToomCook6(x, y *E6) *E6 { } func (e Ext6) Square(x *E6) *E6 { + return e.squareDirect(x) +} + +// squareDirect computes the square of an element in E6 using schoolbook multiplication. +func (e Ext6) squareDirect(x *E6) *E6 { + nonResidue := e.fp.NewElement(-4) + // c0 = a0a0 + β(2*a1a5 + 2*a2a4 + a3a3) + c0 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A0}, {nonResidue, &x.A1, &x.A5}, {nonResidue, &x.A2, &x.A4}, {nonResidue, &x.A3, &x.A3}}, + []int{1, 2, 2, 1}) + // c1 = 2*a0a1 + β(2*a2a5 + 2*a3a4) + c1 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A1}, {nonResidue, &x.A2, &x.A5}, {nonResidue, &x.A3, &x.A4}}, + []int{2, 2, 2}) + // c2 = 2*a0a2 + a1a1 + β(2*a3a5 + a4a4) + c2 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A2}, {&x.A1, &x.A1}, {nonResidue, &x.A3, &x.A5}, {nonResidue, &x.A4, &x.A4}}, + []int{2, 1, 2, 1}) + // c3 = 2*a0a3 + 2*a1a2 + β(2*a4a5) + c3 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A3}, {&x.A1, &x.A2}, {nonResidue, &x.A5, &x.A4}}, + []int{2, 2, 2}) + // c4 = 2*a0a4 + 2*a1a3 + a2a2 + βa5a5 + c4 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A4}, {&x.A1, &x.A3}, {&x.A2, &x.A2}, {nonResidue, &x.A5, &x.A5}}, + []int{2, 2, 1, 1}) + // c5 = 2*a0a5 + 2*a1a4 + 2*a2a3, + c5 := e.fp.Eval([][]*baseEl{{&x.A0, &x.A5}, {&x.A1, &x.A4}, {&x.A2, &x.A3}}, + []int{2, 2, 2}) + + return &E6{ + A0: *c0, + A1: *c1, + A2: *c2, + A3: *c3, + A4: *c4, + A5: *c5, + } +} + +func (e Ext6) squareEmulatedTower(x *E6) *E6 { // We don't use Montgomery-6 or Toom-Cook-6 for the squaring but instead we // simulate a quadratic over cubic extension tower because Karatsuba over // Chung-Hasan SQR2 is better constraint wise. @@ -788,10 +853,94 @@ func (e Ext6) Square(x *E6) *E6 { } } +// Granger-Scott's cyclotomic square +// https://eprint.iacr.org/2009/565.pdf, 3.2 +func (e Ext6) CyclotomicSquareGS(x *E6) *E6 { + return e.cyclotomicSquareGSEval(x) +} + +// cyclotomicSquareGSEval computes [Ext6.CyclotomicSquareGS] but with the non-native Eval method. +func (e Ext6) cyclotomicSquareGSEval(x *E6) *E6 { + // x=(x0,x2,x4,x1,x3,x5) in E6 + // cyclosquare(x) = 3*x4²*u + 3*x0² - 2*x0, + // 6*x1*x5*u + 2*x3, + // 3*x2²*u + 3*x3² - 2*x1, + // 6*x0*x4 + 2*x4, + // 3*x5²*u + 3*x1² - 2*x2, + // 6*x2*x3 + 2*x5, + u := e.fp.NewElement(-4) + mone := e.fp.NewElement(-1) + g0 := x.A0 + g1 := x.A2 + g2 := x.A4 + g3 := x.A1 + g4 := x.A3 + g5 := x.A5 + // h0 = 3*x4²*u + 3*x0² - 2*x0 + h0 := e.fp.Eval([][]*baseEl{{u, &g4, &g4}, {&g0, &g0}, {mone, &g0}}, []int{3, 3, 2}) + // h1 = 3*x2²*u + 3*x3² - 2*x1 + h1 := e.fp.Eval([][]*baseEl{{u, &g2, &g2}, {&g3, &g3}, {mone, &g1}}, []int{3, 3, 2}) + // h2 = 3*x5²*u + 3*x1² - 2*x2 + h2 := e.fp.Eval([][]*baseEl{{u, &g5, &g5}, {&g1, &g1}, {mone, &g2}}, []int{3, 3, 2}) + // h3 = 6*x1*x5*u + 2*x3 + h3 := e.fp.Eval([][]*baseEl{{u, &g1, &g5}, {&g3}}, []int{6, 2}) + // h4 = 6*x0*x4 + 2*x4 + h4 := e.fp.Eval([][]*baseEl{{&g0, &g4}, {&g4}}, []int{6, 2}) + // h5 = 6*x2*x3 + 2*x5 + h5 := e.fp.Eval([][]*baseEl{{&g2, &g3}, {&g5}}, []int{6, 2}) + return &E6{ + A0: *h0, + A1: *h3, + A2: *h1, + A3: *h4, + A4: *h2, + A5: *h5, + } +} + // Karabina's compressed cyclotomic square SQR12345 // https://eprint.iacr.org/2010/542.pdf // Sec. 5.6 with minor modifications to fit our tower func (e Ext6) CyclotomicSquareKarabina12345(x *E6) *E6 { + return e.cyclotomicSquareKarabina12345Eval(x) +} + +// cyclotomicSquareKarabina12345Eval computes +// [Ext6.cyclotomicSquareKarabina12345] but with the non-native Eval method. +func (e Ext6) cyclotomicSquareKarabina12345Eval(x *E6) *E6 { + c := e.fp.NewElement(-4) + mone := e.fp.NewElement(-1) + g1 := x.A2 + g2 := x.A4 + g3 := x.A1 + g4 := x.A3 + g5 := x.A5 + // h1 = 3*c*g2^2 + 3*g3^2 - 2*g1 + h1 := e.fp.Eval([][]*baseEl{{c, &g2, &g2}, {&g3, &g3}, {mone, &g1}}, []int{3, 3, 2}) + // h2 = 3*c*g5^2 + 3*g1^2 - 2*g2 + h2 := e.fp.Eval([][]*baseEl{{c, &g5, &g5}, {&g1, &g1}, {mone, &g2}}, []int{3, 3, 2}) + // h3 = 6*c*g1*g5 + 2*g3 + h3 := e.fp.Eval([][]*baseEl{{c, &g1, &g5}, {&g3}}, []int{6, 2}) + // h4 = 3*c*g2*g5 + 3*g1*g3 - g4 + h4 := e.fp.Eval([][]*baseEl{{c, &g2, &g5}, {&g1, &g3}, {mone, &g4}}, []int{3, 3, 1}) + // h5 = 6*g2*g3 + 2*g5 + h5 := e.fp.Eval([][]*baseEl{{&g2, &g3}, {&g5}}, []int{6, 2}) + + return &E6{ + A0: x.A0, + A1: *h3, + A2: *h1, + A3: *h4, + A4: *h2, + A5: *h5, + } + +} + +// Karabina's compressed cyclotomic square SQR12345 +// https://eprint.iacr.org/2010/542.pdf +// Sec. 5.6 with minor modifications to fit our tower +func (e Ext6) cyclotomicSquareKarabina12345(x *E6) *E6 { x = e.Reduce(x) // h4 = -g4 + 3((g3+g5)(g1+c*g2)-g1g5-c*g3g2) @@ -852,6 +1001,31 @@ func (e Ext6) CyclotomicSquareKarabina12345(x *E6) *E6 { // DecompressKarabina12345 decompresses Karabina's cyclotomic square result SQR12345 func (e Ext6) DecompressKarabina12345(x *E6) *E6 { + return e.decompressKarabina12345Eval(x) +} + +// decompressKarabina12345Eval computes [Ext6.DecompressKarabina12345] but with the non-native Eval method. +func (e Ext6) decompressKarabina12345Eval(x *E6) *E6 { + c := e.fp.NewElement(-4) + g1 := x.A2 + g2 := x.A4 + g3 := x.A1 + g4 := x.A3 + g5 := x.A5 + // h0 = -3*c*g1*g2 + 2*c*g4^2 + c*g3*g5 + 1 + h0 := e.fp.Eval([][]*baseEl{{&g1, &g2}, {c, &g4, &g4}, {c, &g3, &g5}, {e.fp.One()}}, []int{12, 2, 1, 1}) + return &E6{ + A0: *h0, + A1: g3, + A2: g1, + A3: g4, + A4: g2, + A5: g5, + } +} + +// DecompressKarabina12345 decompresses Karabina's cyclotomic square result SQR12345 +func (e Ext6) decompressKarabina12345(x *E6) *E6 { x = e.Reduce(x) // h0 = (2g4^2 + g3g5 - 3g2g1)*c + 1 diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index e2715f3cca..5750f7e63f 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -26,7 +26,7 @@ func (e Ext6) ExpX0Minus1(z *E6) *E6 { result = e.Mul(result, z33) result = e.nSquareKarabina12345(result, 4) result = e.Mul(result, z) - result = e.nSquareKarabina12345(result, 1) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) result = e.nSquareKarabina12345(result, 46) @@ -39,11 +39,11 @@ func (e Ext6) ExpX0Minus1Square(z *E6) *E6 { z = e.Reduce(z) result := e.Copy(z) result = e.nSquareKarabina12345(result, 3) - t0 := e.nSquareKarabina12345(result, 1) + t0 := e.CyclotomicSquareGS(result) t2 := e.Mul(z, t0) result = e.Mul(result, t2) t0 = e.Mul(z, result) - t1 := e.nSquareKarabina12345(t0, 1) + t1 := e.CyclotomicSquareGS(t0) t1 = e.Mul(t2, t1) t3 := e.nSquareKarabina12345(t1, 7) t2 = e.Mul(t2, t3) @@ -65,7 +65,7 @@ func (e Ext6) ExpX0Minus1Square(z *E6) *E6 { func (e Ext6) ExpX0Plus1(z *E6) *E6 { z = e.Reduce(z) result := e.Copy(z) - t := e.nSquareKarabina12345(result, 1) + t := e.CyclotomicSquareGS(result) result = e.nSquareKarabina12345(t, 4) result = e.Mul(result, z) z33 := e.Copy(result) @@ -73,7 +73,7 @@ func (e Ext6) ExpX0Plus1(z *E6) *E6 { result = e.Mul(result, z33) result = e.nSquareKarabina12345(result, 4) result = e.Mul(result, z) - result = e.nSquareKarabina12345(result, 1) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) result = e.nSquareKarabina12345(result, 46) result = e.Mul(result, t) @@ -86,9 +86,10 @@ func (e Ext6) ExpX0Plus1(z *E6) *E6 { func (e Ext6) ExptMinus1Div3(z *E6) *E6 { z = e.Reduce(z) result := e.Copy(z) - result = e.nSquareKarabina12345(result, 2) + result = e.CyclotomicSquareGS(result) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) - result = e.nSquareKarabina12345(result, 1) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) t0 := e.nSquareKarabina12345(result, 7) result = e.Mul(result, t0) @@ -105,9 +106,10 @@ func (e Ext6) ExptMinus1Div3(z *E6) *E6 { func (e Ext6) ExpC1(z *E6) *E6 { z = e.Reduce(z) result := e.Copy(z) - result = e.nSquareKarabina12345(result, 2) + result = e.CyclotomicSquareGS(result) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) - result = e.nSquareKarabina12345(result, 1) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) return result @@ -118,11 +120,11 @@ func (e Ext6) ExpC1(z *E6) *E6 { // C2 = (ht**2+3*hy**2)/4 = 103 func (e Ext6) ExpC2(z *E6) *E6 { z = e.Reduce(z) - result := e.nSquareKarabina12345(z, 1) + result := e.CyclotomicSquareGS(z) result = e.Mul(result, z) t0 := e.nSquareKarabina12345(result, 4) result = e.Mul(result, t0) - result = e.nSquareKarabina12345(result, 1) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) return result @@ -132,6 +134,38 @@ func (e Ext6) ExpC2(z *E6) *E6 { // // E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0} func (e *Ext6) MulBy023(z *E6, c0, c1 *baseEl) *E6 { + return e.mulBy023Direct(z, c0, c1) +} + +// mulBy023Direct multiplies z by an E6 sparse element 023 using schoolbook multiplication +func (e Ext6) mulBy023Direct(z *E6, c0, c1 *baseEl) *E6 { + nonResidue := e.fp.NewElement(-4) + + // z0 = a0c0 + β(a3 + a4c1) + z0 := e.fp.Eval([][]*baseEl{{&z.A0, c0}, {nonResidue, &z.A3}, {nonResidue, &z.A4, c1}}, []int{1, 1, 1}) + // z1 = a1c0 + β(a4 + a5c1) + z1 := e.fp.Eval([][]*baseEl{{&z.A1, c0}, {nonResidue, &z.A4}, {nonResidue, &z.A5, c1}}, []int{1, 1, 1}) + // z2 = a0c1 + a2c0 + β(a5) + z2 := e.fp.Eval([][]*baseEl{{&z.A0, c1}, {&z.A2, c0}, {nonResidue, &z.A5}}, []int{1, 1, 1}) + // c3 = a0 + a1c1 + a3c0 + z3 := e.fp.Eval([][]*baseEl{{&z.A0}, {&z.A1, c1}, {&z.A3, c0}}, []int{1, 1, 1}) + // c4 = a1 + a2c1 + a4c0 + z4 := e.fp.Eval([][]*baseEl{{&z.A1}, {&z.A2, c1}, {&z.A4, c0}}, []int{1, 1, 1}) + // c5 = a2 + a3c1 + a5c0, + z5 := e.fp.Eval([][]*baseEl{{&z.A2}, {&z.A3, c1}, {&z.A5, c0}}, []int{1, 1, 1}) + + return &E6{ + A0: *z0, + A1: *z1, + A2: *z2, + A3: *z3, + A4: *z4, + A5: *z5, + } +} + +// mulBy023 multiplies z by an E6 sparse element 023 +func (e Ext6) mulBy023(z *E6, c0, c1 *baseEl) *E6 { z = e.Reduce(z) a := e.fp.Mul(&z.A0, c0) @@ -198,7 +232,7 @@ func (e *Ext6) MulBy023(z *E6, c0, c1 *baseEl) *E6 { } -// Mul023By023 multiplies two E6 sparse element of the form: +// Mul023By023 multiplies two E6 sparse element of the form: // // E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0} // @@ -206,6 +240,34 @@ func (e *Ext6) MulBy023(z *E6, c0, c1 *baseEl) *E6 { // // E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0} func (e Ext6) Mul023By023(d0, d1, c0, c1 *baseEl) [5]*baseEl { + return e.mul023by023Direct(d0, d1, c0, c1) +} + +// mul023by023Direct multiplies two E6 sparse element using schoolbook multiplication +func (e Ext6) mul023by023Direct(d0, d1, c0, c1 *baseEl) [5]*baseEl { + nonResidue := e.fp.NewElement(-4) + // c0 = d0c0 + β + z0 := e.fp.Eval([][]*baseEl{{d0, c0}, {nonResidue}}, []int{1, 1}) + // c2 = d0c1 + d1c0 + z2 := e.fp.Eval([][]*baseEl{{d0, c1}, {d1, c0}}, []int{1, 1}) + // c3 = d0 + c0 + z3 := e.fp.Add(d0, c0) + // c4 = d1c1 + z4 := e.fp.Eval([][]*baseEl{{d1, c1}}, []int{1}) + // c5 = d1 + c1, + z5 := e.fp.Add(d1, c1) + + return [5]*baseEl{z0, z2, z3, z4, z5} +} + +// mul023By023 multiplies two E6 sparse element of the form: +// +// E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0} +// +// and +// +// E6{A0: c0, A1: 0, A2: c1, A3: 1, A4: 0, A5: 0} +func (e Ext6) mul023By023(d0, d1, c0, c1 *baseEl) [5]*baseEl { x0 := e.fp.Mul(c0, d0) x1 := e.fp.Mul(c1, d1) x04 := e.fp.Add(c0, d0) @@ -224,9 +286,48 @@ func (e Ext6) Mul023By023(d0, d1, c0, c1 *baseEl) [5]*baseEl { // MulBy02345 multiplies z by an E6 sparse element of the form // -// E6{A0: y0, A1: 0, A2: y1, A3: y2, A4: y3, A5: y4}, -// } +// E6{A0: y0, A1: 0, A2: y1, A3: y2, A4: y3, A5: y4} func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 { + return e.mulBy02345Direct(z, x) +} + +// mulBy02345Direct multiplies z by an E6 sparse element using schoolbook multiplication +func (e Ext6) mulBy02345Direct(z *E6, x [5]*baseEl) *E6 { + nonResidue := e.fp.NewElement(-4) + + // c0 = a0y0 + β(a1y4 + a2y3 + a3y2 + a4y1) + c0 := e.fp.Eval([][]*baseEl{{&z.A0, x[0]}, {nonResidue, &z.A1, x[4]}, {nonResidue, &z.A2, x[3]}, {nonResidue, &z.A3, x[2]}, {nonResidue, &z.A4, x[1]}}, + []int{1, 1, 1, 1, 1}) + // c1 = a1y0 + β(a2y4 + a3y3 + a4y2 + a5y1) + c1 := e.fp.Eval([][]*baseEl{{&z.A1, x[0]}, {nonResidue, &z.A2, x[4]}, {nonResidue, &z.A3, x[3]}, {nonResidue, &z.A4, x[2]}, {nonResidue, &z.A5, x[1]}}, + []int{1, 1, 1, 1, 1}) + // c2 = a0y1 + a2y0 + β(a3y4 + a4y3 + a5y2) + c2 := e.fp.Eval([][]*baseEl{{&z.A0, x[1]}, {&z.A2, x[0]}, {nonResidue, &z.A3, x[4]}, {nonResidue, &z.A4, x[3]}, {nonResidue, &z.A5, x[2]}}, + []int{1, 1, 1, 1, 1}) + // c3 = a0y2 + a1y1 + a3y0 + β(a4y4 + a5y3) + c3 := e.fp.Eval([][]*baseEl{{&z.A0, x[2]}, {&z.A1, x[1]}, {&z.A3, x[0]}, {nonResidue, &z.A4, x[4]}, {nonResidue, &z.A5, x[3]}}, + []int{1, 1, 1, 1, 1}) + // c4 = a0y3 + a1y2 + a2y1 + a4y0 + βa5y4 + c4 := e.fp.Eval([][]*baseEl{{&z.A0, x[3]}, {&z.A1, x[2]}, {&z.A2, x[1]}, {&z.A4, x[0]}, {nonResidue, &z.A5, x[4]}}, + []int{1, 1, 1, 1, 1}) + // c5 = a0y4 + a1y3 + a2y2 + a3y1 + a5y0, + c5 := e.fp.Eval([][]*baseEl{{&z.A0, x[4]}, {&z.A1, x[3]}, {&z.A2, x[2]}, {&z.A3, x[1]}, {&z.A5, x[0]}}, + []int{1, 1, 1, 1, 1}) + + return &E6{ + A0: *c0, + A1: *c1, + A2: *c2, + A3: *c3, + A4: *c4, + A5: *c5, + } +} + +// mulBy02345 multiplies z by an E6 sparse element of the form +// +// E6{A0: y0, A1: 0, A2: y1, A3: y2, A4: y3, A5: y4}, +func (e *Ext6) mulBy02345(z *E6, x [5]*baseEl) *E6 { a0 := e.fp.Add(&z.A0, &z.A1) a1 := e.fp.Add(&z.A2, &z.A3) a2 := e.fp.Add(&z.A4, &z.A5) @@ -364,7 +465,7 @@ func (e Ext6) AssertFinalExponentiationIsOne(x *E6) { func (e Ext6) ExpByU2(z *E6) *E6 { z = e.Reduce(z) result := e.Copy(z) - t := e.nSquareKarabina12345(result, 1) + t := e.CyclotomicSquareGS(result) result = e.nSquareKarabina12345(t, 4) result = e.Mul(result, z) z33 := e.Copy(result) @@ -372,7 +473,7 @@ func (e Ext6) ExpByU2(z *E6) *E6 { result = e.Mul(result, z33) result = e.nSquareKarabina12345(result, 4) result = e.Mul(result, z) - result = e.nSquareKarabina12345(result, 1) + result = e.CyclotomicSquareGS(result) result = e.Mul(result, z) result = e.nSquareKarabina12345(result, 46) result = e.Mul(result, t) @@ -383,9 +484,9 @@ func (e Ext6) ExpByU2(z *E6) *E6 { // ExpByU1 set z to z^(x₀^3-x₀^2+1) in E12 and return z // x₀^3-x₀^2+1 = 880904806456922042166256752416502360965158762994674434049 func (e Ext6) ExpByU1(x *E6) *E6 { - t5 := e.nSquareKarabina12345(x, 1) + t5 := e.CyclotomicSquareGS(x) z := e.Mul(x, t5) - t0 := e.nSquareKarabina12345(z, 1) + t0 := e.CyclotomicSquareGS(z) t6 := e.Mul(x, t0) t8 := e.Mul(x, t6) t7 := e.Mul(t5, t8) @@ -394,7 +495,7 @@ func (e Ext6) ExpByU1(x *E6) *E6 { t2 := e.Mul(x, t3) t1 := e.Mul(t6, t2) t0 = e.Mul(t8, t1) - t4 := e.nSquareKarabina12345(t0, 1) + t4 := e.CyclotomicSquareGS(t0) t4 = e.Mul(z, t4) t8 = e.Mul(t8, t4) t2 = e.Mul(t2, t8) diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index b5745e77c6..c011eb8b56 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -105,10 +105,12 @@ type e6MulVariants struct { func (circuit *e6MulVariants) Define(api frontend.API) error { e := NewExt6(api) - expected1 := *e.mulMontgomery6(&circuit.A, &circuit.B) - expected2 := *e.mulToomCook6(&circuit.A, &circuit.B) - e.AssertIsEqual(&expected1, &circuit.C) - e.AssertIsEqual(&expected2, &circuit.C) + expected1 := e.mulMontgomery6(&circuit.A, &circuit.B) + expected2 := e.mulToomCook6(&circuit.A, &circuit.B) + expected3 := e.mulDirect(&circuit.A, &circuit.B) + e.AssertIsEqual(expected1, &circuit.C) + e.AssertIsEqual(expected2, &circuit.C) + e.AssertIsEqual(expected3, &circuit.C) return nil } @@ -135,10 +137,9 @@ type e6Mul struct { } func (circuit *e6Mul) Define(api frontend.API) error { - var expected E6 e := NewExt6(api) - expected = *e.Mul(&circuit.A, &circuit.B) - e.AssertIsEqual(&expected, &circuit.C) + expected := e.Mul(&circuit.A, &circuit.B) + e.AssertIsEqual(expected, &circuit.C) return nil } @@ -160,6 +161,35 @@ func TestMulFp6(t *testing.T) { assert.NoError(err) } +type e6SquareVariants struct { + A, C E6 +} + +func (circuit *e6SquareVariants) Define(api frontend.API) error { + e := NewExt6(api) + expected1 := e.squareDirect(&circuit.A) + expected2 := e.squareEmulatedTower(&circuit.A) + e.AssertIsEqual(expected1, &circuit.C) + e.AssertIsEqual(expected2, &circuit.C) + return nil +} + +func TestSquareVariantsFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, c bw6761.E6 + _, _ = a.SetRandom() + c.Square(&a) + + witness := e6SquareVariants{ + A: FromE6(&a), + C: FromE6(&c), + } + + err := test.IsSolved(&e6SquareVariants{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + type e6Square struct { A, B E6 } @@ -312,6 +342,45 @@ func TestExptFp6(t *testing.T) { assert.NoError(err) } +type e6MulBy023Variants struct { + A E6 `gnark:",public"` + W E6 + B, C baseEl +} + +func (circuit *e6MulBy023Variants) Define(api frontend.API) error { + e := NewExt6(api) + expected1 := e.mulBy023(&circuit.A, &circuit.B, &circuit.C) + expected2 := e.mulBy023Direct(&circuit.A, &circuit.B, &circuit.C) + e.AssertIsEqual(expected1, &circuit.W) + e.AssertIsEqual(expected2, &circuit.W) + return nil +} + +func TestFp6MulBy023Variants(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, w bw6761.E6 + _, _ = a.SetRandom() + var one, b, c fp.Element + one.SetOne() + _, _ = b.SetRandom() + _, _ = c.SetRandom() + w.Set(&a) + w.MulBy014(&b, &c, &one) + + witness := e6MulBy023Variants{ + A: FromE6(&a), + B: emulated.ValueOf[emulated.BW6761Fp](&b), + C: emulated.ValueOf[emulated.BW6761Fp](&c), + W: FromE6(&w), + } + + err := test.IsSolved(&e6MulBy023Variants{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + type e6MulBy023 struct { A E6 `gnark:",public"` W E6 @@ -348,3 +417,104 @@ func TestFp6MulBy023(t *testing.T) { err := test.IsSolved(&e6MulBy023{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } + +type e6Mul023By023Variants struct { + A E6 `gnark:",public"` + B E6 `gnark:",public"` +} + +func (circuit *e6Mul023By023Variants) Define(api frontend.API) error { + e := NewExt6(api) + expected1 := e.mul023By023(&circuit.A.A0, &circuit.A.A2, &circuit.B.A0, &circuit.B.A2) + expected2 := e.mul023by023Direct(&circuit.A.A0, &circuit.A.A2, &circuit.B.A0, &circuit.B.A2) + for i := range expected1 { + e.fp.AssertIsEqual(expected1[i], expected2[i]) + } + return nil +} + +func TestFp6Mul023By023Variants(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + + witness := e6Mul023By023Variants{ + A: FromE6(&a), + B: FromE6(&b), + } + + err := test.IsSolved(&e6Mul023By023Variants{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6MulBy02345Variants struct { + A E6 `gnark:",public"` + B E6 `gnark:",public"` +} + +func (circuit *e6MulBy02345Variants) Define(api frontend.API) error { + e := NewExt6(api) + expected1 := e.mulBy02345(&circuit.A, [5]*baseEl{&circuit.B.A0, &circuit.B.A2, &circuit.B.A3, &circuit.B.A4, &circuit.B.A5}) + expected2 := e.mulBy02345Direct(&circuit.A, [5]*baseEl{&circuit.B.A0, &circuit.B.A2, &circuit.B.A3, &circuit.B.A4, &circuit.B.A5}) + e.AssertIsEqual(expected1, expected2) + return nil +} + +func TestFp6MulBy02345Variants(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + + witness := e6MulBy02345Variants{ + A: FromE6(&a), + B: FromE6(&b), + } + + err := test.IsSolved(&e6MulBy02345Variants{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6CycolotomicSquareKarabina12345Variants struct { + A E6 `gnark:",public"` + C E6 `gnark:",public"` +} + +func (circuit *e6CycolotomicSquareKarabina12345Variants) Define(api frontend.API) error { + e := NewExt6(api) + expected1 := e.cyclotomicSquareKarabina12345(&circuit.A) + expected2 := e.cyclotomicSquareKarabina12345Eval(&circuit.A) + e.AssertIsEqual(expected1, expected2) + dec1 := e.decompressKarabina12345(expected1) + dec2 := e.decompressKarabina12345Eval(expected2) + e.AssertIsEqual(dec1, dec2) + e.fp.AssertIsEqual(&dec1.A1, &circuit.C.A1) + e.fp.AssertIsEqual(&dec1.A2, &circuit.C.A2) + // e.fp.AssertIsEqual(&dec1.A3, &circuit.C.A3) + e.fp.AssertIsEqual(&dec1.A4, &circuit.C.A4) + e.fp.AssertIsEqual(&dec1.A5, &circuit.C.A5) + + return nil +} + +func TestFp6CyclotomicSquareKarabina12345Variants(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, c bw6761.E6 + _, _ = a.SetRandom() + c.CyclotomicSquare(&a) + + witness := e6CycolotomicSquareKarabina12345Variants{ + A: FromE6(&a), + C: FromE6(&c), + } + + err := test.IsSolved(&e6CycolotomicSquareKarabina12345Variants{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +}