diff --git a/README.md b/README.md index f0441c8..2a591eb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# go-circom-prover-verifier [![GoDoc](https://godoc.org/github.com/iden3/go-circom-prover-verifier?status.svg)](https://godoc.org/github.com/iden3/go-circom-prover-verifier) [![Go Report Card](https://goreportcard.com/badge/github.com/iden3/go-circom-prover-verifier)](https://goreportcard.com/report/github.com/iden3/go-circom-prover-verifier) +# go-circom-prover-verifier [![GoDoc](https://godoc.org/github.com/iden3/go-circom-prover-verifier?status.svg)](https://godoc.org/github.com/iden3/go-circom-prover-verifier) [![Go Report Card](https://goreportcard.com/badge/github.com/iden3/go-circom-prover-verifier)](https://goreportcard.com/report/github.com/iden3/go-circom-prover-verifier) [![Test](https://github.com/iden3/go-circom-prover-verifier/workflows/Test/badge.svg)](https://github.com/iden3/go-circom-prover-verifier/actions?query=workflow%3ATest) Experimental Go implementation of the [Groth16 protocol](https://eprint.iacr.org/2016/260.pdf) zkSNARK prover & verifier compatible with [circom](https://github.com/iden3/circom). diff --git a/go.mod b/go.mod index 5741dfd..e98727a 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,12 @@ -module gocircomprover +module go-circom-prover-verifier -go 1.13 +go 1.14 + +replace github.com/iden3/go-circom-prover-verifier => ./ require ( github.com/ethereum/go-ethereum v1.9.12 + github.com/iden3/go-circom-prover-verifier v0.0.0-00010101000000-000000000000 github.com/stretchr/testify v1.5.1 + golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 ) diff --git a/go.sum b/go.sum index 2bca4b8..733cf9e 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,7 @@ github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1 github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag= +github.com/iden3/go-circom-prover-verifier v0.0.0-20200409092022-a439651fafb0 h1:iyMhjvq7JKtKT6kEKuKghXiwanoa4/u7JSCKqUBqvds= github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -119,6 +120,7 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:s github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA= golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= diff --git a/parsers.go b/parsers/parsers.go similarity index 94% rename from parsers.go rename to parsers/parsers.go index 44c6e49..0647a4e 100644 --- a/parsers.go +++ b/parsers/parsers.go @@ -1,4 +1,4 @@ -package gocircomprover +package parsers import ( "bytes" @@ -10,6 +10,7 @@ import ( "strings" bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + "github.com/iden3/go-circom-prover-verifier/types" ) // PkString is the equivalent to the Pk struct in string representation, containing the ProvingKey @@ -53,14 +54,14 @@ type VkString struct { } // ParseWitness parses the json []byte data into the Witness struct -func ParseWitness(wJson []byte) (Witness, error) { +func ParseWitness(wJson []byte) (types.Witness, error) { var ws WitnessString err := json.Unmarshal(wJson, &ws) if err != nil { return nil, err } - var w Witness + var w types.Witness for i := 0; i < len(ws); i++ { bi, err := stringToBigInt(ws[i]) if err != nil { @@ -72,7 +73,7 @@ func ParseWitness(wJson []byte) (Witness, error) { } // ParsePk parses the json []byte data into the Pk struct -func ParsePk(pkJson []byte) (*Pk, error) { +func ParsePk(pkJson []byte) (*types.Pk, error) { var pkStr PkString err := json.Unmarshal(pkJson, &pkStr) if err != nil { @@ -82,8 +83,8 @@ func ParsePk(pkJson []byte) (*Pk, error) { return pk, err } -func pkStringToPk(ps PkString) (*Pk, error) { - var p Pk +func pkStringToPk(ps PkString) (*types.Pk, error) { + var p types.Pk var err error p.A, err = arrayStringToG1(ps.A) @@ -152,8 +153,8 @@ func pkStringToPk(ps PkString) (*Pk, error) { return &p, nil } -func proofStringToProof(pr ProofString) (*Proof, error) { - var p Proof +func proofStringToProof(pr ProofString) (*types.Proof, error) { + var p types.Proof var err error p.A, err = stringToG1(pr.A) if err != nil { @@ -174,7 +175,7 @@ func proofStringToProof(pr ProofString) (*Proof, error) { } // ParseProof takes a json []byte and outputs the *Proof struct -func ParseProof(pj []byte) (*Proof, error) { +func ParseProof(pj []byte) (*types.Proof, error) { var pr ProofString err := json.Unmarshal(pj, &pr) if err != nil { @@ -203,7 +204,7 @@ func ParsePublicSignals(pj []byte) ([]*big.Int, error) { } // ParseVk takes a json []byte and outputs the *Vk struct -func ParseVk(vj []byte) (*Vk, error) { +func ParseVk(vj []byte) (*types.Vk, error) { var vr VkString err := json.Unmarshal(vj, &vr) if err != nil { @@ -213,8 +214,8 @@ func ParseVk(vj []byte) (*Vk, error) { return v, err } -func vkStringToVk(vr VkString) (*Vk, error) { - var v Vk +func vkStringToVk(vr VkString) (*types.Vk, error) { + var v types.Vk var err error v.Alpha, err = stringToG1(vr.Alpha) if err != nil { @@ -464,7 +465,7 @@ func stringToG2(h [][]string) (*bn256.G2, error) { } // ProofToJson outputs the Proof i Json format -func ProofToJson(p *Proof) ([]byte, error) { +func ProofToJson(p *types.Proof) ([]byte, error) { var ps ProofString ps.A = make([]string, 3) ps.B = make([][]string, 3) diff --git a/parsers_test.go b/parsers/parsers_test.go similarity index 99% rename from parsers_test.go rename to parsers/parsers_test.go index f2173b5..1baa0fd 100644 --- a/parsers_test.go +++ b/parsers/parsers_test.go @@ -1,4 +1,4 @@ -package gocircomprover +package parsers import ( "testing" diff --git a/arithmetic.go b/prover/arithmetic.go similarity index 98% rename from arithmetic.go rename to prover/arithmetic.go index f681f5e..547142b 100644 --- a/arithmetic.go +++ b/prover/arithmetic.go @@ -1,4 +1,4 @@ -package gocircomprover +package prover import ( "bytes" diff --git a/ifft.go b/prover/ifft.go similarity index 98% rename from ifft.go rename to prover/ifft.go index ffe62ca..d7edcea 100644 --- a/ifft.go +++ b/prover/ifft.go @@ -1,4 +1,4 @@ -package gocircomprover +package prover import ( "math" diff --git a/prover.go b/prover/prover.go similarity index 93% rename from prover.go rename to prover/prover.go index 8c1edfd..cb6722c 100644 --- a/prover.go +++ b/prover/prover.go @@ -1,10 +1,11 @@ -package gocircomprover +package prover import ( "crypto/rand" "math/big" bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" + "github.com/iden3/go-circom-prover-verifier/types" ) // Proof is the data structure of the Groth16 zkSNARK proof @@ -54,8 +55,8 @@ func randBigInt() (*big.Int, error) { } // GenerateProof generates the Groth16 zkSNARK proof -func GenerateProof(pk *Pk, w Witness) (*Proof, []*big.Int, error) { - var proof Proof +func GenerateProof(pk *types.Pk, w types.Witness) (*types.Proof, []*big.Int, error) { + var proof types.Proof r, err := randBigInt() if err != nil { @@ -105,7 +106,7 @@ func GenerateProof(pk *Pk, w Witness) (*Proof, []*big.Int, error) { return &proof, pubSignals, nil } -func calculateH(pk *Pk, w Witness) []*big.Int { +func calculateH(pk *types.Pk, w types.Witness) []*big.Int { m := pk.DomainSize polAT := arrayOfZeroes(m) polBT := arrayOfZeroes(m) diff --git a/prover/prover_test.go b/prover/prover_test.go new file mode 100644 index 0000000..6964353 --- /dev/null +++ b/prover/prover_test.go @@ -0,0 +1,106 @@ +package prover + +import ( + "encoding/json" + "io/ioutil" + "math/big" + "testing" + + "github.com/iden3/go-circom-prover-verifier/parsers" + "github.com/iden3/go-circom-prover-verifier/types" + "github.com/iden3/go-circom-prover-verifier/verifier" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSmallCircuitGenerateProf(t *testing.T) { + provingKeyJson, err := ioutil.ReadFile("../testdata/small/proving_key.json") + require.Nil(t, err) + pk, err := parsers.ParsePk(provingKeyJson) + require.Nil(t, err) + + witnessJson, err := ioutil.ReadFile("../testdata/small/witness.json") + require.Nil(t, err) + w, err := parsers.ParseWitness(witnessJson) + require.Nil(t, err) + + assert.Equal(t, types.Witness{big.NewInt(1), big.NewInt(33), big.NewInt(3), big.NewInt(11)}, w) + + proof, pubSignals, err := GenerateProof(pk, w) + assert.Nil(t, err) + + proofStr, err := parsers.ProofToJson(proof) + assert.Nil(t, err) + + err = ioutil.WriteFile("../testdata/small/proof.json", proofStr, 0644) + assert.Nil(t, err) + publicStr, err := json.Marshal(parsers.ArrayBigIntToString(pubSignals)) + assert.Nil(t, err) + err = ioutil.WriteFile("../testdata/small/public.json", publicStr, 0644) + assert.Nil(t, err) + + // verify the proof + vkJson, err := ioutil.ReadFile("../testdata/small/verification_key.json") + require.Nil(t, err) + vk, err := parsers.ParseVk(vkJson) + require.Nil(t, err) + + v := verifier.Verify(vk, proof, pubSignals) + assert.True(t, v) + + // to verify the proof with snarkjs: + // snarkjs verify --vk testdata/small/verification_key.json -p testdata/small/proof.json --pub testdata/small/public.json +} + +func TestBigCircuitGenerateProf(t *testing.T) { + provingKeyJson, err := ioutil.ReadFile("../testdata/big/proving_key.json") + require.Nil(t, err) + pk, err := parsers.ParsePk(provingKeyJson) + require.Nil(t, err) + + witnessJson, err := ioutil.ReadFile("../testdata/big/witness.json") + require.Nil(t, err) + w, err := parsers.ParseWitness(witnessJson) + require.Nil(t, err) + + proof, pubSignals, err := GenerateProof(pk, w) + assert.Nil(t, err) + + proofStr, err := parsers.ProofToJson(proof) + assert.Nil(t, err) + + err = ioutil.WriteFile("../testdata/big/proof.json", proofStr, 0644) + assert.Nil(t, err) + publicStr, err := json.Marshal(parsers.ArrayBigIntToString(pubSignals)) + assert.Nil(t, err) + err = ioutil.WriteFile("../testdata/big/public.json", publicStr, 0644) + assert.Nil(t, err) + + // verify the proof + vkJson, err := ioutil.ReadFile("../testdata/big/verification_key.json") + require.Nil(t, err) + vk, err := parsers.ParseVk(vkJson) + require.Nil(t, err) + + v := verifier.Verify(vk, proof, pubSignals) + assert.True(t, v) + + // to verify the proof with snarkjs: + // snarkjs verify --vk testdata/big/verification_key.json -p testdata/big/proof.json --pub testdata/big/public.json +} + +func BenchmarkGenerateProof(b *testing.B) { + provingKeyJson, err := ioutil.ReadFile("../testdata/big/proving_key.json") + require.Nil(b, err) + pk, err := parsers.ParsePk(provingKeyJson) + require.Nil(b, err) + + witnessJson, err := ioutil.ReadFile("../testdata/big/witness.json") + require.Nil(b, err) + w, err := parsers.ParseWitness(witnessJson) + require.Nil(b, err) + + for i := 0; i < b.N; i++ { + GenerateProof(pk, w) + } +} diff --git a/prover_test.go b/prover_test.go deleted file mode 100644 index 702c8ef..0000000 --- a/prover_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package gocircomprover - -import ( - "encoding/json" - "io/ioutil" - "math/big" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestSmallCircuitGenerateProf(t *testing.T) { - provingKeyJson, err := ioutil.ReadFile("testdata/small/proving_key.json") - require.Nil(t, err) - pk, err := ParsePk(provingKeyJson) - require.Nil(t, err) - - witnessJson, err := ioutil.ReadFile("testdata/small/witness.json") - require.Nil(t, err) - w, err := ParseWitness(witnessJson) - require.Nil(t, err) - - assert.Equal(t, Witness{big.NewInt(1), big.NewInt(33), big.NewInt(3), big.NewInt(11)}, w) - - proof, pubSignals, err := GenerateProof(pk, w) - assert.Nil(t, err) - - proofStr, err := ProofToJson(proof) - assert.Nil(t, err) - - err = ioutil.WriteFile("testdata/small/proof.json", proofStr, 0644) - assert.Nil(t, err) - publicStr, err := json.Marshal(ArrayBigIntToString(pubSignals)) - assert.Nil(t, err) - err = ioutil.WriteFile("testdata/small/public.json", publicStr, 0644) - assert.Nil(t, err) - - // verify the proof - vkJson, err := ioutil.ReadFile("testdata/small/verification_key.json") - require.Nil(t, err) - vk, err := ParseVk(vkJson) - require.Nil(t, err) - - v := Verify(vk, proof, pubSignals) - assert.True(t, v) - - // to verify the proof with snarkjs: - // snarkjs verify --vk testdata/small/verification_key.json -p testdata/small/proof.json --pub testdata/small/public.json -} - -func TestBigCircuitGenerateProf(t *testing.T) { - provingKeyJson, err := ioutil.ReadFile("testdata/big/proving_key.json") - require.Nil(t, err) - pk, err := ParsePk(provingKeyJson) - require.Nil(t, err) - - witnessJson, err := ioutil.ReadFile("testdata/big/witness.json") - require.Nil(t, err) - w, err := ParseWitness(witnessJson) - require.Nil(t, err) - - proof, pubSignals, err := GenerateProof(pk, w) - assert.Nil(t, err) - - proofStr, err := ProofToJson(proof) - assert.Nil(t, err) - - err = ioutil.WriteFile("testdata/big/proof.json", proofStr, 0644) - assert.Nil(t, err) - publicStr, err := json.Marshal(ArrayBigIntToString(pubSignals)) - assert.Nil(t, err) - err = ioutil.WriteFile("testdata/big/public.json", publicStr, 0644) - assert.Nil(t, err) - - // verify the proof - vkJson, err := ioutil.ReadFile("testdata/big/verification_key.json") - require.Nil(t, err) - vk, err := ParseVk(vkJson) - require.Nil(t, err) - - v := Verify(vk, proof, pubSignals) - assert.True(t, v) - - // to verify the proof with snarkjs: - // snarkjs verify --vk testdata/big/verification_key.json -p testdata/big/proof.json --pub testdata/big/public.json -} - -func BenchmarkGenerateProof(b *testing.B) { - provingKeyJson, err := ioutil.ReadFile("testdata/big/proving_key.json") - require.Nil(b, err) - pk, err := ParsePk(provingKeyJson) - require.Nil(b, err) - - witnessJson, err := ioutil.ReadFile("testdata/big/witness.json") - require.Nil(b, err) - w, err := ParseWitness(witnessJson) - require.Nil(b, err) - - for i := 0; i < b.N; i++ { - GenerateProof(pk, w) - } -} diff --git a/types/types.go b/types/types.go new file mode 100644 index 0000000..c9b9512 --- /dev/null +++ b/types/types.go @@ -0,0 +1,46 @@ +package types + +import ( + "math/big" + + bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" +) + +// Proof is the data structure of the Groth16 zkSNARK proof +type Proof struct { + A *bn256.G1 + B *bn256.G2 + C *bn256.G1 +} + +// Pk holds the data structure of the ProvingKey +type Pk struct { + A []*bn256.G1 + B2 []*bn256.G2 + B1 []*bn256.G1 + C []*bn256.G1 + NVars int + NPublic int + VkAlpha1 *bn256.G1 + VkDelta1 *bn256.G1 + VkBeta1 *bn256.G1 + VkBeta2 *bn256.G2 + VkDelta2 *bn256.G2 + HExps []*bn256.G1 + DomainSize int + PolsA []map[int]*big.Int + PolsB []map[int]*big.Int + PolsC []map[int]*big.Int +} + +// Witness contains the witness +type Witness []*big.Int + +// Vk is the Verification Key data structure +type Vk struct { + Alpha *bn256.G1 + Beta *bn256.G2 + Gamma *bn256.G2 + Delta *bn256.G2 + IC []*bn256.G1 +} diff --git a/verifier.go b/verifier/verifier.go similarity index 77% rename from verifier.go rename to verifier/verifier.go index 280877c..d1c6111 100644 --- a/verifier.go +++ b/verifier/verifier.go @@ -1,10 +1,12 @@ -package gocircomprover +package verifier import ( "fmt" "math/big" "github.com/ethereum/go-ethereum/crypto/bn256" + "github.com/iden3/go-circom-prover-verifier/prover" + "github.com/iden3/go-circom-prover-verifier/types" ) // Vk is the Verification Key data structure @@ -16,7 +18,7 @@ type Vk struct { IC []*bn256.G1 } -func Verify(vk *Vk, proof *Proof, inputs []*big.Int) bool { +func Verify(vk *types.Vk, proof *types.Proof, inputs []*big.Int) bool { if len(inputs)+1 != len(vk.IC) { fmt.Println("len(inputs)+1 != len(vk.IC)") return false @@ -24,7 +26,7 @@ func Verify(vk *Vk, proof *Proof, inputs []*big.Int) bool { vkX := new(bn256.G1).ScalarBaseMult(big.NewInt(0)) for i := 0; i < len(inputs); i++ { // check input inside field - if inputs[0].Cmp(R) != -1 { + if inputs[0].Cmp(prover.R) != -1 { return false } vkX = new(bn256.G1).Add(vkX, new(bn256.G1).ScalarMult(vk.IC[i+1], inputs[i])) diff --git a/verifier/verifier_test.go b/verifier/verifier_test.go new file mode 100644 index 0000000..6e922eb --- /dev/null +++ b/verifier/verifier_test.go @@ -0,0 +1,50 @@ +package verifier + +import ( + "io/ioutil" + "testing" + + "github.com/iden3/go-circom-prover-verifier/parsers" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVerify1(t *testing.T) { + proofJson, err := ioutil.ReadFile("../testdata/big/proof.json") + require.Nil(t, err) + vkJson, err := ioutil.ReadFile("../testdata/big/verification_key.json") + require.Nil(t, err) + publicJson, err := ioutil.ReadFile("../testdata/big/public.json") + require.Nil(t, err) + + public, err := parsers.ParsePublicSignals(publicJson) + require.Nil(t, err) + proof, err := parsers.ParseProof(proofJson) + require.Nil(t, err) + vk, err := parsers.ParseVk(vkJson) + require.Nil(t, err) + + v := Verify(vk, proof, public) + assert.True(t, v) +} + +func BenchmarkVerify(b *testing.B) { + proofJson, err := ioutil.ReadFile("../testdata/big/proof.json") + require.Nil(b, err) + vkJson, err := ioutil.ReadFile("../testdata/big/verification_key.json") + require.Nil(b, err) + publicJson, err := ioutil.ReadFile("../testdata/big/public.json") + require.Nil(b, err) + + public, err := parsers.ParsePublicSignals(publicJson) + require.Nil(b, err) + proof, err := parsers.ParseProof(proofJson) + require.Nil(b, err) + vk, err := parsers.ParseVk(vkJson) + require.Nil(b, err) + + for i := 0; i < b.N; i++ { + Verify(vk, proof, public) + } +} diff --git a/verifier_test.go b/verifier_test.go deleted file mode 100644 index fe06dc6..0000000 --- a/verifier_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package gocircomprover - -import ( - "io/ioutil" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestVerify1(t *testing.T) { - proofJson, err := ioutil.ReadFile("testdata/big/proof.json") - require.Nil(t, err) - vkJson, err := ioutil.ReadFile("testdata/big/verification_key.json") - require.Nil(t, err) - publicJson, err := ioutil.ReadFile("testdata/big/public.json") - require.Nil(t, err) - - public, err := ParsePublicSignals(publicJson) - require.Nil(t, err) - proof, err := ParseProof(proofJson) - require.Nil(t, err) - vk, err := ParseVk(vkJson) - require.Nil(t, err) - - v := Verify(vk, proof, public) - assert.True(t, v) -} - -func BenchmarkVerify(b *testing.B) { - proofJson, err := ioutil.ReadFile("testdata/big/proof.json") - require.Nil(b, err) - vkJson, err := ioutil.ReadFile("testdata/big/verification_key.json") - require.Nil(b, err) - publicJson, err := ioutil.ReadFile("testdata/big/public.json") - require.Nil(b, err) - - public, err := ParsePublicSignals(publicJson) - require.Nil(b, err) - proof, err := ParseProof(proofJson) - require.Nil(b, err) - vk, err := ParseVk(vkJson) - require.Nil(b, err) - - for i := 0; i < b.N; i++ { - Verify(vk, proof, public) - } -}