diff --git a/README.md b/README.md index d5eb5eb..67fe5ef 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,17 @@ zkSNARK library implementation in Go - `Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture`, Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza https://eprint.iacr.org/2013/879.pdf - `Pinocchio: Nearly practical verifiable computation`, Bryan Parno, Craig Gentry, Jon Howell, Mariana Raykova https://eprint.iacr.org/2013/279.pdf +- `On the Size of Pairing-based Non-interactive Arguments`, Jens Groth https://eprint.iacr.org/2016/260.pdf ## Caution & Warning -Implementation of the zkSNARK [Pinocchio protocol](https://eprint.iacr.org/2013/279.pdf) from scratch in Go to understand the concepts. Do not use in production. +Implementation of the zkSNARK [Pinocchio protocol](https://eprint.iacr.org/2013/279.pdf) and [Groth16 protocol](https://eprint.iacr.org/2016/260.pdf) from scratch in Go to understand the concepts. Do not use in production. Not finished, implementing this in my free time to understand it better, so I don't have much time. -Currently allows to do the complete path with [Pinocchio protocol](https://eprint.iacr.org/2013/279.pdf) : -1. compile circuuit +Currently allows to do the complete path with [Pinocchio protocol](https://eprint.iacr.org/2013/279.pdf) and [Groth16 protocol](https://eprint.iacr.org/2016/260.pdf) : + +0. write circuit +1. compile circuit 2. generate trusted setup 3. calculate witness 4. generate proofs @@ -35,12 +38,13 @@ Improvements from the minimal implementation: - [x] allow `import` in circuits language - [ ] allow `for` in circuits language - [ ] move witness values calculation outside the setup phase -- [ ] Groth16 +- [x] Groth16 - [ ] multiple optimizations ## Usage - [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark?status.svg)](https://godoc.org/github.com/arnaucube/go-snark) zkSnark +- [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/groth16?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/groth16) zkSnark Groth16 - [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/bn128?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/bn128) bn128 (more details: https://github.com/arnaucube/go-snark/tree/master/bn128) - [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/fields?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/fields) Finite Fields operations - [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/r1csqap?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/r1csqap) R1CS to QAP (more details: https://github.com/arnaucube/go-snark/tree/master/r1csqap) @@ -109,6 +113,16 @@ Having the `proofs.json`, `compiledcircuit.json`, `trustedsetup.json` `publicInp ``` This will return a `true` if the proofs are verified, or a `false` if the proofs are not verified. +### Cli using Groth16 +All this process can be done using [Groth16 protocol](https://eprint.iacr.org/2016/260.pdf) protocol: +``` +> ./go-snark-cli compile test.circuit +> ./go-snark-cli groth16 trustedsetup +> ./go-snark-cli groth16 genproofs +> ./go-snark-cli verify +``` + + ### Library usage @@ -179,6 +193,11 @@ assert.True(t, VerifyProof(*circuit, setup, proof, publicSignalsVerif, true)) ``` +## Versions +History of versions & tags of this project: +- v0.0.1: zkSnark complete flow working with Pinocchio protocol +- v0.0.2: circuit language improved (allow function calls and file imports) +- v0.0.3: Groth16 zkSnark protocol added ## Test ``` diff --git a/cli/main.go b/cli/main.go index 0777b46..5af7539 100644 --- a/cli/main.go +++ b/cli/main.go @@ -13,6 +13,7 @@ import ( snark "github.com/arnaucube/go-snark" "github.com/arnaucube/go-snark/circuitcompiler" + "github.com/arnaucube/go-snark/groth16" "github.com/arnaucube/go-snark/r1csqap" "github.com/urfave/cli" ) @@ -48,12 +49,37 @@ var commands = []cli.Command{ Usage: "verify the snark proofs", Action: VerifyProofs, }, + { + Name: "groth16", + Aliases: []string{}, + Usage: "use groth16 protocol", + Subcommands: []cli.Command{ + { + Name: "trustedsetup", + Aliases: []string{}, + Usage: "generate trusted setup for a circuit", + Action: Groth16TrustedSetup, + }, + { + Name: "genproofs", + Aliases: []string{}, + Usage: "generate the snark proofs", + Action: Groth16GenerateProofs, + }, + { + Name: "verify", + Aliases: []string{}, + Usage: "verify the snark proofs", + Action: Groth16VerifyProofs, + }, + }, + }, } func main() { app := cli.NewApp() app.Name = "go-snarks-cli" - app.Version = "0.0.1-alpha" + app.Version = "0.0.3-alpha" app.Flags = []cli.Flag{ cli.StringFlag{Name: "config"}, } @@ -322,3 +348,162 @@ func VerifyProofs(context *cli.Context) error { } return nil } + +func Groth16TrustedSetup(context *cli.Context) error { + // open compiledcircuit.json + compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") + panicErr(err) + var circuit circuitcompiler.Circuit + json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) + panicErr(err) + + // read privateInputs file + privateInputsFile, err := ioutil.ReadFile("privateInputs.json") + panicErr(err) + // read publicInputs file + publicInputsFile, err := ioutil.ReadFile("publicInputs.json") + panicErr(err) + + // parse inputs from inputsFile + var inputs circuitcompiler.Inputs + err = json.Unmarshal([]byte(string(privateInputsFile)), &inputs.Private) + panicErr(err) + err = json.Unmarshal([]byte(string(publicInputsFile)), &inputs.Public) + panicErr(err) + + // calculate wittness + w, err := circuit.CalculateWitness(inputs.Private, inputs.Public) + panicErr(err) + + // R1CS to QAP + alphas, betas, gammas, _ := snark.Utils.PF.R1CSToQAP(circuit.R1CS.A, circuit.R1CS.B, circuit.R1CS.C) + fmt.Println("qap") + fmt.Println(alphas) + fmt.Println(betas) + fmt.Println(gammas) + + // calculate trusted setup + setup, err := groth16.GenerateTrustedSetup(len(w), circuit, alphas, betas, gammas) + panicErr(err) + fmt.Println("\nt:", setup.Toxic.T) + + // remove setup.Toxic + var tsetup groth16.Setup + tsetup.Pk = setup.Pk + tsetup.Vk = setup.Vk + + // store setup to json + jsonData, err := json.Marshal(tsetup) + panicErr(err) + // store setup into file + jsonFile, err := os.Create("trustedsetup.json") + panicErr(err) + defer jsonFile.Close() + jsonFile.Write(jsonData) + jsonFile.Close() + fmt.Println("Trusted Setup data written to ", jsonFile.Name()) + return nil +} + +func Groth16GenerateProofs(context *cli.Context) error { + // open compiledcircuit.json + compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") + panicErr(err) + var circuit circuitcompiler.Circuit + json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) + panicErr(err) + + // open trustedsetup.json + trustedsetupFile, err := ioutil.ReadFile("trustedsetup.json") + panicErr(err) + var trustedsetup groth16.Setup + json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) + panicErr(err) + + // read privateInputs file + privateInputsFile, err := ioutil.ReadFile("privateInputs.json") + panicErr(err) + // read publicInputs file + publicInputsFile, err := ioutil.ReadFile("publicInputs.json") + panicErr(err) + // parse inputs from inputsFile + var inputs circuitcompiler.Inputs + err = json.Unmarshal([]byte(string(privateInputsFile)), &inputs.Private) + panicErr(err) + err = json.Unmarshal([]byte(string(publicInputsFile)), &inputs.Public) + panicErr(err) + + // calculate wittness + w, err := circuit.CalculateWitness(inputs.Private, inputs.Public) + panicErr(err) + fmt.Println("witness", w) + + // flat code to R1CS + a := circuit.R1CS.A + b := circuit.R1CS.B + c := circuit.R1CS.C + // R1CS to QAP + alphas, betas, gammas, _ := groth16.Utils.PF.R1CSToQAP(a, b, c) + _, _, _, px := groth16.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) + hx := groth16.Utils.PF.DivisorPolynomial(px, trustedsetup.Pk.Z) + + fmt.Println(circuit) + fmt.Println(trustedsetup.Pk.PowersTauDelta) + fmt.Println(hx) + fmt.Println(w) + proof, err := groth16.GenerateProofs(circuit, trustedsetup, w, px) + panicErr(err) + + fmt.Println("\n proofs:") + fmt.Println(proof) + + // store proofs to json + jsonData, err := json.Marshal(proof) + panicErr(err) + // store proof into file + jsonFile, err := os.Create("proofs.json") + panicErr(err) + defer jsonFile.Close() + jsonFile.Write(jsonData) + jsonFile.Close() + fmt.Println("Proofs data written to ", jsonFile.Name()) + return nil +} + +func Groth16VerifyProofs(context *cli.Context) error { + // open proofs.json + proofsFile, err := ioutil.ReadFile("proofs.json") + panicErr(err) + var proof groth16.Proof + json.Unmarshal([]byte(string(proofsFile)), &proof) + panicErr(err) + + // open compiledcircuit.json + compiledcircuitFile, err := ioutil.ReadFile("compiledcircuit.json") + panicErr(err) + var circuit circuitcompiler.Circuit + json.Unmarshal([]byte(string(compiledcircuitFile)), &circuit) + panicErr(err) + + // open trustedsetup.json + trustedsetupFile, err := ioutil.ReadFile("trustedsetup.json") + panicErr(err) + var trustedsetup groth16.Setup + json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) + panicErr(err) + + // read publicInputs file + publicInputsFile, err := ioutil.ReadFile("publicInputs.json") + panicErr(err) + var publicSignals []*big.Int + err = json.Unmarshal([]byte(string(publicInputsFile)), &publicSignals) + panicErr(err) + + verified := groth16.VerifyProof(circuit, trustedsetup, proof, publicSignals, true) + if !verified { + fmt.Println("ERROR: proofs not verified") + } else { + fmt.Println("Proofs verified") + } + return nil +} diff --git a/go-snark-cli b/go-snark-cli index b503f67..f819539 100755 Binary files a/go-snark-cli and b/go-snark-cli differ