7 Commits

Author SHA1 Message Date
arnaucube
42961f6b94 Fix pk parser benchmarks
Parsers were working correctly, but the benchmarks had errors.

The benchmarks in the commit d1b3979eb6
are incorrect, correct ones are:

```
BenchmarkParsePk/ParsePkJson_circuit1k-4         	       2	 529437960 ns/op
BenchmarkParsePk/ParsePkBin_circuit1k-4          	       2	 607792597 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit1k-4        	       2	 540594611 ns/op
BenchmarkParsePk/ParsePkJson_circuit5k-4         	       1	2769819086 ns/op
BenchmarkParsePk/ParsePkBin_circuit5k-4          	       1	3094913319 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit5k-4        	       1	2404651389 ns/op
BenchmarkParsePk/ParsePkJson_circuit10k-4        	       1	5374917709 ns/op
BenchmarkParsePk/ParsePkBin_circuit10k-4         	       1	5756633515 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit10k-4       	       1	4782081310 ns/op
BenchmarkParsePk/ParsePkJson_circuit20k-4        	       1	10374987398 ns/op
BenchmarkParsePk/ParsePkBin_circuit20k-4         	       1	11528361584 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit20k-4       	       1	9541829245 ns/op
BenchmarkParsePk/ParsePkJson_circuit50k-4         	       1	25979727146 ns/op
BenchmarkParsePk/ParsePkBin_circuit50k-4          	       1	28434810627 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit50k-4        	       1	23860248412 ns/op
```

The size of ProvingKey file for a circuit of 20k and 50k constraints:
```
circuit 20k constraints:
10097876 bytes of proving_key.go.bin
10097876 bytes of proving_key.bin
29760049 bytes of proving_key.json

circuit 50k constraints:
24195028 bytes of proving_key.go.bin
24194964 bytes of proving_key.bin
71484081 bytes of proving_key.json
```
2020-05-21 13:19:45 +02:00
Eduard S
386758370e Merge pull request #18 from iden3/feature/pk-own-format
Add own go-circom ProvingKey Bin format
2020-05-21 11:25:53 +02:00
arnaucube
2b8a15ca1a Add cli to convert Pk to go-circom binary file 2020-05-20 18:07:10 +02:00
arnaucube
d1b3979eb6 Add own go-circom ProvingKey Bin format
Add parsers from bin to pk and from pk to bin.

```
BenchmarkParsePk/ParsePkJson_circuit1k-4         	  595215	      1954 ns/op
BenchmarkParsePk/ParsePkBin_circuit1k-4          	       2	 522174688 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit1k-4        	  623182	      1901 ns/op
BenchmarkParsePk/ParsePkJson_circuit5k-4         	       1	2861959141 ns/op
BenchmarkParsePk/ParsePkBin_circuit5k-4          	       1	2610638932 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit5k-4        	  612906	      1907 ns/op
BenchmarkParsePk/ParsePkJson_circuit10k-4        	       1	5793696755 ns/op
BenchmarkParsePk/ParsePkBin_circuit10k-4         	       1	5204251056 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit10k-4       	  603478	      1913 ns/op
BenchmarkParsePk/ParsePkJson_circuit20k-4        	       1	11404022165 ns/op
BenchmarkParsePk/ParsePkBin_circuit20k-4         	       1	10392012613 ns/op
BenchmarkParsePk/ParsePkGoBin_circuit20k-4       	  620488	      1921 ns/op
```

For the `10k` constraints circuit is arround `1_368_976x` improvement.
For the `20k` constraints circuit is arround `5_409_689x` improvement.
2020-05-20 13:35:06 +02:00
Eduard S
de95d7a3e4 Merge pull request #16 from iden3/feature/pk-bin
Add ProvingKey binary parser
2020-05-20 12:31:36 +02:00
arnaucube
5c2aaec1ca Add ProvingKey binary parser
The time on the parsing doesn't improve, as the data from the binary
file needs to be converted to `affine` representation, and then parsed
into the `bn256.G1` & `bn256.G2` formats (Montgomery). But the size of
the ProvingKey files in binary is much smaller, so will be better
handled by the smarthpones.

- Parse time benchmarks:
```
BenchmarkParsePk/Parse_Pk_bin_circuit1k-4         	       2	 563729994 ns/op
BenchmarkParsePk/Parse_Pk_json_circuit1k-4        	  635641	      1941 ns/op
BenchmarkParsePk/Parse_Pk_bin_circuit5k-4         	       1	2637941658 ns/op
BenchmarkParsePk/Parse_Pk_json_circuit5k-4        	       1	2986560185 ns/op
BenchmarkParsePk/Parse_Pk_bin_circuit10k-4        	       1	5337639150 ns/op
BenchmarkParsePk/Parse_Pk_json_circuit10k-4       	       1	6149568824 ns/op
BenchmarkParsePk/Parse_Pk_bin_circuit20k-4        	       1	10533654623 ns/op
BenchmarkParsePk/Parse_Pk_json_circuit20k-4       	       1	11657326851 ns/op
```

