Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add PLONK in-circuit verifier #880

Merged
merged 27 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
53fee2d
test: add recursion hash tests
ivokub Nov 3, 2023
e29589c
fix: accumulate MSM result
ivokub Nov 20, 2023
395a6d7
refactor: take emulated element for additional data
ivokub Nov 20, 2023
3945e90
fix: handled infinity point in native multi scalar exp
ThomasPiellard Nov 10, 2023
daae22c
fix: use only nbBits when creating scalar
ivokub Nov 20, 2023
7a41420
feat: add PLONK verifier
ivokub Nov 20, 2023
ed6ce0a
feat: PlaceholderVerifyingKey takes the vk as argument
ThomasPiellard Nov 15, 2023
c72177b
feat: f -> scalarApi
ThomasPiellard Nov 15, 2023
e727a92
feat: addition of computeIthLagrangeAtZeta
ThomasPiellard Nov 15, 2023
9595331
feat: bsb commitments are added to pi
ThomasPiellard Nov 16, 2023
e958ce6
refactor: PlaceholderProof takes the proof as argument
ThomasPiellard Nov 16, 2023
330904c
fix: compute ith lagrange ok, hashToField failing
ThomasPiellard Nov 16, 2023
dbff073
fix: native short hash output size
ivokub Nov 17, 2023
5f8a060
feat: add bw6
ivokub Nov 17, 2023
b7cc8fb
docs: add package documentation
ivokub Nov 20, 2023
75bf361
refactor: describe error in panic
ivokub Nov 20, 2023
01f28ce
refactor: init curve and pairing implicitly
ivokub Nov 20, 2023
e608d57
refactor: remove comments
ivokub Nov 20, 2023
16d88db
docs: add package examples
ivokub Nov 20, 2023
5860e3a
feat: add all supported witness assignments
ivokub Nov 20, 2023
74145f9
test: add MSM test
ivokub Nov 23, 2023
f4fc463
fix: remove todo panic
ivokub Nov 23, 2023
51846bf
feat: add option shortcuts
ivokub Nov 23, 2023
9e3d250
fix: include hash to field in shortcut option
ivokub Nov 28, 2023
a3ec483
feat: use only CCS for placeholder proof and verifyingkey
ivokub Nov 28, 2023
3608b29
chore: typos and cleanup
ivokub Nov 28, 2023
f31664b
docs: add KZG package documentation
ivokub Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
ivokub marked this conversation as resolved.
Show resolved Hide resolved
}
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)

yelhousni marked this conversation as resolved.
Show resolved Hide resolved
// 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)

ivokub marked this conversation as resolved.
Show resolved Hide resolved
// 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