Skip to content

Commit

Permalink
Merge pull request #629 from ConsenSys/feat/emulated/subgroup-check
Browse files Browse the repository at this point in the history
Feat: emulated subgroup check
  • Loading branch information
yelhousni authored Apr 26, 2023
2 parents 94f1ddc + 07416c7 commit eef494e
Show file tree
Hide file tree
Showing 19 changed files with 1,145 additions and 10 deletions.
4 changes: 4 additions & 0 deletions std/algebra/emulated/sw_bls12381/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func (c *PairCircuit) Define(api frontend.API) error {
if err != nil {
return fmt.Errorf("new pairing: %w", err)
}
// Pair method does not check that the points are in the proper groups.
pairing.AssertIsOnG1(&c.InG1)
pairing.AssertIsOnG2(&c.InG2)
// Compute the pairing
res, err := pairing.Pair([]*sw_bls12381.G1Affine{&c.InG1}, []*sw_bls12381.G2Affine{&c.InG2})
if err != nil {
return fmt.Errorf("pair: %w", err)
Expand Down
29 changes: 29 additions & 0 deletions std/algebra/emulated/sw_bls12381/g1.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package sw_bls12381

import (
"fmt"

bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/emulated/sw_emulated"
"github.com/consensys/gnark/std/math/emulated"
)
Expand All @@ -14,3 +17,29 @@ func NewG1Affine(v bls12381.G1Affine) G1Affine {
Y: emulated.ValueOf[emulated.BLS12381Fp](v.Y),
}
}

type G1 struct {
curveF *emulated.Field[emulated.BLS12381Fp]
w *emulated.Element[emulated.BLS12381Fp]
}

func NewG1(api frontend.API) (*G1, error) {
ba, err := emulated.NewField[emulated.BLS12381Fp](api)
if err != nil {
return nil, fmt.Errorf("new base api: %w", err)
}
w := emulated.ValueOf[emulated.BLS12381Fp]("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436")
return &G1{
curveF: ba,
w: &w,
}, nil
}

func (g1 *G1) phi(q *G1Affine) *G1Affine {
x := g1.curveF.Mul(&q.X, g1.w)

return &G1Affine{
X: *x,
Y: q.Y,
}
}
195 changes: 195 additions & 0 deletions std/algebra/emulated/sw_bls12381/g2.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
package sw_bls12381

import (
"math/big"

bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/emulated/fields_bls12381"
"github.com/consensys/gnark/std/math/emulated"
)

type G2 struct {
*fields_bls12381.Ext2
u1, w *emulated.Element[emulated.BLS12381Fp]
v *fields_bls12381.E2
}

type G2Affine struct {
X, Y fields_bls12381.E2
}

func NewG2(api frontend.API) *G2 {
w := emulated.ValueOf[emulated.BLS12381Fp]("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939436")
u1 := emulated.ValueOf[emulated.BLS12381Fp]("4002409555221667392624310435006688643935503118305586438271171395842971157480381377015405980053539358417135540939437")
v := fields_bls12381.E2{
A0: emulated.ValueOf[emulated.BLS12381Fp]("2973677408986561043442465346520108879172042883009249989176415018091420807192182638567116318576472649347015917690530"),
A1: emulated.ValueOf[emulated.BLS12381Fp]("1028732146235106349975324479215795277384839936929757896155643118032610843298655225875571310552543014690878354869257"),
}
return &G2{
Ext2: fields_bls12381.NewExt2(api),
w: &w,
u1: &u1,
v: &v,
}
}

func NewG2Affine(v bls12381.G2Affine) G2Affine {
return G2Affine{
X: fields_bls12381.E2{
Expand All @@ -22,3 +46,174 @@ func NewG2Affine(v bls12381.G2Affine) G2Affine {
},
}
}

func (g2 *G2) psi(q *G2Affine) *G2Affine {
x := g2.Ext2.MulByElement(&q.X, g2.u1)
y := g2.Ext2.Conjugate(&q.Y)
y = g2.Ext2.Mul(y, g2.v)

return &G2Affine{
X: fields_bls12381.E2{A0: x.A1, A1: x.A0},
Y: *y,
}
}

func (g2 *G2) scalarMulBySeed(q *G2Affine) *G2Affine {

z := g2.triple(q)
z = g2.double(z)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 2)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 8)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 31)
z = g2.doubleAndAdd(z, q)
z = g2.doubleN(z, 16)

