From 90617dc59debbb351e404dfc411980fb40bdcd16 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 7 Jul 2023 10:31:56 +0100 Subject: [PATCH 01/38] feat: add bw6 fields --- std/algebra/emulated/fields_bw6761/e3.go | 368 ++++++++++++++++ std/algebra/emulated/fields_bw6761/e3_test.go | 393 +++++++++++++++++ std/algebra/emulated/fields_bw6761/e6.go | 389 ++++++++++++++++ .../emulated/fields_bw6761/e6_pairing.go | 163 +++++++ .../emulated/fields_bw6761/e6_pairing_test.go | 222 ++++++++++ std/algebra/emulated/fields_bw6761/e6_test.go | 415 ++++++++++++++++++ .../emulated/fields_bw6761/frobenius.go | 45 ++ std/algebra/emulated/fields_bw6761/hints.go | 63 +++ std/math/emulated/emparams/emparams.go | 34 ++ std/math/emulated/params.go | 2 + 10 files changed, 2094 insertions(+) create mode 100644 std/algebra/emulated/fields_bw6761/e3.go create mode 100644 std/algebra/emulated/fields_bw6761/e3_test.go create mode 100644 std/algebra/emulated/fields_bw6761/e6.go create mode 100644 std/algebra/emulated/fields_bw6761/e6_pairing.go create mode 100644 std/algebra/emulated/fields_bw6761/e6_pairing_test.go create mode 100644 std/algebra/emulated/fields_bw6761/e6_test.go create mode 100644 std/algebra/emulated/fields_bw6761/frobenius.go create mode 100644 std/algebra/emulated/fields_bw6761/hints.go diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go new file mode 100644 index 0000000000..3e7ecc915f --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -0,0 +1,368 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package fields_bw6761 + +import ( + "math/big" + + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark/std/math/emulated" +) + +type curveF = emulated.Field[emulated.BW6761Fp] +type baseEl = emulated.Element[emulated.BW6761Fp] + +type E3 struct { + A0, A1, A2 baseEl +} + +type Ext3 struct { + fp *curveF +} + +func NewExt3(baseEl *curveF) *Ext3 { + return &Ext3{ + fp: baseEl, + } +} + +func (e Ext3) Reduce(x *E3) *E3 { + var z E3 + z.A0 = *e.fp.Reduce(&x.A0) + z.A1 = *e.fp.Reduce(&x.A1) + z.A2 = *e.fp.Reduce(&x.A2) + return &z +} + +// SetZero sets an *E3 elmt to zero +func (e Ext3) Zero() *E3 { + zero := e.fp.Zero() + return &E3{ + A0: *zero, + A1: *zero, + A2: *zero, + } +} + +// One sets z to 1 in Montgomery form and returns z +func (e Ext3) One() *E3 { + one := e.fp.One() + zero := e.fp.Zero() + return &E3{ + A0: *one, + A1: *zero, + A2: *zero, + } +} + +// Neg negates the *E3 number +func (e Ext3) Neg(x *E3) *E3 { + a0 := e.fp.Neg(&x.A0) + a1 := e.fp.Neg(&x.A1) + a2 := e.fp.Neg(&x.A2) + return &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } +} + +// Add adds two elements of *E3 +func (e Ext3) Add(x, y *E3) *E3 { + a0 := e.fp.Add(&x.A0, &y.A0) + a1 := e.fp.Add(&x.A1, &y.A1) + a2 := e.fp.Add(&x.A2, &y.A2) + return &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } +} + +// Sub two elements of *E3 +func (e Ext3) Sub(x, y *E3) *E3 { + a0 := e.fp.Sub(&x.A0, &y.A0) + a1 := e.fp.Sub(&x.A1, &y.A1) + a2 := e.fp.Sub(&x.A2, &y.A2) + return &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } +} + +// Double doubles an element in *E3 +func (e Ext3) Double(x *E3) *E3 { + 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) + return &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } +} + +func MulByNonResidue(fp *curveF, x *baseEl) *baseEl { + + z := fp.Neg(x) + z = fp.Add(z, z) + z = fp.Add(z, z) + return z +} + +// Conjugate conjugates an element in *E3 +func (e Ext3) Conjugate(x *E3) *E3 { + a1 := e.fp.Neg(&x.A1) + return &E3{ + A0: x.A0, + A1: *a1, + A2: x.A2, + } +} + +// MulByElement multiplies an element in *E3 by an element in fp +func (e Ext3) MulByElement(x *E3, y *baseEl) *E3 { + a0 := e.fp.Mul(&x.A0, y) + a1 := e.fp.Mul(&x.A1, y) + a2 := e.fp.Mul(&x.A2, y) + z := &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } + return z +} + +func (e Ext3) MulByConstElement(x *E3, y *big.Int) *E3 { + a0 := e.fp.MulConst(&x.A0, y) + a1 := e.fp.MulConst(&x.A1, y) + a2 := e.fp.MulConst(&x.A2, y) + return &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } +} + +// MulBy01 multiplication by sparse element (c0,c1,0) +func (e Ext3) MulBy01(z *E3, c0, c1 *baseEl) *E3 { + + a := e.fp.Mul(&z.A0, c0) + b := e.fp.Mul(&z.A1, c1) + + tmp := e.fp.Add(&z.A1, &z.A2) + t0 := e.fp.Mul(c1, tmp) + t0 = e.fp.Sub(t0, b) + t0 = MulByNonResidue(e.fp, t0) + t0 = e.fp.Add(t0, a) + + tmp = e.fp.Add(&z.A0, &z.A2) + t2 := e.fp.Mul(c0, tmp) + t2 = e.fp.Sub(t2, a) + t2 = e.fp.Add(t2, b) + + t1 := e.fp.Add(c0, c1) + tmp = e.fp.Add(&z.A0, &z.A1) + t1 = e.fp.Mul(t1, tmp) + t1 = e.fp.Sub(t1, a) + t1 = e.fp.Sub(t1, b) + + return &E3{ + A0: *t0, + A1: *t1, + A2: *t2, + } +} + +// MulBy1 multiplication of E6 by sparse element (0, c1, 0) +func (e Ext3) MulBy1(z *E3, c1 baseEl) *E3 { + + b := e.fp.Mul(&z.A1, &c1) + + tmp := e.fp.Add(&z.A1, &z.A2) + t0 := e.fp.Mul(&c1, tmp) + t0 = e.fp.Sub(t0, b) + t0 = MulByNonResidue(e.fp, t0) + + tmp = e.fp.Add(&z.A0, &z.A1) + t1 := e.fp.Mul(&c1, tmp) + t1 = e.fp.Sub(t1, b) + + return &E3{ + A0: *t0, + A1: *t1, + A2: *b, + } +} + +// Mul sets z to the *E3-product of x,y, returns z +func (e Ext3) Mul(x, y *E3) *E3 { + // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf + t0 := e.fp.Mul(&x.A0, &y.A0) + t1 := e.fp.Mul(&x.A1, &y.A1) + t2 := e.fp.Mul(&x.A2, &y.A2) + + c0 := e.fp.Add(&x.A1, &x.A2) + tmp := e.fp.Add(&y.A1, &y.A2) + c0 = e.fp.Mul(c0, tmp) + c0 = e.fp.Sub(c0, t1) + c0 = e.fp.Sub(c0, t2) + c0 = MulByNonResidue(e.fp, c0) + + tmp = e.fp.Add(&x.A0, &x.A2) + c2 := e.fp.Add(&y.A0, &y.A2) + c2 = e.fp.Mul(c2, tmp) + c2 = e.fp.Sub(c2, t0) + c2 = e.fp.Sub(c2, t2) + + c1 := e.fp.Add(&x.A0, &x.A1) + tmp = e.fp.Add(&y.A0, &y.A1) + c1 = e.fp.Mul(c1, tmp) + c1 = e.fp.Sub(c1, t0) + c1 = e.fp.Sub(c1, t1) + t2 = MulByNonResidue(e.fp, t2) + + a0 := e.fp.Add(c0, t0) + a1 := e.fp.Add(c1, t2) + a2 := e.fp.Add(c2, t1) + + return &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } +} + +// Square sets z to the *E3-product of x,x, returns z +func (e Ext3) Square(x *E3) *E3 { + + // Algorithm 16 from https://eprint.iacr.org/2010/354.pdf + + c6 := e.fp.MulConst(&x.A1, big.NewInt(2)) + c4 := e.fp.Mul(&x.A0, c6) // x.A0 * xA1 * 2 + c5 := e.fp.Mul(&x.A2, &x.A2) + c1 := MulByNonResidue(e.fp, c5) + c1 = e.fp.Add(c1, c4) + c2 := e.fp.Sub(c4, c5) + + c3 := e.fp.Mul(&x.A0, &x.A0) + c4 = e.fp.Sub(&x.A0, &x.A1) + c4 = e.fp.Add(c4, &x.A2) + c5 = e.fp.Mul(c6, &x.A2) // x.A1 * xA2 * 2 + c4 = e.fp.Mul(c4, c4) + c0 := MulByNonResidue(e.fp, c5) + c4 = e.fp.Add(c4, c5) + c4 = e.fp.Sub(c4, c3) + + a0 := e.fp.Add(c0, c3) + a1 := c1 + a2 := e.fp.Add(c2, c4) + + return &E3{ + A0: *a0, + A1: *a1, + A2: *a2, + } +} + +func (e Ext3) Inverse(x *E3) *E3 { + res, err := e.fp.NewHint(inverseE3Hint, 3, &x.A0, &x.A1, &x.A2) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + + inv := E3{ + A0: *res[0], + A1: *res[1], + A2: *res[2], + } + one := e.One() + + // 1 == inv * x + _one := e.Mul(&inv, x) + e.AssertIsEqual(one, _one) + + return &inv + +} + +func (e Ext3) DivUnchecked(x, y *E3) *E3 { + res, err := e.fp.NewHint(divE3Hint, 6, &x.A0, &x.A1, &x.A2, &y.A0, &y.A1, &y.A2) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + + div := E3{ + A0: *res[0], + A1: *res[1], + A2: *res[2], + } + + // 1 == inv * x + _x := e.Mul(&div, x) + e.AssertIsEqual(x, _x) + + return &div + +} + +// MulByNonResidue mul x by (0,1,0) +func (e Ext3) MulByNonResidue(x *E3) *E3 { + z := &E3{ + A0: x.A2, + A1: x.A0, + A2: x.A1, + } + z.A0 = *MulByNonResidue(e.fp, &z.A0) + return z +} + +// AssertIsEqual constraint self to be equal to other into the given constraint system +func (e Ext3) AssertIsEqual(a, b *E3) { + e.fp.AssertIsEqual(&a.A0, &b.A0) + e.fp.AssertIsEqual(&a.A1, &b.A1) + e.fp.AssertIsEqual(&a.A2, &b.A2) +} + +func (e Ext3) Set(x *E3) *E3 { + return &E3{ + A0: x.A0, + A1: x.A1, + A2: x.A2, + } +} + +// Equal returns true if z equals x, fasle otherwise +func (e Ext3) Equal(a, b *E3) { + e.fp.AssertIsEqual(&a.A0, &b.A0) + e.fp.AssertIsEqual(&a.A1, &b.A1) + e.fp.AssertIsEqual(&a.A2, &b.A2) +} + +func FromE3(a *bw6761.E3) E3 { + return E3{ + A0: emulated.ValueOf[emulated.BW6761Fp](a.A0), + A1: emulated.ValueOf[emulated.BW6761Fp](a.A1), + A2: emulated.ValueOf[emulated.BW6761Fp](a.A2), + } +} diff --git a/std/algebra/emulated/fields_bw6761/e3_test.go b/std/algebra/emulated/fields_bw6761/e3_test.go new file mode 100644 index 0000000000..a43c9e6249 --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/e3_test.go @@ -0,0 +1,393 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package fields_bw6761 + +import ( + "testing" + + "github.com/consensys/gnark-crypto/ecc" + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/emulated" + "github.com/consensys/gnark/test" +) + +type e3Add struct { + A, B, C E3 +} + +func (circuit *e3Add) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Add(&circuit.A, &circuit.B) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestAddFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b, c bw6761.E3 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + c.Add(&a, &b) + + witness := e3Add{ + A: FromE3(&a), + B: FromE3(&b), + C: FromE3(&c), + } + + err := test.IsSolved(&e3Add{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3Sub struct { + A, B, C E3 +} + +func (circuit *e3Sub) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Sub(&circuit.A, &circuit.B) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestSubFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b, c bw6761.E3 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + c.Sub(&a, &b) + + witness := e3Sub{ + A: FromE3(&a), + B: FromE3(&b), + C: FromE3(&c), + } + + err := test.IsSolved(&e3Sub{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3Neg struct { + A, B E3 +} + +func (circuit *e3Neg) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Neg(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestNegFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + b.Neg(&a) + + witness := e3Neg{ + A: FromE3(&a), + B: FromE3(&b), + } + + err := test.IsSolved(&e3Neg{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3Double struct { + A, B E3 +} + +func (circuit *e3Double) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Double(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestDoubleFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + b.Double(&a) + + witness := e3Double{ + A: FromE3(&a), + B: FromE3(&b), + } + + err := test.IsSolved(&e3Double{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3Mul struct { + A, B, C E3 +} + +func (circuit *e3Mul) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Mul(&circuit.A, &circuit.B) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestMulFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b, c bw6761.E3 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + c.Mul(&a, &b) + + witness := e3Mul{ + A: FromE3(&a), + B: FromE3(&b), + C: FromE3(&c), + } + + err := test.IsSolved(&e3Mul{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3MulByNonResidue struct { + A, B E3 +} + +func (circuit *e3MulByNonResidue) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.MulByNonResidue(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestMulByNonResidueFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + b.Set(&a) + b.MulByNonResidue(&a) + + witness := e3MulByNonResidue{ + A: FromE3(&a), + B: FromE3(&b), + } + + err := test.IsSolved(&e3MulByNonResidue{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3MulByElement struct { + A E3 + Y baseEl + B E3 +} + +func (circuit *e3MulByElement) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.MulByElement(&circuit.A, &circuit.Y) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestMulByElementFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + var y fp.Element + y.SetRandom() + b.Set(&a) + b.MulByElement(&a, &y) + + witness := e3MulByElement{ + A: FromE3(&a), + Y: emulated.ValueOf[emulated.BW6761Fp](y), + B: FromE3(&b), + } + + err := test.IsSolved(&e3MulByElement{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3MulBy01 struct { + A E3 + C0, C1 baseEl + B E3 +} + +func (circuit *e3MulBy01) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.MulBy01(&circuit.A, &circuit.C0, &circuit.C1) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestMulBy01Fp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + var c0, c1 fp.Element + c0.SetRandom() + c1.SetRandom() + b.Set(&a) + b.MulBy01(&c0, &c1) + + witness := e3MulBy01{ + A: FromE3(&a), + C0: emulated.ValueOf[emulated.BW6761Fp](c0), + C1: emulated.ValueOf[emulated.BW6761Fp](c1), + B: FromE3(&b), + } + + err := test.IsSolved(&e3MulBy01{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3Square struct { + A, B E3 +} + +func (circuit *e3Square) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Square(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestSquareFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + b.Square(&a) + + witness := e3Square{ + A: FromE3(&a), + B: FromE3(&b), + } + + err := test.IsSolved(&e3Square{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3Inverse struct { + A, B E3 +} + +func (circuit *e3Inverse) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Inverse(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestInverseFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + b.Inverse(&a) + + witness := e3Inverse{ + A: FromE3(&a), + B: FromE3(&b), + } + + // add=50605 equals=769 fromBinary=0 mul=50315 sub=558 toBinary=0 + err := test.IsSolved(&e3Inverse{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e3Conjugate struct { + A, B E3 +} + +func (circuit *e3Conjugate) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.Conjugate(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestConjugateFp3(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E3 + _, _ = a.SetRandom() + b.Conjugate(&a) + + witness := e3Conjugate{ + A: FromE3(&a), + B: FromE3(&b), + } + + err := test.IsSolved(&e3Conjugate{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go new file mode 100644 index 0000000000..e26832abde --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -0,0 +1,389 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package fields_bw6761 + +import ( + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" +) + +type E6 struct { + B0, B1 E3 +} + +type Ext6 struct { + *Ext3 +} + +func (e Ext6) Reduce(x *E6) *E6 { + var z E6 + z.B0 = *e.Ext3.Reduce(&x.B0) + z.B1 = *e.Ext3.Reduce(&x.B1) + return &z +} + +func NewExt6(baseEl *curveF) *Ext6 { + return &Ext6{Ext3: NewExt3(baseEl)} +} + +// SetZero sets an *E3 elmt to zero +func (e Ext6) Zero() *E6 { + b0 := e.Ext3.Zero() + b1 := e.Ext3.Zero() + return &E6{ + B0: *b0, + B1: *b1, + } +} + +// One sets z to 1 in Montgomery form and returns z +func (e Ext6) One() *E6 { + return &E6{ + B0: *e.Ext3.One(), + B1: *e.Ext3.Zero(), + } +} + +// Add set z=x+y in *E6 and return z +func (e Ext6) Add(x, y *E6) *E6 { + return &E6{ + B0: *e.Ext3.Add(&x.B0, &y.B0), + B1: *e.Ext3.Add(&x.B1, &y.B1), + } +} + +// Sub sets z to x sub y and return z +func (e Ext6) Sub(x, y *E6) *E6 { + return &E6{ + B0: *e.Ext3.Sub(&x.B0, &y.B0), + B1: *e.Ext3.Sub(&x.B1, &y.B1), + } +} + +// Double sets z=2*x and returns z +func (e Ext6) Double(x *E6) *E6 { + return &E6{ + B0: *e.Ext3.Double(&x.B0), + B1: *e.Ext3.Double(&x.B1), + } +} + +// Mul set z=x*y in *E6 and return z +func (e Ext6) Mul(x, y *E6) *E6 { + x = e.Reduce(x) + y = e.Reduce(y) + + a := e.Ext3.Add(&x.B0, &x.B1) + b := e.Ext3.Add(&y.B0, &y.B1) + a = e.Ext3.Mul(a, b) + b = e.Ext3.Mul(&x.B0, &y.B0) + c := e.Ext3.Mul(&x.B1, &y.B1) + b1 := e.Ext3.Sub(a, b) + b1 = e.Ext3.Sub(b1, c) + b0 := e.Ext3.MulByNonResidue(c) + b0 = e.Ext3.Add(b0, b) + + return &E6{ + B0: *b0, + B1: *b1, + } +} + +// Square set z=x*x in *E6 and return z +func (e Ext6) Square(x *E6) *E6 { + + //Algorithm 22 from https://eprint.iacr.org/2010/354.pdf + c0 := e.Ext3.Sub(&x.B0, &x.B1) + c3 := e.Ext3.MulByNonResidue(&x.B1) + c3 = e.Ext3.Neg(c3) + c3 = e.Ext3.Add(&x.B0, c3) + c2 := e.Ext3.Mul(&x.B0, &x.B1) + c0 = e.Ext3.Mul(c0, c3) + c0 = e.Ext3.Add(c0, c2) + b1 := e.Ext3.Double(c2) + c2 = e.Ext3.MulByNonResidue(c2) + b0 := e.Ext3.Add(c0, c2) + + return &E6{ + B0: *b0, + B1: *b1, + } +} + +// Karabina's compressed cyclotomic square +// https://eprint.iacr.org/2010/542.pdf +// Th. 3.2 with minor modifications to fit our tower +func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { + x = e.Reduce(x) + + z := e.Set(x) + + var t [7]*baseEl + + // t0 = g1² + t[0] = e.fp.Mul(&x.B0.A1, &x.B0.A1) + // t1 = g5² + t[1] = e.fp.Mul(&x.B1.A2, &x.B1.A2) + // t5 = g1 + g5 + t[5] = e.fp.Add(&x.B0.A1, &x.B1.A2) + // t2 = (g1 + g5)² + t[2] = e.fp.Mul(t[5], t[5]) + + // t3 = g1² + g5² + t[3] = e.fp.Add(t[0], t[1]) + // t5 = 2 * g1 * g5 + t[5] = e.fp.Sub(t[2], t[3]) + + // t6 = g3 + g2 + t[6] = e.fp.Add(&x.B1.A0, &x.B0.A2) + // t3 = (g3 + g2)² + t[3] = e.fp.Mul(t[6], t[6]) + // t2 = g3² + t[2] = e.fp.Mul(&x.B1.A0, &x.B1.A0) + + // t6 = 2 * nr * g1 * g5 + t[6] = MulByNonResidue(e.fp, t[5]) + // t5 = 4 * nr * g1 * g5 + 2 * g3 + t[5] = e.fp.Add(t[6], &x.B1.A0) + t[5] = e.fp.Add(t[5], t[5]) + // z3 = 6 * nr * g1 * g5 + 2 * g3 + z.B1.A0 = *e.fp.Add(t[5], t[6]) + + // t4 = nr * g5² + t[4] = MulByNonResidue(e.fp, t[1]) + // t5 = nr * g5² + g1² + t[5] = e.fp.Add(t[0], t[4]) + // t6 = nr * g5² + g1² - g2 + t[6] = e.fp.Sub(t[5], &x.B0.A2) + + // t1 = g2² + t[1] = e.fp.Mul(&x.B0.A2, &x.B0.A2) + + // t6 = 2 * nr * g5² + 2 * g1² - 2*g2 + t[6] = e.fp.Add(t[6], t[6]) + // z2 = 3 * nr * g5² + 3 * g1² - 2*g2 + z.B0.A2 = *e.fp.Add(t[6], t[5]) + + // t4 = nr * g2² + t[4] = MulByNonResidue(e.fp, t[1]) + // t5 = g3² + nr * g2² + t[5] = e.fp.Add(t[2], t[4]) + // t6 = g3² + nr * g2² - g1 + t[6] = e.fp.Sub(t[5], &x.B0.A1) + // t6 = 2 * g3² + 2 * nr * g2² - 2 * g1 + t[6] = e.fp.Add(t[6], t[6]) + // z1 = 3 * g3² + 3 * nr * g2² - 2 * g1 + z.B0.A1 = *e.fp.Add(t[6], t[5]) + + // t0 = g2² + g3² + t[0] = e.fp.Add(t[2], t[1]) + // t5 = 2 * g3 * g2 + t[5] = e.fp.Sub(t[3], t[0]) + // t6 = 2 * g3 * g2 + g5 + t[6] = e.fp.Add(t[5], &x.B1.A2) + // t6 = 4 * g3 * g2 + 2 * g5 + t[6] = e.fp.Add(t[6], t[6]) + // z5 = 6 * g3 * g2 + 2 * g5 + z.B1.A2 = *e.fp.Add(t[5], t[6]) + + return z +} + +// DecompressKarabina Karabina's cyclotomic square result +// if g3 != 0 +// +// g4 = (E * g5^2 + 3 * g1^2 - 2 * g2)/4g3 +// +// if g3 == 0 +// +// g4 = 2g1g5/g2 +// +// if g3=g2=0 then g4=g5=g1=0 and g0=1 (x=1) +// Theorem 3.1 is well-defined for all x in Gϕₙ\{1} +func (e Ext6) DecompressKarabina(x *E6) *E6 { + + x = e.Reduce(x) + + var z E6 + + var t [3]*baseEl + one := e.fp.One() + + // t0 = g1^2 + t[0] = e.fp.Mul(&x.B0.A1, &x.B0.A1) + // t1 = 3 * g1^2 - 2 * g2 + t[1] = e.fp.Sub(t[0], &x.B0.A2) + t[1] = e.fp.Add(t[1], t[1]) + t[1] = e.fp.Add(t[1], t[0]) + // t0 = E * g5^2 + t1 + t[2] = e.fp.Mul(&x.B1.A2, &x.B1.A2) + t[0] = MulByNonResidue(e.fp, t[2]) + t[0] = e.fp.Add(t[0], t[1]) + // t1 = 1/(4 * g3) + t[1] = e.fp.Add(&x.B1.A0, &x.B1.A0) + t[1] = e.fp.Add(t[1], t[1]) + + // z4 = g4 + z.B1.A1 = *e.fp.Div(t[0], t[1]) // costly + a1 := z.B1.A1 + + // t1 = g2 * g1 + t[1] = e.fp.Mul(&x.B0.A2, &x.B0.A1) + // t2 = 2 * g4² - 3 * g2 * g1 + t[2] = e.fp.Mul(&a1, &a1) + t[2] = e.fp.Sub(t[2], t[1]) + t[2] = e.fp.Add(t[2], t[2]) + t[2] = e.fp.Sub(t[2], t[1]) + // t1 = g3 * g5 (g3 can be 0) + t[1] = e.fp.Mul(&x.B1.A0, &x.B1.A2) + // c₀ = E * (2 * g4² + g3 * g5 - 3 * g2 * g1) + 1 + t[2] = e.fp.Add(t[2], t[1]) + + z.B0.A0 = *MulByNonResidue(e.fp, t[2]) + z.B0.A0 = *e.fp.Add(&z.B0.A0, one) + + z.B0.A1 = x.B0.A1 + z.B0.A2 = x.B0.A2 + z.B1.A0 = x.B1.A0 + z.B1.A2 = x.B1.A2 + + return &z +} + +// Granger-Scott's cyclotomic square +// https://eprint.iacr.org/2009/565.pdf, 3.2 +func (e Ext6) CyclotomicSquare(x *E6) *E6 { + // x=(x0,x1,x2,x3,x4,x5,x6,x7) in E3⁶ + // cyclosquare(x)=(3*x4²*u + 3*x0² - 2*x0, + // 3*x2²*u + 3*x3² - 2*x1, + // 3*x5²*u + 3*x1² - 2*x2, + // 6*x1*x5*u + 2*x3, + // 6*x0*x4 + 2*x4, + // 6*x2*x3 + 2*x5) + + x = e.Reduce(x) + + var t [9]*baseEl + + t[0] = e.fp.Mul(&x.B1.A1, &x.B1.A1) + t[1] = e.fp.Mul(&x.B0.A0, &x.B0.A0) + t[6] = e.fp.Add(&x.B1.A1, &x.B0.A0) + t[6] = e.fp.Mul(t[6], t[6]) + t[6] = e.fp.Sub(t[6], t[0]) + t[6] = e.fp.Sub(t[6], t[1]) // 2*x4*x0 + t[2] = e.fp.Mul(&x.B0.A2, &x.B0.A2) + t[3] = e.fp.Mul(&x.B1.A0, &x.B1.A0) + t[7] = e.fp.Add(&x.B0.A2, &x.B1.A0) + t[7] = e.fp.Mul(t[7], t[7]) + t[7] = e.fp.Sub(t[7], t[2]) + t[7] = e.fp.Sub(t[7], t[3]) // 2*x2*x3 + t[4] = e.fp.Mul(&x.B1.A2, &x.B1.A2) + t[5] = e.fp.Mul(&x.B0.A1, &x.B0.A1) + t[8] = e.fp.Add(&x.B1.A2, &x.B0.A1) + t[8] = e.fp.Mul(t[8], t[8]) + t[8] = e.fp.Sub(t[8], t[4]) + t[8] = e.fp.Sub(t[8], t[5]) + t[8] = MulByNonResidue(e.fp, t[8]) // 2*x5*x1*u + + t[0] = MulByNonResidue(e.fp, t[0]) + t[0] = e.fp.Add(t[0], t[1]) // x4²*u + x0² + t[2] = MulByNonResidue(e.fp, t[2]) + t[2] = e.fp.Add(t[2], t[3]) // x2²*u + x3² + t[4] = MulByNonResidue(e.fp, t[4]) + t[4] = e.fp.Add(t[4], t[5]) // x5²*u + x1² + + var z E6 + z.B0.A0 = *e.fp.Sub(t[0], &x.B0.A0) + z.B0.A0 = *e.fp.Add(&z.B0.A0, &z.B0.A0) + z.B0.A0 = *e.fp.Add(&z.B0.A0, t[0]) + z.B0.A1 = *e.fp.Sub(t[2], &x.B0.A1) + z.B0.A1 = *e.fp.Add(&z.B0.A1, &z.B0.A1) + z.B0.A1 = *e.fp.Add(&z.B0.A1, t[2]) + z.B0.A2 = *e.fp.Sub(t[4], &x.B0.A2) + z.B0.A2 = *e.fp.Add(&z.B0.A2, &z.B0.A2) + z.B0.A2 = *e.fp.Add(&z.B0.A2, t[4]) + + z.B1.A0 = *e.fp.Add(t[8], &x.B1.A0) + z.B1.A0 = *e.fp.Add(&z.B1.A0, &z.B1.A0) + z.B1.A0 = *e.fp.Add(&z.B1.A0, t[8]) + z.B1.A1 = *e.fp.Add(t[6], &x.B1.A1) + z.B1.A1 = *e.fp.Add(&z.B1.A1, &z.B1.A1) + z.B1.A1 = *e.fp.Add(&z.B1.A1, t[6]) + z.B1.A2 = *e.fp.Add(t[7], &x.B1.A2) + z.B1.A2 = *e.fp.Add(&z.B1.A2, &z.B1.A2) + z.B1.A2 = *e.fp.Add(&z.B1.A2, t[7]) + + return &z +} + +// Inverse set z to the inverse of x in *E6 and return z +// +// if x == 0, sets and returns z = x +func (e Ext6) Inverse(x *E6) *E6 { + // Algorithm 23 from https://eprint.iacr.org/2010/354.pdf + + t0 := e.Ext3.Square(&x.B0) + t1 := e.Ext3.Square(&x.B1) + tmp := e.Ext3.MulByNonResidue(t1) + t0 = e.Ext3.Sub(t0, tmp) + t1 = e.Ext3.Inverse(t0) + b0 := e.Ext3.Mul(&x.B0, t1) + b1 := e.Ext3.Mul(&x.B1, t1) + b1 = e.Ext3.Neg(b1) + + return &E6{ + B0: *b0, + B1: *b1, + } +} + +// Conjugate set z to x conjugated and return z +func (e Ext6) Conjugate(x *E6) *E6 { + return &E6{ + B0: x.B0, + B1: *e.Ext3.Neg(&x.B1), + } +} + +// AssertIsEqual constraint self to be equal to other into the given constraint system +func (e Ext6) AssertIsEqual(a, b *E6) { + e.Ext3.AssertIsEqual(&a.B0, &b.B0) + e.Ext3.AssertIsEqual(&a.B1, &b.B1) +} + +func (e Ext6) Set(x *E6) *E6 { + b0 := e.Ext3.Set(&x.B0) + b1 := e.Ext3.Set(&x.B1) + return &E6{ + B0: *b0, + B1: *b1, + } +} + +// Equal returns true if z equals x, fasle otherwise +func (e Ext6) Equal(a, b *E6) { + e.Ext3.Equal(&a.B0, &b.B0) + e.Ext3.Equal(&a.B1, &b.B1) +} + +func FromE6(a *bw6761.E6) E6 { + return E6{ + B0: FromE3(&a.B0), + B1: FromE3(&a.B1), + } +} diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go new file mode 100644 index 0000000000..2a373342b7 --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -0,0 +1,163 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package fields_bw6761 + +type LineEvaluation struct { + R0 baseEl + R1 baseEl + R2 baseEl +} + +func (e Ext6) nSquare(z *E6, n int) *E6 { + for i := 0; i < n; i++ { + z = e.CyclotomicSquare(z) + } + return z +} + +func (e Ext6) nSquareCompressed(z *E6, n int) *E6 { + for i := 0; i < n; i++ { + z = e.CyclotomicSquareCompressed(z) + } + return z +} + +// Expt set z to x^t in *E6 and return z +func (e Ext6) Expt(x *E6) *E6 { + x = e.Reduce(x) + + // const tAbsVal uint64 = 9586122913090633729 + // tAbsVal in binary: 1000010100001000110000000000000000000000000000000000000000000001 + // drop the low 46 bits (all 0 except the least significant bit): 100001010000100011 = 136227 + // Shortest addition chains can be found at https://wwwhomes.uni-bielefeld.de/achim/addition_chain.html + + // a shortest addition chain for 136227 + result := e.Set(x) + result = e.nSquare(result, 5) + result = e.Mul(result, x) + x33 := e.Set(result) + result = e.nSquare(result, 7) + result = e.Mul(result, x33) + result = e.nSquare(result, 4) + result = e.Mul(result, x) + result = e.CyclotomicSquare(result) + result = e.Mul(result, x) + + // the remaining 46 bits + result = e.nSquareCompressed(result, 46) + result = e.DecompressKarabina(result) + result = e.Mul(result, x) + + return e.Set(result) +} + +// Expc2 set z to x^c2 in *E6 and return z +// ht, hy = 13, 9 +// c1 = ht+hy = 22 (10110) +func (e Ext6) Expc2(x *E6) *E6 { + + result := e.CyclotomicSquare(x) + result = e.CyclotomicSquare(result) + result = e.Mul(result, x) + result = e.CyclotomicSquare(result) + result = e.Mul(result, x) + result = e.CyclotomicSquare(result) + + return e.Set(result) +} + +// Expc1 set z to x^c1 in *E6 and return z +// ht, hy = 13, 9 +// c1 = ht**2+3*hy**2 = 412 (110011100) +func (e Ext6) Expc1(x *E6) *E6 { + x = e.Reduce(x) + + result := e.CyclotomicSquare(x) + result = e.Mul(result, x) + result = e.CyclotomicSquare(result) + result = e.CyclotomicSquare(result) + result = e.CyclotomicSquare(result) + result = e.Mul(result, x) + result = e.CyclotomicSquare(result) + result = e.Mul(result, x) + result = e.CyclotomicSquare(result) + result = e.Mul(result, x) + result = e.CyclotomicSquare(result) + result = e.CyclotomicSquare(result) + + return e.Set(result) +} + +// MulBy034 multiplication by sparse element (c0,0,0,c3,c4,0) +func (e Ext6) MulBy034(z *E6, l *LineEvaluation) *E6 { + z = e.Reduce(z) + + a := e.Ext3.MulByElement(&z.B0, &l.R0) + + b := e.Ext3.MulBy01(&z.B1, &l.R1, &l.R2) + + l.R0 = *e.fp.Add(&l.R0, &l.R1) + d := e.Ext3.Add(&z.B0, &z.B1) + d = e.Ext3.MulBy01(d, &l.R0, &l.R2) + + b1 := e.Ext3.Add(a, b) + b1 = e.Ext3.Neg(b1) + b1 = e.Ext3.Add(b1, d) + b0 := e.Ext3.MulByNonResidue(b) + b0 = e.Ext3.Add(b0, a) + + return &E6{ + B0: *b0, + B1: *b1, + } +} + +// Mul034By034 multiplication of sparse element (c0,0,0,c3,c4,0) by sparse element (d0,0,0,d3,d4,0) +func (e Ext6) Mul034By034(d0, d3, d4, c0, c3, c4 *baseEl) *E6 { + + x0 := e.fp.Mul(c0, d0) + x3 := e.fp.Mul(c3, d3) + x4 := e.fp.Mul(c4, d4) + tmp := e.fp.Add(c0, c4) + x04 := e.fp.Add(d0, d4) + x04 = e.fp.Mul(x04, tmp) + x04 = e.fp.Sub(x04, x0) + x04 = e.fp.Sub(x04, x4) + tmp = e.fp.Add(c0, c3) + x03 := e.fp.Add(d0, d3) + x03 = e.fp.Mul(x03, tmp) + x03 = e.fp.Sub(x03, x0) + x03 = e.fp.Sub(x03, x3) + tmp = e.fp.Add(c3, c4) + x34 := e.fp.Add(d3, d4) + x34 = e.fp.Mul(x34, tmp) + x34 = e.fp.Sub(x34, x3) + x34 = e.fp.Sub(x34, x4) + + var z E6 + z.B0.A0 = *MulByNonResidue(e.fp, x4) + z.B0.A0 = *e.fp.Add(&z.B0.A0, x0) + z.B0.A1 = *x3 + z.B0.A2 = *x34 + z.B1.A0 = *x03 + z.B1.A1 = *x04 + z.B1.A2 = *e.fp.Zero() + + return &z +} diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing_test.go b/std/algebra/emulated/fields_bw6761/e6_pairing_test.go new file mode 100644 index 0000000000..b8d61bfa54 --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/e6_pairing_test.go @@ -0,0 +1,222 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package fields_bw6761 + +import ( + "testing" + + "github.com/consensys/gnark-crypto/ecc" + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/emulated" + "github.com/consensys/gnark/test" +) + +type e6Expt struct { + A, B E6 +} + +func (circuit *e6Expt) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Expt(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestExptFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.Expt(&a) + + witness := e6Expt{ + A: FromE6(&a), + B: FromE6(&b), + } + + err := test.IsSolved(&e6Expt{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Expc2 struct { + A, B E6 +} + +func (circuit *e6Expc2) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Expc2(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestExpc2Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.Expc2(&a) + + witness := e6Expc2{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=287618 equals=4068 fromBinary=0 mul=281540 sub=3690 toBinary=0 + // add=197836 equals=3048 fromBinary=0 mul=188488 sub=3810 toBinary=0 + err := test.IsSolved(&e6Expc2{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Expc1 struct { + A, B E6 +} + +func (circuit *e6Expc1) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Expc1(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestExpc1Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.Expc1(&a) + + witness := e6Expc1{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=578954 equals=8028 fromBinary=0 mul=566870 sub=7248 toBinary=0 + err := test.IsSolved(&e6Expc1{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6MulBy034 struct { + A, B E6 + R0, R1, R2 baseEl +} + +func (circuit *e6MulBy034) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + var l LineEvaluation + l.R0 = circuit.R0 + l.R1 = circuit.R1 + l.R2 = circuit.R2 + expected := e.MulBy034(&circuit.A, &l) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestMulBy034Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + var c0, c0Copy, c3, c4 fp.Element + c0.SetRandom() + c3.SetRandom() + c4.SetRandom() + c0Copy.Set(&c0) + b.MulBy034(&c0, &c3, &c4) + + witness := e6MulBy034{ + A: FromE6(&a), + R0: emulated.ValueOf[emulated.BW6761Fp](c0Copy), + R1: emulated.ValueOf[emulated.BW6761Fp](c3), + R2: emulated.ValueOf[emulated.BW6761Fp](c4), + B: FromE6(&b), + } + + // add=54322 equals=823 fromBinary=0 mul=53414 sub=702 toBinary=0 + err := test.IsSolved(&e6MulBy034{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +/* +type e6Mul034By034 struct { + D0, D3, D4 baseEl + C0, C3, C4 baseEl + Res E6 +} + +func (circuit *e6Mul034By034) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Mul034By034(&circuit.D0, &circuit.D3, &circuit.D4, &circuit.C0, &circuit.C3, &circuit.C4) + e.AssertIsEqual(expected, &circuit.Res) + return nil +} + +func TestMul034By034Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a bw6761.E6 + var d0, d3, d4, c0, c3, c4 fp.Element + d0.SetRandom() + d3.SetRandom() + d4.SetRandom() + c0.SetRandom() + c3.SetRandom() + c4.SetRandom() + a = bw6761.Mul034By034(&d0, &d3, &d4, &c0, &c3, &c4) + + witness := e6Mul034By034{ + D0: emulated.ValueOf[emulated.BW6761Fp](d0), + D3: emulated.ValueOf[emulated.BW6761Fp](d3), + D4: emulated.ValueOf[emulated.BW6761Fp](d4), + C0: emulated.ValueOf[emulated.BW6761Fp](c0), + C3: emulated.ValueOf[emulated.BW6761Fp](c3), + C4: emulated.ValueOf[emulated.BW6761Fp](c4), + Res: FromE6(&a), + } + + // add=28733 equals=438 fromBinary=0 mul=28386 sub=401 toBinary=0 + err := test.IsSolved(&e6Mul034By034{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} +*/ diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go new file mode 100644 index 0000000000..1a142a559c --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -0,0 +1,415 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package fields_bw6761 + +import ( + "bytes" + "fmt" + "math/big" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/emulated" + "github.com/consensys/gnark/test" +) + +type e6Add struct { + A, B, C E6 +} + +func (circuit *e6Add) Define(api frontend.API) error { + var expected E6 + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + 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 bw6761.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 { + var expected E6 + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + 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 bw6761.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 e6Double struct { + A, B E6 +} + +func (circuit *e6Double) Define(api frontend.API) error { + var expected E6 + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected = *e.Double(&circuit.A) + e.AssertIsEqual(&expected, &circuit.B) + return nil +} + +func TestDoubleFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Double(&a) + + witness := e6Double{ + A: FromE6(&a), + B: FromE6(&b), + } + + err := test.IsSolved(&e6Double{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Mul struct { + A, B, C E6 +} + +func (circuit *e6Mul) Define(api frontend.API) error { + var expected E6 + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + 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 bw6761.E6 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + c.Mul(&a, &b) + + witness := e6Mul{ + A: FromE6(&a), + B: FromE6(&b), + C: FromE6(&c), + } + + // add=72955 equals=1098 fromBinary=0 mul=71442 sub=1049 toBinary=0 + // counters add=34695 equals=582 fromBinary=0 mul=32806 sub=903 toBinary=0 + err := test.IsSolved(&e6Mul{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Square struct { + A, B E6 +} + +func (circuit *e6Square) Define(api frontend.API) error { + var expected E6 + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected = *e.Square(&circuit.A) + e.AssertIsEqual(&expected, &circuit.B) + return nil +} + +func TestSquareFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Square(&a) + + witness := e6Square{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=51966 equals=768 fromBinary=0 mul=50706 sub=780 toBinary=0 + // add=29636 equals=456 fromBinary=0 mul=28014 sub=686 toBinary=0 + err := test.IsSolved(&e6Square{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Inverse struct { + A, B E6 +} + +func (circuit *e6Inverse) Define(api frontend.API) error { + var expected E6 + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected = *e.Inverse(&circuit.A) + e.AssertIsEqual(&expected, &circuit.B) + return nil +} + +func TestInverseFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Inverse(&a) + + witness := e6Inverse{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=136669 equals=2033 fromBinary=0 mul=134924 sub=1691 toBinary=0 + // add=114515 equals=1721 fromBinary=0 mul=112628 sub=1617 toBinary=0 + err := test.IsSolved(&e6Inverse{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Conjugate struct { + A, B E6 +} + +func (circuit *e6Conjugate) Define(api frontend.API) error { + var expected E6 + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected = *e.Conjugate(&circuit.A) + e.AssertIsEqual(&expected, &circuit.B) + return nil +} + +func TestConjugateFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Conjugate(&a) + + witness := e6Conjugate{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=7095 equals=108 fromBinary=0 mul=6990 sub=165 toBinary=0 + err := test.IsSolved(&e6Conjugate{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6CyclotomicSquareCompressed struct { + A, B E6 +} + +func (circuit *e6CyclotomicSquareCompressed) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.CyclotomicSquareCompressed(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestCyclotomicSquareCompressedFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.CyclotomicSquareCompressed(&a) + + witness := e6CyclotomicSquareCompressed{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=28975 equals=438 fromBinary=0 mul=28342 sub=401 toBinary=0 + // add=19987 equals=298 fromBinary=0 mul=19064 sub=339 toBinary=0 + err := test.IsSolved(&e6CyclotomicSquareCompressed{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6DecompressKarabina struct { + A, B E6 +} + +func (circuit *e6DecompressKarabina) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.DecompressKarabina(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestDecompressKarabinaFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + a.DecompressKarabina(&a) + + witness := e6DecompressKarabina{ + A: FromE6(&b), + B: FromE6(&a), + } + + // add=28723 equals=438 fromBinary=0 mul=28342 sub=389 toBinary=0 + // add=17214 equals=284 fromBinary=0 mul=16709 sub=289 toBinary=0 + err := test.IsSolved(&e6DecompressKarabina{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6CyclotomicSquare struct { + A, B E6 +} + +func (circuit *e6CyclotomicSquare) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.CyclotomicSquare(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestCyclotomicSquareFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.CyclotomicSquare(&a) + + witness := e6CyclotomicSquare{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=39871 equals=603 fromBinary=0 mul=39018 sub=563 toBinary=0 + // add=26229 equals=393 fromBinary=0 mul=24991 sub=495 toBinary=0 + err := test.IsSolved(&e6CyclotomicSquare{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +func TestNewE6(t *testing.T) { + a, _ := new(big.Int).SetString("83175370704474795125412693555818269399912070346366058924020987848926901443521059146219467322598189008118890021654143123310841437365188932207798122475953021372633091598654279100089387195482601214045864119525747542050698192923485116081505909964897146420", 10) + b, _ := new(big.Int).SetString("6368022403585149186567793239866157016295592880888573809019876686976707722559034074218497709896494419772477540172749411175273320318562448286368763367020957539305330983642372720448125982288656809793421178887827471755589212191192898758939906986677524020", 10) + aMod := new(big.Int).Mod(a, fp.Modulus()) + bMod := new(big.Int).Mod(b, fp.Modulus()) + fmt.Println(aMod.String() == bMod.String()) + fmt.Println(a.BitLen()) + +} + +func TestKp(t *testing.T) { + k, _ := new(big.Int).SetString("85056971769626083370706587971739925665000858406518962290778652625791906552342223597311466786558418076119513109067357166262376397813915389059478465293248265362213566556434506971085077824793753991611718645003559647894773349413422987352", 10) + kp := new(big.Int).Mul(k, fp.Modulus()) + fmt.Println(kp.String()) +} + +func TestCreateLimbs(t *testing.T) { + nbBits := 70 + input := big.NewInt(48) + size := input.BitLen() / nbBits + res := make([]*big.Int, size+1) + for i := 0; i < size+1; i++ { + res[i] = new(big.Int) + } + base := new(big.Int).Lsh(big.NewInt(1), uint(nbBits)) + fmt.Println("base:", base.String()) + r, _ := new(big.Int).SetString("21888242871839275222246405745257275088459989201842526013704146201830519410949", 10) + fmt.Println(new(big.Int).Sub(ecc.BN254.ScalarField(), r).String()) + tmp := new(big.Int).Set(input) + for i := 0; i < len(res); i++ { + res[i].Mod(tmp, base) + tmp.Rsh(tmp, uint(nbBits)) + } + var buf bytes.Buffer + for i := 0; i < size+1; i++ { + buf.WriteString(res[i].String()) + if i != size { + buf.WriteString("+") + } + } + fmt.Println(buf.String()) +} diff --git a/std/algebra/emulated/fields_bw6761/frobenius.go b/std/algebra/emulated/fields_bw6761/frobenius.go new file mode 100644 index 0000000000..5ab65ddbb1 --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/frobenius.go @@ -0,0 +1,45 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package fields_bw6761 + +import ( + "github.com/consensys/gnark/std/math/emulated" +) + +var ( + _frobA = emulated.ValueOf[emulated.BW6761Fp]("4922464560225523242118178942575080391082002530232324381063048548642823052024664478336818169867474395270858391911405337707247735739826664939444490469542109391530482826728203582549674992333383150446779312029624171857054392282775648") + _frobB = emulated.ValueOf[emulated.BW6761Fp]("1968985824090209297278610739700577151397666382303825728450741611566800370218827257750865013421937292370006175842381275743914023380727582819905021229583192207421122272650305267822868639090213645505120388400344940985710520836292650") + _frobC = emulated.ValueOf[emulated.BW6761Fp]("4922464560225523242118178942575080391082002530232324381063048548642823052024664478336818169867474395270858391911405337707247735739826664939444490469542109391530482826728203582549674992333383150446779312029624171857054392282775649") + _frobAC = emulated.ValueOf[emulated.BW6761Fp]("-1") + _frobBC = emulated.ValueOf[emulated.BW6761Fp]("1968985824090209297278610739700577151397666382303825728450741611566800370218827257750865013421937292370006175842381275743914023380727582819905021229583192207421122272650305267822868639090213645505120388400344940985710520836292651") +) + +// Frobenius set z in E6 to Frobenius(x), return z +func (e Ext6) Frobenius(x *E6) *E6 { + var z E6 + z.B0.A0 = x.B0.A0 + z.B0.A1 = *e.fp.Mul(&x.B0.A1, &_frobA) + z.B0.A2 = *e.fp.Mul(&x.B0.A2, &_frobB) + + z.B1.A0 = *e.fp.Mul(&x.B1.A0, &_frobC) + z.B1.A1 = *e.fp.Mul(&x.B1.A1, &_frobAC) + z.B1.A2 = *e.fp.Mul(&x.B1.A2, &_frobBC) + + return &z +} diff --git a/std/algebra/emulated/fields_bw6761/hints.go b/std/algebra/emulated/fields_bw6761/hints.go new file mode 100644 index 0000000000..dd4539e2ca --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/hints.go @@ -0,0 +1,63 @@ +package fields_bw6761 + +import ( + "math/big" + + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark/constraint/solver" + "github.com/consensys/gnark/std/math/emulated" +) + +func init() { + solver.RegisterHint(GetHints()...) +} + +// GetHints returns all hint functions used in the package. +func GetHints() []solver.Hint { + return []solver.Hint{ + // E3 + divE3Hint, + inverseE3Hint, + } +} + +func inverseE3Hint(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 bw6761.E3 + + a.A0.SetBigInt(inputs[0]) + a.A1.SetBigInt(inputs[1]) + a.A2.SetBigInt(inputs[2]) + + c.Inverse(&a) + + c.A0.BigInt(outputs[0]) + c.A1.BigInt(outputs[1]) + c.A2.BigInt(outputs[2]) + + return nil + }) +} + +func divE3Hint(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 bw6761.E3 + + a.A0.SetBigInt(inputs[0]) + a.A1.SetBigInt(inputs[1]) + a.A2.SetBigInt(inputs[2]) + b.A0.SetBigInt(inputs[3]) + b.A1.SetBigInt(inputs[4]) + b.A2.SetBigInt(inputs[5]) + + c.Inverse(&b).Mul(&c, &a) + + c.A0.BigInt(outputs[0]) + c.A1.BigInt(outputs[1]) + c.A2.BigInt(outputs[2]) + + return nil + }) +} diff --git a/std/math/emulated/emparams/emparams.go b/std/math/emulated/emparams/emparams.go index 9b888d6a4d..01088ee435 100644 --- a/std/math/emulated/emparams/emparams.go +++ b/std/math/emulated/emparams/emparams.go @@ -31,6 +31,12 @@ func (sixLimbPrimeField) NbLimbs() uint { return 6 } func (sixLimbPrimeField) BitsPerLimb() uint { return 64 } func (sixLimbPrimeField) IsPrime() bool { return true } +type twelveLimbPrimeField struct{} + +func (twelveLimbPrimeField) NbLimbs() uint { return 12 } +func (twelveLimbPrimeField) BitsPerLimb() uint { return 64 } +func (twelveLimbPrimeField) IsPrime() bool { return true } + // Goldilocks provides type parametrization for field emulation: // - limbs: 1 // - limb width: 64 bits @@ -199,3 +205,31 @@ func (P384Fp) Modulus() *big.Int { return elliptic.P384().Params().P } type P384Fr struct{ sixLimbPrimeField } func (P384Fr) Modulus() *big.Int { return elliptic.P384().Params().N } + +// BW6761Fp provides type parametrization for field emulation: +// - limbs: 12 +// - limb width: 64 bits +// +// The prime modulus for type parametrisation is: +// +// 0x122e824fb83ce0ad187c94004faff3eb926186a81d14688528275ef8087be41707ba638e584e91903cebaff25b423048689c8ed12f9fd9071dcd3dc73ebff2e98a116c25667a8f8160cf8aeeaf0a437e6913e6870000082f49d00000000008b (base 16) +// 6891450384315732539396789682275657542479668912536150109513790160209623422243491736087683183289411687640864567753786613451161759120554247759349511699125301598951605099378508850372543631423596795951899700429969112842764913119068299 (base 10) +// +// This is the base field of the BW6-761 curve. +type BW6761Fp struct{ twelveLimbPrimeField } + +func (fp BW6761Fp) Modulus() *big.Int { return ecc.BW6_761.BaseField() } + +// BW6761Fr provides type parametrization for field emulation: +// - limbs: 6 +// - limb width: 64 bits +// +// The prime modulus for type parametrisation is: +// +// 0x1ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001 (base 16) +// 258664426012969094010652733694893533536393512754914660539884262666720468348340822774968888139573360124440321458177 (base 10) +// +// This is the scalar field of the BW6-761 curve. +type BW6761Fr struct{ sixLimbPrimeField } + +func (fp BW6761Fr) Modulus() *big.Int { return ecc.BW6_761.ScalarField() } diff --git a/std/math/emulated/params.go b/std/math/emulated/params.go index fc3d8abfc1..892d141d38 100644 --- a/std/math/emulated/params.go +++ b/std/math/emulated/params.go @@ -37,4 +37,6 @@ type ( P256Fr = emparams.P256Fr P384Fp = emparams.P384Fp P384Fr = emparams.P384Fr + BW6761Fp = emparams.BW6761Fp + BW6761Fr = emparams.BW6761Fr ) From cf4cf207a81567de32720e37fbde75865e85dc06 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 7 Jul 2023 12:11:06 +0100 Subject: [PATCH 02/38] perf(bw6/pairing): hinted inverse and division in E6 --- std/algebra/emulated/fields_bw6761/e6.go | 50 ++++++++++++----- std/algebra/emulated/fields_bw6761/hints.go | 61 +++++++++++++++++++++ 2 files changed, 96 insertions(+), 15 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index e26832abde..89834baeff 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -331,25 +331,45 @@ func (e Ext6) CyclotomicSquare(x *E6) *E6 { return &z } -// Inverse set z to the inverse of x in *E6 and return z -// -// if x == 0, sets and returns z = x func (e Ext6) Inverse(x *E6) *E6 { - // Algorithm 23 from https://eprint.iacr.org/2010/354.pdf + res, err := e.fp.NewHint(inverseE6Hint, 6, &x.B0.A0, &x.B0.A1, &x.B0.A2, &x.B1.A0, &x.B1.A1, &x.B1.A2) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } - t0 := e.Ext3.Square(&x.B0) - t1 := e.Ext3.Square(&x.B1) - tmp := e.Ext3.MulByNonResidue(t1) - t0 = e.Ext3.Sub(t0, tmp) - t1 = e.Ext3.Inverse(t0) - b0 := e.Ext3.Mul(&x.B0, t1) - b1 := e.Ext3.Mul(&x.B1, t1) - b1 = e.Ext3.Neg(b1) + inv := E6{ + B0: E3{A0: *res[0], A1: *res[1], A2: *res[2]}, + B1: E3{A0: *res[3], A1: *res[4], A2: *res[5]}, + } + one := e.One() - return &E6{ - B0: *b0, - B1: *b1, + // 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, 12, &x.B0.A0, &x.B0.A1, &x.B0.A2, &x.B1.A0, &x.B1.A1, &x.B1.A2, &y.B0.A0, &y.B0.A1, &y.B0.A2, &y.B1.A0, &y.B1.A1, &y.B1.A2) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + + div := E6{ + B0: E3{A0: *res[0], A1: *res[1], A2: *res[2]}, + B1: E3{A0: *res[3], A1: *res[4], A2: *res[5]}, } + + // 1 == inv * x + _x := e.Mul(&div, x) + e.AssertIsEqual(x, _x) + + return &div + } // Conjugate set z to x conjugated and return z diff --git a/std/algebra/emulated/fields_bw6761/hints.go b/std/algebra/emulated/fields_bw6761/hints.go index dd4539e2ca..5c100d085c 100644 --- a/std/algebra/emulated/fields_bw6761/hints.go +++ b/std/algebra/emulated/fields_bw6761/hints.go @@ -18,9 +18,13 @@ func GetHints() []solver.Hint { // E3 divE3Hint, inverseE3Hint, + // E6 + divE6Hint, + inverseE6Hint, } } +// E3 func inverseE3Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { return emulated.UnwrapHint(nativeInputs, nativeOutputs, func(mod *big.Int, inputs, outputs []*big.Int) error { @@ -61,3 +65,60 @@ func divE3Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error return nil }) } + +// E6 +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 bw6761.E6 + + a.B0.A0.SetBigInt(inputs[0]) + a.B0.A1.SetBigInt(inputs[1]) + a.B0.A2.SetBigInt(inputs[2]) + a.B1.A0.SetBigInt(inputs[3]) + a.B1.A1.SetBigInt(inputs[4]) + a.B1.A2.SetBigInt(inputs[5]) + + c.Inverse(&a) + + c.B0.A0.BigInt(outputs[0]) + c.B0.A1.BigInt(outputs[1]) + c.B0.A2.BigInt(outputs[2]) + c.B1.A0.BigInt(outputs[3]) + c.B1.A1.BigInt(outputs[4]) + c.B1.A2.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 bw6761.E6 + + a.B0.A0.SetBigInt(inputs[0]) + a.B0.A1.SetBigInt(inputs[1]) + a.B0.A2.SetBigInt(inputs[2]) + a.B1.A0.SetBigInt(inputs[3]) + a.B1.A1.SetBigInt(inputs[4]) + a.B1.A2.SetBigInt(inputs[5]) + b.B0.A0.SetBigInt(inputs[6]) + b.B0.A1.SetBigInt(inputs[7]) + b.B0.A2.SetBigInt(inputs[8]) + b.B1.A0.SetBigInt(inputs[9]) + b.B1.A1.SetBigInt(inputs[10]) + b.B1.A2.SetBigInt(inputs[11]) + + c.Inverse(&b).Mul(&c, &a) + + c.B0.A0.BigInt(outputs[0]) + c.B0.A1.BigInt(outputs[1]) + c.B0.A2.BigInt(outputs[2]) + c.B1.A0.BigInt(outputs[3]) + c.B1.A1.BigInt(outputs[4]) + c.B1.A2.BigInt(outputs[5]) + + return nil + }) +} From 4e30aeecc082280fec5f3d95ce8c7e64aa38efde Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 7 Jul 2023 12:49:14 +0100 Subject: [PATCH 03/38] feat: add bw6 final exp --- std/algebra/emulated/sw_bw6761/g1.go | 34 +++++ std/algebra/emulated/sw_bw6761/g2.go | 34 +++++ std/algebra/emulated/sw_bw6761/pairing.go | 136 ++++++++++++++++++ .../emulated/sw_bw6761/pairing_test.go | 87 +++++++++++ 4 files changed, 291 insertions(+) create mode 100644 std/algebra/emulated/sw_bw6761/g1.go create mode 100644 std/algebra/emulated/sw_bw6761/g2.go create mode 100644 std/algebra/emulated/sw_bw6761/pairing.go create mode 100644 std/algebra/emulated/sw_bw6761/pairing_test.go diff --git a/std/algebra/emulated/sw_bw6761/g1.go b/std/algebra/emulated/sw_bw6761/g1.go new file mode 100644 index 0000000000..143bdb97c7 --- /dev/null +++ b/std/algebra/emulated/sw_bw6761/g1.go @@ -0,0 +1,34 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package sw_bw6761 + +import ( + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark/std/algebra/emulated/sw_emulated" + "github.com/consensys/gnark/std/math/emulated" +) + +type G1Affine = sw_emulated.AffinePoint[emulated.BW6761Fp] + +func NewG1Affine(v bw6761.G1Affine) G1Affine { + return G1Affine{ + X: emulated.ValueOf[emulated.BW6761Fp](v.X), + Y: emulated.ValueOf[emulated.BW6761Fp](v.Y), + } +} diff --git a/std/algebra/emulated/sw_bw6761/g2.go b/std/algebra/emulated/sw_bw6761/g2.go new file mode 100644 index 0000000000..175ac578bc --- /dev/null +++ b/std/algebra/emulated/sw_bw6761/g2.go @@ -0,0 +1,34 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package sw_bw6761 + +import ( + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark/std/algebra/emulated/sw_emulated" + "github.com/consensys/gnark/std/math/emulated" +) + +type G2Affine = sw_emulated.AffinePoint[emulated.BW6761Fp] + +func NewG2Affine(v bw6761.G2Affine) G2Affine { + return G2Affine{ + X: emulated.ValueOf[emulated.BW6761Fp](v.X), + Y: emulated.ValueOf[emulated.BW6761Fp](v.Y), + } +} diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go new file mode 100644 index 0000000000..8db5b8c742 --- /dev/null +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -0,0 +1,136 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package sw_bw6761 + +import ( + "fmt" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/algebra/emulated/fields_bw6761" + "github.com/consensys/gnark/std/math/emulated" +) + +type Pairing struct { + *fields_bw6761.Ext6 + curveF emulated.Field[emulated.BW6761Fp] +} + +func NewPairing(api frontend.API) (*Pairing, error) { + ba, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + return nil, fmt.Errorf("new base api: %w", err) + } + return &Pairing{ + Ext6: fields_bw6761.NewExt6(ba), + }, nil +} + +// GT target group of the pairing +type GT = fields_bw6761.E6 + +// FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ +// where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r +// we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r +// where s is the cofactor 12(x_0+1) (El Housni and Guillevic) +func (pr Pairing) FinalExponentiation(z *GT, _z ...*GT) *GT { + + result := pr.Set(z) + + for _, a := range _z { + result = pr.Mul(result, a) + } + + // Easy part + // (p^3-1)(p+1) + buf := pr.Conjugate(result) + result = pr.Inverse(result) + buf = pr.Mul(buf, result) + result = pr.Frobenius(buf) + result = pr.Mul(result, buf) + + // Hard part (up to permutation) + // El Housni and Guillevic + // https://eprint.iacr.org/2020/351.pdf + m1 := pr.Expt(result) + _m1 := pr.Conjugate(m1) + m2 := pr.Expt(m1) + _m2 := pr.Conjugate(m2) + m3 := pr.Expt(m2) + f0 := pr.Frobenius(result) + f0 = pr.Mul(f0, result) + f0 = pr.Mul(f0, m2) + m2 = pr.CyclotomicSquare(_m1) + f0 = pr.Mul(f0, m2) + f0_36 := pr.CyclotomicSquare(f0) + f0_36 = pr.CyclotomicSquare(f0_36) + f0_36 = pr.CyclotomicSquare(f0_36) + f0_36 = pr.Mul(f0_36, f0) + f0_36 = pr.CyclotomicSquare(f0_36) + f0_36 = pr.CyclotomicSquare(f0_36) + g0 := pr.Mul(result, m1) + g0 = pr.Frobenius(g0) + g0 = pr.Mul(g0, m3) + g0 = pr.Mul(g0, _m2) + g0 = pr.Mul(g0, _m1) + g1 := pr.Expt(g0) + _g1 := pr.Conjugate(g1) + g2 := pr.Expt(g1) + g3 := pr.Expt(g2) + _g3 := pr.Conjugate(g3) + g4 := pr.Expt(g3) + _g4 := pr.Conjugate(g4) + g5 := pr.Expt(g4) + _g5 := pr.Conjugate(g5) + g6 := pr.Expt(g5) + gA := pr.Mul(g3, _g5) + gA = pr.CyclotomicSquare(gA) + gA = pr.Mul(gA, g6) + gA = pr.Mul(gA, g1) + gA = pr.Mul(gA, g0) + g034 := pr.Mul(g0, g3) + g034 = pr.Mul(g034, _g4) + gB := pr.CyclotomicSquare(g034) + gB = pr.Mul(gB, g034) + gB = pr.Mul(gB, g5) + gB = pr.Mul(gB, _g1) + _g1g2 := pr.Mul(_g1, g2) + gC := pr.Mul(_g3, _g1g2) + gC = pr.CyclotomicSquare(gC) + gC = pr.Mul(gC, _g1g2) + gC = pr.Mul(gC, g0) + gC = pr.CyclotomicSquare(gC) + gC = pr.Mul(gC, g2) + gC = pr.Mul(gC, g0) + gC = pr.Mul(gC, g4) + + // ht, hy = 13, 9 + // c1 = ht**2+3*hy**2 = 412 + h1 := pr.Expc1(gA) + // c2 = ht+hy = 22 + h2 := pr.Expc2(gB) + h2g2C := pr.CyclotomicSquare(gC) + h2g2C = pr.Mul(h2g2C, h2) + h4 := pr.CyclotomicSquare(h2g2C) + h4 = pr.Mul(h4, h2g2C) + h4 = pr.CyclotomicSquare(h4) + result = pr.Mul(h1, h4) + result = pr.Mul(result, f0_36) + + return result +} diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go new file mode 100644 index 0000000000..eed0d78c09 --- /dev/null +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -0,0 +1,87 @@ +/* + * + * Copyright © 2020 ConsenSys + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package sw_bw6761 + +import ( + "crypto/rand" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/std/algebra/emulated/fields_bw6761" + "github.com/consensys/gnark/test" +) + +const testCurve = ecc.BN254 + +type finalExponentiationBW6761 struct { + A GT + B GT +} + +func (circuit *finalExponentiationBW6761) Define(api frontend.API) error { + pr, err := NewPairing(api) + if err != nil { + panic(err) + } + expected := pr.FinalExponentiation(&circuit.A) + if err != nil { + return err + } + + pr.Equal(expected, &circuit.B) + + return nil +} + +func TestFinalExponentiationBW6761(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var ( + a bw6761.G1Affine + b bw6761.G2Affine + c bw6761.GT + r1, _ = rand.Int(rand.Reader, fr.Modulus()) + r2, _ = rand.Int(rand.Reader, fr.Modulus()) + ) + _, _, g1, g2 := bw6761.Generators() + + a.ScalarMultiplication(&g1, r1) + b.ScalarMultiplication(&g2, r2) + c, err := bw6761.MillerLoop([]bw6761.G1Affine{a}, []bw6761.G2Affine{b}) + if err != nil { + panic(err) + } + + d := bw6761.FinalExponentiation(&c) + + witness := finalExponentiationBW6761{ + A: fields_bw6761.FromE6(&c), + B: fields_bw6761.FromE6(&d), + } + + err = test.IsSolved(&finalExponentiationBW6761{}, &witness, testCurve.ScalarField()) + assert.NoError(err) + + _, err = frontend.Compile(testCurve.ScalarField(), r1cs.NewBuilder, &finalExponentiationBW6761{}, frontend.IgnoreUnconstrainedInputs()) + assert.NoError(err) +} From 63131b7a820e86cf259fdcb2ab9997cdd084a03b Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 10 Jul 2023 13:34:13 +0100 Subject: [PATCH 04/38] fix: bw6 field emulation --- std/algebra/emulated/fields_bw6761/e3.go | 81 ++++-- std/algebra/emulated/fields_bw6761/e6.go | 40 +-- .../emulated/fields_bw6761/e6_pairing.go | 145 +++++++---- .../emulated/fields_bw6761/e6_pairing_test.go | 222 ----------------- std/algebra/emulated/fields_bw6761/e6_test.go | 185 ++++++++++++++ .../{pairing_test.go => _pairing_test.go} | 0 std/algebra/emulated/sw_bw6761/pairing.go | 235 ++++++++++++++++++ 7 files changed, 600 insertions(+), 308 deletions(-) delete mode 100644 std/algebra/emulated/fields_bw6761/e6_pairing_test.go rename std/algebra/emulated/sw_bw6761/{pairing_test.go => _pairing_test.go} (100%) diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index 3e7ecc915f..3cabb0b0f9 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -140,9 +140,9 @@ func (e Ext3) Conjugate(x *E3) *E3 { // MulByElement multiplies an element in *E3 by an element in fp func (e Ext3) MulByElement(x *E3, y *baseEl) *E3 { - a0 := e.fp.Mul(&x.A0, y) - a1 := e.fp.Mul(&x.A1, y) - a2 := e.fp.Mul(&x.A2, y) + a0 := e.fp.MulMod(&x.A0, y) + a1 := e.fp.MulMod(&x.A1, y) + a2 := e.fp.MulMod(&x.A2, y) z := &E3{ A0: *a0, A1: *a1, @@ -165,23 +165,23 @@ func (e Ext3) MulByConstElement(x *E3, y *big.Int) *E3 { // MulBy01 multiplication by sparse element (c0,c1,0) func (e Ext3) MulBy01(z *E3, c0, c1 *baseEl) *E3 { - a := e.fp.Mul(&z.A0, c0) - b := e.fp.Mul(&z.A1, c1) + a := e.fp.MulMod(&z.A0, c0) + b := e.fp.MulMod(&z.A1, c1) tmp := e.fp.Add(&z.A1, &z.A2) - t0 := e.fp.Mul(c1, tmp) + t0 := e.fp.MulMod(c1, tmp) t0 = e.fp.Sub(t0, b) t0 = MulByNonResidue(e.fp, t0) t0 = e.fp.Add(t0, a) tmp = e.fp.Add(&z.A0, &z.A2) - t2 := e.fp.Mul(c0, tmp) + t2 := e.fp.MulMod(c0, tmp) t2 = e.fp.Sub(t2, a) t2 = e.fp.Add(t2, b) t1 := e.fp.Add(c0, c1) tmp = e.fp.Add(&z.A0, &z.A1) - t1 = e.fp.Mul(t1, tmp) + t1 = e.fp.MulMod(t1, tmp) t1 = e.fp.Sub(t1, a) t1 = e.fp.Sub(t1, b) @@ -195,15 +195,15 @@ func (e Ext3) MulBy01(z *E3, c0, c1 *baseEl) *E3 { // MulBy1 multiplication of E6 by sparse element (0, c1, 0) func (e Ext3) MulBy1(z *E3, c1 baseEl) *E3 { - b := e.fp.Mul(&z.A1, &c1) + b := e.fp.MulMod(&z.A1, &c1) tmp := e.fp.Add(&z.A1, &z.A2) - t0 := e.fp.Mul(&c1, tmp) + t0 := e.fp.MulMod(&c1, tmp) t0 = e.fp.Sub(t0, b) t0 = MulByNonResidue(e.fp, t0) tmp = e.fp.Add(&z.A0, &z.A1) - t1 := e.fp.Mul(&c1, tmp) + t1 := e.fp.MulMod(&c1, tmp) t1 = e.fp.Sub(t1, b) return &E3{ @@ -213,29 +213,66 @@ func (e Ext3) MulBy1(z *E3, c1 baseEl) *E3 { } } +// Mul01By01 multiplies two E3 sparse element of the form: +// +// E3{ +// A0: c0, +// A1: c1, +// A2: 0, +// } +// +// and +// +// E3{ +// A0: d0, +// A1: d1, +// A2: 0, +// } +func (e Ext3) Mul01By01(c0, c1, d0, d1 *baseEl) *E3 { + a := e.fp.MulMod(d0, c0) + b := e.fp.MulMod(d1, c1) + t0 := e.fp.MulMod(c1, d1) + t0 = e.fp.Sub(t0, b) + t0 = MulByNonResidue(e.fp, t0) + t0 = e.fp.Add(t0, a) + t2 := e.fp.MulMod(c0, d0) + t2 = e.fp.Sub(t2, a) + t2 = e.fp.Add(t2, b) + t1 := e.fp.Add(c0, c1) + tmp := e.fp.Add(d0, d1) + t1 = e.fp.MulMod(t1, tmp) + t1 = e.fp.Sub(t1, a) + t1 = e.fp.Sub(t1, b) + return &E3{ + A0: *t0, + A1: *t1, + A2: *t2, + } +} + // Mul sets z to the *E3-product of x,y, returns z func (e Ext3) Mul(x, y *E3) *E3 { // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf - t0 := e.fp.Mul(&x.A0, &y.A0) - t1 := e.fp.Mul(&x.A1, &y.A1) - t2 := e.fp.Mul(&x.A2, &y.A2) + t0 := e.fp.MulMod(&x.A0, &y.A0) + t1 := e.fp.MulMod(&x.A1, &y.A1) + t2 := e.fp.MulMod(&x.A2, &y.A2) c0 := e.fp.Add(&x.A1, &x.A2) tmp := e.fp.Add(&y.A1, &y.A2) - c0 = e.fp.Mul(c0, tmp) + c0 = e.fp.MulMod(c0, tmp) c0 = e.fp.Sub(c0, t1) c0 = e.fp.Sub(c0, t2) c0 = MulByNonResidue(e.fp, c0) tmp = e.fp.Add(&x.A0, &x.A2) c2 := e.fp.Add(&y.A0, &y.A2) - c2 = e.fp.Mul(c2, tmp) + c2 = e.fp.MulMod(c2, tmp) c2 = e.fp.Sub(c2, t0) c2 = e.fp.Sub(c2, t2) c1 := e.fp.Add(&x.A0, &x.A1) tmp = e.fp.Add(&y.A0, &y.A1) - c1 = e.fp.Mul(c1, tmp) + c1 = e.fp.MulMod(c1, tmp) c1 = e.fp.Sub(c1, t0) c1 = e.fp.Sub(c1, t1) t2 = MulByNonResidue(e.fp, t2) @@ -257,17 +294,17 @@ func (e Ext3) Square(x *E3) *E3 { // Algorithm 16 from https://eprint.iacr.org/2010/354.pdf c6 := e.fp.MulConst(&x.A1, big.NewInt(2)) - c4 := e.fp.Mul(&x.A0, c6) // x.A0 * xA1 * 2 - c5 := e.fp.Mul(&x.A2, &x.A2) + c4 := e.fp.MulMod(&x.A0, c6) // x.A0 * xA1 * 2 + c5 := e.fp.MulMod(&x.A2, &x.A2) c1 := MulByNonResidue(e.fp, c5) c1 = e.fp.Add(c1, c4) c2 := e.fp.Sub(c4, c5) - c3 := e.fp.Mul(&x.A0, &x.A0) + c3 := e.fp.MulMod(&x.A0, &x.A0) c4 = e.fp.Sub(&x.A0, &x.A1) c4 = e.fp.Add(c4, &x.A2) - c5 = e.fp.Mul(c6, &x.A2) // x.A1 * xA2 * 2 - c4 = e.fp.Mul(c4, c4) + c5 = e.fp.MulMod(c6, &x.A2) // x.A1 * xA2 * 2 + c4 = e.fp.MulMod(c4, c4) c0 := MulByNonResidue(e.fp, c5) c4 = e.fp.Add(c4, c5) c4 = e.fp.Sub(c4, c3) diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 89834baeff..465a4a9547 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -136,13 +136,13 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { var t [7]*baseEl // t0 = g1² - t[0] = e.fp.Mul(&x.B0.A1, &x.B0.A1) + t[0] = e.fp.MulMod(&x.B0.A1, &x.B0.A1) // t1 = g5² - t[1] = e.fp.Mul(&x.B1.A2, &x.B1.A2) + t[1] = e.fp.MulMod(&x.B1.A2, &x.B1.A2) // t5 = g1 + g5 t[5] = e.fp.Add(&x.B0.A1, &x.B1.A2) // t2 = (g1 + g5)² - t[2] = e.fp.Mul(t[5], t[5]) + t[2] = e.fp.MulMod(t[5], t[5]) // t3 = g1² + g5² t[3] = e.fp.Add(t[0], t[1]) @@ -152,9 +152,9 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { // t6 = g3 + g2 t[6] = e.fp.Add(&x.B1.A0, &x.B0.A2) // t3 = (g3 + g2)² - t[3] = e.fp.Mul(t[6], t[6]) + t[3] = e.fp.MulMod(t[6], t[6]) // t2 = g3² - t[2] = e.fp.Mul(&x.B1.A0, &x.B1.A0) + t[2] = e.fp.MulMod(&x.B1.A0, &x.B1.A0) // t6 = 2 * nr * g1 * g5 t[6] = MulByNonResidue(e.fp, t[5]) @@ -172,7 +172,7 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { t[6] = e.fp.Sub(t[5], &x.B0.A2) // t1 = g2² - t[1] = e.fp.Mul(&x.B0.A2, &x.B0.A2) + t[1] = e.fp.MulMod(&x.B0.A2, &x.B0.A2) // t6 = 2 * nr * g5² + 2 * g1² - 2*g2 t[6] = e.fp.Add(t[6], t[6]) @@ -225,13 +225,13 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { one := e.fp.One() // t0 = g1^2 - t[0] = e.fp.Mul(&x.B0.A1, &x.B0.A1) + t[0] = e.fp.MulMod(&x.B0.A1, &x.B0.A1) // t1 = 3 * g1^2 - 2 * g2 t[1] = e.fp.Sub(t[0], &x.B0.A2) t[1] = e.fp.Add(t[1], t[1]) t[1] = e.fp.Add(t[1], t[0]) // t0 = E * g5^2 + t1 - t[2] = e.fp.Mul(&x.B1.A2, &x.B1.A2) + t[2] = e.fp.MulMod(&x.B1.A2, &x.B1.A2) t[0] = MulByNonResidue(e.fp, t[2]) t[0] = e.fp.Add(t[0], t[1]) // t1 = 1/(4 * g3) @@ -243,14 +243,14 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { a1 := z.B1.A1 // t1 = g2 * g1 - t[1] = e.fp.Mul(&x.B0.A2, &x.B0.A1) + t[1] = e.fp.MulMod(&x.B0.A2, &x.B0.A1) // t2 = 2 * g4² - 3 * g2 * g1 - t[2] = e.fp.Mul(&a1, &a1) + t[2] = e.fp.MulMod(&a1, &a1) t[2] = e.fp.Sub(t[2], t[1]) t[2] = e.fp.Add(t[2], t[2]) t[2] = e.fp.Sub(t[2], t[1]) // t1 = g3 * g5 (g3 can be 0) - t[1] = e.fp.Mul(&x.B1.A0, &x.B1.A2) + t[1] = e.fp.MulMod(&x.B1.A0, &x.B1.A2) // c₀ = E * (2 * g4² + g3 * g5 - 3 * g2 * g1) + 1 t[2] = e.fp.Add(t[2], t[1]) @@ -280,22 +280,22 @@ func (e Ext6) CyclotomicSquare(x *E6) *E6 { var t [9]*baseEl - t[0] = e.fp.Mul(&x.B1.A1, &x.B1.A1) - t[1] = e.fp.Mul(&x.B0.A0, &x.B0.A0) + t[0] = e.fp.MulMod(&x.B1.A1, &x.B1.A1) + t[1] = e.fp.MulMod(&x.B0.A0, &x.B0.A0) t[6] = e.fp.Add(&x.B1.A1, &x.B0.A0) - t[6] = e.fp.Mul(t[6], t[6]) + t[6] = e.fp.MulMod(t[6], t[6]) t[6] = e.fp.Sub(t[6], t[0]) t[6] = e.fp.Sub(t[6], t[1]) // 2*x4*x0 - t[2] = e.fp.Mul(&x.B0.A2, &x.B0.A2) - t[3] = e.fp.Mul(&x.B1.A0, &x.B1.A0) + t[2] = e.fp.MulMod(&x.B0.A2, &x.B0.A2) + t[3] = e.fp.MulMod(&x.B1.A0, &x.B1.A0) t[7] = e.fp.Add(&x.B0.A2, &x.B1.A0) - t[7] = e.fp.Mul(t[7], t[7]) + t[7] = e.fp.MulMod(t[7], t[7]) t[7] = e.fp.Sub(t[7], t[2]) t[7] = e.fp.Sub(t[7], t[3]) // 2*x2*x3 - t[4] = e.fp.Mul(&x.B1.A2, &x.B1.A2) - t[5] = e.fp.Mul(&x.B0.A1, &x.B0.A1) + t[4] = e.fp.MulMod(&x.B1.A2, &x.B1.A2) + t[5] = e.fp.MulMod(&x.B0.A1, &x.B0.A1) t[8] = e.fp.Add(&x.B1.A2, &x.B0.A1) - t[8] = e.fp.Mul(t[8], t[8]) + t[8] = e.fp.MulMod(t[8], t[8]) t[8] = e.fp.Sub(t[8], t[4]) t[8] = e.fp.Sub(t[8], t[5]) t[8] = MulByNonResidue(e.fp, t[8]) // 2*x5*x1*u diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 2a373342b7..7d0e904bff 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -104,60 +104,117 @@ func (e Ext6) Expc1(x *E6) *E6 { return e.Set(result) } -// MulBy034 multiplication by sparse element (c0,0,0,c3,c4,0) -func (e Ext6) MulBy034(z *E6, l *LineEvaluation) *E6 { - z = e.Reduce(z) - - a := e.Ext3.MulByElement(&z.B0, &l.R0) - - b := e.Ext3.MulBy01(&z.B1, &l.R1, &l.R2) - - l.R0 = *e.fp.Add(&l.R0, &l.R1) +// MulBy034 multiplies z by an E6 sparse element of the form +// +// E6{ +// B0: E3{A0: 1, A1: 0, A2: 0}, +// B1: E3{A0: c3, A1: c4, A2: 0}, +// } +func (e *Ext6) MulBy034(z *E6, c3, c4 *baseEl) *E6 { + + a := z.B0 + b := z.B1 + b = *e.Ext3.MulBy01(&b, c3, c4) + c3 = e.fp.Add(e.fp.One(), c3) d := e.Ext3.Add(&z.B0, &z.B1) - d = e.Ext3.MulBy01(d, &l.R0, &l.R2) + d = e.Ext3.MulBy01(d, c3, c4) - b1 := e.Ext3.Add(a, b) - b1 = e.Ext3.Neg(b1) - b1 = e.Ext3.Add(b1, d) - b0 := e.Ext3.MulByNonResidue(b) - b0 = e.Ext3.Add(b0, a) + zC1 := e.Ext3.Add(&a, &b) + zC1 = e.Ext3.Neg(zC1) + zC1 = e.Ext3.Add(zC1, d) + zC0 := e.Ext3.MulByNonResidue(&b) + zC0 = e.Ext3.Add(zC0, &a) return &E6{ - B0: *b0, - B1: *b1, + B0: *zC0, + B1: *zC1, } } -// Mul034By034 multiplication of sparse element (c0,0,0,c3,c4,0) by sparse element (d0,0,0,d3,d4,0) -func (e Ext6) Mul034By034(d0, d3, d4, c0, c3, c4 *baseEl) *E6 { - - x0 := e.fp.Mul(c0, d0) - x3 := e.fp.Mul(c3, d3) - x4 := e.fp.Mul(c4, d4) - tmp := e.fp.Add(c0, c4) - x04 := e.fp.Add(d0, d4) - x04 = e.fp.Mul(x04, tmp) - x04 = e.fp.Sub(x04, x0) - x04 = e.fp.Sub(x04, x4) - tmp = e.fp.Add(c0, c3) - x03 := e.fp.Add(d0, d3) - x03 = e.fp.Mul(x03, tmp) - x03 = e.fp.Sub(x03, x0) - x03 = e.fp.Sub(x03, x3) - tmp = e.fp.Add(c3, c4) +// multiplies two E6 sparse element of the form: +// +// E6{ +// B0: E3{A0: 1, A1: 0, A2: 0}, +// B1: E3{A0: c3, A1: c4, A2: 0}, +// } +// +// and +// +// E6{ +// B0: E3{A0: 1, A1: 0, A2: 0}, +// B1: E3{A0: d3, A1: d4, A2: 0}, +// } +func (e *Ext6) Mul034By034(d3, d4, c3, c4 *baseEl) *[5]baseEl { + x3 := e.fp.MulMod(c3, d3) + x4 := e.fp.MulMod(c4, d4) + x04 := e.fp.Add(c4, d4) + x03 := e.fp.Add(c3, d3) + tmp := e.fp.Add(c3, c4) x34 := e.fp.Add(d3, d4) - x34 = e.fp.Mul(x34, tmp) + x34 = e.fp.MulMod(x34, tmp) x34 = e.fp.Sub(x34, x3) x34 = e.fp.Sub(x34, x4) - var z E6 - z.B0.A0 = *MulByNonResidue(e.fp, x4) - z.B0.A0 = *e.fp.Add(&z.B0.A0, x0) - z.B0.A1 = *x3 - z.B0.A2 = *x34 - z.B1.A0 = *x03 - z.B1.A1 = *x04 - z.B1.A2 = *e.fp.Zero() + zC0B0 := MulByNonResidue(e.fp, x4) + zC0B0 = e.fp.Add(zC0B0, e.fp.One()) + zC0B1 := x3 + zC0B2 := x34 + zC1B0 := x03 + zC1B1 := x04 - return &z + return &[5]baseEl{*zC0B0, *zC0B1, *zC0B2, *zC1B0, *zC1B1} +} + +// MulBy01234 multiplies z by an E6 sparse element of the form +// +// E6{ +// B0: E3{A0: c0, A1: c1, A2: c2}, +// B1: E3{A0: c3, A1: c4, A2: 0}, +// } +func (e *Ext6) MulBy01234(z *E6, x *[5]baseEl) *E6 { + c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} + c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} + a := e.Ext3.Add(&z.B0, &z.B1) + b := e.Ext3.Add(c0, c1) + a = e.Ext3.Mul(a, b) + b = e.Ext3.Mul(&z.B0, c0) + c := e.Ext3.MulBy01(&z.B1, &x[3], &x[4]) + z1 := e.Ext3.Sub(a, b) + z1 = e.Ext3.Sub(z1, c) + z0 := e.Ext3.MulByNonResidue(c) + z0 = e.Ext3.Add(z0, b) + return &E6{ + B0: *z0, + B1: *z1, + } +} + +// multiplies two E6 sparse element of the form: +// +// E6{ +// B0: E3{A0: x0, A1: x1, A2: x2}, +// B1: E3{A0: x3, A1: x4, A2: 0}, +// } +// +// and +// +// E6{ +// B0: E6{A0: 1, A1: 0, A2: 0}, +// B1: E6{A0: z3, A1: z4, A2: 0}, +// } +func (e *Ext6) Mul01234By034(x *[5]baseEl, z3, z4 *baseEl) *E6 { + c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} + c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} + a := e.Ext3.Add(e.Ext3.One(), &E3{A0: *z3, A1: *z4, A2: *e.fp.Zero()}) + b := e.Ext3.Add(c0, c1) + a = e.Ext3.Mul(a, b) + c := e.Ext3.Mul01By01(z3, z4, &x[3], &x[4]) + z1 := e.Ext3.Sub(a, c0) + z1 = e.Ext3.Sub(z1, c) + z0 := e.Ext3.MulByNonResidue(c) + z0 = e.Ext3.Add(z0, c0) + return &E6{ + B0: *z0, + B1: *z1, + } } diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing_test.go b/std/algebra/emulated/fields_bw6761/e6_pairing_test.go deleted file mode 100644 index b8d61bfa54..0000000000 --- a/std/algebra/emulated/fields_bw6761/e6_pairing_test.go +++ /dev/null @@ -1,222 +0,0 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - -package fields_bw6761 - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc" - bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fp" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/math/emulated" - "github.com/consensys/gnark/test" -) - -type e6Expt struct { - A, B E6 -} - -func (circuit *e6Expt) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) - expected := e.Expt(&circuit.A) - e.AssertIsEqual(expected, &circuit.B) - return nil -} - -func TestExptFp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var a, b bw6761.E6 - _, _ = a.SetRandom() - b.Set(&a) - b.Expt(&a) - - witness := e6Expt{ - A: FromE6(&a), - B: FromE6(&b), - } - - err := test.IsSolved(&e6Expt{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e6Expc2 struct { - A, B E6 -} - -func (circuit *e6Expc2) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) - expected := e.Expc2(&circuit.A) - e.AssertIsEqual(expected, &circuit.B) - return nil -} - -func TestExpc2Fp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var a, b bw6761.E6 - _, _ = a.SetRandom() - b.Set(&a) - b.Expc2(&a) - - witness := e6Expc2{ - A: FromE6(&a), - B: FromE6(&b), - } - - // add=287618 equals=4068 fromBinary=0 mul=281540 sub=3690 toBinary=0 - // add=197836 equals=3048 fromBinary=0 mul=188488 sub=3810 toBinary=0 - err := test.IsSolved(&e6Expc2{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e6Expc1 struct { - A, B E6 -} - -func (circuit *e6Expc1) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) - expected := e.Expc1(&circuit.A) - e.AssertIsEqual(expected, &circuit.B) - return nil -} - -func TestExpc1Fp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var a, b bw6761.E6 - _, _ = a.SetRandom() - b.Set(&a) - b.Expc1(&a) - - witness := e6Expc1{ - A: FromE6(&a), - B: FromE6(&b), - } - - // add=578954 equals=8028 fromBinary=0 mul=566870 sub=7248 toBinary=0 - err := test.IsSolved(&e6Expc1{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e6MulBy034 struct { - A, B E6 - R0, R1, R2 baseEl -} - -func (circuit *e6MulBy034) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) - var l LineEvaluation - l.R0 = circuit.R0 - l.R1 = circuit.R1 - l.R2 = circuit.R2 - expected := e.MulBy034(&circuit.A, &l) - e.AssertIsEqual(expected, &circuit.B) - return nil -} - -func TestMulBy034Fp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var a, b bw6761.E6 - _, _ = a.SetRandom() - b.Set(&a) - var c0, c0Copy, c3, c4 fp.Element - c0.SetRandom() - c3.SetRandom() - c4.SetRandom() - c0Copy.Set(&c0) - b.MulBy034(&c0, &c3, &c4) - - witness := e6MulBy034{ - A: FromE6(&a), - R0: emulated.ValueOf[emulated.BW6761Fp](c0Copy), - R1: emulated.ValueOf[emulated.BW6761Fp](c3), - R2: emulated.ValueOf[emulated.BW6761Fp](c4), - B: FromE6(&b), - } - - // add=54322 equals=823 fromBinary=0 mul=53414 sub=702 toBinary=0 - err := test.IsSolved(&e6MulBy034{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -/* -type e6Mul034By034 struct { - D0, D3, D4 baseEl - C0, C3, C4 baseEl - Res E6 -} - -func (circuit *e6Mul034By034) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) - expected := e.Mul034By034(&circuit.D0, &circuit.D3, &circuit.D4, &circuit.C0, &circuit.C3, &circuit.C4) - e.AssertIsEqual(expected, &circuit.Res) - return nil -} - -func TestMul034By034Fp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var a bw6761.E6 - var d0, d3, d4, c0, c3, c4 fp.Element - d0.SetRandom() - d3.SetRandom() - d4.SetRandom() - c0.SetRandom() - c3.SetRandom() - c4.SetRandom() - a = bw6761.Mul034By034(&d0, &d3, &d4, &c0, &c3, &c4) - - witness := e6Mul034By034{ - D0: emulated.ValueOf[emulated.BW6761Fp](d0), - D3: emulated.ValueOf[emulated.BW6761Fp](d3), - D4: emulated.ValueOf[emulated.BW6761Fp](d4), - C0: emulated.ValueOf[emulated.BW6761Fp](c0), - C3: emulated.ValueOf[emulated.BW6761Fp](c3), - C4: emulated.ValueOf[emulated.BW6761Fp](c4), - Res: FromE6(&a), - } - - // add=28733 equals=438 fromBinary=0 mul=28386 sub=401 toBinary=0 - err := test.IsSolved(&e6Mul034By034{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} -*/ diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index 1a142a559c..973f226588 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -413,3 +413,188 @@ func TestCreateLimbs(t *testing.T) { } fmt.Println(buf.String()) } + +type e6Expt struct { + A, B E6 +} + +func (circuit *e6Expt) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Expt(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestExptFp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.Expt(&a) + + witness := e6Expt{ + A: FromE6(&a), + B: FromE6(&b), + } + + err := test.IsSolved(&e6Expt{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Expc2 struct { + A, B E6 +} + +func (circuit *e6Expc2) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Expc2(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestExpc2Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.Expc2(&a) + + witness := e6Expc2{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=287618 equals=4068 fromBinary=0 mul=281540 sub=3690 toBinary=0 + // add=197836 equals=3048 fromBinary=0 mul=188488 sub=3810 toBinary=0 + err := test.IsSolved(&e6Expc2{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6Expc1 struct { + A, B E6 +} + +func (circuit *e6Expc1) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Expc1(&circuit.A) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestExpc1Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + b.Expc1(&a) + + witness := e6Expc1{ + A: FromE6(&a), + B: FromE6(&b), + } + + // add=578954 equals=8028 fromBinary=0 mul=566870 sub=7248 toBinary=0 + err := test.IsSolved(&e6Expc1{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type e6MulBy034 struct { + A, B E6 + c3, c4 baseEl +} + +func (circuit *e6MulBy034) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.MulBy034(&circuit.A, &circuit.c3, &circuit.c4) + e.AssertIsEqual(expected, &circuit.B) + return nil +} + +func TestMulBy034Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a, b bw6761.E6 + _, _ = a.SetRandom() + b.Set(&a) + var c0, c3, c4 fp.Element + c0.SetOne() + c3.SetRandom() + c4.SetRandom() + b.MulBy034(&c0, &c3, &c4) + + witness := e6MulBy034{ + A: FromE6(&a), + c3: emulated.ValueOf[emulated.BW6761Fp](c3), + c4: emulated.ValueOf[emulated.BW6761Fp](c4), + B: FromE6(&b), + } + + err := test.IsSolved(&e6MulBy034{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +/* +type e6Mul034By034 struct { + D0, D3, D4 baseEl + C0, C3, C4 baseEl + Res E6 +} + +func (circuit *e6Mul034By034) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + expected := e.Mul034By034(&circuit.D0, &circuit.D3, &circuit.D4, &circuit.C0, &circuit.C3, &circuit.C4) + e.AssertIsEqual(expected, &circuit.Res) + return nil +} + +func TestMul034By034Fp6(t *testing.T) { + assert := test.NewAssert(t) + // witness values + var a bw6761.E6 + var d0, d3, d4, c0, c3, c4 fp.Element + d0.SetRandom() + d3.SetRandom() + d4.SetRandom() + c0.SetRandom() + c3.SetRandom() + c4.SetRandom() + a = bw6761.Mul034By034(&d0, &d3, &d4, &c0, &c3, &c4) + + witness := e6Mul034By034{ + D0: emulated.ValueOf[emulated.BW6761Fp](d0), + D3: emulated.ValueOf[emulated.BW6761Fp](d3), + D4: emulated.ValueOf[emulated.BW6761Fp](d4), + C0: emulated.ValueOf[emulated.BW6761Fp](c0), + C3: emulated.ValueOf[emulated.BW6761Fp](c3), + C4: emulated.ValueOf[emulated.BW6761Fp](c4), + Res: FromE6(&a), + } + + // add=28733 equals=438 fromBinary=0 mul=28386 sub=401 toBinary=0 + err := test.IsSolved(&e6Mul034By034{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} +*/ diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/_pairing_test.go similarity index 100% rename from std/algebra/emulated/sw_bw6761/pairing_test.go rename to std/algebra/emulated/sw_bw6761/_pairing_test.go diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 8db5b8c742..ccfe5318c3 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -19,13 +19,17 @@ package sw_bw6761 import ( + "errors" "fmt" + "math/big" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/emulated/fields_bw6761" "github.com/consensys/gnark/std/math/emulated" ) +type curveF emulated.Field[emulated.BW6761Fp] + type Pairing struct { *fields_bw6761.Ext6 curveF emulated.Field[emulated.BW6761Fp] @@ -134,3 +138,234 @@ func (pr Pairing) FinalExponentiation(z *GT, _z ...*GT) *GT { return result } + +// lineEvaluation represents a sparse Fp12 Elmt (result of the line evaluation) +// line: 1 + R0(x/y) + R1(1/y) = 0 instead of R0'*y + R1'*x + R2' = 0 This +// makes the multiplication by lines (MulBy034) and between lines (Mul034By034) +// circuit-efficient. +type lineEvaluation struct { + R0, R1 emulated.Element[emulated.BW6761Fp] +} + +// MillerLoop computes the Miller loop +// Eq (4') in https://hackmd.io/@gnark/BW6-761-changes +// f_{u+1,Q}(P) * (f_{u+1})^q_{u^2-2u-1,[u+1]Q}(P) * l^q_{[(u+1)(u^2-2u+1)]Q,-Q}(P) +func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { + res := pr.Ext6.One() + var prodLines [5]emulated.Element[emulated.BW6761Fp] + + var l1, l2 *lineEvaluation + var yInv, xOverY *emulated.Element[emulated.BW6761Fp] + + Qacc := Q + QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} + // P and Q are supposed to be on G1 and G2 respectively of prime order r. + // The point (x,0) is of order 2. But this function does not check + // subgroup membership. + yInv = pr.curveF.Inverse(&P.Y) + xOverY = pr.curveF.MulMod(&P.X, yInv) + + for i := 62; i >= 0; i-- { + // mutualize the square among n Miller loops + // (∏ᵢfᵢ)² + res = pr.Square(res) + + switch loopCounter[i] { + + case 0: + // precompute lines + // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc + Qacc, l1 = pr.doubleStep(Qacc) + + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + res = pr.MulBy034(res, &l1.R0, &l1.R1) + + case 1: + for k := 0; k < n; k++ { + // Qacc[k] ← 2Qacc[k]+Q[k], + // l1 the line ℓ passing Qacc[k] and Q[k] + // l2 the line ℓ passing (Qacc[k]+Q[k]) and Qacc[k] + Qacc[k], l1, l2 = pr.doubleAndAddStep(Qacc[k], Q[k]) + + // line evaluation at P[k] + l1.R0 = *pr.MulByElement(&l1.R0, xOverY[k]) + l1.R1 = *pr.MulByElement(&l1.R1, yInv[k]) + + // line evaluation at P[k] + l2.R0 = *pr.MulByElement(&l2.R0, xOverY[k]) + l2.R1 = *pr.MulByElement(&l2.R1, yInv[k]) + + // ℓ × ℓ + prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) + // (ℓ × ℓ) × res + res = pr.MulBy01234(res, &prodLines) + + } + + case -1: + for k := 0; k < n; k++ { + // Qacc[k] ← 2Qacc[k]-Q[k], + // l1 the line ℓ passing Qacc[k] and -Q[k] + // l2 the line ℓ passing (Qacc[k]-Q[k]) and Qacc[k] + Qacc[k], l1, l2 = pr.doubleAndAddStep(Qacc[k], QNeg[k]) + + // line evaluation at P[k] + l1.R0 = *pr.MulByElement(&l1.R0, xOverY[k]) + l1.R1 = *pr.MulByElement(&l1.R1, yInv[k]) + + // line evaluation at P[k] + l2.R0 = *pr.MulByElement(&l2.R0, xOverY[k]) + l2.R1 = *pr.MulByElement(&l2.R1, yInv[k]) + + // ℓ × ℓ + prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) + // (ℓ × ℓ) × res + res = pr.MulBy01234(res, &prodLines) + + } + + default: + return nil, errors.New("invalid loopCounter") + } + } + + return res, nil +} + +// doubleAndAddStep doubles p1 and adds p2 to the result in affine coordinates, and evaluates the line in Miller loop +// https://eprint.iacr.org/2022/1162 (Section 6.1) +func (pr Pairing) doubleAndAddStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation, *lineEvaluation) { + + var line1, line2 lineEvaluation + var p G2Affine + + // compute λ1 = (y2-y1)/(x2-x1) + n := pr.Ext2.Sub(&p1.Y, &p2.Y) + d := pr.Ext2.Sub(&p1.X, &p2.X) + l1 := pr.Ext2.DivUnchecked(n, d) + + // compute x3 =λ1²-x1-x2 + x3 := pr.Ext2.Square(l1) + x3 = pr.Ext2.Sub(x3, &p1.X) + x3 = pr.Ext2.Sub(x3, &p2.X) + + // omit y3 computation + + // compute line1 + line1.R0 = *pr.Ext2.Neg(l1) + line1.R1 = *pr.Ext2.Mul(l1, &p1.X) + line1.R1 = *pr.Ext2.Sub(&line1.R1, &p1.Y) + + // compute λ2 = -λ1-2y1/(x3-x1) + n = pr.Ext2.Double(&p1.Y) + d = pr.Ext2.Sub(x3, &p1.X) + l2 := pr.Ext2.DivUnchecked(n, d) + l2 = pr.Ext2.Add(l2, l1) + l2 = pr.Ext2.Neg(l2) + + // compute x4 = λ2²-x1-x3 + x4 := pr.Ext2.Square(l2) + x4 = pr.Ext2.Sub(x4, &p1.X) + x4 = pr.Ext2.Sub(x4, x3) + + // compute y4 = λ2(x1 - x4)-y1 + y4 := pr.Ext2.Sub(&p1.X, x4) + y4 = pr.Ext2.Mul(l2, y4) + y4 = pr.Ext2.Sub(y4, &p1.Y) + + p.X = *x4 + p.Y = *y4 + + // compute line2 + line2.R0 = *pr.Ext2.Neg(l2) + line2.R1 = *pr.Ext2.Mul(l2, &p1.X) + line2.R1 = *pr.Ext2.Sub(&line2.R1, &p1.Y) + + return &p, &line1, &line2 +} + +// doubleStep doubles a point in affine coordinates, and evaluates the line in Miller loop +// https://eprint.iacr.org/2022/1162 (Section 6.1) +func (pr Pairing) doubleStep(p1 *G2Affine) (*G2Affine, *lineEvaluation) { + + var p G2Affine + var line lineEvaluation + + // λ = 3x²/2y + n := pr.Ext2.Square(&p1.X) + three := big.NewInt(3) + n = pr.Ext2.MulByConstElement(n, three) + d := pr.Ext2.Double(&p1.Y) + λ := pr.Ext2.DivUnchecked(n, d) + + // xr = λ²-2x + xr := pr.Ext2.Square(λ) + xr = pr.Ext2.Sub(xr, &p1.X) + xr = pr.Ext2.Sub(xr, &p1.X) + + // yr = λ(x-xr)-y + yr := pr.Ext2.Sub(&p1.X, xr) + yr = pr.Ext2.Mul(λ, yr) + yr = pr.Ext2.Sub(yr, &p1.Y) + + p.X = *xr + p.Y = *yr + + line.R0 = *pr.Ext2.Neg(λ) + line.R1 = *pr.Ext2.Mul(λ, &p1.X) + line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + + return &p, &line + +} + +// addStep adds two points in affine coordinates, and evaluates the line in Miller loop +// https://eprint.iacr.org/2022/1162 (Section 6.1) +func (pr Pairing) addStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation) { + + // 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 + λλ := pr.Ext2.Square(λ) + p2xpx = pr.Ext2.Add(&p1.X, &p2.X) + xr := pr.Ext2.Sub(λλ, p2xpx) + + // yr = λ(x1-xr) - y1 + pxrx := pr.Ext2.Sub(&p1.X, xr) + λpxrx := pr.Ext2.Mul(λ, pxrx) + yr := pr.Ext2.Sub(λpxrx, &p1.Y) + + var res G2Affine + res.X = *xr + res.Y = *yr + + var line lineEvaluation + line.R0 = *pr.Ext2.Neg(λ) + line.R1 = *pr.Ext2.Mul(λ, &p1.X) + line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + + return &res, &line + +} + +// lineCompute computes the line that goes through p1 and p2 but does not compute p1+p2 +func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { + + // compute λ = (y2-y1)/(x2-x1) + qypy := pr.Ext2.Sub(&p2.Y, &p1.Y) + qxpx := pr.Ext2.Sub(&p2.X, &p1.X) + λ := pr.Ext2.DivUnchecked(qypy, qxpx) + + var line lineEvaluation + line.R0 = *pr.Ext2.Neg(λ) + line.R1 = *pr.Ext2.Mul(λ, &p1.X) + line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + + return &line + +} From 9da80b937df3d7f479f644d6875b34718103299b Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 10 Jul 2023 15:33:56 +0100 Subject: [PATCH 05/38] test(emulated/bw6): sparse mul in Fp6 --- std/algebra/emulated/fields_bw6761/e6_test.go | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index 973f226588..388659ce0d 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -515,7 +515,7 @@ func TestExpc1Fp6(t *testing.T) { type e6MulBy034 struct { A, B E6 - c3, c4 baseEl + C3, C4 baseEl } func (circuit *e6MulBy034) Define(api frontend.API) error { @@ -524,7 +524,7 @@ func (circuit *e6MulBy034) Define(api frontend.API) error { panic(err) } e := NewExt6(nfield) - expected := e.MulBy034(&circuit.A, &circuit.c3, &circuit.c4) + expected := e.MulBy034(&circuit.A, &circuit.C3, &circuit.C4) e.AssertIsEqual(expected, &circuit.B) return nil } @@ -543,8 +543,8 @@ func TestMulBy034Fp6(t *testing.T) { witness := e6MulBy034{ A: FromE6(&a), - c3: emulated.ValueOf[emulated.BW6761Fp](c3), - c4: emulated.ValueOf[emulated.BW6761Fp](c4), + C3: emulated.ValueOf[emulated.BW6761Fp](c3), + C4: emulated.ValueOf[emulated.BW6761Fp](c4), B: FromE6(&b), } @@ -552,11 +552,10 @@ func TestMulBy034Fp6(t *testing.T) { assert.NoError(err) } -/* type e6Mul034By034 struct { - D0, D3, D4 baseEl - C0, C3, C4 baseEl - Res E6 + D3, D4 baseEl + C3, C4 baseEl + Res E6 } func (circuit *e6Mul034By034) Define(api frontend.API) error { @@ -565,36 +564,39 @@ func (circuit *e6Mul034By034) Define(api frontend.API) error { panic(err) } e := NewExt6(nfield) - expected := e.Mul034By034(&circuit.D0, &circuit.D3, &circuit.D4, &circuit.C0, &circuit.C3, &circuit.C4) - e.AssertIsEqual(expected, &circuit.Res) + expected := e.Mul034By034(&circuit.D3, &circuit.D4, &circuit.C3, &circuit.C4) + e.AssertIsEqual(&E6{ + B0: E3{A0: expected[0], A1: expected[1], A2: expected[2]}, + B1: E3{A0: expected[3], A1: expected[4], A2: emulated.ValueOf[emulated.BW6761Fp](0)}, + }, &circuit.Res) return nil } func TestMul034By034Fp6(t *testing.T) { assert := test.NewAssert(t) // witness values - var a bw6761.E6 - var d0, d3, d4, c0, c3, c4 fp.Element - d0.SetRandom() + var d0, d3, d4, c0, c3, c4, zero fp.Element + zero.SetZero() + d0.SetOne() d3.SetRandom() d4.SetRandom() - c0.SetRandom() + c0.SetOne() c3.SetRandom() c4.SetRandom() - a = bw6761.Mul034By034(&d0, &d3, &d4, &c0, &c3, &c4) + a := bw6761.E6{ + B0: bw6761.E3{A0: d0, A1: zero, A2: zero}, + B1: bw6761.E3{A0: d3, A1: d4, A2: zero}, + } + a.MulBy034(&c0, &c3, &c4) witness := e6Mul034By034{ - D0: emulated.ValueOf[emulated.BW6761Fp](d0), D3: emulated.ValueOf[emulated.BW6761Fp](d3), D4: emulated.ValueOf[emulated.BW6761Fp](d4), - C0: emulated.ValueOf[emulated.BW6761Fp](c0), C3: emulated.ValueOf[emulated.BW6761Fp](c3), C4: emulated.ValueOf[emulated.BW6761Fp](c4), Res: FromE6(&a), } - // add=28733 equals=438 fromBinary=0 mul=28386 sub=401 toBinary=0 err := test.IsSolved(&e6Mul034By034{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -*/ From a27a7fa2fe4256ed9bc27e5eb2c43f62b12ded4c Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 10 Jul 2023 17:06:22 +0100 Subject: [PATCH 06/38] feat: add naive bw6 miller loop --- std/algebra/emulated/fields_bw6761/doc.go | 6 + std/algebra/emulated/fields_bw6761/e3.go | 3 +- std/algebra/emulated/sw_bw6761/pairing.go | 315 +++++++++++++----- .../{_pairing_test.go => pairing_test.go} | 52 +++ 4 files changed, 282 insertions(+), 94 deletions(-) create mode 100644 std/algebra/emulated/fields_bw6761/doc.go rename std/algebra/emulated/sw_bw6761/{_pairing_test.go => pairing_test.go} (65%) diff --git a/std/algebra/emulated/fields_bw6761/doc.go b/std/algebra/emulated/fields_bw6761/doc.go new file mode 100644 index 0000000000..29858f7424 --- /dev/null +++ b/std/algebra/emulated/fields_bw6761/doc.go @@ -0,0 +1,6 @@ +// Package fields_bw6761 implements the fields arithmetic of the Fp6 tower +// used to compute the pairing over the BW6-761 curve. +// +// 𝔽p³[u] = 𝔽p/u³+4 +// 𝔽p⁶[v] = 𝔽p²/v²-u +package fields_bw6761 diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index 3cabb0b0f9..67120e58e0 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -123,8 +123,7 @@ func (e Ext3) Double(x *E3) *E3 { func MulByNonResidue(fp *curveF, x *baseEl) *baseEl { z := fp.Neg(x) - z = fp.Add(z, z) - z = fp.Add(z, z) + z = fp.MulConst(z, big.NewInt(4)) return z } diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index ccfe5318c3..9fc0617c52 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -23,6 +23,7 @@ import ( "fmt" "math/big" + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/emulated/fields_bw6761" "github.com/consensys/gnark/std/math/emulated" @@ -48,6 +49,32 @@ func NewPairing(api frontend.API) (*Pairing, error) { // GT target group of the pairing type GT = fields_bw6761.E6 +func NewGT(v bw6761.GT) GT { + return GT{ + B0: fields_bw6761.E3{ + A0: emulated.ValueOf[emulated.BW6761Fp](v.B0.A0), + A1: emulated.ValueOf[emulated.BW6761Fp](v.B0.A1), + A2: emulated.ValueOf[emulated.BW6761Fp](v.B0.A2), + }, + B1: fields_bw6761.E3{ + A0: emulated.ValueOf[emulated.BW6761Fp](v.B1.A0), + A1: emulated.ValueOf[emulated.BW6761Fp](v.B1.A1), + A2: emulated.ValueOf[emulated.BW6761Fp](v.B1.A2), + }, + } +} + +// Pair calculates the reduced pairing for a set of points e(P, Q). +// +// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. +func (pr Pairing) Pair(P *G1Affine, Q *G2Affine) (*GT, error) { + f, err := pr.MillerLoop(P, Q) + if err != nil { + return nil, err + } + return pr.FinalExponentiation(f), nil +} + // FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ // where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r // we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r @@ -147,10 +174,28 @@ type lineEvaluation struct { R0, R1 emulated.Element[emulated.BW6761Fp] } -// MillerLoop computes the Miller loop -// Eq (4') in https://hackmd.io/@gnark/BW6-761-changes -// f_{u+1,Q}(P) * (f_{u+1})^q_{u^2-2u-1,[u+1]Q}(P) * l^q_{[(u+1)(u^2-2u+1)]Q,-Q}(P) -func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { +// seed x₀=9586122913090633729 +// +// x₀+1 in 2-NAF +var loopCounter1 = [64]int8{ + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, +} + +// x₀^3-x₀^2+1 in 2-NAF +var loopCounter2 = [190]int8{ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, + 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, + 1, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 1, 0, + 1, 0, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, +} + +func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res := pr.Ext6.One() var prodLines [5]emulated.Element[emulated.BW6761Fp] @@ -170,7 +215,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // (∏ᵢfᵢ)² res = pr.Square(res) - switch loopCounter[i] { + switch loopCounter1[i] { case 0: // precompute lines @@ -183,48 +228,121 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res = pr.MulBy034(res, &l1.R0, &l1.R1) case 1: - for k := 0; k < n; k++ { - // Qacc[k] ← 2Qacc[k]+Q[k], - // l1 the line ℓ passing Qacc[k] and Q[k] - // l2 the line ℓ passing (Qacc[k]+Q[k]) and Qacc[k] - Qacc[k], l1, l2 = pr.doubleAndAddStep(Qacc[k], Q[k]) - - // line evaluation at P[k] - l1.R0 = *pr.MulByElement(&l1.R0, xOverY[k]) - l1.R1 = *pr.MulByElement(&l1.R1, yInv[k]) + // Qacc ← 2Qacc+Q, + // l1 the line ℓ passing Qacc and Q + // l2 the line ℓ passing (Qacc+Q) and Qacc + Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, Q) - // line evaluation at P[k] - l2.R0 = *pr.MulByElement(&l2.R0, xOverY[k]) - l2.R1 = *pr.MulByElement(&l2.R1, yInv[k]) + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - // ℓ × ℓ - prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, &prodLines) + // line evaluation at P + l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) + l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - } + // ℓ × ℓ + prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) + // (ℓ × ℓ) × res + res = pr.MulBy01234(res, &prodLines) case -1: - for k := 0; k < n; k++ { - // Qacc[k] ← 2Qacc[k]-Q[k], - // l1 the line ℓ passing Qacc[k] and -Q[k] - // l2 the line ℓ passing (Qacc[k]-Q[k]) and Qacc[k] - Qacc[k], l1, l2 = pr.doubleAndAddStep(Qacc[k], QNeg[k]) + // Qacc ← 2Qacc-Q, + // l1 the line ℓ passing Qacc and -Q + // l2 the line ℓ passing (Qacc-Q) and Qacc + Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, QNeg) + + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + + // line evaluation at P + l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) + l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) + + // ℓ × ℓ + prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) + // (ℓ × ℓ) × res + res = pr.MulBy01234(res, &prodLines) + + default: + return nil, errors.New("invalid loopCounter") + } + } + + return res, nil + +} + +func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { + res := pr.Ext6.One() + var prodLines [5]emulated.Element[emulated.BW6761Fp] + + var l1, l2 *lineEvaluation + var yInv, xOverY *emulated.Element[emulated.BW6761Fp] + + Qacc := Q + QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} + // P and Q are supposed to be on G1 and G2 respectively of prime order r. + // The point (x,0) is of order 2. But this function does not check + // subgroup membership. + yInv = pr.curveF.Inverse(&P.Y) + xOverY = pr.curveF.MulMod(&P.X, yInv) - // line evaluation at P[k] - l1.R0 = *pr.MulByElement(&l1.R0, xOverY[k]) - l1.R1 = *pr.MulByElement(&l1.R1, yInv[k]) + for i := 188; i >= 0; i-- { + // mutualize the square among n Miller loops + // (∏ᵢfᵢ)² + res = pr.Square(res) - // line evaluation at P[k] - l2.R0 = *pr.MulByElement(&l2.R0, xOverY[k]) - l2.R1 = *pr.MulByElement(&l2.R1, yInv[k]) + switch loopCounter2[i] { - // ℓ × ℓ - prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, &prodLines) + case 0: + // precompute lines + // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc + Qacc, l1 = pr.doubleStep(Qacc) + + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + res = pr.MulBy034(res, &l1.R0, &l1.R1) - } + case 1: + // Qacc ← 2Qacc+Q, + // l1 the line ℓ passing Qacc and Q + // l2 the line ℓ passing (Qacc+Q) and Qacc + Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, Q) + + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + + // line evaluation at P + l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) + l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) + + // ℓ × ℓ + prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) + // (ℓ × ℓ) × res + res = pr.MulBy01234(res, &prodLines) + + case -1: + // Qacc ← 2Qacc-Q, + // l1 the line ℓ passing Qacc and -Q + // l2 the line ℓ passing (Qacc-Q) and Qacc + Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, QNeg) + + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + + // line evaluation at P + l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) + l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) + + // ℓ × ℓ + prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) + // (ℓ × ℓ) × res + res = pr.MulBy01234(res, &prodLines) default: return nil, errors.New("invalid loopCounter") @@ -232,6 +350,19 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } return res, nil + +} + +// MillerLoop computes the Miller loop +// Naive BW6 Miller loop: Eq (3) in [bw6-post] +// +// [https://hackmd.io/@gnark/BW6-761-changes] +func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { + ml1, _ := pr.firstLoop(P, Q) + ml2, _ := pr.secondLoop(P, Q) + ml := pr.Frobenius(ml1) + ml = pr.Mul(ml, ml2) + return ml, nil } // doubleAndAddStep doubles p1 and adds p2 to the result in affine coordinates, and evaluates the line in Miller loop @@ -242,46 +373,46 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation var p G2Affine // compute λ1 = (y2-y1)/(x2-x1) - n := pr.Ext2.Sub(&p1.Y, &p2.Y) - d := pr.Ext2.Sub(&p1.X, &p2.X) - l1 := pr.Ext2.DivUnchecked(n, d) + n := pr.curveF.Sub(&p1.Y, &p2.Y) + d := pr.curveF.Sub(&p1.X, &p2.X) + l1 := pr.curveF.Div(n, d) // compute x3 =λ1²-x1-x2 - x3 := pr.Ext2.Square(l1) - x3 = pr.Ext2.Sub(x3, &p1.X) - x3 = pr.Ext2.Sub(x3, &p2.X) + x3 := pr.curveF.Mul(l1, l1) + x3 = pr.curveF.Sub(x3, &p1.X) + x3 = pr.curveF.Sub(x3, &p2.X) // omit y3 computation // compute line1 - line1.R0 = *pr.Ext2.Neg(l1) - line1.R1 = *pr.Ext2.Mul(l1, &p1.X) - line1.R1 = *pr.Ext2.Sub(&line1.R1, &p1.Y) + line1.R0 = *pr.curveF.Neg(l1) + line1.R1 = *pr.curveF.Mul(l1, &p1.X) + line1.R1 = *pr.curveF.Sub(&line1.R1, &p1.Y) // compute λ2 = -λ1-2y1/(x3-x1) - n = pr.Ext2.Double(&p1.Y) - d = pr.Ext2.Sub(x3, &p1.X) - l2 := pr.Ext2.DivUnchecked(n, d) - l2 = pr.Ext2.Add(l2, l1) - l2 = pr.Ext2.Neg(l2) + n = pr.curveF.Add(&p1.Y, &p1.Y) + d = pr.curveF.Sub(x3, &p1.X) + l2 := pr.curveF.Div(n, d) + l2 = pr.curveF.Add(l2, l1) + l2 = pr.curveF.Neg(l2) // compute x4 = λ2²-x1-x3 - x4 := pr.Ext2.Square(l2) - x4 = pr.Ext2.Sub(x4, &p1.X) - x4 = pr.Ext2.Sub(x4, x3) + x4 := pr.curveF.Mul(l2, l2) + x4 = pr.curveF.Sub(x4, &p1.X) + x4 = pr.curveF.Sub(x4, x3) // compute y4 = λ2(x1 - x4)-y1 - y4 := pr.Ext2.Sub(&p1.X, x4) - y4 = pr.Ext2.Mul(l2, y4) - y4 = pr.Ext2.Sub(y4, &p1.Y) + y4 := pr.curveF.Sub(&p1.X, x4) + y4 = pr.curveF.Mul(l2, y4) + y4 = pr.curveF.Sub(y4, &p1.Y) p.X = *x4 p.Y = *y4 // compute line2 - line2.R0 = *pr.Ext2.Neg(l2) - line2.R1 = *pr.Ext2.Mul(l2, &p1.X) - line2.R1 = *pr.Ext2.Sub(&line2.R1, &p1.Y) + line2.R0 = *pr.curveF.Neg(l2) + line2.R1 = *pr.curveF.Mul(l2, &p1.X) + line2.R1 = *pr.curveF.Sub(&line2.R1, &p1.Y) return &p, &line1, &line2 } @@ -294,28 +425,28 @@ func (pr Pairing) doubleStep(p1 *G2Affine) (*G2Affine, *lineEvaluation) { var line lineEvaluation // λ = 3x²/2y - n := pr.Ext2.Square(&p1.X) + n := pr.curveF.Mul(&p1.X, &p1.X) three := big.NewInt(3) - n = pr.Ext2.MulByConstElement(n, three) - d := pr.Ext2.Double(&p1.Y) - λ := pr.Ext2.DivUnchecked(n, d) + n = pr.curveF.MulConst(n, three) + d := pr.curveF.Add(&p1.Y, &p1.Y) + λ := pr.curveF.Div(n, d) // xr = λ²-2x - xr := pr.Ext2.Square(λ) - xr = pr.Ext2.Sub(xr, &p1.X) - xr = pr.Ext2.Sub(xr, &p1.X) + xr := pr.curveF.Mul(λ, λ) + xr = pr.curveF.Sub(xr, &p1.X) + xr = pr.curveF.Sub(xr, &p1.X) // yr = λ(x-xr)-y - yr := pr.Ext2.Sub(&p1.X, xr) - yr = pr.Ext2.Mul(λ, yr) - yr = pr.Ext2.Sub(yr, &p1.Y) + yr := pr.curveF.Sub(&p1.X, xr) + yr = pr.curveF.Mul(λ, yr) + yr = pr.curveF.Sub(yr, &p1.Y) p.X = *xr p.Y = *yr - line.R0 = *pr.Ext2.Neg(λ) - line.R1 = *pr.Ext2.Mul(λ, &p1.X) - line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + line.R0 = *pr.curveF.Neg(λ) + line.R1 = *pr.curveF.Mul(λ, &p1.X) + line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) return &p, &line @@ -326,28 +457,28 @@ func (pr Pairing) doubleStep(p1 *G2Affine) (*G2Affine, *lineEvaluation) { func (pr Pairing) addStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation) { // 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) + p2ypy := pr.curveF.Sub(&p2.Y, &p1.Y) + p2xpx := pr.curveF.Sub(&p2.X, &p1.X) + λ := pr.curveF.Div(p2ypy, p2xpx) // xr = λ²-x1-x2 - λλ := pr.Ext2.Square(λ) - p2xpx = pr.Ext2.Add(&p1.X, &p2.X) - xr := pr.Ext2.Sub(λλ, p2xpx) + λλ := pr.curveF.Mul(λ, λ) + p2xpx = pr.curveF.Add(&p1.X, &p2.X) + xr := pr.curveF.Sub(λλ, p2xpx) // yr = λ(x1-xr) - y1 - pxrx := pr.Ext2.Sub(&p1.X, xr) - λpxrx := pr.Ext2.Mul(λ, pxrx) - yr := pr.Ext2.Sub(λpxrx, &p1.Y) + pxrx := pr.curveF.Sub(&p1.X, xr) + λpxrx := pr.curveF.Mul(λ, pxrx) + yr := pr.curveF.Sub(λpxrx, &p1.Y) var res G2Affine res.X = *xr res.Y = *yr var line lineEvaluation - line.R0 = *pr.Ext2.Neg(λ) - line.R1 = *pr.Ext2.Mul(λ, &p1.X) - line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + line.R0 = *pr.curveF.Neg(λ) + line.R1 = *pr.curveF.Mul(λ, &p1.X) + line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) return &res, &line @@ -357,14 +488,14 @@ func (pr Pairing) addStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation) { func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { // compute λ = (y2-y1)/(x2-x1) - qypy := pr.Ext2.Sub(&p2.Y, &p1.Y) - qxpx := pr.Ext2.Sub(&p2.X, &p1.X) - λ := pr.Ext2.DivUnchecked(qypy, qxpx) + qypy := pr.curveF.Sub(&p2.Y, &p1.Y) + qxpx := pr.curveF.Sub(&p2.X, &p1.X) + λ := pr.curveF.Div(qypy, qxpx) var line lineEvaluation - line.R0 = *pr.Ext2.Neg(λ) - line.R1 = *pr.Ext2.Mul(λ, &p1.X) - line.R1 = *pr.Ext2.Sub(&line.R1, &p1.Y) + line.R0 = *pr.curveF.Neg(λ) + line.R1 = *pr.curveF.Mul(λ, &p1.X) + line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) return &line diff --git a/std/algebra/emulated/sw_bw6761/_pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go similarity index 65% rename from std/algebra/emulated/sw_bw6761/_pairing_test.go rename to std/algebra/emulated/sw_bw6761/pairing_test.go index eed0d78c09..d33f5c86c9 100644 --- a/std/algebra/emulated/sw_bw6761/_pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -20,6 +20,7 @@ package sw_bw6761 import ( "crypto/rand" + "fmt" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -33,6 +34,24 @@ import ( const testCurve = ecc.BN254 +func randomG1G2Affines() (bw6761.G1Affine, bw6761.G2Affine) { + _, _, G1AffGen, G2AffGen := bw6761.Generators() + mod := bw6761.ID.ScalarField() + s1, err := rand.Int(rand.Reader, mod) + if err != nil { + panic(err) + } + s2, err := rand.Int(rand.Reader, mod) + if err != nil { + panic(err) + } + var p bw6761.G1Affine + p.ScalarMultiplication(&G1AffGen, s1) + var q bw6761.G2Affine + q.ScalarMultiplication(&G2AffGen, s2) + return p, q +} + type finalExponentiationBW6761 struct { A GT B GT @@ -85,3 +104,36 @@ func TestFinalExponentiationBW6761(t *testing.T) { _, err = frontend.Compile(testCurve.ScalarField(), r1cs.NewBuilder, &finalExponentiationBW6761{}, frontend.IgnoreUnconstrainedInputs()) assert.NoError(err) } + +type PairCircuit struct { + InG1 G1Affine + InG2 G2Affine + Res GT +} + +func (c *PairCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + res, err := pairing.Pair(&c.InG1, &c.InG2) + if err != nil { + return fmt.Errorf("pair: %w", err) + } + pairing.AssertIsEqual(res, &c.Res) + return nil +} + +func TestPairTestSolve(t *testing.T) { + assert := test.NewAssert(t) + p, q := randomG1G2Affines() + res, err := bw6761.Pair([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) + assert.NoError(err) + witness := PairCircuit{ + InG1: NewG1Affine(p), + InG2: NewG2Affine(q), + Res: NewGT(res), + } + err = test.IsSolved(&PairCircuit{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} From 547d5f88bc4ac38bceef0af95fc3b8ef134344b0 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 09:40:43 -0400 Subject: [PATCH 07/38] refactor: clean comments --- std/algebra/emulated/fields_bw6761/e3.go | 18 --------------- std/algebra/emulated/fields_bw6761/e3_test.go | 18 --------------- std/algebra/emulated/fields_bw6761/e6.go | 18 --------------- .../emulated/fields_bw6761/e6_pairing.go | 18 --------------- std/algebra/emulated/fields_bw6761/e6_test.go | 23 ++----------------- .../emulated/fields_bw6761/frobenius.go | 18 --------------- std/algebra/emulated/sw_bw6761/g1.go | 18 --------------- std/algebra/emulated/sw_bw6761/g2.go | 18 --------------- std/algebra/emulated/sw_bw6761/pairing.go | 18 --------------- .../emulated/sw_bw6761/pairing_test.go | 18 --------------- 10 files changed, 2 insertions(+), 183 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index 67120e58e0..4a0436386f 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package fields_bw6761 import ( diff --git a/std/algebra/emulated/fields_bw6761/e3_test.go b/std/algebra/emulated/fields_bw6761/e3_test.go index a43c9e6249..6a366c1b51 100644 --- a/std/algebra/emulated/fields_bw6761/e3_test.go +++ b/std/algebra/emulated/fields_bw6761/e3_test.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package fields_bw6761 import ( diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 465a4a9547..98e2cd2e44 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package fields_bw6761 import ( diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 7d0e904bff..9b34fb6022 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package fields_bw6761 type LineEvaluation struct { diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index 388659ce0d..1614178075 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -1,27 +1,6 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package fields_bw6761 import ( - "bytes" - "fmt" - "math/big" "testing" "github.com/consensys/gnark-crypto/ecc" @@ -371,6 +350,7 @@ func TestCyclotomicSquareFp6(t *testing.T) { assert.NoError(err) } +/* func TestNewE6(t *testing.T) { a, _ := new(big.Int).SetString("83175370704474795125412693555818269399912070346366058924020987848926901443521059146219467322598189008118890021654143123310841437365188932207798122475953021372633091598654279100089387195482601214045864119525747542050698192923485116081505909964897146420", 10) b, _ := new(big.Int).SetString("6368022403585149186567793239866157016295592880888573809019876686976707722559034074218497709896494419772477540172749411175273320318562448286368763367020957539305330983642372720448125982288656809793421178887827471755589212191192898758939906986677524020", 10) @@ -413,6 +393,7 @@ func TestCreateLimbs(t *testing.T) { } fmt.Println(buf.String()) } +*/ type e6Expt struct { A, B E6 diff --git a/std/algebra/emulated/fields_bw6761/frobenius.go b/std/algebra/emulated/fields_bw6761/frobenius.go index 5ab65ddbb1..7c94be72c8 100644 --- a/std/algebra/emulated/fields_bw6761/frobenius.go +++ b/std/algebra/emulated/fields_bw6761/frobenius.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package fields_bw6761 import ( diff --git a/std/algebra/emulated/sw_bw6761/g1.go b/std/algebra/emulated/sw_bw6761/g1.go index 143bdb97c7..0fe5ff2f40 100644 --- a/std/algebra/emulated/sw_bw6761/g1.go +++ b/std/algebra/emulated/sw_bw6761/g1.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package sw_bw6761 import ( diff --git a/std/algebra/emulated/sw_bw6761/g2.go b/std/algebra/emulated/sw_bw6761/g2.go index 175ac578bc..8de701405a 100644 --- a/std/algebra/emulated/sw_bw6761/g2.go +++ b/std/algebra/emulated/sw_bw6761/g2.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package sw_bw6761 import ( diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 9fc0617c52..7912b50cf0 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package sw_bw6761 import ( diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index d33f5c86c9..1771f3dc02 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -1,21 +1,3 @@ -/* - * - * Copyright © 2020 ConsenSys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * / - */ - package sw_bw6761 import ( From 81499cb4fc679cb5290557af53826b6d6204917e Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 11:12:31 -0400 Subject: [PATCH 08/38] fix: use M-twist (014) for emulated BW6 pairing --- .../emulated/fields_bw6761/e6_pairing.go | 149 ++++++++---------- std/algebra/emulated/fields_bw6761/e6_test.go | 92 +++-------- std/algebra/emulated/sw_bls12381/pairing.go | 2 +- std/algebra/emulated/sw_bw6761/pairing.go | 61 +++---- 4 files changed, 115 insertions(+), 189 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 9b34fb6022..f58c53ee44 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -86,24 +86,30 @@ func (e Ext6) Expc1(x *E6) *E6 { return e.Set(result) } -// MulBy034 multiplies z by an E6 sparse element of the form +// MulBy014 multiplies z by an E6 sparse element of the form // // E6{ -// B0: E3{A0: 1, A1: 0, A2: 0}, -// B1: E3{A0: c3, A1: c4, A2: 0}, +// C0: E3{A0: c0, A1: c1, A2: 0}, +// C1: E3{A0: 0, A1: 1, A2: 0}, // } -func (e *Ext6) MulBy034(z *E6, c3, c4 *baseEl) *E6 { +func (e *Ext6) MulBy014(z *E6, c0, c1 *baseEl) *E6 { a := z.B0 - b := z.B1 - b = *e.Ext3.MulBy01(&b, c3, c4) - c3 = e.fp.Add(e.fp.One(), c3) - d := e.Ext3.Add(&z.B0, &z.B1) - d = e.Ext3.MulBy01(d, c3, c4) - - zC1 := e.Ext3.Add(&a, &b) - zC1 = e.Ext3.Neg(zC1) - zC1 = e.Ext3.Add(zC1, d) + a = *e.MulBy01(&a, c0, c1) + + var b E3 + // Mul by E3{0, 1, 0} + b.A0 = *MulByNonResidue(e.fp, &z.B1.A2) + b.A2 = z.B1.A1 + b.A1 = z.B1.A0 + + one := e.fp.One() + d := e.fp.Add(c1, one) + + zC1 := e.Ext3.Add(&z.B1, &z.B0) + zC1 = e.Ext3.MulBy01(zC1, c0, d) + zC1 = e.Ext3.Sub(zC1, &a) + zC1 = e.Ext3.Sub(zC1, &b) zC0 := e.Ext3.MulByNonResidue(&b) zC0 = e.Ext3.Add(zC0, &a) @@ -113,90 +119,67 @@ func (e *Ext6) MulBy034(z *E6, c3, c4 *baseEl) *E6 { } } +/* // multiplies two E6 sparse element of the form: // // E6{ -// B0: E3{A0: 1, A1: 0, A2: 0}, -// B1: E3{A0: c3, A1: c4, A2: 0}, +// C0: E6{B0: c0, B1: c1, B2: 0}, +// C1: E6{B0: 0, B1: 1, B2: 0}, // } // // and // // E6{ -// B0: E3{A0: 1, A1: 0, A2: 0}, -// B1: E3{A0: d3, A1: d4, A2: 0}, -// } -func (e *Ext6) Mul034By034(d3, d4, c3, c4 *baseEl) *[5]baseEl { - x3 := e.fp.MulMod(c3, d3) - x4 := e.fp.MulMod(c4, d4) - x04 := e.fp.Add(c4, d4) - x03 := e.fp.Add(c3, d3) - tmp := e.fp.Add(c3, c4) - x34 := e.fp.Add(d3, d4) - x34 = e.fp.MulMod(x34, tmp) - x34 = e.fp.Sub(x34, x3) - x34 = e.fp.Sub(x34, x4) - - zC0B0 := MulByNonResidue(e.fp, x4) - zC0B0 = e.fp.Add(zC0B0, e.fp.One()) - zC0B1 := x3 - zC0B2 := x34 - zC1B0 := x03 - zC1B1 := x04 - - return &[5]baseEl{*zC0B0, *zC0B1, *zC0B2, *zC1B0, *zC1B1} -} - -// MulBy01234 multiplies z by an E6 sparse element of the form -// -// E6{ -// B0: E3{A0: c0, A1: c1, A2: c2}, -// B1: E3{A0: c3, A1: c4, A2: 0}, +// C0: E6{B0: d0, B1: d1, B2: 0}, +// C1: E6{B0: 0, B1: 1, B2: 0}, // } -func (e *Ext6) MulBy01234(z *E6, x *[5]baseEl) *E6 { - c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} - a := e.Ext3.Add(&z.B0, &z.B1) - b := e.Ext3.Add(c0, c1) - a = e.Ext3.Mul(a, b) - b = e.Ext3.Mul(&z.B0, c0) - c := e.Ext3.MulBy01(&z.B1, &x[3], &x[4]) - z1 := e.Ext3.Sub(a, b) - z1 = e.Ext3.Sub(z1, c) - z0 := e.Ext3.MulByNonResidue(c) - z0 = e.Ext3.Add(z0, b) - return &E6{ - B0: *z0, - B1: *z1, - } +func (e Ext6) Mul014By014(d0, d1, c0, c1 *E2) *[5]E2 { + one := e.Ext2.One() + x0 := e.Ext2.Mul(c0, d0) + x1 := e.Ext2.Mul(c1, d1) + tmp := e.Ext2.Add(c0, one) + x04 := e.Ext2.Add(d0, one) + x04 = e.Ext2.Mul(x04, tmp) + x04 = e.Ext2.Sub(x04, x0) + x04 = e.Ext2.Sub(x04, one) + tmp = e.Ext2.Add(c0, c1) + x01 := e.Ext2.Add(d0, d1) + x01 = e.Ext2.Mul(x01, tmp) + x01 = e.Ext2.Sub(x01, x0) + x01 = e.Ext2.Sub(x01, x1) + tmp = e.Ext2.Add(c1, one) + x14 := e.Ext2.Add(d1, one) + x14 = e.Ext2.Mul(x14, tmp) + x14 = e.Ext2.Sub(x14, x1) + x14 = e.Ext2.Sub(x14, one) + + zC0B0 := e.Ext2.NonResidue() + zC0B0 = e.Ext2.Add(zC0B0, x0) + + return &[5]E2{*zC0B0, *x01, *x1, *x04, *x14} } -// multiplies two E6 sparse element of the form: -// -// E6{ -// B0: E3{A0: x0, A1: x1, A2: x2}, -// B1: E3{A0: x3, A1: x4, A2: 0}, -// } -// -// and +// MulBy01245 multiplies z by an E6 sparse element of the form // // E6{ -// B0: E6{A0: 1, A1: 0, A2: 0}, -// B1: E6{A0: z3, A1: z4, A2: 0}, +// C0: E6{B0: c0, B1: c1, B2: c2}, +// C1: E6{B0: 0, B1: c4, B2: c5}, // } -func (e *Ext6) Mul01234By034(x *[5]baseEl, z3, z4 *baseEl) *E6 { - c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} - a := e.Ext3.Add(e.Ext3.One(), &E3{A0: *z3, A1: *z4, A2: *e.fp.Zero()}) - b := e.Ext3.Add(c0, c1) - a = e.Ext3.Mul(a, b) - c := e.Ext3.Mul01By01(z3, z4, &x[3], &x[4]) - z1 := e.Ext3.Sub(a, c0) - z1 = e.Ext3.Sub(z1, c) - z0 := e.Ext3.MulByNonResidue(c) - z0 = e.Ext3.Add(z0, c0) +func (e *Ext6) MulBy01245(z *E6, x *[5]E2) *E6 { + c0 := &E6{B0: x[0], B1: x[1], B2: x[2]} + c1 := &E6{B0: *e.Ext2.Zero(), B1: x[3], B2: x[4]} + a := e.Ext6.Add(&z.C0, &z.C1) + b := e.Ext6.Add(c0, c1) + a = e.Ext6.Mul(a, b) + b = e.Ext6.Mul(&z.C0, c0) + c := e.Ext6.MulBy12(&z.C1, &x[3], &x[4]) + z1 := e.Ext6.Sub(a, b) + z1 = e.Ext6.Sub(z1, c) + z0 := e.Ext6.MulByNonResidue(c) + z0 = e.Ext6.Add(z0, b) return &E6{ - B0: *z0, - B1: *z1, + C0: *z0, + C1: *z1, } } +*/ diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index 1614178075..a91fea9ac4 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -494,90 +494,44 @@ func TestExpc1Fp6(t *testing.T) { assert.NoError(err) } -type e6MulBy034 struct { - A, B E6 - C3, C4 baseEl +type e6MulBy014 struct { + A E6 `gnark:",public"` + W E6 + B, C baseEl } -func (circuit *e6MulBy034) Define(api frontend.API) error { +func (circuit *e6MulBy014) Define(api frontend.API) error { nfield, err := emulated.NewField[emulated.BW6761Fp](api) if err != nil { panic(err) } e := NewExt6(nfield) - expected := e.MulBy034(&circuit.A, &circuit.C3, &circuit.C4) - e.AssertIsEqual(expected, &circuit.B) + res := e.MulBy014(&circuit.A, &circuit.B, &circuit.C) + e.AssertIsEqual(res, &circuit.W) return nil } -func TestMulBy034Fp6(t *testing.T) { +func TestFp12MulBy014(t *testing.T) { + assert := test.NewAssert(t) // witness values - var a, b bw6761.E6 + var a, w bw6761.E6 _, _ = a.SetRandom() - b.Set(&a) - var c0, c3, c4 fp.Element - c0.SetOne() - c3.SetRandom() - c4.SetRandom() - b.MulBy034(&c0, &c3, &c4) - - witness := e6MulBy034{ - A: FromE6(&a), - C3: emulated.ValueOf[emulated.BW6761Fp](c3), - C4: emulated.ValueOf[emulated.BW6761Fp](c4), - B: FromE6(&b), - } - - err := test.IsSolved(&e6MulBy034{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e6Mul034By034 struct { - D3, D4 baseEl - C3, C4 baseEl - Res E6 -} - -func (circuit *e6Mul034By034) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) - expected := e.Mul034By034(&circuit.D3, &circuit.D4, &circuit.C3, &circuit.C4) - e.AssertIsEqual(&E6{ - B0: E3{A0: expected[0], A1: expected[1], A2: expected[2]}, - B1: E3{A0: expected[3], A1: expected[4], A2: emulated.ValueOf[emulated.BW6761Fp](0)}, - }, &circuit.Res) - return nil -} + var one, b, c fp.Element + one.SetOne() + _, _ = b.SetRandom() + _, _ = c.SetRandom() + w.Set(&a) + w.MulBy014(&b, &c, &one) -func TestMul034By034Fp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var d0, d3, d4, c0, c3, c4, zero fp.Element - zero.SetZero() - d0.SetOne() - d3.SetRandom() - d4.SetRandom() - c0.SetOne() - c3.SetRandom() - c4.SetRandom() - a := bw6761.E6{ - B0: bw6761.E3{A0: d0, A1: zero, A2: zero}, - B1: bw6761.E3{A0: d3, A1: d4, A2: zero}, - } - a.MulBy034(&c0, &c3, &c4) - - witness := e6Mul034By034{ - D3: emulated.ValueOf[emulated.BW6761Fp](d3), - D4: emulated.ValueOf[emulated.BW6761Fp](d4), - C3: emulated.ValueOf[emulated.BW6761Fp](c3), - C4: emulated.ValueOf[emulated.BW6761Fp](c4), - Res: FromE6(&a), + witness := e6MulBy014{ + A: FromE6(&a), + B: emulated.ValueOf[emulated.BW6761Fp](&b), + C: emulated.ValueOf[emulated.BW6761Fp](&c), + W: FromE6(&w), } - err := test.IsSolved(&e6Mul034By034{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e6MulBy014{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) + } diff --git a/std/algebra/emulated/sw_bls12381/pairing.go b/std/algebra/emulated/sw_bls12381/pairing.go index 9f4996d457..7969a7dcbe 100644 --- a/std/algebra/emulated/sw_bls12381/pairing.go +++ b/std/algebra/emulated/sw_bls12381/pairing.go @@ -346,7 +346,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // i = 62, separately to avoid an E12 Square // (Square(res) = 1² = 1) - // k = 0, separately to avoid MulBy034 (res × ℓ) + // k = 0, separately to avoid MulBy014 (res × ℓ) // Qacc[k] ← 3Qacc[k], // l1 the tangent ℓ to 2Q[k] diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 7912b50cf0..87596d8a49 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -165,21 +165,20 @@ var loopCounter1 = [64]int8{ 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, } -// x₀^3-x₀^2+1 in 2-NAF +// x₀^3-x₀^2-x₀ in 2-NAF var loopCounter2 = [190]int8{ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, - 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, - 1, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 1, 0, - 1, 0, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 1, 0, -1, + 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, -1, 0, 0, + -1, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 1, 0, 1, 0, 0, 0, 1, 0, -1, 0, + -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, } func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res := pr.Ext6.One() - var prodLines [5]emulated.Element[emulated.BW6761Fp] var l1, l2 *lineEvaluation var yInv, xOverY *emulated.Element[emulated.BW6761Fp] @@ -207,7 +206,7 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy034(res, &l1.R0, &l1.R1) + res = pr.MulBy014(res, &l1.R1, &l1.R0) case 1: // Qacc ← 2Qacc+Q, @@ -218,15 +217,12 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + res = pr.MulBy014(res, &l1.R1, &l1.R0) // line evaluation at P l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - - // ℓ × ℓ - prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, &prodLines) + res = pr.MulBy014(res, &l2.R1, &l2.R0) case -1: // Qacc ← 2Qacc-Q, @@ -237,15 +233,12 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + res = pr.MulBy014(res, &l1.R1, &l1.R0) // line evaluation at P l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - - // ℓ × ℓ - prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, &prodLines) + res = pr.MulBy014(res, &l2.R1, &l2.R0) default: return nil, errors.New("invalid loopCounter") @@ -258,7 +251,6 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res := pr.Ext6.One() - var prodLines [5]emulated.Element[emulated.BW6761Fp] var l1, l2 *lineEvaluation var yInv, xOverY *emulated.Element[emulated.BW6761Fp] @@ -286,7 +278,7 @@ func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy034(res, &l1.R0, &l1.R1) + res = pr.MulBy014(res, &l1.R1, &l1.R0) case 1: // Qacc ← 2Qacc+Q, @@ -297,15 +289,12 @@ func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + res = pr.MulBy014(res, &l1.R1, &l1.R0) // line evaluation at P l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - - // ℓ × ℓ - prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, &prodLines) + res = pr.MulBy014(res, &l2.R1, &l2.R0) case -1: // Qacc ← 2Qacc-Q, @@ -316,15 +305,12 @@ func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + res = pr.MulBy014(res, &l1.R1, &l1.R0) // line evaluation at P l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - - // ℓ × ℓ - prodLines = *pr.Mul034By034(&l1.R0, &l1.R1, &l2.R0, &l2.R1) - // (ℓ × ℓ) × res - res = pr.MulBy01234(res, &prodLines) + res = pr.MulBy014(res, &l2.R1, &l2.R0) default: return nil, errors.New("invalid loopCounter") @@ -336,14 +322,17 @@ func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } // MillerLoop computes the Miller loop -// Naive BW6 Miller loop: Eq (3) in [bw6-post] +// Naive BW6 Miller loop: Eq (1) in [bw6-post] +// f_{u+1,Q}(P) * (f_{u^3-u^2-u,Q}(P))^q // // [https://hackmd.io/@gnark/BW6-761-changes] func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { + // f_{u+1,Q}(P) ml1, _ := pr.firstLoop(P, Q) + // f_{u^3-u^2-u,Q}(P) ml2, _ := pr.secondLoop(P, Q) - ml := pr.Frobenius(ml1) - ml = pr.Mul(ml, ml2) + ml := pr.Frobenius(ml2) + ml = pr.Mul(ml, ml1) return ml, nil } From 3ea1a490cfe4347ddffb87047a9f715ac5aa7268 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 11:52:50 -0400 Subject: [PATCH 09/38] perf(emulated/bw6): first loop in binary --- std/algebra/emulated/sw_bw6761/pairing.go | 34 +++++------------------ 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 87596d8a49..d399709ada 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -15,7 +15,7 @@ type curveF emulated.Field[emulated.BW6761Fp] type Pairing struct { *fields_bw6761.Ext6 - curveF emulated.Field[emulated.BW6761Fp] + curveF *emulated.Field[emulated.BW6761Fp] } func NewPairing(api frontend.API) (*Pairing, error) { @@ -24,7 +24,8 @@ func NewPairing(api frontend.API) (*Pairing, error) { return nil, fmt.Errorf("new base api: %w", err) } return &Pairing{ - Ext6: fields_bw6761.NewExt6(ba), + Ext6: fields_bw6761.NewExt6(ba), + curveF: ba, }, nil } @@ -158,10 +159,10 @@ type lineEvaluation struct { // seed x₀=9586122913090633729 // -// x₀+1 in 2-NAF +// x₀+1 in binary var loopCounter1 = [64]int8{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, } @@ -184,7 +185,6 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { var yInv, xOverY *emulated.Element[emulated.BW6761Fp] Qacc := Q - QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} // P and Q are supposed to be on G1 and G2 respectively of prime order r. // The point (x,0) is of order 2. But this function does not check // subgroup membership. @@ -196,9 +196,7 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // (∏ᵢfᵢ)² res = pr.Square(res) - switch loopCounter1[i] { - - case 0: + if loopCounter1[i] == 0 { // precompute lines // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc Qacc, l1 = pr.doubleStep(Qacc) @@ -208,7 +206,7 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) res = pr.MulBy014(res, &l1.R1, &l1.R0) - case 1: + } else { // Qacc ← 2Qacc+Q, // l1 the line ℓ passing Qacc and Q // l2 the line ℓ passing (Qacc+Q) and Qacc @@ -224,24 +222,6 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) res = pr.MulBy014(res, &l2.R1, &l2.R0) - case -1: - // Qacc ← 2Qacc-Q, - // l1 the line ℓ passing Qacc and -Q - // l2 the line ℓ passing (Qacc-Q) and Qacc - Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, QNeg) - - // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy014(res, &l1.R1, &l1.R0) - - // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) - l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res = pr.MulBy014(res, &l2.R1, &l2.R0) - - default: - return nil, errors.New("invalid loopCounter") } } From a266590f70147956726228ed9414ec40b78ef1bb Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 20:29:15 -0400 Subject: [PATCH 10/38] perf: naive emulated bw6 pairing working --- std/algebra/emulated/sw_bw6761/pairing.go | 125 +++++++++++----------- 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index d399709ada..ffe95764b5 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -178,23 +178,37 @@ var loopCounter2 = [190]int8{ -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, } -func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { - res := pr.Ext6.One() +// MillerLoop computes the Miller loop +// Naive BW6 Miller loop: Eq (1) in [bw6-post] +// f_{u+1,Q}(P) * (f_{u^3-u^2-u,Q}(P))^q +// +// [https://hackmd.io/@gnark/BW6-761-changes] +func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { var l1, l2 *lineEvaluation - var yInv, xOverY *emulated.Element[emulated.BW6761Fp] + var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] + // f_{u+1,Q}(P) + res1 := pr.Ext6.One() Qacc := Q - // P and Q are supposed to be on G1 and G2 respectively of prime order r. - // The point (x,0) is of order 2. But this function does not check - // subgroup membership. yInv = pr.curveF.Inverse(&P.Y) - xOverY = pr.curveF.MulMod(&P.X, yInv) - - for i := 62; i >= 0; i-- { + xNegOverY = pr.curveF.MulMod(&P.X, yInv) + xNegOverY = pr.curveF.Neg(xNegOverY) + + // i = 62 + // precompute lines + // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc + Qacc, l1 = pr.doubleStep(Qacc) + // line evaluation at P + // and assign line to res1 (R1, R0, 0, 0, 1, 0) + res1.B0.A0 = *pr.curveF.Mul(&l1.R1, yInv) + res1.B0.A1 = *pr.curveF.Mul(&l1.R0, xNegOverY) + res1.B1.A1 = *pr.curveF.One() + + for i := 61; i >= 0; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² - res = pr.Square(res) + res1 = pr.Square(res1) if loopCounter1[i] == 0 { // precompute lines @@ -202,9 +216,9 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { Qacc, l1 = pr.doubleStep(Qacc) // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy014(res, &l1.R1, &l1.R0) + res1 = pr.MulBy014(res1, &l1.R1, &l1.R0) } else { // Qacc ← 2Qacc+Q, @@ -213,40 +227,39 @@ func (pr Pairing) firstLoop(P *G1Affine, Q *G2Affine) (*GT, error) { Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, Q) // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy014(res, &l1.R1, &l1.R0) + res1 = pr.MulBy014(res1, &l1.R1, &l1.R0) // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) + l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res = pr.MulBy014(res, &l2.R1, &l2.R0) + res1 = pr.MulBy014(res1, &l2.R1, &l2.R0) } } - return res, nil - -} + // f_{u^3-u^2-u,Q}(P) + res2 := pr.Ext6.One() -func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { - res := pr.Ext6.One() + Qacc = Q + QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} - var l1, l2 *lineEvaluation - var yInv, xOverY *emulated.Element[emulated.BW6761Fp] + // i = 188 + // precompute lines + // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc + Qacc, l1 = pr.doubleStep(Qacc) - Qacc := Q - QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} - // P and Q are supposed to be on G1 and G2 respectively of prime order r. - // The point (x,0) is of order 2. But this function does not check - // subgroup membership. - yInv = pr.curveF.Inverse(&P.Y) - xOverY = pr.curveF.MulMod(&P.X, yInv) + // line evaluation at P + // and assign line to res2 (R1, R0, 0, 0, 1, 0) + res2.B0.A0 = *pr.curveF.Mul(&l1.R1, yInv) + res2.B0.A1 = *pr.curveF.Mul(&l1.R0, xNegOverY) + res2.B1.A1 = *pr.curveF.One() - for i := 188; i >= 0; i-- { + for i := 187; i >= 0; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² - res = pr.Square(res) + res2 = pr.Square(res2) switch loopCounter2[i] { @@ -256,9 +269,9 @@ func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { Qacc, l1 = pr.doubleStep(Qacc) // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy014(res, &l1.R1, &l1.R0) + res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) case 1: // Qacc ← 2Qacc+Q, @@ -267,14 +280,14 @@ func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, Q) // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy014(res, &l1.R1, &l1.R0) + res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) + l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res = pr.MulBy014(res, &l2.R1, &l2.R0) + res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) case -1: // Qacc ← 2Qacc-Q, @@ -283,37 +296,23 @@ func (pr Pairing) secondLoop(P *G1Affine, Q *G2Affine) (*GT, error) { Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, QNeg) // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xOverY) + l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res = pr.MulBy014(res, &l1.R1, &l1.R0) + res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xOverY) + l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res = pr.MulBy014(res, &l2.R1, &l2.R0) + res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) default: return nil, errors.New("invalid loopCounter") } } - return res, nil + res2 = pr.Frobenius(res2) -} - -// MillerLoop computes the Miller loop -// Naive BW6 Miller loop: Eq (1) in [bw6-post] -// f_{u+1,Q}(P) * (f_{u^3-u^2-u,Q}(P))^q -// -// [https://hackmd.io/@gnark/BW6-761-changes] -func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { - // f_{u+1,Q}(P) - ml1, _ := pr.firstLoop(P, Q) - // f_{u^3-u^2-u,Q}(P) - ml2, _ := pr.secondLoop(P, Q) - ml := pr.Frobenius(ml2) - ml = pr.Mul(ml, ml1) - return ml, nil + return pr.Mul(res1, res2), nil } // doubleAndAddStep doubles p1 and adds p2 to the result in affine coordinates, and evaluates the line in Miller loop @@ -336,7 +335,7 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation // omit y3 computation // compute line1 - line1.R0 = *pr.curveF.Neg(l1) + line1.R0 = *l1 line1.R1 = *pr.curveF.Mul(l1, &p1.X) line1.R1 = *pr.curveF.Sub(&line1.R1, &p1.Y) @@ -361,7 +360,7 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation p.Y = *y4 // compute line2 - line2.R0 = *pr.curveF.Neg(l2) + line2.R0 = *l2 line2.R1 = *pr.curveF.Mul(l2, &p1.X) line2.R1 = *pr.curveF.Sub(&line2.R1, &p1.Y) @@ -395,7 +394,7 @@ func (pr Pairing) doubleStep(p1 *G2Affine) (*G2Affine, *lineEvaluation) { p.X = *xr p.Y = *yr - line.R0 = *pr.curveF.Neg(λ) + line.R0 = *λ line.R1 = *pr.curveF.Mul(λ, &p1.X) line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) @@ -427,7 +426,7 @@ func (pr Pairing) addStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation) { res.Y = *yr var line lineEvaluation - line.R0 = *pr.curveF.Neg(λ) + line.R0 = *λ line.R1 = *pr.curveF.Mul(λ, &p1.X) line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) @@ -444,7 +443,7 @@ func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { λ := pr.curveF.Div(qypy, qxpx) var line lineEvaluation - line.R0 = *pr.curveF.Neg(λ) + line.R0 = *λ line.R1 = *pr.curveF.Mul(λ, &p1.X) line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) From c2ba96fdcb277103d0d6fdc1b19412e572fe9329 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 20:55:30 -0400 Subject: [PATCH 11/38] perf(emulated/bw6): isolate last iterations in Miller loops --- std/algebra/emulated/sw_bw6761/pairing.go | 79 +++++++++++++++++++---- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index ffe95764b5..194abcbe82 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -188,7 +188,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { var l1, l2 *lineEvaluation var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] - // f_{u+1,Q}(P) + // 1. f1 = f_{u+1,Q}(P) res1 := pr.Ext6.One() Qacc := Q yInv = pr.curveF.Inverse(&P.Y) @@ -205,7 +205,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res1.B0.A1 = *pr.curveF.Mul(&l1.R0, xNegOverY) res1.B1.A1 = *pr.curveF.One() - for i := 61; i >= 0; i-- { + for i := 61; i >= 1; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² res1 = pr.Square(res1) @@ -239,7 +239,17 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } } - // f_{u^3-u^2-u,Q}(P) + // i = 0, separately to avoid a point doubling + res1 = pr.Square(res1) + // l1 the tangent ℓ passing 2Qacc + l1 = pr.tangentCompute(Qacc) + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + // ℓ × res1 + res1 = pr.MulBy014(res1, &l1.R1, &l1.R0) + + // 2. f2 = f_{u^3-u^2-u,Q}(P) res2 := pr.Ext6.One() Qacc = Q @@ -256,7 +266,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res2.B0.A1 = *pr.curveF.Mul(&l1.R0, xNegOverY) res2.B1.A1 = *pr.curveF.One() - for i := 187; i >= 0; i-- { + for i := 187; i >= 1; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² res2 = pr.Square(res2) @@ -310,9 +320,23 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } } + // i = 0, separately to avoid a point doubling and an addition + res2 = pr.Square(res2) + l1, l2 = pr.tangentAndLineCompute(Qacc, QNeg) + // line evaluation at P + l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) + l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) + // line evaluation at P + l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) + l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) + res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) + + // 3. f1 * f2^q res2 = pr.Frobenius(res2) + res := pr.Mul(res1, res2) - return pr.Mul(res1, res2), nil + return res, nil } // doubleAndAddStep doubles p1 and adds p2 to the result in affine coordinates, and evaluates the line in Miller loop @@ -434,13 +458,15 @@ func (pr Pairing) addStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation) { } -// lineCompute computes the line that goes through p1 and p2 but does not compute p1+p2 -func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { +// tangentCompute computes the line that goes through p1 and p2 but does not compute p1+p2 +func (pr Pairing) tangentCompute(p1 *G2Affine) *lineEvaluation { - // compute λ = (y2-y1)/(x2-x1) - qypy := pr.curveF.Sub(&p2.Y, &p1.Y) - qxpx := pr.curveF.Sub(&p2.X, &p1.X) - λ := pr.curveF.Div(qypy, qxpx) + // λ = 3x²/2y + n := pr.curveF.Mul(&p1.X, &p1.X) + three := big.NewInt(3) + n = pr.curveF.MulConst(n, three) + d := pr.curveF.Add(&p1.Y, &p1.Y) + λ := pr.curveF.Div(n, d) var line lineEvaluation line.R0 = *λ @@ -450,3 +476,34 @@ func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { return &line } + +func (pr Pairing) tangentAndLineCompute(p1, p2 *G2Affine) (*lineEvaluation, *lineEvaluation) { + + // compute λ1 = (y2-y1)/(x2-x1) + n := pr.curveF.Sub(&p1.Y, &p2.Y) + d := pr.curveF.Sub(&p1.X, &p2.X) + l1 := pr.curveF.Div(n, d) + + // compute x3 =λ1²-x1-x2 + x3 := pr.curveF.Mul(l1, l1) + x3 = pr.curveF.Sub(x3, &p1.X) + x3 = pr.curveF.Sub(x3, &p2.X) + + // compute λ2 = -λ1-2y1/(x3-x1) + n = pr.curveF.Add(&p1.Y, &p1.Y) + d = pr.curveF.Sub(x3, &p1.X) + l2 := pr.curveF.Div(n, d) + l2 = pr.curveF.Add(l2, l1) + l2 = pr.curveF.Neg(l2) + + var line1, line2 lineEvaluation + line1.R0 = *l1 + line1.R1 = *pr.curveF.Mul(l1, &p1.X) + line1.R1 = *pr.curveF.Sub(&line1.R1, &p1.Y) + line2.R0 = *l2 + line2.R1 = *pr.curveF.Mul(l2, &p1.X) + line2.R1 = *pr.curveF.Sub(&line2.R1, &p1.Y) + + return &line1, &line2 + +} From 84a83a929ba01ac7dd43cde19feea5a84951e3c5 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 21:41:49 -0400 Subject: [PATCH 12/38] perf(emulated/bw6): add line by line multiplication --- std/algebra/emulated/fields_bw6761/e3.go | 26 ++++++ .../emulated/fields_bw6761/e6_pairing.go | 83 ++++++++++--------- std/algebra/emulated/sw_bw6761/pairing.go | 4 - 3 files changed, 69 insertions(+), 44 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index 4a0436386f..b81a9b7aa2 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -194,6 +194,32 @@ func (e Ext3) MulBy1(z *E3, c1 baseEl) *E3 { } } +// MulBy12 multiplication by sparse element (0,b1,b2) +func (e Ext3) MulBy12(x *E3, b1, b2 *baseEl) *E3 { + t1 := e.fp.Mul(&x.A1, b1) + t2 := e.fp.Mul(&x.A2, b2) + c0 := e.fp.Add(&x.A1, &x.A2) + tmp := e.fp.Add(b1, b2) + c0 = e.fp.Mul(c0, tmp) + c0 = e.fp.Sub(c0, t1) + c0 = e.fp.Sub(c0, t2) + c0 = MulByNonResidue(e.fp, c0) + c1 := e.fp.Add(&x.A0, &x.A1) + c1 = e.fp.Mul(c1, b1) + c1 = e.fp.Sub(c1, t1) + tmp = MulByNonResidue(e.fp, t2) + c1 = e.fp.Add(c1, tmp) + tmp = e.fp.Add(&x.A0, &x.A2) + c2 := e.fp.Mul(b2, tmp) + c2 = e.fp.Sub(c2, t2) + c2 = e.fp.Add(c2, t1) + return &E3{ + A0: *c0, + A1: *c1, + A2: *c2, + } +} + // Mul01By01 multiplies two E3 sparse element of the form: // // E3{ diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index f58c53ee44..0e4561fb3a 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -1,5 +1,7 @@ package fields_bw6761 +import "math/big" + type LineEvaluation struct { R0 baseEl R1 baseEl @@ -119,7 +121,6 @@ func (e *Ext6) MulBy014(z *E6, c0, c1 *baseEl) *E6 { } } -/* // multiplies two E6 sparse element of the form: // // E6{ @@ -133,30 +134,33 @@ func (e *Ext6) MulBy014(z *E6, c0, c1 *baseEl) *E6 { // C0: E6{B0: d0, B1: d1, B2: 0}, // C1: E6{B0: 0, B1: 1, B2: 0}, // } -func (e Ext6) Mul014By014(d0, d1, c0, c1 *E2) *[5]E2 { - one := e.Ext2.One() - x0 := e.Ext2.Mul(c0, d0) - x1 := e.Ext2.Mul(c1, d1) - tmp := e.Ext2.Add(c0, one) - x04 := e.Ext2.Add(d0, one) - x04 = e.Ext2.Mul(x04, tmp) - x04 = e.Ext2.Sub(x04, x0) - x04 = e.Ext2.Sub(x04, one) - tmp = e.Ext2.Add(c0, c1) - x01 := e.Ext2.Add(d0, d1) - x01 = e.Ext2.Mul(x01, tmp) - x01 = e.Ext2.Sub(x01, x0) - x01 = e.Ext2.Sub(x01, x1) - tmp = e.Ext2.Add(c1, one) - x14 := e.Ext2.Add(d1, one) - x14 = e.Ext2.Mul(x14, tmp) - x14 = e.Ext2.Sub(x14, x1) - x14 = e.Ext2.Sub(x14, one) - - zC0B0 := e.Ext2.NonResidue() - zC0B0 = e.Ext2.Add(zC0B0, x0) - - return &[5]E2{*zC0B0, *x01, *x1, *x04, *x14} +func (e Ext6) Mul014By014(d0, d1, c0, c1 *baseEl) *[5]baseEl { + one := e.fp.One() + x0 := e.fp.Mul(c0, d0) + x1 := e.fp.Mul(c1, d1) + tmp := e.fp.Add(c0, one) + x04 := e.fp.Add(d0, one) + x04 = e.fp.Mul(x04, tmp) + x04 = e.fp.Sub(x04, x0) + x04 = e.fp.Sub(x04, one) + tmp = e.fp.Add(c0, c1) + x01 := e.fp.Add(d0, d1) + x01 = e.fp.Mul(x01, tmp) + x01 = e.fp.Sub(x01, x0) + x01 = e.fp.Sub(x01, x1) + tmp = e.fp.Add(c1, one) + x14 := e.fp.Add(d1, one) + x14 = e.fp.Mul(x14, tmp) + x14 = e.fp.Sub(x14, x1) + x14 = e.fp.Sub(x14, one) + + // NonResidue() + zC0B0 := e.fp.MulConst(e.fp.One(), big.NewInt(4)) + zC0B0 = e.fp.Neg(zC0B0) + + zC0B0 = e.fp.Add(zC0B0, x0) + + return &[5]baseEl{*zC0B0, *x01, *x1, *x04, *x14} } // MulBy01245 multiplies z by an E6 sparse element of the form @@ -165,21 +169,20 @@ func (e Ext6) Mul014By014(d0, d1, c0, c1 *E2) *[5]E2 { // C0: E6{B0: c0, B1: c1, B2: c2}, // C1: E6{B0: 0, B1: c4, B2: c5}, // } -func (e *Ext6) MulBy01245(z *E6, x *[5]E2) *E6 { - c0 := &E6{B0: x[0], B1: x[1], B2: x[2]} - c1 := &E6{B0: *e.Ext2.Zero(), B1: x[3], B2: x[4]} - a := e.Ext6.Add(&z.C0, &z.C1) - b := e.Ext6.Add(c0, c1) - a = e.Ext6.Mul(a, b) - b = e.Ext6.Mul(&z.C0, c0) - c := e.Ext6.MulBy12(&z.C1, &x[3], &x[4]) - z1 := e.Ext6.Sub(a, b) - z1 = e.Ext6.Sub(z1, c) - z0 := e.Ext6.MulByNonResidue(c) - z0 = e.Ext6.Add(z0, b) +func (e *Ext6) MulBy01245(z *E6, x *[5]baseEl) *E6 { + c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} + c1 := &E3{A0: *e.fp.Zero(), A1: x[3], A2: x[4]} + a := e.Ext3.Add(&z.B0, &z.B1) + b := e.Ext3.Add(c0, c1) + a = e.Ext3.Mul(a, b) + b = e.Ext3.Mul(&z.B0, c0) + c := e.Ext3.MulBy12(&z.B1, &x[3], &x[4]) + z1 := e.Ext3.Sub(a, b) + z1 = e.Ext3.Sub(z1, c) + z0 := e.Ext3.MulByNonResidue(c) + z0 = e.Ext3.Add(z0, b) return &E6{ - C0: *z0, - C1: *z1, + B0: *z0, + B1: *z1, } } -*/ diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 194abcbe82..a368bff132 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -196,7 +196,6 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { xNegOverY = pr.curveF.Neg(xNegOverY) // i = 62 - // precompute lines // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc Qacc, l1 = pr.doubleStep(Qacc) // line evaluation at P @@ -211,7 +210,6 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res1 = pr.Square(res1) if loopCounter1[i] == 0 { - // precompute lines // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc Qacc, l1 = pr.doubleStep(Qacc) @@ -256,7 +254,6 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} // i = 188 - // precompute lines // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc Qacc, l1 = pr.doubleStep(Qacc) @@ -274,7 +271,6 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { switch loopCounter2[i] { case 0: - // precompute lines // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc Qacc, l1 = pr.doubleStep(Qacc) From 118edc34b6cc3319fabf38b77e97cf0f07a202de Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 22:49:35 -0400 Subject: [PATCH 13/38] perf(emulated/bw6): init second Miller loop by the first one --- std/algebra/emulated/sw_bw6761/pairing.go | 105 ++++++++++++---------- 1 file changed, 57 insertions(+), 48 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index a368bff132..e30e4f4904 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -159,28 +159,28 @@ type lineEvaluation struct { // seed x₀=9586122913090633729 // -// x₀+1 in binary +// x₀ in binary var loopCounter1 = [64]int8{ - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, } -// x₀^3-x₀^2-x₀ in 2-NAF -var loopCounter2 = [190]int8{ - -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 1, 0, -1, - 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, -1, 0, 0, - -1, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 1, 0, 1, 0, 0, 0, 1, 0, -1, 0, - -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, +// x₀^2-x₀-1 in 2-NAF +var loopCounter2 = [127]int8{ + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, + 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, + 0, 0, 1, } // MillerLoop computes the Miller loop -// Naive BW6 Miller loop: Eq (1) in [bw6-post] -// f_{u+1,Q}(P) * (f_{u^3-u^2-u,Q}(P))^q +// +// BW6-761 Miller loop: Eq (2') in [bw6-post] +// +// f_{u,Q}(P) * l_{[u]Q,Q}(P) * (f_u)^q_{u^2-u-1,[u]Q}(P) // // [https://hackmd.io/@gnark/BW6-761-changes] func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { @@ -188,7 +188,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { var l1, l2 *lineEvaluation var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] - // 1. f1 = f_{u+1,Q}(P) + // f1 = f_{u,Q}(P) res1 := pr.Ext6.One() Qacc := Q yInv = pr.curveF.Inverse(&P.Y) @@ -204,7 +204,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res1.B0.A1 = *pr.curveF.Mul(&l1.R0, xNegOverY) res1.B1.A1 = *pr.curveF.One() - for i := 61; i >= 1; i-- { + for i := 61; i >= 0; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² res1 = pr.Square(res1) @@ -237,33 +237,22 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } } - // i = 0, separately to avoid a point doubling - res1 = pr.Square(res1) - // l1 the tangent ℓ passing 2Qacc - l1 = pr.tangentCompute(Qacc) - // line evaluation at P + // l = l_{uQ,Q}(P) + res1Old := res1 + res1Inv := pr.Conjugate(res1) + uQ := Qacc + + l1 = pr.lineCompute(Qacc, Q) l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - // ℓ × res1 + // f1 = f1 * l_{uQ,Q}(P) res1 = pr.MulBy014(res1, &l1.R1, &l1.R0) - // 2. f2 = f_{u^3-u^2-u,Q}(P) - res2 := pr.Ext6.One() + // f2 = f_{u^2-u-1,uQ}(P) + res2 := res1Old + uQNeg := &G2Affine{X: uQ.X, Y: *pr.curveF.Neg(&uQ.Y)} - Qacc = Q - QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} - - // i = 188 - // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc - Qacc, l1 = pr.doubleStep(Qacc) - - // line evaluation at P - // and assign line to res2 (R1, R0, 0, 0, 1, 0) - res2.B0.A0 = *pr.curveF.Mul(&l1.R1, yInv) - res2.B0.A1 = *pr.curveF.Mul(&l1.R0, xNegOverY) - res2.B1.A1 = *pr.curveF.One() - - for i := 187; i >= 1; i-- { + for i := 125; i >= 1; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² res2 = pr.Square(res2) @@ -280,10 +269,10 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) case 1: - // Qacc ← 2Qacc+Q, - // l1 the line ℓ passing Qacc and Q - // l2 the line ℓ passing (Qacc+Q) and Qacc - Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, Q) + // Qacc ← 2Qacc+uQ, + // l1 the line ℓ passing Qacc and uQ + // l2 the line ℓ passing (Qacc+uQ) and Qacc + Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, uQ) // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) @@ -294,12 +283,13 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) + res2 = pr.Mul(res2, res1Old) case -1: - // Qacc ← 2Qacc-Q, - // l1 the line ℓ passing Qacc and -Q - // l2 the line ℓ passing (Qacc-Q) and Qacc - Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, QNeg) + // Qacc ← 2Qacc-uQ, + // l1 the line ℓ passing Qacc and -uQ + // l2 the line ℓ passing (Qacc-uQ) and Qacc + Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, uQNeg) // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) @@ -310,6 +300,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) + res2 = pr.Mul(res2, res1Inv) default: return nil, errors.New("invalid loopCounter") @@ -318,7 +309,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { // i = 0, separately to avoid a point doubling and an addition res2 = pr.Square(res2) - l1, l2 = pr.tangentAndLineCompute(Qacc, QNeg) + l1, l2 = pr.tangentAndLineCompute(Qacc, uQNeg) // line evaluation at P l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) @@ -327,8 +318,9 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) + res2 = pr.Mul(res2, res1Inv) - // 3. f1 * f2^q + // f1 * f2^q res2 = pr.Frobenius(res2) res := pr.Mul(res1, res2) @@ -503,3 +495,20 @@ func (pr Pairing) tangentAndLineCompute(p1, p2 *G2Affine) (*lineEvaluation, *lin return &line1, &line2 } + +// lineCompute computes the line that goes through p1 and p2 but does not compute p1+p2 +func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { + + // compute λ = (y2-y1)/(x2-x1) + qypy := pr.curveF.Sub(&p2.Y, &p1.Y) + qxpx := pr.curveF.Sub(&p2.X, &p1.X) + λ := pr.curveF.Div(qypy, qxpx) + + var line lineEvaluation + line.R0 = *λ + line.R1 = *pr.curveF.Mul(λ, &p1.X) + line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) + + return &line + +} From da202f354469be1b0b91ff649eaf218c23c49a0f Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 3 Oct 2023 23:07:44 -0400 Subject: [PATCH 14/38] perf(emulated/bw6): new development of the optimal ate pairing --- std/algebra/emulated/sw_bw6761/pairing.go | 40 ++++++++--------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index e30e4f4904..c4e43afb44 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -159,18 +159,18 @@ type lineEvaluation struct { // seed x₀=9586122913090633729 // -// x₀ in binary +// x₀+1 in binary var loopCounter1 = [64]int8{ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, } -// x₀^2-x₀-1 in 2-NAF +// (x₀-1)^2 in 2-NAF var loopCounter2 = [127]int8{ - -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, @@ -178,9 +178,9 @@ var loopCounter2 = [127]int8{ // MillerLoop computes the Miller loop // -// BW6-761 Miller loop: Eq (2') in [bw6-post] +// BW6-761 Miller loop: Eq (4') in [bw6-post] // -// f_{u,Q}(P) * l_{[u]Q,Q}(P) * (f_u)^q_{u^2-u-1,[u]Q}(P) +// f_{u+1,Q}(P) * (f_{u+1})^q_{u^2-2u-1,[u+1]Q}(P) * l^q_{[(u+1)(u^2-2u+1)]Q,-Q}(P) // // [https://hackmd.io/@gnark/BW6-761-changes] func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { @@ -188,7 +188,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { var l1, l2 *lineEvaluation var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] - // f1 = f_{u,Q}(P) + // f1 = f_{u+1,Q}(P) res1 := pr.Ext6.One() Qacc := Q yInv = pr.curveF.Inverse(&P.Y) @@ -242,17 +242,11 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res1Inv := pr.Conjugate(res1) uQ := Qacc - l1 = pr.lineCompute(Qacc, Q) - l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - // f1 = f1 * l_{uQ,Q}(P) - res1 = pr.MulBy014(res1, &l1.R1, &l1.R0) - - // f2 = f_{u^2-u-1,uQ}(P) + // f2 = f_{u^2-2u+1,uQ}(P) res2 := res1Old uQNeg := &G2Affine{X: uQ.X, Y: *pr.curveF.Neg(&uQ.Y)} - for i := 125; i >= 1; i-- { + for i := 125; i >= 0; i-- { // mutualize the square among n Miller loops // (∏ᵢfᵢ)² res2 = pr.Square(res2) @@ -307,18 +301,12 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } } - // i = 0, separately to avoid a point doubling and an addition - res2 = pr.Square(res2) - l1, l2 = pr.tangentAndLineCompute(Qacc, uQNeg) - // line evaluation at P + // l_{(u+1)vQ,-Q}(P) + QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} + l1 = pr.lineCompute(Qacc, QNeg) l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) - // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) - l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) - res2 = pr.Mul(res2, res1Inv) // f1 * f2^q res2 = pr.Frobenius(res2) From 337c79c1353e4f530f3749f10c5f1792a8e98660 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 4 Oct 2023 00:14:06 -0400 Subject: [PATCH 15/38] perf(emulated/bw6): use Mul istead of MulMod in tower --- std/algebra/emulated/fields_bw6761/e3.go | 54 +++++++++++------------ std/algebra/emulated/fields_bw6761/e6.go | 40 ++++++++--------- std/algebra/emulated/sw_bw6761/pairing.go | 50 --------------------- 3 files changed, 47 insertions(+), 97 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index b81a9b7aa2..737f4dc28b 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -121,9 +121,9 @@ func (e Ext3) Conjugate(x *E3) *E3 { // MulByElement multiplies an element in *E3 by an element in fp func (e Ext3) MulByElement(x *E3, y *baseEl) *E3 { - a0 := e.fp.MulMod(&x.A0, y) - a1 := e.fp.MulMod(&x.A1, y) - a2 := e.fp.MulMod(&x.A2, y) + a0 := e.fp.Mul(&x.A0, y) + a1 := e.fp.Mul(&x.A1, y) + a2 := e.fp.Mul(&x.A2, y) z := &E3{ A0: *a0, A1: *a1, @@ -146,23 +146,23 @@ func (e Ext3) MulByConstElement(x *E3, y *big.Int) *E3 { // MulBy01 multiplication by sparse element (c0,c1,0) func (e Ext3) MulBy01(z *E3, c0, c1 *baseEl) *E3 { - a := e.fp.MulMod(&z.A0, c0) - b := e.fp.MulMod(&z.A1, c1) + a := e.fp.Mul(&z.A0, c0) + b := e.fp.Mul(&z.A1, c1) tmp := e.fp.Add(&z.A1, &z.A2) - t0 := e.fp.MulMod(c1, tmp) + t0 := e.fp.Mul(c1, tmp) t0 = e.fp.Sub(t0, b) t0 = MulByNonResidue(e.fp, t0) t0 = e.fp.Add(t0, a) tmp = e.fp.Add(&z.A0, &z.A2) - t2 := e.fp.MulMod(c0, tmp) + t2 := e.fp.Mul(c0, tmp) t2 = e.fp.Sub(t2, a) t2 = e.fp.Add(t2, b) t1 := e.fp.Add(c0, c1) tmp = e.fp.Add(&z.A0, &z.A1) - t1 = e.fp.MulMod(t1, tmp) + t1 = e.fp.Mul(t1, tmp) t1 = e.fp.Sub(t1, a) t1 = e.fp.Sub(t1, b) @@ -176,15 +176,15 @@ func (e Ext3) MulBy01(z *E3, c0, c1 *baseEl) *E3 { // MulBy1 multiplication of E6 by sparse element (0, c1, 0) func (e Ext3) MulBy1(z *E3, c1 baseEl) *E3 { - b := e.fp.MulMod(&z.A1, &c1) + b := e.fp.Mul(&z.A1, &c1) tmp := e.fp.Add(&z.A1, &z.A2) - t0 := e.fp.MulMod(&c1, tmp) + t0 := e.fp.Mul(&c1, tmp) t0 = e.fp.Sub(t0, b) t0 = MulByNonResidue(e.fp, t0) tmp = e.fp.Add(&z.A0, &z.A1) - t1 := e.fp.MulMod(&c1, tmp) + t1 := e.fp.Mul(&c1, tmp) t1 = e.fp.Sub(t1, b) return &E3{ @@ -236,18 +236,18 @@ func (e Ext3) MulBy12(x *E3, b1, b2 *baseEl) *E3 { // A2: 0, // } func (e Ext3) Mul01By01(c0, c1, d0, d1 *baseEl) *E3 { - a := e.fp.MulMod(d0, c0) - b := e.fp.MulMod(d1, c1) - t0 := e.fp.MulMod(c1, d1) + a := e.fp.Mul(d0, c0) + b := e.fp.Mul(d1, c1) + t0 := e.fp.Mul(c1, d1) t0 = e.fp.Sub(t0, b) t0 = MulByNonResidue(e.fp, t0) t0 = e.fp.Add(t0, a) - t2 := e.fp.MulMod(c0, d0) + t2 := e.fp.Mul(c0, d0) t2 = e.fp.Sub(t2, a) t2 = e.fp.Add(t2, b) t1 := e.fp.Add(c0, c1) tmp := e.fp.Add(d0, d1) - t1 = e.fp.MulMod(t1, tmp) + t1 = e.fp.Mul(t1, tmp) t1 = e.fp.Sub(t1, a) t1 = e.fp.Sub(t1, b) return &E3{ @@ -260,26 +260,26 @@ func (e Ext3) Mul01By01(c0, c1, d0, d1 *baseEl) *E3 { // Mul sets z to the *E3-product of x,y, returns z func (e Ext3) Mul(x, y *E3) *E3 { // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf - t0 := e.fp.MulMod(&x.A0, &y.A0) - t1 := e.fp.MulMod(&x.A1, &y.A1) - t2 := e.fp.MulMod(&x.A2, &y.A2) + t0 := e.fp.Mul(&x.A0, &y.A0) + t1 := e.fp.Mul(&x.A1, &y.A1) + t2 := e.fp.Mul(&x.A2, &y.A2) c0 := e.fp.Add(&x.A1, &x.A2) tmp := e.fp.Add(&y.A1, &y.A2) - c0 = e.fp.MulMod(c0, tmp) + c0 = e.fp.Mul(c0, tmp) c0 = e.fp.Sub(c0, t1) c0 = e.fp.Sub(c0, t2) c0 = MulByNonResidue(e.fp, c0) tmp = e.fp.Add(&x.A0, &x.A2) c2 := e.fp.Add(&y.A0, &y.A2) - c2 = e.fp.MulMod(c2, tmp) + c2 = e.fp.Mul(c2, tmp) c2 = e.fp.Sub(c2, t0) c2 = e.fp.Sub(c2, t2) c1 := e.fp.Add(&x.A0, &x.A1) tmp = e.fp.Add(&y.A0, &y.A1) - c1 = e.fp.MulMod(c1, tmp) + c1 = e.fp.Mul(c1, tmp) c1 = e.fp.Sub(c1, t0) c1 = e.fp.Sub(c1, t1) t2 = MulByNonResidue(e.fp, t2) @@ -301,17 +301,17 @@ func (e Ext3) Square(x *E3) *E3 { // Algorithm 16 from https://eprint.iacr.org/2010/354.pdf c6 := e.fp.MulConst(&x.A1, big.NewInt(2)) - c4 := e.fp.MulMod(&x.A0, c6) // x.A0 * xA1 * 2 - c5 := e.fp.MulMod(&x.A2, &x.A2) + c4 := e.fp.Mul(&x.A0, c6) // x.A0 * xA1 * 2 + c5 := e.fp.Mul(&x.A2, &x.A2) c1 := MulByNonResidue(e.fp, c5) c1 = e.fp.Add(c1, c4) c2 := e.fp.Sub(c4, c5) - c3 := e.fp.MulMod(&x.A0, &x.A0) + c3 := e.fp.Mul(&x.A0, &x.A0) c4 = e.fp.Sub(&x.A0, &x.A1) c4 = e.fp.Add(c4, &x.A2) - c5 = e.fp.MulMod(c6, &x.A2) // x.A1 * xA2 * 2 - c4 = e.fp.MulMod(c4, c4) + c5 = e.fp.Mul(c6, &x.A2) // x.A1 * xA2 * 2 + c4 = e.fp.Mul(c4, c4) c0 := MulByNonResidue(e.fp, c5) c4 = e.fp.Add(c4, c5) c4 = e.fp.Sub(c4, c3) diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 98e2cd2e44..c12638bb31 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -118,13 +118,13 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { var t [7]*baseEl // t0 = g1² - t[0] = e.fp.MulMod(&x.B0.A1, &x.B0.A1) + t[0] = e.fp.Mul(&x.B0.A1, &x.B0.A1) // t1 = g5² - t[1] = e.fp.MulMod(&x.B1.A2, &x.B1.A2) + t[1] = e.fp.Mul(&x.B1.A2, &x.B1.A2) // t5 = g1 + g5 t[5] = e.fp.Add(&x.B0.A1, &x.B1.A2) // t2 = (g1 + g5)² - t[2] = e.fp.MulMod(t[5], t[5]) + t[2] = e.fp.Mul(t[5], t[5]) // t3 = g1² + g5² t[3] = e.fp.Add(t[0], t[1]) @@ -134,9 +134,9 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { // t6 = g3 + g2 t[6] = e.fp.Add(&x.B1.A0, &x.B0.A2) // t3 = (g3 + g2)² - t[3] = e.fp.MulMod(t[6], t[6]) + t[3] = e.fp.Mul(t[6], t[6]) // t2 = g3² - t[2] = e.fp.MulMod(&x.B1.A0, &x.B1.A0) + t[2] = e.fp.Mul(&x.B1.A0, &x.B1.A0) // t6 = 2 * nr * g1 * g5 t[6] = MulByNonResidue(e.fp, t[5]) @@ -154,7 +154,7 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { t[6] = e.fp.Sub(t[5], &x.B0.A2) // t1 = g2² - t[1] = e.fp.MulMod(&x.B0.A2, &x.B0.A2) + t[1] = e.fp.Mul(&x.B0.A2, &x.B0.A2) // t6 = 2 * nr * g5² + 2 * g1² - 2*g2 t[6] = e.fp.Add(t[6], t[6]) @@ -207,13 +207,13 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { one := e.fp.One() // t0 = g1^2 - t[0] = e.fp.MulMod(&x.B0.A1, &x.B0.A1) + t[0] = e.fp.Mul(&x.B0.A1, &x.B0.A1) // t1 = 3 * g1^2 - 2 * g2 t[1] = e.fp.Sub(t[0], &x.B0.A2) t[1] = e.fp.Add(t[1], t[1]) t[1] = e.fp.Add(t[1], t[0]) // t0 = E * g5^2 + t1 - t[2] = e.fp.MulMod(&x.B1.A2, &x.B1.A2) + t[2] = e.fp.Mul(&x.B1.A2, &x.B1.A2) t[0] = MulByNonResidue(e.fp, t[2]) t[0] = e.fp.Add(t[0], t[1]) // t1 = 1/(4 * g3) @@ -225,14 +225,14 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { a1 := z.B1.A1 // t1 = g2 * g1 - t[1] = e.fp.MulMod(&x.B0.A2, &x.B0.A1) + t[1] = e.fp.Mul(&x.B0.A2, &x.B0.A1) // t2 = 2 * g4² - 3 * g2 * g1 - t[2] = e.fp.MulMod(&a1, &a1) + t[2] = e.fp.Mul(&a1, &a1) t[2] = e.fp.Sub(t[2], t[1]) t[2] = e.fp.Add(t[2], t[2]) t[2] = e.fp.Sub(t[2], t[1]) // t1 = g3 * g5 (g3 can be 0) - t[1] = e.fp.MulMod(&x.B1.A0, &x.B1.A2) + t[1] = e.fp.Mul(&x.B1.A0, &x.B1.A2) // c₀ = E * (2 * g4² + g3 * g5 - 3 * g2 * g1) + 1 t[2] = e.fp.Add(t[2], t[1]) @@ -262,22 +262,22 @@ func (e Ext6) CyclotomicSquare(x *E6) *E6 { var t [9]*baseEl - t[0] = e.fp.MulMod(&x.B1.A1, &x.B1.A1) - t[1] = e.fp.MulMod(&x.B0.A0, &x.B0.A0) + t[0] = e.fp.Mul(&x.B1.A1, &x.B1.A1) + t[1] = e.fp.Mul(&x.B0.A0, &x.B0.A0) t[6] = e.fp.Add(&x.B1.A1, &x.B0.A0) - t[6] = e.fp.MulMod(t[6], t[6]) + t[6] = e.fp.Mul(t[6], t[6]) t[6] = e.fp.Sub(t[6], t[0]) t[6] = e.fp.Sub(t[6], t[1]) // 2*x4*x0 - t[2] = e.fp.MulMod(&x.B0.A2, &x.B0.A2) - t[3] = e.fp.MulMod(&x.B1.A0, &x.B1.A0) + t[2] = e.fp.Mul(&x.B0.A2, &x.B0.A2) + t[3] = e.fp.Mul(&x.B1.A0, &x.B1.A0) t[7] = e.fp.Add(&x.B0.A2, &x.B1.A0) - t[7] = e.fp.MulMod(t[7], t[7]) + t[7] = e.fp.Mul(t[7], t[7]) t[7] = e.fp.Sub(t[7], t[2]) t[7] = e.fp.Sub(t[7], t[3]) // 2*x2*x3 - t[4] = e.fp.MulMod(&x.B1.A2, &x.B1.A2) - t[5] = e.fp.MulMod(&x.B0.A1, &x.B0.A1) + t[4] = e.fp.Mul(&x.B1.A2, &x.B1.A2) + t[5] = e.fp.Mul(&x.B0.A1, &x.B0.A1) t[8] = e.fp.Add(&x.B1.A2, &x.B0.A1) - t[8] = e.fp.MulMod(t[8], t[8]) + t[8] = e.fp.Mul(t[8], t[8]) t[8] = e.fp.Sub(t[8], t[4]) t[8] = e.fp.Sub(t[8], t[5]) t[8] = MulByNonResidue(e.fp, t[8]) // 2*x5*x1*u diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index c4e43afb44..7050145366 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -434,56 +434,6 @@ func (pr Pairing) addStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation) { } -// tangentCompute computes the line that goes through p1 and p2 but does not compute p1+p2 -func (pr Pairing) tangentCompute(p1 *G2Affine) *lineEvaluation { - - // λ = 3x²/2y - n := pr.curveF.Mul(&p1.X, &p1.X) - three := big.NewInt(3) - n = pr.curveF.MulConst(n, three) - d := pr.curveF.Add(&p1.Y, &p1.Y) - λ := pr.curveF.Div(n, d) - - var line lineEvaluation - line.R0 = *λ - line.R1 = *pr.curveF.Mul(λ, &p1.X) - line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) - - return &line - -} - -func (pr Pairing) tangentAndLineCompute(p1, p2 *G2Affine) (*lineEvaluation, *lineEvaluation) { - - // compute λ1 = (y2-y1)/(x2-x1) - n := pr.curveF.Sub(&p1.Y, &p2.Y) - d := pr.curveF.Sub(&p1.X, &p2.X) - l1 := pr.curveF.Div(n, d) - - // compute x3 =λ1²-x1-x2 - x3 := pr.curveF.Mul(l1, l1) - x3 = pr.curveF.Sub(x3, &p1.X) - x3 = pr.curveF.Sub(x3, &p2.X) - - // compute λ2 = -λ1-2y1/(x3-x1) - n = pr.curveF.Add(&p1.Y, &p1.Y) - d = pr.curveF.Sub(x3, &p1.X) - l2 := pr.curveF.Div(n, d) - l2 = pr.curveF.Add(l2, l1) - l2 = pr.curveF.Neg(l2) - - var line1, line2 lineEvaluation - line1.R0 = *l1 - line1.R1 = *pr.curveF.Mul(l1, &p1.X) - line1.R1 = *pr.curveF.Sub(&line1.R1, &p1.Y) - line2.R0 = *l2 - line2.R1 = *pr.curveF.Mul(l2, &p1.X) - line2.R1 = *pr.curveF.Sub(&line2.R1, &p1.Y) - - return &line1, &line2 - -} - // lineCompute computes the line that goes through p1 and p2 but does not compute p1+p2 func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { From 94af6c86c7f91415cfcd9591019f4400de712c90 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Oct 2023 12:14:08 -0400 Subject: [PATCH 16/38] perf(emulated/bw6): manually reduce Fp12 elements --- std/algebra/emulated/fields_bw6761/e6.go | 7 +++--- .../emulated/fields_bw6761/e6_pairing.go | 24 +++++++------------ std/algebra/emulated/sw_bw6761/pairing.go | 6 +---- 3 files changed, 12 insertions(+), 25 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index c12638bb31..b381a6d70e 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -89,6 +89,7 @@ func (e Ext6) Mul(x, y *E6) *E6 { // Square set z=x*x in *E6 and return z func (e Ext6) Square(x *E6) *E6 { + x = e.Reduce(x) //Algorithm 22 from https://eprint.iacr.org/2010/354.pdf c0 := e.Ext3.Sub(&x.B0, &x.B1) c3 := e.Ext3.MulByNonResidue(&x.B1) @@ -111,9 +112,7 @@ func (e Ext6) Square(x *E6) *E6 { // https://eprint.iacr.org/2010/542.pdf // Th. 3.2 with minor modifications to fit our tower func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { - x = e.Reduce(x) - - z := e.Set(x) + z := e.Reduce(x) var t [7]*baseEl @@ -221,7 +220,7 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { t[1] = e.fp.Add(t[1], t[1]) // z4 = g4 - z.B1.A1 = *e.fp.Div(t[0], t[1]) // costly + z.B1.A1 = *e.fp.Div(t[0], t[1]) a1 := z.B1.A1 // t1 = g2 * g1 diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 0e4561fb3a..4b0ca3cee9 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -2,19 +2,6 @@ package fields_bw6761 import "math/big" -type LineEvaluation struct { - R0 baseEl - R1 baseEl - R2 baseEl -} - -func (e Ext6) nSquare(z *E6, n int) *E6 { - for i := 0; i < n; i++ { - z = e.CyclotomicSquare(z) - } - return z -} - func (e Ext6) nSquareCompressed(z *E6, n int) *E6 { for i := 0; i < n; i++ { z = e.CyclotomicSquareCompressed(z) @@ -33,12 +20,15 @@ func (e Ext6) Expt(x *E6) *E6 { // a shortest addition chain for 136227 result := e.Set(x) - result = e.nSquare(result, 5) + result = e.nSquareCompressed(result, 5) + result = e.DecompressKarabina(result) result = e.Mul(result, x) x33 := e.Set(result) - result = e.nSquare(result, 7) + result = e.nSquareCompressed(result, 7) + result = e.DecompressKarabina(result) result = e.Mul(result, x33) - result = e.nSquare(result, 4) + result = e.nSquareCompressed(result, 4) + result = e.DecompressKarabina(result) result = e.Mul(result, x) result = e.CyclotomicSquare(result) result = e.Mul(result, x) @@ -96,6 +86,8 @@ func (e Ext6) Expc1(x *E6) *E6 { // } func (e *Ext6) MulBy014(z *E6, c0, c1 *baseEl) *E6 { + z = e.Reduce(z) + a := z.B0 a = *e.MulBy01(&a, c0, c1) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 7050145366..4ae9721525 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -62,14 +62,10 @@ func (pr Pairing) Pair(P *G1Affine, Q *G2Affine) (*GT, error) { // where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r // we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r // where s is the cofactor 12(x_0+1) (El Housni and Guillevic) -func (pr Pairing) FinalExponentiation(z *GT, _z ...*GT) *GT { +func (pr Pairing) FinalExponentiation(z *GT) *GT { result := pr.Set(z) - for _, a := range _z { - result = pr.Mul(result, a) - } - // Easy part // (p^3-1)(p+1) buf := pr.Conjugate(result) From 9d8302cc120f333f47d4be1a358d379c014ec910 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Oct 2023 13:18:09 -0400 Subject: [PATCH 17/38] perf(emulated/bw6): more use of Karabina cycloSquare --- std/algebra/emulated/fields_bw6761/e6.go | 4 +- .../emulated/fields_bw6761/e6_pairing.go | 39 +++++++------------ std/algebra/emulated/sw_bw6761/pairing.go | 6 +-- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index c12638bb31..b92a4075c6 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -89,6 +89,7 @@ func (e Ext6) Mul(x, y *E6) *E6 { // Square set z=x*x in *E6 and return z func (e Ext6) Square(x *E6) *E6 { + x = e.Reduce(x) //Algorithm 22 from https://eprint.iacr.org/2010/354.pdf c0 := e.Ext3.Sub(&x.B0, &x.B1) c3 := e.Ext3.MulByNonResidue(&x.B1) @@ -112,7 +113,6 @@ func (e Ext6) Square(x *E6) *E6 { // Th. 3.2 with minor modifications to fit our tower func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { x = e.Reduce(x) - z := e.Set(x) var t [7]*baseEl @@ -221,7 +221,7 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { t[1] = e.fp.Add(t[1], t[1]) // z4 = g4 - z.B1.A1 = *e.fp.Div(t[0], t[1]) // costly + z.B1.A1 = *e.fp.Div(t[0], t[1]) a1 := z.B1.A1 // t1 = g2 * g1 diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 0e4561fb3a..643dee78f1 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -2,19 +2,6 @@ package fields_bw6761 import "math/big" -type LineEvaluation struct { - R0 baseEl - R1 baseEl - R2 baseEl -} - -func (e Ext6) nSquare(z *E6, n int) *E6 { - for i := 0; i < n; i++ { - z = e.CyclotomicSquare(z) - } - return z -} - func (e Ext6) nSquareCompressed(z *E6, n int) *E6 { for i := 0; i < n; i++ { z = e.CyclotomicSquareCompressed(z) @@ -33,12 +20,15 @@ func (e Ext6) Expt(x *E6) *E6 { // a shortest addition chain for 136227 result := e.Set(x) - result = e.nSquare(result, 5) + result = e.nSquareCompressed(result, 5) + result = e.DecompressKarabina(result) result = e.Mul(result, x) x33 := e.Set(result) - result = e.nSquare(result, 7) + result = e.nSquareCompressed(result, 7) + result = e.DecompressKarabina(result) result = e.Mul(result, x33) - result = e.nSquare(result, 4) + result = e.nSquareCompressed(result, 4) + result = e.DecompressKarabina(result) result = e.Mul(result, x) result = e.CyclotomicSquare(result) result = e.Mul(result, x) @@ -56,8 +46,9 @@ func (e Ext6) Expt(x *E6) *E6 { // c1 = ht+hy = 22 (10110) func (e Ext6) Expc2(x *E6) *E6 { - result := e.CyclotomicSquare(x) - result = e.CyclotomicSquare(result) + result := x + result = e.nSquareCompressed(result, 2) + result = e.DecompressKarabina(result) result = e.Mul(result, x) result = e.CyclotomicSquare(result) result = e.Mul(result, x) @@ -74,18 +65,16 @@ func (e Ext6) Expc1(x *E6) *E6 { result := e.CyclotomicSquare(x) result = e.Mul(result, x) - result = e.CyclotomicSquare(result) - result = e.CyclotomicSquare(result) - result = e.CyclotomicSquare(result) + result = e.nSquareCompressed(result, 3) + result = e.DecompressKarabina(result) result = e.Mul(result, x) result = e.CyclotomicSquare(result) result = e.Mul(result, x) result = e.CyclotomicSquare(result) result = e.Mul(result, x) - result = e.CyclotomicSquare(result) - result = e.CyclotomicSquare(result) + result = e.nSquareCompressed(result, 2) - return e.Set(result) + return e.DecompressKarabina(result) } // MulBy014 multiplies z by an E6 sparse element of the form @@ -96,6 +85,8 @@ func (e Ext6) Expc1(x *E6) *E6 { // } func (e *Ext6) MulBy014(z *E6, c0, c1 *baseEl) *E6 { + z = e.Reduce(z) + a := z.B0 a = *e.MulBy01(&a, c0, c1) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 7050145366..4ae9721525 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -62,14 +62,10 @@ func (pr Pairing) Pair(P *G1Affine, Q *G2Affine) (*GT, error) { // where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r // we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r // where s is the cofactor 12(x_0+1) (El Housni and Guillevic) -func (pr Pairing) FinalExponentiation(z *GT, _z ...*GT) *GT { +func (pr Pairing) FinalExponentiation(z *GT) *GT { result := pr.Set(z) - for _, a := range _z { - result = pr.Mul(result, a) - } - // Easy part // (p^3-1)(p+1) buf := pr.Conjugate(result) From 849d2045ecf44bceb7ff3ea6beb99ad8049f5733 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Oct 2023 13:36:16 -0400 Subject: [PATCH 18/38] refactor: address PR review --- std/algebra/emulated/fields_bw6761/e3.go | 9 +-------- std/algebra/emulated/fields_bw6761/e6.go | 14 ++++---------- std/algebra/emulated/fields_bw6761/e6_pairing.go | 8 ++++---- std/algebra/emulated/fields_bw6761/e6_test.go | 2 +- std/algebra/emulated/sw_bw6761/pairing.go | 2 +- std/algebra/emulated/sw_bw6761/pairing_test.go | 2 +- 6 files changed, 12 insertions(+), 25 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index 737f4dc28b..ff373c0c48 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -388,7 +388,7 @@ func (e Ext3) AssertIsEqual(a, b *E3) { e.fp.AssertIsEqual(&a.A2, &b.A2) } -func (e Ext3) Set(x *E3) *E3 { +func (e Ext3) Copy(x *E3) *E3 { return &E3{ A0: x.A0, A1: x.A1, @@ -396,13 +396,6 @@ func (e Ext3) Set(x *E3) *E3 { } } -// Equal returns true if z equals x, fasle otherwise -func (e Ext3) Equal(a, b *E3) { - e.fp.AssertIsEqual(&a.A0, &b.A0) - e.fp.AssertIsEqual(&a.A1, &b.A1) - e.fp.AssertIsEqual(&a.A2, &b.A2) -} - func FromE3(a *bw6761.E3) E3 { return E3{ A0: emulated.ValueOf[emulated.BW6761Fp](a.A0), diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index b92a4075c6..8332ccb43e 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -113,7 +113,7 @@ func (e Ext6) Square(x *E6) *E6 { // Th. 3.2 with minor modifications to fit our tower func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { x = e.Reduce(x) - z := e.Set(x) + z := e.Copy(x) var t [7]*baseEl @@ -368,21 +368,15 @@ func (e Ext6) AssertIsEqual(a, b *E6) { e.Ext3.AssertIsEqual(&a.B1, &b.B1) } -func (e Ext6) Set(x *E6) *E6 { - b0 := e.Ext3.Set(&x.B0) - b1 := e.Ext3.Set(&x.B1) +func (e Ext6) Copy(x *E6) *E6 { + b0 := e.Ext3.Copy(&x.B0) + b1 := e.Ext3.Copy(&x.B1) return &E6{ B0: *b0, B1: *b1, } } -// Equal returns true if z equals x, fasle otherwise -func (e Ext6) Equal(a, b *E6) { - e.Ext3.Equal(&a.B0, &b.B0) - e.Ext3.Equal(&a.B1, &b.B1) -} - func FromE6(a *bw6761.E6) E6 { return E6{ B0: FromE3(&a.B0), diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 643dee78f1..9fe5146715 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -19,11 +19,11 @@ func (e Ext6) Expt(x *E6) *E6 { // Shortest addition chains can be found at https://wwwhomes.uni-bielefeld.de/achim/addition_chain.html // a shortest addition chain for 136227 - result := e.Set(x) + result := e.Copy(x) result = e.nSquareCompressed(result, 5) result = e.DecompressKarabina(result) result = e.Mul(result, x) - x33 := e.Set(result) + x33 := e.Copy(result) result = e.nSquareCompressed(result, 7) result = e.DecompressKarabina(result) result = e.Mul(result, x33) @@ -38,7 +38,7 @@ func (e Ext6) Expt(x *E6) *E6 { result = e.DecompressKarabina(result) result = e.Mul(result, x) - return e.Set(result) + return result } // Expc2 set z to x^c2 in *E6 and return z @@ -54,7 +54,7 @@ func (e Ext6) Expc2(x *E6) *E6 { result = e.Mul(result, x) result = e.CyclotomicSquare(result) - return e.Set(result) + return result } // Expc1 set z to x^c1 in *E6 and return z diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index a91fea9ac4..517d22d0af 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -393,7 +393,6 @@ func TestCreateLimbs(t *testing.T) { } fmt.Println(buf.String()) } -*/ type e6Expt struct { A, B E6 @@ -493,6 +492,7 @@ func TestExpc1Fp6(t *testing.T) { err := test.IsSolved(&e6Expc1{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } +*/ type e6MulBy014 struct { A E6 `gnark:",public"` diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 4ae9721525..74fd984892 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -64,7 +64,7 @@ func (pr Pairing) Pair(P *G1Affine, Q *G2Affine) (*GT, error) { // where s is the cofactor 12(x_0+1) (El Housni and Guillevic) func (pr Pairing) FinalExponentiation(z *GT) *GT { - result := pr.Set(z) + result := pr.Copy(z) // Easy part // (p^3-1)(p+1) diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 1771f3dc02..53d9a738e8 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -49,7 +49,7 @@ func (circuit *finalExponentiationBW6761) Define(api frontend.API) error { return err } - pr.Equal(expected, &circuit.B) + pr.AssertIsEqual(expected, &circuit.B) return nil } From 996c82f7ff8895c027187848335501573be25ba1 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Oct 2023 18:04:48 -0400 Subject: [PATCH 19/38] refactor: consolidate emulated bw6-761 pairing with other curves --- std/algebra/emulated/fields_bw6761/e3.go | 4 +- std/algebra/emulated/fields_bw6761/e3_test.go | 36 +++ std/algebra/emulated/fields_bw6761/e6.go | 4 +- std/algebra/emulated/fields_bw6761/e6_test.go | 36 +++ std/algebra/emulated/sw_bw6761/doc.go | 7 + std/algebra/emulated/sw_bw6761/doc_test.go | 103 +++++++++ std/algebra/emulated/sw_bw6761/pairing.go | 206 ++++++++++-------- .../emulated/sw_bw6761/pairing_test.go | 132 +++++++---- 8 files changed, 382 insertions(+), 146 deletions(-) create mode 100644 std/algebra/emulated/sw_bw6761/doc.go create mode 100644 std/algebra/emulated/sw_bw6761/doc_test.go diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index ff373c0c48..f79a6e2678 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -362,8 +362,8 @@ func (e Ext3) DivUnchecked(x, y *E3) *E3 { A2: *res[2], } - // 1 == inv * x - _x := e.Mul(&div, x) + // x = div * y + _x := e.Mul(&div, y) e.AssertIsEqual(x, _x) return &div diff --git a/std/algebra/emulated/fields_bw6761/e3_test.go b/std/algebra/emulated/fields_bw6761/e3_test.go index 6a366c1b51..18fc7881a1 100644 --- a/std/algebra/emulated/fields_bw6761/e3_test.go +++ b/std/algebra/emulated/fields_bw6761/e3_test.go @@ -343,6 +343,42 @@ func TestInverseFp3(t *testing.T) { assert.NoError(err) } +type e3Div struct { + A, B, C E3 +} + +func (circuit *e3Div) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt3(nfield) + expected := e.DivUnchecked(&circuit.A, &circuit.B) + e.AssertIsEqual(expected, &circuit.C) + return nil +} + +func TestDivFp3(t *testing.T) { + + assert := test.NewAssert(t) + // witness values + var a, b, c bw6761.E3 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + c.Inverse(&b) + c.Mul(&a, &c) + + witness := e3Div{ + A: FromE3(&a), + B: FromE3(&b), + C: FromE3(&c), + } + + err := test.IsSolved(&e3Div{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + +} + type e3Conjugate struct { A, B E3 } diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 8332ccb43e..201a1b1edb 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -346,8 +346,8 @@ func (e Ext6) DivUnchecked(x, y *E6) *E6 { B1: E3{A0: *res[3], A1: *res[4], A2: *res[5]}, } - // 1 == inv * x - _x := e.Mul(&div, x) + // x = div * y + _x := e.Mul(&div, y) e.AssertIsEqual(x, _x) return &div diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index 517d22d0af..ef90689297 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -215,6 +215,42 @@ func TestInverseFp6(t *testing.T) { assert.NoError(err) } +type e6Div struct { + A, B, C E6 +} + +func (circuit *e6Div) Define(api frontend.API) error { + nfield, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } + e := NewExt6(nfield) + 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 bw6761.E6 + _, _ = a.SetRandom() + _, _ = b.SetRandom() + c.Inverse(&b) + c.Mul(&a, &c) + + witness := e6Div{ + A: FromE6(&a), + B: FromE6(&b), + C: FromE6(&c), + } + + err := test.IsSolved(&e6Div{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + +} + type e6Conjugate struct { A, B E6 } diff --git a/std/algebra/emulated/sw_bw6761/doc.go b/std/algebra/emulated/sw_bw6761/doc.go new file mode 100644 index 0000000000..e608cc7d1c --- /dev/null +++ b/std/algebra/emulated/sw_bw6761/doc.go @@ -0,0 +1,7 @@ +// Package sw_bw6761 implements G1 and G2 arithmetics and pairing computation over BW6-761 curve. +// +// The implementation follows [Housni22]: "Pairings in Rank-1 Constraint Systems" and [BW6-761-hackmd]. +// +// [Housni22]: https://eprint.iacr.org/2022/1162 +// [BW6-761-hackmd]: https://hackmd.io/@gnark/BW6-761-changes +package sw_bw6761 diff --git a/std/algebra/emulated/sw_bw6761/doc_test.go b/std/algebra/emulated/sw_bw6761/doc_test.go new file mode 100644 index 0000000000..e524186f3a --- /dev/null +++ b/std/algebra/emulated/sw_bw6761/doc_test.go @@ -0,0 +1,103 @@ +package sw_bw6761_test + +import ( + "crypto/rand" + "fmt" + + "github.com/consensys/gnark-crypto/ecc" + bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark/backend/groth16" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/std/algebra/emulated/sw_bw6761" +) + +type PairCircuit struct { + InG1 sw_bw6761.G1Affine + InG2 sw_bw6761.G2Affine + Res sw_bw6761.GTEl +} + +func (c *PairCircuit) Define(api frontend.API) error { + pairing, err := sw_bw6761.NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + // Pair method does not check that the points are in the proper groups. + // Compute the pairing + res, err := pairing.Pair([]*sw_bw6761.G1Affine{&c.InG1}, []*sw_bw6761.G2Affine{&c.InG2}) + if err != nil { + return fmt.Errorf("pair: %w", err) + } + pairing.AssertIsEqual(res, &c.Res) + return nil +} + +func ExamplePairing() { + p, q, err := randomG1G2Affines() + if err != nil { + panic(err) + } + res, err := bw6761.Pair([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) + if err != nil { + panic(err) + } + circuit := PairCircuit{} + witness := PairCircuit{ + InG1: sw_bw6761.NewG1Affine(p), + InG2: sw_bw6761.NewG2Affine(q), + Res: sw_bw6761.NewGTEl(res), + } + ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &circuit) + if err != nil { + panic(err) + } else { + fmt.Println("compiled") + } + pk, vk, err := groth16.Setup(ccs) + if err != nil { + panic(err) + } else { + fmt.Println("setup done") + } + secretWitness, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) + if err != nil { + panic(err) + } else { + fmt.Println("secret witness") + } + publicWitness, err := secretWitness.Public() + if err != nil { + panic(err) + } else { + fmt.Println("public witness") + } + proof, err := groth16.Prove(ccs, pk, secretWitness) + if err != nil { + panic(err) + } else { + fmt.Println("proof") + } + err = groth16.Verify(proof, vk, publicWitness) + if err != nil { + panic(err) + } else { + fmt.Println("verify") + } +} + +func randomG1G2Affines() (p bw6761.G1Affine, q bw6761.G2Affine, err error) { + _, _, G1AffGen, G2AffGen := bw6761.Generators() + mod := bw6761.ID.ScalarField() + s1, err := rand.Int(rand.Reader, mod) + if err != nil { + return p, q, err + } + s2, err := rand.Int(rand.Reader, mod) + if err != nil { + return p, q, err + } + p.ScalarMultiplication(&G1AffGen, s1) + q.ScalarMultiplication(&G2AffGen, s2) + return +} diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 74fd984892..5d5139fab3 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -11,29 +11,15 @@ import ( "github.com/consensys/gnark/std/math/emulated" ) -type curveF emulated.Field[emulated.BW6761Fp] - type Pairing struct { *fields_bw6761.Ext6 curveF *emulated.Field[emulated.BW6761Fp] } -func NewPairing(api frontend.API) (*Pairing, error) { - ba, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - return nil, fmt.Errorf("new base api: %w", err) - } - return &Pairing{ - Ext6: fields_bw6761.NewExt6(ba), - curveF: ba, - }, nil -} - -// GT target group of the pairing -type GT = fields_bw6761.E6 +type GTEl = fields_bw6761.E6 -func NewGT(v bw6761.GT) GT { - return GT{ +func NewGTEl(v bw6761.GT) GTEl { + return GTEl{ B0: fields_bw6761.E3{ A0: emulated.ValueOf[emulated.BW6761Fp](v.B0.A0), A1: emulated.ValueOf[emulated.BW6761Fp](v.B0.A1), @@ -47,34 +33,37 @@ func NewGT(v bw6761.GT) GT { } } -// Pair calculates the reduced pairing for a set of points e(P, Q). -// -// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. -func (pr Pairing) Pair(P *G1Affine, Q *G2Affine) (*GT, error) { - f, err := pr.MillerLoop(P, Q) +func NewPairing(api frontend.API) (*Pairing, error) { + ba, err := emulated.NewField[emulated.BW6761Fp](api) if err != nil { - return nil, err + return nil, fmt.Errorf("new base api: %w", err) } - return pr.FinalExponentiation(f), nil + return &Pairing{ + Ext6: fields_bw6761.NewExt6(ba), + curveF: ba, + }, nil } -// FinalExponentiation computes the exponentiation (∏ᵢ zᵢ)ᵈ -// where d = (p^6-1)/r = (p^6-1)/Φ_6(p) ⋅ Φ_6(p)/r = (p^3-1)(p+1)(p^2 - p +1)/r -// we use instead d=s ⋅ (p^3-1)(p+1)(p^2 - p +1)/r -// where s is the cofactor 12(x_0+1) (El Housni and Guillevic) -func (pr Pairing) FinalExponentiation(z *GT) *GT { +// 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 12(x₀+1) (El Housni and Guillevic) +// https://eprint.iacr.org/2020/351.pdf +func (pr Pairing) FinalExponentiation(z *GTEl) *GTEl { result := pr.Copy(z) - // Easy part - // (p^3-1)(p+1) + // 1. Easy part + // (p³-1)(p+1) buf := pr.Conjugate(result) - result = pr.Inverse(result) - buf = pr.Mul(buf, result) + buf = pr.DivUnchecked(buf, result) result = pr.Frobenius(buf) result = pr.Mul(result, buf) - // Hard part (up to permutation) + // 2. Hard part (up to permutation) + // 12(x₀+1)(p²-p+1)/r // El Housni and Guillevic // https://eprint.iacr.org/2020/351.pdf m1 := pr.Expt(result) @@ -87,9 +76,10 @@ func (pr Pairing) FinalExponentiation(z *GT) *GT { f0 = pr.Mul(f0, m2) m2 = pr.CyclotomicSquare(_m1) f0 = pr.Mul(f0, m2) - f0_36 := pr.CyclotomicSquare(f0) - f0_36 = pr.CyclotomicSquare(f0_36) - f0_36 = pr.CyclotomicSquare(f0_36) + f0_36 := pr.CyclotomicSquareCompressed(f0) + f0_36 = pr.CyclotomicSquareCompressed(f0_36) + f0_36 = pr.CyclotomicSquareCompressed(f0_36) + f0_36 = pr.DecompressKarabina(f0_36) f0_36 = pr.Mul(f0_36, f0) f0_36 = pr.CyclotomicSquare(f0_36) f0_36 = pr.CyclotomicSquare(f0_36) @@ -130,7 +120,7 @@ func (pr Pairing) FinalExponentiation(z *GT) *GT { gC = pr.Mul(gC, g4) // ht, hy = 13, 9 - // c1 = ht**2+3*hy**2 = 412 + // c1 = ht²+3hy² = 412 h1 := pr.Expc1(gA) // c2 = ht+hy = 22 h2 := pr.Expc2(gB) @@ -145,24 +135,55 @@ func (pr Pairing) FinalExponentiation(z *GT) *GT { return result } -// lineEvaluation represents a sparse Fp12 Elmt (result of the line evaluation) +// lineEvaluation represents a sparse Fp6 Elmt (result of the line evaluation) // line: 1 + R0(x/y) + R1(1/y) = 0 instead of R0'*y + R1'*x + R2' = 0 This -// makes the multiplication by lines (MulBy034) and between lines (Mul034By034) -// circuit-efficient. +// makes the multiplication by lines (MulBy014) circuit-efficient. type lineEvaluation struct { R0, R1 emulated.Element[emulated.BW6761Fp] } +// Pair calculates the reduced pairing for a set of points +// ∏ᵢ e(Pᵢ, Qᵢ). +// +// This function doesn't check that the inputs are in the correct subgroup. See IsInSubGroup. +func (pr Pairing) Pair(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { + f, err := pr.MillerLoop(P, Q) + if err != nil { + return nil, err + } + return pr.FinalExponentiation(f), nil +} + +// PairingCheck calculates the reduced pairing for a set of points and asserts if the result is One +// ∏ᵢ e(Pᵢ, Qᵢ) =? 1 +// +// This function doesn't check that the inputs are in the correct subgroups. +func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { + f, err := pr.Pair(P, Q) + if err != nil { + return err + + } + one := pr.One() + pr.AssertIsEqual(f, one) + + return nil +} + +func (pr Pairing) AssertIsEqual(x, y *GTEl) { + pr.Ext6.AssertIsEqual(x, y) +} + // seed x₀=9586122913090633729 // -// x₀+1 in binary +// loopCounter1 = x₀+1 in binary var loopCounter1 = [64]int8{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, } -// (x₀-1)^2 in 2-NAF +// loopCounter2 = (x₀-1)² in 2-NAF var loopCounter2 = [127]int8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -172,26 +193,26 @@ var loopCounter2 = [127]int8{ 0, 0, 1, } -// MillerLoop computes the Miller loop -// -// BW6-761 Miller loop: Eq (4') in [bw6-post] +// millerLoopSingle computes the Miller loop // -// f_{u+1,Q}(P) * (f_{u+1})^q_{u^2-2u-1,[u+1]Q}(P) * l^q_{[(u+1)(u^2-2u+1)]Q,-Q}(P) +// f_{u+1,Q}(P) * (f_{u+1})^q_{u²-2u-1,[u+1]Q}(P) * l^q_{[(u+1)(u²-2u+1)]Q,-Q}(P) // -// [https://hackmd.io/@gnark/BW6-761-changes] -func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { +// Eq (4') in https://hackmd.io/@gnark/BW6-761-changes +func (pr Pairing) millerLoopSingle(P *G1Affine, Q *G2Affine) (*GTEl, error) { var l1, l2 *lineEvaluation var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] - // f1 = f_{u+1,Q}(P) + // 1. f1 = f_{u+1,Q}(P) res1 := pr.Ext6.One() Qacc := Q yInv = pr.curveF.Inverse(&P.Y) xNegOverY = pr.curveF.MulMod(&P.X, yInv) xNegOverY = pr.curveF.Neg(xNegOverY) - // i = 62 + // i = 62, separately to avoid an E6 Square + // (Square(res) = 1² = 1) + // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc Qacc, l1 = pr.doubleStep(Qacc) // line evaluation at P @@ -201,8 +222,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { res1.B1.A1 = *pr.curveF.One() for i := 61; i >= 0; i-- { - // mutualize the square among n Miller loops - // (∏ᵢfᵢ)² + // f² res1 = pr.Square(res1) if loopCounter1[i] == 0 { @@ -233,18 +253,17 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } } - // l = l_{uQ,Q}(P) - res1Old := res1 + // Cache values for the second Miller loop + res1Cached := res1 res1Inv := pr.Conjugate(res1) uQ := Qacc - // f2 = f_{u^2-2u+1,uQ}(P) - res2 := res1Old + // 2. f2 = f_{u²-2u+1,uQ}(P) + res2 := res1Cached uQNeg := &G2Affine{X: uQ.X, Y: *pr.curveF.Neg(&uQ.Y)} for i := 125; i >= 0; i-- { - // mutualize the square among n Miller loops - // (∏ᵢfᵢ)² + // f² res2 = pr.Square(res2) switch loopCounter2[i] { @@ -273,7 +292,7 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) - res2 = pr.Mul(res2, res1Old) + res2 = pr.Mul(res2, res1Cached) case -1: // Qacc ← 2Qacc-uQ, @@ -297,20 +316,51 @@ func (pr Pairing) MillerLoop(P *G1Affine, Q *G2Affine) (*GT, error) { } } - // l_{(u+1)vQ,-Q}(P) + // 3. l_{(u+1)vQ,-Q}(P) QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} l1 = pr.lineCompute(Qacc, QNeg) l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) + + // f2 = f2 * l_{(u+1)vQ,-Q}(P) res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) - // f1 * f2^q + // 4. f1 * f2^q res2 = pr.Frobenius(res2) res := pr.Mul(res1, res2) return res, nil } +// MillerLoop computes the multi-Miller loop +func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { + + // check input size match + n := len(P) + if n == 0 || n != len(Q) { + return nil, errors.New("invalid inputs sizes") + } + + res := pr.Ext6.One() + + // k = 0 + res, err := pr.millerLoopSingle(P[0], Q[0]) + if err != nil { + return >El{}, err + } + + for k := 1; k < n; k++ { + m, err := pr.millerLoopSingle(P[k], Q[k]) + if err != nil { + return >El{}, err + } + res = pr.Mul(res, m) + } + + return res, nil + +} + // doubleAndAddStep doubles p1 and adds p2 to the result in affine coordinates, and evaluates the line in Miller loop // https://eprint.iacr.org/2022/1162 (Section 6.1) func (pr Pairing) doubleAndAddStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation, *lineEvaluation) { @@ -398,38 +448,6 @@ func (pr Pairing) doubleStep(p1 *G2Affine) (*G2Affine, *lineEvaluation) { } -// addStep adds two points in affine coordinates, and evaluates the line in Miller loop -// https://eprint.iacr.org/2022/1162 (Section 6.1) -func (pr Pairing) addStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation) { - - // compute λ = (y2-y1)/(x2-x1) - p2ypy := pr.curveF.Sub(&p2.Y, &p1.Y) - p2xpx := pr.curveF.Sub(&p2.X, &p1.X) - λ := pr.curveF.Div(p2ypy, p2xpx) - - // xr = λ²-x1-x2 - λλ := pr.curveF.Mul(λ, λ) - p2xpx = pr.curveF.Add(&p1.X, &p2.X) - xr := pr.curveF.Sub(λλ, p2xpx) - - // yr = λ(x1-xr) - y1 - pxrx := pr.curveF.Sub(&p1.X, xr) - λpxrx := pr.curveF.Mul(λ, pxrx) - yr := pr.curveF.Sub(λpxrx, &p1.Y) - - var res G2Affine - res.X = *xr - res.Y = *yr - - var line lineEvaluation - line.R0 = *λ - line.R1 = *pr.curveF.Mul(λ, &p1.X) - line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) - - return &res, &line - -} - // lineCompute computes the line that goes through p1 and p2 but does not compute p1+p2 func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 53d9a738e8..2d0b931bbb 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -1,21 +1,20 @@ package sw_bw6761 import ( + "bytes" "crypto/rand" "fmt" "testing" "github.com/consensys/gnark-crypto/ecc" bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" + "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" - "github.com/consensys/gnark/std/algebra/emulated/fields_bw6761" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/test" ) -const testCurve = ecc.BN254 - func randomG1G2Affines() (bw6761.G1Affine, bw6761.G2Affine) { _, _, G1AffGen, G2AffGen := bw6761.Generators() mod := bw6761.ID.ScalarField() @@ -34,63 +33,38 @@ func randomG1G2Affines() (bw6761.G1Affine, bw6761.G2Affine) { return p, q } -type finalExponentiationBW6761 struct { - A GT - B GT +type FinalExponentiationCircuit struct { + InGt GTEl + Res GTEl } -func (circuit *finalExponentiationBW6761) Define(api frontend.API) error { - pr, err := NewPairing(api) - if err != nil { - panic(err) - } - expected := pr.FinalExponentiation(&circuit.A) +func (c *FinalExponentiationCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) if err != nil { - return err + return fmt.Errorf("new pairing: %w", err) } - - pr.AssertIsEqual(expected, &circuit.B) - + res := pairing.FinalExponentiation(&c.InGt) + pairing.AssertIsEqual(res, &c.Res) return nil } -func TestFinalExponentiationBW6761(t *testing.T) { +func TestFinalExponentiationTestSolve(t *testing.T) { assert := test.NewAssert(t) - // witness values - var ( - a bw6761.G1Affine - b bw6761.G2Affine - c bw6761.GT - r1, _ = rand.Int(rand.Reader, fr.Modulus()) - r2, _ = rand.Int(rand.Reader, fr.Modulus()) - ) - _, _, g1, g2 := bw6761.Generators() - - a.ScalarMultiplication(&g1, r1) - b.ScalarMultiplication(&g2, r2) - c, err := bw6761.MillerLoop([]bw6761.G1Affine{a}, []bw6761.G2Affine{b}) - if err != nil { - panic(err) - } - - d := bw6761.FinalExponentiation(&c) - - witness := finalExponentiationBW6761{ - A: fields_bw6761.FromE6(&c), - B: fields_bw6761.FromE6(&d), + var gt bw6761.GT + gt.SetRandom() + res := bw6761.FinalExponentiation(>) + witness := FinalExponentiationCircuit{ + InGt: NewGTEl(gt), + Res: NewGTEl(res), } - - err = test.IsSolved(&finalExponentiationBW6761{}, &witness, testCurve.ScalarField()) - assert.NoError(err) - - _, err = frontend.Compile(testCurve.ScalarField(), r1cs.NewBuilder, &finalExponentiationBW6761{}, frontend.IgnoreUnconstrainedInputs()) + err := test.IsSolved(&FinalExponentiationCircuit{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } type PairCircuit struct { InG1 G1Affine InG2 G2Affine - Res GT + Res GTEl } func (c *PairCircuit) Define(api frontend.API) error { @@ -98,7 +72,7 @@ func (c *PairCircuit) Define(api frontend.API) error { if err != nil { return fmt.Errorf("new pairing: %w", err) } - res, err := pairing.Pair(&c.InG1, &c.InG2) + res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -114,8 +88,70 @@ func TestPairTestSolve(t *testing.T) { witness := PairCircuit{ InG1: NewG1Affine(p), InG2: NewG2Affine(q), - Res: NewGT(res), + Res: NewGTEl(res), } err = test.IsSolved(&PairCircuit{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } + +// bench +func BenchmarkPairing(b *testing.B) { + + p, q := randomG1G2Affines() + res, err := bw6761.Pair([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) + witness := PairCircuit{ + InG1: NewG1Affine(p), + InG2: NewG2Affine(q), + 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, &PairCircuit{}); 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, &PairCircuit{}); err != nil { + b.Fatal(err) + } + } + }) + buf.Reset() + _, err = ccs.WriteTo(&buf) + if err != nil { + b.Fatal(err) + } + b.Logf("r1cs size: %d (bytes), nb constraints %d, nbInstructions: %d", buf.Len(), ccs.GetNbConstraints(), ccs.GetNbInstructions()) + + b.Run("solve r1cs", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := ccs.Solve(w); err != nil { + b.Fatal(err) + } + } + }) +} From 70715c085df771c86a4ba7111330b8d6b60797a5 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Oct 2023 19:31:18 -0400 Subject: [PATCH 20/38] refactor: consolidate bw6-761 tower + fix GT exp tests --- std/algebra/emulated/fields_bw6761/e3.go | 46 +++-- std/algebra/emulated/fields_bw6761/e3_test.go | 72 ++----- std/algebra/emulated/fields_bw6761/e6.go | 32 ++-- .../emulated/fields_bw6761/e6_pairing.go | 8 +- std/algebra/emulated/fields_bw6761/e6_test.go | 178 ++++-------------- std/algebra/emulated/sw_bw6761/pairing.go | 4 +- 6 files changed, 91 insertions(+), 249 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index f79a6e2678..95ad19f1f8 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -4,6 +4,7 @@ import ( "math/big" bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/math/emulated" ) @@ -15,12 +16,18 @@ type E3 struct { } type Ext3 struct { - fp *curveF + api frontend.API + fp *curveF } -func NewExt3(baseEl *curveF) *Ext3 { +func NewExt3(api frontend.API) *Ext3 { + fp, err := emulated.NewField[emulated.BW6761Fp](api) + if err != nil { + panic(err) + } return &Ext3{ - fp: baseEl, + api: api, + fp: fp, } } @@ -32,7 +39,6 @@ func (e Ext3) Reduce(x *E3) *E3 { return &z } -// SetZero sets an *E3 elmt to zero func (e Ext3) Zero() *E3 { zero := e.fp.Zero() return &E3{ @@ -42,7 +48,6 @@ func (e Ext3) Zero() *E3 { } } -// One sets z to 1 in Montgomery form and returns z func (e Ext3) One() *E3 { one := e.fp.One() zero := e.fp.Zero() @@ -53,7 +58,6 @@ func (e Ext3) One() *E3 { } } -// Neg negates the *E3 number func (e Ext3) Neg(x *E3) *E3 { a0 := e.fp.Neg(&x.A0) a1 := e.fp.Neg(&x.A1) @@ -65,7 +69,6 @@ func (e Ext3) Neg(x *E3) *E3 { } } -// Add adds two elements of *E3 func (e Ext3) Add(x, y *E3) *E3 { a0 := e.fp.Add(&x.A0, &y.A0) a1 := e.fp.Add(&x.A1, &y.A1) @@ -77,7 +80,6 @@ func (e Ext3) Add(x, y *E3) *E3 { } } -// Sub two elements of *E3 func (e Ext3) Sub(x, y *E3) *E3 { a0 := e.fp.Sub(&x.A0, &y.A0) a1 := e.fp.Sub(&x.A1, &y.A1) @@ -89,7 +91,6 @@ func (e Ext3) Sub(x, y *E3) *E3 { } } -// Double doubles an element in *E3 func (e Ext3) Double(x *E3) *E3 { two := big.NewInt(2) a0 := e.fp.MulConst(&x.A0, two) @@ -102,14 +103,13 @@ func (e Ext3) Double(x *E3) *E3 { } } -func MulByNonResidue(fp *curveF, x *baseEl) *baseEl { +func mulFpByNonResidue(fp *curveF, x *baseEl) *baseEl { z := fp.Neg(x) z = fp.MulConst(z, big.NewInt(4)) return z } -// Conjugate conjugates an element in *E3 func (e Ext3) Conjugate(x *E3) *E3 { a1 := e.fp.Neg(&x.A1) return &E3{ @@ -119,7 +119,6 @@ func (e Ext3) Conjugate(x *E3) *E3 { } } -// MulByElement multiplies an element in *E3 by an element in fp func (e Ext3) MulByElement(x *E3, y *baseEl) *E3 { a0 := e.fp.Mul(&x.A0, y) a1 := e.fp.Mul(&x.A1, y) @@ -152,7 +151,7 @@ func (e Ext3) MulBy01(z *E3, c0, c1 *baseEl) *E3 { tmp := e.fp.Add(&z.A1, &z.A2) t0 := e.fp.Mul(c1, tmp) t0 = e.fp.Sub(t0, b) - t0 = MulByNonResidue(e.fp, t0) + t0 = mulFpByNonResidue(e.fp, t0) t0 = e.fp.Add(t0, a) tmp = e.fp.Add(&z.A0, &z.A2) @@ -181,7 +180,7 @@ func (e Ext3) MulBy1(z *E3, c1 baseEl) *E3 { tmp := e.fp.Add(&z.A1, &z.A2) t0 := e.fp.Mul(&c1, tmp) t0 = e.fp.Sub(t0, b) - t0 = MulByNonResidue(e.fp, t0) + t0 = mulFpByNonResidue(e.fp, t0) tmp = e.fp.Add(&z.A0, &z.A1) t1 := e.fp.Mul(&c1, tmp) @@ -203,11 +202,11 @@ func (e Ext3) MulBy12(x *E3, b1, b2 *baseEl) *E3 { c0 = e.fp.Mul(c0, tmp) c0 = e.fp.Sub(c0, t1) c0 = e.fp.Sub(c0, t2) - c0 = MulByNonResidue(e.fp, c0) + c0 = mulFpByNonResidue(e.fp, c0) c1 := e.fp.Add(&x.A0, &x.A1) c1 = e.fp.Mul(c1, b1) c1 = e.fp.Sub(c1, t1) - tmp = MulByNonResidue(e.fp, t2) + tmp = mulFpByNonResidue(e.fp, t2) c1 = e.fp.Add(c1, tmp) tmp = e.fp.Add(&x.A0, &x.A2) c2 := e.fp.Mul(b2, tmp) @@ -240,7 +239,7 @@ func (e Ext3) Mul01By01(c0, c1, d0, d1 *baseEl) *E3 { b := e.fp.Mul(d1, c1) t0 := e.fp.Mul(c1, d1) t0 = e.fp.Sub(t0, b) - t0 = MulByNonResidue(e.fp, t0) + t0 = mulFpByNonResidue(e.fp, t0) t0 = e.fp.Add(t0, a) t2 := e.fp.Mul(c0, d0) t2 = e.fp.Sub(t2, a) @@ -257,7 +256,6 @@ func (e Ext3) Mul01By01(c0, c1, d0, d1 *baseEl) *E3 { } } -// Mul sets z to the *E3-product of x,y, returns z func (e Ext3) Mul(x, y *E3) *E3 { // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf t0 := e.fp.Mul(&x.A0, &y.A0) @@ -269,7 +267,7 @@ func (e Ext3) Mul(x, y *E3) *E3 { c0 = e.fp.Mul(c0, tmp) c0 = e.fp.Sub(c0, t1) c0 = e.fp.Sub(c0, t2) - c0 = MulByNonResidue(e.fp, c0) + c0 = mulFpByNonResidue(e.fp, c0) tmp = e.fp.Add(&x.A0, &x.A2) c2 := e.fp.Add(&y.A0, &y.A2) @@ -282,7 +280,7 @@ func (e Ext3) Mul(x, y *E3) *E3 { c1 = e.fp.Mul(c1, tmp) c1 = e.fp.Sub(c1, t0) c1 = e.fp.Sub(c1, t1) - t2 = MulByNonResidue(e.fp, t2) + t2 = mulFpByNonResidue(e.fp, t2) a0 := e.fp.Add(c0, t0) a1 := e.fp.Add(c1, t2) @@ -295,7 +293,6 @@ func (e Ext3) Mul(x, y *E3) *E3 { } } -// Square sets z to the *E3-product of x,x, returns z func (e Ext3) Square(x *E3) *E3 { // Algorithm 16 from https://eprint.iacr.org/2010/354.pdf @@ -303,7 +300,7 @@ func (e Ext3) Square(x *E3) *E3 { c6 := e.fp.MulConst(&x.A1, big.NewInt(2)) c4 := e.fp.Mul(&x.A0, c6) // x.A0 * xA1 * 2 c5 := e.fp.Mul(&x.A2, &x.A2) - c1 := MulByNonResidue(e.fp, c5) + c1 := mulFpByNonResidue(e.fp, c5) c1 = e.fp.Add(c1, c4) c2 := e.fp.Sub(c4, c5) @@ -312,7 +309,7 @@ func (e Ext3) Square(x *E3) *E3 { c4 = e.fp.Add(c4, &x.A2) c5 = e.fp.Mul(c6, &x.A2) // x.A1 * xA2 * 2 c4 = e.fp.Mul(c4, c4) - c0 := MulByNonResidue(e.fp, c5) + c0 := mulFpByNonResidue(e.fp, c5) c4 = e.fp.Add(c4, c5) c4 = e.fp.Sub(c4, c3) @@ -377,11 +374,10 @@ func (e Ext3) MulByNonResidue(x *E3) *E3 { A1: x.A0, A2: x.A1, } - z.A0 = *MulByNonResidue(e.fp, &z.A0) + z.A0 = *mulFpByNonResidue(e.fp, &z.A0) return z } -// AssertIsEqual constraint self to be equal to other into the given constraint system func (e Ext3) AssertIsEqual(a, b *E3) { e.fp.AssertIsEqual(&a.A0, &b.A0) e.fp.AssertIsEqual(&a.A1, &b.A1) diff --git a/std/algebra/emulated/fields_bw6761/e3_test.go b/std/algebra/emulated/fields_bw6761/e3_test.go index 18fc7881a1..707be87b67 100644 --- a/std/algebra/emulated/fields_bw6761/e3_test.go +++ b/std/algebra/emulated/fields_bw6761/e3_test.go @@ -16,11 +16,7 @@ type e3Add struct { } func (circuit *e3Add) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Add(&circuit.A, &circuit.B) e.AssertIsEqual(expected, &circuit.C) return nil @@ -49,11 +45,7 @@ type e3Sub struct { } func (circuit *e3Sub) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Sub(&circuit.A, &circuit.B) e.AssertIsEqual(expected, &circuit.C) return nil @@ -82,11 +74,7 @@ type e3Neg struct { } func (circuit *e3Neg) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Neg(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -113,11 +101,7 @@ type e3Double struct { } func (circuit *e3Double) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Double(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -144,11 +128,7 @@ type e3Mul struct { } func (circuit *e3Mul) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Mul(&circuit.A, &circuit.B) e.AssertIsEqual(expected, &circuit.C) return nil @@ -177,11 +157,7 @@ type e3MulByNonResidue struct { } func (circuit *e3MulByNonResidue) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.MulByNonResidue(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -211,11 +187,7 @@ type e3MulByElement struct { } func (circuit *e3MulByElement) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.MulByElement(&circuit.A, &circuit.Y) e.AssertIsEqual(expected, &circuit.B) return nil @@ -248,11 +220,7 @@ type e3MulBy01 struct { } func (circuit *e3MulBy01) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.MulBy01(&circuit.A, &circuit.C0, &circuit.C1) e.AssertIsEqual(expected, &circuit.B) return nil @@ -285,11 +253,7 @@ type e3Square struct { } func (circuit *e3Square) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Square(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -316,11 +280,7 @@ type e3Inverse struct { } func (circuit *e3Inverse) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Inverse(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -348,11 +308,7 @@ type e3Div struct { } func (circuit *e3Div) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.DivUnchecked(&circuit.A, &circuit.B) e.AssertIsEqual(expected, &circuit.C) return nil @@ -384,11 +340,7 @@ type e3Conjugate struct { } func (circuit *e3Conjugate) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt3(nfield) + e := NewExt3(api) expected := e.Conjugate(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 201a1b1edb..50f4f28441 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -2,6 +2,7 @@ package fields_bw6761 import ( bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" + "github.com/consensys/gnark/frontend" ) type E6 struct { @@ -19,11 +20,10 @@ func (e Ext6) Reduce(x *E6) *E6 { return &z } -func NewExt6(baseEl *curveF) *Ext6 { - return &Ext6{Ext3: NewExt3(baseEl)} +func NewExt6(api frontend.API) *Ext6 { + return &Ext6{Ext3: NewExt3(api)} } -// SetZero sets an *E3 elmt to zero func (e Ext6) Zero() *E6 { b0 := e.Ext3.Zero() b1 := e.Ext3.Zero() @@ -33,7 +33,6 @@ func (e Ext6) Zero() *E6 { } } -// One sets z to 1 in Montgomery form and returns z func (e Ext6) One() *E6 { return &E6{ B0: *e.Ext3.One(), @@ -41,7 +40,6 @@ func (e Ext6) One() *E6 { } } -// Add set z=x+y in *E6 and return z func (e Ext6) Add(x, y *E6) *E6 { return &E6{ B0: *e.Ext3.Add(&x.B0, &y.B0), @@ -49,7 +47,6 @@ func (e Ext6) Add(x, y *E6) *E6 { } } -// Sub sets z to x sub y and return z func (e Ext6) Sub(x, y *E6) *E6 { return &E6{ B0: *e.Ext3.Sub(&x.B0, &y.B0), @@ -57,7 +54,6 @@ func (e Ext6) Sub(x, y *E6) *E6 { } } -// Double sets z=2*x and returns z func (e Ext6) Double(x *E6) *E6 { return &E6{ B0: *e.Ext3.Double(&x.B0), @@ -65,7 +61,6 @@ func (e Ext6) Double(x *E6) *E6 { } } -// Mul set z=x*y in *E6 and return z func (e Ext6) Mul(x, y *E6) *E6 { x = e.Reduce(x) y = e.Reduce(y) @@ -86,7 +81,6 @@ func (e Ext6) Mul(x, y *E6) *E6 { } } -// Square set z=x*x in *E6 and return z func (e Ext6) Square(x *E6) *E6 { x = e.Reduce(x) @@ -139,7 +133,7 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { t[2] = e.fp.Mul(&x.B1.A0, &x.B1.A0) // t6 = 2 * nr * g1 * g5 - t[6] = MulByNonResidue(e.fp, t[5]) + t[6] = mulFpByNonResidue(e.fp, t[5]) // t5 = 4 * nr * g1 * g5 + 2 * g3 t[5] = e.fp.Add(t[6], &x.B1.A0) t[5] = e.fp.Add(t[5], t[5]) @@ -147,7 +141,7 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { z.B1.A0 = *e.fp.Add(t[5], t[6]) // t4 = nr * g5² - t[4] = MulByNonResidue(e.fp, t[1]) + t[4] = mulFpByNonResidue(e.fp, t[1]) // t5 = nr * g5² + g1² t[5] = e.fp.Add(t[0], t[4]) // t6 = nr * g5² + g1² - g2 @@ -162,7 +156,7 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { z.B0.A2 = *e.fp.Add(t[6], t[5]) // t4 = nr * g2² - t[4] = MulByNonResidue(e.fp, t[1]) + t[4] = mulFpByNonResidue(e.fp, t[1]) // t5 = g3² + nr * g2² t[5] = e.fp.Add(t[2], t[4]) // t6 = g3² + nr * g2² - g1 @@ -214,7 +208,7 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { t[1] = e.fp.Add(t[1], t[0]) // t0 = E * g5^2 + t1 t[2] = e.fp.Mul(&x.B1.A2, &x.B1.A2) - t[0] = MulByNonResidue(e.fp, t[2]) + t[0] = mulFpByNonResidue(e.fp, t[2]) t[0] = e.fp.Add(t[0], t[1]) // t1 = 1/(4 * g3) t[1] = e.fp.Add(&x.B1.A0, &x.B1.A0) @@ -236,7 +230,7 @@ func (e Ext6) DecompressKarabina(x *E6) *E6 { // c₀ = E * (2 * g4² + g3 * g5 - 3 * g2 * g1) + 1 t[2] = e.fp.Add(t[2], t[1]) - z.B0.A0 = *MulByNonResidue(e.fp, t[2]) + z.B0.A0 = *mulFpByNonResidue(e.fp, t[2]) z.B0.A0 = *e.fp.Add(&z.B0.A0, one) z.B0.A1 = x.B0.A1 @@ -280,13 +274,13 @@ func (e Ext6) CyclotomicSquare(x *E6) *E6 { t[8] = e.fp.Mul(t[8], t[8]) t[8] = e.fp.Sub(t[8], t[4]) t[8] = e.fp.Sub(t[8], t[5]) - t[8] = MulByNonResidue(e.fp, t[8]) // 2*x5*x1*u + t[8] = mulFpByNonResidue(e.fp, t[8]) // 2*x5*x1*u - t[0] = MulByNonResidue(e.fp, t[0]) + t[0] = mulFpByNonResidue(e.fp, t[0]) t[0] = e.fp.Add(t[0], t[1]) // x4²*u + x0² - t[2] = MulByNonResidue(e.fp, t[2]) + t[2] = mulFpByNonResidue(e.fp, t[2]) t[2] = e.fp.Add(t[2], t[3]) // x2²*u + x3² - t[4] = MulByNonResidue(e.fp, t[4]) + t[4] = mulFpByNonResidue(e.fp, t[4]) t[4] = e.fp.Add(t[4], t[5]) // x5²*u + x1² var z E6 @@ -354,7 +348,6 @@ func (e Ext6) DivUnchecked(x, y *E6) *E6 { } -// Conjugate set z to x conjugated and return z func (e Ext6) Conjugate(x *E6) *E6 { return &E6{ B0: x.B0, @@ -362,7 +355,6 @@ func (e Ext6) Conjugate(x *E6) *E6 { } } -// AssertIsEqual constraint self to be equal to other into the given constraint system func (e Ext6) AssertIsEqual(a, b *E6) { e.Ext3.AssertIsEqual(&a.B0, &b.B0) e.Ext3.AssertIsEqual(&a.B1, &b.B1) diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 9fe5146715..977427937b 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -9,7 +9,7 @@ func (e Ext6) nSquareCompressed(z *E6, n int) *E6 { return z } -// Expt set z to x^t in *E6 and return z +// Expt set x to x^t in E6 and return x func (e Ext6) Expt(x *E6) *E6 { x = e.Reduce(x) @@ -41,7 +41,7 @@ func (e Ext6) Expt(x *E6) *E6 { return result } -// Expc2 set z to x^c2 in *E6 and return z +// Expc2 set x to x^c2 in E6 and return x // ht, hy = 13, 9 // c1 = ht+hy = 22 (10110) func (e Ext6) Expc2(x *E6) *E6 { @@ -57,7 +57,7 @@ func (e Ext6) Expc2(x *E6) *E6 { return result } -// Expc1 set z to x^c1 in *E6 and return z +// Expc1 set x to x^c1 in E6 and return x // ht, hy = 13, 9 // c1 = ht**2+3*hy**2 = 412 (110011100) func (e Ext6) Expc1(x *E6) *E6 { @@ -92,7 +92,7 @@ func (e *Ext6) MulBy014(z *E6, c0, c1 *baseEl) *E6 { var b E3 // Mul by E3{0, 1, 0} - b.A0 = *MulByNonResidue(e.fp, &z.B1.A2) + b.A0 = *mulFpByNonResidue(e.fp, &z.B1.A2) b.A2 = z.B1.A1 b.A1 = z.B1.A0 diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index ef90689297..d0a035eb5e 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -17,11 +17,7 @@ type e6Add struct { func (circuit *e6Add) Define(api frontend.API) error { var expected E6 - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected = *e.Add(&circuit.A, &circuit.B) e.AssertIsEqual(&expected, &circuit.C) return nil @@ -51,11 +47,7 @@ type e6Sub struct { func (circuit *e6Sub) Define(api frontend.API) error { var expected E6 - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected = *e.Sub(&circuit.A, &circuit.B) e.AssertIsEqual(&expected, &circuit.C) return nil @@ -85,11 +77,7 @@ type e6Double struct { func (circuit *e6Double) Define(api frontend.API) error { var expected E6 - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected = *e.Double(&circuit.A) e.AssertIsEqual(&expected, &circuit.B) return nil @@ -117,11 +105,7 @@ type e6Mul struct { func (circuit *e6Mul) Define(api frontend.API) error { var expected E6 - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected = *e.Mul(&circuit.A, &circuit.B) e.AssertIsEqual(&expected, &circuit.C) return nil @@ -141,8 +125,6 @@ func TestMulFp6(t *testing.T) { C: FromE6(&c), } - // add=72955 equals=1098 fromBinary=0 mul=71442 sub=1049 toBinary=0 - // counters add=34695 equals=582 fromBinary=0 mul=32806 sub=903 toBinary=0 err := test.IsSolved(&e6Mul{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -153,11 +135,7 @@ type e6Square struct { func (circuit *e6Square) Define(api frontend.API) error { var expected E6 - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected = *e.Square(&circuit.A) e.AssertIsEqual(&expected, &circuit.B) return nil @@ -175,8 +153,6 @@ func TestSquareFp6(t *testing.T) { B: FromE6(&b), } - // add=51966 equals=768 fromBinary=0 mul=50706 sub=780 toBinary=0 - // add=29636 equals=456 fromBinary=0 mul=28014 sub=686 toBinary=0 err := test.IsSolved(&e6Square{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -187,11 +163,7 @@ type e6Inverse struct { func (circuit *e6Inverse) Define(api frontend.API) error { var expected E6 - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected = *e.Inverse(&circuit.A) e.AssertIsEqual(&expected, &circuit.B) return nil @@ -209,8 +181,6 @@ func TestInverseFp6(t *testing.T) { B: FromE6(&b), } - // add=136669 equals=2033 fromBinary=0 mul=134924 sub=1691 toBinary=0 - // add=114515 equals=1721 fromBinary=0 mul=112628 sub=1617 toBinary=0 err := test.IsSolved(&e6Inverse{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -220,11 +190,7 @@ type e6Div struct { } func (circuit *e6Div) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected := e.DivUnchecked(&circuit.A, &circuit.B) e.AssertIsEqual(expected, &circuit.C) return nil @@ -257,11 +223,7 @@ type e6Conjugate struct { func (circuit *e6Conjugate) Define(api frontend.API) error { var expected E6 - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected = *e.Conjugate(&circuit.A) e.AssertIsEqual(&expected, &circuit.B) return nil @@ -279,7 +241,6 @@ func TestConjugateFp6(t *testing.T) { B: FromE6(&b), } - // add=7095 equals=108 fromBinary=0 mul=6990 sub=165 toBinary=0 err := test.IsSolved(&e6Conjugate{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -289,11 +250,7 @@ type e6CyclotomicSquareCompressed struct { } func (circuit *e6CyclotomicSquareCompressed) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected := e.CyclotomicSquareCompressed(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -312,8 +269,6 @@ func TestCyclotomicSquareCompressedFp6(t *testing.T) { B: FromE6(&b), } - // add=28975 equals=438 fromBinary=0 mul=28342 sub=401 toBinary=0 - // add=19987 equals=298 fromBinary=0 mul=19064 sub=339 toBinary=0 err := test.IsSolved(&e6CyclotomicSquareCompressed{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -323,11 +278,7 @@ type e6DecompressKarabina struct { } func (circuit *e6DecompressKarabina) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected := e.DecompressKarabina(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -346,8 +297,6 @@ func TestDecompressKarabinaFp6(t *testing.T) { B: FromE6(&a), } - // add=28723 equals=438 fromBinary=0 mul=28342 sub=389 toBinary=0 - // add=17214 equals=284 fromBinary=0 mul=16709 sub=289 toBinary=0 err := test.IsSolved(&e6DecompressKarabina{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -357,11 +306,7 @@ type e6CyclotomicSquare struct { } func (circuit *e6CyclotomicSquare) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected := e.CyclotomicSquare(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -380,66 +325,16 @@ func TestCyclotomicSquareFp6(t *testing.T) { B: FromE6(&b), } - // add=39871 equals=603 fromBinary=0 mul=39018 sub=563 toBinary=0 - // add=26229 equals=393 fromBinary=0 mul=24991 sub=495 toBinary=0 err := test.IsSolved(&e6CyclotomicSquare{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -/* -func TestNewE6(t *testing.T) { - a, _ := new(big.Int).SetString("83175370704474795125412693555818269399912070346366058924020987848926901443521059146219467322598189008118890021654143123310841437365188932207798122475953021372633091598654279100089387195482601214045864119525747542050698192923485116081505909964897146420", 10) - b, _ := new(big.Int).SetString("6368022403585149186567793239866157016295592880888573809019876686976707722559034074218497709896494419772477540172749411175273320318562448286368763367020957539305330983642372720448125982288656809793421178887827471755589212191192898758939906986677524020", 10) - aMod := new(big.Int).Mod(a, fp.Modulus()) - bMod := new(big.Int).Mod(b, fp.Modulus()) - fmt.Println(aMod.String() == bMod.String()) - fmt.Println(a.BitLen()) - -} - -func TestKp(t *testing.T) { - k, _ := new(big.Int).SetString("85056971769626083370706587971739925665000858406518962290778652625791906552342223597311466786558418076119513109067357166262376397813915389059478465293248265362213566556434506971085077824793753991611718645003559647894773349413422987352", 10) - kp := new(big.Int).Mul(k, fp.Modulus()) - fmt.Println(kp.String()) -} - -func TestCreateLimbs(t *testing.T) { - nbBits := 70 - input := big.NewInt(48) - size := input.BitLen() / nbBits - res := make([]*big.Int, size+1) - for i := 0; i < size+1; i++ { - res[i] = new(big.Int) - } - base := new(big.Int).Lsh(big.NewInt(1), uint(nbBits)) - fmt.Println("base:", base.String()) - r, _ := new(big.Int).SetString("21888242871839275222246405745257275088459989201842526013704146201830519410949", 10) - fmt.Println(new(big.Int).Sub(ecc.BN254.ScalarField(), r).String()) - tmp := new(big.Int).Set(input) - for i := 0; i < len(res); i++ { - res[i].Mod(tmp, base) - tmp.Rsh(tmp, uint(nbBits)) - } - var buf bytes.Buffer - for i := 0; i < size+1; i++ { - buf.WriteString(res[i].String()) - if i != size { - buf.WriteString("+") - } - } - fmt.Println(buf.String()) -} - type e6Expt struct { A, B E6 } func (circuit *e6Expt) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected := e.Expt(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -450,7 +345,14 @@ func TestExptFp6(t *testing.T) { // witness values var a, b bw6761.E6 _, _ = a.SetRandom() - b.Set(&a) + + // put a in the cyclotomic subgroup + var tmp bw6761.E6 + tmp.Conjugate(&a) + a.Inverse(&a) + tmp.Mul(&tmp, &a) + a.Frobenius(&tmp).Mul(&a, &tmp) + b.Expt(&a) witness := e6Expt{ @@ -467,11 +369,7 @@ type e6Expc2 struct { } func (circuit *e6Expc2) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected := e.Expc2(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -482,7 +380,14 @@ func TestExpc2Fp6(t *testing.T) { // witness values var a, b bw6761.E6 _, _ = a.SetRandom() - b.Set(&a) + + // put a in the cyclotomic subgroup + var tmp bw6761.E6 + tmp.Conjugate(&a) + a.Inverse(&a) + tmp.Mul(&tmp, &a) + a.Frobenius(&tmp).Mul(&a, &tmp) + b.Expc2(&a) witness := e6Expc2{ @@ -490,8 +395,6 @@ func TestExpc2Fp6(t *testing.T) { B: FromE6(&b), } - // add=287618 equals=4068 fromBinary=0 mul=281540 sub=3690 toBinary=0 - // add=197836 equals=3048 fromBinary=0 mul=188488 sub=3810 toBinary=0 err := test.IsSolved(&e6Expc2{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } @@ -501,11 +404,7 @@ type e6Expc1 struct { } func (circuit *e6Expc1) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) expected := e.Expc1(&circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil @@ -516,7 +415,14 @@ func TestExpc1Fp6(t *testing.T) { // witness values var a, b bw6761.E6 _, _ = a.SetRandom() - b.Set(&a) + + // put a in the cyclotomic subgroup + var tmp bw6761.E6 + tmp.Conjugate(&a) + a.Inverse(&a) + tmp.Mul(&tmp, &a) + a.Frobenius(&tmp).Mul(&a, &tmp) + b.Expc1(&a) witness := e6Expc1{ @@ -524,11 +430,9 @@ func TestExpc1Fp6(t *testing.T) { B: FromE6(&b), } - // add=578954 equals=8028 fromBinary=0 mul=566870 sub=7248 toBinary=0 err := test.IsSolved(&e6Expc1{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } -*/ type e6MulBy014 struct { A E6 `gnark:",public"` @@ -537,11 +441,7 @@ type e6MulBy014 struct { } func (circuit *e6MulBy014) Define(api frontend.API) error { - nfield, err := emulated.NewField[emulated.BW6761Fp](api) - if err != nil { - panic(err) - } - e := NewExt6(nfield) + e := NewExt6(api) res := e.MulBy014(&circuit.A, &circuit.B, &circuit.C) e.AssertIsEqual(res, &circuit.W) return nil diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 5d5139fab3..af66c8492a 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -12,6 +12,7 @@ import ( ) type Pairing struct { + api frontend.API *fields_bw6761.Ext6 curveF *emulated.Field[emulated.BW6761Fp] } @@ -39,7 +40,8 @@ func NewPairing(api frontend.API) (*Pairing, error) { return nil, fmt.Errorf("new base api: %w", err) } return &Pairing{ - Ext6: fields_bw6761.NewExt6(ba), + api: api, + Ext6: fields_bw6761.NewExt6(api), curveF: ba, }, nil } From 8e95140aa8d737f04e762f12704209966b32e4ac Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 5 Oct 2023 20:43:02 -0400 Subject: [PATCH 21/38] refactor(bw6 pairing): use MillerLoopOptAte in gnark-crypto for test --- std/algebra/emulated/sw_bw6761/pairing_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 2d0b931bbb..b8ebe00a14 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -83,7 +83,8 @@ func (c *PairCircuit) Define(api frontend.API) error { func TestPairTestSolve(t *testing.T) { assert := test.NewAssert(t) p, q := randomG1G2Affines() - res, err := bw6761.Pair([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) + res, err := bw6761.MillerLoopOptAte([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) + res = bw6761.FinalExponentiation(&res) assert.NoError(err) witness := PairCircuit{ InG1: NewG1Affine(p), From 7fc5925c4ba24f62ae002c01339ae3735100e8ac Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Oct 2023 13:13:40 -0400 Subject: [PATCH 22/38] feat(emulated bw6 pairing): optimal tate version working --- .../emulated/fields_bw6761/e6_pairing.go | 153 +++++++++++++++ std/algebra/emulated/sw_bw6761/pairing.go | 177 ++++++++++++++++++ .../emulated/sw_bw6761/pairing_test.go | 35 ++++ 3 files changed, 365 insertions(+) diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 977427937b..a5e18395a1 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -177,3 +177,156 @@ func (e *Ext6) MulBy01245(z *E6, x *[5]baseEl) *E6 { B1: *z1, } } + +// Square034 squares an E6 sparse element of the form +// +// E6{ +// B0: E3{A0: 1, A1: 0, A2: 0}, +// B1: E3{A0: c3, A1: c4, A2: 0}, +// } +func (e *Ext6) Square034(x *E6) *E6 { + c0 := E3{ + A0: *e.fp.Sub(&x.B0.A0, &x.B1.A0), + A1: *e.fp.Neg(&x.B1.A1), + A2: *e.fp.Zero(), + } + + c3 := E3{ + A0: x.B0.A0, + A1: *e.fp.Neg(&x.B1.A0), + A2: *e.fp.Neg(&x.B1.A1), + } + + c2 := E3{ + A0: x.B1.A0, + A1: x.B1.A1, + A2: *e.fp.Zero(), + } + c3 = *e.MulBy01(&c3, &c0.A0, &c0.A1) + c3 = *e.Ext3.Add(&c3, &c2) + + var z E6 + z.B1.A0 = *e.fp.Add(&c2.A0, &c2.A0) + z.B1.A1 = *e.fp.Add(&c2.A1, &c2.A1) + + z.B0.A0 = c3.A0 + z.B0.A1 = *e.fp.Add(&c3.A1, &c2.A0) + z.B0.A2 = *e.fp.Add(&c3.A2, &c2.A1) + + return &z +} + +// MulBy034 multiplies z by an E6 sparse element of the form +// +// E6{ +// B0: E3{A0: 1, A1: 0, A2: 0}, +// B1: E3{A0: c3, A1: c4, A2: 0}, +// } +func (e *Ext6) MulBy034(z *E6, c3, c4 *baseEl) *E6 { + + a := z.B0 + b := z.B1 + b = *e.MulBy01(&b, c3, c4) + c3 = e.fp.Add(e.fp.One(), c3) + d := e.Ext3.Add(&z.B0, &z.B1) + d = e.MulBy01(d, c3, c4) + + zC1 := e.Ext3.Add(&a, &b) + zC1 = e.Ext3.Neg(zC1) + zC1 = e.Ext3.Add(zC1, d) + zC0 := e.Ext3.MulByNonResidue(&b) + zC0 = e.Ext3.Add(zC0, &a) + + return &E6{ + B0: *zC0, + B1: *zC1, + } +} + +// multiplies two E6 sparse element of the form: +// +// E6{ +// C0: E6{B0: 1, B1: 0, B2: 0}, +// C1: E6{B0: c3, B1: c4, B2: 0}, +// } +// +// and +// +// E6{ +// C0: E6{B0: 1, B1: 0, B2: 0}, +// C1: E6{B0: d3, B1: d4, B2: 0}, +// } +func (e *Ext6) Mul034By034(d3, d4, c3, c4 *baseEl) *[5]baseEl { + x3 := e.fp.Mul(c3, d3) + x4 := e.fp.Mul(c4, d4) + x04 := e.fp.Add(c4, d4) + x03 := e.fp.Add(c3, d3) + tmp := e.fp.Add(c3, c4) + x34 := e.fp.Add(d3, d4) + x34 = e.fp.Mul(x34, tmp) + x34 = e.fp.Sub(x34, x3) + x34 = e.fp.Sub(x34, x4) + + zC0B0 := mulFpByNonResidue(e.fp, x4) + zC0B0 = e.fp.Add(zC0B0, e.fp.One()) + zC0B1 := x3 + zC0B2 := x34 + zC1B0 := x03 + zC1B1 := x04 + + return &[5]baseEl{*zC0B0, *zC0B1, *zC0B2, *zC1B0, *zC1B1} +} + +// MulBy01234 multiplies z by an E6 sparse element of the form +// +// E6{ +// C0: E3{A0: c0, A1: c1, A2: c2}, +// C1: E3{A0: c3, A1: c4, A2: 0}, +// } +func (e *Ext6) MulBy01234(z *E6, x *[5]baseEl) *E6 { + c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} + c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} + a := e.Ext3.Add(&z.B0, &z.B1) + b := e.Ext3.Add(c0, c1) + a = e.Ext3.Mul(a, b) + b = e.Ext3.Mul(&z.B0, c0) + c := e.Ext3.MulBy01(&z.B1, &x[3], &x[4]) + z1 := e.Ext3.Sub(a, b) + z1 = e.Ext3.Sub(z1, c) + z0 := e.Ext3.MulByNonResidue(c) + z0 = e.Ext3.Add(z0, b) + return &E6{ + B0: *z0, + B1: *z1, + } +} + +// multiplies two E6 sparse element of the form: +// +// E6{ +// C0: E2{A0: x0, A1: x1, A2: x2}, +// C1: E2{A0: x3, A1: x4, A2: 0}, +// } +// +// and +// +// E6{ +// C0: E3{A0: 1, A1: 0, A2: 0}, +// C1: E3{A0: z3, A1: z4, A2: 0}, +// } +func (e *Ext6) Mul01234By034(x *[5]baseEl, z3, z4 *baseEl) *E6 { + c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} + c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} + a := e.Ext3.Add(e.Ext3.One(), &E3{A0: *z3, A1: *z4, A2: *e.fp.Zero()}) + b := e.Ext3.Add(c0, c1) + a = e.Ext3.Mul(a, b) + c := e.Ext3.Mul01By01(z3, z4, &x[3], &x[4]) + z1 := e.Ext3.Sub(a, c0) + z1 = e.Ext3.Sub(z1, c) + z0 := e.Ext3.MulByNonResidue(c) + z0 = e.Ext3.Add(z0, c0) + return &E6{ + B0: *z0, + B1: *z1, + } +} diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index af66c8492a..5739445a68 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -466,3 +466,180 @@ func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { return &line } + +// --- Alternative Miller loop --- +var thirdRootOneG2 = emulated.ValueOf[emulated.BW6761Fp]("3876905175468200631077310367084681598448315841795389501393935922030716896759491089791062239139884430736136043081596370525752532152533918168748948422532524762769433379258873205270018176434449950195784127083892851850798970002242935133594411783692478449434154543435837344414891700653141782682622592665272535258486114040810216200011591719198498884598067930925845038459634787676665023756334020972459098834655430741989740290995313870292460737326506741396444022500") + +var loopCounterAlt1 = [190]int8{ + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +} +var loopCounterAlt2 = [190]int8{ + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, + 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, + 1, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 1, 0, + 1, 0, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, +} + +func (pr Pairing) MillerLoopAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { + + // precomputations + var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] + yInv = pr.curveF.Inverse(&Q.Y) + xNegOverY = pr.curveF.MulMod(&Q.X, yInv) + xNegOverY = pr.curveF.Neg(xNegOverY) + p0 := &G1Affine{X: P.X, Y: P.Y} + p0neg := &G1Affine{X: p0.X, Y: *pr.curveF.Neg(&p0.Y)} + p1 := &G1Affine{ + X: *pr.curveF.MulMod(&p0.X, &thirdRootOneG2), + Y: *pr.curveF.Neg(&p0.Y), + } + p1neg := &G1Affine{X: p1.X, Y: *pr.curveF.Neg(&p1.Y)} + + // l_{p0,p1}(q) + p01, l01 := pr.addStep(p0, p1) + l01.R0 = *pr.curveF.MulMod(&l01.R0, xNegOverY) + l01.R1 = *pr.curveF.MulMod(&l01.R1, yInv) + p01neg := &G1Affine{X: p01.X, Y: *pr.curveF.Neg(&p01.Y)} + + // l_{p0,-p1}(q) + p10, l10 := pr.addStep(p0, p1neg) + l10.R0 = *pr.curveF.MulMod(&l10.R0, xNegOverY) + l10.R1 = *pr.curveF.MulMod(&l10.R1, yInv) + p10neg := &G1Affine{X: p10.X, Y: *pr.curveF.Neg(&p10.Y)} + + // f_{a0+\lambda*a1,P}(Q) + result := pr.Ext6.One() + + var j int8 + + // i = 188 + var l *lineEvaluation + pAcc, l0 := pr.doubleStep(p1) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) + result = pr.MulBy034(result, &l0.R0, &l0.R1) + + for i := 187; i >= 1; i-- { + // (∏ᵢfᵢ)² + result = pr.Square(result) + + j = loopCounterAlt2[i]*3 + loopCounterAlt1[i] + + pAcc, l0 = pr.doubleStep(pAcc) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) + result = pr.MulBy034(result, &l0.R0, &l0.R1) + + switch j { + case -4: + pAcc, l = pr.addStep(pAcc, p01neg) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + result = pr.MulBy034(result, &l01.R0, &l01.R1) + case -3: + pAcc, l = pr.addStep(pAcc, p1neg) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + case -2: + pAcc, l = pr.addStep(pAcc, p10) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + result = pr.MulBy034(result, &l01.R0, &l01.R1) + case -1: + pAcc, l = pr.addStep(pAcc, p0neg) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + case 0: + continue + case 1: + pAcc, l = pr.addStep(pAcc, p0) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + case 2: + pAcc, l = pr.addStep(pAcc, p10neg) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + result = pr.MulBy034(result, &l01.R0, &l01.R1) + case 3: + pAcc, l = pr.addStep(pAcc, p1) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + case 4: + pAcc, l = pr.addStep(pAcc, p01) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv) + result = pr.MulBy034(result, &l.R0, &l.R1) + result = pr.MulBy034(result, &l01.R0, &l01.R1) + default: + return nil, errors.New("invalid loopCounter") + } + } + + // i = 0, j = -3 + result = pr.Square(result) + pAcc, l0 = pr.doubleStep(pAcc) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) + result = pr.MulBy034(result, &l0.R0, &l0.R1) + + return result, nil + +} + +// addStep adds two points in affine coordinates, and evaluates the line in Miller loop +// https://eprint.iacr.org/2022/1162 (Section 6.1) +func (pr Pairing) addStep(p1, p2 *G1Affine) (*G1Affine, *lineEvaluation) { + + // compute λ = (y2-y1)/(x2-x1) + p2ypy := pr.curveF.Sub(&p2.Y, &p1.Y) + p2xpx := pr.curveF.Sub(&p2.X, &p1.X) + λ := pr.curveF.Div(p2ypy, p2xpx) + + // xr = λ²-x1-x2 + λλ := pr.curveF.Mul(λ, λ) + p2xpx = pr.curveF.Add(&p1.X, &p2.X) + xr := pr.curveF.Sub(λλ, p2xpx) + + // yr = λ(x1-xr) - y1 + pxrx := pr.curveF.Sub(&p1.X, xr) + λpxrx := pr.curveF.Mul(λ, pxrx) + yr := pr.curveF.Sub(λpxrx, &p1.Y) + + var res G2Affine + res.X = *xr + res.Y = *yr + + var line lineEvaluation + line.R0 = *λ + line.R1 = *pr.curveF.Mul(λ, &p1.X) + line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) + + return &res, &line + +} + +func (pr Pairing) PairAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { + f, err := pr.MillerLoopAlt(P, Q) + if err != nil { + return nil, err + } + return pr.FinalExponentiation(f), nil +} diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index b8ebe00a14..129548a296 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -67,6 +67,40 @@ type PairCircuit struct { Res GTEl } +func (c *PairCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + res, err := pairing.PairAlt(&c.InG1, &c.InG2) + if err != nil { + return fmt.Errorf("pair: %w", err) + } + pairing.AssertIsEqual(res, &c.Res) + return nil +} + +func TestPairTestSolve(t *testing.T) { + assert := test.NewAssert(t) + p, q := randomG1G2Affines() + res, err := bw6761.Pair([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) + assert.NoError(err) + witness := PairCircuit{ + InG1: NewG1Affine(p), + InG2: NewG2Affine(q), + Res: NewGTEl(res), + } + err = test.IsSolved(&PairCircuit{}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +/* +type PairCircuit struct { + InG1 G1Affine + InG2 G2Affine + Res GTEl +} + func (c *PairCircuit) Define(api frontend.API) error { pairing, err := NewPairing(api) if err != nil { @@ -94,6 +128,7 @@ func TestPairTestSolve(t *testing.T) { err = test.IsSolved(&PairCircuit{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } +*/ // bench func BenchmarkPairing(b *testing.B) { From f66b21ad0f783adf0dcd6ae67852294346d9d237 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Oct 2023 16:15:34 -0400 Subject: [PATCH 23/38] perf(bw6/optimal tate): mul lines 2-by-2 --- std/algebra/emulated/sw_bw6761/pairing.go | 117 ++++++++++++++++------ 1 file changed, 85 insertions(+), 32 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 5739445a68..fd3410b021 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -502,21 +502,26 @@ func (pr Pairing) MillerLoopAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { p0neg := &G1Affine{X: p0.X, Y: *pr.curveF.Neg(&p0.Y)} p1 := &G1Affine{ X: *pr.curveF.MulMod(&p0.X, &thirdRootOneG2), - Y: *pr.curveF.Neg(&p0.Y), + Y: p0neg.Y, } - p1neg := &G1Affine{X: p1.X, Y: *pr.curveF.Neg(&p1.Y)} + p1neg := &G1Affine{X: p1.X, Y: p0.Y} - // l_{p0,p1}(q) + // p01 = p0+p1 and l01 = l_{p0,p1}(q) p01, l01 := pr.addStep(p0, p1) l01.R0 = *pr.curveF.MulMod(&l01.R0, xNegOverY) l01.R1 = *pr.curveF.MulMod(&l01.R1, yInv) p01neg := &G1Affine{X: p01.X, Y: *pr.curveF.Neg(&p01.Y)} - // l_{p0,-p1}(q) - p10, l10 := pr.addStep(p0, p1neg) - l10.R0 = *pr.curveF.MulMod(&l10.R0, xNegOverY) - l10.R1 = *pr.curveF.MulMod(&l10.R1, yInv) - p10neg := &G1Affine{X: p10.X, Y: *pr.curveF.Neg(&p10.Y)} + // p10 = p0-p1 + p10 := &G1Affine{ + X: *pr.curveF.Add(&p0.X, &p1.X), + Y: p1.Y, + } + p10.X = *pr.curveF.Neg(&p10.X) + p10neg := &G1Affine{ + X: p10.X, + Y: p0.Y, + } // f_{a0+\lambda*a1,P}(Q) result := pr.Ext6.One() @@ -525,68 +530,97 @@ func (pr Pairing) MillerLoopAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { // i = 188 var l *lineEvaluation + var prodLines [5]emulated.Element[emulated.BW6761Fp] pAcc, l0 := pr.doubleStep(p1) + result.B1.A0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + result.B1.A1 = *pr.curveF.MulMod(&l0.R1, yInv) + + // i = 187 + result = pr.Square034(result) + pAcc, l0 = pr.doubleStep(pAcc) l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) result = pr.MulBy034(result, &l0.R0, &l0.R1) - for i := 187; i >= 1; i-- { + for i := 186; i >= 1; i-- { // (∏ᵢfᵢ)² result = pr.Square(result) j = loopCounterAlt2[i]*3 + loopCounterAlt1[i] - pAcc, l0 = pr.doubleStep(pAcc) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - result = pr.MulBy034(result, &l0.R0, &l0.R1) - switch j { case -4: - pAcc, l = pr.addStep(pAcc, p01neg) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p01neg) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) result = pr.MulBy034(result, &l01.R0, &l01.R1) case -3: - pAcc, l = pr.addStep(pAcc, p1neg) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p1neg) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) case -2: - pAcc, l = pr.addStep(pAcc, p10) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p10) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) result = pr.MulBy034(result, &l01.R0, &l01.R1) case -1: - pAcc, l = pr.addStep(pAcc, p0neg) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p0neg) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) case 0: - continue + pAcc, l0 = pr.doubleStep(pAcc) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) + result = pr.MulBy034(result, &l0.R0, &l0.R1) case 1: - pAcc, l = pr.addStep(pAcc, p0) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p0) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) case 2: - pAcc, l = pr.addStep(pAcc, p10neg) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p10neg) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) result = pr.MulBy034(result, &l01.R0, &l01.R1) case 3: - pAcc, l = pr.addStep(pAcc, p1) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p1) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) case 4: - pAcc, l = pr.addStep(pAcc, p01) + pAcc, l0, l = pr.doubleAndAddStep(pAcc, p01) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - result = pr.MulBy034(result, &l.R0, &l.R1) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) result = pr.MulBy034(result, &l01.R0, &l01.R1) default: return nil, errors.New("invalid loopCounter") @@ -595,7 +629,7 @@ func (pr Pairing) MillerLoopAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { // i = 0, j = -3 result = pr.Square(result) - pAcc, l0 = pr.doubleStep(pAcc) + l0 = pr.tangentCompute(pAcc) l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) result = pr.MulBy034(result, &l0.R0, &l0.R1) @@ -636,6 +670,25 @@ func (pr Pairing) addStep(p1, p2 *G1Affine) (*G1Affine, *lineEvaluation) { } +// tangentCompute computes the line that goes through p1 and p2 but does not compute p1+p2 +func (pr Pairing) tangentCompute(p1 *G1Affine) *lineEvaluation { + + // λ = 3x²/2y + n := pr.curveF.Mul(&p1.X, &p1.X) + three := big.NewInt(3) + n = pr.curveF.MulConst(n, three) + d := pr.curveF.Add(&p1.Y, &p1.Y) + λ := pr.curveF.Div(n, d) + + var line lineEvaluation + line.R0 = *λ + line.R1 = *pr.curveF.Mul(λ, &p1.X) + line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) + + return &line + +} + func (pr Pairing) PairAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { f, err := pr.MillerLoopAlt(P, Q) if err != nil { From a9b4c1ca636b602e55ca4eae7da9bfb550e12715 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Oct 2023 17:28:15 -0400 Subject: [PATCH 24/38] refactor: keep one version + multi-pairing --- std/algebra/emulated/fields_bw6761/e6.go | 2 + std/algebra/emulated/sw_bw6761/pairing.go | 311 ++++-------------- .../emulated/sw_bw6761/pairing_test.go | 79 ++++- 3 files changed, 129 insertions(+), 263 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 50f4f28441..4d79e340c7 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -191,6 +191,8 @@ func (e Ext6) CyclotomicSquareCompressed(x *E6) *E6 { // // if g3=g2=0 then g4=g5=g1=0 and g0=1 (x=1) // Theorem 3.1 is well-defined for all x in Gϕₙ\{1} +// +// TODO: handle the edge cases func (e Ext6) DecompressKarabina(x *E6) *E6 { x = e.Reduce(x) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index fd3410b021..beed6197fa 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -176,162 +176,27 @@ func (pr Pairing) AssertIsEqual(x, y *GTEl) { pr.Ext6.AssertIsEqual(x, y) } -// seed x₀=9586122913090633729 -// -// loopCounter1 = x₀+1 in binary -var loopCounter1 = [64]int8{ - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, - 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, -} +var thirdRootOneG2 = emulated.ValueOf[emulated.BW6761Fp]("3876905175468200631077310367084681598448315841795389501393935922030716896759491089791062239139884430736136043081596370525752532152533918168748948422532524762769433379258873205270018176434449950195784127083892851850798970002242935133594411783692478449434154543435837344414891700653141782682622592665272535258486114040810216200011591719198498884598067930925845038459634787676665023756334020972459098834655430741989740290995313870292460737326506741396444022500") -// loopCounter2 = (x₀-1)² in 2-NAF -var loopCounter2 = [127]int8{ +var loopCounterAlt1 = [190]int8{ + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, - 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, - 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } - -// millerLoopSingle computes the Miller loop -// -// f_{u+1,Q}(P) * (f_{u+1})^q_{u²-2u-1,[u+1]Q}(P) * l^q_{[(u+1)(u²-2u+1)]Q,-Q}(P) -// -// Eq (4') in https://hackmd.io/@gnark/BW6-761-changes -func (pr Pairing) millerLoopSingle(P *G1Affine, Q *G2Affine) (*GTEl, error) { - - var l1, l2 *lineEvaluation - var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] - - // 1. f1 = f_{u+1,Q}(P) - res1 := pr.Ext6.One() - Qacc := Q - yInv = pr.curveF.Inverse(&P.Y) - xNegOverY = pr.curveF.MulMod(&P.X, yInv) - xNegOverY = pr.curveF.Neg(xNegOverY) - - // i = 62, separately to avoid an E6 Square - // (Square(res) = 1² = 1) - - // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc - Qacc, l1 = pr.doubleStep(Qacc) - // line evaluation at P - // and assign line to res1 (R1, R0, 0, 0, 1, 0) - res1.B0.A0 = *pr.curveF.Mul(&l1.R1, yInv) - res1.B0.A1 = *pr.curveF.Mul(&l1.R0, xNegOverY) - res1.B1.A1 = *pr.curveF.One() - - for i := 61; i >= 0; i-- { - // f² - res1 = pr.Square(res1) - - if loopCounter1[i] == 0 { - // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc - Qacc, l1 = pr.doubleStep(Qacc) - - // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res1 = pr.MulBy014(res1, &l1.R1, &l1.R0) - - } else { - // Qacc ← 2Qacc+Q, - // l1 the line ℓ passing Qacc and Q - // l2 the line ℓ passing (Qacc+Q) and Qacc - Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, Q) - - // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res1 = pr.MulBy014(res1, &l1.R1, &l1.R0) - - // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) - l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res1 = pr.MulBy014(res1, &l2.R1, &l2.R0) - - } - } - - // Cache values for the second Miller loop - res1Cached := res1 - res1Inv := pr.Conjugate(res1) - uQ := Qacc - - // 2. f2 = f_{u²-2u+1,uQ}(P) - res2 := res1Cached - uQNeg := &G2Affine{X: uQ.X, Y: *pr.curveF.Neg(&uQ.Y)} - - for i := 125; i >= 0; i-- { - // f² - res2 = pr.Square(res2) - - switch loopCounter2[i] { - - case 0: - // Qacc ← 2Qacc and l1 the tangent ℓ passing 2Qacc - Qacc, l1 = pr.doubleStep(Qacc) - - // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) - - case 1: - // Qacc ← 2Qacc+uQ, - // l1 the line ℓ passing Qacc and uQ - // l2 the line ℓ passing (Qacc+uQ) and Qacc - Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, uQ) - - // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) - - // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) - l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) - res2 = pr.Mul(res2, res1Cached) - - case -1: - // Qacc ← 2Qacc-uQ, - // l1 the line ℓ passing Qacc and -uQ - // l2 the line ℓ passing (Qacc-uQ) and Qacc - Qacc, l1, l2 = pr.doubleAndAddStep(Qacc, uQNeg) - - // line evaluation at P - l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) - - // line evaluation at P - l2.R0 = *pr.curveF.Mul(&l2.R0, xNegOverY) - l2.R1 = *pr.curveF.Mul(&l2.R1, yInv) - res2 = pr.MulBy014(res2, &l2.R1, &l2.R0) - res2 = pr.Mul(res2, res1Inv) - - default: - return nil, errors.New("invalid loopCounter") - } - } - - // 3. l_{(u+1)vQ,-Q}(P) - QNeg := &G2Affine{X: Q.X, Y: *pr.curveF.Neg(&Q.Y)} - l1 = pr.lineCompute(Qacc, QNeg) - l1.R0 = *pr.curveF.Mul(&l1.R0, xNegOverY) - l1.R1 = *pr.curveF.Mul(&l1.R1, yInv) - - // f2 = f2 * l_{(u+1)vQ,-Q}(P) - res2 = pr.MulBy014(res2, &l1.R1, &l1.R0) - - // 4. f1 * f2^q - res2 = pr.Frobenius(res2) - res := pr.Mul(res1, res2) - - return res, nil +var loopCounterAlt2 = [190]int8{ + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, + 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, + 1, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 1, 0, + 1, 0, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, } // MillerLoop computes the multi-Miller loop @@ -342,17 +207,16 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { if n == 0 || n != len(Q) { return nil, errors.New("invalid inputs sizes") } - res := pr.Ext6.One() // k = 0 - res, err := pr.millerLoopSingle(P[0], Q[0]) + res, err := pr.millerLoop(P[0], Q[0]) if err != nil { return >El{}, err } for k := 1; k < n; k++ { - m, err := pr.millerLoopSingle(P[k], Q[k]) + m, err := pr.millerLoop(P[k], Q[k]) if err != nil { return >El{}, err } @@ -363,12 +227,44 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } +// addStep adds two points in affine coordinates, and evaluates the line in Miller loop +// https://eprint.iacr.org/2022/1162 (Section 6.1) +func (pr Pairing) addStep(p1, p2 *G1Affine) (*G1Affine, *lineEvaluation) { + + // compute λ = (y2-y1)/(x2-x1) + p2ypy := pr.curveF.Sub(&p2.Y, &p1.Y) + p2xpx := pr.curveF.Sub(&p2.X, &p1.X) + λ := pr.curveF.Div(p2ypy, p2xpx) + + // xr = λ²-x1-x2 + λλ := pr.curveF.Mul(λ, λ) + p2xpx = pr.curveF.Add(&p1.X, &p2.X) + xr := pr.curveF.Sub(λλ, p2xpx) + + // yr = λ(x1-xr) - y1 + pxrx := pr.curveF.Sub(&p1.X, xr) + λpxrx := pr.curveF.Mul(λ, pxrx) + yr := pr.curveF.Sub(λpxrx, &p1.Y) + + var res G2Affine + res.X = *xr + res.Y = *yr + + var line lineEvaluation + line.R0 = *λ + line.R1 = *pr.curveF.Mul(λ, &p1.X) + line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) + + return &res, &line + +} + // doubleAndAddStep doubles p1 and adds p2 to the result in affine coordinates, and evaluates the line in Miller loop // https://eprint.iacr.org/2022/1162 (Section 6.1) -func (pr Pairing) doubleAndAddStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation, *lineEvaluation) { +func (pr Pairing) doubleAndAddStep(p1, p2 *G1Affine) (*G1Affine, *lineEvaluation, *lineEvaluation) { var line1, line2 lineEvaluation - var p G2Affine + var p G1Affine // compute λ1 = (y2-y1)/(x2-x1) n := pr.curveF.Sub(&p1.Y, &p2.Y) @@ -417,9 +313,9 @@ func (pr Pairing) doubleAndAddStep(p1, p2 *G2Affine) (*G2Affine, *lineEvaluation // doubleStep doubles a point in affine coordinates, and evaluates the line in Miller loop // https://eprint.iacr.org/2022/1162 (Section 6.1) -func (pr Pairing) doubleStep(p1 *G2Affine) (*G2Affine, *lineEvaluation) { +func (pr Pairing) doubleStep(p1 *G1Affine) (*G1Affine, *lineEvaluation) { - var p G2Affine + var p G1Affine var line lineEvaluation // λ = 3x²/2y @@ -450,13 +346,15 @@ func (pr Pairing) doubleStep(p1 *G2Affine) (*G2Affine, *lineEvaluation) { } -// lineCompute computes the line that goes through p1 and p2 but does not compute p1+p2 -func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { +// tangentCompute computes the line that goes through p1 and p2 but does not compute p1+p2 +func (pr Pairing) tangentCompute(p1 *G1Affine) *lineEvaluation { - // compute λ = (y2-y1)/(x2-x1) - qypy := pr.curveF.Sub(&p2.Y, &p1.Y) - qxpx := pr.curveF.Sub(&p2.X, &p1.X) - λ := pr.curveF.Div(qypy, qxpx) + // λ = 3x²/2y + n := pr.curveF.Mul(&p1.X, &p1.X) + three := big.NewInt(3) + n = pr.curveF.MulConst(n, three) + d := pr.curveF.Add(&p1.Y, &p1.Y) + λ := pr.curveF.Div(n, d) var line lineEvaluation line.R0 = *λ @@ -467,31 +365,7 @@ func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { } -// --- Alternative Miller loop --- -var thirdRootOneG2 = emulated.ValueOf[emulated.BW6761Fp]("3876905175468200631077310367084681598448315841795389501393935922030716896759491089791062239139884430736136043081596370525752532152533918168748948422532524762769433379258873205270018176434449950195784127083892851850798970002242935133594411783692478449434154543435837344414891700653141782682622592665272535258486114040810216200011591719198498884598067930925845038459634787676665023756334020972459098834655430741989740290995313870292460737326506741396444022500") - -var loopCounterAlt1 = [190]int8{ - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -} -var loopCounterAlt2 = [190]int8{ - -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, -1, - 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, -1, 0, 0, - 1, 0, 0, 0, -1, 0, 0, -1, 0, 1, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, -1, 0, 1, 0, - 1, 0, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, -} - -func (pr Pairing) MillerLoopAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { +func (pr Pairing) millerLoop(P *G1Affine, Q *G2Affine) (*GTEl, error) { // precomputations var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] @@ -637,62 +511,3 @@ func (pr Pairing) MillerLoopAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { return result, nil } - -// addStep adds two points in affine coordinates, and evaluates the line in Miller loop -// https://eprint.iacr.org/2022/1162 (Section 6.1) -func (pr Pairing) addStep(p1, p2 *G1Affine) (*G1Affine, *lineEvaluation) { - - // compute λ = (y2-y1)/(x2-x1) - p2ypy := pr.curveF.Sub(&p2.Y, &p1.Y) - p2xpx := pr.curveF.Sub(&p2.X, &p1.X) - λ := pr.curveF.Div(p2ypy, p2xpx) - - // xr = λ²-x1-x2 - λλ := pr.curveF.Mul(λ, λ) - p2xpx = pr.curveF.Add(&p1.X, &p2.X) - xr := pr.curveF.Sub(λλ, p2xpx) - - // yr = λ(x1-xr) - y1 - pxrx := pr.curveF.Sub(&p1.X, xr) - λpxrx := pr.curveF.Mul(λ, pxrx) - yr := pr.curveF.Sub(λpxrx, &p1.Y) - - var res G2Affine - res.X = *xr - res.Y = *yr - - var line lineEvaluation - line.R0 = *λ - line.R1 = *pr.curveF.Mul(λ, &p1.X) - line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) - - return &res, &line - -} - -// tangentCompute computes the line that goes through p1 and p2 but does not compute p1+p2 -func (pr Pairing) tangentCompute(p1 *G1Affine) *lineEvaluation { - - // λ = 3x²/2y - n := pr.curveF.Mul(&p1.X, &p1.X) - three := big.NewInt(3) - n = pr.curveF.MulConst(n, three) - d := pr.curveF.Add(&p1.Y, &p1.Y) - λ := pr.curveF.Div(n, d) - - var line lineEvaluation - line.R0 = *λ - line.R1 = *pr.curveF.Mul(λ, &p1.X) - line.R1 = *pr.curveF.Sub(&line.R1, &p1.Y) - - return &line - -} - -func (pr Pairing) PairAlt(P *G1Affine, Q *G2Affine) (*GTEl, error) { - f, err := pr.MillerLoopAlt(P, Q) - if err != nil { - return nil, err - } - return pr.FinalExponentiation(f), nil -} diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 129548a296..12aadf7438 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -72,7 +72,7 @@ func (c *PairCircuit) Define(api frontend.API) error { if err != nil { return fmt.Errorf("new pairing: %w", err) } - res, err := pairing.PairAlt(&c.InG1, &c.InG2) + res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -94,19 +94,24 @@ func TestPairTestSolve(t *testing.T) { assert.NoError(err) } -/* -type PairCircuit struct { +type MultiPairCircuit struct { InG1 G1Affine InG2 G2Affine Res GTEl + n int } -func (c *PairCircuit) Define(api frontend.API) error { +func (c *MultiPairCircuit) Define(api frontend.API) error { pairing, err := NewPairing(api) if err != nil { return fmt.Errorf("new pairing: %w", err) } - res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) + P, Q := []*G1Affine{}, []*G2Affine{} + for i := 0; i < c.n; i++ { + P = append(P, &c.InG1) + Q = append(Q, &c.InG2) + } + res, err := pairing.Pair(P, Q) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -114,18 +119,62 @@ func (c *PairCircuit) Define(api frontend.API) error { return nil } -func TestPairTestSolve(t *testing.T) { +func TestMultiPairTestSolve(t *testing.T) { assert := test.NewAssert(t) - p, q := randomG1G2Affines() - res, err := bw6761.MillerLoopOptAte([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) - res = bw6761.FinalExponentiation(&res) - assert.NoError(err) - witness := PairCircuit{ - InG1: NewG1Affine(p), - InG2: NewG2Affine(q), - Res: NewGTEl(res), + p1, q1 := randomG1G2Affines() + p := make([]bw6761.G1Affine, 10) + q := make([]bw6761.G2Affine, 10) + for i := 0; i < 10; i++ { + p[i] = p1 + q[i] = q1 } - err = test.IsSolved(&PairCircuit{}, &witness, ecc.BN254.ScalarField()) + + for i := 2; i < 10; i++ { + res, err := bw6761.Pair(p[:i], q[:i]) + assert.NoError(err) + witness := MultiPairCircuit{ + InG1: NewG1Affine(p1), + InG2: NewG2Affine(q1), + Res: NewGTEl(res), + } + err = test.IsSolved(&MultiPairCircuit{n: i}, &witness, ecc.BN254.ScalarField()) + assert.NoError(err) + } +} + +/* +type PairingCheckCircuit struct { + In1G1 G1Affine + In2G1 G1Affine + In1G2 G2Affine + In2G2 G2Affine +} + +func (c *PairingCheckCircuit) Define(api frontend.API) error { + pairing, err := NewPairing(api) + if err != nil { + return fmt.Errorf("new pairing: %w", err) + } + err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In1G1, &c.In2G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2, &c.In1G2, &c.In2G2}) + if err != nil { + return fmt.Errorf("pair: %w", err) + } + return nil +} + +func TestPairingCheckTestSolve(t *testing.T) { + assert := test.NewAssert(t) + p1, q1 := randomG1G2Affines() + _, q2 := randomG1G2Affines() + var p2 bw6761.G1Affine + p2.Neg(&p1) + witness := PairingCheckCircuit{ + In1G1: NewG1Affine(p1), + In1G2: NewG2Affine(q1), + In2G1: NewG1Affine(p2), + In2G2: NewG2Affine(q2), + } + err := test.IsSolved(&PairingCheckCircuit{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } */ From cb6133ef27f9a5fe7b4842e059ea4648e19b8fb6 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Oct 2023 18:42:43 -0400 Subject: [PATCH 25/38] perf(bw6/optimal tate): optimize multi-miller loop --- std/algebra/emulated/sw_bw6761/pairing.go | 342 ++++++++++++---------- 1 file changed, 185 insertions(+), 157 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index beed6197fa..d21867e3d4 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -207,23 +207,198 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { if n == 0 || n != len(Q) { return nil, errors.New("invalid inputs sizes") } - res := pr.Ext6.One() + // precomputations + p0 := make([]*G1Affine, n) + p1 := make([]*G1Affine, n) + p0neg := make([]*G1Affine, n) + p1neg := make([]*G1Affine, n) + p01 := make([]*G1Affine, n) + p10 := make([]*G1Affine, n) + p01neg := make([]*G1Affine, n) + p10neg := make([]*G1Affine, n) + pAcc := make([]*G1Affine, n) + yInv := make([]*emulated.Element[emulated.BW6761Fp], n) + xNegOverY := make([]*emulated.Element[emulated.BW6761Fp], n) + l01 := make([]*lineEvaluation, n) + + for k := 0; k < n; k++ { + yInv[k] = pr.curveF.Inverse(&Q[k].Y) + xNegOverY[k] = pr.curveF.MulMod(&Q[k].X, yInv[k]) + xNegOverY[k] = pr.curveF.Neg(xNegOverY[k]) + p0[k] = &G1Affine{X: P[k].X, Y: P[k].Y} + p0[k].X = P[k].X + p0[k].Y = P[k].Y + p0neg[k] = &G1Affine{X: p0[k].X, Y: *pr.curveF.Neg(&p0[k].Y)} + p1[k] = &G1Affine{X: *pr.curveF.MulMod(&p0[k].X, &thirdRootOneG2), Y: p0neg[k].Y} + p1neg[k] = &G1Affine{X: p1[k].X, Y: p0[k].Y} + + // p01 = p0+p1 and l01 = l_{p0,p1}(q) + p01[k], l01[k] = pr.addStep(p0[k], p1[k]) + l01[k].R0 = *pr.curveF.MulMod(&l01[k].R0, xNegOverY[k]) + l01[k].R1 = *pr.curveF.MulMod(&l01[k].R1, yInv[k]) + p01neg[k] = &G1Affine{X: p01[k].X, Y: *pr.curveF.Neg(&p01[k].Y)} + + // p10 = p0-p1 + p10[k] = &G1Affine{ + X: *pr.curveF.Add(&p0[k].X, &p1[k].X), + Y: p1[k].Y, + } + p10[k].X = *pr.curveF.Neg(&p10[k].X) + p10neg[k] = &G1Affine{X: p10[k].X, Y: p0[k].Y} + + pAcc[k] = p1[k] + } + + // f_{a0+\lambda*a1,P}(Q) + result := pr.Ext6.One() + var prodLines [5]emulated.Element[emulated.BW6761Fp] + var l, l0 *lineEvaluation + + // i = 188 // k = 0 - res, err := pr.millerLoop(P[0], Q[0]) - if err != nil { - return >El{}, err + pAcc[0], l0 = pr.doubleStep(p1[0]) + result.B1.A0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[0]) + result.B1.A1 = *pr.curveF.MulMod(&l0.R1, yInv[0]) + + if n >= 2 { + pAcc[1], l0 = pr.doubleStep(pAcc[1]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[1]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[1]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &result.B1.A0, &result.B1.A1) + result.B0.A0 = prodLines[0] + result.B0.A1 = prodLines[1] + result.B0.A2 = prodLines[2] + result.B1.A0 = prodLines[3] + result.B1.A1 = prodLines[4] + + } + + if n >= 3 { + pAcc[2], l0 = pr.doubleStep(pAcc[2]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[2]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[2]) + result = pr.Mul01234By034(&prodLines, &l0.R0, &l0.R1) + + // k >= 3 + for k := 3; k < n; k++ { + pAcc[k], l0 = pr.doubleStep(pAcc[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + result = pr.MulBy034(result, &l0.R0, &l0.R1) + } + } + + // i = 187 + if n == 1 { + result = pr.Square034(result) + } else { + result = pr.Square(result) + } + for k := 0; k < n; k++ { + pAcc[k], l0 = pr.doubleStep(pAcc[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + result = pr.MulBy034(result, &l0.R0, &l0.R1) } - for k := 1; k < n; k++ { - m, err := pr.millerLoop(P[k], Q[k]) - if err != nil { - return >El{}, err + for i := 186; i >= 1; i-- { + // (∏ᵢfᵢ)² + result = pr.Square(result) + + j := loopCounterAlt2[i]*3 + loopCounterAlt1[i] + + for k := 0; k < n; k++ { + switch j { + case -4: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p01neg[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) + case -3: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p1neg[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + case -2: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p10[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) + case -1: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p0neg[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + case 0: + pAcc[k], l0 = pr.doubleStep(pAcc[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + result = pr.MulBy034(result, &l0.R0, &l0.R1) + case 1: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p0[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + case 2: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p10neg[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) + case 3: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p1[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + case 4: + pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p01[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) + l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) + prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, &prodLines) + result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) + default: + return nil, errors.New("invalid loopCounter") + } } - res = pr.Mul(res, m) } - return res, nil + // i = 0, j = -3 + result = pr.Square(result) + for k := 0; k < n; k++ { + l0 = pr.tangentCompute(pAcc[k]) + l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) + l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + result = pr.MulBy034(result, &l0.R0, &l0.R1) + } + + return result, nil } @@ -364,150 +539,3 @@ func (pr Pairing) tangentCompute(p1 *G1Affine) *lineEvaluation { return &line } - -func (pr Pairing) millerLoop(P *G1Affine, Q *G2Affine) (*GTEl, error) { - - // precomputations - var yInv, xNegOverY *emulated.Element[emulated.BW6761Fp] - yInv = pr.curveF.Inverse(&Q.Y) - xNegOverY = pr.curveF.MulMod(&Q.X, yInv) - xNegOverY = pr.curveF.Neg(xNegOverY) - p0 := &G1Affine{X: P.X, Y: P.Y} - p0neg := &G1Affine{X: p0.X, Y: *pr.curveF.Neg(&p0.Y)} - p1 := &G1Affine{ - X: *pr.curveF.MulMod(&p0.X, &thirdRootOneG2), - Y: p0neg.Y, - } - p1neg := &G1Affine{X: p1.X, Y: p0.Y} - - // p01 = p0+p1 and l01 = l_{p0,p1}(q) - p01, l01 := pr.addStep(p0, p1) - l01.R0 = *pr.curveF.MulMod(&l01.R0, xNegOverY) - l01.R1 = *pr.curveF.MulMod(&l01.R1, yInv) - p01neg := &G1Affine{X: p01.X, Y: *pr.curveF.Neg(&p01.Y)} - - // p10 = p0-p1 - p10 := &G1Affine{ - X: *pr.curveF.Add(&p0.X, &p1.X), - Y: p1.Y, - } - p10.X = *pr.curveF.Neg(&p10.X) - p10neg := &G1Affine{ - X: p10.X, - Y: p0.Y, - } - - // f_{a0+\lambda*a1,P}(Q) - result := pr.Ext6.One() - - var j int8 - - // i = 188 - var l *lineEvaluation - var prodLines [5]emulated.Element[emulated.BW6761Fp] - pAcc, l0 := pr.doubleStep(p1) - result.B1.A0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - result.B1.A1 = *pr.curveF.MulMod(&l0.R1, yInv) - - // i = 187 - result = pr.Square034(result) - pAcc, l0 = pr.doubleStep(pAcc) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - result = pr.MulBy034(result, &l0.R0, &l0.R1) - - for i := 186; i >= 1; i-- { - // (∏ᵢfᵢ)² - result = pr.Square(result) - - j = loopCounterAlt2[i]*3 + loopCounterAlt1[i] - - switch j { - case -4: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p01neg) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - result = pr.MulBy034(result, &l01.R0, &l01.R1) - case -3: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p1neg) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - case -2: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p10) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - result = pr.MulBy034(result, &l01.R0, &l01.R1) - case -1: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p0neg) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - case 0: - pAcc, l0 = pr.doubleStep(pAcc) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - result = pr.MulBy034(result, &l0.R0, &l0.R1) - case 1: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p0) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - case 2: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p10neg) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - result = pr.MulBy034(result, &l01.R0, &l01.R1) - case 3: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p1) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - case 4: - pAcc, l0, l = pr.doubleAndAddStep(pAcc, p01) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) - result = pr.MulBy034(result, &l01.R0, &l01.R1) - default: - return nil, errors.New("invalid loopCounter") - } - } - - // i = 0, j = -3 - result = pr.Square(result) - l0 = pr.tangentCompute(pAcc) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv) - result = pr.MulBy034(result, &l0.R0, &l0.R1) - - return result, nil - -} From 4507e7cf5509b6bcd330ce81ff4b3d05113dd47d Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 6 Oct 2023 23:55:40 -0400 Subject: [PATCH 26/38] style: clean and document the code --- std/algebra/emulated/fields_bw6761/e6.go | 20 ++++ .../emulated/fields_bw6761/e6_pairing.go | 103 ------------------ std/algebra/emulated/fields_bw6761/e6_test.go | 14 +-- .../emulated/fields_bw6761/frobenius.go | 27 ----- std/algebra/emulated/sw_bw6761/pairing.go | 60 +++++++--- 5 files changed, 73 insertions(+), 151 deletions(-) delete mode 100644 std/algebra/emulated/fields_bw6761/frobenius.go diff --git a/std/algebra/emulated/fields_bw6761/e6.go b/std/algebra/emulated/fields_bw6761/e6.go index 4d79e340c7..43340bdcd8 100644 --- a/std/algebra/emulated/fields_bw6761/e6.go +++ b/std/algebra/emulated/fields_bw6761/e6.go @@ -3,6 +3,7 @@ package fields_bw6761 import ( bw6761 "github.com/consensys/gnark-crypto/ecc/bw6-761" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/emulated" ) type E6 struct { @@ -377,3 +378,22 @@ func FromE6(a *bw6761.E6) E6 { B1: FromE3(&a.B1), } } + +// Frobenius set z in E6 to Frobenius(x), return z +func (e Ext6) Frobenius(x *E6) *E6 { + _frobA := emulated.ValueOf[emulated.BW6761Fp]("4922464560225523242118178942575080391082002530232324381063048548642823052024664478336818169867474395270858391911405337707247735739826664939444490469542109391530482826728203582549674992333383150446779312029624171857054392282775648") + _frobB := emulated.ValueOf[emulated.BW6761Fp]("1968985824090209297278610739700577151397666382303825728450741611566800370218827257750865013421937292370006175842381275743914023380727582819905021229583192207421122272650305267822868639090213645505120388400344940985710520836292650") + _frobC := emulated.ValueOf[emulated.BW6761Fp]("4922464560225523242118178942575080391082002530232324381063048548642823052024664478336818169867474395270858391911405337707247735739826664939444490469542109391530482826728203582549674992333383150446779312029624171857054392282775649") + _frobAC := emulated.ValueOf[emulated.BW6761Fp]("-1") + _frobBC := emulated.ValueOf[emulated.BW6761Fp]("1968985824090209297278610739700577151397666382303825728450741611566800370218827257750865013421937292370006175842381275743914023380727582819905021229583192207421122272650305267822868639090213645505120388400344940985710520836292651") + var z E6 + z.B0.A0 = x.B0.A0 + z.B0.A1 = *e.fp.Mul(&x.B0.A1, &_frobA) + z.B0.A2 = *e.fp.Mul(&x.B0.A2, &_frobB) + + z.B1.A0 = *e.fp.Mul(&x.B1.A0, &_frobC) + z.B1.A1 = *e.fp.Mul(&x.B1.A1, &_frobAC) + z.B1.A2 = *e.fp.Mul(&x.B1.A2, &_frobBC) + + return &z +} diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index a5e18395a1..901d2eb1b3 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -1,7 +1,5 @@ package fields_bw6761 -import "math/big" - func (e Ext6) nSquareCompressed(z *E6, n int) *E6 { for i := 0; i < n; i++ { z = e.CyclotomicSquareCompressed(z) @@ -77,107 +75,6 @@ func (e Ext6) Expc1(x *E6) *E6 { return e.DecompressKarabina(result) } -// MulBy014 multiplies z by an E6 sparse element of the form -// -// E6{ -// C0: E3{A0: c0, A1: c1, A2: 0}, -// C1: E3{A0: 0, A1: 1, A2: 0}, -// } -func (e *Ext6) MulBy014(z *E6, c0, c1 *baseEl) *E6 { - - z = e.Reduce(z) - - a := z.B0 - a = *e.MulBy01(&a, c0, c1) - - var b E3 - // Mul by E3{0, 1, 0} - b.A0 = *mulFpByNonResidue(e.fp, &z.B1.A2) - b.A2 = z.B1.A1 - b.A1 = z.B1.A0 - - one := e.fp.One() - d := e.fp.Add(c1, one) - - zC1 := e.Ext3.Add(&z.B1, &z.B0) - zC1 = e.Ext3.MulBy01(zC1, c0, d) - zC1 = e.Ext3.Sub(zC1, &a) - zC1 = e.Ext3.Sub(zC1, &b) - zC0 := e.Ext3.MulByNonResidue(&b) - zC0 = e.Ext3.Add(zC0, &a) - - return &E6{ - B0: *zC0, - B1: *zC1, - } -} - -// multiplies two E6 sparse element of the form: -// -// E6{ -// C0: E6{B0: c0, B1: c1, B2: 0}, -// C1: E6{B0: 0, B1: 1, B2: 0}, -// } -// -// and -// -// E6{ -// C0: E6{B0: d0, B1: d1, B2: 0}, -// C1: E6{B0: 0, B1: 1, B2: 0}, -// } -func (e Ext6) Mul014By014(d0, d1, c0, c1 *baseEl) *[5]baseEl { - one := e.fp.One() - x0 := e.fp.Mul(c0, d0) - x1 := e.fp.Mul(c1, d1) - tmp := e.fp.Add(c0, one) - x04 := e.fp.Add(d0, one) - x04 = e.fp.Mul(x04, tmp) - x04 = e.fp.Sub(x04, x0) - x04 = e.fp.Sub(x04, one) - tmp = e.fp.Add(c0, c1) - x01 := e.fp.Add(d0, d1) - x01 = e.fp.Mul(x01, tmp) - x01 = e.fp.Sub(x01, x0) - x01 = e.fp.Sub(x01, x1) - tmp = e.fp.Add(c1, one) - x14 := e.fp.Add(d1, one) - x14 = e.fp.Mul(x14, tmp) - x14 = e.fp.Sub(x14, x1) - x14 = e.fp.Sub(x14, one) - - // NonResidue() - zC0B0 := e.fp.MulConst(e.fp.One(), big.NewInt(4)) - zC0B0 = e.fp.Neg(zC0B0) - - zC0B0 = e.fp.Add(zC0B0, x0) - - return &[5]baseEl{*zC0B0, *x01, *x1, *x04, *x14} -} - -// MulBy01245 multiplies z by an E6 sparse element of the form -// -// E6{ -// C0: E6{B0: c0, B1: c1, B2: c2}, -// C1: E6{B0: 0, B1: c4, B2: c5}, -// } -func (e *Ext6) MulBy01245(z *E6, x *[5]baseEl) *E6 { - c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1 := &E3{A0: *e.fp.Zero(), A1: x[3], A2: x[4]} - a := e.Ext3.Add(&z.B0, &z.B1) - b := e.Ext3.Add(c0, c1) - a = e.Ext3.Mul(a, b) - b = e.Ext3.Mul(&z.B0, c0) - c := e.Ext3.MulBy12(&z.B1, &x[3], &x[4]) - z1 := e.Ext3.Sub(a, b) - z1 = e.Ext3.Sub(z1, c) - z0 := e.Ext3.MulByNonResidue(c) - z0 = e.Ext3.Add(z0, b) - return &E6{ - B0: *z0, - B1: *z1, - } -} - // Square034 squares an E6 sparse element of the form // // E6{ diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index d0a035eb5e..01969e9cee 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -434,20 +434,20 @@ func TestExpc1Fp6(t *testing.T) { assert.NoError(err) } -type e6MulBy014 struct { +type e6MulBy034 struct { A E6 `gnark:",public"` W E6 B, C baseEl } -func (circuit *e6MulBy014) Define(api frontend.API) error { +func (circuit *e6MulBy034) Define(api frontend.API) error { e := NewExt6(api) - res := e.MulBy014(&circuit.A, &circuit.B, &circuit.C) + res := e.MulBy034(&circuit.A, &circuit.B, &circuit.C) e.AssertIsEqual(res, &circuit.W) return nil } -func TestFp12MulBy014(t *testing.T) { +func TestFp12MulBy034(t *testing.T) { assert := test.NewAssert(t) // witness values @@ -458,16 +458,16 @@ func TestFp12MulBy014(t *testing.T) { _, _ = b.SetRandom() _, _ = c.SetRandom() w.Set(&a) - w.MulBy014(&b, &c, &one) + w.MulBy034(&one, &b, &c) - witness := e6MulBy014{ + witness := e6MulBy034{ A: FromE6(&a), B: emulated.ValueOf[emulated.BW6761Fp](&b), C: emulated.ValueOf[emulated.BW6761Fp](&c), W: FromE6(&w), } - err := test.IsSolved(&e6MulBy014{}, &witness, ecc.BN254.ScalarField()) + err := test.IsSolved(&e6MulBy034{}, &witness, ecc.BN254.ScalarField()) assert.NoError(err) } diff --git a/std/algebra/emulated/fields_bw6761/frobenius.go b/std/algebra/emulated/fields_bw6761/frobenius.go deleted file mode 100644 index 7c94be72c8..0000000000 --- a/std/algebra/emulated/fields_bw6761/frobenius.go +++ /dev/null @@ -1,27 +0,0 @@ -package fields_bw6761 - -import ( - "github.com/consensys/gnark/std/math/emulated" -) - -var ( - _frobA = emulated.ValueOf[emulated.BW6761Fp]("4922464560225523242118178942575080391082002530232324381063048548642823052024664478336818169867474395270858391911405337707247735739826664939444490469542109391530482826728203582549674992333383150446779312029624171857054392282775648") - _frobB = emulated.ValueOf[emulated.BW6761Fp]("1968985824090209297278610739700577151397666382303825728450741611566800370218827257750865013421937292370006175842381275743914023380727582819905021229583192207421122272650305267822868639090213645505120388400344940985710520836292650") - _frobC = emulated.ValueOf[emulated.BW6761Fp]("4922464560225523242118178942575080391082002530232324381063048548642823052024664478336818169867474395270858391911405337707247735739826664939444490469542109391530482826728203582549674992333383150446779312029624171857054392282775649") - _frobAC = emulated.ValueOf[emulated.BW6761Fp]("-1") - _frobBC = emulated.ValueOf[emulated.BW6761Fp]("1968985824090209297278610739700577151397666382303825728450741611566800370218827257750865013421937292370006175842381275743914023380727582819905021229583192207421122272650305267822868639090213645505120388400344940985710520836292651") -) - -// Frobenius set z in E6 to Frobenius(x), return z -func (e Ext6) Frobenius(x *E6) *E6 { - var z E6 - z.B0.A0 = x.B0.A0 - z.B0.A1 = *e.fp.Mul(&x.B0.A1, &_frobA) - z.B0.A2 = *e.fp.Mul(&x.B0.A2, &_frobB) - - z.B1.A0 = *e.fp.Mul(&x.B1.A0, &_frobC) - z.B1.A1 = *e.fp.Mul(&x.B1.A1, &_frobAC) - z.B1.A2 = *e.fp.Mul(&x.B1.A2, &_frobBC) - - return &z -} diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index d21867e3d4..dbb8347364 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -139,7 +139,7 @@ func (pr Pairing) FinalExponentiation(z *GTEl) *GTEl { // lineEvaluation represents a sparse Fp6 Elmt (result of the line evaluation) // line: 1 + R0(x/y) + R1(1/y) = 0 instead of R0'*y + R1'*x + R2' = 0 This -// makes the multiplication by lines (MulBy014) circuit-efficient. +// makes the multiplication by lines (MulBy034) and between lines (Mul034By034) type lineEvaluation struct { R0, R1 emulated.Element[emulated.BW6761Fp] } @@ -176,8 +176,9 @@ func (pr Pairing) AssertIsEqual(x, y *GTEl) { pr.Ext6.AssertIsEqual(x, y) } -var thirdRootOneG2 = emulated.ValueOf[emulated.BW6761Fp]("3876905175468200631077310367084681598448315841795389501393935922030716896759491089791062239139884430736136043081596370525752532152533918168748948422532524762769433379258873205270018176434449950195784127083892851850798970002242935133594411783692478449434154543435837344414891700653141782682622592665272535258486114040810216200011591719198498884598067930925845038459634787676665023756334020972459098834655430741989740290995313870292460737326506741396444022500") - +// seed x₀=9586122913090633729 +// +// x₀+1 in binary (64 bits) padded with 0s var loopCounterAlt1 = [190]int8{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, @@ -188,6 +189,8 @@ var loopCounterAlt1 = [190]int8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } + +// x₀³-x₀²-x₀ in 2-NAF var loopCounterAlt2 = [190]int8{ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -199,7 +202,16 @@ var loopCounterAlt2 = [190]int8{ 1, 0, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, } -// MillerLoop computes the multi-Miller loop +// thirdRootOne² + thirdRootOne + 1 = 0 in BW6761Fp +var thirdRootOne = emulated.ValueOf[emulated.BW6761Fp]("4922464560225523242118178942575080391082002530232324381063048548642823052024664478336818169867474395270858391911405337707247735739826664939444490469542109391530482826728203582549674992333383150446779312029624171857054392282775648") + +// MillerLoop computes the optimal Tate multi-Miller loop +// (or twisted ate or Eta revisited) +// +// ∏ᵢ { fᵢ_{x₀+1+λ(x₀³-x₀²-x₀),Pᵢ}(Qᵢ) } +// +// Alg.2 in https://eprint.iacr.org/2021/1359.pdf +// Eq. (6) in https://hackmd.io/@gnark/BW6-761-changes func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // check input size match @@ -223,45 +235,54 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { l01 := make([]*lineEvaluation, n) for k := 0; k < n; k++ { + // P and Q are supposed to be on G1 and G2 respectively of prime order r. + // The point (x,0) is of order 2. But this function does not check + // subgroup membership. yInv[k] = pr.curveF.Inverse(&Q[k].Y) xNegOverY[k] = pr.curveF.MulMod(&Q[k].X, yInv[k]) xNegOverY[k] = pr.curveF.Neg(xNegOverY[k]) + // p0 = P = (x, y) p0[k] = &G1Affine{X: P[k].X, Y: P[k].Y} - p0[k].X = P[k].X - p0[k].Y = P[k].Y + // p0neg = -P = (x, -y) p0neg[k] = &G1Affine{X: p0[k].X, Y: *pr.curveF.Neg(&p0[k].Y)} - p1[k] = &G1Affine{X: *pr.curveF.MulMod(&p0[k].X, &thirdRootOneG2), Y: p0neg[k].Y} + // p1 = (w*x, -y) + p1[k] = &G1Affine{X: *pr.curveF.MulMod(&p0[k].X, &thirdRootOne), Y: p0neg[k].Y} + // p1neg = (w*x, y) p1neg[k] = &G1Affine{X: p1[k].X, Y: p0[k].Y} - - // p01 = p0+p1 and l01 = l_{p0,p1}(q) + // p01 = p0+p1 and l01 line through p0 and p1 p01[k], l01[k] = pr.addStep(p0[k], p1[k]) l01[k].R0 = *pr.curveF.MulMod(&l01[k].R0, xNegOverY[k]) l01[k].R1 = *pr.curveF.MulMod(&l01[k].R1, yInv[k]) + // p01neg = -p01 p01neg[k] = &G1Affine{X: p01[k].X, Y: *pr.curveF.Neg(&p01[k].Y)} - // p10 = p0-p1 p10[k] = &G1Affine{ X: *pr.curveF.Add(&p0[k].X, &p1[k].X), Y: p1[k].Y, } p10[k].X = *pr.curveF.Neg(&p10[k].X) + // p10neg = -p10 p10neg[k] = &G1Affine{X: p10[k].X, Y: p0[k].Y} - + // point accumulator initialized to p1 pAcc[k] = p1[k] } - // f_{a0+\lambda*a1,P}(Q) + // f_{x₀+1+λ(x₀³-x₀²-x₀),P}(Q) result := pr.Ext6.One() var prodLines [5]emulated.Element[emulated.BW6761Fp] var l, l0 *lineEvaluation - // i = 188 - // k = 0 + // i = 188, separately to avoid an E6 Square + // (Square(res) = 1² = 1) + // k = 0, separately to avoid MulBy034 (res × ℓ) + // (assign line to res) pAcc[0], l0 = pr.doubleStep(p1[0]) result.B1.A0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[0]) result.B1.A1 = *pr.curveF.MulMod(&l0.R1, yInv[0]) if n >= 2 { + // k = 1, separately to avoid MulBy034 (res × ℓ) + // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) pAcc[1], l0 = pr.doubleStep(pAcc[1]) l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[1]) l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[1]) @@ -275,6 +296,8 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } if n >= 3 { + // k = 2, separately to avoid MulBy034 (res × ℓ) + // (res has a zero E2 element, so we use Mul01234By034) pAcc[2], l0 = pr.doubleStep(pAcc[2]) l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[2]) l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[2]) @@ -303,6 +326,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } for i := 186; i >= 1; i-- { + // mutualize the square among n Miller loops // (∏ᵢfᵢ)² result = pr.Square(result) @@ -390,6 +414,14 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } // i = 0, j = -3 + // The resulting accumulator point is the infinity point because + // [(x₀+1) + λ(x₀³-x₀²-x₀)]P = [3(x₀-1)² ⋅ r]P = ∞ + // since we're using affine coordinates, the addStep in the last iteration + // (j=-3) will fail as the slope of a vertical line in indefinite. But in + // projective coordinates, vertinal lines meet at (0:1:0) so the result + // should be unchanged if we ommit the addStep in this case. Moreover we + // just compute before the tangent line and not the full doubleStep as we + // only care about the Miller loop result in Fp6 and not the point itself. result = pr.Square(result) for k := 0; k < n; k++ { l0 = pr.tangentCompute(pAcc[k]) From 85c5887759b89ded8e5f9ecc07cc6cb316612a82 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sat, 7 Oct 2023 00:00:03 -0400 Subject: [PATCH 27/38] fix: ineffectual assignment to err --- std/algebra/emulated/sw_bw6761/pairing_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 12aadf7438..d62600e732 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -184,6 +184,9 @@ func BenchmarkPairing(b *testing.B) { p, q := randomG1G2Affines() res, err := bw6761.Pair([]bw6761.G1Affine{p}, []bw6761.G2Affine{q}) + if err != nil { + b.Fatal(err) + } witness := PairCircuit{ InG1: NewG1Affine(p), InG2: NewG2Affine(q), From 1c8b9e005d875461c21f3bda727de9ce784407df Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Oct 2023 17:35:00 -0400 Subject: [PATCH 28/38] perf(bw6): optimize final exponentiation --- .../emulated/fields_bw6761/e6_pairing.go | 66 +++++++++--- std/algebra/emulated/sw_bw6761/pairing.go | 101 ++++++------------ .../emulated/sw_bw6761/pairing_test.go | 8 ++ 3 files changed, 88 insertions(+), 87 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 901d2eb1b3..cded69ebf6 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -39,40 +39,72 @@ func (e Ext6) Expt(x *E6) *E6 { return result } -// Expc2 set x to x^c2 in E6 and return x -// ht, hy = 13, 9 -// c1 = ht+hy = 22 (10110) -func (e Ext6) Expc2(x *E6) *E6 { +// ExptMinus1 set x to x^(t-1) in E6 and return x +func (e Ext6) ExptMinus1(x *E6) *E6 { + result := e.Expt(x) + result = e.Mul(result, e.Conjugate(x)) + return result +} - result := x - result = e.nSquareCompressed(result, 2) - result = e.DecompressKarabina(result) +// ExptPlus1 set x to x^(t+1) in E6 and return x +func (e Ext6) ExptPlus1(x *E6) *E6 { + result := e.Expt(x) result = e.Mul(result, x) + return result +} + +// ExptMinus1Div3 set x to x^(t-1)/3 in E6 and return x +func (e Ext6) ExptMinus1Div3(x *E6) *E6 { + x = e.Reduce(x) + result := e.Copy(x) result = e.CyclotomicSquare(result) result = e.Mul(result, x) - result = e.CyclotomicSquare(result) + t0 := e.Mul(result, x) + t0 = e.CyclotomicSquare(t0) + result = e.Mul(result, t0) + t0 = e.CyclotomicSquare(result) + t0 = e.nSquareCompressed(t0, 6) + t0 = e.DecompressKarabina(t0) + result = e.Mul(result, t0) + result = e.nSquareCompressed(result, 5) + result = e.DecompressKarabina(result) + result = e.Mul(result, x) + result = e.nSquareCompressed(result, 46) + result = e.DecompressKarabina(result) return result } -// Expc1 set x to x^c1 in E6 and return x +// Expc1 set x to x^c2 in E6 and return x // ht, hy = 13, 9 -// c1 = ht**2+3*hy**2 = 412 (110011100) +// c1 = (ht+hy)/2 = 11 func (e Ext6) Expc1(x *E6) *E6 { x = e.Reduce(x) - result := e.CyclotomicSquare(x) result = e.Mul(result, x) - result = e.nSquareCompressed(result, 3) - result = e.DecompressKarabina(result) - result = e.Mul(result, x) - result = e.CyclotomicSquare(result) + t0 := e.Mul(x, result) + t0 = e.CyclotomicSquare(t0) + result = e.Mul(result, t0) + + return result +} + +// Expc2 set x to x^c1 in E6 and return x +// ht, hy = 13, 9 +// c1 = (ht**2+3*hy**2)/4 = 103 +func (e Ext6) Expc2(x *E6) *E6 { + x = e.Reduce(x) + + result := e.CyclotomicSquare(x) result = e.Mul(result, x) + t0 := e.CyclotomicSquare(result) + t0 = e.nSquareCompressed(t0, 3) + t0 = e.DecompressKarabina(t0) + result = e.Mul(result, t0) result = e.CyclotomicSquare(result) result = e.Mul(result, x) - result = e.nSquareCompressed(result, 2) - return e.DecompressKarabina(result) + return result } // Square034 squares an E6 sparse element of the form diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index dbb8347364..6a6878e20d 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -51,8 +51,7 @@ func NewPairing(api frontend.API) (*Pairing, error) { // 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 12(x₀+1) (El Housni and Guillevic) -// https://eprint.iacr.org/2020/351.pdf +// where s is the cofactor (x₀+1) func (pr Pairing) FinalExponentiation(z *GTEl) *GTEl { result := pr.Copy(z) @@ -65,74 +64,36 @@ func (pr Pairing) FinalExponentiation(z *GTEl) *GTEl { result = pr.Mul(result, buf) // 2. Hard part (up to permutation) - // 12(x₀+1)(p²-p+1)/r - // El Housni and Guillevic - // https://eprint.iacr.org/2020/351.pdf - m1 := pr.Expt(result) - _m1 := pr.Conjugate(m1) - m2 := pr.Expt(m1) - _m2 := pr.Conjugate(m2) - m3 := pr.Expt(m2) - f0 := pr.Frobenius(result) - f0 = pr.Mul(f0, result) - f0 = pr.Mul(f0, m2) - m2 = pr.CyclotomicSquare(_m1) - f0 = pr.Mul(f0, m2) - f0_36 := pr.CyclotomicSquareCompressed(f0) - f0_36 = pr.CyclotomicSquareCompressed(f0_36) - f0_36 = pr.CyclotomicSquareCompressed(f0_36) - f0_36 = pr.DecompressKarabina(f0_36) - f0_36 = pr.Mul(f0_36, f0) - f0_36 = pr.CyclotomicSquare(f0_36) - f0_36 = pr.CyclotomicSquare(f0_36) - g0 := pr.Mul(result, m1) - g0 = pr.Frobenius(g0) - g0 = pr.Mul(g0, m3) - g0 = pr.Mul(g0, _m2) - g0 = pr.Mul(g0, _m1) - g1 := pr.Expt(g0) - _g1 := pr.Conjugate(g1) - g2 := pr.Expt(g1) - g3 := pr.Expt(g2) - _g3 := pr.Conjugate(g3) - g4 := pr.Expt(g3) - _g4 := pr.Conjugate(g4) - g5 := pr.Expt(g4) - _g5 := pr.Conjugate(g5) - g6 := pr.Expt(g5) - gA := pr.Mul(g3, _g5) - gA = pr.CyclotomicSquare(gA) - gA = pr.Mul(gA, g6) - gA = pr.Mul(gA, g1) - gA = pr.Mul(gA, g0) - g034 := pr.Mul(g0, g3) - g034 = pr.Mul(g034, _g4) - gB := pr.CyclotomicSquare(g034) - gB = pr.Mul(gB, g034) - gB = pr.Mul(gB, g5) - gB = pr.Mul(gB, _g1) - _g1g2 := pr.Mul(_g1, g2) - gC := pr.Mul(_g3, _g1g2) - gC = pr.CyclotomicSquare(gC) - gC = pr.Mul(gC, _g1g2) - gC = pr.Mul(gC, g0) - gC = pr.CyclotomicSquare(gC) - gC = pr.Mul(gC, g2) - gC = pr.Mul(gC, g0) - gC = pr.Mul(gC, g4) - - // ht, hy = 13, 9 - // c1 = ht²+3hy² = 412 - h1 := pr.Expc1(gA) - // c2 = ht+hy = 22 - h2 := pr.Expc2(gB) - h2g2C := pr.CyclotomicSquare(gC) - h2g2C = pr.Mul(h2g2C, h2) - h4 := pr.CyclotomicSquare(h2g2C) - h4 = pr.Mul(h4, h2g2C) - h4 = pr.CyclotomicSquare(h4) - result = pr.Mul(h1, h4) - result = pr.Mul(result, f0_36) + // (x₀+1)(p²-p+1)/r + // Algorithm 4.4 from https://yelhousni.github.io/phd.pdf + a := pr.ExptMinus1(result) + a = pr.ExptMinus1(a) + a = pr.Mul(a, pr.Frobenius(result)) + b := pr.ExptPlus1(a) + b = pr.Mul(b, pr.Conjugate(result)) + t := pr.CyclotomicSquare(a) + a = pr.Mul(a, t) + c := pr.ExptMinus1Div3(b) + d := pr.ExptMinus1(c) + e := pr.ExptMinus1(d) + e = pr.ExptMinus1(e) + e = pr.Mul(e, d) + d = pr.Conjugate(d) + f := pr.Mul(d, b) + g := pr.ExptPlus1(e) + g = pr.Mul(g, f) + h := pr.Mul(g, c) + i := pr.Mul(g, d) + i = pr.ExptPlus1(i) + i = pr.Mul(i, pr.Conjugate(f)) + j := pr.Expc1(h) + j = pr.Mul(j, e) + k := pr.CyclotomicSquare(j) + k = pr.Mul(k, j) + k = pr.Mul(k, b) + t = pr.Expc2(i) + k = pr.Mul(k, t) + result = pr.Mul(a, k) return result } diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index d62600e732..557c8597fb 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -44,6 +44,9 @@ func (c *FinalExponentiationCircuit) Define(api frontend.API) error { return fmt.Errorf("new pairing: %w", err) } res := pairing.FinalExponentiation(&c.InGt) + // gnark-crypto exponent is 12(x₀+1) and gnark is (x₀-1) + tmp := pairing.Expc1(res) + res = pairing.Mul(res, tmp) pairing.AssertIsEqual(res, &c.Res) return nil } @@ -73,6 +76,9 @@ func (c *PairCircuit) Define(api frontend.API) error { return fmt.Errorf("new pairing: %w", err) } res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) + // gnark-crypto exponent is 12(x₀+1) and gnark is (x₀-1) + tmp := pairing.Expc1(res) + res = pairing.Mul(res, tmp) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -112,6 +118,8 @@ func (c *MultiPairCircuit) Define(api frontend.API) error { Q = append(Q, &c.InG2) } res, err := pairing.Pair(P, Q) + tmp := pairing.Expc1(res) + res = pairing.Mul(res, tmp) if err != nil { return fmt.Errorf("pair: %w", err) } From 95e0f542e8537a55670d31cfc32c31ab427d033c Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Oct 2023 17:57:32 -0400 Subject: [PATCH 29/38] test(bw6): recude multi-pairing size in tests --- std/algebra/emulated/sw_bw6761/pairing_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 557c8597fb..ce86dc8eb1 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -130,14 +130,14 @@ func (c *MultiPairCircuit) Define(api frontend.API) error { func TestMultiPairTestSolve(t *testing.T) { assert := test.NewAssert(t) p1, q1 := randomG1G2Affines() - p := make([]bw6761.G1Affine, 10) - q := make([]bw6761.G2Affine, 10) - for i := 0; i < 10; i++ { + p := make([]bw6761.G1Affine, 4) + q := make([]bw6761.G2Affine, 4) + for i := 0; i < 4; i++ { p[i] = p1 q[i] = q1 } - for i := 2; i < 10; i++ { + for i := 2; i < 4; i++ { res, err := bw6761.Pair(p[:i], q[:i]) assert.NoError(err) witness := MultiPairCircuit{ From 85ca6316bd46aac6132f6508df89bce27b508299 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Oct 2023 18:10:28 -0400 Subject: [PATCH 30/38] fix: remove outdated test --- std/algebra/emulated/fields_bw6761/e6_test.go | 70 ------------------- 1 file changed, 70 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index 01969e9cee..7dff4577b8 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -364,76 +364,6 @@ func TestExptFp6(t *testing.T) { assert.NoError(err) } -type e6Expc2 struct { - A, B E6 -} - -func (circuit *e6Expc2) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Expc2(&circuit.A) - e.AssertIsEqual(expected, &circuit.B) - return nil -} - -func TestExpc2Fp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var a, b bw6761.E6 - _, _ = a.SetRandom() - - // put a in the cyclotomic subgroup - var tmp bw6761.E6 - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.Frobenius(&tmp).Mul(&a, &tmp) - - b.Expc2(&a) - - witness := e6Expc2{ - A: FromE6(&a), - B: FromE6(&b), - } - - err := test.IsSolved(&e6Expc2{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type e6Expc1 struct { - A, B E6 -} - -func (circuit *e6Expc1) Define(api frontend.API) error { - e := NewExt6(api) - expected := e.Expc1(&circuit.A) - e.AssertIsEqual(expected, &circuit.B) - return nil -} - -func TestExpc1Fp6(t *testing.T) { - assert := test.NewAssert(t) - // witness values - var a, b bw6761.E6 - _, _ = a.SetRandom() - - // put a in the cyclotomic subgroup - var tmp bw6761.E6 - tmp.Conjugate(&a) - a.Inverse(&a) - tmp.Mul(&tmp, &a) - a.Frobenius(&tmp).Mul(&a, &tmp) - - b.Expc1(&a) - - witness := e6Expc1{ - A: FromE6(&a), - B: FromE6(&b), - } - - err := test.IsSolved(&e6Expc1{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) -} - type e6MulBy034 struct { A E6 `gnark:",public"` W E6 From 6c84f3dd3b2a1e4d73fc154959e20667fb5f8fdc Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Oct 2023 21:53:37 -0400 Subject: [PATCH 31/38] perf(bw6): use more efficient addchains --- std/algebra/emulated/fields_bw6761/e3.go | 8 +- .../emulated/fields_bw6761/e6_pairing.go | 124 ++++++++++-------- std/algebra/emulated/sw_bw6761/pairing.go | 18 ++- .../emulated/sw_bw6761/pairing_test.go | 6 +- 4 files changed, 87 insertions(+), 69 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e3.go b/std/algebra/emulated/fields_bw6761/e3.go index 95ad19f1f8..53361f6ab4 100644 --- a/std/algebra/emulated/fields_bw6761/e3.go +++ b/std/algebra/emulated/fields_bw6761/e3.go @@ -173,17 +173,17 @@ func (e Ext3) MulBy01(z *E3, c0, c1 *baseEl) *E3 { } // MulBy1 multiplication of E6 by sparse element (0, c1, 0) -func (e Ext3) MulBy1(z *E3, c1 baseEl) *E3 { +func (e Ext3) MulBy1(z *E3, c1 *baseEl) *E3 { - b := e.fp.Mul(&z.A1, &c1) + b := e.fp.Mul(&z.A1, c1) tmp := e.fp.Add(&z.A1, &z.A2) - t0 := e.fp.Mul(&c1, tmp) + t0 := e.fp.Mul(c1, tmp) t0 = e.fp.Sub(t0, b) t0 = mulFpByNonResidue(e.fp, t0) tmp = e.fp.Add(&z.A0, &z.A1) - t1 := e.fp.Mul(&c1, tmp) + t1 := e.fp.Mul(c1, tmp) t1 = e.fp.Sub(t1, b) return &E3{ diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index cded69ebf6..e1487b564b 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -7,102 +7,122 @@ func (e Ext6) nSquareCompressed(z *E6, n int) *E6 { return z } -// Expt set x to x^t in E6 and return x -func (e Ext6) Expt(x *E6) *E6 { - x = e.Reduce(x) - - // const tAbsVal uint64 = 9586122913090633729 - // tAbsVal in binary: 1000010100001000110000000000000000000000000000000000000000000001 - // drop the low 46 bits (all 0 except the least significant bit): 100001010000100011 = 136227 - // Shortest addition chains can be found at https://wwwhomes.uni-bielefeld.de/achim/addition_chain.html - - // a shortest addition chain for 136227 - result := e.Copy(x) +// ExpX0Minus1 set z to z^{x₀-1} in E6 and return z +// x₀-1 = 91893752504881257682351033800651177983 +func (e Ext6) ExpX0Minus1(z *E6) *E6 { + z = e.Reduce(z) + result := e.Copy(z) result = e.nSquareCompressed(result, 5) result = e.DecompressKarabina(result) - result = e.Mul(result, x) - x33 := e.Copy(result) + result = e.Mul(result, z) + z33 := e.Copy(result) result = e.nSquareCompressed(result, 7) result = e.DecompressKarabina(result) - result = e.Mul(result, x33) + result = e.Mul(result, z33) result = e.nSquareCompressed(result, 4) result = e.DecompressKarabina(result) - result = e.Mul(result, x) + result = e.Mul(result, z) result = e.CyclotomicSquare(result) - result = e.Mul(result, x) - - // the remaining 46 bits + result = e.Mul(result, z) result = e.nSquareCompressed(result, 46) result = e.DecompressKarabina(result) - result = e.Mul(result, x) return result } -// ExptMinus1 set x to x^(t-1) in E6 and return x -func (e Ext6) ExptMinus1(x *E6) *E6 { - result := e.Expt(x) - result = e.Mul(result, e.Conjugate(x)) +// ExpX0Minus1Square set z to z^{(x₀-1)²} in E6 and return z +// (x₀-1)² = 91893752504881257682351033800651177984 +func (e Ext6) ExpX0Minus1Square(z *E6) *E6 { + z = e.Reduce(z) + result := e.Copy(z) + result = e.CyclotomicSquare(result) + t0 := e.Mul(z, result) + t1 := e.CyclotomicSquare(t0) + t0 = e.Mul(t0, t1) + result = e.Mul(result, t0) + t1 = e.Mul(t1, result) + t0 = e.Mul(t0, t1) + t2 := e.CyclotomicSquare(t0) + t2 = e.Mul(t1, t2) + t0 = e.Mul(t0, t2) + t2 = e.nSquareCompressed(t2, 7) + t2 = e.DecompressKarabina(t2) + t1 = e.Mul(t1, t2) + t1 = e.nSquareCompressed(t1, 11) + t1 = e.DecompressKarabina(t1) + t1 = e.Mul(t0, t1) + t1 = e.nSquareCompressed(t1, 9) + t1 = e.DecompressKarabina(t1) + t0 = e.Mul(t0, t1) + t0 = e.CyclotomicSquare(t0) + result = e.Mul(result, t0) + result = e.nSquareCompressed(result, 92) + result = e.DecompressKarabina(result) + return result + } -// ExptPlus1 set x to x^(t+1) in E6 and return x -func (e Ext6) ExptPlus1(x *E6) *E6 { - result := e.Expt(x) - result = e.Mul(result, x) +// ExpX0Plus1 set z to z^(x₀+1) in E6 and return z +// x₀+1 = 91893752504881257682351033800651177985 +func (e Ext6) ExpX0Plus1(z *E6) *E6 { + result := e.ExpX0Minus1(z) + t := e.CyclotomicSquare(z) + result = e.Mul(result, t) return result } -// ExptMinus1Div3 set x to x^(t-1)/3 in E6 and return x -func (e Ext6) ExptMinus1Div3(x *E6) *E6 { - x = e.Reduce(x) - result := e.Copy(x) +// ExpX0Minus1Div3 set z to z^(x₀-1)/3 in E6 and return z +// (x₀-1)/3 = 3195374304363544576 +func (e Ext6) ExptMinus1Div3(z *E6) *E6 { + z = e.Reduce(z) + result := e.Copy(z) result = e.CyclotomicSquare(result) - result = e.Mul(result, x) - t0 := e.Mul(result, x) + result = e.Mul(result, z) + t0 := e.Mul(result, z) t0 = e.CyclotomicSquare(t0) result = e.Mul(result, t0) - t0 = e.CyclotomicSquare(result) - t0 = e.nSquareCompressed(t0, 6) + t0 = result + t0 = e.nSquareCompressed(t0, 7) t0 = e.DecompressKarabina(t0) result = e.Mul(result, t0) result = e.nSquareCompressed(result, 5) result = e.DecompressKarabina(result) - result = e.Mul(result, x) + result = e.Mul(result, z) result = e.nSquareCompressed(result, 46) result = e.DecompressKarabina(result) return result } -// Expc1 set x to x^c2 in E6 and return x +// ExpC1 set z to z^C1 in E6 and return z // ht, hy = 13, 9 -// c1 = (ht+hy)/2 = 11 -func (e Ext6) Expc1(x *E6) *E6 { - x = e.Reduce(x) - result := e.CyclotomicSquare(x) - result = e.Mul(result, x) - t0 := e.Mul(x, result) +// C1 = (ht+hy)/2 = 11 +func (e Ext6) ExpC1(z *E6) *E6 { + z = e.Reduce(z) + result := e.CyclotomicSquare(z) + result = e.Mul(result, z) + t0 := e.Mul(z, result) t0 = e.CyclotomicSquare(t0) result = e.Mul(result, t0) return result } -// Expc2 set x to x^c1 in E6 and return x +// ExpC2 set z to z^C2 in E6 and return z // ht, hy = 13, 9 -// c1 = (ht**2+3*hy**2)/4 = 103 -func (e Ext6) Expc2(x *E6) *E6 { - x = e.Reduce(x) +// C2 = (ht**2+3*hy**2)/4 = 103 +func (e Ext6) ExpC2(z *E6) *E6 { + z = e.Reduce(z) - result := e.CyclotomicSquare(x) - result = e.Mul(result, x) - t0 := e.CyclotomicSquare(result) - t0 = e.nSquareCompressed(t0, 3) + result := e.CyclotomicSquare(z) + result = e.Mul(result, z) + t0 := result + t0 = e.nSquareCompressed(t0, 4) t0 = e.DecompressKarabina(t0) result = e.Mul(result, t0) result = e.CyclotomicSquare(result) - result = e.Mul(result, x) + result = e.Mul(result, z) return result } diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 6a6878e20d..dbd753f5ee 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -66,32 +66,30 @@ func (pr Pairing) FinalExponentiation(z *GTEl) *GTEl { // 2. Hard part (up to permutation) // (x₀+1)(p²-p+1)/r // Algorithm 4.4 from https://yelhousni.github.io/phd.pdf - a := pr.ExptMinus1(result) - a = pr.ExptMinus1(a) + a := pr.ExpX0Minus1Square(result) a = pr.Mul(a, pr.Frobenius(result)) - b := pr.ExptPlus1(a) + b := pr.ExpX0Plus1(a) b = pr.Mul(b, pr.Conjugate(result)) t := pr.CyclotomicSquare(a) a = pr.Mul(a, t) c := pr.ExptMinus1Div3(b) - d := pr.ExptMinus1(c) - e := pr.ExptMinus1(d) - e = pr.ExptMinus1(e) + d := pr.ExpX0Minus1(c) + e := pr.ExpX0Minus1Square(d) e = pr.Mul(e, d) d = pr.Conjugate(d) f := pr.Mul(d, b) - g := pr.ExptPlus1(e) + g := pr.ExpX0Plus1(e) g = pr.Mul(g, f) h := pr.Mul(g, c) i := pr.Mul(g, d) - i = pr.ExptPlus1(i) + i = pr.ExpX0Plus1(i) i = pr.Mul(i, pr.Conjugate(f)) - j := pr.Expc1(h) + j := pr.ExpC1(h) j = pr.Mul(j, e) k := pr.CyclotomicSquare(j) k = pr.Mul(k, j) k = pr.Mul(k, b) - t = pr.Expc2(i) + t = pr.ExpC2(i) k = pr.Mul(k, t) result = pr.Mul(a, k) diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index ce86dc8eb1..994b294420 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -45,7 +45,7 @@ func (c *FinalExponentiationCircuit) Define(api frontend.API) error { } res := pairing.FinalExponentiation(&c.InGt) // gnark-crypto exponent is 12(x₀+1) and gnark is (x₀-1) - tmp := pairing.Expc1(res) + tmp := pairing.ExpC1(res) res = pairing.Mul(res, tmp) pairing.AssertIsEqual(res, &c.Res) return nil @@ -77,7 +77,7 @@ func (c *PairCircuit) Define(api frontend.API) error { } res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) // gnark-crypto exponent is 12(x₀+1) and gnark is (x₀-1) - tmp := pairing.Expc1(res) + tmp := pairing.ExpC1(res) res = pairing.Mul(res, tmp) if err != nil { return fmt.Errorf("pair: %w", err) @@ -118,7 +118,7 @@ func (c *MultiPairCircuit) Define(api frontend.API) error { Q = append(Q, &c.InG2) } res, err := pairing.Pair(P, Q) - tmp := pairing.Expc1(res) + tmp := pairing.ExpC1(res) res = pairing.Mul(res, tmp) if err != nil { return fmt.Errorf("pair: %w", err) From bc201ea36aba20449ac7dbb04ff9214b03c827d4 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Oct 2023 21:57:15 -0400 Subject: [PATCH 32/38] fix(bw6): fix Expt test --- std/algebra/emulated/fields_bw6761/e6_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/std/algebra/emulated/fields_bw6761/e6_test.go b/std/algebra/emulated/fields_bw6761/e6_test.go index 7dff4577b8..77033ba24c 100644 --- a/std/algebra/emulated/fields_bw6761/e6_test.go +++ b/std/algebra/emulated/fields_bw6761/e6_test.go @@ -335,7 +335,8 @@ type e6Expt struct { func (circuit *e6Expt) Define(api frontend.API) error { e := NewExt6(api) - expected := e.Expt(&circuit.A) + expected := e.ExpX0Minus1(&circuit.A) + expected = e.Mul(expected, &circuit.A) e.AssertIsEqual(expected, &circuit.B) return nil } From 4027ddd955babcd6f233ac24e223334ec4c1a51c Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 10 Oct 2023 22:46:54 -0400 Subject: [PATCH 33/38] perf(bw6): manually reducing E12 at some places yields better perf --- std/algebra/emulated/fields_bw6761/e6_pairing.go | 2 ++ std/algebra/emulated/sw_bw6761/pairing.go | 1 + 2 files changed, 3 insertions(+) diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index e1487b564b..3bcc4042c4 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -134,6 +134,7 @@ func (e Ext6) ExpC2(z *E6) *E6 { // B1: E3{A0: c3, A1: c4, A2: 0}, // } func (e *Ext6) Square034(x *E6) *E6 { + x = e.Reduce(x) c0 := E3{ A0: *e.fp.Sub(&x.B0.A0, &x.B1.A0), A1: *e.fp.Neg(&x.B1.A1), @@ -173,6 +174,7 @@ func (e *Ext6) Square034(x *E6) *E6 { // } func (e *Ext6) MulBy034(z *E6, c3, c4 *baseEl) *E6 { + z = e.Reduce(z) a := z.B0 b := z.B1 b = *e.MulBy01(&b, c3, c4) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index dbd753f5ee..60866e71f9 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -54,6 +54,7 @@ func NewPairing(api frontend.API) (*Pairing, error) { // where s is the cofactor (x₀+1) func (pr Pairing) FinalExponentiation(z *GTEl) *GTEl { + z = pr.Reduce(z) result := pr.Copy(z) // 1. Easy part From 2cddf283c82beaf14a30be07266d53ae63edd287 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Wed, 11 Oct 2023 12:23:15 +0200 Subject: [PATCH 34/38] chore: avoid dereferencing into existing Elements --- .../emulated/fields_bw6761/e6_pairing.go | 37 ++-- std/algebra/emulated/sw_bw6761/pairing.go | 206 +++++++++++------- 2 files changed, 149 insertions(+), 94 deletions(-) diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 3bcc4042c4..1e55d911a1 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -135,25 +135,25 @@ func (e Ext6) ExpC2(z *E6) *E6 { // } func (e *Ext6) Square034(x *E6) *E6 { x = e.Reduce(x) - c0 := E3{ + c0 := &E3{ A0: *e.fp.Sub(&x.B0.A0, &x.B1.A0), A1: *e.fp.Neg(&x.B1.A1), A2: *e.fp.Zero(), } - c3 := E3{ + c3 := &E3{ A0: x.B0.A0, A1: *e.fp.Neg(&x.B1.A0), A2: *e.fp.Neg(&x.B1.A1), } - c2 := E3{ + c2 := &E3{ A0: x.B1.A0, A1: x.B1.A1, A2: *e.fp.Zero(), } - c3 = *e.MulBy01(&c3, &c0.A0, &c0.A1) - c3 = *e.Ext3.Add(&c3, &c2) + c3 = e.MulBy01(c3, &c0.A0, &c0.A1) + c3 = e.Ext3.Add(c3, c2) var z E6 z.B1.A0 = *e.fp.Add(&c2.A0, &c2.A0) @@ -176,16 +176,15 @@ func (e *Ext6) MulBy034(z *E6, c3, c4 *baseEl) *E6 { z = e.Reduce(z) a := z.B0 - b := z.B1 - b = *e.MulBy01(&b, c3, c4) + b := e.MulBy01(&z.B1, c3, c4) c3 = e.fp.Add(e.fp.One(), c3) d := e.Ext3.Add(&z.B0, &z.B1) d = e.MulBy01(d, c3, c4) - zC1 := e.Ext3.Add(&a, &b) + zC1 := e.Ext3.Add(&a, b) zC1 = e.Ext3.Neg(zC1) zC1 = e.Ext3.Add(zC1, d) - zC0 := e.Ext3.MulByNonResidue(&b) + zC0 := e.Ext3.MulByNonResidue(b) zC0 = e.Ext3.Add(zC0, &a) return &E6{ @@ -207,7 +206,7 @@ func (e *Ext6) MulBy034(z *E6, c3, c4 *baseEl) *E6 { // C0: E6{B0: 1, B1: 0, B2: 0}, // C1: E6{B0: d3, B1: d4, B2: 0}, // } -func (e *Ext6) Mul034By034(d3, d4, c3, c4 *baseEl) *[5]baseEl { +func (e *Ext6) Mul034By034(d3, d4, c3, c4 *baseEl) [5]*baseEl { x3 := e.fp.Mul(c3, d3) x4 := e.fp.Mul(c4, d4) x04 := e.fp.Add(c4, d4) @@ -225,7 +224,7 @@ func (e *Ext6) Mul034By034(d3, d4, c3, c4 *baseEl) *[5]baseEl { zC1B0 := x03 zC1B1 := x04 - return &[5]baseEl{*zC0B0, *zC0B1, *zC0B2, *zC1B0, *zC1B1} + return [5]*baseEl{zC0B0, zC0B1, zC0B2, zC1B0, zC1B1} } // MulBy01234 multiplies z by an E6 sparse element of the form @@ -234,14 +233,14 @@ func (e *Ext6) Mul034By034(d3, d4, c3, c4 *baseEl) *[5]baseEl { // C0: E3{A0: c0, A1: c1, A2: c2}, // C1: E3{A0: c3, A1: c4, A2: 0}, // } -func (e *Ext6) MulBy01234(z *E6, x *[5]baseEl) *E6 { - c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} +func (e *Ext6) MulBy01234(z *E6, x [5]*baseEl) *E6 { + c0 := &E3{A0: *x[0], A1: *x[1], A2: *x[2]} + c1 := &E3{A0: *x[3], A1: *x[4], A2: *e.fp.Zero()} a := e.Ext3.Add(&z.B0, &z.B1) b := e.Ext3.Add(c0, c1) a = e.Ext3.Mul(a, b) b = e.Ext3.Mul(&z.B0, c0) - c := e.Ext3.MulBy01(&z.B1, &x[3], &x[4]) + c := e.Ext3.MulBy01(&z.B1, x[3], x[4]) z1 := e.Ext3.Sub(a, b) z1 = e.Ext3.Sub(z1, c) z0 := e.Ext3.MulByNonResidue(c) @@ -265,13 +264,13 @@ func (e *Ext6) MulBy01234(z *E6, x *[5]baseEl) *E6 { // C0: E3{A0: 1, A1: 0, A2: 0}, // C1: E3{A0: z3, A1: z4, A2: 0}, // } -func (e *Ext6) Mul01234By034(x *[5]baseEl, z3, z4 *baseEl) *E6 { - c0 := &E3{A0: x[0], A1: x[1], A2: x[2]} - c1 := &E3{A0: x[3], A1: x[4], A2: *e.fp.Zero()} +func (e *Ext6) Mul01234By034(x [5]*baseEl, z3, z4 *baseEl) *E6 { + c0 := &E3{A0: *x[0], A1: *x[1], A2: *x[2]} + c1 := &E3{A0: *x[3], A1: *x[4], A2: *e.fp.Zero()} a := e.Ext3.Add(e.Ext3.One(), &E3{A0: *z3, A1: *z4, A2: *e.fp.Zero()}) b := e.Ext3.Add(c0, c1) a = e.Ext3.Mul(a, b) - c := e.Ext3.Mul01By01(z3, z4, &x[3], &x[4]) + c := e.Ext3.Mul01By01(z3, z4, x[3], x[4]) z1 := e.Ext3.Sub(a, c0) z1 = e.Ext3.Sub(z1, c) z0 := e.Ext3.MulByNonResidue(c) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 60866e71f9..921eda678c 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -211,16 +211,19 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { p1neg[k] = &G1Affine{X: p1[k].X, Y: p0[k].Y} // p01 = p0+p1 and l01 line through p0 and p1 p01[k], l01[k] = pr.addStep(p0[k], p1[k]) - l01[k].R0 = *pr.curveF.MulMod(&l01[k].R0, xNegOverY[k]) - l01[k].R1 = *pr.curveF.MulMod(&l01[k].R1, yInv[k]) + l01[k] = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l01[k].R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l01[k].R1, yInv[k]), + } // p01neg = -p01 p01neg[k] = &G1Affine{X: p01[k].X, Y: *pr.curveF.Neg(&p01[k].Y)} // p10 = p0-p1 + p10kx := pr.curveF.Add(&p0[k].X, &p1[k].X) + p10kx = pr.curveF.Neg(p10kx) p10[k] = &G1Affine{ - X: *pr.curveF.Add(&p0[k].X, &p1[k].X), + X: *p10kx, Y: p1[k].Y, } - p10[k].X = *pr.curveF.Neg(&p10[k].X) // p10neg = -p10 p10neg[k] = &G1Affine{X: p10[k].X, Y: p0[k].Y} // point accumulator initialized to p1 @@ -229,7 +232,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // f_{x₀+1+λ(x₀³-x₀²-x₀),P}(Q) result := pr.Ext6.One() - var prodLines [5]emulated.Element[emulated.BW6761Fp] + var prodLines [5]*emulated.Element[emulated.BW6761Fp] var l, l0 *lineEvaluation // i = 188, separately to avoid an E6 Square @@ -237,37 +240,52 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // k = 0, separately to avoid MulBy034 (res × ℓ) // (assign line to res) pAcc[0], l0 = pr.doubleStep(p1[0]) - result.B1.A0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[0]) - result.B1.A1 = *pr.curveF.MulMod(&l0.R1, yInv[0]) + result.B1 = fields_bw6761.E3{ + A0: *pr.curveF.MulMod(&l0.R0, xNegOverY[0]), + A1: *pr.curveF.MulMod(&l0.R1, yInv[0]), + A2: result.B1.A2, + } if n >= 2 { // k = 1, separately to avoid MulBy034 (res × ℓ) // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) pAcc[1], l0 = pr.doubleStep(pAcc[1]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[1]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[1]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &result.B1.A0, &result.B1.A1) - result.B0.A0 = prodLines[0] - result.B0.A1 = prodLines[1] - result.B0.A2 = prodLines[2] - result.B1.A0 = prodLines[3] - result.B1.A1 = prodLines[4] - + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[1]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[1]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &result.B1.A0, &result.B1.A1) + result = &fields_bw6761.E6{ + B0: fields_bw6761.E3{ + A0: *prodLines[0], + A1: *prodLines[1], + A2: *prodLines[2], + }, + B1: fields_bw6761.E3{ + A0: *prodLines[3], + A1: *prodLines[4], + A2: result.B1.A2, + }, + } } if n >= 3 { // k = 2, separately to avoid MulBy034 (res × ℓ) // (res has a zero E2 element, so we use Mul01234By034) pAcc[2], l0 = pr.doubleStep(pAcc[2]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[2]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[2]) - result = pr.Mul01234By034(&prodLines, &l0.R0, &l0.R1) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[2]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[2]), + } + result = pr.Mul01234By034(prodLines, &l0.R0, &l0.R1) // k >= 3 for k := 3; k < n; k++ { pAcc[k], l0 = pr.doubleStep(pAcc[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } result = pr.MulBy034(result, &l0.R0, &l0.R1) } } @@ -280,8 +298,10 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } for k := 0; k < n; k++ { pAcc[k], l0 = pr.doubleStep(pAcc[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } result = pr.MulBy034(result, &l0.R0, &l0.R1) } @@ -296,76 +316,110 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { switch j { case -4: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p01neg[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) case -3: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p1neg[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) case -2: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p10[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) case -1: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p0neg[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) case 0: pAcc[k], l0 = pr.doubleStep(pAcc[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } result = pr.MulBy034(result, &l0.R0, &l0.R1) case 1: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p0[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) case 2: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p10neg[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) case 3: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p1[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) case 4: pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p01[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) - l.R0 = *pr.curveF.MulMod(&l.R0, xNegOverY[k]) - l.R1 = *pr.curveF.MulMod(&l.R1, yInv[k]) - prodLines = *pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, &prodLines) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } + l = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + } + prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) + result = pr.MulBy01234(result, prodLines) result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) default: return nil, errors.New("invalid loopCounter") @@ -385,8 +439,10 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { result = pr.Square(result) for k := 0; k < n; k++ { l0 = pr.tangentCompute(pAcc[k]) - l0.R0 = *pr.curveF.MulMod(&l0.R0, xNegOverY[k]) - l0.R1 = *pr.curveF.MulMod(&l0.R1, yInv[k]) + l0 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), + } result = pr.MulBy034(result, &l0.R0, &l0.R1) } From f4d077cb8531dbc08cf866ae1b72aa6cd2c1898a Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 11 Oct 2023 11:29:44 -0400 Subject: [PATCH 35/38] refactor(bw6): remove some unnecessary computations --- std/algebra/emulated/sw_bw6761/pairing.go | 157 +++++++--------------- 1 file changed, 45 insertions(+), 112 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 921eda678c..394accf1b9 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -139,7 +139,7 @@ func (pr Pairing) AssertIsEqual(x, y *GTEl) { // seed x₀=9586122913090633729 // // x₀+1 in binary (64 bits) padded with 0s -var loopCounterAlt1 = [190]int8{ +var loopCounter1 = [190]int8{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -151,7 +151,7 @@ var loopCounterAlt1 = [190]int8{ } // x₀³-x₀²-x₀ in 2-NAF -var loopCounterAlt2 = [190]int8{ +var loopCounter2 = [190]int8{ -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -181,18 +181,12 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } // precomputations - p0 := make([]*G1Affine, n) - p1 := make([]*G1Affine, n) - p0neg := make([]*G1Affine, n) - p1neg := make([]*G1Affine, n) - p01 := make([]*G1Affine, n) - p10 := make([]*G1Affine, n) - p01neg := make([]*G1Affine, n) - p10neg := make([]*G1Affine, n) - pAcc := make([]*G1Affine, n) + negP := make([]*G1Affine, n) + imP := make([]*G1Affine, n) + imPneg := make([]*G1Affine, n) + accP := make([]*G1Affine, n) yInv := make([]*emulated.Element[emulated.BW6761Fp], n) xNegOverY := make([]*emulated.Element[emulated.BW6761Fp], n) - l01 := make([]*lineEvaluation, n) for k := 0; k < n; k++ { // P and Q are supposed to be on G1 and G2 respectively of prime order r. @@ -201,33 +195,14 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { yInv[k] = pr.curveF.Inverse(&Q[k].Y) xNegOverY[k] = pr.curveF.MulMod(&Q[k].X, yInv[k]) xNegOverY[k] = pr.curveF.Neg(xNegOverY[k]) - // p0 = P = (x, y) - p0[k] = &G1Affine{X: P[k].X, Y: P[k].Y} - // p0neg = -P = (x, -y) - p0neg[k] = &G1Affine{X: p0[k].X, Y: *pr.curveF.Neg(&p0[k].Y)} - // p1 = (w*x, -y) - p1[k] = &G1Affine{X: *pr.curveF.MulMod(&p0[k].X, &thirdRootOne), Y: p0neg[k].Y} - // p1neg = (w*x, y) - p1neg[k] = &G1Affine{X: p1[k].X, Y: p0[k].Y} - // p01 = p0+p1 and l01 line through p0 and p1 - p01[k], l01[k] = pr.addStep(p0[k], p1[k]) - l01[k] = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l01[k].R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l01[k].R1, yInv[k]), - } - // p01neg = -p01 - p01neg[k] = &G1Affine{X: p01[k].X, Y: *pr.curveF.Neg(&p01[k].Y)} - // p10 = p0-p1 - p10kx := pr.curveF.Add(&p0[k].X, &p1[k].X) - p10kx = pr.curveF.Neg(p10kx) - p10[k] = &G1Affine{ - X: *p10kx, - Y: p1[k].Y, - } - // p10neg = -p10 - p10neg[k] = &G1Affine{X: p10[k].X, Y: p0[k].Y} - // point accumulator initialized to p1 - pAcc[k] = p1[k] + // negP = -P = (x, -y) + negP[k] = &G1Affine{X: P[k].X, Y: *pr.curveF.Neg(&P[k].Y)} + // imP = (w*x, -y) + imP[k] = &G1Affine{X: *pr.curveF.MulMod(&P[k].X, &thirdRootOne), Y: negP[k].Y} + // imPneg = (w*x, y) + imPneg[k] = &G1Affine{X: imP[k].X, Y: P[k].Y} + // point accumulator initialized to imP + accP[k] = imP[k] } // f_{x₀+1+λ(x₀³-x₀²-x₀),P}(Q) @@ -239,7 +214,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // (Square(res) = 1² = 1) // k = 0, separately to avoid MulBy034 (res × ℓ) // (assign line to res) - pAcc[0], l0 = pr.doubleStep(p1[0]) + accP[0], l0 = pr.doubleStep(imP[0]) result.B1 = fields_bw6761.E3{ A0: *pr.curveF.MulMod(&l0.R0, xNegOverY[0]), A1: *pr.curveF.MulMod(&l0.R1, yInv[0]), @@ -249,7 +224,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { if n >= 2 { // k = 1, separately to avoid MulBy034 (res × ℓ) // (res is also a line at this point, so we use Mul034By034 ℓ × ℓ) - pAcc[1], l0 = pr.doubleStep(pAcc[1]) + accP[1], l0 = pr.doubleStep(accP[1]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[1]), R1: *pr.curveF.MulMod(&l0.R1, yInv[1]), @@ -272,7 +247,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { if n >= 3 { // k = 2, separately to avoid MulBy034 (res × ℓ) // (res has a zero E2 element, so we use Mul01234By034) - pAcc[2], l0 = pr.doubleStep(pAcc[2]) + accP[2], l0 = pr.doubleStep(accP[2]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[2]), R1: *pr.curveF.MulMod(&l0.R1, yInv[2]), @@ -281,7 +256,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // k >= 3 for k := 3; k < n; k++ { - pAcc[k], l0 = pr.doubleStep(pAcc[k]) + accP[k], l0 = pr.doubleStep(accP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), @@ -297,7 +272,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { result = pr.Square(result) } for k := 0; k < n; k++ { - pAcc[k], l0 = pr.doubleStep(pAcc[k]) + accP[k], l0 = pr.doubleStep(accP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), @@ -310,25 +285,14 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // (∏ᵢfᵢ)² result = pr.Square(result) - j := loopCounterAlt2[i]*3 + loopCounterAlt1[i] + j := loopCounter2[i]*3 + loopCounter1[i] - for k := 0; k < n; k++ { - switch j { - case -4: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p01neg[k]) - l0 = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), - } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), - } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) - result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) - case -3: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p1neg[k]) + switch j { + // cases -4, -2, 2 and 4 are omitted as they do not occur given the + // static loop counters. + case -3: + for k := 0; k < n; k++ { + accP[k], l0, l = pr.doubleAndAddStep(accP[k], imPneg[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), @@ -339,21 +303,10 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) result = pr.MulBy01234(result, prodLines) - case -2: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p10[k]) - l0 = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), - } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), - } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) - result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) - case -1: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p0neg[k]) + } + case -1: + for k := 0; k < n; k++ { + accP[k], l0, l = pr.doubleAndAddStep(accP[k], negP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), @@ -364,40 +317,19 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) result = pr.MulBy01234(result, prodLines) - case 0: - pAcc[k], l0 = pr.doubleStep(pAcc[k]) + } + case 0: + for k := 0; k < n; k++ { + accP[k], l0 = pr.doubleStep(accP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), } result = pr.MulBy034(result, &l0.R0, &l0.R1) - case 1: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p0[k]) - l0 = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), - } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), - } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) - case 2: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p10neg[k]) - l0 = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), - } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), - } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) - result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) - case 3: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p1[k]) + } + case 1: + for k := 0; k < n; k++ { + accP[k], l0, l = pr.doubleAndAddStep(accP[k], P[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), @@ -408,8 +340,10 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) result = pr.MulBy01234(result, prodLines) - case 4: - pAcc[k], l0, l = pr.doubleAndAddStep(pAcc[k], p01[k]) + } + case 3: + for k := 0; k < n; k++ { + accP[k], l0, l = pr.doubleAndAddStep(accP[k], imP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), @@ -420,10 +354,9 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { } prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) result = pr.MulBy01234(result, prodLines) - result = pr.MulBy034(result, &l01[k].R0, &l01[k].R1) - default: - return nil, errors.New("invalid loopCounter") } + default: + return nil, errors.New("invalid loopCounter") } } @@ -438,7 +371,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // only care about the Miller loop result in Fp6 and not the point itself. result = pr.Square(result) for k := 0; k < n; k++ { - l0 = pr.tangentCompute(pAcc[k]) + l0 = pr.tangentCompute(accP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), From cb9ff6753ef5c7abba944c4435631fa98cace9c6 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 11 Oct 2023 12:25:56 -0400 Subject: [PATCH 36/38] perf(bw6): lines-by-acc mul gives better results than line-by-line mul --- std/algebra/emulated/sw_bw6761/pairing.go | 82 ++++++++++------------- 1 file changed, 37 insertions(+), 45 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 394accf1b9..80d719f7dc 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -99,7 +99,7 @@ func (pr Pairing) FinalExponentiation(z *GTEl) *GTEl { // lineEvaluation represents a sparse Fp6 Elmt (result of the line evaluation) // line: 1 + R0(x/y) + R1(1/y) = 0 instead of R0'*y + R1'*x + R2' = 0 This -// makes the multiplication by lines (MulBy034) and between lines (Mul034By034) +// makes the multiplication by lines (MulBy034) type lineEvaluation struct { R0, R1 emulated.Element[emulated.BW6761Fp] } @@ -208,7 +208,7 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { // f_{x₀+1+λ(x₀³-x₀²-x₀),P}(Q) result := pr.Ext6.One() var prodLines [5]*emulated.Element[emulated.BW6761Fp] - var l, l0 *lineEvaluation + var l0, l1 *lineEvaluation // i = 188, separately to avoid an E6 Square // (Square(res) = 1² = 1) @@ -287,76 +287,68 @@ func (pr Pairing) MillerLoop(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { j := loopCounter2[i]*3 + loopCounter1[i] - switch j { - // cases -4, -2, 2 and 4 are omitted as they do not occur given the - // static loop counters. - case -3: - for k := 0; k < n; k++ { - accP[k], l0, l = pr.doubleAndAddStep(accP[k], imPneg[k]) + for k := 0; k < n; k++ { + switch j { + // cases -4, -2, 2 and 4 are omitted as they do not occur given the + // static loop counters. + case -3: + accP[k], l0, l1 = pr.doubleAndAddStep(accP[k], imPneg[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + result = pr.MulBy034(result, &l0.R0, &l0.R1) + l1 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l1.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l1.R1, yInv[k]), } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) - } - case -1: - for k := 0; k < n; k++ { - accP[k], l0, l = pr.doubleAndAddStep(accP[k], negP[k]) + result = pr.MulBy034(result, &l1.R0, &l1.R1) + case -1: + accP[k], l0, l1 = pr.doubleAndAddStep(accP[k], negP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + result = pr.MulBy034(result, &l0.R0, &l0.R1) + l1 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l1.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l1.R1, yInv[k]), } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) - } - case 0: - for k := 0; k < n; k++ { + result = pr.MulBy034(result, &l1.R0, &l1.R1) + case 0: accP[k], l0 = pr.doubleStep(accP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), } result = pr.MulBy034(result, &l0.R0, &l0.R1) - } - case 1: - for k := 0; k < n; k++ { - accP[k], l0, l = pr.doubleAndAddStep(accP[k], P[k]) + case 1: + accP[k], l0, l1 = pr.doubleAndAddStep(accP[k], P[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + result = pr.MulBy034(result, &l0.R0, &l0.R1) + l1 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l1.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l1.R1, yInv[k]), } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) - } - case 3: - for k := 0; k < n; k++ { - accP[k], l0, l = pr.doubleAndAddStep(accP[k], imP[k]) + result = pr.MulBy034(result, &l1.R0, &l1.R1) + case 3: + accP[k], l0, l1 = pr.doubleAndAddStep(accP[k], imP[k]) l0 = &lineEvaluation{ R0: *pr.curveF.MulMod(&l0.R0, xNegOverY[k]), R1: *pr.curveF.MulMod(&l0.R1, yInv[k]), } - l = &lineEvaluation{ - R0: *pr.curveF.MulMod(&l.R0, xNegOverY[k]), - R1: *pr.curveF.MulMod(&l.R1, yInv[k]), + result = pr.MulBy034(result, &l0.R0, &l0.R1) + l1 = &lineEvaluation{ + R0: *pr.curveF.MulMod(&l1.R0, xNegOverY[k]), + R1: *pr.curveF.MulMod(&l1.R1, yInv[k]), } - prodLines = pr.Mul034By034(&l0.R0, &l0.R1, &l.R0, &l.R1) - result = pr.MulBy01234(result, prodLines) + result = pr.MulBy034(result, &l1.R0, &l1.R1) + default: + return nil, errors.New("invalid loopCounter") } - default: - return nil, errors.New("invalid loopCounter") } } From e9ff5ac39d4b092ec066a8542f20146f35589d3f Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 12 Oct 2023 13:12:46 -0400 Subject: [PATCH 37/38] fix: test final exp without gnark-crypto hack --- std/algebra/emulated/sw_bw6761/pairing_test.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 994b294420..5e9ffbf867 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -44,9 +44,6 @@ func (c *FinalExponentiationCircuit) Define(api frontend.API) error { return fmt.Errorf("new pairing: %w", err) } res := pairing.FinalExponentiation(&c.InGt) - // gnark-crypto exponent is 12(x₀+1) and gnark is (x₀-1) - tmp := pairing.ExpC1(res) - res = pairing.Mul(res, tmp) pairing.AssertIsEqual(res, &c.Res) return nil } @@ -76,9 +73,6 @@ func (c *PairCircuit) Define(api frontend.API) error { return fmt.Errorf("new pairing: %w", err) } res, err := pairing.Pair([]*G1Affine{&c.InG1}, []*G2Affine{&c.InG2}) - // gnark-crypto exponent is 12(x₀+1) and gnark is (x₀-1) - tmp := pairing.ExpC1(res) - res = pairing.Mul(res, tmp) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -118,8 +112,6 @@ func (c *MultiPairCircuit) Define(api frontend.API) error { Q = append(Q, &c.InG2) } res, err := pairing.Pair(P, Q) - tmp := pairing.ExpC1(res) - res = pairing.Mul(res, tmp) if err != nil { return fmt.Errorf("pair: %w", err) } From d183679ddd582a1a9d74b8c3bd3f8698ba6ab914 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 17 Oct 2023 10:40:32 -0400 Subject: [PATCH 38/38] chore: update gnark-crypto to latest --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 7ed857bba6..28c4e209fb 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/bits-and-blooms/bitset v1.8.0 github.com/blang/semver/v4 v4.0.0 github.com/consensys/bavard v0.1.13 - github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb + github.com/consensys/gnark-crypto v0.12.2-0.20231017134050-6652a8b98254 github.com/fxamacker/cbor/v2 v2.5.0 github.com/google/go-cmp v0.5.9 github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b diff --git a/go.sum b/go.sum index 5cd759d52b..cfb98a25a5 100644 --- a/go.sum +++ b/go.sum @@ -4,10 +4,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.12.2-0.20231012161402-206544105834 h1:o+Q1/PSZfNkoUAl/Gf5N/u+H6LkOzR3gF0mS8nRSWYM= -github.com/consensys/gnark-crypto v0.12.2-0.20231012161402-206544105834/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= -github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb h1:f0BMgIjhZy4lSRHCXFbQst85f5agZAjtDMixQqBWNpc= -github.com/consensys/gnark-crypto v0.12.2-0.20231013160410-1f65e75b6dfb/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/consensys/gnark-crypto v0.12.2-0.20231017134050-6652a8b98254 h1:21iGClQpXhJ0PA6lRBpRbpmJFtfdV7R9QMCsQJ3A9mA= +github.com/consensys/gnark-crypto v0.12.2-0.20231017134050-6652a8b98254/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=