cli
@@ -1,1 +1,6 @@
*.Backup +cli/compiledcircuit.json +cli/inputs.json +cli/proofs.json +cli/test.circuit +cli/trustedsetup.json
@@ -33,6 +33,7 @@ - [](https://godoc.org/github.com/arnaucube/go-snark/fields) Finite Fields operations
- [](https://godoc.org/github.com/arnaucube/go-snark/r1csqap) R1CS to QAP (more details: https://github.com/arnaucube/go-snark/tree/master/r1csqap) - [](https://godoc.org/github.com/arnaucube/go-snark/circuitcompiler) Circuit Compiler +#### Library usage Example: ```go // compile circuit and get the R1CS@@ -103,6 +104,56 @@ assert.Nil(t, err)
assert.True(t, snark.VerifyProof(circuit, setup, proof)) ``` + +#### CLI usage + +##### Compile circuit +Having a circuit file `test.circuit`: +``` +func test(x): + aux = x*x + y = aux*x + z = x + y + out = z + 5 +``` +And a inputs file `inputs.json` +``` +[ + 3 +] +``` + +In the command line, execute: +``` +> go-snark compile test.circuit +``` + +This will output the `compiledcircuit.json` file. + +##### Trusted Setup +Having the `compiledcircuit.json`, now we can generate the `TrustedSetup`: +``` +> go-snark trustedsetup compiledcircuit.json +``` +This will create the file `trustedsetup.json` with the TrustedSetup data, and also a `toxic.json` file, with the parameters to delete from the `Trusted Setup`. + + +##### Generate Proofs +Assumming that we have the `compiledcircuit.json` and the `trustedsetup.json`, we can now generate the `Proofs` with the following command: +``` +> go-snark genproofs +``` + +This will store the file `proofs.json`, that contains all the SNARK proofs. + +##### Verify Proofs +Having the `proofs.json`, `compiledcircuit.json`, `trustedsetup.json` files, we can now verify the `Pairings` of the proofs, in order to verify the proofs. +``` +> go-snark verify +``` +This will return a `true` if the proofs are verified, or a `false` if the proofs are not verified. + + ### Test ```
@@ -144,7 +144,10 @@ }
} // CalculateWitness calculates the Witness of a Circuit based on the given inputs -func (circ *Circuit) CalculateWitness(inputs []*big.Int) []*big.Int { +func (circ *Circuit) CalculateWitness(inputs []*big.Int) ([]*big.Int, error) { + if len(inputs) != len(circ.Inputs) { + return []*big.Int{}, errors.New("given inputs != circuit.Inputs") + } w := r1csqap.ArrayOfBigZeros(len(circ.Signals)) w[0] = big.NewInt(int64(1)) for i, input := range inputs {@@ -162,5 +165,5 @@ } else if constraint.Op == "/" {
w[indexInArray(circ.Signals, constraint.Out)] = new(big.Int).Div(grabVar(circ.Signals, w, constraint.V1), grabVar(circ.Signals, w, constraint.V2)) } } - return w + return w, nil }
@@ -78,6 +78,7 @@
b3 := big.NewInt(int64(3)) inputs := []*big.Int{b3} // Calculate Witness - w := circuit.CalculateWitness(inputs) + w, err := circuit.CalculateWitness(inputs) + assert.Nil(t, err) fmt.Println("w", w) }
@@ -0,0 +1,248 @@
+package main + +import ( + "bufio" + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "math/big" + "os" + + snark "github.com/arnaucube/go-snark" + "github.com/arnaucube/go-snark/circuitcompiler" + "github.com/arnaucube/go-snark/r1csqap" + "github.com/urfave/cli" +) + +func panicErr(err error) { + if err != nil { + panic(err) + } +} + +var commands = []cli.Command{ + { + Name: "compile", + Aliases: []string{}, + Usage: "compile a circuit", + Action: CompileCircuit, + }, + { + Name: "genproofs", + Aliases: []string{}, + Usage: "generate the snark proofs", + Action: GenerateProofs, + }, + { + Name: "verify", + Aliases: []string{}, + Usage: "verify the snark proofs", + Action: VerifyProofs, + }, +} + +func main() { + app := cli.NewApp() + app.Name = "go-snarks-cli" + app.Version = "0.1.0-alpha" + app.Flags = []cli.Flag{ + cli.StringFlag{Name: "config"}, + } + app.Commands = commands + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} + +func CompileCircuit(context *cli.Context) error { + fmt.Println("cli") + + // read circuit file + circuitFile, err := os.Open("test.circuit") + panicErr(err) + + // parse circuit code + parser := circuitcompiler.NewParser(bufio.NewReader(circuitFile)) + circuit, err := parser.Parse() + panicErr(err) + fmt.Println("\ncircuit data:", circuit) + + // read inputs file + inputsFile, err := ioutil.ReadFile("inputs.json") + panicErr(err) + + // parse inputs from inputsFile + var inputs []*big.Int + json.Unmarshal([]byte(string(inputsFile)), &inputs) + panicErr(err) + + // calculate wittness + w, err := circuit.CalculateWitness(inputs) + panicErr(err) + fmt.Println("\nwitness", w) + + // flat code to R1CS + fmt.Println("\ngenerating R1CS from flat code") + a, b, c := circuit.GenerateR1CS() + fmt.Println("\nR1CS:") + fmt.Println("a:", a) + fmt.Println("b:", b) + fmt.Println("c:", c) + + // R1CS to QAP + alphas, betas, gammas, zx := snark.Utils.PF.R1CSToQAP(a, b, c) + fmt.Println("qap") + fmt.Println(alphas) + fmt.Println(betas) + fmt.Println(gammas) + + ax, bx, cx, px := snark.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) + + hx := snark.Utils.PF.DivisorPolynomial(px, zx) + + // hx==px/zx so px==hx*zx + // assert.Equal(t, px, snark.Utils.PF.Mul(hx, zx)) + if !r1csqap.BigArraysEqual(px, snark.Utils.PF.Mul(hx, zx)) { + panic(errors.New("px != hx*zx")) + } + + // p(x) = a(x) * b(x) - c(x) == h(x) * z(x) + abc := snark.Utils.PF.Sub(snark.Utils.PF.Mul(ax, bx), cx) + // assert.Equal(t, abc, px) + if !r1csqap.BigArraysEqual(abc, px) { + panic(errors.New("abc != px")) + } + hz := snark.Utils.PF.Mul(hx, zx) + if !r1csqap.BigArraysEqual(abc, hz) { + panic(errors.New("abc != hz")) + } + // assert.Equal(t, abc, hz) + + div, rem := snark.Utils.PF.Div(px, zx) + if !r1csqap.BigArraysEqual(hx, div) { + panic(errors.New("hx != div")) + } + // assert.Equal(t, hx, div) + // assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(4)) + for _, r := range rem { + if !bytes.Equal(r.Bytes(), big.NewInt(int64(0)).Bytes()) { + panic(errors.New("error:error: px/zx rem not equal to zeros")) + } + } + + // calculate trusted setup + setup, err := snark.GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas, zx) + panicErr(err) + fmt.Println("\nt:", setup.Toxic.T) + + // store circuit to json + jsonData, err := json.Marshal(circuit) + panicErr(err) + // store setup into file + jsonFile, err := os.Create("compiledcircuit.json") + panicErr(err) + defer jsonFile.Close() + jsonFile.Write(jsonData) + jsonFile.Close() + fmt.Println("Compiled Circuit data written to ", jsonFile.Name()) + // store setup to json + jsonData, err = json.Marshal(setup) + 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 GenerateProofs(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 snark.Setup + json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) + panicErr(err) + + // read inputs file + inputsFile, err := ioutil.ReadFile("inputs.json") + panicErr(err) + // parse inputs from inputsFile + var inputs []*big.Int + json.Unmarshal([]byte(string(inputsFile)), &inputs) + panicErr(err) + // calculate wittness + w, err := circuit.CalculateWitness(inputs) + panicErr(err) + fmt.Println("\nwitness", w) + + // flat code to R1CS + a, b, c := circuit.GenerateR1CS() + // R1CS to QAP + alphas, betas, gammas, zx := snark.Utils.PF.R1CSToQAP(a, b, c) + _, _, _, px := snark.Utils.PF.CombinePolynomials(w, alphas, betas, gammas) + hx := snark.Utils.PF.DivisorPolynomial(px, zx) + + proof, err := snark.GenerateProofs(circuit, trustedsetup, hx, w) + 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 VerifyProofs(context *cli.Context) error { + // open proofs.json + proofsFile, err := ioutil.ReadFile("proofs.json") + panicErr(err) + var proof snark.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 snark.Setup + json.Unmarshal([]byte(string(trustedsetupFile)), &trustedsetup) + panicErr(err) + + verified := snark.VerifyProof(circuit, trustedsetup, proof, true) + if !verified { + fmt.Println("ERROR: proofs not verified") + } else { + fmt.Println("Proofs verified") + } + return nil +}
@@ -143,10 +143,13 @@ return bytes.Equal(and.Bytes(), big.NewInt(int64(1)).Bytes())
} func (fq12 Fq12) Exp(base [2][3][2]*big.Int, e *big.Int) [2][3][2]*big.Int { + // TODO fix bottleneck + res := fq12.One() rem := fq12.Fq2.F.Copy(e) exp := base + // before := time.Now() for !bytes.Equal(rem.Bytes(), big.NewInt(int64(0)).Bytes()) { if BigIsOdd(rem) { res = fq12.Mul(res, exp)@@ -154,6 +157,7 @@ }
exp = fq12.Square(exp) rem = new(big.Int).Rsh(rem, 1) } + // fmt.Println("time elapsed:", time.Since(before)) return res } func (fq12 Fq12) Affine(a [2][3][2]*big.Int) [2][3][2]*big.Int {
@@ -4,3 +4,5 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
@@ -1,6 +1,7 @@
package r1csqap import ( + "bytes" "math/big" "github.com/arnaucube/go-snark/fields"@@ -27,6 +28,17 @@ for i := 0; i < num; i++ {
r = append(r, bigZero) } return r +} +func BigArraysEqual(a, b []*big.Int) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if !bytes.Equal(a[i].Bytes(), b[i].Bytes()) { + return false + } + } + return true } // PolynomialField is the Polynomial over a Finite Field where the polynomial operations are performed
@@ -237,14 +237,16 @@ return proof, nil
} // VerifyProof verifies over the BN128 the Pairings of the Proof -func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof) bool { +func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof, printVer bool) bool { // e(piA, Va) == e(piA', g2) pairingPiaVa := Utils.Bn.Pairing(proof.PiA, setup.Vk.Vka) pairingPiapG2 := Utils.Bn.Pairing(proof.PiAp, Utils.Bn.G2.G) if !Utils.Bn.Fq12.Equal(pairingPiaVa, pairingPiapG2) { return false } - fmt.Println("✓ e(piA, Va) == e(piA', g2), valid knowledge commitment for A") + if printVer { + fmt.Println("✓ e(piA, Va) == e(piA', g2), valid knowledge commitment for A") + } // e(Vb, piB) == e(piB', g2) pairingVbPib := Utils.Bn.Pairing(setup.Vk.Vkb, proof.PiB)@@ -252,7 +254,9 @@ pairingPibpG2 := Utils.Bn.Pairing(proof.PiBp, Utils.Bn.G2.G)
if !Utils.Bn.Fq12.Equal(pairingVbPib, pairingPibpG2) { return false } - fmt.Println("✓ e(Vb, piB) == e(piB', g2), valid knowledge commitment for B") + if printVer { + fmt.Println("✓ e(Vb, piB) == e(piB', g2), valid knowledge commitment for B") + } // e(piC, Vc) == e(piC', g2) pairingPicVc := Utils.Bn.Pairing(proof.PiC, setup.Vk.Vkc)@@ -260,7 +264,9 @@ pairingPicpG2 := Utils.Bn.Pairing(proof.PiCp, Utils.Bn.G2.G)
if !Utils.Bn.Fq12.Equal(pairingPicVc, pairingPicpG2) { return false } - fmt.Println("✓ e(piC, Vc) == e(piC', g2), valid knowledge commitment for C") + if printVer { + fmt.Println("✓ e(piC, Vc) == e(piC', g2), valid knowledge commitment for C") + } // Vkx, to then calculate Vkx+piA vkxpia := setup.Vk.A[0]@@ -276,7 +282,9 @@ Utils.Bn.Pairing(proof.PiH, setup.Vk.Vkz),
Utils.Bn.Pairing(proof.PiC, Utils.Bn.G2.G))) { return false } - fmt.Println("✓ e(Vkx+piA, piB) == e(piH, Vkz) * e(piC, g2), QAP disibility checked") + if printVer { + fmt.Println("✓ e(Vkx+piA, piB) == e(piH, Vkz) * e(piC, g2), QAP disibility checked") + } // e(Vkx+piA+piC, g2KbetaKgamma) * e(g1KbetaKgamma, piB) // == e(piK, g2Kgamma)@@ -288,7 +296,9 @@ pairingR := Utils.Bn.Pairing(proof.PiKp, setup.Vk.G2Kg)
if !Utils.Bn.Fq12.Equal(pairingL, pairingR) { return false } - fmt.Println("✓ e(Vkx+piA+piC, g2KbetaKgamma) * e(g1KbetaKgamma, piB) == e(piK, g2Kgamma)") + if printVer { + fmt.Println("✓ e(Vkx+piA+piC, g2KbetaKgamma) * e(g1KbetaKgamma, piB) == e(piK, g2Kgamma)") + } return true }
@@ -34,7 +34,8 @@
b3 := big.NewInt(int64(3)) inputs := []*big.Int{b3} // wittness - w := circuit.CalculateWitness(inputs) + w, err := circuit.CalculateWitness(inputs) + assert.Nil(t, err) fmt.Println("\nwitness", w) // flat code to R1CS@@ -81,7 +82,7 @@
fmt.Println("\n proofs:") fmt.Println(proof) before := time.Now() - assert.True(t, VerifyProof(*circuit, setup, proof)) + assert.True(t, VerifyProof(*circuit, setup, proof, false)) fmt.Println("verify proof time elapsed:", time.Since(before)) }@@ -142,11 +143,64 @@
// calculate trusted setup setup, err := GenerateTrustedSetup(len(w), circuit, alphas, betas, gammas, zx) assert.Nil(t, err) - fmt.Println("t", setup.Toxic.T) // piA = g1 * A(t), piB = g2 * B(t), piC = g1 * C(t), piH = g1 * H(t) proof, err := GenerateProofs(circuit, setup, hx, w) assert.Nil(t, err) - assert.True(t, VerifyProof(circuit, setup, proof)) + assert.True(t, VerifyProof(circuit, setup, proof, true)) +} + +func TestZkMultiplication(t *testing.T) { + + // compile circuit and get the R1CS + flatCode := ` + func test(a, b): + out = a * b + ` + + // parse the code + parser := circuitcompiler.NewParser(strings.NewReader(flatCode)) + circuit, err := parser.Parse() + assert.Nil(t, err) + + b3 := big.NewInt(int64(3)) + b4 := big.NewInt(int64(4)) + inputs := []*big.Int{b3, b4} + // wittness + w, err := circuit.CalculateWitness(inputs) + assert.Nil(t, err) + + // flat code to R1CS + a, b, c := circuit.GenerateR1CS() + + // R1CS to QAP + alphas, betas, gammas, zx := Utils.PF.R1CSToQAP(a, b, c) + + ax, bx, cx, px := Utils.PF.CombinePolynomials(w, alphas, betas, gammas) + + hx := Utils.PF.DivisorPolynomial(px, zx) + + // hx==px/zx so px==hx*zx + assert.Equal(t, px, Utils.PF.Mul(hx, zx)) + + // p(x) = a(x) * b(x) - c(x) == h(x) * z(x) + abc := Utils.PF.Sub(Utils.PF.Mul(ax, bx), cx) + assert.Equal(t, abc, px) + hz := Utils.PF.Mul(hx, zx) + assert.Equal(t, abc, hz) + + div, rem := Utils.PF.Div(px, zx) + assert.Equal(t, hx, div) + assert.Equal(t, rem, r1csqap.ArrayOfBigZeros(1)) + + // calculate trusted setup + setup, err := GenerateTrustedSetup(len(w), *circuit, alphas, betas, gammas, zx) + assert.Nil(t, err) + + // piA = g1 * A(t), piB = g2 * B(t), piC = g1 * C(t), piH = g1 * H(t) + proof, err := GenerateProofs(*circuit, setup, hx, w) + assert.Nil(t, err) + + assert.True(t, VerifyProof(*circuit, setup, proof, false)) }