From 2ebbae1784e7b94c04a23ab01a7f651cf5047bf4 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 12 Apr 2022 16:21:51 +0200 Subject: [PATCH 01/11] feat(std/sw_bls12377): G2 scalar multiplication --- std/algebra/fields_bls12377/e2.go | 9 + std/algebra/sw_bls12377/g1.go | 4 +- std/algebra/sw_bls12377/g2.go | 327 +++++++++++++++++++++++------ std/algebra/sw_bls12377/g2_test.go | 184 +++++++++++++++- std/algebra/sw_bls12377/inner.go | 35 +-- 5 files changed, 481 insertions(+), 78 deletions(-) diff --git a/std/algebra/fields_bls12377/e2.go b/std/algebra/fields_bls12377/e2.go index 0db7253d87..fc7e9fa02a 100644 --- a/std/algebra/fields_bls12377/e2.go +++ b/std/algebra/fields_bls12377/e2.go @@ -232,3 +232,12 @@ func (e *E2) AssertIsEqual(api frontend.API, other E2) { api.AssertIsEqual(e.A0, other.A0) api.AssertIsEqual(e.A1, other.A1) } + +// Select sets e to r1 if b=1, r2 otherwise +func (e *E2) Select(api frontend.API, b frontend.Variable, r1, r2 E2) *E2 { + + e.A0 = api.Select(b, r1.A0, r2.A0) + e.A1 = api.Select(b, r1.A1, r2.A1) + + return e +} diff --git a/std/algebra/sw_bls12377/g1.go b/std/algebra/sw_bls12377/g1.go index aea34f7646..947795c31f 100644 --- a/std/algebra/sw_bls12377/g1.go +++ b/std/algebra/sw_bls12377/g1.go @@ -283,7 +283,7 @@ func (P *G1Affine) varScalarMul(api frontend.API, Q G1Affine, s frontend.Variabl var tableQ, tablePhiQ [2]G1Affine tableQ[1] = Q tableQ[0].Neg(api, Q) - cc.phi(api, &tablePhiQ[1], &Q) + cc.phi1(api, &tablePhiQ[1], &Q) tablePhiQ[0].Neg(api, tablePhiQ[1]) // We now initialize the accumulator. Due to the way the scalar is @@ -345,7 +345,7 @@ func (P *G1Affine) constScalarMul(api frontend.API, Q G1Affine, s *big.Int) *G1A var Acc, negQ, negPhiQ, phiQ G1Affine cc := innerCurve(api.Compiler().Curve()) s.Mod(s, cc.fr) - cc.phi(api, &phiQ, &Q) + cc.phi1(api, &phiQ, &Q) k := ecc.SplitScalar(s, cc.glvBasis) if k[0].Sign() == -1 { diff --git a/std/algebra/sw_bls12377/g2.go b/std/algebra/sw_bls12377/g2.go index ab3c4615dd..10ef852db0 100644 --- a/std/algebra/sw_bls12377/g2.go +++ b/std/algebra/sw_bls12377/g2.go @@ -17,7 +17,11 @@ limitations under the License. package sw_bls12377 import ( + "math/big" + + "github.com/consensys/gnark-crypto/ecc" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" + "github.com/consensys/gnark/backend/hint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/fields_bls12377" ) @@ -27,27 +31,13 @@ type G2Jac struct { X, Y, Z fields_bls12377.E2 } -type G2Proj struct { - X, Y, Z fields_bls12377.E2 -} - // G2Affine point in affine coords type G2Affine struct { X, Y fields_bls12377.E2 } -// ToProj sets p to p1 in projective coords and return it -func (p *G2Jac) ToProj(api frontend.API, p1 *G2Jac) *G2Jac { - p.X.Mul(api, p1.X, p1.Z) - p.Y = p1.Y - var t fields_bls12377.E2 - t.Square(api, p1.Z) - p.Z.Mul(api, p.Z, t) - return p -} - // Neg outputs -p -func (p *G2Jac) Neg(api frontend.API, p1 *G2Jac) *G2Jac { +func (p *G2Jac) Neg(api frontend.API, p1 G2Jac) *G2Jac { p.Y.Neg(api, p1.Y) p.X = p1.X p.Z = p1.Z @@ -55,12 +45,38 @@ func (p *G2Jac) Neg(api frontend.API, p1 *G2Jac) *G2Jac { } // Neg outputs -p -func (p *G2Affine) Neg(api frontend.API, p1 *G2Affine) *G2Affine { +func (p *G2Affine) Neg(api frontend.API, p1 G2Affine) *G2Affine { p.Y.Neg(api, p1.Y) p.X = p1.X return p } +// AddAssign add p1 to p and return p +func (p *G2Affine) AddAssign(api frontend.API, p1 G2Affine) *G2Affine { + + var n, d, l, xr, yr fields_bls12377.E2 + + // compute lambda = (p1.y-p.y)/(p1.x-p.x) + n.Sub(api, p1.Y, p.Y) + d.Sub(api, p1.X, p.X) + l.DivUnchecked(api, n, d) + + // xr =lambda**2-p1.x-p.x + xr.Square(api, l). + Sub(api, xr, p1.X). + Sub(api, xr, p.X) + + // yr = lambda(p.x - xr)-p.y + yr.Sub(api, p.X, xr). + Mul(api, l, yr). + Sub(api, yr, p.Y) + + p.X = xr + p.Y = yr + + return p +} + // AddAssign adds 2 point in Jacobian coordinates // p=p, a=p1 func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac) *G2Jac { @@ -115,35 +131,58 @@ func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac) *G2Jac { return p } -// AddAssign add p1 to p and return p -func (p *G2Affine) AddAssign(api frontend.API, p1 *G2Affine) *G2Affine { +// Double doubles a point in jacobian coords +func (p *G2Jac) Double(api frontend.API, p1 G2Jac) *G2Jac { - var n, d, l, xr, yr fields_bls12377.E2 + var XX, YY, YYYY, ZZ, S, M, T fields_bls12377.E2 - // compute lambda = (p1.y-p.y)/(p1.x-p.x) - n.Sub(api, p1.Y, p.Y) - d.Sub(api, p1.X, p.X) - l.DivUnchecked(api, n, d) + XX.Square(api, p.X) + YY.Square(api, p.Y) + YYYY.Square(api, YY) + ZZ.Square(api, p.Z) + S.Add(api, p.X, YY) + S.Square(api, S) + S.Sub(api, S, XX) + S.Sub(api, S, YYYY) + S.Add(api, S, S) + M.MulByFp(api, XX, 3) // M = 3*XX+a*ZZ², here a=0 (we suppose sw has j invariant 0) + p.Z.Add(api, p.Z, p.Y) + p.Z.Square(api, p.Z) + p.Z.Sub(api, p.Z, YY) + p.Z.Sub(api, p.Z, ZZ) + p.X.Square(api, M) + T.Add(api, S, S) + p.X.Sub(api, p.X, T) + p.Y.Sub(api, S, p.X) + p.Y.Mul(api, p.Y, M) + YYYY.MulByFp(api, YYYY, 8) + p.Y.Sub(api, p.Y, YYYY) - // xr =lambda**2-p1.x-p.x - xr.Square(api, l). - Sub(api, xr, p1.X). - Sub(api, xr, p.X) + return p +} - // yr = lambda(p.x - xr)-p.y - yr.Sub(api, p.X, xr). - Mul(api, l, yr). - Sub(api, yr, p.Y) +// Select sets p1 if b=1, p2 if b=0, and returns it. b must be boolean constrained +func (p *G2Affine) Select(api frontend.API, b frontend.Variable, p1, p2 G2Affine) *G2Affine { - p.X = xr - p.Y = yr + p.X.Select(api, b, p1.X, p2.X) + p.Y.Select(api, b, p1.Y, p2.Y) return p } +// FromJac sets p to p1 in affine and returns it +func (p *G2Affine) FromJac(api frontend.API, p1 G2Jac) *G2Affine { + var s fields_bls12377.E2 + s.Mul(api, p1.Z, p1.Z) + p.X.DivUnchecked(api, p1.X, s) + s.Mul(api, s, p1.Z) + p.Y.DivUnchecked(api, p1.Y, s) + return p +} + // Double compute 2*p1, assign the result to p and return it // Only for curve with j invariant 0 (a=0). -func (p *G2Affine) Double(api frontend.API, p1 *G2Affine) *G2Affine { +func (p *G2Affine) Double(api frontend.API, p1 G2Affine) *G2Affine { var n, d, l, xr, yr fields_bls12377.E2 @@ -169,34 +208,204 @@ func (p *G2Affine) Double(api frontend.API, p1 *G2Affine) *G2Affine { } -// Double doubles a point in jacobian coords -func (p *G2Jac) Double(api frontend.API, p1 *G2Jac) *G2Jac { +// ScalarMul sets P = [s] Q and returns P. +// +// The method chooses an implementation based on scalar s. If it is constant, +// then the compiled circuit depends on s. If it is variable type, then +// the circuit is independent of the inputs. +func (P *G2Affine) ScalarMul(api frontend.API, Q G2Affine, s interface{}) *G2Affine { + if n, ok := api.Compiler().ConstantValue(s); ok { + return P.constScalarMul(api, Q, n) + } else { + return P.varScalarMul(api, Q, s) + } +} - var XX, YY, YYYY, ZZ, S, M, T fields_bls12377.E2 +var DecomposeScalarG2 = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) error { + cc := innerCurve(curve) + sp := ecc.SplitScalar(inputs[0], cc.glvBasis) + res[0].Set(&(sp[0])) + res[1].Set(&(sp[1])) + one := big.NewInt(1) + // add (lambda+1, lambda) until scalar compostion is over Fr to ensure that + // the high bits are set in decomposition. + for res[0].Cmp(cc.lambda) < 1 && res[1].Cmp(cc.lambda) < 1 { + res[0].Add(res[0], cc.lambda) + res[0].Add(res[0], one) + res[1].Add(res[1], cc.lambda) + } + // figure out how many times we have overflowed + res[2].Mul(res[1], cc.lambda).Add(res[2], res[0]) + res[2].Sub(res[2], inputs[0]) + res[2].Div(res[2], cc.fr) + + return nil +} - XX.Square(api, p.X) - YY.Square(api, p.Y) - YYYY.Square(api, YY) - ZZ.Square(api, p.Z) - S.Add(api, p.X, YY) - S.Square(api, S) - S.Sub(api, S, XX) - S.Sub(api, S, YYYY) - S.Add(api, S, S) - M.MulByFp(api, XX, 3) // M = 3*XX+a*ZZ², here a=0 (we suppose sw has j invariant 0) - p.Z.Add(api, p.Z, p.Y) - p.Z.Square(api, p.Z) - p.Z.Sub(api, p.Z, YY) - p.Z.Sub(api, p.Z, ZZ) - p.X.Square(api, M) - T.Add(api, S, S) - p.X.Sub(api, p.X, T) - p.Y.Sub(api, S, p.X) - p.Y.Mul(api, p.Y, M) - YYYY.MulByFp(api, YYYY, 8) - p.Y.Sub(api, p.Y, YYYY) +func init() { + hint.Register(DecomposeScalarG2) +} - return p +// varScalarMul sets P = [s] Q and returns P. +func (P *G2Affine) varScalarMul(api frontend.API, Q G2Affine, s frontend.Variable) *G2Affine { + // This method computes [s] Q. We use several methods to reduce the number + // of added constraints - first, instead of classical double-and-add, we use + // the optimized version from https://github.com/zcash/zcash/issues/3924 + // which allows to omit computation of several intermediate values. + // Secondly, we use the GLV scalar multiplication to reduce the number + // iterations in the main loop. There is a small difference though - as + // two-bit select takes three constraints, then it takes as many constraints + // to compute ± Q ± Φ(Q) every iteration instead of selecting the value + // from a precomputed table. However, precomputing the table adds 12 + // additional constraints and thus table-version is more expensive than + // addition-version. + + // The context we are working is based on the `outer` curve. However, the + // points and the operations on the points are performed on the `inner` + // curve of the outer curve. We require some parameters from the inner + // curve. + cc := innerCurve(api.Compiler().Curve()) + + // the hints allow to decompose the scalar s into s1 and s2 such that + // s1 + λ * s2 == s mod r, + // where λ is third root of one in 𝔽_r. + sd, err := api.Compiler().NewHint(DecomposeScalarG2, 3, s) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + s1, s2 := sd[0], sd[1] + + // when we split scalar, then s1, s2 < lambda by default. However, to have + // the high 1-2 bits of s1, s2 set, the hint functions compute the + // decomposition for + // s + k*r (for some k) + // instead and omits the last reduction. Thus, to constrain s1 and s2, we + // have to assert that + // s1 + λ * s2 == s + k*r + api.AssertIsEqual(api.Add(s1, api.Mul(s2, cc.lambda)), api.Add(s, api.Mul(cc.fr, sd[2]))) + + // As the decomposed scalars are not fully reduced, then in addition of + // having the high bit set, an overflow bit may also be set. Thus, the total + // number of bits may be one more than the bitlength of λ. + nbits := cc.lambda.BitLen() + 1 + + s1bits := api.ToBinary(s1, nbits) + s2bits := api.ToBinary(s2, nbits) + + var Acc /*accumulator*/, B, B2 /*tmp vars*/ G2Affine + // precompute -Q, -Φ(Q), Φ(Q) + var tableQ, tablePhiQ [2]G2Affine + tableQ[1] = Q + tableQ[0].Neg(api, Q) + cc.phi2(api, &tablePhiQ[1], &Q) + tablePhiQ[0].Neg(api, tablePhiQ[1]) + + // We now initialize the accumulator. Due to the way the scalar is + // decomposed, either the high bits of s1 or s2 are set and we can use the + // incomplete addition laws. + + // Acc = Q + Φ(Q) + Acc = tableQ[1] + Acc.AddAssign(api, tablePhiQ[1]) + + // However, we can not directly add step value conditionally as we may get + // to incomplete path of the addition formula. We either add or subtract + // step value from [2] Acc (instead of conditionally adding step value to + // Acc): + // Acc = [2] (Q + Φ(Q)) ± Q ± Φ(Q) + Acc.Double(api, Acc) + // only y coordinate differs for negation, select on that instead. + B.X = tableQ[0].X + B.Y.Select(api, s1bits[nbits-1], tableQ[1].Y, tableQ[0].Y) + Acc.AddAssign(api, B) + B.X = tablePhiQ[0].X + B.Y.Select(api, s2bits[nbits-1], tablePhiQ[1].Y, tablePhiQ[0].Y) + Acc.AddAssign(api, B) + + // second bit + Acc.Double(api, Acc) + B.X = tableQ[0].X + B.Y.Select(api, s1bits[nbits-2], tableQ[1].Y, tableQ[0].Y) + Acc.AddAssign(api, B) + B.X = tablePhiQ[0].X + B.Y.Select(api, s2bits[nbits-2], tablePhiQ[1].Y, tablePhiQ[0].Y) + Acc.AddAssign(api, B) + + B2.X = tablePhiQ[0].X + for i := nbits - 3; i > 0; i-- { + B.X = Q.X + B.Y.Select(api, s1bits[i], tableQ[1].Y, tableQ[0].Y) + B2.Y.Select(api, s2bits[i], tablePhiQ[1].Y, tablePhiQ[0].Y) + B.AddAssign(api, B2) + Acc.DoubleAndAdd(api, &Acc, &B) + } + + tableQ[0].AddAssign(api, Acc) + Acc.Select(api, s1bits[0], Acc, tableQ[0]) + tablePhiQ[0].AddAssign(api, Acc) + Acc.Select(api, s2bits[0], Acc, tablePhiQ[0]) + + P.X = Acc.X + P.Y = Acc.Y + + return P +} + +// constScalarMul sets P = [s] Q and returns P. +func (P *G2Affine) constScalarMul(api frontend.API, Q G2Affine, s *big.Int) *G2Affine { + // see the comments in varScalarMul. However, two-bit lookup is cheaper if + // bits are constant and here it makes sense to use the table in the main + // loop. + var Acc, negQ, negPhiQ, phiQ G2Affine + cc := innerCurve(api.Compiler().Curve()) + s.Mod(s, cc.fr) + cc.phi2(api, &phiQ, &Q) + + k := ecc.SplitScalar(s, cc.glvBasis) + if k[0].Sign() == -1 { + k[0].Neg(&k[0]) + Q.Neg(api, Q) + } + if k[1].Sign() == -1 { + k[1].Neg(&k[1]) + phiQ.Neg(api, phiQ) + } + nbits := k[0].BitLen() + if k[1].BitLen() > nbits { + nbits = k[1].BitLen() + } + negQ.Neg(api, Q) + negPhiQ.Neg(api, phiQ) + var table [4]G2Affine + table[0] = negQ + table[0].AddAssign(api, negPhiQ) + table[1] = Q + table[1].AddAssign(api, negPhiQ) + table[2] = negQ + table[2].AddAssign(api, phiQ) + table[3] = Q + table[3].AddAssign(api, phiQ) + + Acc = table[3] + // if both high bits are set, then we would get to the incomplete part, + // handle it separately. + if k[0].Bit(nbits-1) == 1 && k[1].Bit(nbits-1) == 1 { + Acc.Double(api, Acc) + Acc.AddAssign(api, table[3]) + nbits = nbits - 1 + } + for i := nbits - 1; i > 0; i-- { + Acc.DoubleAndAdd(api, &Acc, &table[k[0].Bit(i)+2*k[1].Bit(i)]) + } + + negQ.AddAssign(api, Acc) + Acc.Select(api, k[0].Bit(0), Acc, negQ) + negPhiQ.AddAssign(api, Acc) + Acc.Select(api, k[1].Bit(0), Acc, negPhiQ) + P.X, P.Y = Acc.X, Acc.Y + + return P } // Assign a value to self (witness assignment) diff --git a/std/algebra/sw_bls12377/g2_test.go b/std/algebra/sw_bls12377/g2_test.go index 561d524109..54bfe04b57 100644 --- a/std/algebra/sw_bls12377/g2_test.go +++ b/std/algebra/sw_bls12377/g2_test.go @@ -24,6 +24,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/test" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" @@ -76,7 +77,7 @@ type g2AddAssignAffine struct { func (circuit *g2AddAssignAffine) Define(api frontend.API) error { expected := circuit.A - expected.AddAssign(api, &circuit.B) + expected.AddAssign(api, circuit.B) expected.AssertIsEqual(api, circuit.C) return nil } @@ -117,7 +118,7 @@ type g2DoubleAssign struct { func (circuit *g2DoubleAssign) Define(api frontend.API) error { expected := circuit.A - expected.Double(api, &circuit.A) + expected.Double(api, circuit.A) expected.AssertIsEqual(api, circuit.C) return nil } @@ -193,7 +194,7 @@ type g2DoubleAffine struct { func (circuit *g2DoubleAffine) Define(api frontend.API) error { expected := circuit.A - expected.Double(api, &circuit.A) + expected.Double(api, circuit.A) expected.AssertIsEqual(api, circuit.C) return nil } @@ -231,7 +232,7 @@ type g2Neg struct { func (circuit *g2Neg) Define(api frontend.API) error { expected := G2Jac{} - expected.Neg(api, &circuit.A) + expected.Neg(api, circuit.A) expected.AssertIsEqual(api, circuit.C) return nil } @@ -256,6 +257,123 @@ func TestNegG2(t *testing.T) { } +// ------------------------------------------------------------------------------------------------- +// Scalar multiplication + +type g2constantScalarMul struct { + A G2Affine + C G2Affine `gnark:",public"` + R *big.Int +} + +func (circuit *g2constantScalarMul) Define(api frontend.API) error { + expected := G2Affine{} + expected.constScalarMul(api, circuit.A, circuit.R) + expected.AssertIsEqual(api, circuit.C) + return nil +} + +func TestConstantScalarMulG2(t *testing.T) { + // sample random point + _a := randomPointG2() + var a, c bls12377.G2Affine + a.FromJacobian(&_a) + + // create the cs + var circuit, witness g2constantScalarMul + var r fr.Element + r.SetRandom() + // assign the inputs + witness.A.Assign(&a) + // compute the result + br := new(big.Int) + r.ToBigIntRegular(br) + // br is a circuit parameter + circuit.R = br + _a.ScalarMultiplication(&_a, br) + c.FromJacobian(&_a) + witness.C.Assign(&c) + + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_761)) + +} + +type g2varScalarMul struct { + A G2Affine + C G2Affine `gnark:",public"` + R frontend.Variable +} + +func (circuit *g2varScalarMul) Define(api frontend.API) error { + expected := G2Affine{} + expected.varScalarMul(api, circuit.A, circuit.R) + expected.AssertIsEqual(api, circuit.C) + return nil +} + +func TestVarScalarMulG2(t *testing.T) { + // sample random point + _a := randomPointG2() + var a, c bls12377.G2Affine + a.FromJacobian(&_a) + + // create the cs + var circuit, witness g2varScalarMul + var r fr.Element + r.SetRandom() + witness.R = r.String() + // assign the inputs + witness.A.Assign(&a) + // compute the result + var br big.Int + _a.ScalarMultiplication(&_a, r.ToBigIntRegular(&br)) + c.FromJacobian(&_a) + witness.C.Assign(&c) + + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_761)) +} + +type g2ScalarMul struct { + A G2Affine + C G2Affine `gnark:",public"` + Rvar frontend.Variable + Rcon fr.Element +} + +func (circuit *g2ScalarMul) Define(api frontend.API) error { + var expected, expected2 G2Affine + expected.ScalarMul(api, circuit.A, circuit.Rvar) + expected.AssertIsEqual(api, circuit.C) + expected2.ScalarMul(api, circuit.A, circuit.Rcon) + expected2.AssertIsEqual(api, circuit.C) + return nil +} + +func TestScalarMulG2(t *testing.T) { + // sample random point + _a := randomPointG2() + var a, c bls12377.G2Affine + a.FromJacobian(&_a) + + // create the cs + var circuit, witness g2ScalarMul + var r fr.Element + r.SetRandom() + witness.Rvar = r.String() + circuit.Rcon = r + // assign the inputs + witness.A.Assign(&a) + // compute the result + var br big.Int + _a.ScalarMultiplication(&_a, r.ToBigIntRegular(&br)) + c.FromJacobian(&_a) + witness.C.Assign(&c) + + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_761)) +} func randomPointG2() bls12377.G2Jac { _, p2, _, _ := bls12377.Generators() @@ -299,3 +417,61 @@ func BenchmarkDoubleAndAddAffineG2(b *testing.B) { }) b.Log("groth16", ccsBench.GetNbConstraints()) } + +func BenchmarkConstScalarMulG2(b *testing.B) { + var c g2constantScalarMul + // this is q - 1 + r, ok := new(big.Int).SetString("660539884262666720468348340822774968888139573360124440321458176", 10) + if !ok { + b.Fatal("invalid integer") + } + c.R = r + b.Run("groth16", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_761, r1cs.NewBuilder, &c) + } + + }) + b.Log("groth16", ccsBench.GetNbConstraints()) + b.Run("plonk", func(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + ccsBench, err = frontend.Compile(ecc.BW6_761, scs.NewBuilder, &c) + if err != nil { + b.Fatal(err) + } + } + + }) + b.Log("plonk", ccsBench.GetNbConstraints()) + +} + +func BenchmarkVarScalarMulG2(b *testing.B) { + var c g2varScalarMul + // this is q - 1 + r, ok := new(big.Int).SetString("660539884262666720468348340822774968888139573360124440321458176", 10) + if !ok { + b.Fatal("invalid integer") + } + c.R = r + b.Run("groth16", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_761, r1cs.NewBuilder, &c) + } + + }) + b.Log("groth16", ccsBench.GetNbConstraints()) + b.Run("plonk", func(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + ccsBench, err = frontend.Compile(ecc.BW6_761, scs.NewBuilder, &c) + if err != nil { + b.Fatal(err) + } + } + + }) + b.Log("plonk", ccsBench.GetNbConstraints()) + +} diff --git a/std/algebra/sw_bls12377/inner.go b/std/algebra/sw_bls12377/inner.go index 7c78f11e2a..319192de38 100644 --- a/std/algebra/sw_bls12377/inner.go +++ b/std/algebra/sw_bls12377/inner.go @@ -12,18 +12,20 @@ import ( func init() { mappingOnce.Do(func() { bls12377lambda := new(big.Int).SetBytes([]byte{0x45, 0x22, 0x17, 0xcc, 0x90, 0x00, 0x00, 0x01, 0x0a, 0x11, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00}) - bls12377thirdRootOne := new(big.Int).SetBytes([]byte{ + bls12377thirdRootOne1 := new(big.Int).SetBytes([]byte{ 0x09, 0xb3, 0xaf, 0x05, 0xdd, 0x14, 0xf6, 0xec, 0x61, 0x9a, 0xaf, 0x7d, 0x34, 0x59, 0x4a, 0xab, 0xc5, 0xed, 0x13, 0x47, 0x97, 0x0d, 0xec, 0x00, 0x45, 0x22, 0x17, 0xcc, 0x90, 0x00, 0x00, 0x00, 0x85, 0x08, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01}) + bls12377thirdRootOne2 := new(big.Int).Mul(bls12377thirdRootOne1, bls12377thirdRootOne1) bls12377glvBasis := new(ecc.Lattice) ecc.PrecomputeLattice(ecc.BLS12_377.Info().Fr.Modulus(), bls12377lambda, bls12377glvBasis) innerCurves[ecc.BW6_761] = &innerConfig{ - thirdRootOne: bls12377thirdRootOne, - glvBasis: bls12377glvBasis, - lambda: bls12377lambda, - fp: ecc.BLS12_377.Info().Fp.Modulus(), - fr: ecc.BLS12_377.Info().Fr.Modulus(), + thirdRootOne1: bls12377thirdRootOne1, + thirdRootOne2: bls12377thirdRootOne2, + glvBasis: bls12377glvBasis, + lambda: bls12377lambda, + fp: ecc.BLS12_377.Info().Fp.Modulus(), + fr: ecc.BLS12_377.Info().Fr.Modulus(), } }) } @@ -31,17 +33,24 @@ func init() { var mappingOnce sync.Once type innerConfig struct { - thirdRootOne *big.Int - glvBasis *ecc.Lattice - lambda *big.Int - fr *big.Int - fp *big.Int + thirdRootOne1 *big.Int + thirdRootOne2 *big.Int + glvBasis *ecc.Lattice + lambda *big.Int + fr *big.Int + fp *big.Int } var innerCurves = make(map[ecc.ID]*innerConfig) -func (cc *innerConfig) phi(api frontend.API, res, P *G1Affine) *G1Affine { - res.X = api.Mul(P.X, cc.thirdRootOne) +func (cc *innerConfig) phi1(api frontend.API, res, P *G1Affine) *G1Affine { + res.X = api.Mul(P.X, cc.thirdRootOne1) + res.Y = P.Y + return res +} + +func (cc *innerConfig) phi2(api frontend.API, res, P *G2Affine) *G2Affine { + res.X.MulByFp(api, P.X, cc.thirdRootOne2) res.Y = P.Y return res } From d7a778cf8972cac63be59d9f601f74b48b0a0fa8 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 12 Apr 2022 16:37:09 +0200 Subject: [PATCH 02/11] feat(std/sw_bls24315): G2 scalar multiplication --- std/algebra/fields_bls24315/e2.go | 9 + std/algebra/fields_bls24315/e4.go | 9 + std/algebra/sw_bls24315/g1.go | 4 +- std/algebra/sw_bls24315/g2.go | 327 +++++++++++++++++++++++------ std/algebra/sw_bls24315/g2_test.go | 184 +++++++++++++++- std/algebra/sw_bls24315/inner.go | 35 +-- std/algebra/sw_bls24315/pairing.go | 2 +- 7 files changed, 491 insertions(+), 79 deletions(-) diff --git a/std/algebra/fields_bls24315/e2.go b/std/algebra/fields_bls24315/e2.go index 49cee8c1ed..435e5ccccf 100644 --- a/std/algebra/fields_bls24315/e2.go +++ b/std/algebra/fields_bls24315/e2.go @@ -236,3 +236,12 @@ func (e *E2) AssertIsEqual(api frontend.API, other E2) { api.AssertIsEqual(e.A0, other.A0) api.AssertIsEqual(e.A1, other.A1) } + +// Select sets e to r1 if b=1, r2 otherwise +func (e *E2) Select(api frontend.API, b frontend.Variable, r1, r2 E2) *E2 { + + e.A0 = api.Select(b, r1.A0, r2.A0) + e.A1 = api.Select(b, r1.A1, r2.A1) + + return e +} diff --git a/std/algebra/fields_bls24315/e4.go b/std/algebra/fields_bls24315/e4.go index ae8af9586b..eea4e2e687 100644 --- a/std/algebra/fields_bls24315/e4.go +++ b/std/algebra/fields_bls24315/e4.go @@ -245,3 +245,12 @@ func (e *E4) AssertIsEqual(api frontend.API, other E4) { e.B0.AssertIsEqual(api, other.B0) e.B1.AssertIsEqual(api, other.B1) } + +// Select sets e to r1 if b=1, r2 otherwise +func (e *E4) Select(api frontend.API, b frontend.Variable, r1, r2 E4) *E4 { + + e.B0.Select(api, b, r1.B0, r2.B0) + e.B1.Select(api, b, r1.B1, r2.B1) + + return e +} diff --git a/std/algebra/sw_bls24315/g1.go b/std/algebra/sw_bls24315/g1.go index a1b3e17112..8cf4c3cc6c 100644 --- a/std/algebra/sw_bls24315/g1.go +++ b/std/algebra/sw_bls24315/g1.go @@ -283,7 +283,7 @@ func (P *G1Affine) varScalarMul(api frontend.API, Q G1Affine, s frontend.Variabl var tableQ, tablePhiQ [2]G1Affine tableQ[1] = Q tableQ[0].Neg(api, Q) - cc.phi(api, &tablePhiQ[1], &Q) + cc.phi1(api, &tablePhiQ[1], &Q) tablePhiQ[0].Neg(api, tablePhiQ[1]) // We now initialize the accumulator. Due to the way the scalar is @@ -345,7 +345,7 @@ func (P *G1Affine) constScalarMul(api frontend.API, Q G1Affine, s *big.Int) *G1A var Acc, negQ, negPhiQ, phiQ G1Affine cc := innerCurve(api.Compiler().Curve()) s.Mod(s, cc.fr) - cc.phi(api, &phiQ, &Q) + cc.phi1(api, &phiQ, &Q) k := ecc.SplitScalar(s, cc.glvBasis) if k[0].Sign() == -1 { diff --git a/std/algebra/sw_bls24315/g2.go b/std/algebra/sw_bls24315/g2.go index 0b38f62c27..01622c4be0 100644 --- a/std/algebra/sw_bls24315/g2.go +++ b/std/algebra/sw_bls24315/g2.go @@ -17,7 +17,11 @@ limitations under the License. package sw_bls24315 import ( + "math/big" + + "github.com/consensys/gnark-crypto/ecc" bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315" + "github.com/consensys/gnark/backend/hint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/fields_bls24315" ) @@ -27,27 +31,13 @@ type G2Jac struct { X, Y, Z fields_bls24315.E4 } -type G2Proj struct { - X, Y, Z fields_bls24315.E4 -} - // G2Affine point in affine coords type G2Affine struct { X, Y fields_bls24315.E4 } -// ToProj sets p to p1 in projective coords and return it -func (p *G2Jac) ToProj(api frontend.API, p1 *G2Jac) *G2Jac { - p.X.Mul(api, p1.X, p1.Z) - p.Y = p1.Y - var t fields_bls24315.E4 - t.Square(api, p1.Z) - p.Z.Mul(api, p.Z, t) - return p -} - // Neg outputs -p -func (p *G2Jac) Neg(api frontend.API, p1 *G2Jac) *G2Jac { +func (p *G2Jac) Neg(api frontend.API, p1 G2Jac) *G2Jac { p.Y.Neg(api, p1.Y) p.X = p1.X p.Z = p1.Z @@ -55,12 +45,38 @@ func (p *G2Jac) Neg(api frontend.API, p1 *G2Jac) *G2Jac { } // Neg outputs -p -func (p *G2Affine) Neg(api frontend.API, p1 *G2Affine) *G2Affine { +func (p *G2Affine) Neg(api frontend.API, p1 G2Affine) *G2Affine { p.Y.Neg(api, p1.Y) p.X = p1.X return p } +// AddAssign add p1 to p and return p +func (p *G2Affine) AddAssign(api frontend.API, p1 G2Affine) *G2Affine { + + var n, d, l, xr, yr fields_bls24315.E4 + + // compute lambda = (p1.y-p.y)/(p1.x-p.x) + n.Sub(api, p1.Y, p.Y) + d.Sub(api, p1.X, p.X) + l.DivUnchecked(api, n, d) + + // xr =lambda**2-p1.x-p.x + xr.Square(api, l). + Sub(api, xr, p1.X). + Sub(api, xr, p.X) + + // yr = lambda(p.x - xr)-p.y + yr.Sub(api, p.X, xr). + Mul(api, l, yr). + Sub(api, yr, p.Y) + + p.X = xr + p.Y = yr + + return p +} + // AddAssign adds 2 point in Jacobian coordinates // p=p, a=p1 func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac) *G2Jac { @@ -115,35 +131,58 @@ func (p *G2Jac) AddAssign(api frontend.API, p1 *G2Jac) *G2Jac { return p } -// AddAssign add p1 to p and return p -func (p *G2Affine) AddAssign(api frontend.API, p1 *G2Affine) *G2Affine { +// Double doubles a point in jacobian coords +func (p *G2Jac) Double(api frontend.API, p1 G2Jac) *G2Jac { - var n, d, l, xr, yr fields_bls24315.E4 + var XX, YY, YYYY, ZZ, S, M, T fields_bls24315.E4 - // compute lambda = (p1.y-p.y)/(p1.x-p.x) - n.Sub(api, p1.Y, p.Y) - d.Sub(api, p1.X, p.X) - l.DivUnchecked(api, n, d) + XX.Square(api, p.X) + YY.Square(api, p.Y) + YYYY.Square(api, YY) + ZZ.Square(api, p.Z) + S.Add(api, p.X, YY) + S.Square(api, S) + S.Sub(api, S, XX) + S.Sub(api, S, YYYY) + S.Add(api, S, S) + M.MulByFp(api, XX, 3) // M = 3*XX+a*ZZ², here a=0 (we suppose sw has j invariant 0) + p.Z.Add(api, p.Z, p.Y) + p.Z.Square(api, p.Z) + p.Z.Sub(api, p.Z, YY) + p.Z.Sub(api, p.Z, ZZ) + p.X.Square(api, M) + T.Add(api, S, S) + p.X.Sub(api, p.X, T) + p.Y.Sub(api, S, p.X) + p.Y.Mul(api, p.Y, M) + YYYY.MulByFp(api, YYYY, 8) + p.Y.Sub(api, p.Y, YYYY) - // xr =lambda**2-p1.x-p.x - xr.Square(api, l). - Sub(api, xr, p1.X). - Sub(api, xr, p.X) + return p +} - // yr = lambda(p.x - xr)-p.y - yr.Sub(api, p.X, xr). - Mul(api, l, yr). - Sub(api, yr, p.Y) +// Select sets p1 if b=1, p2 if b=0, and returns it. b must be boolean constrained +func (p *G2Affine) Select(api frontend.API, b frontend.Variable, p1, p2 G2Affine) *G2Affine { - p.X = xr - p.Y = yr + p.X.Select(api, b, p1.X, p2.X) + p.Y.Select(api, b, p1.Y, p2.Y) return p } +// FromJac sets p to p1 in affine and returns it +func (p *G2Affine) FromJac(api frontend.API, p1 G2Jac) *G2Affine { + var s fields_bls24315.E4 + s.Mul(api, p1.Z, p1.Z) + p.X.DivUnchecked(api, p1.X, s) + s.Mul(api, s, p1.Z) + p.Y.DivUnchecked(api, p1.Y, s) + return p +} + // Double compute 2*p1, assign the result to p and return it // Only for curve with j invariant 0 (a=0). -func (p *G2Affine) Double(api frontend.API, p1 *G2Affine) *G2Affine { +func (p *G2Affine) Double(api frontend.API, p1 G2Affine) *G2Affine { var n, d, l, xr, yr fields_bls24315.E4 @@ -169,34 +208,204 @@ func (p *G2Affine) Double(api frontend.API, p1 *G2Affine) *G2Affine { } -// Double doubles a point in jacobian coords -func (p *G2Jac) Double(api frontend.API, p1 *G2Jac) *G2Jac { +// ScalarMul sets P = [s] Q and returns P. +// +// The method chooses an implementation based on scalar s. If it is constant, +// then the compiled circuit depends on s. If it is variable type, then +// the circuit is independent of the inputs. +func (P *G2Affine) ScalarMul(api frontend.API, Q G2Affine, s interface{}) *G2Affine { + if n, ok := api.Compiler().ConstantValue(s); ok { + return P.constScalarMul(api, Q, n) + } else { + return P.varScalarMul(api, Q, s) + } +} - var XX, YY, YYYY, ZZ, S, M, T fields_bls24315.E4 +var DecomposeScalarG2 = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) error { + cc := innerCurve(curve) + sp := ecc.SplitScalar(inputs[0], cc.glvBasis) + res[0].Set(&(sp[0])) + res[1].Set(&(sp[1])) + one := big.NewInt(1) + // add (lambda+1, lambda) until scalar compostion is over Fr to ensure that + // the high bits are set in decomposition. + for res[0].Cmp(cc.lambda) < 1 && res[1].Cmp(cc.lambda) < 1 { + res[0].Add(res[0], cc.lambda) + res[0].Add(res[0], one) + res[1].Add(res[1], cc.lambda) + } + // figure out how many times we have overflowed + res[2].Mul(res[1], cc.lambda).Add(res[2], res[0]) + res[2].Sub(res[2], inputs[0]) + res[2].Div(res[2], cc.fr) + + return nil +} - XX.Square(api, p.X) - YY.Square(api, p.Y) - YYYY.Square(api, YY) - ZZ.Square(api, p.Z) - S.Add(api, p.X, YY) - S.Square(api, S) - S.Sub(api, S, XX) - S.Sub(api, S, YYYY) - S.Add(api, S, S) - M.MulByFp(api, XX, 3) // M = 3*XX+a*ZZ², here a=0 (we suppose sw has j invariant 0) - p.Z.Add(api, p.Z, p.Y) - p.Z.Square(api, p.Z) - p.Z.Sub(api, p.Z, YY) - p.Z.Sub(api, p.Z, ZZ) - p.X.Square(api, M) - T.Add(api, S, S) - p.X.Sub(api, p.X, T) - p.Y.Sub(api, S, p.X) - p.Y.Mul(api, p.Y, M) - YYYY.MulByFp(api, YYYY, 8) - p.Y.Sub(api, p.Y, YYYY) +func init() { + hint.Register(DecomposeScalarG2) +} - return p +// varScalarMul sets P = [s] Q and returns P. +func (P *G2Affine) varScalarMul(api frontend.API, Q G2Affine, s frontend.Variable) *G2Affine { + // This method computes [s] Q. We use several methods to reduce the number + // of added constraints - first, instead of classical double-and-add, we use + // the optimized version from https://github.com/zcash/zcash/issues/3924 + // which allows to omit computation of several intermediate values. + // Secondly, we use the GLV scalar multiplication to reduce the number + // iterations in the main loop. There is a small difference though - as + // two-bit select takes three constraints, then it takes as many constraints + // to compute ± Q ± Φ(Q) every iteration instead of selecting the value + // from a precomputed table. However, precomputing the table adds 12 + // additional constraints and thus table-version is more expensive than + // addition-version. + + // The context we are working is based on the `outer` curve. However, the + // points and the operations on the points are performed on the `inner` + // curve of the outer curve. We require some parameters from the inner + // curve. + cc := innerCurve(api.Compiler().Curve()) + + // the hints allow to decompose the scalar s into s1 and s2 such that + // s1 + λ * s2 == s mod r, + // where λ is third root of one in 𝔽_r. + sd, err := api.Compiler().NewHint(DecomposeScalarG2, 3, s) + if err != nil { + // err is non-nil only for invalid number of inputs + panic(err) + } + s1, s2 := sd[0], sd[1] + + // when we split scalar, then s1, s2 < lambda by default. However, to have + // the high 1-2 bits of s1, s2 set, the hint functions compute the + // decomposition for + // s + k*r (for some k) + // instead and omits the last reduction. Thus, to constrain s1 and s2, we + // have to assert that + // s1 + λ * s2 == s + k*r + api.AssertIsEqual(api.Add(s1, api.Mul(s2, cc.lambda)), api.Add(s, api.Mul(cc.fr, sd[2]))) + + // As the decomposed scalars are not fully reduced, then in addition of + // having the high bit set, an overflow bit may also be set. Thus, the total + // number of bits may be one more than the bitlength of λ. + nbits := cc.lambda.BitLen() + 1 + + s1bits := api.ToBinary(s1, nbits) + s2bits := api.ToBinary(s2, nbits) + + var Acc /*accumulator*/, B, B2 /*tmp vars*/ G2Affine + // precompute -Q, -Φ(Q), Φ(Q) + var tableQ, tablePhiQ [2]G2Affine + tableQ[1] = Q + tableQ[0].Neg(api, Q) + cc.phi2(api, &tablePhiQ[1], &Q) + tablePhiQ[0].Neg(api, tablePhiQ[1]) + + // We now initialize the accumulator. Due to the way the scalar is + // decomposed, either the high bits of s1 or s2 are set and we can use the + // incomplete addition laws. + + // Acc = Q + Φ(Q) + Acc = tableQ[1] + Acc.AddAssign(api, tablePhiQ[1]) + + // However, we can not directly add step value conditionally as we may get + // to incomplete path of the addition formula. We either add or subtract + // step value from [2] Acc (instead of conditionally adding step value to + // Acc): + // Acc = [2] (Q + Φ(Q)) ± Q ± Φ(Q) + Acc.Double(api, Acc) + // only y coordinate differs for negation, select on that instead. + B.X = tableQ[0].X + B.Y.Select(api, s1bits[nbits-1], tableQ[1].Y, tableQ[0].Y) + Acc.AddAssign(api, B) + B.X = tablePhiQ[0].X + B.Y.Select(api, s2bits[nbits-1], tablePhiQ[1].Y, tablePhiQ[0].Y) + Acc.AddAssign(api, B) + + // second bit + Acc.Double(api, Acc) + B.X = tableQ[0].X + B.Y.Select(api, s1bits[nbits-2], tableQ[1].Y, tableQ[0].Y) + Acc.AddAssign(api, B) + B.X = tablePhiQ[0].X + B.Y.Select(api, s2bits[nbits-2], tablePhiQ[1].Y, tablePhiQ[0].Y) + Acc.AddAssign(api, B) + + B2.X = tablePhiQ[0].X + for i := nbits - 3; i > 0; i-- { + B.X = Q.X + B.Y.Select(api, s1bits[i], tableQ[1].Y, tableQ[0].Y) + B2.Y.Select(api, s2bits[i], tablePhiQ[1].Y, tablePhiQ[0].Y) + B.AddAssign(api, B2) + Acc.DoubleAndAdd(api, &Acc, &B) + } + + tableQ[0].AddAssign(api, Acc) + Acc.Select(api, s1bits[0], Acc, tableQ[0]) + tablePhiQ[0].AddAssign(api, Acc) + Acc.Select(api, s2bits[0], Acc, tablePhiQ[0]) + + P.X = Acc.X + P.Y = Acc.Y + + return P +} + +// constScalarMul sets P = [s] Q and returns P. +func (P *G2Affine) constScalarMul(api frontend.API, Q G2Affine, s *big.Int) *G2Affine { + // see the comments in varScalarMul. However, two-bit lookup is cheaper if + // bits are constant and here it makes sense to use the table in the main + // loop. + var Acc, negQ, negPhiQ, phiQ G2Affine + cc := innerCurve(api.Compiler().Curve()) + s.Mod(s, cc.fr) + cc.phi2(api, &phiQ, &Q) + + k := ecc.SplitScalar(s, cc.glvBasis) + if k[0].Sign() == -1 { + k[0].Neg(&k[0]) + Q.Neg(api, Q) + } + if k[1].Sign() == -1 { + k[1].Neg(&k[1]) + phiQ.Neg(api, phiQ) + } + nbits := k[0].BitLen() + if k[1].BitLen() > nbits { + nbits = k[1].BitLen() + } + negQ.Neg(api, Q) + negPhiQ.Neg(api, phiQ) + var table [4]G2Affine + table[0] = negQ + table[0].AddAssign(api, negPhiQ) + table[1] = Q + table[1].AddAssign(api, negPhiQ) + table[2] = negQ + table[2].AddAssign(api, phiQ) + table[3] = Q + table[3].AddAssign(api, phiQ) + + Acc = table[3] + // if both high bits are set, then we would get to the incomplete part, + // handle it separately. + if k[0].Bit(nbits-1) == 1 && k[1].Bit(nbits-1) == 1 { + Acc.Double(api, Acc) + Acc.AddAssign(api, table[3]) + nbits = nbits - 1 + } + for i := nbits - 1; i > 0; i-- { + Acc.DoubleAndAdd(api, &Acc, &table[k[0].Bit(i)+2*k[1].Bit(i)]) + } + + negQ.AddAssign(api, Acc) + Acc.Select(api, k[0].Bit(0), Acc, negQ) + negPhiQ.AddAssign(api, Acc) + Acc.Select(api, k[1].Bit(0), Acc, negPhiQ) + P.X, P.Y = Acc.X, Acc.Y + + return P } // Assign a value to self (witness assignment) diff --git a/std/algebra/sw_bls24315/g2_test.go b/std/algebra/sw_bls24315/g2_test.go index 9a10575c6b..6e7f7f8b9d 100644 --- a/std/algebra/sw_bls24315/g2_test.go +++ b/std/algebra/sw_bls24315/g2_test.go @@ -24,6 +24,7 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/test" bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315" @@ -76,7 +77,7 @@ type g2AddAssignAffine struct { func (circuit *g2AddAssignAffine) Define(api frontend.API) error { expected := circuit.A - expected.AddAssign(api, &circuit.B) + expected.AddAssign(api, circuit.B) expected.AssertIsEqual(api, circuit.C) return nil } @@ -117,7 +118,7 @@ type g2DoubleAssign struct { func (circuit *g2DoubleAssign) Define(api frontend.API) error { expected := circuit.A - expected.Double(api, &circuit.A) + expected.Double(api, circuit.A) expected.AssertIsEqual(api, circuit.C) return nil } @@ -193,7 +194,7 @@ type g2DoubleAffine struct { func (circuit *g2DoubleAffine) Define(api frontend.API) error { expected := circuit.A - expected.Double(api, &circuit.A) + expected.Double(api, circuit.A) expected.AssertIsEqual(api, circuit.C) return nil } @@ -231,7 +232,7 @@ type g2Neg struct { func (circuit *g2Neg) Define(api frontend.API) error { expected := G2Jac{} - expected.Neg(api, &circuit.A) + expected.Neg(api, circuit.A) expected.AssertIsEqual(api, circuit.C) return nil } @@ -256,6 +257,123 @@ func TestNegG2(t *testing.T) { } +// ------------------------------------------------------------------------------------------------- +// Scalar multiplication + +type g2constantScalarMul struct { + A G2Affine + C G2Affine `gnark:",public"` + R *big.Int +} + +func (circuit *g2constantScalarMul) Define(api frontend.API) error { + expected := G2Affine{} + expected.constScalarMul(api, circuit.A, circuit.R) + expected.AssertIsEqual(api, circuit.C) + return nil +} + +func TestConstantScalarMulG2(t *testing.T) { + // sample random point + _a := randomPointG2() + var a, c bls24315.G2Affine + a.FromJacobian(&_a) + + // create the cs + var circuit, witness g2constantScalarMul + var r fr.Element + r.SetRandom() + // assign the inputs + witness.A.Assign(&a) + // compute the result + br := new(big.Int) + r.ToBigIntRegular(br) + // br is a circuit parameter + circuit.R = br + _a.ScalarMultiplication(&_a, br) + c.FromJacobian(&_a) + witness.C.Assign(&c) + + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) + +} + +type g2varScalarMul struct { + A G2Affine + C G2Affine `gnark:",public"` + R frontend.Variable +} + +func (circuit *g2varScalarMul) Define(api frontend.API) error { + expected := G2Affine{} + expected.varScalarMul(api, circuit.A, circuit.R) + expected.AssertIsEqual(api, circuit.C) + return nil +} + +func TestVarScalarMulG2(t *testing.T) { + // sample random point + _a := randomPointG2() + var a, c bls24315.G2Affine + a.FromJacobian(&_a) + + // create the cs + var circuit, witness g2varScalarMul + var r fr.Element + r.SetRandom() + witness.R = r.String() + // assign the inputs + witness.A.Assign(&a) + // compute the result + var br big.Int + _a.ScalarMultiplication(&_a, r.ToBigIntRegular(&br)) + c.FromJacobian(&_a) + witness.C.Assign(&c) + + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) +} + +type g2ScalarMul struct { + A G2Affine + C G2Affine `gnark:",public"` + Rvar frontend.Variable + Rcon fr.Element +} + +func (circuit *g2ScalarMul) Define(api frontend.API) error { + var expected, expected2 G2Affine + expected.ScalarMul(api, circuit.A, circuit.Rvar) + expected.AssertIsEqual(api, circuit.C) + expected2.ScalarMul(api, circuit.A, circuit.Rcon) + expected2.AssertIsEqual(api, circuit.C) + return nil +} + +func TestScalarMulG2(t *testing.T) { + // sample random point + _a := randomPointG2() + var a, c bls24315.G2Affine + a.FromJacobian(&_a) + + // create the cs + var circuit, witness g2ScalarMul + var r fr.Element + r.SetRandom() + witness.Rvar = r.String() + circuit.Rcon = r + // assign the inputs + witness.A.Assign(&a) + // compute the result + var br big.Int + _a.ScalarMultiplication(&_a, r.ToBigIntRegular(&br)) + c.FromJacobian(&_a) + witness.C.Assign(&c) + + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) +} func randomPointG2() bls24315.G2Jac { _, p2, _, _ := bls24315.Generators() @@ -299,3 +417,61 @@ func BenchmarkDoubleAndAddAffineG2(b *testing.B) { }) b.Log("groth16", ccsBench.GetNbConstraints()) } + +func BenchmarkConstScalarMulG2(b *testing.B) { + var c g2constantScalarMul + // this is q - 1 + r, ok := new(big.Int).SetString("660539884262666720468348340822774968888139573360124440321458176", 10) + if !ok { + b.Fatal("invalid integer") + } + c.R = r + b.Run("groth16", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_633, r1cs.NewBuilder, &c) + } + + }) + b.Log("groth16", ccsBench.GetNbConstraints()) + b.Run("plonk", func(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + ccsBench, err = frontend.Compile(ecc.BW6_633, scs.NewBuilder, &c) + if err != nil { + b.Fatal(err) + } + } + + }) + b.Log("plonk", ccsBench.GetNbConstraints()) + +} + +func BenchmarkVarScalarMulG2(b *testing.B) { + var c g2varScalarMul + // this is q - 1 + r, ok := new(big.Int).SetString("660539884262666720468348340822774968888139573360124440321458176", 10) + if !ok { + b.Fatal("invalid integer") + } + c.R = r + b.Run("groth16", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_633, r1cs.NewBuilder, &c) + } + + }) + b.Log("groth16", ccsBench.GetNbConstraints()) + b.Run("plonk", func(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + ccsBench, err = frontend.Compile(ecc.BW6_633, scs.NewBuilder, &c) + if err != nil { + b.Fatal(err) + } + } + + }) + b.Log("plonk", ccsBench.GetNbConstraints()) + +} diff --git a/std/algebra/sw_bls24315/inner.go b/std/algebra/sw_bls24315/inner.go index 6f237eec6c..da726a3f0d 100644 --- a/std/algebra/sw_bls24315/inner.go +++ b/std/algebra/sw_bls24315/inner.go @@ -15,20 +15,22 @@ func init() { 0x4a, 0x9d, 0xa1, 0x2b, 0x25, 0xfc, 0x7e, 0xc9, 0xcf, 0x92, 0x7a, 0x99, 0x19, 0x73, 0x9f, 0x46, 0x27, 0xd9, 0x92, 0x6e, 0x38, 0x20, 0xfb, 0xfa, 0x01, 0x80, 0x00, 0x01}) - bls24315thirdRootOne := new(big.Int).SetBytes([]byte{ + bls24315thirdRootOne1 := new(big.Int).SetBytes([]byte{ 0x04, 0xc2, 0x3a, 0x02, 0xa2, 0x79, 0x2a, 0xda, 0xed, 0x93, 0x38, 0xb4, 0xa8, 0x19, 0x5d, 0x81, 0xe9, 0xa0, 0x5f, 0x2f, 0x09, 0x88, 0xc6, 0x57, 0x4e, 0xbb, 0xb2, 0xb0, 0xf7, 0x7c, 0x94, 0x0a, 0x4f, 0x58, 0x14, 0xfe, 0x80, 0x60, 0x00, 0x02, }) + bls24315thirdRootOne2 := new(big.Int).Mul(bls24315thirdRootOne1, bls24315thirdRootOne1) bls24315glvBasis := new(ecc.Lattice) ecc.PrecomputeLattice(ecc.BLS24_315.Info().Fr.Modulus(), bls24315lambda, bls24315glvBasis) innerCurves[ecc.BW6_633] = &innerConfig{ - thirdRootOne: bls24315thirdRootOne, - glvBasis: bls24315glvBasis, - lambda: bls24315lambda, - fp: ecc.BLS24_315.Info().Fp.Modulus(), - fr: ecc.BLS24_315.Info().Fr.Modulus(), + thirdRootOne1: bls24315thirdRootOne1, + thirdRootOne2: bls24315thirdRootOne2, + glvBasis: bls24315glvBasis, + lambda: bls24315lambda, + fp: ecc.BLS24_315.Info().Fp.Modulus(), + fr: ecc.BLS24_315.Info().Fr.Modulus(), } }) } @@ -36,17 +38,24 @@ func init() { var mappingOnce sync.Once type innerConfig struct { - thirdRootOne *big.Int - glvBasis *ecc.Lattice - lambda *big.Int - fr *big.Int - fp *big.Int + thirdRootOne1 *big.Int + thirdRootOne2 *big.Int + glvBasis *ecc.Lattice + lambda *big.Int + fr *big.Int + fp *big.Int } var innerCurves = make(map[ecc.ID]*innerConfig) -func (cc *innerConfig) phi(api frontend.API, res, P *G1Affine) *G1Affine { - res.X = api.Mul(P.X, cc.thirdRootOne) +func (cc *innerConfig) phi1(api frontend.API, res, P *G1Affine) *G1Affine { + res.X = api.Mul(P.X, cc.thirdRootOne1) + res.Y = P.Y + return res +} + +func (cc *innerConfig) phi2(api frontend.API, res, P *G2Affine) *G2Affine { + res.X.MulByFp(api, P.X, cc.thirdRootOne2) res.Y = P.Y return res } diff --git a/std/algebra/sw_bls24315/pairing.go b/std/algebra/sw_bls24315/pairing.go index eedf746aa7..b1e1fb11e0 100644 --- a/std/algebra/sw_bls24315/pairing.go +++ b/std/algebra/sw_bls24315/pairing.go @@ -56,7 +56,7 @@ func MillerLoop(api frontend.API, P []G1Affine, Q []G2Affine) (GT, error) { xOverY := make([]frontend.Variable, n) for k := 0; k < n; k++ { Qacc[k] = Q[k] - Qneg[k].Neg(api, &Q[k]) + Qneg[k].Neg(api, Q[k]) yInv[k] = api.DivUnchecked(1, P[k].Y) xOverY[k] = api.DivUnchecked(P[k].X, P[k].Y) } From 8a2f2316227241ccfe2d65283b9e2d555fc81aad Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 14 Apr 2022 09:29:49 +0200 Subject: [PATCH 03/11] Merge branch develop into feat/std/kzg-verifier --- std/algebra/sw_bls12377/g1.go | 6 +- std/kzg_bls12377/verifier.go | 80 +++++++++++++++++++++ std/kzg_bls12377/verifier_test.go | 114 ++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 std/kzg_bls12377/verifier.go create mode 100644 std/kzg_bls12377/verifier_test.go diff --git a/std/algebra/sw_bls12377/g1.go b/std/algebra/sw_bls12377/g1.go index 947795c31f..ec2cb7f3b8 100644 --- a/std/algebra/sw_bls12377/g1.go +++ b/std/algebra/sw_bls12377/g1.go @@ -206,7 +206,7 @@ func (P *G1Affine) ScalarMul(api frontend.API, Q G1Affine, s interface{}) *G1Aff } } -var DecomposeScalar = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) error { +var DecomposeScalarG1 = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) error { cc := innerCurve(curve) sp := ecc.SplitScalar(inputs[0], cc.glvBasis) res[0].Set(&(sp[0])) @@ -228,7 +228,7 @@ var DecomposeScalar = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) erro } func init() { - hint.Register(DecomposeScalar) + hint.Register(DecomposeScalarG1) } // varScalarMul sets P = [s] Q and returns P. @@ -254,7 +254,7 @@ func (P *G1Affine) varScalarMul(api frontend.API, Q G1Affine, s frontend.Variabl // the hints allow to decompose the scalar s into s1 and s2 such that // s1 + λ * s2 == s mod r, // where λ is third root of one in 𝔽_r. - sd, err := api.Compiler().NewHint(DecomposeScalar, 3, s) + sd, err := api.Compiler().NewHint(DecomposeScalarG1, 3, s) if err != nil { // err is non-nil only for invalid number of inputs panic(err) diff --git a/std/kzg_bls12377/verifier.go b/std/kzg_bls12377/verifier.go new file mode 100644 index 0000000000..9f749ea7b4 --- /dev/null +++ b/std/kzg_bls12377/verifier.go @@ -0,0 +1,80 @@ +/* +Copyright © 2020 ConsenSys + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package kzg_bls12377 provides a ZKP-circuit function to verify BLS12_377 KZG inside a BW6_761 circuit. +package kzg_bls12377 + +import ( + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/algebra/fields_bls12377" + "github.com/consensys/gnark/std/algebra/sw_bls12377" +) + +// Digest commitment of a polynomial. +type Digest = sw_bls12377.G1Affine + +// Pointc evaluation point. +type Point = frontend.Variable + +// VK verification key (G2 part of SRS) +type VK struct { + G1 sw_bls12377.G1Affine // G₁ + G2 [2]sw_bls12377.G2Affine // [G₂, [α]G₂] +} + +// OpeningProof KZG proof for opening at a single point. +type OpeningProof struct { + // H quotient polynomial (f - f(z))/(x-z) + H sw_bls12377.G1Affine + + // ClaimedValue purported value + ClaimedValue frontend.Variable +} + +// Verify verifies a KZG opening proof at a single point +func Verify(api frontend.API, commitment Digest, proof OpeningProof, point Point, srs VK) { + + // [f(a)]G₁ + var claimedValueG1Aff sw_bls12377.G1Affine + claimedValueG1Aff.ScalarMul(api, srs.G1, proof.ClaimedValue) + + // [f(α) - f(a)]G₁ + var fminusfaG1 sw_bls12377.G1Affine + fminusfaG1.Neg(api, claimedValueG1Aff) + fminusfaG1.AddAssign(api, commitment) + + // [-H(α)]G₁ + var negH sw_bls12377.G1Affine + negH.Neg(api, proof.H) + + // [α-a]G₂ + var alphaMinusaG2 sw_bls12377.G2Affine + alphaMinusaG2.ScalarMul(api, srs.G2[0], &point). + Neg(api, alphaMinusaG2). + AddAssign(api, srs.G2[1]) + + // e([f(α) - f(a)]G₁, G₂).e([-H(α)]G₁, [α-a]G₂) ==? 1 + resPairing, _ := sw_bls12377.Pair( + api, + []sw_bls12377.G1Affine{fminusfaG1, negH}, + []sw_bls12377.G2Affine{srs.G2[0], alphaMinusaG2}, + ) + + var one fields_bls12377.E12 + one.SetOne() + resPairing.AssertIsEqual(api, one) + +} diff --git a/std/kzg_bls12377/verifier_test.go b/std/kzg_bls12377/verifier_test.go new file mode 100644 index 0000000000..1c6013c7f2 --- /dev/null +++ b/std/kzg_bls12377/verifier_test.go @@ -0,0 +1,114 @@ +/* +Copyright © 2020 ConsenSys + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kzg_bls12377 + +import ( + "math/big" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + kzg_bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/kzg" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/test" +) + +type verifierCircuit struct { + VerifKey VK + Proof OpeningProof + Com Digest + S Point +} + +func (circuit *verifierCircuit) Define(api frontend.API) error { + + // create the verifier cs + Verify(api, circuit.Com, circuit.Proof, circuit.S, circuit.VerifKey) + + return nil +} + +func TestVerifier(t *testing.T) { + + var circuit, witness verifierCircuit + + // create a polynomial + f := randomPolynomial(60) + + // commit the polynomial + digest, err := kzg_bls12377.Commit(f, testSRS) + if err != nil { + t.Fatal(err) + } + + // compute opening proof at a random point + var point fr.Element + point.SetString("4321") + proof, err := kzg_bls12377.Open(f, point, testSRS) + if err != nil { + t.Fatal(err) + } + + // verify the claimed valued + expected := eval(f, point) + if !proof.ClaimedValue.Equal(&expected) { + t.Fatal("inconsistant claimed value") + } + + witness.Proof.H.Assign(&proof.H) + witness.Proof.ClaimedValue = proof.ClaimedValue + witness.Com.Assign(&digest) + witness.S = point + witness.VerifKey.G1.Assign(&testSRS.G1[0]) + witness.VerifKey.G2[0].Assign(&testSRS.G2[0]) + witness.VerifKey.G2[1].Assign(&testSRS.G2[1]) + + // cs values + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_761)) + +} + +// utils +// testSRS re-used accross tests of the KZG scheme +var testSRS *kzg_bls12377.SRS + +func init() { + const srsSize = 230 + testSRS, _ = kzg_bls12377.NewSRS(ecc.NextPowerOfTwo(srsSize), new(big.Int).SetInt64(42)) +} + +// samples a random polynomial +func randomPolynomial(size int) []fr.Element { + f := make([]fr.Element, size) + for i := 0; i < size; i++ { + f[i].SetRandom() + } + return f +} + +// eval returns p(point) where p is interpreted as a polynomial +// ∑_{i= 0; i-- { + res.Mul(&res, &point).Add(&res, &p[i]) + } + return res +} From af8ae923465c317f4461022bcb22b6e7d1a62c4e Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 14 Apr 2022 15:44:33 +0200 Subject: [PATCH 04/11] feat(std): KZG verifier gadget bls24-315 (with static witness) --- std/kzg_bls12377/verifier.go | 7 +-- std/kzg_bls12377/verifier_test.go | 89 ++++++++++------------------- std/kzg_bls24315/verifier.go | 77 +++++++++++++++++++++++++ std/kzg_bls24315/verifier_test.go | 93 +++++++++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 65 deletions(-) create mode 100644 std/kzg_bls24315/verifier.go create mode 100644 std/kzg_bls24315/verifier_test.go diff --git a/std/kzg_bls12377/verifier.go b/std/kzg_bls12377/verifier.go index 9f749ea7b4..58e4ceb085 100644 --- a/std/kzg_bls12377/verifier.go +++ b/std/kzg_bls12377/verifier.go @@ -26,9 +26,6 @@ import ( // Digest commitment of a polynomial. type Digest = sw_bls12377.G1Affine -// Pointc evaluation point. -type Point = frontend.Variable - // VK verification key (G2 part of SRS) type VK struct { G1 sw_bls12377.G1Affine // G₁ @@ -45,7 +42,7 @@ type OpeningProof struct { } // Verify verifies a KZG opening proof at a single point -func Verify(api frontend.API, commitment Digest, proof OpeningProof, point Point, srs VK) { +func Verify(api frontend.API, commitment Digest, proof OpeningProof, point frontend.Variable, srs VK) { // [f(a)]G₁ var claimedValueG1Aff sw_bls12377.G1Affine @@ -62,7 +59,7 @@ func Verify(api frontend.API, commitment Digest, proof OpeningProof, point Point // [α-a]G₂ var alphaMinusaG2 sw_bls12377.G2Affine - alphaMinusaG2.ScalarMul(api, srs.G2[0], &point). + alphaMinusaG2.ScalarMul(api, srs.G2[0], point). Neg(api, alphaMinusaG2). AddAssign(api, srs.G2[1]) diff --git a/std/kzg_bls12377/verifier_test.go b/std/kzg_bls12377/verifier_test.go index 1c6013c7f2..caef623654 100644 --- a/std/kzg_bls12377/verifier_test.go +++ b/std/kzg_bls12377/verifier_test.go @@ -17,13 +17,11 @@ limitations under the License. package kzg_bls12377 import ( - "math/big" "testing" "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - kzg_bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/kzg" "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/test" ) @@ -31,7 +29,7 @@ type verifierCircuit struct { VerifKey VK Proof OpeningProof Com Digest - S Point + S frontend.Variable } func (circuit *verifierCircuit) Define(api frontend.API) error { @@ -46,36 +44,25 @@ func TestVerifier(t *testing.T) { var circuit, witness verifierCircuit - // create a polynomial - f := randomPolynomial(60) - - // commit the polynomial - digest, err := kzg_bls12377.Commit(f, testSRS) - if err != nil { - t.Fatal(err) - } - - // compute opening proof at a random point - var point fr.Element - point.SetString("4321") - proof, err := kzg_bls12377.Open(f, point, testSRS) - if err != nil { - t.Fatal(err) - } - - // verify the claimed valued - expected := eval(f, point) - if !proof.ClaimedValue.Equal(&expected) { - t.Fatal("inconsistant claimed value") - } - - witness.Proof.H.Assign(&proof.H) - witness.Proof.ClaimedValue = proof.ClaimedValue - witness.Com.Assign(&digest) - witness.S = point - witness.VerifKey.G1.Assign(&testSRS.G1[0]) - witness.VerifKey.G2[0].Assign(&testSRS.G2[0]) - witness.VerifKey.G2[1].Assign(&testSRS.G2[1]) + // static witness + witness.Com.X = "145429059828629443506099208441019164249918805265766585069511130101715300037889375544644493566733059056337445574142" + witness.Com.Y = "7748648670212409231552941907406345586179813940682493172078407968203200311849395869785335293628955566021478572791" + + witness.Proof.H.X = "142546216630759857020142552653688574597188212934274836451979072858880695115513802425442488457664742720974070355453" + witness.Proof.H.Y = "51742728231756961100409716107519203689800988928890924645730616869717553365749083029986151526811552917856555146906" + + witness.Proof.ClaimedValue = "7211341386127354417397285211336133449231039596179023429378585109196698597268" + witness.S = "4321" + witness.VerifKey.G1.X = "81937999373150964239938255573465948239988671502647976594219695644855304257327692006745978603320413799295628339695" + witness.VerifKey.G1.Y = "241266749859715473739788878240585681733927191168601896383759122102112907357779751001206799952863815012735208165030" + witness.VerifKey.G2[0].X.A0 = "233578398248691099356572568220835526895379068987715365179118596935057653620464273615301663571204657964920925606294" + witness.VerifKey.G2[0].X.A1 = "140913150380207355837477652521042157274541796891053068589147167627541651775299824604154852141315666357241556069118" + witness.VerifKey.G2[0].Y.A0 = "63160294768292073209381361943935198908131692476676907196754037919244929611450776219210369229519898517858833747423" + witness.VerifKey.G2[0].Y.A1 = "149157405641012693445398062341192467754805999074082136895788947234480009303640899064710353187729182149407503257491" + witness.VerifKey.G2[1].X.A0 = "123747009012703414871739433259892117784672459657097139998749475279099125411579029748101735145753812822027512995199" + witness.VerifKey.G2[1].X.A1 = "62735868045337090199933301723513128455431585854943778977190757050206710789139082141526891028732261537358701287808" + witness.VerifKey.G2[1].Y.A0 = "212548833831227473592895134150456464278558858278752454560645447355770538424096804613692943525553353783189853308160" + witness.VerifKey.G2[1].Y.A1 = "123051654588413991319606911619099872563646143639520520553172600449178549047186983142138529976243874838154671706124" // cs values assert := test.NewAssert(t) @@ -83,32 +70,14 @@ func TestVerifier(t *testing.T) { } -// utils -// testSRS re-used accross tests of the KZG scheme -var testSRS *kzg_bls12377.SRS - -func init() { - const srsSize = 230 - testSRS, _ = kzg_bls12377.NewSRS(ecc.NextPowerOfTwo(srsSize), new(big.Int).SetInt64(42)) -} - -// samples a random polynomial -func randomPolynomial(size int) []fr.Element { - f := make([]fr.Element, size) - for i := 0; i < size; i++ { - f[i].SetRandom() - } - return f -} +// bench +var ccsBench frontend.CompiledConstraintSystem -// eval returns p(point) where p is interpreted as a polynomial -// ∑_{i= 0; i-- { - res.Mul(&res, &point).Add(&res, &p[i]) +func BenchmarkVerifyKZG(b *testing.B) { + var c verifierCircuit + b.ResetTimer() + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_761, r1cs.NewBuilder, &c) } - return res + b.Log("groth16", ccsBench.GetNbConstraints()) } diff --git a/std/kzg_bls24315/verifier.go b/std/kzg_bls24315/verifier.go new file mode 100644 index 0000000000..4748a1e9e3 --- /dev/null +++ b/std/kzg_bls24315/verifier.go @@ -0,0 +1,77 @@ +/* +Copyright © 2020 ConsenSys + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package kzg_bls24315 provides a ZKP-circuit function to verify BLS24_315 KZG inside a BW6_633 circuit. +package kzg_bls24315 + +import ( + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/algebra/fields_bls24315" + "github.com/consensys/gnark/std/algebra/sw_bls24315" +) + +// Digest commitment of a polynomial. +type Digest = sw_bls24315.G1Affine + +// VK verification key (G2 part of SRS) +type VK struct { + G1 sw_bls24315.G1Affine // G₁ + G2 [2]sw_bls24315.G2Affine // [G₂, [α]G₂] +} + +// OpeningProof KZG proof for opening at a single point. +type OpeningProof struct { + // H quotient polynomial (f - f(z))/(x-z) + H sw_bls24315.G1Affine + + // ClaimedValue purported value + ClaimedValue frontend.Variable +} + +// Verify verifies a KZG opening proof at a single point +func Verify(api frontend.API, commitment Digest, proof OpeningProof, point frontend.Variable, srs VK) { + + // [f(a)]G₁ + var claimedValueG1Aff sw_bls24315.G1Affine + claimedValueG1Aff.ScalarMul(api, srs.G1, proof.ClaimedValue) + + // [f(α) - f(a)]G₁ + var fminusfaG1 sw_bls24315.G1Affine + fminusfaG1.Neg(api, claimedValueG1Aff) + fminusfaG1.AddAssign(api, commitment) + + // [-H(α)]G₁ + var negH sw_bls24315.G1Affine + negH.Neg(api, proof.H) + + // [α-a]G₂ + var alphaMinusaG2 sw_bls24315.G2Affine + alphaMinusaG2.ScalarMul(api, srs.G2[0], point). + Neg(api, alphaMinusaG2). + AddAssign(api, srs.G2[1]) + + // e([f(α) - f(a)]G₁, G₂).e([-H(α)]G₁, [α-a]G₂) ==? 1 + resPairing, _ := sw_bls24315.Pair( + api, + []sw_bls24315.G1Affine{fminusfaG1, negH}, + []sw_bls24315.G2Affine{srs.G2[0], alphaMinusaG2}, + ) + + var one fields_bls24315.E24 + one.SetOne() + resPairing.AssertIsEqual(api, one) + +} diff --git a/std/kzg_bls24315/verifier_test.go b/std/kzg_bls24315/verifier_test.go new file mode 100644 index 0000000000..07872357ac --- /dev/null +++ b/std/kzg_bls24315/verifier_test.go @@ -0,0 +1,93 @@ +/* +Copyright © 2020 ConsenSys + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kzg_bls24315 + +import ( + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/test" +) + +type verifierCircuit struct { + VerifKey VK + Proof OpeningProof + Com Digest + S frontend.Variable +} + +func (circuit *verifierCircuit) Define(api frontend.API) error { + + // create the verifier cs + Verify(api, circuit.Com, circuit.Proof, circuit.S, circuit.VerifKey) + + return nil +} + +func TestVerifier(t *testing.T) { + + var circuit, witness verifierCircuit + + // static witness + witness.Com.X = "35386189147256460787905142428026982693834102687669771641361389281756222188309133371287736011496" + witness.Com.Y = "27110917293370507654960132415484655252529074592699870521959828295621560278434020539890708345149" + + witness.Proof.H.X = "237024382315576057940476197527646514934539639879200035206834755549615436908306104502862432730" + witness.Proof.H.Y = "24965199876048664783103146001620612576865473814618781613850899751573655382828001319566087837055" + + witness.Proof.ClaimedValue = "10347231107172233075459792371577505115223937655290126532055162077965558980163" + witness.S = "4321" + witness.VerifKey.G1.X = "34223510504517033132712852754388476272837911830964394866541204856091481856889569724484362330263" + witness.VerifKey.G1.Y = "24215295174889464585413596429561903295150472552154479431771837786124301185073987899223459122783" + + witness.VerifKey.G2[0].X.B0.A0 = "24614737899199071964341749845083777103809664018538138889239909664991294445469052467064654073699" + witness.VerifKey.G2[0].X.B0.A1 = "17049297748993841127032249156255993089778266476087413538366212660716380683149731996715975282972" + witness.VerifKey.G2[0].X.B1.A0 = "11950668649125904104557740112865942804623051114821811669564995102755430514441092495782202668342" + witness.VerifKey.G2[0].X.B1.A1 = "3603055379462539802413979855826194299714805833759849528529386570240639115620788686893505938793" + witness.VerifKey.G2[0].Y.B0.A0 = "31740092748246070457677943092194030978994615503726570180895475408200863271773078192139722193079" + witness.VerifKey.G2[0].Y.B0.A1 = "30261413948955264769241509843031153941332801192447678605718183215275065425758214858190865971597" + witness.VerifKey.G2[0].Y.B1.A0 = "14195825602561496219090410113749222574308144851497375443809100117082380611212823440674391088885" + witness.VerifKey.G2[0].Y.B1.A1 = "2391152940984805871402135750194189812615420966694899795235607856168224901793030297133493038211" + + witness.VerifKey.G2[1].X.B0.A0 = "32770621494303675347306576037414743205466109457179006780112295339591667866879607994893522201077" + witness.VerifKey.G2[1].X.B0.A1 = "26234307989293079589757302086025391411007046129273969450459586440325937793578626756390716239607" + witness.VerifKey.G2[1].X.B1.A0 = "12885920290770633767625725164719407698814564441475093302178981579150678620682561869830892647708" + witness.VerifKey.G2[1].X.B1.A1 = "27040439362534196619980827988108357486576687369306457236523666215277529311368226649309430321857" + witness.VerifKey.G2[1].Y.B0.A0 = "37891043881493427277825396947634598161159358734636209357686614942355583145029806490020871408089" + witness.VerifKey.G2[1].Y.B0.A1 = "24578978782210992183339450660991675754164024355249488228592063724386132418314115963198249364981" + witness.VerifKey.G2[1].Y.B1.A0 = "2561567173101794713286533032340948733218695754942152779206184132595475750392464489574163449132" + witness.VerifKey.G2[1].Y.B1.A1 = "22410372563820522534342381636929948962663337994936763276489712608156477267640544532767398832260" + + // cs values + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) + +} + +// bench +var ccsBench frontend.CompiledConstraintSystem + +func BenchmarkVerifyKZG(b *testing.B) { + var c verifierCircuit + b.ResetTimer() + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_633, r1cs.NewBuilder, &c) + } + b.Log("groth16", ccsBench.GetNbConstraints()) +} From bf552442c5d3d8b9e28d5e513f1adff5a59287cb Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 14 Apr 2022 19:11:32 +0200 Subject: [PATCH 05/11] style(std/bls24): rename DecomposeScalar hint (consistent with bls12) --- std/algebra/sw_bls24315/g1.go | 6 +++--- std/hints.go | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/std/algebra/sw_bls24315/g1.go b/std/algebra/sw_bls24315/g1.go index 8cf4c3cc6c..0284970101 100644 --- a/std/algebra/sw_bls24315/g1.go +++ b/std/algebra/sw_bls24315/g1.go @@ -206,7 +206,7 @@ func (P *G1Affine) ScalarMul(api frontend.API, Q G1Affine, s interface{}) *G1Aff } } -var DecomposeScalar = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) error { +var DecomposeScalarG1 = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) error { cc := innerCurve(curve) sp := ecc.SplitScalar(inputs[0], cc.glvBasis) res[0].Set(&(sp[0])) @@ -228,7 +228,7 @@ var DecomposeScalar = func(curve ecc.ID, inputs []*big.Int, res []*big.Int) erro } func init() { - hint.Register(DecomposeScalar) + hint.Register(DecomposeScalarG1) } // varScalarMul sets P = [s] Q and returns P. @@ -254,7 +254,7 @@ func (P *G1Affine) varScalarMul(api frontend.API, Q G1Affine, s frontend.Variabl // the hints allow to decompose the scalar s into s1 and s2 such that // s1 + λ * s2 == s mod r, // where λ is third root of one in 𝔽_r. - sd, err := api.Compiler().NewHint(DecomposeScalar, 3, s) + sd, err := api.Compiler().NewHint(DecomposeScalarG1, 3, s) if err != nil { // err is non-nil only for invalid number of inputs panic(err) diff --git a/std/hints.go b/std/hints.go index 565fb0afce..33f2aac757 100644 --- a/std/hints.go +++ b/std/hints.go @@ -21,8 +21,10 @@ func RegisterHints() { func registerHints() { // note that importing these packages may already triggers a call to hint.Register(...) - hint.Register(sw_bls24315.DecomposeScalar) - hint.Register(sw_bls12377.DecomposeScalar) + hint.Register(sw_bls24315.DecomposeScalarG1) + hint.Register(sw_bls12377.DecomposeScalarG1) + hint.Register(sw_bls24315.DecomposeScalarG2) + hint.Register(sw_bls12377.DecomposeScalarG2) hint.Register(bits.NTrits) hint.Register(bits.NNAF) hint.Register(bits.IthBit) From 44e5c9136891aa65f1879ad0b56a60bda7748ab2 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 28 Jun 2022 13:57:04 +0100 Subject: [PATCH 06/11] fix: kzg verifier test --- std/algebra/sw_bls12377/g1.go | 2 +- std/algebra/sw_bls24315/g1.go | 2 +- std/kzg_bls12377/verifier_test.go | 2 +- std/kzg_bls24315/verifier_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/std/algebra/sw_bls12377/g1.go b/std/algebra/sw_bls12377/g1.go index 84149951a8..303315a833 100644 --- a/std/algebra/sw_bls12377/g1.go +++ b/std/algebra/sw_bls12377/g1.go @@ -206,7 +206,7 @@ func (P *G1Affine) ScalarMul(api frontend.API, Q G1Affine, s interface{}) *G1Aff } } -var DecomposeScalar = func(scalarField *big.Int, inputs []*big.Int, res []*big.Int) error { +var DecomposeScalarG1 = func(scalarField *big.Int, inputs []*big.Int, res []*big.Int) error { cc := getInnerCurveConfig(scalarField) sp := ecc.SplitScalar(inputs[0], cc.glvBasis) res[0].Set(&(sp[0])) diff --git a/std/algebra/sw_bls24315/g1.go b/std/algebra/sw_bls24315/g1.go index 9e3af21fc6..1e8b12fd0e 100644 --- a/std/algebra/sw_bls24315/g1.go +++ b/std/algebra/sw_bls24315/g1.go @@ -206,7 +206,7 @@ func (P *G1Affine) ScalarMul(api frontend.API, Q G1Affine, s interface{}) *G1Aff } } -var DecomposeScalar = func(scalarField *big.Int, inputs []*big.Int, res []*big.Int) error { +var DecomposeScalarG1 = func(scalarField *big.Int, inputs []*big.Int, res []*big.Int) error { cc := getInnerCurveConfig(scalarField) sp := ecc.SplitScalar(inputs[0], cc.glvBasis) res[0].Set(&(sp[0])) diff --git a/std/kzg_bls12377/verifier_test.go b/std/kzg_bls12377/verifier_test.go index caef623654..3c1d92684d 100644 --- a/std/kzg_bls12377/verifier_test.go +++ b/std/kzg_bls12377/verifier_test.go @@ -77,7 +77,7 @@ func BenchmarkVerifyKZG(b *testing.B) { var c verifierCircuit b.ResetTimer() for i := 0; i < b.N; i++ { - ccsBench, _ = frontend.Compile(ecc.BW6_761, r1cs.NewBuilder, &c) + ccsBench, _ = frontend.Compile(ecc.BW6_761.ScalarField(), r1cs.NewBuilder, &c) } b.Log("groth16", ccsBench.GetNbConstraints()) } diff --git a/std/kzg_bls24315/verifier_test.go b/std/kzg_bls24315/verifier_test.go index 07872357ac..ed72a39b75 100644 --- a/std/kzg_bls24315/verifier_test.go +++ b/std/kzg_bls24315/verifier_test.go @@ -87,7 +87,7 @@ func BenchmarkVerifyKZG(b *testing.B) { var c verifierCircuit b.ResetTimer() for i := 0; i < b.N; i++ { - ccsBench, _ = frontend.Compile(ecc.BW6_633, r1cs.NewBuilder, &c) + ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), r1cs.NewBuilder, &c) } b.Log("groth16", ccsBench.GetNbConstraints()) } From 899b27c6604e097933c1a3776fd11612f3f06a65 Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Tue, 28 Jun 2022 18:08:22 +0100 Subject: [PATCH 07/11] test: bench kzg verifier with plonk --- std/algebra/sw_bls12377/pairing_test.go | 3 +-- std/algebra/sw_bls24315/pairing_test.go | 5 ++--- std/kzg_bls12377/verifier_test.go | 15 ++++++++++++--- std/kzg_bls24315/verifier_test.go | 15 ++++++++++++--- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/std/algebra/sw_bls12377/pairing_test.go b/std/algebra/sw_bls12377/pairing_test.go index 11edab03aa..b16e3617b3 100644 --- a/std/algebra/sw_bls12377/pairing_test.go +++ b/std/algebra/sw_bls12377/pairing_test.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" - "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/algebra/fields_bls12377" @@ -54,7 +53,7 @@ func TestFinalExp(t *testing.T) { circuit.R = pairingRes assert := test.NewAssert(t) - assert.SolvingSucceeded(&circuit, &witness, test.WithBackends(backend.PLONK), test.WithCurves(ecc.BW6_761)) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_761)) } type pairingBLS377 struct { diff --git a/std/algebra/sw_bls24315/pairing_test.go b/std/algebra/sw_bls24315/pairing_test.go index 715f6a3cc5..f8d78b2384 100644 --- a/std/algebra/sw_bls24315/pairing_test.go +++ b/std/algebra/sw_bls24315/pairing_test.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc" bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" - "github.com/consensys/gnark/backend" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/algebra/fields_bls24315" @@ -87,7 +86,7 @@ func TestPairingBLS24315(t *testing.T) { witness.Q.Assign(&Q) assert := test.NewAssert(t) - assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633), test.WithBackends(backend.GROTH16)) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) } @@ -124,7 +123,7 @@ func TestTriplePairingBLS24315(t *testing.T) { witness.Q3.Assign(&Q[2]) assert := test.NewAssert(t) - assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633), test.WithBackends(backend.GROTH16)) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) } diff --git a/std/kzg_bls12377/verifier_test.go b/std/kzg_bls12377/verifier_test.go index 3c1d92684d..164ce1a757 100644 --- a/std/kzg_bls12377/verifier_test.go +++ b/std/kzg_bls12377/verifier_test.go @@ -22,6 +22,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/test" ) @@ -76,8 +77,16 @@ var ccsBench frontend.CompiledConstraintSystem func BenchmarkVerifyKZG(b *testing.B) { var c verifierCircuit b.ResetTimer() - for i := 0; i < b.N; i++ { - ccsBench, _ = frontend.Compile(ecc.BW6_761.ScalarField(), r1cs.NewBuilder, &c) - } + b.Run("groth16", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_761.ScalarField(), r1cs.NewBuilder, &c) + } + }) b.Log("groth16", ccsBench.GetNbConstraints()) + b.Run("plonk", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_761.ScalarField(), scs.NewBuilder, &c) + } + }) + b.Log("plonk", ccsBench.GetNbConstraints()) } diff --git a/std/kzg_bls24315/verifier_test.go b/std/kzg_bls24315/verifier_test.go index ed72a39b75..4ecdd2267c 100644 --- a/std/kzg_bls24315/verifier_test.go +++ b/std/kzg_bls24315/verifier_test.go @@ -22,6 +22,7 @@ import ( "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" "github.com/consensys/gnark/test" ) @@ -86,8 +87,16 @@ var ccsBench frontend.CompiledConstraintSystem func BenchmarkVerifyKZG(b *testing.B) { var c verifierCircuit b.ResetTimer() - for i := 0; i < b.N; i++ { - ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), r1cs.NewBuilder, &c) - } + b.Run("groth16", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), r1cs.NewBuilder, &c) + } + }) b.Log("groth16", ccsBench.GetNbConstraints()) + b.Run("plonk", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), scs.NewBuilder, &c) + } + }) + b.Log("plonk", ccsBench.GetNbConstraints()) } From d8066d7530b40d820671f2cb97d78b17135b7599 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 30 Jun 2022 14:58:38 +0200 Subject: [PATCH 08/11] style: moved kzg in circuit in commitment/ folder --- .../kzg_bls12377}/kzg_bls12377/verifier.go | 0 .../kzg_bls12377/verifier_test.go | 47 +++++++++++++++++++ .../kzg_bls24315}/kzg_bls24315/verifier.go | 0 .../kzg_bls24315/verifier_test.go | 0 4 files changed, 47 insertions(+) rename std/{ => commitments/kzg_bls12377}/kzg_bls12377/verifier.go (100%) rename std/{ => commitments/kzg_bls12377}/kzg_bls12377/verifier_test.go (79%) rename std/{ => commitments/kzg_bls24315}/kzg_bls24315/verifier.go (100%) rename std/{ => commitments/kzg_bls24315}/kzg_bls24315/verifier_test.go (100%) diff --git a/std/kzg_bls12377/verifier.go b/std/commitments/kzg_bls12377/kzg_bls12377/verifier.go similarity index 100% rename from std/kzg_bls12377/verifier.go rename to std/commitments/kzg_bls12377/kzg_bls12377/verifier.go diff --git a/std/kzg_bls12377/verifier_test.go b/std/commitments/kzg_bls12377/kzg_bls12377/verifier_test.go similarity index 79% rename from std/kzg_bls12377/verifier_test.go rename to std/commitments/kzg_bls12377/kzg_bls12377/verifier_test.go index 164ce1a757..c35a0d455b 100644 --- a/std/kzg_bls12377/verifier_test.go +++ b/std/commitments/kzg_bls12377/kzg_bls12377/verifier_test.go @@ -17,9 +17,12 @@ limitations under the License. package kzg_bls12377 import ( + "crypto/rand" "testing" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" + "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/kzg" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/frontend/cs/scs" @@ -41,6 +44,50 @@ func (circuit *verifierCircuit) Define(api frontend.API) error { return nil } +//------------------------------------------------------- +// proof generated using gnark-crypto + +func TestVerifierVariable(t *testing.T) { + + assert := test.NewAssert(t) + + // sizes of polynomials, kzg + const kzgSize = 128 + const polynomialSize = 100 + + // trusted setup + alpha, err := rand.Int(rand.Reader, ecc.BLS12_377.ScalarField()) + assert.NoError(err) + srs, err := kzg.NewSRS(kzgSize, alpha) + assert.NoError(err) + + // random polynomial + f := make([]fr.Element, polynomialSize) + for i := 0; i < 60; i++ { + f[i].SetRandom() + } + + // commit to the polynomial + com, err := kzg.Commit(f, srs) + assert.NoError(err) + + // create opening proof + var point fr.Element + point.SetRandom() + proof, err := kzg.Open(f, point, srs) + assert.NoError(err) + + // check that the proof is correct + err = kzg.Verify(&com, &proof, point, srs) + if err != nil { + t.Fatal(err) + } + +} + +//------------------------------------------------------- +// harcoded values + func TestVerifier(t *testing.T) { var circuit, witness verifierCircuit diff --git a/std/kzg_bls24315/verifier.go b/std/commitments/kzg_bls24315/kzg_bls24315/verifier.go similarity index 100% rename from std/kzg_bls24315/verifier.go rename to std/commitments/kzg_bls24315/kzg_bls24315/verifier.go diff --git a/std/kzg_bls24315/verifier_test.go b/std/commitments/kzg_bls24315/kzg_bls24315/verifier_test.go similarity index 100% rename from std/kzg_bls24315/verifier_test.go rename to std/commitments/kzg_bls24315/kzg_bls24315/verifier_test.go From 32e0f42be27f53dde74fa66e9875d076d8cd97a8 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 30 Jun 2022 15:11:10 +0200 Subject: [PATCH 09/11] feat: addition of dynamic test kzg bls12377 --- .../{kzg_bls12377 => }/verifier.go | 0 .../{kzg_bls12377 => }/verifier_test.go | 32 +++++- .../kzg_bls24315/kzg_bls24315/verifier.go | 77 ------------- .../kzg_bls24315/verifier_test.go | 102 ------------------ 4 files changed, 31 insertions(+), 180 deletions(-) rename std/commitments/kzg_bls12377/{kzg_bls12377 => }/verifier.go (100%) rename std/commitments/kzg_bls12377/{kzg_bls12377 => }/verifier_test.go (82%) delete mode 100644 std/commitments/kzg_bls24315/kzg_bls24315/verifier.go delete mode 100644 std/commitments/kzg_bls24315/kzg_bls24315/verifier_test.go diff --git a/std/commitments/kzg_bls12377/kzg_bls12377/verifier.go b/std/commitments/kzg_bls12377/verifier.go similarity index 100% rename from std/commitments/kzg_bls12377/kzg_bls12377/verifier.go rename to std/commitments/kzg_bls12377/verifier.go diff --git a/std/commitments/kzg_bls12377/kzg_bls12377/verifier_test.go b/std/commitments/kzg_bls12377/verifier_test.go similarity index 82% rename from std/commitments/kzg_bls12377/kzg_bls12377/verifier_test.go rename to std/commitments/kzg_bls12377/verifier_test.go index c35a0d455b..c28eb5328d 100644 --- a/std/commitments/kzg_bls12377/kzg_bls12377/verifier_test.go +++ b/std/commitments/kzg_bls12377/verifier_test.go @@ -47,7 +47,7 @@ func (circuit *verifierCircuit) Define(api frontend.API) error { //------------------------------------------------------- // proof generated using gnark-crypto -func TestVerifierVariable(t *testing.T) { +func TestVerifierDynamic(t *testing.T) { assert := test.NewAssert(t) @@ -83,6 +83,36 @@ func TestVerifierVariable(t *testing.T) { t.Fatal(err) } + // verify the proof in circuit + var witness verifierCircuit + + // populate the witness + witness.Com.X = com.X.String() + witness.Com.Y = com.Y.String() + + witness.Proof.H.X = proof.H.X.String() + witness.Proof.H.Y = proof.H.Y.String() + + witness.Proof.ClaimedValue = proof.ClaimedValue.String() + + witness.S = point.String() + + witness.VerifKey.G1.X = srs.G1[0].X.String() + witness.VerifKey.G1.Y = srs.G1[0].Y.String() + + witness.VerifKey.G2[0].X.A0 = srs.G2[0].X.A0.String() + witness.VerifKey.G2[0].X.A1 = srs.G2[0].X.A1.String() + witness.VerifKey.G2[0].Y.A0 = srs.G2[0].Y.A0.String() + witness.VerifKey.G2[0].Y.A1 = srs.G2[0].Y.A1.String() + witness.VerifKey.G2[1].X.A0 = srs.G2[1].X.A0.String() + witness.VerifKey.G2[1].X.A1 = srs.G2[1].X.A1.String() + witness.VerifKey.G2[1].Y.A0 = srs.G2[1].Y.A0.String() + witness.VerifKey.G2[1].Y.A1 = srs.G2[1].Y.A1.String() + + // check if the circuit is solved + var circuit verifierCircuit + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_761)) + } //------------------------------------------------------- diff --git a/std/commitments/kzg_bls24315/kzg_bls24315/verifier.go b/std/commitments/kzg_bls24315/kzg_bls24315/verifier.go deleted file mode 100644 index 4748a1e9e3..0000000000 --- a/std/commitments/kzg_bls24315/kzg_bls24315/verifier.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright © 2020 ConsenSys - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package kzg_bls24315 provides a ZKP-circuit function to verify BLS24_315 KZG inside a BW6_633 circuit. -package kzg_bls24315 - -import ( - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/algebra/fields_bls24315" - "github.com/consensys/gnark/std/algebra/sw_bls24315" -) - -// Digest commitment of a polynomial. -type Digest = sw_bls24315.G1Affine - -// VK verification key (G2 part of SRS) -type VK struct { - G1 sw_bls24315.G1Affine // G₁ - G2 [2]sw_bls24315.G2Affine // [G₂, [α]G₂] -} - -// OpeningProof KZG proof for opening at a single point. -type OpeningProof struct { - // H quotient polynomial (f - f(z))/(x-z) - H sw_bls24315.G1Affine - - // ClaimedValue purported value - ClaimedValue frontend.Variable -} - -// Verify verifies a KZG opening proof at a single point -func Verify(api frontend.API, commitment Digest, proof OpeningProof, point frontend.Variable, srs VK) { - - // [f(a)]G₁ - var claimedValueG1Aff sw_bls24315.G1Affine - claimedValueG1Aff.ScalarMul(api, srs.G1, proof.ClaimedValue) - - // [f(α) - f(a)]G₁ - var fminusfaG1 sw_bls24315.G1Affine - fminusfaG1.Neg(api, claimedValueG1Aff) - fminusfaG1.AddAssign(api, commitment) - - // [-H(α)]G₁ - var negH sw_bls24315.G1Affine - negH.Neg(api, proof.H) - - // [α-a]G₂ - var alphaMinusaG2 sw_bls24315.G2Affine - alphaMinusaG2.ScalarMul(api, srs.G2[0], point). - Neg(api, alphaMinusaG2). - AddAssign(api, srs.G2[1]) - - // e([f(α) - f(a)]G₁, G₂).e([-H(α)]G₁, [α-a]G₂) ==? 1 - resPairing, _ := sw_bls24315.Pair( - api, - []sw_bls24315.G1Affine{fminusfaG1, negH}, - []sw_bls24315.G2Affine{srs.G2[0], alphaMinusaG2}, - ) - - var one fields_bls24315.E24 - one.SetOne() - resPairing.AssertIsEqual(api, one) - -} diff --git a/std/commitments/kzg_bls24315/kzg_bls24315/verifier_test.go b/std/commitments/kzg_bls24315/kzg_bls24315/verifier_test.go deleted file mode 100644 index 4ecdd2267c..0000000000 --- a/std/commitments/kzg_bls24315/kzg_bls24315/verifier_test.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Copyright © 2020 ConsenSys - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kzg_bls24315 - -import ( - "testing" - - "github.com/consensys/gnark-crypto/ecc" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend/cs/r1cs" - "github.com/consensys/gnark/frontend/cs/scs" - "github.com/consensys/gnark/test" -) - -type verifierCircuit struct { - VerifKey VK - Proof OpeningProof - Com Digest - S frontend.Variable -} - -func (circuit *verifierCircuit) Define(api frontend.API) error { - - // create the verifier cs - Verify(api, circuit.Com, circuit.Proof, circuit.S, circuit.VerifKey) - - return nil -} - -func TestVerifier(t *testing.T) { - - var circuit, witness verifierCircuit - - // static witness - witness.Com.X = "35386189147256460787905142428026982693834102687669771641361389281756222188309133371287736011496" - witness.Com.Y = "27110917293370507654960132415484655252529074592699870521959828295621560278434020539890708345149" - - witness.Proof.H.X = "237024382315576057940476197527646514934539639879200035206834755549615436908306104502862432730" - witness.Proof.H.Y = "24965199876048664783103146001620612576865473814618781613850899751573655382828001319566087837055" - - witness.Proof.ClaimedValue = "10347231107172233075459792371577505115223937655290126532055162077965558980163" - witness.S = "4321" - witness.VerifKey.G1.X = "34223510504517033132712852754388476272837911830964394866541204856091481856889569724484362330263" - witness.VerifKey.G1.Y = "24215295174889464585413596429561903295150472552154479431771837786124301185073987899223459122783" - - witness.VerifKey.G2[0].X.B0.A0 = "24614737899199071964341749845083777103809664018538138889239909664991294445469052467064654073699" - witness.VerifKey.G2[0].X.B0.A1 = "17049297748993841127032249156255993089778266476087413538366212660716380683149731996715975282972" - witness.VerifKey.G2[0].X.B1.A0 = "11950668649125904104557740112865942804623051114821811669564995102755430514441092495782202668342" - witness.VerifKey.G2[0].X.B1.A1 = "3603055379462539802413979855826194299714805833759849528529386570240639115620788686893505938793" - witness.VerifKey.G2[0].Y.B0.A0 = "31740092748246070457677943092194030978994615503726570180895475408200863271773078192139722193079" - witness.VerifKey.G2[0].Y.B0.A1 = "30261413948955264769241509843031153941332801192447678605718183215275065425758214858190865971597" - witness.VerifKey.G2[0].Y.B1.A0 = "14195825602561496219090410113749222574308144851497375443809100117082380611212823440674391088885" - witness.VerifKey.G2[0].Y.B1.A1 = "2391152940984805871402135750194189812615420966694899795235607856168224901793030297133493038211" - - witness.VerifKey.G2[1].X.B0.A0 = "32770621494303675347306576037414743205466109457179006780112295339591667866879607994893522201077" - witness.VerifKey.G2[1].X.B0.A1 = "26234307989293079589757302086025391411007046129273969450459586440325937793578626756390716239607" - witness.VerifKey.G2[1].X.B1.A0 = "12885920290770633767625725164719407698814564441475093302178981579150678620682561869830892647708" - witness.VerifKey.G2[1].X.B1.A1 = "27040439362534196619980827988108357486576687369306457236523666215277529311368226649309430321857" - witness.VerifKey.G2[1].Y.B0.A0 = "37891043881493427277825396947634598161159358734636209357686614942355583145029806490020871408089" - witness.VerifKey.G2[1].Y.B0.A1 = "24578978782210992183339450660991675754164024355249488228592063724386132418314115963198249364981" - witness.VerifKey.G2[1].Y.B1.A0 = "2561567173101794713286533032340948733218695754942152779206184132595475750392464489574163449132" - witness.VerifKey.G2[1].Y.B1.A1 = "22410372563820522534342381636929948962663337994936763276489712608156477267640544532767398832260" - - // cs values - assert := test.NewAssert(t) - assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) - -} - -// bench -var ccsBench frontend.CompiledConstraintSystem - -func BenchmarkVerifyKZG(b *testing.B) { - var c verifierCircuit - b.ResetTimer() - b.Run("groth16", func(b *testing.B) { - for i := 0; i < b.N; i++ { - ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), r1cs.NewBuilder, &c) - } - }) - b.Log("groth16", ccsBench.GetNbConstraints()) - b.Run("plonk", func(b *testing.B) { - for i := 0; i < b.N; i++ { - ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), scs.NewBuilder, &c) - } - }) - b.Log("plonk", ccsBench.GetNbConstraints()) -} From ad6b1295044ea770ebed62082cf5a46735600c8c Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 30 Jun 2022 15:32:51 +0200 Subject: [PATCH 10/11] feat: addition of dynamic test for kzg bls24315 --- std/commitments/kzg_bls24315/verifier.go | 77 +++++++ std/commitments/kzg_bls24315/verifier_test.go | 188 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 std/commitments/kzg_bls24315/verifier.go create mode 100644 std/commitments/kzg_bls24315/verifier_test.go diff --git a/std/commitments/kzg_bls24315/verifier.go b/std/commitments/kzg_bls24315/verifier.go new file mode 100644 index 0000000000..4748a1e9e3 --- /dev/null +++ b/std/commitments/kzg_bls24315/verifier.go @@ -0,0 +1,77 @@ +/* +Copyright © 2020 ConsenSys + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package kzg_bls24315 provides a ZKP-circuit function to verify BLS24_315 KZG inside a BW6_633 circuit. +package kzg_bls24315 + +import ( + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/algebra/fields_bls24315" + "github.com/consensys/gnark/std/algebra/sw_bls24315" +) + +// Digest commitment of a polynomial. +type Digest = sw_bls24315.G1Affine + +// VK verification key (G2 part of SRS) +type VK struct { + G1 sw_bls24315.G1Affine // G₁ + G2 [2]sw_bls24315.G2Affine // [G₂, [α]G₂] +} + +// OpeningProof KZG proof for opening at a single point. +type OpeningProof struct { + // H quotient polynomial (f - f(z))/(x-z) + H sw_bls24315.G1Affine + + // ClaimedValue purported value + ClaimedValue frontend.Variable +} + +// Verify verifies a KZG opening proof at a single point +func Verify(api frontend.API, commitment Digest, proof OpeningProof, point frontend.Variable, srs VK) { + + // [f(a)]G₁ + var claimedValueG1Aff sw_bls24315.G1Affine + claimedValueG1Aff.ScalarMul(api, srs.G1, proof.ClaimedValue) + + // [f(α) - f(a)]G₁ + var fminusfaG1 sw_bls24315.G1Affine + fminusfaG1.Neg(api, claimedValueG1Aff) + fminusfaG1.AddAssign(api, commitment) + + // [-H(α)]G₁ + var negH sw_bls24315.G1Affine + negH.Neg(api, proof.H) + + // [α-a]G₂ + var alphaMinusaG2 sw_bls24315.G2Affine + alphaMinusaG2.ScalarMul(api, srs.G2[0], point). + Neg(api, alphaMinusaG2). + AddAssign(api, srs.G2[1]) + + // e([f(α) - f(a)]G₁, G₂).e([-H(α)]G₁, [α-a]G₂) ==? 1 + resPairing, _ := sw_bls24315.Pair( + api, + []sw_bls24315.G1Affine{fminusfaG1, negH}, + []sw_bls24315.G2Affine{srs.G2[0], alphaMinusaG2}, + ) + + var one fields_bls24315.E24 + one.SetOne() + resPairing.AssertIsEqual(api, one) + +} diff --git a/std/commitments/kzg_bls24315/verifier_test.go b/std/commitments/kzg_bls24315/verifier_test.go new file mode 100644 index 0000000000..3855d1a994 --- /dev/null +++ b/std/commitments/kzg_bls24315/verifier_test.go @@ -0,0 +1,188 @@ +/* +Copyright © 2020 ConsenSys + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kzg_bls24315 + +import ( + "crypto/rand" + "testing" + + "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" + "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/kzg" + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/frontend/cs/r1cs" + "github.com/consensys/gnark/frontend/cs/scs" + "github.com/consensys/gnark/test" +) + +type verifierCircuit struct { + VerifKey VK + Proof OpeningProof + Com Digest + S frontend.Variable +} + +func (circuit *verifierCircuit) Define(api frontend.API) error { + + // create the verifier cs + Verify(api, circuit.Com, circuit.Proof, circuit.S, circuit.VerifKey) + + return nil +} + +//------------------------------------------------------- +// proof generated using gnark-crypto + +func TestVerifierDynamic(t *testing.T) { + + assert := test.NewAssert(t) + + // sizes of polynomials, kzg + const kzgSize = 128 + const polynomialSize = 100 + + // trusted setup + alpha, err := rand.Int(rand.Reader, ecc.BLS24_315.ScalarField()) + assert.NoError(err) + srs, err := kzg.NewSRS(kzgSize, alpha) + assert.NoError(err) + + // random polynomial + f := make([]fr.Element, polynomialSize) + for i := 0; i < 60; i++ { + f[i].SetRandom() + } + + // commit to the polynomial + com, err := kzg.Commit(f, srs) + assert.NoError(err) + + // create opening proof + var point fr.Element + point.SetRandom() + proof, err := kzg.Open(f, point, srs) + assert.NoError(err) + + // check that the proof is correct + err = kzg.Verify(&com, &proof, point, srs) + if err != nil { + t.Fatal(err) + } + + // verify the proof in circuit + var witness verifierCircuit + + // populate the witness + witness.Com.X = com.X.String() + witness.Com.Y = com.Y.String() + + witness.Proof.H.X = proof.H.X.String() + witness.Proof.H.Y = proof.H.Y.String() + + witness.Proof.ClaimedValue = proof.ClaimedValue.String() + + witness.S = point.String() + + witness.VerifKey.G1.X = srs.G1[0].X.String() + witness.VerifKey.G1.Y = srs.G1[0].Y.String() + + witness.VerifKey.G2[0].X.B0.A0 = srs.G2[0].X.B0.A0.String() + witness.VerifKey.G2[0].X.B0.A1 = srs.G2[0].X.B0.A1.String() + witness.VerifKey.G2[0].X.B1.A0 = srs.G2[0].X.B1.A0.String() + witness.VerifKey.G2[0].X.B1.A1 = srs.G2[0].X.B1.A1.String() + witness.VerifKey.G2[0].Y.B0.A0 = srs.G2[0].Y.B0.A0.String() + witness.VerifKey.G2[0].Y.B0.A1 = srs.G2[0].Y.B0.A1.String() + witness.VerifKey.G2[0].Y.B1.A0 = srs.G2[0].Y.B1.A0.String() + witness.VerifKey.G2[0].Y.B1.A1 = srs.G2[0].Y.B1.A1.String() + + witness.VerifKey.G2[1].X.B0.A0 = srs.G2[1].X.B0.A0.String() + witness.VerifKey.G2[1].X.B0.A1 = srs.G2[1].X.B0.A1.String() + witness.VerifKey.G2[1].X.B1.A0 = srs.G2[1].X.B1.A0.String() + witness.VerifKey.G2[1].X.B1.A1 = srs.G2[1].X.B1.A1.String() + witness.VerifKey.G2[1].Y.B0.A0 = srs.G2[1].Y.B0.A0.String() + witness.VerifKey.G2[1].Y.B0.A1 = srs.G2[1].Y.B0.A1.String() + witness.VerifKey.G2[1].Y.B1.A0 = srs.G2[1].Y.B1.A0.String() + witness.VerifKey.G2[1].Y.B1.A1 = srs.G2[1].Y.B1.A1.String() + + // check if the circuit is solved + var circuit verifierCircuit + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) + +} + +//------------------------------------------------------- +// harcoded values + +func TestVerifier(t *testing.T) { + + var circuit, witness verifierCircuit + + // static witness + witness.Com.X = "35386189147256460787905142428026982693834102687669771641361389281756222188309133371287736011496" + witness.Com.Y = "27110917293370507654960132415484655252529074592699870521959828295621560278434020539890708345149" + + witness.Proof.H.X = "237024382315576057940476197527646514934539639879200035206834755549615436908306104502862432730" + witness.Proof.H.Y = "24965199876048664783103146001620612576865473814618781613850899751573655382828001319566087837055" + + witness.Proof.ClaimedValue = "10347231107172233075459792371577505115223937655290126532055162077965558980163" + witness.S = "4321" + witness.VerifKey.G1.X = "34223510504517033132712852754388476272837911830964394866541204856091481856889569724484362330263" + witness.VerifKey.G1.Y = "24215295174889464585413596429561903295150472552154479431771837786124301185073987899223459122783" + + witness.VerifKey.G2[0].X.B0.A0 = "24614737899199071964341749845083777103809664018538138889239909664991294445469052467064654073699" + witness.VerifKey.G2[0].X.B0.A1 = "17049297748993841127032249156255993089778266476087413538366212660716380683149731996715975282972" + witness.VerifKey.G2[0].X.B1.A0 = "11950668649125904104557740112865942804623051114821811669564995102755430514441092495782202668342" + witness.VerifKey.G2[0].X.B1.A1 = "3603055379462539802413979855826194299714805833759849528529386570240639115620788686893505938793" + witness.VerifKey.G2[0].Y.B0.A0 = "31740092748246070457677943092194030978994615503726570180895475408200863271773078192139722193079" + witness.VerifKey.G2[0].Y.B0.A1 = "30261413948955264769241509843031153941332801192447678605718183215275065425758214858190865971597" + witness.VerifKey.G2[0].Y.B1.A0 = "14195825602561496219090410113749222574308144851497375443809100117082380611212823440674391088885" + witness.VerifKey.G2[0].Y.B1.A1 = "2391152940984805871402135750194189812615420966694899795235607856168224901793030297133493038211" + + witness.VerifKey.G2[1].X.B0.A0 = "32770621494303675347306576037414743205466109457179006780112295339591667866879607994893522201077" + witness.VerifKey.G2[1].X.B0.A1 = "26234307989293079589757302086025391411007046129273969450459586440325937793578626756390716239607" + witness.VerifKey.G2[1].X.B1.A0 = "12885920290770633767625725164719407698814564441475093302178981579150678620682561869830892647708" + witness.VerifKey.G2[1].X.B1.A1 = "27040439362534196619980827988108357486576687369306457236523666215277529311368226649309430321857" + witness.VerifKey.G2[1].Y.B0.A0 = "37891043881493427277825396947634598161159358734636209357686614942355583145029806490020871408089" + witness.VerifKey.G2[1].Y.B0.A1 = "24578978782210992183339450660991675754164024355249488228592063724386132418314115963198249364981" + witness.VerifKey.G2[1].Y.B1.A0 = "2561567173101794713286533032340948733218695754942152779206184132595475750392464489574163449132" + witness.VerifKey.G2[1].Y.B1.A1 = "22410372563820522534342381636929948962663337994936763276489712608156477267640544532767398832260" + + // cs values + assert := test.NewAssert(t) + assert.SolvingSucceeded(&circuit, &witness, test.WithCurves(ecc.BW6_633)) + +} + +// bench +var ccsBench frontend.CompiledConstraintSystem + +func BenchmarkVerifyKZG(b *testing.B) { + var c verifierCircuit + b.ResetTimer() + b.Run("groth16", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), r1cs.NewBuilder, &c) + } + }) + b.Log("groth16", ccsBench.GetNbConstraints()) + b.Run("plonk", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ccsBench, _ = frontend.Compile(ecc.BW6_633.ScalarField(), scs.NewBuilder, &c) + } + }) + b.Log("plonk", ccsBench.GetNbConstraints()) +} From 9729606b2bb62fa563b4e32d6f923e518b12c07d Mon Sep 17 00:00:00 2001 From: Youssef El Housni Date: Thu, 30 Jun 2022 18:01:52 +0200 Subject: [PATCH 11/11] style: comment about scalar fields of in-circuit KZG --- std/commitments/kzg_bls12377/verifier.go | 7 +++++++ std/commitments/kzg_bls24315/verifier.go | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/std/commitments/kzg_bls12377/verifier.go b/std/commitments/kzg_bls12377/verifier.go index 58e4ceb085..49f9ed5b19 100644 --- a/std/commitments/kzg_bls12377/verifier.go +++ b/std/commitments/kzg_bls12377/verifier.go @@ -43,6 +43,13 @@ type OpeningProof struct { // Verify verifies a KZG opening proof at a single point func Verify(api frontend.API, commitment Digest, proof OpeningProof, point frontend.Variable, srs VK) { + // We take the ClaimedValue and point to be frontend.Variable wich + // are elements in 𝔽_p, i.e. the BW6-761 scalar field. + // This is different from 𝔽_r, i.e. the BLS12-377 scalar field + // but r << p (p-r ≈ 377-bit) so when adding two 𝔽_r elements + // as 𝔽_p there is no reduction mod p. + // However, we should be cautious about negative elements and take + // the negative of points instead (-[f(a)]G₁ and -[a]G₂). // [f(a)]G₁ var claimedValueG1Aff sw_bls12377.G1Affine diff --git a/std/commitments/kzg_bls24315/verifier.go b/std/commitments/kzg_bls24315/verifier.go index 4748a1e9e3..4d7625e2e9 100644 --- a/std/commitments/kzg_bls24315/verifier.go +++ b/std/commitments/kzg_bls24315/verifier.go @@ -43,6 +43,13 @@ type OpeningProof struct { // Verify verifies a KZG opening proof at a single point func Verify(api frontend.API, commitment Digest, proof OpeningProof, point frontend.Variable, srs VK) { + // We take the ClaimedValue and point to be frontend.Variable wich + // are elements in 𝔽_p, i.e. the BW6-633 scalar field. + // This is different from 𝔽_r, i.e. the BLS24-315 scalar field + // but r << p (p-r ≈ 315-bit) so when adding two 𝔽_r elements + // as 𝔽_p there is no reduction mod p. + // However, we should be cautious about negative elements and take + // the negative of points instead (-[f(a)]G₁ and -[a]G₂). // [f(a)]G₁ var claimedValueG1Aff sw_bls24315.G1Affine