return g2.neg(z)
}

func (g2 G2) add(p, q *G2Affine) *G2Affine {
// compute λ = (q.y-p.y)/(q.x-p.x)
qypy := g2.Ext2.Sub(&q.Y, &p.Y)
qxpx := g2.Ext2.Sub(&q.X, &p.X)
λ := g2.Ext2.DivUnchecked(qypy, qxpx)

// xr = λ²-p.x-q.x
λλ := g2.Ext2.Square(λ)
qxpx = g2.Ext2.Add(&p.X, &q.X)
xr := g2.Ext2.Sub(λλ, qxpx)

// p.y = λ(p.x-r.x) - p.y
pxrx := g2.Ext2.Sub(&p.X, xr)
λpxrx := g2.Ext2.Mul(λ, pxrx)
yr := g2.Ext2.Sub(λpxrx, &p.Y)

return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 G2) neg(p *G2Affine) *G2Affine {
xr := &p.X
yr := g2.Ext2.Neg(&p.Y)
return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 G2) sub(p, q *G2Affine) *G2Affine {
qNeg := g2.neg(q)
return g2.add(p, qNeg)
}

func (g2 *G2) double(p *G2Affine) *G2Affine {
// compute λ = (3p.x²)/2*p.y
xx3a := g2.Square(&p.X)
xx3a = g2.MulByConstElement(xx3a, big.NewInt(3))
y2 := g2.Double(&p.Y)
λ := g2.DivUnchecked(xx3a, y2)

// xr = λ²-2p.x
x2 := g2.Double(&p.X)
λλ := g2.Square(λ)
xr := g2.Sub(λλ, x2)

// yr = λ(p-xr) - p.y
pxrx := g2.Sub(&p.X, xr)
λpxrx := g2.Mul(λ, pxrx)
yr := g2.Sub(λpxrx, &p.Y)

return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 *G2) doubleN(p *G2Affine, n int) *G2Affine {
pn := p
for s := 0; s < n; s++ {
pn = g2.double(pn)
}
return pn
}

func (g2 G2) triple(p *G2Affine) *G2Affine {

// compute λ1 = (3p.x²)/2p.y
xx := g2.Square(&p.X)
xx = g2.MulByConstElement(xx, big.NewInt(3))
y2 := g2.Double(&p.Y)
λ1 := g2.DivUnchecked(xx, y2)

// xr = λ1²-2p.x
x2 := g2.MulByConstElement(&p.X, big.NewInt(2))
λ1λ1 := g2.Square(λ1)
x2 = g2.Sub(λ1λ1, x2)

// ommit y2 computation, and
// compute λ2 = 2p.y/(x2 − p.x) − λ1.
x1x2 := g2.Sub(&p.X, x2)
λ2 := g2.DivUnchecked(y2, x1x2)
λ2 = g2.Sub(λ2, λ1)

// xr = λ²-p.x-x2
λ2λ2 := g2.Square(λ2)
qxrx := g2.Add(x2, &p.X)
xr := g2.Sub(λ2λ2, qxrx)

// yr = λ(p.x-xr) - p.y
pxrx := g2.Sub(&p.X, xr)
λ2pxrx := g2.Mul(λ2, pxrx)
yr := g2.Sub(λ2pxrx, &p.Y)

return &G2Affine{
X: *xr,
Y: *yr,
}
}

