Skip to content

Commit

Permalink
feat: add IsOnG2 for BN254 (#1204)
Browse files Browse the repository at this point in the history
* feat: add IsOnTwist method

* feat: add IsEqual methods

* feat: add IsOnG2 method

* docs: rename internal method
  • Loading branch information
ivokub authored Jul 16, 2024
1 parent 8c1cd75 commit 1453c00
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 10 deletions.
8 changes: 8 additions & 0 deletions std/algebra/emulated/fields_bn254/e2.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,14 @@ func (e Ext2) AssertIsEqual(x, y *E2) {
e.fp.AssertIsEqual(&x.A1, &y.A1)
}

func (e Ext2) IsEqual(x, y *E2) frontend.Variable {
xDiff := e.fp.Sub(&x.A0, &y.A0)
yDiff := e.fp.Sub(&x.A1, &y.A1)
xIsZero := e.fp.IsZero(xDiff)
yIsZero := e.fp.IsZero(yDiff)
return e.api.And(xIsZero, yIsZero)
}

func FromE2(y *bn254.E2) E2 {
return E2{
A0: emulated.ValueOf[emulated.BN254Fp](y.A0),
Expand Down
8 changes: 8 additions & 0 deletions std/algebra/emulated/sw_bn254/g2.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
)

type G2 struct {
api frontend.API
*fields_bn254.Ext2
w *emulated.Element[BaseField]
u, v *fields_bn254.E2
Expand Down Expand Up @@ -49,6 +50,7 @@ func NewG2(api frontend.API) *G2 {
A1: emulated.ValueOf[BaseField]("3505843767911556378687030309984248845540243509899259641013678093033130930403"),
}
return &G2{
api: api,
Ext2: fields_bn254.NewExt2(api),
w: &w,
u: &u,
Expand Down Expand Up @@ -262,3 +264,9 @@ func (g2 *G2) AssertIsEqual(p, q *G2Affine) {
g2.Ext2.AssertIsEqual(&p.P.X, &q.P.X)
g2.Ext2.AssertIsEqual(&p.P.Y, &q.P.Y)
}

func (g2 *G2) IsEqual(p, q *G2Affine) frontend.Variable {
xEqual := g2.Ext2.IsEqual(&p.P.X, &q.P.X)
yEqual := g2.Ext2.IsEqual(&p.P.Y, &q.P.Y)
return g2.api.And(xEqual, yEqual)
}
47 changes: 37 additions & 10 deletions std/algebra/emulated/sw_bn254/pairing.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,33 +273,40 @@ func (pr Pairing) AssertIsOnCurve(P *G1Affine) {
pr.curve.AssertIsOnCurve(P)
}

func (pr Pairing) AssertIsOnTwist(Q *G2Affine) {
func (pr Pairing) computeTwistEquation(Q *G2Affine) (left, right *fields_bn254.E2) {
// Twist: Y² == X³ + aX + b, where a=0 and b=3/(9+u)
// (X,Y) ∈ {Y² == X³ + aX + b} U (0,0)

// if Q=(0,0) we assign b=0 otherwise 3/(9+u), and continue
selector := pr.api.And(pr.Ext2.IsZero(&Q.P.X), pr.Ext2.IsZero(&Q.P.Y))
b := pr.Ext2.Select(selector, pr.Ext2.Zero(), pr.bTwist)

left := pr.Ext2.Square(&Q.P.Y)
right := pr.Ext2.Square(&Q.P.X)
left = pr.Ext2.Square(&Q.P.Y)
right = pr.Ext2.Square(&Q.P.X)
right = pr.Ext2.Mul(right, &Q.P.X)
right = pr.Ext2.Add(right, b)
return left, right
}

func (pr Pairing) AssertIsOnTwist(Q *G2Affine) {
left, right := pr.computeTwistEquation(Q)
pr.Ext2.AssertIsEqual(left, right)
}

// IsOnTwist returns a boolean indicating if the G2 point is in the twist.
func (pr Pairing) IsOnTwist(Q *G2Affine) frontend.Variable {
left, right := pr.computeTwistEquation(Q)
diff := pr.Ext2.Sub(left, right)
return pr.Ext2.IsZero(diff)
}

func (pr Pairing) AssertIsOnG1(P *G1Affine) {
// BN254 has a prime order, so we only
// 1- Check P is on the curve
pr.AssertIsOnCurve(P)
}

func (pr Pairing) AssertIsOnG2(Q *G2Affine) {
// 1- Check Q is on the curve
pr.AssertIsOnTwist(Q)

// 2- Check Q has the right subgroup order

func (pr Pairing) computeG2ShortVector(Q *G2Affine) (_Q *G2Affine) {
// [x₀]Q
xQ := pr.g2.scalarMulBySeed(Q)
// ψ([x₀]Q)
Expand All @@ -311,14 +318,34 @@ func (pr Pairing) AssertIsOnG2(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(psi2xQ, psi3xxQ)
_Q = pr.g2.sub(_Q, psixQ)
_Q = pr.g2.sub(_Q, xQ)
return _Q
}

func (pr Pairing) AssertIsOnG2(Q *G2Affine) {
// 1- Check Q is on the curve
pr.AssertIsOnTwist(Q)

// 2- Check Q has the right subgroup order
_Q := pr.computeG2ShortVector(Q)
// [r]Q == 0 <==> _Q == Q
pr.g2.AssertIsEqual(Q, _Q)
}

// IsOnG2 returns a boolean indicating if the G2 point is in the subgroup. The
// method assumes that the point is already on the curve. Call
// [Pairing.AssertIsOnTwist] before to ensure point is on the curve.
func (pr Pairing) IsOnG2(Q *G2Affine) frontend.Variable {
// 1 - is Q on curve
isOnCurve := pr.IsOnTwist(Q)
// 2 - is Q in the subgroup
_Q := pr.computeG2ShortVector(Q)
isInSubgroup := pr.g2.IsEqual(Q, _Q)
return pr.api.And(isOnCurve, isInSubgroup)
}

// loopCounter = 6x₀+2 = 29793968203157093288
//
// in 2-NAF
Expand Down
108 changes: 108 additions & 0 deletions std/algebra/emulated/sw_bn254/pairing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,114 @@ func TestGroupMembershipSolve(t *testing.T) {
assert.NoError(err)
}

type IsOnTwistCircuit struct {
Q G2Affine
Expected frontend.Variable
}

func (c *IsOnTwistCircuit) Define(api frontend.API) error {
pairing, err := NewPairing(api)
if err != nil {
return fmt.Errorf("new pairing: %w", err)
}
res := pairing.IsOnTwist(&c.Q)
api.AssertIsEqual(res, c.Expected)
return nil
}

func TestIsOnTwistSolve(t *testing.T) {
assert := test.NewAssert(t)
// test for a point not on the twist
var Q bn254.G2Affine
_, err := Q.X.A0.SetString("0x119606e6d3ea97cea4eff54433f5c7dbc026b8d0670ddfbe6441e31225028d31")
assert.NoError(err)
_, err = Q.X.A1.SetString("0x1d3df5be6084324da6333a6ad1367091ca9fbceb70179ec484543a58b8cb5d63")
assert.NoError(err)
_, err = Q.Y.A0.SetString("0x1b9a36ea373fe2c5b713557042ce6deb2907d34e12be595f9bbe84c144de86ef")
assert.NoError(err)
_, err = Q.Y.A1.SetString("0x49fe60975e8c78b7b31a6ed16a338ac8b28cf6a065cfd2ca47e9402882518ba0")
assert.NoError(err)
assert.False(Q.IsOnCurve())
witness := IsOnTwistCircuit{
Q: NewG2Affine(Q),
Expected: 0,
}
err = test.IsSolved(&IsOnTwistCircuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
// test for a point on the twist
_, Q = randomG1G2Affines()
assert.True(Q.IsOnCurve())
witness = IsOnTwistCircuit{
Q: NewG2Affine(Q),
Expected: 1,
}
err = test.IsSolved(&IsOnTwistCircuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

type IsOnG2Circuit struct {
Q G2Affine
Expected frontend.Variable
}

func (c *IsOnG2Circuit) Define(api frontend.API) error {
pairing, err := NewPairing(api)
if err != nil {
return fmt.Errorf("new pairing: %w", err)
}
res := pairing.IsOnG2(&c.Q)
api.AssertIsEqual(res, c.Expected)
return nil
}

func TestIsOnG2Solve(t *testing.T) {
assert := test.NewAssert(t)
// test for a point not on the curve
var Q bn254.G2Affine
_, err := Q.X.A0.SetString("0x119606e6d3ea97cea4eff54433f5c7dbc026b8d0670ddfbe6441e31225028d31")
assert.NoError(err)
_, err = Q.X.A1.SetString("0x1d3df5be6084324da6333a6ad1367091ca9fbceb70179ec484543a58b8cb5d63")
assert.NoError(err)
_, err = Q.Y.A0.SetString("0x1b9a36ea373fe2c5b713557042ce6deb2907d34e12be595f9bbe84c144de86ef")
assert.NoError(err)
_, err = Q.Y.A1.SetString("0x49fe60975e8c78b7b31a6ed16a338ac8b28cf6a065cfd2ca47e9402882518ba0")
assert.NoError(err)
assert.False(Q.IsOnCurve())
witness := IsOnG2Circuit{
Q: NewG2Affine(Q),
Expected: 0,
}
err = test.IsSolved(&IsOnG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
// test for a point on curve not in G2
_, err = Q.X.A0.SetString("0x07192b9fd0e2a32e3e1caa8e59462b757326d48f641924e6a1d00d66478913eb")
assert.NoError(err)
_, err = Q.X.A1.SetString("0x15ce93f1b1c4946dd6cfbb3d287d9c9a1cdedb264bda7aada0844416d8a47a63")
assert.NoError(err)
_, err = Q.Y.A0.SetString("0x0fa65a9b48ba018361ed081e3b9e958451de5d9e8ae0bd251833ebb4b2fafc96")
assert.NoError(err)
_, err = Q.Y.A1.SetString("0x06e1f5e20f68f6dfa8a91a3bea048df66d9eaf56cc7f11215401f7e05027e0c6")
assert.NoError(err)
assert.True(Q.IsOnCurve())
assert.False(Q.IsInSubGroup())
witness = IsOnG2Circuit{
Q: NewG2Affine(Q),
Expected: 0,
}
err = test.IsSolved(&IsOnG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
// test for a point in G2
_, Q = randomG1G2Affines()
assert.True(Q.IsOnCurve())
assert.True(Q.IsInSubGroup())
witness = IsOnG2Circuit{
Q: NewG2Affine(Q),
Expected: 1,
}
err = test.IsSolved(&IsOnG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

// bench
func BenchmarkPairing(b *testing.B) {

Expand Down

0 comments on commit 1453c00

Please sign in to comment.