Browse Source

cli

pull/5/head
arnaucube 5 years ago
parent
commit
33de628a91
11 changed files with 404 additions and 13 deletions
  1. +5
    -0
      .gitignore
  2. +51
    -0
      README.md
  3. +5
    -2
      circuitcompiler/circuit.go
  4. +2
    -1
      circuitcompiler/circuit_test.go
  5. +248
    -0
      cli/main.go
  6. +4
    -0
      fields/fq12.go
  7. +1
    -0
      go.mod
  8. +2
    -0
      go.sum
  9. +12
    -0
      r1csqap/r1csqap.go
  10. +16
    -6
      snark.go
  11. +58
    -4
      snark_test.go

+ 5
- 0
.gitignore

@ -1 +1,6 @@
*.Backup
cli/compiledcircuit.json
cli/inputs.json
cli/proofs.json
cli/test.circuit
cli/trustedsetup.json

+ 51
- 0
README.md

@ -33,6 +33,7 @@ Current implementation status:
- [![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)
- [![GoDoc](https://godoc.org/github.com/arnaucube/go-snark/circuitcompiler?status.svg)](https://godoc.org/github.com/arnaucube/go-snark/circuitcompiler) Circuit Compiler
#### Library usage
Example:
```go
// compile circuit and get the R1CS
@ -104,6 +105,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
```
go test ./... -v

+ 5
- 2
circuitcompiler/circuit.go

@ -144,7 +144,10 @@ func grabVar(signals []string, w []*big.Int, vStr string) *big.Int {
}
// 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 @@ func (circ *Circuit) CalculateWitness(inputs []*big.Int) []*big.Int {
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
}

+ 2
- 1
circuitcompiler/circuit_test.go

@ -78,6 +78,7 @@ func TestCircuitParser(t *testing.T) {
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)
}

+ 248
- 0
cli/main.go

@ -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
}

+ 4
- 0
fields/fq12.go

@ -143,10 +143,13 @@ func BigIsOdd(n *big.Int) bool {
}
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 @@ func (fq12 Fq12) Exp(base [2][3][2]*big.Int, e *big.Int) [2][3][2]*big.Int {
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 {

+ 1
- 0
go.mod

@ -4,4 +4,5 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
github.com/urfave/cli v1.20.0
)

+ 2
- 0
go.sum

@ -4,3 +4,5 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
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=

+ 12
- 0
r1csqap/r1csqap.go

@ -1,6 +1,7 @@
package r1csqap
import (
"bytes"
"math/big"
"github.com/arnaucube/go-snark/fields"
@ -28,6 +29,17 @@ func ArrayOfBigZeros(num int) []*big.Int {
}
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
type PolynomialField struct {

+ 16
- 6
snark.go

@ -237,14 +237,16 @@ func GenerateProofs(circuit circuitcompiler.Circuit, setup Setup, hx []*big.Int,
}
// 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 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof) bool
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 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof) bool
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 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof) bool
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 @@ func VerifyProof(circuit circuitcompiler.Circuit, setup Setup, proof Proof) bool
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
}

+ 58
- 4
snark_test.go

@ -34,7 +34,8 @@ func TestZkFromFlatCircuitCode(t *testing.T) {
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 @@ func TestZkFromFlatCircuitCode(t *testing.T) {
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 @@ func TestZkFromHardcodedR1CS(t *testing.T) {
// 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))
}

Loading…
Cancel
Save