diff --git a/internal/stats/latest_stats.csv b/internal/stats/latest_stats.csv index e54404e66..10e9a3edf 100644 --- a/internal/stats/latest_stats.csv +++ b/internal/stats/latest_stats.csv @@ -181,14 +181,14 @@ pairing_bls24315,bls24_315,plonk,0,0 pairing_bls24315,bls24_317,plonk,0,0 pairing_bls24315,bw6_761,plonk,0,0 pairing_bls24315,bw6_633,plonk,141249,141249 -pairing_bn254,bn254,groth16,604783,990919 +pairing_bn254,bn254,groth16,607378,995098 pairing_bn254,bls12_377,groth16,0,0 pairing_bn254,bls12_381,groth16,0,0 pairing_bn254,bls24_315,groth16,0,0 pairing_bn254,bls24_317,groth16,0,0 pairing_bn254,bw6_761,groth16,0,0 pairing_bn254,bw6_633,groth16,0,0 -pairing_bn254,bn254,plonk,2319665,2030447 +pairing_bn254,bn254,plonk,2329131,2039205 pairing_bn254,bls12_377,plonk,0,0 pairing_bn254,bls12_381,plonk,0,0 pairing_bn254,bls24_315,plonk,0,0 diff --git a/std/algebra/emulated/sw_bn254/g2.go b/std/algebra/emulated/sw_bn254/g2.go index 01299f7f0..10767d191 100644 --- a/std/algebra/emulated/sw_bn254/g2.go +++ b/std/algebra/emulated/sw_bn254/g2.go @@ -101,7 +101,7 @@ func (g2 *G2) phi(q *G2Affine) *G2Affine { return &G2Affine{ P: g2AffP{ X: *x, - Y: q.P.Y, + Y: *g2.Ext2.Neg(&q.P.Y), }, } } diff --git a/std/algebra/emulated/sw_bn254/g2_test.go b/std/algebra/emulated/sw_bn254/g2_test.go index 3e61f05cc..4d4879620 100644 --- a/std/algebra/emulated/sw_bn254/g2_test.go +++ b/std/algebra/emulated/sw_bn254/g2_test.go @@ -126,7 +126,6 @@ type endomorphismG2Circuit struct { func (c *endomorphismG2Circuit) Define(api frontend.API) error { g2 := NewG2(api) res1 := g2.phi(&c.In1) - res1 = g2.neg(res1) res2 := g2.psi(&c.In1) res2 = g2.psi(res2) g2.AssertIsEqual(res1, res2) diff --git a/std/algebra/emulated/sw_bn254/pairing.go b/std/algebra/emulated/sw_bn254/pairing.go index a66755246..6831e31b7 100644 --- a/std/algebra/emulated/sw_bn254/pairing.go +++ b/std/algebra/emulated/sw_bn254/pairing.go @@ -81,10 +81,10 @@ func NewPairing(api frontend.API) (*Pairing, error) { }, nil } -// Pair calculates the reduced pairing for a set of points -// ∏ᵢ e(Pᵢ, Qᵢ). +// Pair calculates the reduced pairing for a set of points ∏ᵢ e(Pᵢ, Qᵢ). // -// This function doesn't check that the inputs are in the correct subgroups. See AssertIsOnG1 and AssertIsOnG2. +// This function checks that the Qᵢ are in the correct subgroup, but does not +// check Pᵢ. See AssertIsOnG1. func (pr Pairing) Pair(P []*G1Affine, Q []*G2Affine) (*GTEl, error) { res, err := pr.MillerLoop(P, Q) if err != nil { @@ -206,10 +206,13 @@ func (pr Pairing) AssertFinalExponentiationIsOne(a *GTEl) { pr.AssertIsEqual(t0, t2) } -// PairingCheck calculates the reduced pairing for a set of points and asserts if the result is One -// ∏ᵢ e(Pᵢ, Qᵢ) =? 1 +// PairingCheck calculates the reduced pairing for a set of points and asserts +// if the result is one: // -// This function doesn't check that the inputs are in the correct subgroups. See AssertIsOnG1 and AssertIsOnG2. +// ∏ᵢ e(Pᵢ, Qᵢ) =? 1 +// +// This function checks that the Qᵢ are in the correct subgroup, but does not +// check Pᵢ. See AssertIsOnG1. func (pr Pairing) PairingCheck(P []*G1Affine, Q []*G2Affine) error { f, err := pr.MillerLoop(P, Q) if err != nil { @@ -266,6 +269,7 @@ func (pr Pairing) AssertIsOnG1(P *G1Affine) { pr.AssertIsOnCurve(P) } +// computeG2ShortVector computes ψ³([2x₀]Q) - ψ²([x₀]Q) - ψ([x₀]Q) - [x₀]Q func (pr Pairing) computeG2ShortVector(Q *G2Affine) (_Q *G2Affine) { // [x₀]Q xQ := pr.g2.scalarMulBySeed(Q) @@ -278,7 +282,7 @@ func (pr Pairing) computeG2ShortVector(Q *G2Affine) (_Q *G2Affine) { psi3xxQ = pr.g2.psi(psi3xxQ) // _Q = ψ³([2x₀]Q) - ψ²([x₀]Q) - ψ([x₀]Q) - [x₀]Q - _Q = pr.g2.sub(psi2xQ, psi3xxQ) + _Q = pr.g2.sub(psi3xxQ, psi2xQ) _Q = pr.g2.sub(_Q, psixQ) _Q = pr.g2.sub(_Q, xQ) return _Q @@ -289,8 +293,10 @@ func (pr Pairing) AssertIsOnG2(Q *G2Affine) { pr.AssertIsOnTwist(Q) // 2- Check Q has the right subgroup order + // [r]Q == 0 <==> ψ³([2x₀]Q) - ψ²([x₀]Q) - ψ([x₀]Q) - [x₀]Q == Q + // This is a valid short vector since x₀ ≠ 5422 mod 2196. + // See Sec. 3.1.2 (Example 1) in https://eprint.iacr.org/2022/348. _Q := pr.computeG2ShortVector(Q) - // [r]Q == 0 <==> _Q == Q pr.g2.AssertIsEqual(Q, _Q) } diff --git a/std/algebra/emulated/sw_bn254/precomputations.go b/std/algebra/emulated/sw_bn254/precomputations.go index 84f8ffeab..32bd85c77 100644 --- a/std/algebra/emulated/sw_bn254/precomputations.go +++ b/std/algebra/emulated/sw_bn254/precomputations.go @@ -31,6 +31,10 @@ func precomputeLines(Q bn254.G2Affine) lineEvaluations { func (p *Pairing) computeLines(Q *g2AffP) lineEvaluations { + // check Q is on curve + Qaff := G2Affine{P: *Q, Lines: nil} + p.IsOnTwist(&Qaff) + var cLines lineEvaluations Qacc := Q n := len(bn254.LoopCounter) @@ -50,6 +54,20 @@ func (p *Pairing) computeLines(Q *g2AffP) lineEvaluations { } } + // Check that Q is on G2 subgroup: + // [r]Q == 0 <==> [6x₀+2]Q + ψ(Q) + ψ³(Q) = ψ²(Q). + // This is a valid short vector since x₀ ≠ 4 mod 13 and x₀ ≠ 92 mod 97. + // See Sec. 3.1.2 (Remark 2) in https://eprint.iacr.org/2022/348. + // This test is equivalent to [computeG2ShortVector] in [AssertIsOnG2]. + // + // At this point Qacc = [6x₀+2]Q. + psiQ := p.g2.psi(&Qaff) // ψ(Q) + psi2Q := p.g2.phi(&Qaff) // ϕ(Q)=ψ²(Q) + psi3Q := p.g2.psi(psi2Q) // ψ³(Q) + lhs := p.g2.add(&G2Affine{P: *Qacc, Lines: nil}, psiQ) + lhs = p.g2.add(lhs, psi3Q) + p.g2.AssertIsEqual(lhs, psi2Q) + Q1X := p.Ext2.Conjugate(&Q.X) Q1X = p.Ext2.MulByNonResidue1Power2(Q1X) Q1Y := p.Ext2.Conjugate(&Q.Y) diff --git a/std/evmprecompiles/08-bnpairing.go b/std/evmprecompiles/08-bnpairing.go index 25ab396b0..f4ff6b1b8 100644 --- a/std/evmprecompiles/08-bnpairing.go +++ b/std/evmprecompiles/08-bnpairing.go @@ -40,11 +40,8 @@ func ECPair(api frontend.API, P []*sw_bn254.G1Affine, Q []*sw_bn254.G2Affine) { if err != nil { panic(err) } - // 1- Check that Pᵢ are on G1 (done in the zkEVM ⚠️ ) - // 2- Check that Qᵢ are on G2 - for i := 0; i < len(Q); i++ { - pair.AssertIsOnG2(Q[i]) - } + // 1- Check that Pᵢ are on G1 (done in the zkEVM ⚠️ + // 2- Check that Qᵢ are on G2 (done in `computeLines` in `MillerLoopAndMul` and `MillerLoopAndFinalExpCheck) // 3- Check that ∏ᵢ e(Pᵢ, Qᵢ) == 1 ml := pair.Ext12.One() @@ -79,7 +76,6 @@ func ECPairMillerLoopAndMul(api frontend.API, accumulator *sw_bn254.GTEl, P *sw_ if err != nil { return fmt.Errorf("new pairing: %w", err) } - pairing.AssertIsOnG2(Q) ml, err := pairing.MillerLoopAndMul(P, Q, accumulator) if err != nil { return fmt.Errorf("miller loop and mul: %w", err) @@ -97,7 +93,6 @@ func ECPairMillerLoopAndFinalExpCheck(api frontend.API, accumulator *sw_bn254.GT if err != nil { return fmt.Errorf("new pairing: %w", err) } - pairing.AssertIsOnG2(Q) isSuccess := pairing.IsMillerLoopAndFinalExpOne(P, Q, accumulator) api.AssertIsEqual(expectedIsSuccess, isSuccess)