- Size of ProvingKey file for a circuit of 50k constraints:
```
circuit 20k constraints:
10097812 bytes of proving_key.bin
29760049 bytes of proving_key.json

circuit 50k constraints:
24194964 bytes of proving_key.bin
71484081 bytes of proving_key.json
```
2020-05-20 12:25:27 +02:00
arnau
bedd64cc70 Merge pull request #15 from iden3/feature/proofjson
Add JSON Marshal/Unmarshal to Proof
2020-05-15 12:00:33 +02:00
8 changed files with 905 additions and 15 deletions

View File

@@ -23,6 +23,6 @@ jobs:
- name: Compile circuits and execute Go tests
run: |
cd testdata && sh ./compile-circuits.sh && cd ..
go run cli/cli.go -prove -provingkey=testdata/circuit1k/proving_key.json -witness=testdata/circuit1k/witness.json -proof=testdata/circuit1k/proof.json -public=testdata/circuit1k/public.json
go run cli/cli.go -prove -provingkey=testdata/circuit5k/proving_key.json -witness=testdata/circuit5k/witness.json -proof=testdata/circuit5k/proof.json -public=testdata/circuit5k/public.json
go run cli/cli.go -prove -pk=testdata/circuit1k/proving_key.json -witness=testdata/circuit1k/witness.json -proof=testdata/circuit1k/proof.json -public=testdata/circuit1k/public.json
go run cli/cli.go -prove -pk=testdata/circuit5k/proving_key.json -witness=testdata/circuit5k/witness.json -proof=testdata/circuit5k/proof.json -public=testdata/circuit5k/public.json
go test ./...

View File

@@ -21,12 +21,14 @@ func main() {
prove := flag.Bool("prove", false, "prover mode")
verify := flag.Bool("verify", false, "verifier mode")
convert := flag.Bool("convert", false, "convert mode, to convert between proving_key.json to proving_key.go.bin")
provingKeyPath := flag.String("provingkey", "proving_key.json", "provingKey path")
provingKeyPath := flag.String("pk", "proving_key.json", "provingKey path")
witnessPath := flag.String("witness", "witness.json", "witness path")
proofPath := flag.String("proof", "proof.json", "proof path")
verificationKeyPath := flag.String("verificationkey", "verification_key.json", "verificationKey path")
verificationKeyPath := flag.String("vk", "verification_key.json", "verificationKey path")
publicPath := flag.String("public", "public.json", "public signals path")
provingKeyBinPath := flag.String("pkbin", "proving_key.go.bin", "provingKey Bin path")
flag.Parse()
@@ -42,6 +44,12 @@ func main() {
fmt.Println("Error:", err)
}
os.Exit(0)
} else if *convert {
err := cmdConvert(*provingKeyPath, *provingKeyBinPath)
if err != nil {
fmt.Println("Error:", err)
}
os.Exit(0)
}
flag.PrintDefaults()
}
@@ -133,3 +141,25 @@ func cmdVerify(proofPath, verificationKeyPath, publicPath string) error {
fmt.Println("verification:", v)
return nil
}
func cmdConvert(provingKeyPath, provingKeyBinPath string) error {
fmt.Println("Convertion tool")
provingKeyJson, err := ioutil.ReadFile(provingKeyPath)
if err != nil {
return err
}
pk, err := parsers.ParsePk(provingKeyJson)
if err != nil {
return err
}
fmt.Printf("Converting proving key json (%s)\nto go proving key binary (%s)\n", provingKeyPath, provingKeyBinPath)
pkGBin, err := parsers.PkToGoBin(pk)
if err != nil {
return err
}
err = ioutil.WriteFile(provingKeyBinPath, pkGBin, 0644)
return nil
}

View File

