Skip to content

Commit

Permalink
feat: add PLONK in-circuit verifier (#880)
Browse files Browse the repository at this point in the history
* test: add recursion hash tests

* fix: accumulate MSM result

* refactor: take emulated element for additional data

* fix: handled infinity point in native multi scalar exp

* fix: use only nbBits when creating scalar

* feat: add PLONK verifier

* feat: PlaceholderVerifyingKey takes the vk as argument

* feat: f -> scalarApi

* feat: addition of computeIthLagrangeAtZeta

* feat: bsb commitments are added to pi

* refactor: PlaceholderProof takes the proof as argument

* fix: compute ith lagrange ok, hashToField failing

* fix: native short hash output size

* feat: add bw6

* docs: add package documentation

* refactor: describe error in panic

* refactor: init curve and pairing implicitly

* refactor: remove comments

* docs: add package examples

* feat: add all supported witness assignments

* test: add MSM test

* fix: remove todo panic

* feat: add option shortcuts

* fix: include hash to field in shortcut option

* feat: use only CCS for placeholder proof and verifyingkey

* chore: typos and cleanup

* docs: add KZG package documentation

---------

Co-authored-by: Thomas Piellard <thomas.piellard@consensys.net>
  • Loading branch information
ivokub and ThomasPiellard committed Nov 28, 2023
1 parent 62b52ea commit a99d198
Show file tree
Hide file tree
Showing 15 changed files with 1,849 additions and 13 deletions.
2 changes: 1 addition & 1 deletion std/algebra/emulated/sw_emulated/point.go
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ func (c *Curve[B, S]) MultiScalarMul(p []*AffinePoint[B], s []*emulated.Element[
res := c.ScalarMul(p[0], s[0])
for i := 1; i < len(p); i++ {
q := c.ScalarMul(p[i], s[i])
c.AddUnified(res, q)
res = c.AddUnified(res, q)
}
return res, nil
}
67 changes: 67 additions & 0 deletions std/algebra/emulated/sw_emulated/point_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
fr_secp "github.com/consensys/gnark-crypto/ecc/secp256k1/fr"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/math/emulated"
"github.com/consensys/gnark/std/math/emulated/emparams"
"github.com/consensys/gnark/test"
)

Expand Down Expand Up @@ -920,3 +921,69 @@ func TestJointScalarMulBase(t *testing.T) {
err := test.IsSolved(&circuit, &witness, testCurve.ScalarField())
assert.NoError(err)
}

type MultiScalarMulTest[T, S emulated.FieldParams] struct {
Points []AffinePoint[T]
Scalars []emulated.Element[S]
Res AffinePoint[T]
}

func (c *MultiScalarMulTest[T, S]) Define(api frontend.API) error {
cr, err := New[T, S](api, GetCurveParams[T]())
if err != nil {
return err
}
ps := make([]*AffinePoint[T], len(c.Points))
for i := range c.Points {
ps[i] = &c.Points[i]
}
ss := make([]*emulated.Element[S], len(c.Scalars))
for i := range c.Scalars {
ss[i] = &c.Scalars[i]
}
res, err := cr.MultiScalarMul(ps, ss)
if err != nil {
return err
}
cr.AssertIsEqual(res, &c.Res)
return nil
}

func TestMultiScalarMul(t *testing.T) {
assert := test.NewAssert(t)
nbLen := 4
P := make([]secp256k1.G1Affine, nbLen)
S := make([]fr_secp.Element, nbLen)
for i := 0; i < nbLen; i++ {
S[i].SetRandom()
P[i].ScalarMultiplicationBase(S[i].BigInt(new(big.Int)))
}
var res secp256k1.G1Affine
_, err := res.MultiExp(P, S, ecc.MultiExpConfig{})

assert.NoError(err)
cP := make([]AffinePoint[emulated.Secp256k1Fp], len(P))
for i := range cP {
cP[i] = AffinePoint[emparams.Secp256k1Fp]{
X: emulated.ValueOf[emparams.Secp256k1Fp](P[i].X),
Y: emulated.ValueOf[emparams.Secp256k1Fp](P[i].Y),
}
}
cS := make([]emulated.Element[emparams.Secp256k1Fr], len(S))
for i := range cS {
cS[i] = emulated.ValueOf[emparams.Secp256k1Fr](S[i])
}
assignment := MultiScalarMulTest[emparams.Secp256k1Fp, emparams.Secp256k1Fr]{
Points: cP,
Scalars: cS,
Res: AffinePoint[emparams.Secp256k1Fp]{
X: emulated.ValueOf[emparams.Secp256k1Fp](res.X),
Y: emulated.ValueOf[emparams.Secp256k1Fp](res.Y),
},
}
err = test.IsSolved(&MultiScalarMulTest[emparams.Secp256k1Fp, emparams.P256Fr]{
Points: make([]AffinePoint[emparams.Secp256k1Fp], nbLen),
Scalars: make([]emulated.Element[emparams.P256Fr], nbLen),
}, &assignment, ecc.BN254.ScalarField())
assert.NoError(err)
}
61 changes: 61 additions & 0 deletions std/algebra/native/sw_bls12377/g1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/consensys/gnark/frontend/cs/scs"
"github.com/consensys/gnark/std/math/emulated"
"github.com/consensys/gnark/test"

bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377"
Expand Down Expand Up @@ -487,6 +488,66 @@ func TestVarScalarMulBaseG1(t *testing.T) {
assert.CheckCircuit(&circuit, test.WithValidAssignment(&witness), test.WithCurves(ecc.BW6_761))
}

type MultiScalarMulTest struct {
Points []G1Affine
Scalars []emulated.Element[ScalarField]
Res G1Affine
}

func (c *MultiScalarMulTest) Define(api frontend.API) error {
cr, err := NewCurve(api)
if err != nil {
return err
}
ps := make([]*G1Affine, len(c.Points))
for i := range c.Points {
ps[i] = &c.Points[i]
}
ss := make([]*emulated.Element[ScalarField], len(c.Scalars))
for i := range c.Scalars {
ss[i] = &c.Scalars[i]
}
res, err := cr.MultiScalarMul(ps, ss)
if err != nil {
return err
}
cr.AssertIsEqual(res, &c.Res)
return nil
}

func TestMultiScalarMul(t *testing.T) {
assert := test.NewAssert(t)
nbLen := 4
P := make([]bls12377.G1Affine, nbLen)
S := make([]fr.Element, nbLen)
for i := 0; i < nbLen; i++ {
S[i].SetRandom()
P[i].ScalarMultiplicationBase(S[i].BigInt(new(big.Int)))
}
var res bls12377.G1Affine
_, err := res.MultiExp(P, S, ecc.MultiExpConfig{})

assert.NoError(err)
cP := make([]G1Affine, len(P))
for i := range cP {
cP[i] = NewG1Affine(P[i])
}
cS := make([]emulated.Element[ScalarField], len(S))
for i := range cS {
cS[i] = NewScalar(S[i])
}
assignment := MultiScalarMulTest{
Points: cP,
Scalars: cS,
Res: NewG1Affine(res),
}
err = test.IsSolved(&MultiScalarMulTest{
Points: make([]G1Affine, nbLen),
Scalars: make([]emulated.Element[ScalarField], nbLen),
}, &assignment, ecc.BW6_761.ScalarField())
assert.NoError(err)
}

func randomPointG1() bls12377.G1Jac {

p1, _, _, _ := bls12377.Generators()
Expand Down
8 changes: 6 additions & 2 deletions std/algebra/native/sw_bls12377/pairing2.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ func (c *Curve) Add(P, Q *G1Affine) *G1Affine {
// AssertIsEqual asserts the equality of P and Q.
func (c *Curve) AssertIsEqual(P, Q *G1Affine) {
P.AssertIsEqual(c.api, *Q)
panic("todo")
}

// Neg negates P and returns the result. Does not modify P.
Expand Down Expand Up @@ -126,7 +125,12 @@ func (c *Curve) MultiScalarMul(P []*G1Affine, scalars []*Scalar) (*G1Affine, err
res := c.ScalarMul(P[0], scalars[0])
for i := 1; i < len(P); i++ {
q := c.ScalarMul(P[i], scalars[i])
c.Add(res, q)

// check for infinity
isInfinity := c.api.And(c.api.IsZero(P[i].X), c.api.IsZero(P[i].Y))
tmp := c.Add(res, q)
res.X = c.api.Select(isInfinity, res.X, tmp.X)
res.Y = c.api.Select(isInfinity, res.Y, tmp.Y)
}
return res, nil
}
Expand Down
61 changes: 61 additions & 0 deletions std/algebra/native/sw_bls24315/g1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/consensys/gnark/frontend/cs/scs"
"github.com/consensys/gnark/std/math/emulated"
"github.com/consensys/gnark/test"

bls24315 "github.com/consensys/gnark-crypto/ecc/bls24-315"
Expand Down Expand Up @@ -489,6 +490,66 @@ func TestVarScalarMulBaseG1(t *testing.T) {
assert.CheckCircuit(&circuit, test.WithValidAssignment(&witness), test.WithCurves(ecc.BW6_633), test.NoProverChecks())
}

type MultiScalarMulTest struct {
Points []G1Affine
Scalars []emulated.Element[ScalarField]
Res G1Affine
}

func (c *MultiScalarMulTest) Define(api frontend.API) error {
cr, err := NewCurve(api)
if err != nil {
return err
}
ps := make([]*G1Affine, len(c.Points))
for i := range c.Points {
ps[i] = &c.Points[i]
}
ss := make([]*emulated.Element[ScalarField], len(c.Scalars))
for i := range c.Scalars {
ss[i] = &c.Scalars[i]
}
res, err := cr.MultiScalarMul(ps, ss)
if err != nil {
return err
}
cr.AssertIsEqual(res, &c.Res)
return nil
}

func TestMultiScalarMul(t *testing.T) {
assert := test.NewAssert(t)
nbLen := 4
P := make([]bls24315.G1Affine, nbLen)
S := make([]fr.Element, nbLen)
for i := 0; i < nbLen; i++ {
S[i].SetRandom()
P[i].ScalarMultiplicationBase(S[i].BigInt(new(big.Int)))
}
var res bls24315.G1Affine
_, err := res.MultiExp(P, S, ecc.MultiExpConfig{})

assert.NoError(err)
cP := make([]G1Affine, len(P))
for i := range cP {
cP[i] = NewG1Affine(P[i])
}
cS := make([]emulated.Element[ScalarField], len(S))
for i := range cS {
cS[i] = NewScalar(S[i])
}
assignment := MultiScalarMulTest{
Points: cP,
Scalars: cS,
Res: NewG1Affine(res),
}
err = test.IsSolved(&MultiScalarMulTest{
Points: make([]G1Affine, nbLen),
Scalars: make([]emulated.Element[ScalarField], nbLen),
}, &assignment, ecc.BW6_633.ScalarField())
assert.NoError(err)
}

func randomPointG1() bls24315.G1Jac {

p1, _, _, _ := bls24315.Generators()
Expand Down
8 changes: 6 additions & 2 deletions std/algebra/native/sw_bls24315/pairing2.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ func (c *Curve) Add(P, Q *G1Affine) *G1Affine {
// AssertIsEqual asserts the equality of P and Q.
func (c *Curve) AssertIsEqual(P, Q *G1Affine) {
P.AssertIsEqual(c.api, *Q)
panic("todo")
}

// Neg negates P and returns the result. Does not modify P.
Expand Down Expand Up @@ -126,7 +125,12 @@ func (c *Curve) MultiScalarMul(P []*G1Affine, scalars []*Scalar) (*G1Affine, err
res := c.ScalarMul(P[0], scalars[0])
for i := 1; i < len(P); i++ {
q := c.ScalarMul(P[i], scalars[i])
c.Add(res, q)

// check for infinity...
isInfinity := c.api.And(c.api.IsZero(P[i].X), c.api.IsZero(P[i].Y))
tmp := c.Add(res, q)
res.X = c.api.Select(isInfinity, res.X, tmp.X)
res.Y = c.api.Select(isInfinity, res.Y, tmp.Y)
}
return res, nil
}
Expand Down
20 changes: 13 additions & 7 deletions std/commitments/kzg/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/consensys/gnark/std/algebra/emulated/sw_bw6761"
"github.com/consensys/gnark/std/algebra/native/sw_bls12377"
"github.com/consensys/gnark/std/algebra/native/sw_bls24315"
"github.com/consensys/gnark/std/math/bits"
"github.com/consensys/gnark/std/math/emulated"
"github.com/consensys/gnark/std/recursion"
)
Expand Down Expand Up @@ -364,7 +365,8 @@ func (v *Verifier[FR, G1El, G2El, GTEl]) CheckOpeningProof(commitment Commitment
return nil
}

func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifySinglePoint(digests []Commitment[G1El], batchOpeningProof BatchOpeningProof[FR, G1El], point emulated.Element[FR], vk VerifyingKey[G1El, G2El], dataTranscript ...frontend.Variable) error {
// BatchVerifySinglePoint verifies multiple opening proofs at a single point.
func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifySinglePoint(digests []Commitment[G1El], batchOpeningProof BatchOpeningProof[FR, G1El], point emulated.Element[FR], vk VerifyingKey[G1El, G2El], dataTranscript ...emulated.Element[FR]) error {
// fold the proof
foldedProof, foldedDigest, err := v.FoldProof(digests, batchOpeningProof, point, dataTranscript...)
if err != nil {
Expand All @@ -378,6 +380,7 @@ func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifySinglePoint(digests []Commit
return nil
}

// BatchVerifyMultiPoints verifies multiple opening proofs at different points.
func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifyMultiPoints(digests []Commitment[G1El], proofs []OpeningProof[FR, G1El], points []emulated.Element[FR], vk VerifyingKey[G1El, G2El]) error {
var fr FR

Expand Down Expand Up @@ -418,7 +421,7 @@ func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifyMultiPoints(digests []Commit
}

seed := whSnark.Sum()
binSeed := v.api.ToBinary(seed)
binSeed := bits.ToBinary(v.api, seed, bits.WithNbDigits(fr.Modulus().BitLen()))
randomNumbers[1] = v.scalarApi.FromBits(binSeed...)

for i := 2; i < len(randomNumbers); i++ {
Expand Down Expand Up @@ -489,7 +492,8 @@ func (v *Verifier[FR, G1El, G2El, GTEl]) BatchVerifyMultiPoints(digests []Commit
return err
}

func (v *Verifier[FR, G1El, G2El, GTEl]) FoldProof(digests []Commitment[G1El], batchOpeningProof BatchOpeningProof[FR, G1El], point emulated.Element[FR], dataTranscript ...frontend.Variable) (OpeningProof[FR, G1El], Commitment[G1El], error) {
// FoldProof folds multiple commitments and a batch opening proof for a single opening check.
func (v *Verifier[FR, G1El, G2El, GTEl]) FoldProof(digests []Commitment[G1El], batchOpeningProof BatchOpeningProof[FR, G1El], point emulated.Element[FR], dataTranscript ...emulated.Element[FR]) (OpeningProof[FR, G1El], Commitment[G1El], error) {
var retP OpeningProof[FR, G1El]
var retC Commitment[G1El]
nbDigests := len(digests)
Expand Down Expand Up @@ -525,7 +529,7 @@ func (v *Verifier[FR, G1El, G2El, GTEl]) FoldProof(digests []Commitment[G1El], b

// deriveGamma derives a challenge using Fiat Shamir to fold proofs.
// dataTranscript are supposed to be bits.
func (v *Verifier[FR, G1El, G2El, GTEl]) deriveGamma(point emulated.Element[FR], digests []Commitment[G1El], claimedValues []emulated.Element[FR], dataTranscript ...frontend.Variable) (*emulated.Element[FR], error) {
func (v *Verifier[FR, G1El, G2El, GTEl]) deriveGamma(point emulated.Element[FR], digests []Commitment[G1El], claimedValues []emulated.Element[FR], dataTranscript ...emulated.Element[FR]) (*emulated.Element[FR], error) {
var fr FR
fs, err := recursion.NewTranscript(v.api, fr.Modulus(), []string{"gamma"})
if err != nil {
Expand All @@ -546,15 +550,17 @@ func (v *Verifier[FR, G1El, G2El, GTEl]) deriveGamma(point emulated.Element[FR],
}
}

if err := fs.Bind("gamma", dataTranscript); err != nil {
return nil, fmt.Errorf("bind data transcript: %w", err)
for i := range dataTranscript {
if err := fs.Bind("gamma", v.curve.MarshalScalar(dataTranscript[i])); err != nil {
return nil, fmt.Errorf("bind %d-ith data transcript: %w", i, err)
}
}

gamma, err := fs.ComputeChallenge("gamma")
if err != nil {
return nil, fmt.Errorf("compute challenge: %w", err)
}
bGamma := v.api.ToBinary(gamma)
bGamma := bits.ToBinary(v.api, gamma, bits.WithNbDigits(fr.Modulus().BitLen()))
gammaS := v.scalarApi.FromBits(bGamma...)

return gammaS, nil
Expand Down
2 changes: 2 additions & 0 deletions std/recursion/plonk/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package plonk implements in-circuit PLONK verifier.
package plonk
Loading

0 comments on commit a99d198

Please sign in to comment.