func (g2 G2) doubleAndAdd(p, q *G2Affine) *G2Affine {

// compute λ1 = (q.y-p.y)/(q.x-p.x)
yqyp := g2.Ext2.Sub(&q.Y, &p.Y)
xqxp := g2.Ext2.Sub(&q.X, &p.X)
λ1 := g2.Ext2.DivUnchecked(yqyp, xqxp)

// compute x2 = λ1²-p.x-q.x
λ1λ1 := g2.Ext2.Square(λ1)
xqxp = g2.Ext2.Add(&p.X, &q.X)
x2 := g2.Ext2.Sub(λ1λ1, xqxp)

// ommit y2 computation
// compute λ2 = -λ1-2*p.y/(x2-p.x)
ypyp := g2.Ext2.Add(&p.Y, &p.Y)
x2xp := g2.Ext2.Sub(x2, &p.X)
λ2 := g2.Ext2.DivUnchecked(ypyp, x2xp)
λ2 = g2.Ext2.Add(λ1, λ2)
λ2 = g2.Ext2.Neg(λ2)

// compute x3 =λ2²-p.x-x3
λ2λ2 := g2.Ext2.Square(λ2)
x3 := g2.Ext2.Sub(λ2λ2, &p.X)
x3 = g2.Ext2.Sub(x3, x2)

// compute y3 = λ2*(p.x - x3)-p.y
y3 := g2.Ext2.Sub(&p.X, x3)
y3 = g2.Ext2.Mul(λ2, y3)
y3 = g2.Ext2.Sub(y3, &p.Y)

return &G2Affine{
X: *x3,
Y: *y3,
}
}

// AssertIsEqual asserts that p and q are the same point.
func (g2 *G2) AssertIsEqual(p, q *G2Affine) {
g2.Ext2.AssertIsEqual(&p.X, &q.X)
g2.Ext2.AssertIsEqual(&p.Y, &q.Y)
}
120 changes: 120 additions & 0 deletions std/algebra/emulated/sw_bls12381/g2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package sw_bls12381

import (
"math/big"
"testing"

"github.com/consensys/gnark-crypto/ecc"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/test"
)

type addG2Circuit struct {
In1, In2 G2Affine
Res G2Affine
}

func (c *addG2Circuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.add(&c.In1, &c.In2)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestAddG2TestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
_, in2 := randomG1G2Affines(assert)
var res bls12381.G2Affine
res.Add(&in1, &in2)
witness := addG2Circuit{
In1: NewG2Affine(in1),
In2: NewG2Affine(in2),
Res: NewG2Affine(res),
}
err := test.IsSolved(&addG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

type doubleG2Circuit struct {
In1 G2Affine
Res G2Affine
}

func (c *doubleG2Circuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.double(&c.In1)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestDoubleG2TestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
var res bls12381.G2Affine
var in1Jac, resJac bls12381.G2Jac
in1Jac.FromAffine(&in1)
resJac.Double(&in1Jac)
res.FromJacobian(&resJac)
witness := doubleG2Circuit{
In1: NewG2Affine(in1),
Res: NewG2Affine(res),
}
err := test.IsSolved(&doubleG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

type doubleAndAddG2Circuit struct {
In1, In2 G2Affine
Res G2Affine
}

func (c *doubleAndAddG2Circuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.doubleAndAdd(&c.In1, &c.In2)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestDoubleAndAddG2TestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
_, in2 := randomG1G2Affines(assert)
var res bls12381.G2Affine
res.Double(&in1).
Add(&res, &in2)
witness := doubleAndAddG2Circuit{
In1: NewG2Affine(in1),
In2: NewG2Affine(in2),
Res: NewG2Affine(res),
}
err := test.IsSolved(&doubleAndAddG2Circuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}

type scalarMulG2BySeedCircuit struct {
In1 G2Affine
Res G2Affine
}

func (c *scalarMulG2BySeedCircuit) Define(api frontend.API) error {
g2 := NewG2(api)
res := g2.scalarMulBySeed(&c.In1)
g2.AssertIsEqual(res, &c.Res)
return nil
}

func TestScalarMulG2BySeedTestSolve(t *testing.T) {
assert := test.NewAssert(t)
_, in1 := randomG1G2Affines(assert)
var res bls12381.G2Affine
x0, _ := new(big.Int).SetString("15132376222941642752", 10)
res.ScalarMultiplication(&in1, x0).Neg(&res)
witness := scalarMulG2BySeedCircuit{
In1: NewG2Affine(in1),
Res: NewG2Affine(res),
}
err := test.IsSolved(&scalarMulG2BySeedCircuit{}, &witness, ecc.BN254.ScalarField())
assert.NoError(err)
}
Loading

0 comments on commit eef494e

Please sign in to comment.