@@ -3,6 +3,7 @@ package parsers
import (
"bufio"
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
@@ -308,6 +309,13 @@ func stringToBigInt(s string) (*big.Int, error) {
return n, nil
}
func addPadding32(b []byte) []byte {
if len(b) != 32 {
b = addZPadding(b)
}
return b
}
func addZPadding(b []byte) []byte {
var z [32]byte
var r []byte
@@ -599,3 +607,715 @@ func swapEndianness(b []byte) []byte {
}
return o
}
func readNBytes(r io.Reader, n int) ([]byte, error) {
b := make([]byte, n)
_, err := io.ReadFull(r, b)
if err != nil {
return b, err
}
return b, nil
}
// ParsePkBin parses binary file representation of the ProvingKey into the ProvingKey struct
func ParsePkBin(f *os.File) (*types.Pk, error) {
o := 0
var pk types.Pk
r := bufio.NewReader(f)
b, err := readNBytes(r, 12)
if err != nil {
return nil, err
}
pk.NVars = int(binary.LittleEndian.Uint32(b[:4]))
pk.NPublic = int(binary.LittleEndian.Uint32(b[4:8]))
pk.DomainSize = int(binary.LittleEndian.Uint32(b[8:12]))
o += 12
b, err = readNBytes(r, 8)
if err != nil {
return nil, err
}
pPolsA := int(binary.LittleEndian.Uint32(b[:4]))
pPolsB := int(binary.LittleEndian.Uint32(b[4:8]))
o += 8
b, err = readNBytes(r, 20)
if err != nil {
return nil, err
}
pPointsA := int(binary.LittleEndian.Uint32(b[:4]))
pPointsB1 := int(binary.LittleEndian.Uint32(b[4:8]))
pPointsB2 := int(binary.LittleEndian.Uint32(b[8:12]))
pPointsC := int(binary.LittleEndian.Uint32(b[12:16]))
pPointsHExps := int(binary.LittleEndian.Uint32(b[16:20]))
o += 20
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
pk.VkAlpha1 = new(bn256.G1)
_, err = pk.VkAlpha1.Unmarshal(fromMont1Q(b))
if err != nil {
return nil, err
}
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
pk.VkBeta1 = new(bn256.G1)
_, err = pk.VkBeta1.Unmarshal(fromMont1Q(b))
if err != nil {
return nil, err
}
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
pk.VkDelta1 = new(bn256.G1)
_, err = pk.VkDelta1.Unmarshal(fromMont1Q(b))
if err != nil {
return nil, err
}
b, err = readNBytes(r, 128)
if err != nil {
return nil, err
}
pk.VkBeta2 = new(bn256.G2)
_, err = pk.VkBeta2.Unmarshal(fromMont2Q(b))
if err != nil {
return nil, err
}
b, err = readNBytes(r, 128)
if err != nil {
return nil, err
}
pk.VkDelta2 = new(bn256.G2)
_, err = pk.VkDelta2.Unmarshal(fromMont2Q(b))
if err != nil {
return nil, err
}
o += 448
if o != pPolsA {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsA, o)
}
// PolsA
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 4)
if err != nil {
return nil, err
}
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
o += 4
polsMap := make(map[int]*big.Int)
for j := 0; j < keysLength; j++ {
bK, err := readNBytes(r, 4)
if err != nil {
return nil, err
}
key := int(binary.LittleEndian.Uint32(bK[:4]))
o += 4
b, err := readNBytes(r, 32)
if err != nil {
return nil, err
}
polsMap[key] = new(big.Int).SetBytes(fromMont1R(b[:32]))
o += 32
}
pk.PolsA = append(pk.PolsA, polsMap)
}
if o != pPolsB {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsB, o)
}
// PolsB
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 4)
if err != nil {
return nil, err
}
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
o += 4
polsMap := make(map[int]*big.Int)
for j := 0; j < keysLength; j++ {
bK, err := readNBytes(r, 4)
if err != nil {
return nil, err
}
key := int(binary.LittleEndian.Uint32(bK[:4]))
o += 4
b, err := readNBytes(r, 32)
if err != nil {
return nil, err
}
polsMap[key] = new(big.Int).SetBytes(fromMont1R(b[:32]))
o += 32
}
pk.PolsB = append(pk.PolsB, polsMap)
}
if o != pPointsA {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsA, o)
}
// A
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(fromMont1Q(b))
if err != nil {
return nil, err
}
pk.A = append(pk.A, p1)
o += 64
}
if o != pPointsB1 {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB1, o)
}
// B1
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(fromMont1Q(b))
if err != nil {
return nil, err
}
pk.B1 = append(pk.B1, p1)
o += 64
}
if o != pPointsB2 {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB2, o)
}
// B2
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 128)
if err != nil {
return nil, err
}
p2 := new(bn256.G2)
_, err = p2.Unmarshal(fromMont2Q(b))
if err != nil {
return nil, err
}
pk.B2 = append(pk.B2, p2)
o += 128
}
if o != pPointsC {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsC, o)
}
// C
zb := make([]byte, 64)
z := new(bn256.G1)
_, err = z.Unmarshal(zb)
if err != nil {
return nil, err
}
pk.C = append(pk.C, z) // circom behaviour (3x null==["0", "0", "0"])
pk.C = append(pk.C, z)
pk.C = append(pk.C, z)
for i := pk.NPublic + 1; i < pk.NVars; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(fromMont1Q(b))
if err != nil {
return nil, err
}
pk.C = append(pk.C, p1)
o += 64
}
if o != pPointsHExps {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsHExps, o)
}
// HExps
for i := 0; i < pk.DomainSize; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(fromMont1Q(b))
if err != nil {
return nil, err
}
pk.HExps = append(pk.HExps, p1)
}
return &pk, nil
}
func fromMont1Q(m []byte) []byte {
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
b := new(big.Int).SetBytes(swapEndianness(m[32:64]))
x := coordFromMont(a, types.Q)
y := coordFromMont(b, types.Q)
if bytes.Equal(x.Bytes(), big.NewInt(1).Bytes()) {
x = big.NewInt(0)
}
if bytes.Equal(y.Bytes(), big.NewInt(1).Bytes()) {
y = big.NewInt(0)
}
xBytes := x.Bytes()
yBytes := y.Bytes()
if len(xBytes) != 32 {
xBytes = addZPadding(xBytes)
}
if len(yBytes) != 32 {
yBytes = addZPadding(yBytes)
}
var p []byte
p = append(p, xBytes...)
p = append(p, yBytes...)
return p
}
func fromMont2Q(m []byte) []byte {
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
b := new(big.Int).SetBytes(swapEndianness(m[32:64]))
c := new(big.Int).SetBytes(swapEndianness(m[64:96]))
d := new(big.Int).SetBytes(swapEndianness(m[96:128]))
x := coordFromMont(a, types.Q)
y := coordFromMont(b, types.Q)
z := coordFromMont(c, types.Q)
t := coordFromMont(d, types.Q)
if bytes.Equal(x.Bytes(), big.NewInt(1).Bytes()) {
x = big.NewInt(0)
}
if bytes.Equal(y.Bytes(), big.NewInt(1).Bytes()) {
y = big.NewInt(0)
}
if bytes.Equal(z.Bytes(), big.NewInt(1).Bytes()) {
z = big.NewInt(0)
}
if bytes.Equal(t.Bytes(), big.NewInt(1).Bytes()) {
t = big.NewInt(0)
}
xBytes := x.Bytes()
yBytes := y.Bytes()
zBytes := z.Bytes()
tBytes := t.Bytes()
if len(xBytes) != 32 {
xBytes = addZPadding(xBytes)
}
if len(yBytes) != 32 {
yBytes = addZPadding(yBytes)
}
if len(zBytes) != 32 {
zBytes = addZPadding(zBytes)
}
if len(tBytes) != 32 {
tBytes = addZPadding(tBytes)
}
var p []byte
p = append(p, yBytes...) // swap
p = append(p, xBytes...)
p = append(p, tBytes...)
p = append(p, zBytes...)
return p
}
func fromMont1R(m []byte) []byte {
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
x := coordFromMont(a, types.R)
return x.Bytes()
}
func fromMont2R(m []byte) []byte {
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
b := new(big.Int).SetBytes(swapEndianness(m[32:64]))
c := new(big.Int).SetBytes(swapEndianness(m[64:96]))
d := new(big.Int).SetBytes(swapEndianness(m[96:128]))
x := coordFromMont(a, types.R)
y := coordFromMont(b, types.R)
z := coordFromMont(c, types.R)
t := coordFromMont(d, types.R)
var p []byte
p = append(p, y.Bytes()...) // swap
p = append(p, x.Bytes()...)
p = append(p, t.Bytes()...)
p = append(p, z.Bytes()...)
return p
}
func coordFromMont(u, q *big.Int) *big.Int {
return new(big.Int).Mod(
new(big.Int).Mul(
u,
new(big.Int).ModInverse(
new(big.Int).Lsh(big.NewInt(1), 256),
q,
),
),
q,
)
}
// PkToGoBin converts the ProvingKey (*types.Pk) into binary format defined by
// go-circom-prover-verifier. PkGoBin is a own go-circom-prover-verifier
// binary format that allows to go faster when parsing.
func PkToGoBin(pk *types.Pk) ([]byte, error) {
var r []byte
o := 0
var b [4]byte
binary.LittleEndian.PutUint32(b[:], uint32(pk.NVars))
r = append(r, b[:]...)
binary.LittleEndian.PutUint32(b[:], uint32(pk.NPublic))
r = append(r, b[:]...)
binary.LittleEndian.PutUint32(b[:], uint32(pk.DomainSize))
r = append(r, b[:]...)
o += 12
// reserve space for pols (A, B) pos
b = [4]byte{}
r = append(r, b[:]...) // 12:16
r = append(r, b[:]...) // 16:20
o += 8
// reserve space for points (A, B1, B2, C, HExps) pos
r = append(r, b[:]...) // 20:24
r = append(r, b[:]...) // 24
r = append(r, b[:]...) // 28
r = append(r, b[:]...) // 32
r = append(r, b[:]...) // 36:40
o += 20
pb1 := pk.VkAlpha1.Marshal()
r = append(r, pb1[:]...)
pb1 = pk.VkBeta1.Marshal()
r = append(r, pb1[:]...)
pb1 = pk.VkDelta1.Marshal()
r = append(r, pb1[:]...)
pb2 := pk.VkBeta2.Marshal()
r = append(r, pb2[:]...)
pb2 = pk.VkDelta2.Marshal()
r = append(r, pb2[:]...)
o += 448
// polsA
binary.LittleEndian.PutUint32(r[12:16], uint32(o))
for i := 0; i < pk.NVars; i++ {
binary.LittleEndian.PutUint32(b[:], uint32(len(pk.PolsA[i])))
r = append(r, b[:]...)
o += 4
for j, v := range pk.PolsA[i] {
binary.LittleEndian.PutUint32(b[:], uint32(j))
r = append(r, b[:]...)
r = append(r, addPadding32(v.Bytes())...)
o += 32 + 4
}
}
// polsB
binary.LittleEndian.PutUint32(r[16:20], uint32(o))
for i := 0; i < pk.NVars; i++ {
binary.LittleEndian.PutUint32(b[:], uint32(len(pk.PolsB[i])))
r = append(r, b[:]...)
o += 4
for j, v := range pk.PolsB[i] {
binary.LittleEndian.PutUint32(b[:], uint32(j))
r = append(r, b[:]...)
r = append(r, addPadding32(v.Bytes())...)
o += 32 + 4
}
}
// A
binary.LittleEndian.PutUint32(r[20:24], uint32(o))
for i := 0; i < pk.NVars; i++ {
pb1 = pk.A[i].Marshal()
r = append(r, pb1[:]...)
o += 64
}
// B1
binary.LittleEndian.PutUint32(r[24:28], uint32(o))
for i := 0; i < pk.NVars; i++ {
pb1 = pk.B1[i].Marshal()
r = append(r, pb1[:]...)
o += 64
}
// B2
binary.LittleEndian.PutUint32(r[28:32], uint32(o))
for i := 0; i < pk.NVars; i++ {
pb2 = pk.B2[i].Marshal()
r = append(r, pb2[:]...)
o += 128
}
// C
binary.LittleEndian.PutUint32(r[32:36], uint32(o))
for i := pk.NPublic + 1; i < pk.NVars; i++ {
pb1 = pk.C[i].Marshal()
r = append(r, pb1[:]...)
o += 64
}
// HExps
binary.LittleEndian.PutUint32(r[36:40], uint32(o))
for i := 0; i < pk.DomainSize+1; i++ {
pb1 = pk.HExps[i].Marshal()
r = append(r, pb1[:]...)
o += 64
}
return r[:], nil
}
// ParsePkGoBin parses go-circom-prover-verifier binary file representation of
// the ProvingKey into ProvingKey struct (*types.Pk). PkGoBin is a own
// go-circom-prover-verifier binary format that allows to go faster when
// parsing.
func ParsePkGoBin(f *os.File) (*types.Pk, error) {
o := 0
var pk types.Pk
r := bufio.NewReader(f)
b, err := readNBytes(r, 12)
if err != nil {
return nil, err
}
pk.NVars = int(binary.LittleEndian.Uint32(b[:4]))
pk.NPublic = int(binary.LittleEndian.Uint32(b[4:8]))
pk.DomainSize = int(binary.LittleEndian.Uint32(b[8:12]))
o += 12
b, err = readNBytes(r, 8)
if err != nil {
return nil, err
}
pPolsA := int(binary.LittleEndian.Uint32(b[:4]))
pPolsB := int(binary.LittleEndian.Uint32(b[4:8]))
o += 8
b, err = readNBytes(r, 20)
if err != nil {
return nil, err
}
pPointsA := int(binary.LittleEndian.Uint32(b[:4]))
pPointsB1 := int(binary.LittleEndian.Uint32(b[4:8]))
pPointsB2 := int(binary.LittleEndian.Uint32(b[8:12]))
pPointsC := int(binary.LittleEndian.Uint32(b[12:16]))
pPointsHExps := int(binary.LittleEndian.Uint32(b[16:20]))
o += 20
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
pk.VkAlpha1 = new(bn256.G1)
_, err = pk.VkAlpha1.Unmarshal(b)
if err != nil {
return &pk, err
}
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
pk.VkBeta1 = new(bn256.G1)
_, err = pk.VkBeta1.Unmarshal(b)
if err != nil {
return &pk, err
}
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
pk.VkDelta1 = new(bn256.G1)
_, err = pk.VkDelta1.Unmarshal(b)
if err != nil {
return &pk, err
}
b, err = readNBytes(r, 128)
if err != nil {
return nil, err
}
pk.VkBeta2 = new(bn256.G2)
_, err = pk.VkBeta2.Unmarshal(b)
if err != nil {
return &pk, err
}
b, err = readNBytes(r, 128)
if err != nil {
return nil, err
}
pk.VkDelta2 = new(bn256.G2)
_, err = pk.VkDelta2.Unmarshal(b)
if err != nil {
return &pk, err
}
o += 448
if o != pPolsA {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsA, o)
}
// PolsA
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 4)
if err != nil {
return nil, err
}
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
o += 4
polsMap := make(map[int]*big.Int)
for j := 0; j < keysLength; j++ {
bK, err := readNBytes(r, 4)
if err != nil {
return nil, err
}
key := int(binary.LittleEndian.Uint32(bK[:4]))
o += 4
b, err := readNBytes(r, 32)
if err != nil {
return nil, err
}
polsMap[key] = new(big.Int).SetBytes(b[:32])
o += 32
}
pk.PolsA = append(pk.PolsA, polsMap)
}
if o != pPolsB {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsB, o)
}
// PolsB
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 4)
if err != nil {
return nil, err
}
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
o += 4
polsMap := make(map[int]*big.Int)
for j := 0; j < keysLength; j++ {
bK, err := readNBytes(r, 4)
if err != nil {
return nil, err
}
key := int(binary.LittleEndian.Uint32(bK[:4]))
o += 4
b, err := readNBytes(r, 32)
if err != nil {
return nil, err
}
polsMap[key] = new(big.Int).SetBytes(b[:32])
o += 32
}
pk.PolsB = append(pk.PolsB, polsMap)
}
if o != pPointsA {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsA, o)
}
// A
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(b)
if err != nil {
return nil, err
}
pk.A = append(pk.A, p1)
o += 64
}
if o != pPointsB1 {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB1, o)
}
// B1
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(b)
if err != nil {
return nil, err
}
pk.B1 = append(pk.B1, p1)
o += 64
}
if o != pPointsB2 {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB2, o)
}
// B2
for i := 0; i < pk.NVars; i++ {
b, err = readNBytes(r, 128)
if err != nil {
return nil, err
}
p2 := new(bn256.G2)
_, err = p2.Unmarshal(b)
if err != nil {
return nil, err
}
pk.B2 = append(pk.B2, p2)
o += 128
}
if o != pPointsC {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsC, o)
}
// C
zb := make([]byte, 64)
z := new(bn256.G1)
_, err = z.Unmarshal(zb)
if err != nil {
return nil, err
}
pk.C = append(pk.C, z)
pk.C = append(pk.C, z)
pk.C = append(pk.C, z)
for i := pk.NPublic + 1; i < pk.NVars; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(b)
if err != nil {
return nil, err
}
pk.C = append(pk.C, p1)
o += 64
}
if o != pPointsHExps {
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsHExps, o)
}
// HExps
for i := 0; i < pk.DomainSize+1; i++ {
b, err = readNBytes(r, 64)
if err != nil {
return nil, err
}
p1 := new(bn256.G1)
_, err = p1.Unmarshal(b)
if err != nil {
return nil, err
}
pk.HExps = append(pk.HExps, p1)
}
return &pk, nil
}

