Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add IsOnG2 for BN254 #1204

Merged
merged 4 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) computeSubgroupEquation(Q *G2Affine) (_Q *G2Affine) {
ivokub marked this conversation as resolved.
Show resolved Hide resolved
// [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.computeSubgroupEquation(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.computeSubgroupEquation(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
Loading