From 3d5c2ac55d06352c6a1126086a5540b6c20dbb94 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 23 May 2023 20:39:33 +0100 Subject: [PATCH 1/5] feat: emulated pairing 2-by-2 fixed circuit for EVM --- std/evmprecompiles/08-bnpairing.go | 68 +++++++- std/evmprecompiles/bn_test.go | 261 +++++++++++++++++++++++++++-- 2 files changed, 313 insertions(+), 16 deletions(-) diff --git a/std/evmprecompiles/08-bnpairing.go b/std/evmprecompiles/08-bnpairing.go index 01dbbab99c..1626e955c7 100644 --- a/std/evmprecompiles/08-bnpairing.go +++ b/std/evmprecompiles/08-bnpairing.go @@ -8,7 +8,47 @@ import ( // ECPair implements [ALT_BN128_PAIRING_CHECK] precompile contract at address 0x08. // // [ALT_BN128_PAIRING_CHECK]: https://ethereum.github.io/execution-specs/autoapi/ethereum/paris/vm/precompiled_contracts/alt_bn128/index.html#alt-bn128-pairing-check -func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { +// +// To have a fixed-circuit regardless of the number of inputs, we need 4 fixed circuits: +// - Fixed-size Miller loop (n=1) +// - Fixed-size Miller loop (n=2) +// - Multiplication in Fp12 +// - Final exponentiation +// +// Examples: +// Batch 1: P1 ∈ G1 and Q1 ∈ G2 +// Batch 2: P1, P2 ∈ G1 and Q1, Q2 ∈ G2 +// Batch 3: P1, P2, P3 ∈ G1 and Q1, Q2, Q3 ∈ G2 +// Batch 4: P1, P2, P3, P4 ∈ G1 and Q1, Q2, Q3, Q4 ∈ G2 +// Batch 5: P1, P2, P3, P4, P5 ∈ G1 and Q1, Q2, Q3, Q4, Q5 ∈ G2 +// +// * Batch 1 should never occur because e(P,Q)≠1 ∀P, Q ∈ G1, G2. So the precompile +// would fail anyway. +// * Batch 2 occurs for e.g. BLS signature (single) verification, KZG verification... +// e(P1,Q1)*e(P2,Q2) = | ml := MillerLoop2({P1,P1},{Q1,Q2}) +// | f := FinalExponentiation(ml) +// * Batch 3 occurs for e.g. QAP divisibility check in Pinocchio protocol verification. +// e(P1,Q1)*e(P2,Q2)*e(P3,Q3) = | ml1 := MillerLoop2({P1,P2},{Q1,Q2}) +// | ml2 := MillerLoop1(P3,Q3) +// | ml := Mul(ml1, ml2) +// | f := FinalExponentiation(ml) +// * Batch 4 occurs for e.g. Groth16 verification. +// e(P1,Q1)*e(P2,Q2)*e(P3,Q3)*e(P4,Q4) = | ml1 := MillerLoop2({P1,P2},{Q1,Q2}) +// | ml2 := MillerLoop2({P3,P4},{Q3,Q4}) +// | ml := Mul(ml1, ml2) +// | f := FinalExponentiation(ml) +// * Batch 5 might occur for e.g. BLS signature (aggregated) verification. +// e(P1,Q1)*e(P2,Q2)*e(P3,Q3)*e(P4,Q4)*e(P5,Q5) = | ml1 := MillerLoop2({P1,P2},{Q1,Q2}) +// | ml2 := MillerLoop2({P3,P4},{Q3,Q4}) +// | ml3 := MillerLoop1(P5,Q5) +// | ml := Mul(ml1, ml2) +// | ml = Mul(ml, ml3) +// | f := FinalExponentiation(ml) +// +// N.B.: Batches 3, 4 and 5 are sub-optimal compared to Pair() but the result is +// a fixed-circuit. + +func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine, n int) { pair, err := sw_bn254.NewPairing(api) if err != nil { panic(err) @@ -20,7 +60,31 @@ func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { } // 3- Check that ∏ᵢ e(Pᵢ, Qᵢ) == 1 - if err := pair.PairingCheck(P, Q); err != nil { + i := 1 + ml, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[i-1], P[i]}, []*sw_bn254.G2Affine{Q[0], Q[1]}) + if err != nil { panic(err) } + acc := ml + + for i < n-2 { + ml, err = pair.MillerLoop([]*sw_bn254.G1Affine{P[i+1], P[i+2]}, []*sw_bn254.G2Affine{Q[i+1], Q[i+2]}) + if err != nil { + panic(err) + } + acc = pair.Mul(ml, acc) + i += 2 + } + + if n%2 != 0 { + ml, err = pair.MillerLoop([]*sw_bn254.G1Affine{P[n-1]}, []*sw_bn254.G2Affine{Q[n-1]}) + if err != nil { + panic(err) + } + acc = pair.Mul(ml, acc) + } + + res := pair.FinalExponentiation(acc) + one := pair.One() + pair.AssertIsEqual(res, one) } diff --git a/std/evmprecompiles/bn_test.go b/std/evmprecompiles/bn_test.go index f5efe1baa3..d4ccad3ef3 100644 --- a/std/evmprecompiles/bn_test.go +++ b/std/evmprecompiles/bn_test.go @@ -132,37 +132,270 @@ func TestECMulCircuitFull(t *testing.T) { ) } -type ecpairCircuit struct { +type ecpairBatch2Circuit struct { P [2]sw_bn254.G1Affine Q [2]sw_bn254.G2Affine } -func (c *ecpairCircuit) Define(api frontend.API) error { +func (c *ecpairBatch2Circuit) Define(api frontend.API) error { P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1]} Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1]} - ECPair(api, P, Q) + ECPair(api, P, Q, 2) return nil } -func TestECPairCircuitShort(t *testing.T) { +func TestECPair2Circuit(t *testing.T) { assert := test.NewAssert(t) - _, _, p1, q1 := bn254.Generators() + _, _, p, q := bn254.Generators() var u, v fr.Element u.SetRandom() v.SetRandom() - p1.ScalarMultiplication(&p1, u.BigInt(new(big.Int))) - q1.ScalarMultiplication(&q1, v.BigInt(new(big.Int))) + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - var p2 bn254.G1Affine - var q2 bn254.G2Affine - p2.Neg(&p1) - q2.Set(&q1) + var np bn254.G1Affine + np.Neg(&p) - err := test.IsSolved(&ecpairCircuit{}, &ecpairCircuit{ - P: [2]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p1), sw_bn254.NewG1Affine(p2)}, - Q: [2]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q1), sw_bn254.NewG2Affine(q2)}, + err := test.IsSolved(&ecpairBatch2Circuit{}, &ecpairBatch2Circuit{ + P: [2]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, + Q: [2]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, + }, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type ecpairBatch3Circuit struct { + P [3]sw_bn254.G1Affine + Q [3]sw_bn254.G2Affine +} + +func (c *ecpairBatch3Circuit) Define(api frontend.API) error { + P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2]} + Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2]} + ECPair(api, P, Q, 3) + return nil +} + +func TestECPair3Circuit(t *testing.T) { + assert := test.NewAssert(t) + _, _, p, q := bn254.Generators() + + var u, v fr.Element + u.SetRandom() + v.SetRandom() + + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) + + var p2, np bn254.G1Affine + p2.Double(&p) + np.Neg(&p) + + err := test.IsSolved(&ecpairBatch3Circuit{}, &ecpairBatch3Circuit{ + P: [3]sw_bn254.G1Affine{sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, + Q: [3]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, + }, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type ecpairBatch4Circuit struct { + P [4]sw_bn254.G1Affine + Q [4]sw_bn254.G2Affine +} + +func (c *ecpairBatch4Circuit) Define(api frontend.API) error { + P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3]} + Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3]} + ECPair(api, P, Q, 4) + return nil +} + +func TestECPair4Circuit(t *testing.T) { + assert := test.NewAssert(t) + _, _, p, q := bn254.Generators() + + var u, v fr.Element + u.SetRandom() + v.SetRandom() + + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) + + var np bn254.G1Affine + np.Neg(&p) + + err := test.IsSolved(&ecpairBatch4Circuit{}, &ecpairBatch4Circuit{ + P: [4]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, + Q: [4]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, + }, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type ecpairBatch5Circuit struct { + P [5]sw_bn254.G1Affine + Q [5]sw_bn254.G2Affine +} + +func (c *ecpairBatch5Circuit) Define(api frontend.API) error { + P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4]} + Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4]} + ECPair(api, P, Q, 5) + return nil +} + +func TestECPair5Circuit(t *testing.T) { + assert := test.NewAssert(t) + _, _, p, q := bn254.Generators() + + var u, v fr.Element + u.SetRandom() + v.SetRandom() + + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) + + var p2, np bn254.G1Affine + p2.Double(&p) + np.Neg(&p) + + err := test.IsSolved(&ecpairBatch5Circuit{}, &ecpairBatch5Circuit{ + P: [5]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, + Q: [5]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, + }, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type ecpairBatch6Circuit struct { + P [6]sw_bn254.G1Affine + Q [6]sw_bn254.G2Affine +} + +func (c *ecpairBatch6Circuit) Define(api frontend.API) error { + P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5]} + Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5]} + ECPair(api, P, Q, 6) + return nil +} + +func TestECPair6Circuit(t *testing.T) { + assert := test.NewAssert(t) + _, _, p, q := bn254.Generators() + + var u, v fr.Element + u.SetRandom() + v.SetRandom() + + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) + + var np bn254.G1Affine + np.Neg(&p) + + err := test.IsSolved(&ecpairBatch6Circuit{}, &ecpairBatch6Circuit{ + P: [6]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, + Q: [6]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, + }, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type ecpairBatch7Circuit struct { + P [7]sw_bn254.G1Affine + Q [7]sw_bn254.G2Affine +} + +func (c *ecpairBatch7Circuit) Define(api frontend.API) error { + P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5], &c.P[6]} + Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5], &c.Q[6]} + ECPair(api, P, Q, 7) + return nil +} + +func TestECPair7Circuit(t *testing.T) { + assert := test.NewAssert(t) + _, _, p, q := bn254.Generators() + + var u, v fr.Element + u.SetRandom() + v.SetRandom() + + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) + + var p2, np bn254.G1Affine + p2.Double(&p) + np.Neg(&p) + + err := test.IsSolved(&ecpairBatch7Circuit{}, &ecpairBatch7Circuit{ + P: [7]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, + Q: [7]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, + }, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type ecpairBatch8Circuit struct { + P [8]sw_bn254.G1Affine + Q [8]sw_bn254.G2Affine +} + +func (c *ecpairBatch8Circuit) Define(api frontend.API) error { + P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5], &c.P[6], &c.P[7]} + Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5], &c.Q[6], &c.Q[7]} + ECPair(api, P, Q, 8) + return nil +} + +func TestECPair8Circuit(t *testing.T) { + assert := test.NewAssert(t) + _, _, p, q := bn254.Generators() + + var u, v fr.Element + u.SetRandom() + v.SetRandom() + + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) + + var np bn254.G1Affine + np.Neg(&p) + + err := test.IsSolved(&ecpairBatch8Circuit{}, &ecpairBatch8Circuit{ + P: [8]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, + Q: [8]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, + }, ecc.BN254.ScalarField()) + assert.NoError(err) +} + +type ecpairBatch9Circuit struct { + P [9]sw_bn254.G1Affine + Q [9]sw_bn254.G2Affine +} + +func (c *ecpairBatch9Circuit) Define(api frontend.API) error { + P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5], &c.P[6], &c.P[7], &c.P[8]} + Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5], &c.Q[6], &c.Q[7], &c.Q[8]} + ECPair(api, P, Q, 9) + return nil +} + +func TestECPair9Circuit(t *testing.T) { + assert := test.NewAssert(t) + _, _, p, q := bn254.Generators() + + var u, v fr.Element + u.SetRandom() + v.SetRandom() + + p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) + q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) + + var p2, np bn254.G1Affine + p2.Double(&p) + np.Neg(&p) + + err := test.IsSolved(&ecpairBatch9Circuit{}, &ecpairBatch9Circuit{ + P: [9]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, + Q: [9]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, }, ecc.BN254.ScalarField()) assert.NoError(err) } From 9d97eb087e867efd3a6f4c3c4cd4c4a3a2095461 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Thu, 25 May 2023 12:32:32 +0200 Subject: [PATCH 2/5] test: handle all cases in a single parametric circuit --- std/evmprecompiles/bn_test.go | 294 ++++++---------------------------- 1 file changed, 45 insertions(+), 249 deletions(-) diff --git a/std/evmprecompiles/bn_test.go b/std/evmprecompiles/bn_test.go index d4ccad3ef3..e1620e1198 100644 --- a/std/evmprecompiles/bn_test.go +++ b/std/evmprecompiles/bn_test.go @@ -1,6 +1,7 @@ package evmprecompiles import ( + "fmt" "math/big" "testing" @@ -132,253 +133,43 @@ func TestECMulCircuitFull(t *testing.T) { ) } -type ecpairBatch2Circuit struct { - P [2]sw_bn254.G1Affine - Q [2]sw_bn254.G2Affine +type ecPairBatchCircuit struct { + P sw_bn254.G1Affine + NP sw_bn254.G1Affine + DP sw_bn254.G1Affine + Q sw_bn254.G2Affine + n int } -func (c *ecpairBatch2Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1]} - ECPair(api, P, Q, 2) - return nil -} - -func TestECPair2Circuit(t *testing.T) { - assert := test.NewAssert(t) - _, _, p, q := bn254.Generators() - - var u, v fr.Element - u.SetRandom() - v.SetRandom() - - p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) - q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - - var np bn254.G1Affine - np.Neg(&p) - - err := test.IsSolved(&ecpairBatch2Circuit{}, &ecpairBatch2Circuit{ - P: [2]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, - Q: [2]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type ecpairBatch3Circuit struct { - P [3]sw_bn254.G1Affine - Q [3]sw_bn254.G2Affine -} - -func (c *ecpairBatch3Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2]} - ECPair(api, P, Q, 3) - return nil -} - -func TestECPair3Circuit(t *testing.T) { - assert := test.NewAssert(t) - _, _, p, q := bn254.Generators() - - var u, v fr.Element - u.SetRandom() - v.SetRandom() - - p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) - q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - - var p2, np bn254.G1Affine - p2.Double(&p) - np.Neg(&p) - - err := test.IsSolved(&ecpairBatch3Circuit{}, &ecpairBatch3Circuit{ - P: [3]sw_bn254.G1Affine{sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, - Q: [3]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type ecpairBatch4Circuit struct { - P [4]sw_bn254.G1Affine - Q [4]sw_bn254.G2Affine -} - -func (c *ecpairBatch4Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3]} - ECPair(api, P, Q, 4) - return nil -} - -func TestECPair4Circuit(t *testing.T) { - assert := test.NewAssert(t) - _, _, p, q := bn254.Generators() - - var u, v fr.Element - u.SetRandom() - v.SetRandom() - - p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) - q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - - var np bn254.G1Affine - np.Neg(&p) - - err := test.IsSolved(&ecpairBatch4Circuit{}, &ecpairBatch4Circuit{ - P: [4]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, - Q: [4]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type ecpairBatch5Circuit struct { - P [5]sw_bn254.G1Affine - Q [5]sw_bn254.G2Affine -} - -func (c *ecpairBatch5Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4]} - ECPair(api, P, Q, 5) - return nil -} - -func TestECPair5Circuit(t *testing.T) { - assert := test.NewAssert(t) - _, _, p, q := bn254.Generators() - - var u, v fr.Element - u.SetRandom() - v.SetRandom() - - p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) - q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - - var p2, np bn254.G1Affine - p2.Double(&p) - np.Neg(&p) - - err := test.IsSolved(&ecpairBatch5Circuit{}, &ecpairBatch5Circuit{ - P: [5]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, - Q: [5]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type ecpairBatch6Circuit struct { - P [6]sw_bn254.G1Affine - Q [6]sw_bn254.G2Affine -} - -func (c *ecpairBatch6Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5]} - ECPair(api, P, Q, 6) - return nil -} - -func TestECPair6Circuit(t *testing.T) { - assert := test.NewAssert(t) - _, _, p, q := bn254.Generators() - - var u, v fr.Element - u.SetRandom() - v.SetRandom() - - p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) - q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - - var np bn254.G1Affine - np.Neg(&p) - - err := test.IsSolved(&ecpairBatch6Circuit{}, &ecpairBatch6Circuit{ - P: [6]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, - Q: [6]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type ecpairBatch7Circuit struct { - P [7]sw_bn254.G1Affine - Q [7]sw_bn254.G2Affine -} - -func (c *ecpairBatch7Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5], &c.P[6]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5], &c.Q[6]} - ECPair(api, P, Q, 7) - return nil -} - -func TestECPair7Circuit(t *testing.T) { - assert := test.NewAssert(t) - _, _, p, q := bn254.Generators() - - var u, v fr.Element - u.SetRandom() - v.SetRandom() - - p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) - q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - - var p2, np bn254.G1Affine - p2.Double(&p) - np.Neg(&p) - - err := test.IsSolved(&ecpairBatch7Circuit{}, &ecpairBatch7Circuit{ - P: [7]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, - Q: [7]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type ecpairBatch8Circuit struct { - P [8]sw_bn254.G1Affine - Q [8]sw_bn254.G2Affine -} - -func (c *ecpairBatch8Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5], &c.P[6], &c.P[7]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5], &c.Q[6], &c.Q[7]} - ECPair(api, P, Q, 8) - return nil -} - -func TestECPair8Circuit(t *testing.T) { - assert := test.NewAssert(t) - _, _, p, q := bn254.Generators() - - var u, v fr.Element - u.SetRandom() - v.SetRandom() - - p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) - q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - - var np bn254.G1Affine - np.Neg(&p) - - err := test.IsSolved(&ecpairBatch8Circuit{}, &ecpairBatch8Circuit{ - P: [8]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np)}, - Q: [8]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) -} - -type ecpairBatch9Circuit struct { - P [9]sw_bn254.G1Affine - Q [9]sw_bn254.G2Affine -} - -func (c *ecpairBatch9Circuit) Define(api frontend.API) error { - P := []*sw_bn254.G1Affine{&c.P[0], &c.P[1], &c.P[2], &c.P[3], &c.P[4], &c.P[5], &c.P[6], &c.P[7], &c.P[8]} - Q := []*sw_bn254.G2Affine{&c.Q[0], &c.Q[1], &c.Q[2], &c.Q[3], &c.Q[4], &c.Q[5], &c.Q[6], &c.Q[7], &c.Q[8]} - ECPair(api, P, Q, 9) +func (c *ecPairBatchCircuit) Define(api frontend.API) error { + Q := make([]*sw_bn254.G2Affine, c.n) + for i := range Q { + Q[i] = &c.Q + } + switch c.n { + case 2: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP}, Q, 2) + case 3: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.NP, &c.NP, &c.DP}, Q, 3) + case 4: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP}, Q, 4) + case 5: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q, 5) + case 6: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP}, Q, 6) + case 7: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q, 7) + case 8: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP}, Q, 8) + case 9: + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q, 9) + default: + return fmt.Errorf("not handled %d", c.n) + } return nil } -func TestECPair9Circuit(t *testing.T) { +func TestECPairMulBatch(t *testing.T) { assert := test.NewAssert(t) _, _, p, q := bn254.Generators() @@ -389,13 +180,18 @@ func TestECPair9Circuit(t *testing.T) { p.ScalarMultiplication(&p, u.BigInt(new(big.Int))) q.ScalarMultiplication(&q, v.BigInt(new(big.Int))) - var p2, np bn254.G1Affine - p2.Double(&p) + var dp, np bn254.G1Affine + dp.Double(&p) np.Neg(&p) - err := test.IsSolved(&ecpairBatch9Circuit{}, &ecpairBatch9Circuit{ - P: [9]sw_bn254.G1Affine{sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(np), sw_bn254.NewG1Affine(p2)}, - Q: [9]sw_bn254.G2Affine{sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q), sw_bn254.NewG2Affine(q)}, - }, ecc.BN254.ScalarField()) - assert.NoError(err) + for i := 2; i < 10; i++ { + err := test.IsSolved(&ecPairBatchCircuit{n: i}, &ecPairBatchCircuit{ + n: i, + P: sw_bn254.NewG1Affine(p), + NP: sw_bn254.NewG1Affine(np), + DP: sw_bn254.NewG1Affine(dp), + Q: sw_bn254.NewG2Affine(q), + }, ecc.BN254.ScalarField()) + assert.NoError(err) + } } From 388c4e82b70bcf9f847a307828299db53a20b861 Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Thu, 25 May 2023 12:35:14 +0200 Subject: [PATCH 3/5] refactor: get the input length for pair lengths --- std/evmprecompiles/08-bnpairing.go | 9 ++++++++- std/evmprecompiles/bn_test.go | 16 ++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/std/evmprecompiles/08-bnpairing.go b/std/evmprecompiles/08-bnpairing.go index 1626e955c7..2572876fd3 100644 --- a/std/evmprecompiles/08-bnpairing.go +++ b/std/evmprecompiles/08-bnpairing.go @@ -48,7 +48,14 @@ import ( // N.B.: Batches 3, 4 and 5 are sub-optimal compared to Pair() but the result is // a fixed-circuit. -func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine, n int) { +func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { + if len(P) != len(Q) { + panic("P and Q length mismatch") + } + if len(P) < 2 { + panic("invalid multipairing size bound") + } + n := len(P) pair, err := sw_bn254.NewPairing(api) if err != nil { panic(err) diff --git a/std/evmprecompiles/bn_test.go b/std/evmprecompiles/bn_test.go index e1620e1198..24cb21c7ba 100644 --- a/std/evmprecompiles/bn_test.go +++ b/std/evmprecompiles/bn_test.go @@ -148,21 +148,21 @@ func (c *ecPairBatchCircuit) Define(api frontend.API) error { } switch c.n { case 2: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP}, Q, 2) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP}, Q) case 3: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.NP, &c.NP, &c.DP}, Q, 3) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.NP, &c.NP, &c.DP}, Q) case 4: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP}, Q, 4) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP}, Q) case 5: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q, 5) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q) case 6: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP}, Q, 6) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP}, Q) case 7: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q, 7) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q) case 8: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP}, Q, 8) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP}, Q) case 9: - ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q, 9) + ECPair(api, []*sw_emulated.AffinePoint[emulated.BN254Fp]{&c.P, &c.NP, &c.P, &c.NP, &c.P, &c.NP, &c.NP, &c.NP, &c.DP}, Q) default: return fmt.Errorf("not handled %d", c.n) } From f547af71a81f71984d29b9eb15556c009b24089a Mon Sep 17 00:00:00 2001 From: Ivo Kubjas Date: Thu, 25 May 2023 12:36:00 +0200 Subject: [PATCH 4/5] style: fewer vars --- std/evmprecompiles/08-bnpairing.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/std/evmprecompiles/08-bnpairing.go b/std/evmprecompiles/08-bnpairing.go index 2572876fd3..bc6cfbe449 100644 --- a/std/evmprecompiles/08-bnpairing.go +++ b/std/evmprecompiles/08-bnpairing.go @@ -67,24 +67,21 @@ func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { } // 3- Check that ∏ᵢ e(Pᵢ, Qᵢ) == 1 - i := 1 - ml, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[i-1], P[i]}, []*sw_bn254.G2Affine{Q[0], Q[1]}) + acc, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[0], P[1]}, []*sw_bn254.G2Affine{Q[0], Q[1]}) if err != nil { panic(err) } - acc := ml - for i < n-2 { - ml, err = pair.MillerLoop([]*sw_bn254.G1Affine{P[i+1], P[i+2]}, []*sw_bn254.G2Affine{Q[i+1], Q[i+2]}) + for i := 1; i < n-2; i += 2 { + ml, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[i+1], P[i+2]}, []*sw_bn254.G2Affine{Q[i+1], Q[i+2]}) if err != nil { panic(err) } acc = pair.Mul(ml, acc) - i += 2 } if n%2 != 0 { - ml, err = pair.MillerLoop([]*sw_bn254.G1Affine{P[n-1]}, []*sw_bn254.G2Affine{Q[n-1]}) + ml, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[n-1]}, []*sw_bn254.G2Affine{Q[n-1]}) if err != nil { panic(err) } From b51a676242c70d31e82b7fa8573f4b623c3d329e Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Mon, 5 Jun 2023 11:22:47 +0100 Subject: [PATCH 5/5] refactor(evm/ecpair): reduce the number of fixed circuits --- std/algebra/emulated/sw_bn254/pairing.go | 23 ++++++++ std/evmprecompiles/08-bnpairing.go | 68 +++++------------------- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index 5ed4e08f81..cfaa613220 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -670,3 +670,26 @@ func (pr Pairing) lineCompute(p1, p2 *G2Affine) *lineEvaluation { return &line } + +// MillerLoopAndMul computes the Miller loop between P and Q +// and multiplies it in 𝔽p¹² by previous. +// +// This method is needed for evmprecompiles/ecpair. +func (pr Pairing) MillerLoopAndMul(P *G1Affine, Q *G2Affine, previous *GTEl) (*GTEl, error) { + res, err := pr.MillerLoop([]*G1Affine{P}, []*G2Affine{Q}) + if err != nil { + return nil, fmt.Errorf("miller loop: %w", err) + } + res = pr.Mul(res, previous) + return res, err +} + +// FinalExponentiationIsOne performs the final exponentiation on e +// and checks that the result in 1 in GT. +// +// This method is needed for evmprecompiles/ecpair. +func (pr Pairing) FinalExponentiationIsOne(e *GTEl) { + res := pr.finalExponentiation(e, false) + one := pr.One() + pr.AssertIsEqual(res, one) +} diff --git a/std/evmprecompiles/08-bnpairing.go b/std/evmprecompiles/08-bnpairing.go index bc6cfbe449..9d25cd20ec 100644 --- a/std/evmprecompiles/08-bnpairing.go +++ b/std/evmprecompiles/08-bnpairing.go @@ -9,44 +9,13 @@ import ( // // [ALT_BN128_PAIRING_CHECK]: https://ethereum.github.io/execution-specs/autoapi/ethereum/paris/vm/precompiled_contracts/alt_bn128/index.html#alt-bn128-pairing-check // -// To have a fixed-circuit regardless of the number of inputs, we need 4 fixed circuits: -// - Fixed-size Miller loop (n=1) -// - Fixed-size Miller loop (n=2) -// - Multiplication in Fp12 -// - Final exponentiation +// To have a fixed-circuit regardless of the number of inputs, we need 2 fixed circuits: +// - A Miller loop of fixed size 1 followed with a multiplication in 𝔽p¹² (MillerLoopAndMul) +// - A final exponentiation followed with an equality check in GT (FinalExponentiationIsOne) // -// Examples: -// Batch 1: P1 ∈ G1 and Q1 ∈ G2 -// Batch 2: P1, P2 ∈ G1 and Q1, Q2 ∈ G2 -// Batch 3: P1, P2, P3 ∈ G1 and Q1, Q2, Q3 ∈ G2 -// Batch 4: P1, P2, P3, P4 ∈ G1 and Q1, Q2, Q3, Q4 ∈ G2 -// Batch 5: P1, P2, P3, P4, P5 ∈ G1 and Q1, Q2, Q3, Q4, Q5 ∈ G2 -// -// * Batch 1 should never occur because e(P,Q)≠1 ∀P, Q ∈ G1, G2. So the precompile -// would fail anyway. -// * Batch 2 occurs for e.g. BLS signature (single) verification, KZG verification... -// e(P1,Q1)*e(P2,Q2) = | ml := MillerLoop2({P1,P1},{Q1,Q2}) -// | f := FinalExponentiation(ml) -// * Batch 3 occurs for e.g. QAP divisibility check in Pinocchio protocol verification. -// e(P1,Q1)*e(P2,Q2)*e(P3,Q3) = | ml1 := MillerLoop2({P1,P2},{Q1,Q2}) -// | ml2 := MillerLoop1(P3,Q3) -// | ml := Mul(ml1, ml2) -// | f := FinalExponentiation(ml) -// * Batch 4 occurs for e.g. Groth16 verification. -// e(P1,Q1)*e(P2,Q2)*e(P3,Q3)*e(P4,Q4) = | ml1 := MillerLoop2({P1,P2},{Q1,Q2}) -// | ml2 := MillerLoop2({P3,P4},{Q3,Q4}) -// | ml := Mul(ml1, ml2) -// | f := FinalExponentiation(ml) -// * Batch 5 might occur for e.g. BLS signature (aggregated) verification. -// e(P1,Q1)*e(P2,Q2)*e(P3,Q3)*e(P4,Q4)*e(P5,Q5) = | ml1 := MillerLoop2({P1,P2},{Q1,Q2}) -// | ml2 := MillerLoop2({P3,P4},{Q3,Q4}) -// | ml3 := MillerLoop1(P5,Q5) -// | ml := Mul(ml1, ml2) -// | ml = Mul(ml, ml3) -// | f := FinalExponentiation(ml) -// -// N.B.: Batches 3, 4 and 5 are sub-optimal compared to Pair() but the result is -// a fixed-circuit. +// N.B.: This is a sub-optimal routine but defines a fixed circuit regardless +// of the number of inputs. We can extend this routine to handle a 2-by-2 +// logic but we prefer a minimal number of circuits (2). func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { if len(P) != len(Q) { @@ -67,28 +36,15 @@ func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { } // 3- Check that ∏ᵢ e(Pᵢ, Qᵢ) == 1 - acc, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[0], P[1]}, []*sw_bn254.G2Affine{Q[0], Q[1]}) - if err != nil { - panic(err) - } - - for i := 1; i < n-2; i += 2 { - ml, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[i+1], P[i+2]}, []*sw_bn254.G2Affine{Q[i+1], Q[i+2]}) - if err != nil { - panic(err) - } - acc = pair.Mul(ml, acc) - } - - if n%2 != 0 { - ml, err := pair.MillerLoop([]*sw_bn254.G1Affine{P[n-1]}, []*sw_bn254.G2Affine{Q[n-1]}) + ml := pair.One() + for i := 0; i < n; i++ { + // fixed circuit 1 + ml, err = pair.MillerLoopAndMul(P[i], Q[i], ml) if err != nil { panic(err) } - acc = pair.Mul(ml, acc) } - res := pair.FinalExponentiation(acc) - one := pair.One() - pair.AssertIsEqual(res, one) + // fixed circuit 2 + pair.FinalExponentiationIsOne(ml) }