View File

@@ -211,3 +211,115 @@ func TestProofJSON(t *testing.T) {
require.Nil(t, err)
require.Equal(t, *proof, proof1)
}
func testCircuitParsePkBin(t *testing.T, circuit string) {
pkBinFile, err := os.Open("../testdata/" + circuit + "/proving_key.bin")
require.Nil(t, err)
defer pkBinFile.Close()
pk, err := ParsePkBin(pkBinFile)
require.Nil(t, err)
pkJson, err := ioutil.ReadFile("../testdata/" + circuit + "/proving_key.json")
require.Nil(t, err)
pkJ, err := ParsePk(pkJson)
require.Nil(t, err)
assert.Equal(t, pkJ.NVars, pk.NVars)
assert.Equal(t, pkJ.NPublic, pk.NPublic)
assert.Equal(t, pkJ.DomainSize, pk.DomainSize)
assert.Equal(t, pkJ.VkAlpha1, pk.VkAlpha1)
assert.Equal(t, pkJ.VkBeta1, pk.VkBeta1)
assert.Equal(t, pkJ.VkDelta1, pk.VkDelta1)
assert.Equal(t, pkJ.VkDelta2, pk.VkDelta2)
assert.Equal(t, pkJ.PolsA, pk.PolsA)
assert.Equal(t, pkJ.PolsB, pk.PolsB)
assert.Equal(t, pkJ.A, pk.A)
assert.Equal(t, pkJ.B1, pk.B1)
assert.Equal(t, pkJ.B2, pk.B2)
assert.Equal(t, pkJ.C, pk.C)
assert.Equal(t, pkJ.HExps[:pkJ.DomainSize], pk.HExps[:pk.DomainSize]) // circom behaviour
}
func TestParsePkBin(t *testing.T) {
testCircuitParsePkBin(t, "circuit1k")
testCircuitParsePkBin(t, "circuit5k")
}
func testGoCircomPkFormat(t *testing.T, circuit string) {
pkJson, err := ioutil.ReadFile("../testdata/" + circuit + "/proving_key.json")
require.Nil(t, err)
pk, err := ParsePk(pkJson)
require.Nil(t, err)
pkGBin, err := PkToGoBin(pk)
require.Nil(t, err)
err = ioutil.WriteFile("../testdata/"+circuit+"/proving_key.go.bin", pkGBin, 0644)
assert.Nil(t, err)
// parse ProvingKeyGo
pkGoBinFile, err := os.Open("../testdata/" + circuit + "/proving_key.go.bin")
require.Nil(t, err)
defer pkGoBinFile.Close()
pkG, err := ParsePkGoBin(pkGoBinFile)
require.Nil(t, err)
assert.Equal(t, pk.VkAlpha1, pkG.VkAlpha1)
assert.Equal(t, pk.VkBeta1, pkG.VkBeta1)
assert.Equal(t, pk.VkDelta1, pkG.VkDelta1)
assert.Equal(t, pk.VkBeta2, pkG.VkBeta2)
assert.Equal(t, pk.VkDelta2, pkG.VkDelta2)
assert.Equal(t, pk.A, pkG.A)
assert.Equal(t, pk.B1, pkG.B1)
assert.Equal(t, pk.B2, pkG.B2)
assert.Equal(t, pk.C, pkG.C)
assert.Equal(t, pk.HExps, pkG.HExps)
assert.Equal(t, pk.PolsA, pkG.PolsA)
assert.Equal(t, pk.PolsB, pkG.PolsB)
}
func TestGoCircomPkFormat(t *testing.T) {
testGoCircomPkFormat(t, "circuit1k")
testGoCircomPkFormat(t, "circuit5k")
// testGoCircomPkFormat(t, "circuit10k")
// testGoCircomPkFormat(t, "circuit20k")
}
func benchmarkParsePk(b *testing.B, circuit string) {
pkJson, err := ioutil.ReadFile("../testdata/" + circuit + "/proving_key.json")
require.Nil(b, err)
pkBinFile, err := os.Open("../testdata/" + circuit + "/proving_key.bin")
require.Nil(b, err)
defer pkBinFile.Close()
pkGoBinFile, err := os.Open("../testdata/" + circuit + "/proving_key.go.bin")
require.Nil(b, err)
defer pkGoBinFile.Close()
b.Run("ParsePkJson "+circuit, func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err = ParsePk(pkJson)
require.Nil(b, err)
}
})
b.Run("ParsePkBin "+circuit, func(b *testing.B) {
for i := 0; i < b.N; i++ {
pkBinFile.Seek(0, 0)
_, err = ParsePkBin(pkBinFile)
require.Nil(b, err)
}
})
b.Run("ParsePkGoBin "+circuit, func(b *testing.B) {
for i := 0; i < b.N; i++ {
pkGoBinFile.Seek(0, 0)
_, err = ParsePkGoBin(pkGoBinFile)
require.Nil(b, err)
}
})
}
func BenchmarkParsePk(b *testing.B) {
benchmarkParsePk(b, "circuit1k")
benchmarkParsePk(b, "circuit5k")
// benchmarkParsePk(b, "circuit10k")
// benchmarkParsePk(b, "circuit20k")
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"testing"
"time"
@@ -16,25 +17,45 @@ import (
func TestCircuitsGenerateProof(t *testing.T) {
testCircuitGenerateProof(t, "circuit1k") // 1000 constraints
testCircuitGenerateProof(t, "circuit5k") // 5000 constraints
//testCircuitGenerateProof(t, "circuit10k") // 10000 constraints
//testCircuitGenerateProof(t, "circuit20k") // 20000 constraints
// testCircuitGenerateProof(t, "circuit10k") // 10000 constraints
// testCircuitGenerateProof(t, "circuit20k") // 20000 constraints
}
func testCircuitGenerateProof(t *testing.T, circuit string) {
provingKeyJson, err := ioutil.ReadFile("../testdata/" + circuit + "/proving_key.json")
// Using json provingKey file:
// provingKeyJson, err := ioutil.ReadFile("../testdata/" + circuit + "/proving_key.json")
// require.Nil(t, err)
// pk, err := parsers.ParsePk(provingKeyJson)
// require.Nil(t, err)
// witnessJson, err := ioutil.ReadFile("../testdata/" + circuit + "/witness.json")
// require.Nil(t, err)
// w, err := parsers.ParseWitness(witnessJson)
// require.Nil(t, err)
// Using bin provingKey file:
// pkBinFile, err := os.Open("../testdata/" + circuit + "/proving_key.bin")
// require.Nil(t, err)
// defer pkBinFile.Close()
// pk, err := parsers.ParsePkBin(pkBinFile)
// require.Nil(t, err)
// Using go bin provingKey file:
pkGoBinFile, err := os.Open("../testdata/" + circuit + "/proving_key.go.bin")
require.Nil(t, err)
pk, err := parsers.ParsePk(provingKeyJson)
defer pkGoBinFile.Close()
pk, err := parsers.ParsePkGoBin(pkGoBinFile)
require.Nil(t, err)
witnessJson, err := ioutil.ReadFile("../testdata/" + circuit + "/witness.json")
witnessBinFile, err := os.Open("../testdata/" + circuit + "/witness.bin")
require.Nil(t, err)
w, err := parsers.ParseWitness(witnessJson)
defer witnessBinFile.Close()
w, err := parsers.ParseWitnessBin(witnessBinFile)
require.Nil(t, err)
beforeT := time.Now()
proof, pubSignals, err := GenerateProof(pk, w)
assert.Nil(t, err)
fmt.Println("proof generation time elapsed:", time.Since(beforeT))
fmt.Println("proof generation time for "+circuit+" elapsed:", time.Since(beforeT))
proofStr, err := parsers.ProofToJson(proof)
assert.Nil(t, err)

View File

@@ -7,3 +7,4 @@ rm */*.cpp
rm */*.sym
rm */*.r1cs
rm */*.sol
rm */*.bin

View File

@@ -45,19 +45,23 @@ compile_and_ts_and_witness
cd ../
echo "convert witness & pk of circuit1k to bin"
echo "convert witness & pk of circuit1k to bin & go bin"
node node_modules/wasmsnark/tools/buildwitness.js -i circuit1k/witness.json -o circuit1k/witness.bin
node node_modules/wasmsnark/tools/buildpkey.js -i circuit1k/proving_key.json -o circuit1k/proving_key.bin
go run ../cli/cli.go -convert -pk circuit1k/proving_key.json -pkbin circuit1k/proving_key.go.bin
echo "convert witness & pk of circuit5k to bin"
echo "convert witness & pk of circuit5k to bin & go bin"
node node_modules/wasmsnark/tools/buildwitness.js -i circuit5k/witness.json -o circuit5k/witness.bin
node node_modules/wasmsnark/tools/buildpkey.js -i circuit5k/proving_key.json -o circuit5k/proving_key.bin
go run ../cli/cli.go -convert -pk circuit5k/proving_key.json -pkbin circuit5k/proving_key.go.bin
# echo "convert witness & pk of circuit10k to bin"
# echo "convert witness & pk of circuit10k to bin & go bin"
# node node_modules/wasmsnark/tools/buildwitness.js -i circuit10k/witness.json -o circuit10k/witness.bin
# node node_modules/wasmsnark/tools/buildpkey.js -i circuit10k/proving_key.json -o circuit10k/proving_key.bin
# go run ../cli/cli.go -convert -pk circuit10k/proving_key.json -pkbin circuit10k/proving_key.go.bin
#
# echo "convert witness & pk of circuit20k to bin"
# echo "convert witness & pk of circuit20k to bin & go bin"
# node node_modules/wasmsnark/tools/buildwitness.js -i circuit20k/witness.json -o circuit20k/witness.bin
# node node_modules/wasmsnark/tools/buildpkey.js -i circuit20k/proving_key.json -o circuit20k/proving_key.bin
# go run ../cli/cli.go -convert -pk circuit20k/proving_key.json -pkbin circuit20k/proving_key.go.bin

View File

@@ -8,6 +8,8 @@ import (
bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare"
)
var Q, _ = new(big.Int).SetString("21888242871839275222246405745257275088696311157297823662689037894645226208583", 10)
// R is the mod of the finite field
var R, _ = new(big.Int).SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10)