From 53ed73b1a637df6b13bd020d5408ddc0d905b06f Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Sun, 14 Jul 2024 17:00:23 +0200 Subject: [PATCH 01/11] perf: eliminate final exp in bls12-377 --- .../emulated/sw_bls12381/pairing_test.go | 9 ++- std/algebra/emulated/sw_bn254/pairing_test.go | 9 ++- .../emulated/sw_bw6761/pairing_test.go | 9 ++- .../native/fields_bls12377/e12_pairing.go | 67 ++++++++++++++++++- std/algebra/native/fields_bls12377/hints.go | 41 ++++++++++++ std/algebra/native/sw_bls12377/pairing.go | 17 +++-- .../native/sw_bls12377/pairing_test.go | 4 +- 7 files changed, 139 insertions(+), 17 deletions(-) diff --git a/std/algebra/emulated/sw_bls12381/pairing_test.go b/std/algebra/emulated/sw_bls12381/pairing_test.go index dc723eb012..76dc6764f2 100644 --- a/std/algebra/emulated/sw_bls12381/pairing_test.go +++ b/std/algebra/emulated/sw_bls12381/pairing_test.go @@ -177,7 +177,7 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error { 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}) + err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2}) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -186,10 +186,13 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error { func TestPairingCheckTestSolve(t *testing.T) { assert := test.NewAssert(t) + // e(a,2b) * e(-2a,b) == 1 p1, q1 := randomG1G2Affines() - _, q2 := randomG1G2Affines() var p2 bls12381.G1Affine - p2.Neg(&p1) + p2.Double(&p1).Neg(&p2) + var q2 bls12381.G2Affine + q2.Set(&q1) + q1.Double(&q1) witness := PairingCheckCircuit{ In1G1: NewG1Affine(p1), In1G2: NewG2Affine(q1), diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index 10a3913ce6..ea7753b0a7 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -214,7 +214,7 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error { 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}) + err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2}) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -223,10 +223,13 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error { func TestPairingCheckTestSolve(t *testing.T) { assert := test.NewAssert(t) + // e(a,2b) * e(-2a,b) == 1 p1, q1 := randomG1G2Affines() - _, q2 := randomG1G2Affines() var p2 bn254.G1Affine - p2.Neg(&p1) + p2.Double(&p1).Neg(&p2) + var q2 bn254.G2Affine + q2.Set(&q1) + q1.Double(&q1) witness := PairingCheckCircuit{ In1G1: NewG1Affine(p1), In1G2: NewG2Affine(q1), diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 06b3276afa..1af31c2016 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -175,7 +175,7 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error { 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}) + err = pairing.PairingCheck([]*G1Affine{&c.In1G1, &c.In2G1}, []*G2Affine{&c.In1G2, &c.In2G2}) if err != nil { return fmt.Errorf("pair: %w", err) } @@ -184,10 +184,13 @@ func (c *PairingCheckCircuit) Define(api frontend.API) error { func TestPairingCheckTestSolve(t *testing.T) { assert := test.NewAssert(t) + // e(a,2b) * e(-2a,b) == 1 p1, q1 := randomG1G2Affines() - _, q2 := randomG1G2Affines() var p2 bw6761.G1Affine - p2.Neg(&p1) + p2.Double(&p1).Neg(&p2) + var q2 bw6761.G2Affine + q2.Set(&q1) + q1.Double(&q1) witness := PairingCheckCircuit{ In1G1: NewG1Affine(p1), In1G2: NewG2Affine(q1), diff --git a/std/algebra/native/fields_bls12377/e12_pairing.go b/std/algebra/native/fields_bls12377/e12_pairing.go index a4895b425d..201e82d0c6 100644 --- a/std/algebra/native/fields_bls12377/e12_pairing.go +++ b/std/algebra/native/fields_bls12377/e12_pairing.go @@ -1,6 +1,8 @@ package fields_bls12377 -import "github.com/consensys/gnark/frontend" +import ( + "github.com/consensys/gnark/frontend" +) // nSquareKarabina2345 repeated compressed cyclotmic square func (e *E12) nSquareKarabina2345(api frontend.API, n int) { @@ -125,7 +127,7 @@ func (e *E12) MulBy01234(api frontend.API, x [5]E2) *E12 { return e } -// ExpX0 compute e1^X0, where X0=9586122913090633729 +// ExpX0 compute e1^X0, where X0=0x8508c00000000001 func (e *E12) ExpX0(api frontend.API, e1 E12) *E12 { res := e1 @@ -148,7 +150,7 @@ func (e *E12) ExpX0(api frontend.API, e1 E12) *E12 { } -// ExpX0Minus1Square computes e1^((X0-1)^2), where X0=9586122913090633729 +// ExpX0Minus1Square computes e1^((X0-1)^2), where X0=0x8508c00000000001 func (e *E12) ExpX0Minus1Square(api frontend.API, e1 E12) *E12 { var t0, t1, t2, t3, res E12 @@ -176,3 +178,62 @@ func (e *E12) ExpX0Minus1Square(api frontend.API, e1 E12) *E12 { return e } + +// ExpU compute e1^U, where U=(X0-1)^2/3 and X0=0x8508c00000000001 +func (e *E12) ExpU(api frontend.API, e1 E12) *E12 { + + var t0, t1, t2, t3 E12 + t0.CyclotomicSquare(api, e1) + e.Mul(api, e1, t0) + t0.Mul(api, t0, *e) + t1.CyclotomicSquare(api, t0) + t2.Mul(api, e1, t1) + t1.CyclotomicSquare(api, t2) + t1.Mul(api, e1, t1) + t3.CyclotomicSquare(api, t1) + t3.nSquareKarabina2345(api, 7) + t2.Mul(api, t2, t3) + t2.nSquareKarabina2345(api, 6) + t1.Mul(api, t1, t2) + t1.nSquareKarabina2345(api, 4) + t0.Mul(api, t0, t1) + t0.nSquareKarabina2345(api, 4) + t0.Mul(api, e1, t0) + t0.nSquareKarabina2345(api, 6) + e.Mul(api, *e, t0) + e.nSquareKarabina2345(api, 92) + + return e +} + +// FinalExponentiationCheck checks that a Miller function output x lies in the +// same equivalence class as the reduced pairing. This replaces the final +// exponentiation step in-circuit. +// The method follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen. +// +// [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf +func (x *E12) FinalExponentiationCheck(api frontend.API) *E12 { + res, err := api.NewHint(finalExpHint, 12, x.C0.B0.A0, x.C0.B0.A1, x.C0.B1.A0, x.C0.B1.A1, x.C0.B2.A0, x.C0.B2.A1, x.C1.B0.A0, x.C1.B0.A1, x.C1.B1.A0, x.C1.B1.A1, x.C1.B2.A0, x.C1.B2.A1) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + + var residueWitness, t0, t1 E12 + residueWitness.assign(res[:12]) + + // Check that x == residueWitness^r by checking that: + // x^k == residueWitness^(q-u) + // where k = (u-1)^2/3, u=0x8508c00000000001 the BLS12-377 seed + // and residueWitness from the hint. + t0.Frobenius(api, residueWitness) + // exponentiation by u + t1.ExpX0(api, residueWitness) + t0.DivUnchecked(api, t0, t1) + // exponentiation by U=(u-1)^2/3 + t1.ExpU(api, *x) + + t0.AssertIsEqual(api, t1) + + return nil +} diff --git a/std/algebra/native/fields_bls12377/hints.go b/std/algebra/native/fields_bls12377/hints.go index 9138ca997c..b7503343eb 100644 --- a/std/algebra/native/fields_bls12377/hints.go +++ b/std/algebra/native/fields_bls12377/hints.go @@ -15,6 +15,7 @@ func GetHints() []solver.Hint { inverseE2Hint, inverseE6Hint, inverseE12Hint, + finalExpHint, } } @@ -183,3 +184,43 @@ func inverseE12Hint(_ *big.Int, inputs []*big.Int, res []*big.Int) error { return nil } + +func finalExpHint(_ *big.Int, inputs, outputs []*big.Int) error { + // This follows section 4.1 of https://eprint.iacr.org/2024/640.pdf (Th. 1) + var millerLoop, residueWitness bls12377.E12 + var rInv big.Int + + millerLoop.C0.B0.A0.SetBigInt(inputs[0]) + millerLoop.C0.B0.A1.SetBigInt(inputs[1]) + millerLoop.C0.B1.A0.SetBigInt(inputs[2]) + millerLoop.C0.B1.A1.SetBigInt(inputs[3]) + millerLoop.C0.B2.A0.SetBigInt(inputs[4]) + millerLoop.C0.B2.A1.SetBigInt(inputs[5]) + millerLoop.C1.B0.A0.SetBigInt(inputs[6]) + millerLoop.C1.B0.A1.SetBigInt(inputs[7]) + millerLoop.C1.B1.A0.SetBigInt(inputs[8]) + millerLoop.C1.B1.A1.SetBigInt(inputs[9]) + millerLoop.C1.B2.A0.SetBigInt(inputs[10]) + millerLoop.C1.B2.A1.SetBigInt(inputs[11]) + + // compute r-th root: + // Exponentiate to rInv where + // rInv = 1/r mod (p^12-1)/r + rInv.SetString("10208748786837724877156759805239199917177088105850452097581398357781838343395535357717189281303817989030853426518402432972842003898131337328036123767288343472740290055992597327047843733820915319931789911460922332092893340406586659134897564792528992841360758317184371729851779228767022291575333619687450508149408291808665688425747351943672765401504532352302933193380323797562510328188696218527623617803336085522730402534476672219254656573543964081905263468361471236263414063051754798717965006033535737262535426559024265337552194770692912115634227850824349287678445075667227500062644865552196269410386640693498752691820776510255615212781349365166954324182425386812172284685207545357376138024103577229096125179210029150108085917426622002636460159193270457153824484424740291304472618370893768349010724508505559223070061402562692522679753894779470665228357312064233458448950731987606712484774731314132451528794596084167373606499619244419076763354699647800243598600024554183146018594109053978613659377521869110074983807776968064443737295525761159893356460041590615623520614511285178649677625190127954790028024727845772017830862600186003274909528270245217670645634670358694128233669703480796660621582552083085232238280068277127279315415621696399036462472389073266975782056160166232984523898881", 10) + residueWitness.Exp(millerLoop, &rInv) + + residueWitness.C0.B0.A0.BigInt(outputs[0]) + residueWitness.C0.B0.A1.BigInt(outputs[1]) + residueWitness.C0.B1.A0.BigInt(outputs[2]) + residueWitness.C0.B1.A1.BigInt(outputs[3]) + residueWitness.C0.B2.A0.BigInt(outputs[4]) + residueWitness.C0.B2.A1.BigInt(outputs[5]) + residueWitness.C1.B0.A0.BigInt(outputs[6]) + residueWitness.C1.B0.A1.BigInt(outputs[7]) + residueWitness.C1.B1.A0.BigInt(outputs[8]) + residueWitness.C1.B1.A1.BigInt(outputs[9]) + residueWitness.C1.B2.A0.BigInt(outputs[10]) + residueWitness.C1.B2.A1.BigInt(outputs[11]) + + return nil +} diff --git a/std/algebra/native/sw_bls12377/pairing.go b/std/algebra/native/sw_bls12377/pairing.go index fa9febf7c3..759a55ca9b 100644 --- a/std/algebra/native/sw_bls12377/pairing.go +++ b/std/algebra/native/sw_bls12377/pairing.go @@ -253,13 +253,22 @@ func Pair(api frontend.API, P []G1Affine, Q []G2Affine) (GT, error) { // // This function doesn't check that the inputs are in the correct subgroups func PairingCheck(api frontend.API, P []G1Affine, Q []G2Affine) error { - f, err := Pair(api, P, Q) + f, err := MillerLoop(api, P, Q) if err != nil { return err } - var one GT - one.SetOne() - f.AssertIsEqual(api, one) + // We perform the easy part of the final exp to push f to the cyclotomic + // subgroup so that FinalExponentiationCheck is carried with optimized + // cyclotomic squaring (e.g. Karabina12345). + // + // f = f^(p⁶-1)(p²+1) + var buf GT + buf.Conjugate(api, f) + buf.DivUnchecked(api, buf, f) + f.FrobeniusSquare(api, buf). + Mul(api, f, buf) + + f.FinalExponentiationCheck(api) return nil } diff --git a/std/algebra/native/sw_bls12377/pairing_test.go b/std/algebra/native/sw_bls12377/pairing_test.go index 7524263c6e..1956764d2a 100644 --- a/std/algebra/native/sw_bls12377/pairing_test.go +++ b/std/algebra/native/sw_bls12377/pairing_test.go @@ -251,9 +251,11 @@ func pairingData() (P bls12377.G1Affine, Q bls12377.G2Affine, milRes, pairingRes } func pairingCheckData() (P [2]bls12377.G1Affine, Q [2]bls12377.G2Affine) { + // e(a,2b) * e(-2a,b) == 1 _, _, P[0], Q[0] = bls12377.Generators() - P[1].Neg(&P[0]) + P[1].Double(&P[0]).Neg(&P[1]) Q[1].Set(&Q[0]) + Q[0].Double(&Q[0]) return } From 5a08f35dd264a544ce2f738babc43801fb31ffa1 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 16 Jul 2024 08:54:22 +0100 Subject: [PATCH 02/11] fix(bls12-377): use FinalExponentiationCheck in pairing2.go --- std/algebra/native/sw_bls12377/pairing2.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/std/algebra/native/sw_bls12377/pairing2.go b/std/algebra/native/sw_bls12377/pairing2.go index f977ab916d..5d6e736368 100644 --- a/std/algebra/native/sw_bls12377/pairing2.go +++ b/std/algebra/native/sw_bls12377/pairing2.go @@ -298,13 +298,11 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { for i := range Q { inQ[i] = *Q[i] } - res, err := Pair(p.api, inP, inQ) + res, err := MillerLoop(p.api, inP, inQ) if err != nil { return err } - var one fields_bls12377.E12 - one.SetOne() - res.AssertIsEqual(p.api, one) + res.FinalExponentiationCheck(p.api) return nil } From 6e61298eb6718592f7bd400c36230b87836b9fd5 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 16 Jul 2024 12:18:12 +0100 Subject: [PATCH 03/11] fix(bls12-377): push to cyclo group in pairing2.go --- std/algebra/native/sw_bls12377/pairing2.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/std/algebra/native/sw_bls12377/pairing2.go b/std/algebra/native/sw_bls12377/pairing2.go index 5d6e736368..a633024d24 100644 --- a/std/algebra/native/sw_bls12377/pairing2.go +++ b/std/algebra/native/sw_bls12377/pairing2.go @@ -302,6 +302,16 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { if err != nil { return err } + // We perform the easy part of the final exp to push res to the cyclotomic + // subgroup so that FinalExponentiationCheck is carried with optimized + // cyclotomic squaring (e.g. Karabina12345). + // + // res = res^(p⁶-1)(p²+1) + var buf GT + buf.Conjugate(p.api, res) + buf.DivUnchecked(p.api, buf, res) + res.FrobeniusSquare(p.api, buf).Mul(p.api, res, buf) + res.FinalExponentiationCheck(p.api) return nil } From 654df8084103b99da13d1336ebf57e6e71b71b76 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Wed, 17 Jul 2024 15:01:03 +0100 Subject: [PATCH 04/11] perf: eliminate final exp in bls12-381 optimized --- .../emulated/fields_bls12381/e12_pairing.go | 30 +++-- std/algebra/emulated/fields_bls12381/hints.go | 111 ++++++++++++++---- 2 files changed, 111 insertions(+), 30 deletions(-) diff --git a/std/algebra/emulated/fields_bls12381/e12_pairing.go b/std/algebra/emulated/fields_bls12381/e12_pairing.go index 7bbb60b7e5..a4e958c547 100644 --- a/std/algebra/emulated/fields_bls12381/e12_pairing.go +++ b/std/algebra/emulated/fields_bls12381/e12_pairing.go @@ -387,17 +387,18 @@ func (e Ext12) FrobeniusSquareTorus(y *E6) *E6 { // FinalExponentiationCheck checks that a Miller function output x lies in the // same equivalence class as the reduced pairing. This replaces the final // exponentiation step in-circuit. -// The method follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen. +// The method is inspired from [On Proving Pairings] paper by A. Novakovic and +// L. Eagen, and is based on a personal communication with A. Novakovic. // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { - res, err := e.fp.NewHint(finalExpHint, 12, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) + res, err := e.fp.NewHint(finalExpHint, 24, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) if err != nil { // err is non-nil only for invalid number of inputs panic(err) } - residueWitness := E12{ + scalingFactor := E12{ C0: E6{ B0: E2{A0: *res[0], A1: *res[1]}, B1: E2{A0: *res[2], A1: *res[3]}, @@ -409,17 +410,28 @@ func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { B2: E2{A0: *res[10], A1: *res[11]}, }, } + residueWitness := E12{ + C0: E6{ + B0: E2{A0: *res[12], A1: *res[13]}, + B1: E2{A0: *res[14], A1: *res[15]}, + B2: E2{A0: *res[16], A1: *res[17]}, + }, + C1: E6{ + B0: E2{A0: *res[18], A1: *res[19]}, + B1: E2{A0: *res[20], A1: *res[21]}, + B2: E2{A0: *res[22], A1: *res[23]}, + }, + } - // Check that x == residueWitness^r by checking that: - // x^k == residueWitness^(q-u) - // where k = (u-1)^2/3, u=-0xd201000000010000 the BLS12-381 seed - // and residueWitness from the hint. + // Check that x * scalingFactor == residueWitness^(q-u) + // where u=-0xd201000000010000 is the BLS12-381 seed, + // and residueWitness, scalingFactor from the hint. t0 := e.Frobenius(&residueWitness) // exponentiation by -u t1 := e.Expt(&residueWitness) t0 = e.Mul(t0, t1) - // exponentiation by U=(u-1)^2/3 - t1 = e.ExpByU(x) + + t1 = e.Mul(x, &scalingFactor) e.AssertIsEqual(t0, t1) diff --git a/std/algebra/emulated/fields_bls12381/hints.go b/std/algebra/emulated/fields_bls12381/hints.go index 320aaacb9d..ad33d0daff 100644 --- a/std/algebra/emulated/fields_bls12381/hints.go +++ b/std/algebra/emulated/fields_bls12381/hints.go @@ -271,11 +271,11 @@ func divE12Hint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) erro } func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) error { - // This follows section 4.1 of https://eprint.iacr.org/2024/640.pdf (Th. 1) + // This is inspired from https://eprint.iacr.org/2024/640.pdf + // and based on a personal communication with the author Andrija Novakovic. return emulated.UnwrapHint(nativeInputs, nativeOutputs, func(mod *big.Int, inputs, outputs []*big.Int) error { - var millerLoop, residueWitness bls12381.E12 - var rInv big.Int + var millerLoop bls12381.E12 millerLoop.C0.B0.A0.SetBigInt(inputs[0]) millerLoop.C0.B0.A1.SetBigInt(inputs[1]) @@ -290,24 +290,93 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er millerLoop.C1.B2.A0.SetBigInt(inputs[10]) millerLoop.C1.B2.A1.SetBigInt(inputs[11]) - // compute r-th root: - // Exponentiate to rInv where - // rInv = 1/r mod (p^12-1)/r - rInv.SetString("169662389312441398885310937191698694666993326870281216192803558492181163400934408837135364582394949149589560242411491538960982200559697133935443307582773537814554128992403254243871087441488619811839498788505657962013599019994544063402394719913759780901881538869078447034832302535303591303383830742161317593225991746471557492001710830538428792119562309446698444646787667517629943447802199824630112988907247336627481159245442124709621313522294197747687500252452962523217400829932174349352696726049683687654879009114460723993703760367089269403767790334911644010940272722630305066645230222732316445557889124653426141642271480304669447694344127599708992364443461893123938202386892312748211835322692697497854107961493711137028209148238339237355911496376520814450515612396561384525661635220451168152178239892009375229296874955612623691164738926395993739297557487207643426168321070539996994036837992284584225139752716615623194417718962478029165908544042568334172107008712033983002554672734519081879196926275059798317879322062358113986901925780890205936071364647548199159506709147492864081514759663116291487638998943660232689862634717010538047493292265992334130695994203833154950619462266484292385471162124464248375625748097868775829652908052615424796255913420292818674303286242639225711610323988077268116737", 10) - residueWitness.Exp(millerLoop, &rInv) - - residueWitness.C0.B0.A0.BigInt(outputs[0]) - residueWitness.C0.B0.A1.BigInt(outputs[1]) - residueWitness.C0.B1.A0.BigInt(outputs[2]) - residueWitness.C0.B1.A1.BigInt(outputs[3]) - residueWitness.C0.B2.A0.BigInt(outputs[4]) - residueWitness.C0.B2.A1.BigInt(outputs[5]) - residueWitness.C1.B0.A0.BigInt(outputs[6]) - residueWitness.C1.B0.A1.BigInt(outputs[7]) - residueWitness.C1.B1.A0.BigInt(outputs[8]) - residueWitness.C1.B1.A1.BigInt(outputs[9]) - residueWitness.C1.B2.A0.BigInt(outputs[10]) - residueWitness.C1.B2.A1.BigInt(outputs[11]) + var y, wj, rootPthInverse, root27thInverse, residueWitness, scalingFactor bls12381.E12 + var ord, pw, exp, h3, v, vInv, s, p big.Int + // p = (1-x)/3 + p.SetString("5044125407647214251", 10) + // h3 = ((q**12 - 1) / r) / (27 * p) + h3.SetString("2366356426548243601069753987687709088104621721678962410379583120840019275952471579477684846670499039076873213559162845121989217658133790336552276567078487633052653005423051750848782286407340332979263075575489766963251914185767058009683318020965829271737924625612375201545022326908440428522712877494557944965298566001441468676802477524234094954960009227631543471415676620753242466901942121887152806837594306028649150255258504417829961387165043999299071444887652375514277477719817175923289019181393803729926249507024121957184340179467502106891835144220611408665090353102353194448552304429530104218473070114105759487413726485729058069746063140422361472585604626055492939586602274983146215294625774144156395553405525711143696689756441298365274341189385646499074862712688473936093315628166094221735056483459332831845007196600723053356837526749543765815988577005929923802636375670820616189737737304893769679803809426304143627363860243558537831172903494450556755190448279875942974830469855835666815454271389438587399739607656399812689280234103023464545891697941661992848552456326290792224091557256350095392859243101357349751064730561345062266850238821755009430903520645523345000326783803935359711318798844368754833295302563158150573540616830138810935344206231367357992991289265295323280", 10) + + // 1. get pth-root inverse + v.Mul(&h3, big.NewInt(27)) + wj.Exp(millerLoop, &v) + if wj.IsOne() { + rootPthInverse.SetOne() + } else { + vInv.ModInverse(&v, &p) + s.Neg(&vInv).Mod(&s, &p) + rootPthInverse.Exp(wj, &s) + } + + // 2.1. get order of 3rd primitive root + exp.Mul(&p, &h3) + y.Exp(millerLoop, &exp) + if y.IsOne() { + pw.SetUint64(0) + } + y.Exp(y, big.NewInt(3)) + if y.IsOne() { + pw.SetUint64(1) + } + y.Exp(y, big.NewInt(3)) + if y.IsOne() { + pw.SetUint64(2) + } + y.Exp(y, big.NewInt(3)) + if y.IsOne() { + pw.SetUint64(3) + } + + // 2.2. get 27th root inverse + if pw.Uint64() == 0 { + root27thInverse.SetOne() + } else { + ord.Exp(big.NewInt(3), &pw, nil) + v.Mul(&p, &h3) + wj.Exp(millerLoop, &v) + vInv.ModInverse(&v, &ord) + s.Neg(&vInv).Mod(&s, &ord) + root27thInverse.Exp(wj, &s) + } + + // return the scaling factor so that millerLoop * scalingFactor is of order h3 + scalingFactor.Mul(&rootPthInverse, &root27thInverse) + + scalingFactor.C0.B0.A0.BigInt(outputs[0]) + scalingFactor.C0.B0.A1.BigInt(outputs[1]) + scalingFactor.C0.B1.A0.BigInt(outputs[2]) + scalingFactor.C0.B1.A1.BigInt(outputs[3]) + scalingFactor.C0.B2.A0.BigInt(outputs[4]) + scalingFactor.C0.B2.A1.BigInt(outputs[5]) + scalingFactor.C1.B0.A0.BigInt(outputs[6]) + scalingFactor.C1.B0.A1.BigInt(outputs[7]) + scalingFactor.C1.B1.A0.BigInt(outputs[8]) + scalingFactor.C1.B1.A1.BigInt(outputs[9]) + scalingFactor.C1.B2.A0.BigInt(outputs[10]) + scalingFactor.C1.B2.A1.BigInt(outputs[11]) + + millerLoop.Mul(&millerLoop, &scalingFactor) + + // 3. get the witness residue + // lambda = q - u + var lambda big.Int + lambda.SetString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539", 10) + exp.ModInverse(&lambda, &h3) + residueWitness.Exp(millerLoop, &exp) + + // return the witness residue + residueWitness.C0.B0.A0.BigInt(outputs[12]) + residueWitness.C0.B0.A1.BigInt(outputs[13]) + residueWitness.C0.B1.A0.BigInt(outputs[14]) + residueWitness.C0.B1.A1.BigInt(outputs[15]) + residueWitness.C0.B2.A0.BigInt(outputs[16]) + residueWitness.C0.B2.A1.BigInt(outputs[17]) + residueWitness.C1.B0.A0.BigInt(outputs[18]) + residueWitness.C1.B0.A1.BigInt(outputs[19]) + residueWitness.C1.B1.A0.BigInt(outputs[20]) + residueWitness.C1.B1.A1.BigInt(outputs[21]) + residueWitness.C1.B2.A0.BigInt(outputs[22]) + residueWitness.C1.B2.A1.BigInt(outputs[23]) return nil }) From 064b02cd97065056452f5da49817e4e2a006dc0c Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 18 Jul 2024 14:22:59 +0100 Subject: [PATCH 05/11] refactor: clean code --- std/algebra/emulated/fields_bls12381/hints.go | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/std/algebra/emulated/fields_bls12381/hints.go b/std/algebra/emulated/fields_bls12381/hints.go index ad33d0daff..584d7ed939 100644 --- a/std/algebra/emulated/fields_bls12381/hints.go +++ b/std/algebra/emulated/fields_bls12381/hints.go @@ -290,58 +290,71 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er millerLoop.C1.B2.A0.SetBigInt(inputs[10]) millerLoop.C1.B2.A1.SetBigInt(inputs[11]) - var y, wj, rootPthInverse, root27thInverse, residueWitness, scalingFactor bls12381.E12 - var ord, pw, exp, h3, v, vInv, s, p big.Int - // p = (1-x)/3 - p.SetString("5044125407647214251", 10) - // h3 = ((q**12 - 1) / r) / (27 * p) - h3.SetString("2366356426548243601069753987687709088104621721678962410379583120840019275952471579477684846670499039076873213559162845121989217658133790336552276567078487633052653005423051750848782286407340332979263075575489766963251914185767058009683318020965829271737924625612375201545022326908440428522712877494557944965298566001441468676802477524234094954960009227631543471415676620753242466901942121887152806837594306028649150255258504417829961387165043999299071444887652375514277477719817175923289019181393803729926249507024121957184340179467502106891835144220611408665090353102353194448552304429530104218473070114105759487413726485729058069746063140422361472585604626055492939586602274983146215294625774144156395553405525711143696689756441298365274341189385646499074862712688473936093315628166094221735056483459332831845007196600723053356837526749543765815988577005929923802636375670820616189737737304893769679803809426304143627363860243558537831172903494450556755190448279875942974830469855835666815454271389438587399739607656399812689280234103023464545891697941661992848552456326290792224091557256350095392859243101357349751064730561345062266850238821755009430903520645523345000326783803935359711318798844368754833295302563158150573540616830138810935344206231367357992991289265295323280", 10) + var root, rootPthInverse, root27thInverse, residueWitness, scalingFactor bls12381.E12 + var order3rd, order3rdPower, exponent, exponentInv, finalExpFactor, polyFactor big.Int + // polyFactor = (1-x)/3 + polyFactor.SetString("5044125407647214251", 10) + // finalExpFactor = ((q^12 - 1) / r) / (27 * polyFactor) + finalExpFactor.SetString("2366356426548243601069753987687709088104621721678962410379583120840019275952471579477684846670499039076873213559162845121989217658133790336552276567078487633052653005423051750848782286407340332979263075575489766963251914185767058009683318020965829271737924625612375201545022326908440428522712877494557944965298566001441468676802477524234094954960009227631543471415676620753242466901942121887152806837594306028649150255258504417829961387165043999299071444887652375514277477719817175923289019181393803729926249507024121957184340179467502106891835144220611408665090353102353194448552304429530104218473070114105759487413726485729058069746063140422361472585604626055492939586602274983146215294625774144156395553405525711143696689756441298365274341189385646499074862712688473936093315628166094221735056483459332831845007196600723053356837526749543765815988577005929923802636375670820616189737737304893769679803809426304143627363860243558537831172903494450556755190448279875942974830469855835666815454271389438587399739607656399812689280234103023464545891697941661992848552456326290792224091557256350095392859243101357349751064730561345062266850238821755009430903520645523345000326783803935359711318798844368754833295302563158150573540616830138810935344206231367357992991289265295323280", 10) // 1. get pth-root inverse - v.Mul(&h3, big.NewInt(27)) - wj.Exp(millerLoop, &v) - if wj.IsOne() { + exponent.Mul(&finalExpFactor, big.NewInt(27)) + root.Exp(millerLoop, &exponent) + if root.IsOne() { rootPthInverse.SetOne() } else { - vInv.ModInverse(&v, &p) - s.Neg(&vInv).Mod(&s, &p) - rootPthInverse.Exp(wj, &s) + exponentInv.ModInverse(&exponent, &polyFactor) + exponent.Neg(&exponentInv).Mod(&exponent, &polyFactor) + rootPthInverse.Exp(root, &exponent) } // 2.1. get order of 3rd primitive root - exp.Mul(&p, &h3) - y.Exp(millerLoop, &exp) - if y.IsOne() { - pw.SetUint64(0) + var three big.Int + three.SetUint64(3) + exponent.Mul(&polyFactor, &finalExpFactor) + root.Exp(millerLoop, &exponent) + if root.IsOne() { + order3rdPower.SetUint64(0) } - y.Exp(y, big.NewInt(3)) - if y.IsOne() { - pw.SetUint64(1) + root.Exp(root, &three) + if root.IsOne() { + order3rdPower.SetUint64(1) } - y.Exp(y, big.NewInt(3)) - if y.IsOne() { - pw.SetUint64(2) + root.Exp(root, &three) + if root.IsOne() { + order3rdPower.SetUint64(2) } - y.Exp(y, big.NewInt(3)) - if y.IsOne() { - pw.SetUint64(3) + root.Exp(root, &three) + if root.IsOne() { + order3rdPower.SetUint64(3) } // 2.2. get 27th root inverse - if pw.Uint64() == 0 { + if order3rdPower.Uint64() == 0 { root27thInverse.SetOne() } else { - ord.Exp(big.NewInt(3), &pw, nil) - v.Mul(&p, &h3) - wj.Exp(millerLoop, &v) - vInv.ModInverse(&v, &ord) - s.Neg(&vInv).Mod(&s, &ord) - root27thInverse.Exp(wj, &s) + order3rd.Exp(&three, &order3rdPower, nil) + exponent.Mul(&polyFactor, &finalExpFactor) + root.Exp(millerLoop, &exponent) + exponentInv.ModInverse(&exponent, &order3rd) + exponent.Neg(&exponentInv).Mod(&exponent, &order3rd) + root27thInverse.Exp(root, &exponent) } - // return the scaling factor so that millerLoop * scalingFactor is of order h3 + // 2.3. shift the Miller loop result so that millerLoop * scalingFactor + // is of order finalExpFactor scalingFactor.Mul(&rootPthInverse, &root27thInverse) + millerLoop.Mul(&millerLoop, &scalingFactor) + + // 3. get the witness residue + // + // lambda = q - u, the optimal exponent + var lambda big.Int + lambda.SetString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539", 10) + exponent.ModInverse(&lambda, &finalExpFactor) + residueWitness.Exp(millerLoop, &exponent) + // return the scaling factor scalingFactor.C0.B0.A0.BigInt(outputs[0]) scalingFactor.C0.B0.A1.BigInt(outputs[1]) scalingFactor.C0.B1.A0.BigInt(outputs[2]) @@ -355,15 +368,6 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er scalingFactor.C1.B2.A0.BigInt(outputs[10]) scalingFactor.C1.B2.A1.BigInt(outputs[11]) - millerLoop.Mul(&millerLoop, &scalingFactor) - - // 3. get the witness residue - // lambda = q - u - var lambda big.Int - lambda.SetString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129030796414117214202539", 10) - exp.ModInverse(&lambda, &h3) - residueWitness.Exp(millerLoop, &exp) - // return the witness residue residueWitness.C0.B0.A0.BigInt(outputs[12]) residueWitness.C0.B0.A1.BigInt(outputs[13]) From faedf43d289a0d05b283cb83a8a114ec31918a67 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Fri, 19 Jul 2024 12:28:48 +0100 Subject: [PATCH 06/11] fix: avoid malicious hint in BLS12-381 final exp check --- std/algebra/emulated/fields_bls12381/e12_pairing.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/std/algebra/emulated/fields_bls12381/e12_pairing.go b/std/algebra/emulated/fields_bls12381/e12_pairing.go index a4e958c547..51121269db 100644 --- a/std/algebra/emulated/fields_bls12381/e12_pairing.go +++ b/std/algebra/emulated/fields_bls12381/e12_pairing.go @@ -398,17 +398,14 @@ func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { panic(err) } + // constrain cubicNonResiduePower to be in Fp6 scalingFactor := E12{ C0: E6{ B0: E2{A0: *res[0], A1: *res[1]}, B1: E2{A0: *res[2], A1: *res[3]}, B2: E2{A0: *res[4], A1: *res[5]}, }, - C1: E6{ - B0: E2{A0: *res[6], A1: *res[7]}, - B1: E2{A0: *res[8], A1: *res[9]}, - B2: E2{A0: *res[10], A1: *res[11]}, - }, + C1: (*e.Ext6.Zero()), } residueWitness := E12{ C0: E6{ From 154fbf8f53e2f7e5208de90da321ab8392dbb21f Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 23 Jul 2024 12:38:09 +0100 Subject: [PATCH 07/11] fix: remove unconstrained and unused variables --- .../emulated/fields_bls12381/e12_pairing.go | 20 ++++----- std/algebra/emulated/fields_bls12381/hints.go | 44 ++++++++----------- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/std/algebra/emulated/fields_bls12381/e12_pairing.go b/std/algebra/emulated/fields_bls12381/e12_pairing.go index 51121269db..a59a3d192f 100644 --- a/std/algebra/emulated/fields_bls12381/e12_pairing.go +++ b/std/algebra/emulated/fields_bls12381/e12_pairing.go @@ -392,32 +392,32 @@ func (e Ext12) FrobeniusSquareTorus(y *E6) *E6 { // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { - res, err := e.fp.NewHint(finalExpHint, 24, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) + res, err := e.fp.NewHint(finalExpHint, 18, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) if err != nil { // err is non-nil only for invalid number of inputs panic(err) } - // constrain cubicNonResiduePower to be in Fp6 - scalingFactor := E12{ + residueWitness := E12{ C0: E6{ B0: E2{A0: *res[0], A1: *res[1]}, B1: E2{A0: *res[2], A1: *res[3]}, B2: E2{A0: *res[4], A1: *res[5]}, }, - C1: (*e.Ext6.Zero()), + C1: E6{ + B0: E2{A0: *res[6], A1: *res[7]}, + B1: E2{A0: *res[8], A1: *res[9]}, + B2: E2{A0: *res[10], A1: *res[11]}, + }, } - residueWitness := E12{ + // constrain cubicNonResiduePower to be in Fp6 + scalingFactor := E12{ C0: E6{ B0: E2{A0: *res[12], A1: *res[13]}, B1: E2{A0: *res[14], A1: *res[15]}, B2: E2{A0: *res[16], A1: *res[17]}, }, - C1: E6{ - B0: E2{A0: *res[18], A1: *res[19]}, - B1: E2{A0: *res[20], A1: *res[21]}, - B2: E2{A0: *res[22], A1: *res[23]}, - }, + C1: (*e.Ext6.Zero()), } // Check that x * scalingFactor == residueWitness^(q-u) diff --git a/std/algebra/emulated/fields_bls12381/hints.go b/std/algebra/emulated/fields_bls12381/hints.go index 584d7ed939..e5cc172606 100644 --- a/std/algebra/emulated/fields_bls12381/hints.go +++ b/std/algebra/emulated/fields_bls12381/hints.go @@ -354,33 +354,27 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er exponent.ModInverse(&lambda, &finalExpFactor) residueWitness.Exp(millerLoop, &exponent) - // return the scaling factor - scalingFactor.C0.B0.A0.BigInt(outputs[0]) - scalingFactor.C0.B0.A1.BigInt(outputs[1]) - scalingFactor.C0.B1.A0.BigInt(outputs[2]) - scalingFactor.C0.B1.A1.BigInt(outputs[3]) - scalingFactor.C0.B2.A0.BigInt(outputs[4]) - scalingFactor.C0.B2.A1.BigInt(outputs[5]) - scalingFactor.C1.B0.A0.BigInt(outputs[6]) - scalingFactor.C1.B0.A1.BigInt(outputs[7]) - scalingFactor.C1.B1.A0.BigInt(outputs[8]) - scalingFactor.C1.B1.A1.BigInt(outputs[9]) - scalingFactor.C1.B2.A0.BigInt(outputs[10]) - scalingFactor.C1.B2.A1.BigInt(outputs[11]) + // return the witness residue + residueWitness.C0.B0.A0.BigInt(outputs[0]) + residueWitness.C0.B0.A1.BigInt(outputs[1]) + residueWitness.C0.B1.A0.BigInt(outputs[2]) + residueWitness.C0.B1.A1.BigInt(outputs[3]) + residueWitness.C0.B2.A0.BigInt(outputs[4]) + residueWitness.C0.B2.A1.BigInt(outputs[5]) + residueWitness.C1.B0.A0.BigInt(outputs[6]) + residueWitness.C1.B0.A1.BigInt(outputs[7]) + residueWitness.C1.B1.A0.BigInt(outputs[8]) + residueWitness.C1.B1.A1.BigInt(outputs[9]) + residueWitness.C1.B2.A0.BigInt(outputs[10]) + residueWitness.C1.B2.A1.BigInt(outputs[11]) // return the witness residue - residueWitness.C0.B0.A0.BigInt(outputs[12]) - residueWitness.C0.B0.A1.BigInt(outputs[13]) - residueWitness.C0.B1.A0.BigInt(outputs[14]) - residueWitness.C0.B1.A1.BigInt(outputs[15]) - residueWitness.C0.B2.A0.BigInt(outputs[16]) - residueWitness.C0.B2.A1.BigInt(outputs[17]) - residueWitness.C1.B0.A0.BigInt(outputs[18]) - residueWitness.C1.B0.A1.BigInt(outputs[19]) - residueWitness.C1.B1.A0.BigInt(outputs[20]) - residueWitness.C1.B1.A1.BigInt(outputs[21]) - residueWitness.C1.B2.A0.BigInt(outputs[22]) - residueWitness.C1.B2.A1.BigInt(outputs[23]) + scalingFactor.C0.B0.A0.BigInt(outputs[12]) + scalingFactor.C0.B0.A1.BigInt(outputs[13]) + scalingFactor.C0.B1.A0.BigInt(outputs[14]) + scalingFactor.C0.B1.A1.BigInt(outputs[15]) + scalingFactor.C0.B2.A0.BigInt(outputs[16]) + scalingFactor.C0.B2.A1.BigInt(outputs[17]) return nil }) From 81cff65af7399c32b5bcf3b42b5f7ad7d599e500 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 23 Jul 2024 15:48:10 +0100 Subject: [PATCH 08/11] fix(pairing): fix benchmarks --- .../emulated/sw_bls12381/pairing_test.go | 8 +++--- std/algebra/emulated/sw_bn254/pairing_test.go | 8 +++--- .../emulated/sw_bw6761/pairing_test.go | 26 ++++++++++--------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/std/algebra/emulated/sw_bls12381/pairing_test.go b/std/algebra/emulated/sw_bls12381/pairing_test.go index 76dc6764f2..9ffdd18d56 100644 --- a/std/algebra/emulated/sw_bls12381/pairing_test.go +++ b/std/algebra/emulated/sw_bls12381/pairing_test.go @@ -231,11 +231,13 @@ func TestGroupMembershipSolve(t *testing.T) { // bench func BenchmarkPairing(b *testing.B) { - + // e(a,2b) * e(-2a,b) == 1 p1, q1 := randomG1G2Affines() - _, q2 := randomG1G2Affines() var p2 bls12381.G1Affine - p2.Neg(&p1) + p2.Double(&p1).Neg(&p2) + var q2 bls12381.G2Affine + q2.Set(&q1) + q1.Double(&q1) witness := PairingCheckCircuit{ In1G1: NewG1Affine(p1), In1G2: NewG2Affine(q1), diff --git a/std/algebra/emulated/sw_bn254/pairing_test.go b/std/algebra/emulated/sw_bn254/pairing_test.go index 30a9e3d5a4..8b49607441 100644 --- a/std/algebra/emulated/sw_bn254/pairing_test.go +++ b/std/algebra/emulated/sw_bn254/pairing_test.go @@ -480,11 +480,13 @@ func TestIsMillerLoopAndFinalExpCircuitTestSolve(t *testing.T) { // bench func BenchmarkPairing(b *testing.B) { - + // e(a,2b) * e(-2a,b) == 1 p1, q1 := randomG1G2Affines() - _, q2 := randomG1G2Affines() var p2 bn254.G1Affine - p2.Neg(&p1) + p2.Double(&p1).Neg(&p2) + var q2 bn254.G2Affine + q2.Set(&q1) + q1.Double(&q1) witness := PairingCheckCircuit{ In1G1: NewG1Affine(p1), In1G2: NewG2Affine(q1), diff --git a/std/algebra/emulated/sw_bw6761/pairing_test.go b/std/algebra/emulated/sw_bw6761/pairing_test.go index 1af31c2016..65f087a555 100644 --- a/std/algebra/emulated/sw_bw6761/pairing_test.go +++ b/std/algebra/emulated/sw_bw6761/pairing_test.go @@ -229,16 +229,18 @@ func TestGroupMembershipSolve(t *testing.T) { // bench 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), - Res: NewGTEl(res), + // e(a,2b) * e(-2a,b) == 1 + p1, q1 := randomG1G2Affines() + var p2 bw6761.G1Affine + p2.Double(&p1).Neg(&p2) + var q2 bw6761.G2Affine + q2.Set(&q1) + q1.Double(&q1) + witness := PairingCheckCircuit{ + In1G1: NewG1Affine(p1), + In1G2: NewG2Affine(q1), + In2G1: NewG1Affine(p2), + In2G2: NewG2Affine(q2), } w, err := frontend.NewWitness(&witness, ecc.BN254.ScalarField()) if err != nil { @@ -248,7 +250,7 @@ func BenchmarkPairing(b *testing.B) { 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 { + if ccs, err = frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &PairingCheckCircuit{}); err != nil { b.Fatal(err) } } @@ -270,7 +272,7 @@ func BenchmarkPairing(b *testing.B) { 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 { + if ccs, err = frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &PairingCheckCircuit{}); err != nil { b.Fatal(err) } } From f16bc35dcf7169bbfb48ff816dc576055ae4e9bc Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 25 Jul 2024 12:43:42 +0100 Subject: [PATCH 09/11] perf: optimize final exp bls12-377 --- std/algebra/emulated/fields_bls12381/hints.go | 2 +- .../native/fields_bls12377/e12_pairing.go | 30 +++++++--- std/algebra/native/fields_bls12377/hints.go | 59 ++++++++++++++++--- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/std/algebra/emulated/fields_bls12381/hints.go b/std/algebra/emulated/fields_bls12381/hints.go index e5cc172606..77863d3ffa 100644 --- a/std/algebra/emulated/fields_bls12381/hints.go +++ b/std/algebra/emulated/fields_bls12381/hints.go @@ -368,7 +368,7 @@ func finalExpHint(nativeMod *big.Int, nativeInputs, nativeOutputs []*big.Int) er residueWitness.C1.B2.A0.BigInt(outputs[10]) residueWitness.C1.B2.A1.BigInt(outputs[11]) - // return the witness residue + // return the scaling factor scalingFactor.C0.B0.A0.BigInt(outputs[12]) scalingFactor.C0.B0.A1.BigInt(outputs[13]) scalingFactor.C0.B1.A0.BigInt(outputs[14]) diff --git a/std/algebra/native/fields_bls12377/e12_pairing.go b/std/algebra/native/fields_bls12377/e12_pairing.go index 201e82d0c6..434fde8be6 100644 --- a/std/algebra/native/fields_bls12377/e12_pairing.go +++ b/std/algebra/native/fields_bls12377/e12_pairing.go @@ -213,25 +213,37 @@ func (e *E12) ExpU(api frontend.API, e1 E12) *E12 { // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf func (x *E12) FinalExponentiationCheck(api frontend.API) *E12 { - res, err := api.NewHint(finalExpHint, 12, x.C0.B0.A0, x.C0.B0.A1, x.C0.B1.A0, x.C0.B1.A1, x.C0.B2.A0, x.C0.B2.A1, x.C1.B0.A0, x.C1.B0.A1, x.C1.B1.A0, x.C1.B1.A1, x.C1.B2.A0, x.C1.B2.A1) + res, err := api.NewHint(finalExpHint, 18, x.C0.B0.A0, x.C0.B0.A1, x.C0.B1.A0, x.C0.B1.A1, x.C0.B2.A0, x.C0.B2.A1, x.C1.B0.A0, x.C1.B0.A1, x.C1.B1.A0, x.C1.B1.A1, x.C1.B2.A0, x.C1.B2.A1) if err != nil { // err is non-nil only for invalid number of inputs panic(err) } - var residueWitness, t0, t1 E12 + var residueWitness, scalingFactor, t0, t1 E12 residueWitness.assign(res[:12]) - - // Check that x == residueWitness^r by checking that: - // x^k == residueWitness^(q-u) - // where k = (u-1)^2/3, u=0x8508c00000000001 the BLS12-377 seed - // and residueWitness from the hint. + // constrain cubicNonResiduePower to be in Fp6 + scalingFactor.C0.B0.A0 = res[12] + scalingFactor.C0.B0.A1 = res[13] + scalingFactor.C0.B1.A0 = res[14] + scalingFactor.C0.B1.A1 = res[15] + scalingFactor.C0.B2.A0 = res[16] + scalingFactor.C0.B2.A1 = res[17] + scalingFactor.C1.B0.A0 = 0 + scalingFactor.C1.B0.A1 = 0 + scalingFactor.C1.B1.A0 = 0 + scalingFactor.C1.B1.A1 = 0 + scalingFactor.C1.B2.A0 = 0 + scalingFactor.C1.B2.A1 = 0 + + // Check that x * scalingFactor == residueWitness^(q-u) + // where u=0x8508c00000000001 is the BLS12-377 seed, + // and residueWitness, scalingFactor from the hint. t0.Frobenius(api, residueWitness) // exponentiation by u t1.ExpX0(api, residueWitness) t0.DivUnchecked(api, t0, t1) - // exponentiation by U=(u-1)^2/3 - t1.ExpU(api, *x) + + t1.Mul(api, *x, scalingFactor) t0.AssertIsEqual(api, t1) diff --git a/std/algebra/native/fields_bls12377/hints.go b/std/algebra/native/fields_bls12377/hints.go index b7503343eb..bc92f3ce97 100644 --- a/std/algebra/native/fields_bls12377/hints.go +++ b/std/algebra/native/fields_bls12377/hints.go @@ -186,9 +186,7 @@ func inverseE12Hint(_ *big.Int, inputs []*big.Int, res []*big.Int) error { } func finalExpHint(_ *big.Int, inputs, outputs []*big.Int) error { - // This follows section 4.1 of https://eprint.iacr.org/2024/640.pdf (Th. 1) - var millerLoop, residueWitness bls12377.E12 - var rInv big.Int + var millerLoop bls12377.E12 millerLoop.C0.B0.A0.SetBigInt(inputs[0]) millerLoop.C0.B0.A1.SetBigInt(inputs[1]) @@ -203,12 +201,49 @@ func finalExpHint(_ *big.Int, inputs, outputs []*big.Int) error { millerLoop.C1.B2.A0.SetBigInt(inputs[10]) millerLoop.C1.B2.A1.SetBigInt(inputs[11]) - // compute r-th root: - // Exponentiate to rInv where - // rInv = 1/r mod (p^12-1)/r - rInv.SetString("10208748786837724877156759805239199917177088105850452097581398357781838343395535357717189281303817989030853426518402432972842003898131337328036123767288343472740290055992597327047843733820915319931789911460922332092893340406586659134897564792528992841360758317184371729851779228767022291575333619687450508149408291808665688425747351943672765401504532352302933193380323797562510328188696218527623617803336085522730402534476672219254656573543964081905263468361471236263414063051754798717965006033535737262535426559024265337552194770692912115634227850824349287678445075667227500062644865552196269410386640693498752691820776510255615212781349365166954324182425386812172284685207545357376138024103577229096125179210029150108085917426622002636460159193270457153824484424740291304472618370893768349010724508505559223070061402562692522679753894779470665228357312064233458448950731987606712484774731314132451528794596084167373606499619244419076763354699647800243598600024554183146018594109053978613659377521869110074983807776968064443737295525761159893356460041590615623520614511285178649677625190127954790028024727845772017830862600186003274909528270245217670645634670358694128233669703480796660621582552083085232238280068277127279315415621696399036462472389073266975782056160166232984523898881", 10) - residueWitness.Exp(millerLoop, &rInv) + var root, rootPthInverse, root2304thInverse, residueWitness, scalingFactor bls12377.E12 + var exponent, exponentInv, finalExpFactor, polyFactor big.Int + // polyFactor = (1-x)/192 + polyFactor.SetString("49927723505680384", 10) + // finalExpFactor = ((q^12 - 1) / r) / (2304 * polyFactor) + finalExpFactor.SetString("92351561334497520756349650336409370070948672672207914824247073415859727964231807559847070685040742345026775319680739143654748316009031763764029886042408725311062057776702838555815712331129279611544378217895455619058809454575474763035923260395518532422855090028311239234310116353269618927871828693919559964406939845784130633021661399269804065961999062695977580539176029238189119059338698461832966347603096853909366901376879505972606045770762516580639801134008192256366142553202619529638202068488750102055204336502584141399828818871664747496033599618827160583206926869573005874449182200210044444351826855938563862937638034918413235278166699461287943529570559518592586872860190313088429391521694808994276205429071153237122495989095857292965461625387657577981811772819764071512345106346232882471034669258055302790607847924560040527682025558360106509628206144255667203317787586698694011876342903106644003067103035176245790275561392007119121995936066014208972135762663107247939004517852248103325700169848524693333524025685325993207375736519358185783520948988673594976115901587076295116293065682366935313875411927779217584729138600463438806153265891176654957439524358472291492028580820575807385461119025678550977847392818655362610734928283105671242634809807533919011078145", 10) + + // 1. get pth-root inverse + exponent.Mul(&finalExpFactor, big.NewInt(2304)) + root.Exp(millerLoop, &exponent) + if root.IsOne() { + rootPthInverse.SetOne() + } else { + exponentInv.ModInverse(&exponent, &polyFactor) + exponent.Neg(&exponentInv).Mod(&exponent, &polyFactor) + rootPthInverse.Exp(root, &exponent) + } + + // 2. get 2304th-root inverse + exponent.Mul(&finalExpFactor, &polyFactor) + root.Exp(millerLoop, &exponent) + if root.IsOne() { + root2304thInverse.SetOne() + } else { + exponentInv.ModInverse(&exponent, big.NewInt(2304)) + exponent.Neg(&exponentInv).Mod(&exponent, big.NewInt(2304)) + root2304thInverse.Exp(root, &exponent) + } + // 3. shift the Miller loop result so that millerLoop * scalingFactor + // is of order finalExpFactor + scalingFactor.Mul(&rootPthInverse, &root2304thInverse) + millerLoop.Mul(&millerLoop, &scalingFactor) + + // 4. get the witness residue + // + // lambda = q - u, the optimal exponent + var lambda big.Int + lambda.SetString("258664426012969094010652733694893533536393512754914660539884262666720468348340822774968888139563774001527230824448", 10) + exponent.ModInverse(&lambda, &finalExpFactor) + residueWitness.Exp(millerLoop, &exponent) + + // return the witness residue residueWitness.C0.B0.A0.BigInt(outputs[0]) residueWitness.C0.B0.A1.BigInt(outputs[1]) residueWitness.C0.B1.A0.BigInt(outputs[2]) @@ -222,5 +257,13 @@ func finalExpHint(_ *big.Int, inputs, outputs []*big.Int) error { residueWitness.C1.B2.A0.BigInt(outputs[10]) residueWitness.C1.B2.A1.BigInt(outputs[11]) + // return the scaling factor + scalingFactor.C0.B0.A0.BigInt(outputs[12]) + scalingFactor.C0.B0.A1.BigInt(outputs[13]) + scalingFactor.C0.B1.A0.BigInt(outputs[14]) + scalingFactor.C0.B1.A1.BigInt(outputs[15]) + scalingFactor.C0.B2.A0.BigInt(outputs[16]) + scalingFactor.C0.B2.A1.BigInt(outputs[17]) + return nil } From 2b7adb6aef5a318441c587ddcc4d7ba0fb2f5540 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 25 Jul 2024 14:05:32 +0100 Subject: [PATCH 10/11] fix(bls12-377): hint computation for final exp --- .../native/fields_bls12377/e12_pairing.go | 7 +----- std/algebra/native/fields_bls12377/hints.go | 25 ++++++------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/std/algebra/native/fields_bls12377/e12_pairing.go b/std/algebra/native/fields_bls12377/e12_pairing.go index 434fde8be6..60684d23d7 100644 --- a/std/algebra/native/fields_bls12377/e12_pairing.go +++ b/std/algebra/native/fields_bls12377/e12_pairing.go @@ -228,12 +228,7 @@ func (x *E12) FinalExponentiationCheck(api frontend.API) *E12 { scalingFactor.C0.B1.A1 = res[15] scalingFactor.C0.B2.A0 = res[16] scalingFactor.C0.B2.A1 = res[17] - scalingFactor.C1.B0.A0 = 0 - scalingFactor.C1.B0.A1 = 0 - scalingFactor.C1.B1.A0 = 0 - scalingFactor.C1.B1.A1 = 0 - scalingFactor.C1.B2.A0 = 0 - scalingFactor.C1.B2.A1 = 0 + scalingFactor.C1.SetZero() // Check that x * scalingFactor == residueWitness^(q-u) // where u=0x8508c00000000001 is the BLS12-377 seed, diff --git a/std/algebra/native/fields_bls12377/hints.go b/std/algebra/native/fields_bls12377/hints.go index bc92f3ce97..0325cf4aea 100644 --- a/std/algebra/native/fields_bls12377/hints.go +++ b/std/algebra/native/fields_bls12377/hints.go @@ -201,16 +201,16 @@ func finalExpHint(_ *big.Int, inputs, outputs []*big.Int) error { millerLoop.C1.B2.A0.SetBigInt(inputs[10]) millerLoop.C1.B2.A1.SetBigInt(inputs[11]) - var root, rootPthInverse, root2304thInverse, residueWitness, scalingFactor bls12377.E12 + var root, rootPthInverse, residueWitness, scalingFactor bls12377.E12 var exponent, exponentInv, finalExpFactor, polyFactor big.Int - // polyFactor = (1-x)/192 - polyFactor.SetString("49927723505680384", 10) - // finalExpFactor = ((q^12 - 1) / r) / (2304 * polyFactor) + // polyFactor = 12(x-1) + polyFactor.SetString("115033474957087604736", 10) + // finalExpFactor = ((q^12 - 1) / r) / polyFactor finalExpFactor.SetString("92351561334497520756349650336409370070948672672207914824247073415859727964231807559847070685040742345026775319680739143654748316009031763764029886042408725311062057776702838555815712331129279611544378217895455619058809454575474763035923260395518532422855090028311239234310116353269618927871828693919559964406939845784130633021661399269804065961999062695977580539176029238189119059338698461832966347603096853909366901376879505972606045770762516580639801134008192256366142553202619529638202068488750102055204336502584141399828818871664747496033599618827160583206926869573005874449182200210044444351826855938563862937638034918413235278166699461287943529570559518592586872860190313088429391521694808994276205429071153237122495989095857292965461625387657577981811772819764071512345106346232882471034669258055302790607847924560040527682025558360106509628206144255667203317787586698694011876342903106644003067103035176245790275561392007119121995936066014208972135762663107247939004517852248103325700169848524693333524025685325993207375736519358185783520948988673594976115901587076295116293065682366935313875411927779217584729138600463438806153265891176654957439524358472291492028580820575807385461119025678550977847392818655362610734928283105671242634809807533919011078145", 10) // 1. get pth-root inverse - exponent.Mul(&finalExpFactor, big.NewInt(2304)) - root.Exp(millerLoop, &exponent) + exponent.Set(&finalExpFactor) + root.Exp(millerLoop, &finalExpFactor) if root.IsOne() { rootPthInverse.SetOne() } else { @@ -219,20 +219,9 @@ func finalExpHint(_ *big.Int, inputs, outputs []*big.Int) error { rootPthInverse.Exp(root, &exponent) } - // 2. get 2304th-root inverse - exponent.Mul(&finalExpFactor, &polyFactor) - root.Exp(millerLoop, &exponent) - if root.IsOne() { - root2304thInverse.SetOne() - } else { - exponentInv.ModInverse(&exponent, big.NewInt(2304)) - exponent.Neg(&exponentInv).Mod(&exponent, big.NewInt(2304)) - root2304thInverse.Exp(root, &exponent) - } - // 3. shift the Miller loop result so that millerLoop * scalingFactor // is of order finalExpFactor - scalingFactor.Mul(&rootPthInverse, &root2304thInverse) + scalingFactor.Set(&rootPthInverse) millerLoop.Mul(&millerLoop, &scalingFactor) // 4. get the witness residue From 831eac45c7171ecd37bf806de13f6860801081e1 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 25 Jul 2024 15:55:03 +0100 Subject: [PATCH 11/11] refactor: apply review suggestions --- std/algebra/emulated/fields_bls12381/e12_pairing.go | 6 ++---- std/algebra/emulated/fields_bn254/e12_pairing.go | 6 ++---- std/algebra/emulated/fields_bw6761/e6_pairing.go | 6 ++---- std/algebra/emulated/sw_bls12381/pairing.go | 4 ++-- std/algebra/emulated/sw_bn254/pairing.go | 4 ++-- std/algebra/emulated/sw_bw6761/pairing.go | 4 ++-- std/algebra/native/fields_bls12377/e12_pairing.go | 6 ++---- std/algebra/native/sw_bls12377/pairing.go | 4 ++-- std/algebra/native/sw_bls12377/pairing2.go | 4 ++-- 9 files changed, 18 insertions(+), 26 deletions(-) diff --git a/std/algebra/emulated/fields_bls12381/e12_pairing.go b/std/algebra/emulated/fields_bls12381/e12_pairing.go index a59a3d192f..a2a3f25ebc 100644 --- a/std/algebra/emulated/fields_bls12381/e12_pairing.go +++ b/std/algebra/emulated/fields_bls12381/e12_pairing.go @@ -384,14 +384,14 @@ func (e Ext12) FrobeniusSquareTorus(y *E6) *E6 { return &E6{B0: *t0, B1: *t1, B2: *t2} } -// FinalExponentiationCheck checks that a Miller function output x lies in the +// AssertFinalExponentiationIsOne checks that a Miller function output x lies in the // same equivalence class as the reduced pairing. This replaces the final // exponentiation step in-circuit. // The method is inspired from [On Proving Pairings] paper by A. Novakovic and // L. Eagen, and is based on a personal communication with A. Novakovic. // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { +func (e Ext12) AssertFinalExponentiationIsOne(x *E12) { res, err := e.fp.NewHint(finalExpHint, 18, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) if err != nil { // err is non-nil only for invalid number of inputs @@ -431,8 +431,6 @@ func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { t1 = e.Mul(x, &scalingFactor) e.AssertIsEqual(t0, t1) - - return nil } func (e Ext12) Frobenius(x *E12) *E12 { diff --git a/std/algebra/emulated/fields_bn254/e12_pairing.go b/std/algebra/emulated/fields_bn254/e12_pairing.go index 64d0d11fb8..f7c93c75e7 100644 --- a/std/algebra/emulated/fields_bn254/e12_pairing.go +++ b/std/algebra/emulated/fields_bn254/e12_pairing.go @@ -422,13 +422,13 @@ func (e Ext12) FrobeniusCubeTorus(y *E6) *E6 { return res } -// FinalExponentiationCheck checks that a Miller function output x lies in the +// AssertFinalExponentiationIsOne checks that a Miller function output x lies in the // same equivalence class as the reduced pairing. This replaces the final // exponentiation step in-circuit. // The method follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen. // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { +func (e Ext12) AssertFinalExponentiationIsOne(x *E12) { res, err := e.fp.NewHint(finalExpHint, 24, &x.C0.B0.A0, &x.C0.B0.A1, &x.C0.B1.A0, &x.C0.B1.A1, &x.C0.B2.A0, &x.C0.B2.A1, &x.C1.B0.A0, &x.C1.B0.A1, &x.C1.B1.A0, &x.C1.B1.A1, &x.C1.B2.A0, &x.C1.B2.A1) if err != nil { // err is non-nil only for invalid number of inputs @@ -474,8 +474,6 @@ func (e Ext12) FinalExponentiationCheck(x *E12) *E12 { t0 = e.Mul(t0, t1) e.AssertIsEqual(t0, t2) - - return nil } func (e Ext12) Frobenius(x *E12) *E12 { diff --git a/std/algebra/emulated/fields_bw6761/e6_pairing.go b/std/algebra/emulated/fields_bw6761/e6_pairing.go index 8361ed8146..8907c0535b 100644 --- a/std/algebra/emulated/fields_bw6761/e6_pairing.go +++ b/std/algebra/emulated/fields_bw6761/e6_pairing.go @@ -322,13 +322,13 @@ func (e *Ext6) MulBy02345(z *E6, x [5]*baseEl) *E6 { } } -// FinalExponentiationCheck checks that a Miller function output x lies in the +// AssertFinalExponentiationIsOne checks that a Miller function output x lies in the // same equivalence class as the reduced pairing. This replaces the final // exponentiation step in-circuit. // The method is adapted from Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen. // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (e Ext6) FinalExponentiationCheck(x *E6) *E6 { +func (e Ext6) AssertFinalExponentiationIsOne(x *E6) { res, err := e.fp.NewHint(finalExpHint, 6, &x.A0, &x.A1, &x.A2, &x.A3, &x.A4, &x.A5) if err != nil { // err is non-nil only for invalid number of inputs @@ -357,8 +357,6 @@ func (e Ext6) FinalExponentiationCheck(x *E6) *E6 { t0 = e.DivUnchecked(t0, t1) e.AssertIsEqual(t0, x) - - return nil } // ExpByU2 set z to z^(x₀+1) in E12 and return z diff --git a/std/algebra/emulated/sw_bls12381/pairing.go b/std/algebra/emulated/sw_bls12381/pairing.go index c5f96e62d8..4bc99671d6 100644 --- a/std/algebra/emulated/sw_bls12381/pairing.go +++ b/std/algebra/emulated/sw_bls12381/pairing.go @@ -251,7 +251,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { } // We perform the easy part of the final exp to push f to the cyclotomic - // subgroup so that FinalExponentiationCheck is carried with optimized + // subgroup so that AssertFinalExponentiationIsOne is carried with optimized // cyclotomic squaring (e.g. Karabina12345). // // f = f^(p⁶-1)(p²+1) @@ -260,7 +260,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { f = pr.FrobeniusSquare(buf) f = pr.Mul(f, buf) - pr.FinalExponentiationCheck(f) + pr.AssertFinalExponentiationIsOne(f) return nil } diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 9f6567946e..61a7e051e6 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -251,7 +251,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { } // We perform the easy part of the final exp to push f to the cyclotomic - // subgroup so that FinalExponentiationCheck is carried with optimized + // subgroup so that AssertFinalExponentiationIsOne is carried with optimized // cyclotomic squaring (e.g. Karabina12345). // // f = f^(p⁶-1)(p²+1) @@ -260,7 +260,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { f = pr.FrobeniusSquare(buf) f = pr.Mul(f, buf) - pr.FinalExponentiationCheck(f) + pr.AssertFinalExponentiationIsOne(f) return nil } diff --git a/std/algebra/emulated/sw_bw6761/pairing.go b/std/algebra/emulated/sw_bw6761/pairing.go index 88485288cb..47ad915567 100644 --- a/std/algebra/emulated/sw_bw6761/pairing.go +++ b/std/algebra/emulated/sw_bw6761/pairing.go @@ -147,7 +147,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { } // We perform the easy part of the final exp to push f to the cyclotomic - // subgroup so that FinalExponentiationCheck is carried with optimized + // subgroup so that AssertFinalExponentiationIsOne is carried with optimized // cyclotomic squaring (e.g. Karabina12345). // // f = f^(p³-1)(p+1) @@ -156,7 +156,7 @@ func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { f = pr.Frobenius(buf) f = pr.Mul(f, buf) - pr.FinalExponentiationCheck(f) + pr.AssertFinalExponentiationIsOne(f) return nil } diff --git a/std/algebra/native/fields_bls12377/e12_pairing.go b/std/algebra/native/fields_bls12377/e12_pairing.go index 60684d23d7..de72c44645 100644 --- a/std/algebra/native/fields_bls12377/e12_pairing.go +++ b/std/algebra/native/fields_bls12377/e12_pairing.go @@ -206,13 +206,13 @@ func (e *E12) ExpU(api frontend.API, e1 E12) *E12 { return e } -// FinalExponentiationCheck checks that a Miller function output x lies in the +// AssertFinalExponentiationIsOne checks that a Miller function output x lies in the // same equivalence class as the reduced pairing. This replaces the final // exponentiation step in-circuit. // The method follows Section 4 of [On Proving Pairings] paper by A. Novakovic and L. Eagen. // // [On Proving Pairings]: https://eprint.iacr.org/2024/640.pdf -func (x *E12) FinalExponentiationCheck(api frontend.API) *E12 { +func (x *E12) AssertFinalExponentiationIsOne(api frontend.API) { res, err := api.NewHint(finalExpHint, 18, x.C0.B0.A0, x.C0.B0.A1, x.C0.B1.A0, x.C0.B1.A1, x.C0.B2.A0, x.C0.B2.A1, x.C1.B0.A0, x.C1.B0.A1, x.C1.B1.A0, x.C1.B1.A1, x.C1.B2.A0, x.C1.B2.A1) if err != nil { // err is non-nil only for invalid number of inputs @@ -241,6 +241,4 @@ func (x *E12) FinalExponentiationCheck(api frontend.API) *E12 { t1.Mul(api, *x, scalingFactor) t0.AssertIsEqual(api, t1) - - return nil } diff --git a/std/algebra/native/sw_bls12377/pairing.go b/std/algebra/native/sw_bls12377/pairing.go index 759a55ca9b..9cd1ce851b 100644 --- a/std/algebra/native/sw_bls12377/pairing.go +++ b/std/algebra/native/sw_bls12377/pairing.go @@ -258,7 +258,7 @@ func PairingCheck(api frontend.API, P []G1Affine, Q []G2Affine) error { return err } // We perform the easy part of the final exp to push f to the cyclotomic - // subgroup so that FinalExponentiationCheck is carried with optimized + // subgroup so that AssertFinalExponentiationIsOne is carried with optimized // cyclotomic squaring (e.g. Karabina12345). // // f = f^(p⁶-1)(p²+1) @@ -268,7 +268,7 @@ func PairingCheck(api frontend.API, P []G1Affine, Q []G2Affine) error { f.FrobeniusSquare(api, buf). Mul(api, f, buf) - f.FinalExponentiationCheck(api) + f.AssertFinalExponentiationIsOne(api) return nil } diff --git a/std/algebra/native/sw_bls12377/pairing2.go b/std/algebra/native/sw_bls12377/pairing2.go index a633024d24..ed5eb3b4e9 100644 --- a/std/algebra/native/sw_bls12377/pairing2.go +++ b/std/algebra/native/sw_bls12377/pairing2.go @@ -303,7 +303,7 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { return err } // We perform the easy part of the final exp to push res to the cyclotomic - // subgroup so that FinalExponentiationCheck is carried with optimized + // subgroup so that AssertFinalExponentiationIsOne is carried with optimized // cyclotomic squaring (e.g. Karabina12345). // // res = res^(p⁶-1)(p²+1) @@ -312,7 +312,7 @@ func (p *Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { buf.DivUnchecked(p.api, buf, res) res.FrobeniusSquare(p.api, buf).Mul(p.api, res, buf) - res.FinalExponentiationCheck(p.api) + res.AssertFinalExponentiationIsOne(p.api) return nil }