arnaucube
arnaucube/go-circom-prover-verifierPublic Self-hosted repository
Add polynomials arithmetic in goff

Polynomials and ifft moved to goff (iden3/go-iden3-crypto/ff) instead of *big.Int.

Benchmarks:

- Before:
BenchmarkArithmetic/polynomialSub-4         	    2774	    441063 ns/op
BenchmarkArithmetic/polynomialMul-4         	       1	1135732757 ns/op
BenchmarkArithmetic/polynomialDiv-4         	     768	   1425192 ns/op
BenchmarkGenerateProof-4                    	       1	2844488975 ns/op

- With this commit:
BenchmarkArithmetic/polynomialSubE-4        	   23097	     54152 ns/op
BenchmarkArithmetic/polynomialMulE-4        	      25	  44914327 ns/op
BenchmarkArithmetic/polynomialDivE-4        	    8703	    132573 ns/op
BenchmarkGenerateProof-4                    	       1	1530398526 ns/op
arnaucube root@arnaucube.com
Mon, 20 Apr 2020 12:37:49 +0200
commit

324c817d42d7570b2d0e0ff161eb7345dae3970b

parent

3f5f8e23188a42cdf9ca1a818f03c46e3abd5b1e

7 files changed, 178 insertions(+), 33 deletions(-)

jump to
M README.md → README.md

@@ -9,6 +9,14 @@ ### Example

- Generate Proof ```go +import ( + "github.com/iden3/go-circom-prover-verifier/parsers" + "github.com/iden3/go-circom-prover-verifier/prover" + "github.com/iden3/go-circom-prover-verifier/verifier" +) + +[...] + // read ProvingKey & Witness files provingKeyJson, _ := ioutil.ReadFile("../testdata/small/proving_key.json") witnessJson, _ := ioutil.ReadFile("../testdata/small/witness.json")

@@ -20,7 +28,7 @@ // parse Witness

w, _ := parsers.ParseWitness(witnessJson) // generate the proof -proof, pubSignals, _ := GenerateProof(pk, w) +proof, pubSignals, _ := prover.GenerateProof(pk, w) // print proof & publicSignals proofStr, _ := parsers.ProofToJson(proof)

@@ -42,7 +50,7 @@ proof, _ := parsers.ParseProof(proofJson)

vk, _ := parsers.ParseVk(vkJson) // verify the proof with the given verificationKey & publicSignals -v := Verify(vk, proof, public) +v := verifier.Verify(vk, proof, public) fmt.Println(v) ```
M go.mod → go.mod

@@ -7,6 +7,7 @@

require ( github.com/ethereum/go-ethereum v1.9.12 github.com/iden3/go-circom-prover-verifier v0.0.0-00010101000000-000000000000 + github.com/iden3/go-iden3-crypto v0.0.4 github.com/stretchr/testify v1.5.1 golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 )
M go.sum → go.sum

@@ -32,6 +32,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=

github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dchest/blake512 v1.0.0/go.mod h1:FV1x7xPPLWukZlpDpWQ88rF/SFwZ5qbskrzhLMB92JI= github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=

@@ -40,6 +41,7 @@ github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=

github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.8.1-0.20180330100440-37f05ff46ffa/go.mod h1:cdorVVzy1fhmEqmtgqkoE3bYtCfSCkVyjTyCIo22xvs= +github.com/ethereum/go-ethereum v1.8.27/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHaJv9Ln3kPCZLwY= github.com/ethereum/go-ethereum v1.9.12 h1:EPtimwsp/KGDSiXcNunzsI4kefdsMHZGJntKx3fvbaI= github.com/ethereum/go-ethereum v1.9.12/go.mod h1:PvsVkQmhZFx92Y+h2ylythYlheEDt/uBgFbl61Js/jo= github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=

@@ -62,6 +64,8 @@ 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/iden3/go-iden3-crypto v0.0.4 h1:rGQEFBvX6d4fDxqkQTizVq5UefB+xdZAg8j5FQ6uv6g= +github.com/iden3/go-iden3-crypto v0.0.4/go.mod h1:LLcgB7DLWAUs+8eBSKne+ZHy5z7xtAmlYlEz0M9M8gE= 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=

@@ -120,6 +124,7 @@ github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=

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-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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=
M prover/arithmetic.go → prover/arithmetic.go

@@ -3,12 +3,21 @@

import ( "bytes" "math/big" + + "github.com/iden3/go-iden3-crypto/ff" ) func arrayOfZeroes(n int) []*big.Int { var r []*big.Int for i := 0; i < n; i++ { r = append(r, new(big.Int).SetInt64(0)) + } + return r +} +func arrayOfZeroesE(n int) []*ff.Element { + var r []*ff.Element + for i := 0; i < n; i++ { + r = append(r, ff.NewElement()) } return r }

@@ -75,6 +84,17 @@ }

return r } +func polynomialSubE(a, b []*ff.Element) []*ff.Element { + r := arrayOfZeroesE(max(len(a), len(b))) + for i := 0; i < len(a); i++ { + r[i].Add(r[i], a[i]) + } + for i := 0; i < len(b); i++ { + r[i].Sub(r[i], b[i]) + } + return r +} + func polynomialMul(a, b []*big.Int) []*big.Int { r := arrayOfZeroes(len(a) + len(b) - 1) for i := 0; i < len(a); i++ {

@@ -85,6 +105,16 @@ }

return r } +func polynomialMulE(a, b []*ff.Element) []*ff.Element { + r := arrayOfZeroesE(len(a) + len(b) - 1) + for i := 0; i < len(a); i++ { + for j := 0; j < len(b); j++ { + r[i+j].Add(r[i+j], ff.NewElement().Mul(a[i], b[j])) + } + } + return r +} + func polynomialDiv(a, b []*big.Int) ([]*big.Int, []*big.Int) { // https://en.wikipedia.org/wiki/Division_algorithm r := arrayOfZeroes(len(a) - len(b) + 1)

@@ -100,3 +130,31 @@ rem = aux2[:len(aux2)-1]

} return r, rem } + +func polynomialDivE(a, b []*ff.Element) ([]*ff.Element, []*ff.Element) { + // https://en.wikipedia.org/wiki/Division_algorithm + r := arrayOfZeroesE(len(a) - len(b) + 1) + rem := a + for len(rem) >= len(b) { + l := ff.NewElement().Div(rem[len(rem)-1], b[len(b)-1]) + pos := len(rem) - len(b) + r[pos] = l + aux := arrayOfZeroesE(pos) + aux1 := append(aux, l) + aux2 := polynomialSubE(rem, polynomialMulE(b, aux1)) + rem = aux2[:len(aux2)-1] + } + return r, rem +} + +// once https://github.com/iden3/go-iden3-crypto/pull/22 is merged, use the fucntion from there +func ElementArrayToBigIntArray(e []*ff.Element) []*big.Int { + var o []*big.Int + for i := range e { + ei := e[i] + bi := big.NewInt(0) + ei.ToBigIntRegular(bi) + o = append(o, bi) + } + return o +}
A prover/arithmetic_test.go

@@ -0,0 +1,66 @@

+package prover + +import ( + "crypto/rand" + "math/big" + "testing" + + cryptoConstants "github.com/iden3/go-iden3-crypto/constants" + "github.com/iden3/go-iden3-crypto/utils" +) + +func randBI() *big.Int { + maxbits := 253 + b := make([]byte, (maxbits/8)-1) + _, err := rand.Read(b) + if err != nil { + panic(err) + } + r := new(big.Int).SetBytes(b) + return new(big.Int).Mod(r, cryptoConstants.Q) +} + +func BenchmarkArithmetic(b *testing.B) { + // generate arrays with bigint + var p, q []*big.Int + for i := 0; i < 1000; i++ { + pi := randBI() + p = append(p, pi) + } + for i := 1000 - 1; i >= 0; i-- { + q = append(q, p[i]) + } + pe := utils.BigIntArrayToElementArray(p) + qe := utils.BigIntArrayToElementArray(q) + + b.Run("polynomialSub", func(b *testing.B) { + for i := 0; i < b.N; i++ { + polynomialSub(p, q) + } + }) + b.Run("polynomialSubE", func(b *testing.B) { + for i := 0; i < b.N; i++ { + polynomialSubE(pe, qe) + } + }) + b.Run("polynomialMul", func(b *testing.B) { + for i := 0; i < b.N; i++ { + polynomialMul(p, q) + } + }) + b.Run("polynomialMulE", func(b *testing.B) { + for i := 0; i < b.N; i++ { + polynomialMulE(pe, qe) + } + }) + b.Run("polynomialDiv", func(b *testing.B) { + for i := 0; i < b.N; i++ { + polynomialDiv(p, q) + } + }) + b.Run("polynomialDivE", func(b *testing.B) { + for i := 0; i < b.N; i++ { + polynomialDivE(pe, qe) + } + }) +}
M prover/ifft.go → prover/ifft.go

@@ -3,11 +3,13 @@

import ( "math" "math/big" + + "github.com/iden3/go-iden3-crypto/ff" ) type rootsT struct { - roots [][]*big.Int - w []*big.Int + roots [][]*ff.Element + w []*ff.Element } func newRootsT() rootsT {

@@ -19,15 +21,15 @@ for rem.Bit(0) == 0 { // rem.Bit==0 when even

s++ rem = new(big.Int).Rsh(rem, 1) } - roots.w = make([]*big.Int, s+1) - roots.w[s] = fExp(big.NewInt(5), rem) + roots.w = make([]*ff.Element, s+1) + roots.w[s] = ff.NewElement().SetBigInt(fExp(big.NewInt(5), rem)) n := s - 1 for n >= 0 { - roots.w[n] = fMul(roots.w[n+1], roots.w[n+1]) + roots.w[n] = ff.NewElement().Mul(roots.w[n+1], roots.w[n+1]) n-- } - roots.roots = make([][]*big.Int, 50) // TODO WIP + roots.roots = make([][]*ff.Element, 50) // TODO WIP roots.setRoots(15) return roots

@@ -35,25 +37,25 @@ }

func (roots rootsT) setRoots(n int) { for i := n; i >= 0 && nil == roots.roots[i]; i-- { // TODO tmp i<=len(r) - r := big.NewInt(1) + r := ff.NewElement().SetBigInt(big.NewInt(1)) nroots := 1 << i - var rootsi []*big.Int + var rootsi []*ff.Element for j := 0; j < nroots; j++ { rootsi = append(rootsi, r) - r = fMul(r, roots.w[i]) + r = ff.NewElement().Mul(r, roots.w[i]) } roots.roots[i] = rootsi } } -func fft(roots rootsT, pall []*big.Int, bits, offset, step int) []*big.Int { +func fft(roots rootsT, pall []*ff.Element, bits, offset, step int) []*ff.Element { n := 1 << bits if n == 1 { - return []*big.Int{pall[offset]} + return []*ff.Element{pall[offset]} } else if n == 2 { - return []*big.Int{ - fAdd(pall[offset], pall[offset+step]), // TODO tmp - fSub(pall[offset], pall[offset+step]), + return []*ff.Element{ + ff.NewElement().Add(pall[offset], pall[offset+step]), // TODO tmp + ff.NewElement().Sub(pall[offset], pall[offset+step]), } }

@@ -61,17 +63,16 @@ ndiv2 := n >> 1

p1 := fft(roots, pall, bits-1, offset, step*2) p2 := fft(roots, pall, bits-1, offset+step, step*2) - // var out []*big.Int - out := make([]*big.Int, n) + out := make([]*ff.Element, n) for i := 0; i < ndiv2; i++ { // fmt.Println(i, len(roots.roots)) - out[i] = fAdd(p1[i], fMul(roots.roots[bits][i], p2[i])) - out[i+ndiv2] = fSub(p1[i], fMul(roots.roots[bits][i], p2[i])) + out[i] = ff.NewElement().Add(p1[i], ff.NewElement().Mul(roots.roots[bits][i], p2[i])) + out[i+ndiv2] = ff.NewElement().Sub(p1[i], ff.NewElement().Mul(roots.roots[bits][i], p2[i])) } return out } -func ifft(p []*big.Int) []*big.Int { +func ifft(p []*ff.Element) []*ff.Element { if len(p) <= 1 { return p }

@@ -82,20 +83,20 @@ m := 1 << int(bits)

ep := extend(p, m) res := fft(roots, ep, int(bits), 0, 1) - twoinvm := fInv(fMul(big.NewInt(1), big.NewInt(int64(m)))) + twoinvm := ff.NewElement().SetBigInt(fInv(fMul(big.NewInt(1), big.NewInt(int64(m))))) - var resn []*big.Int + var resn []*ff.Element for i := 0; i < m; i++ { - resn = append(resn, fMul(res[(m-i)%m], twoinvm)) + resn = append(resn, ff.NewElement().Mul(res[(m-i)%m], twoinvm)) } return resn } -func extend(p []*big.Int, e int) []*big.Int { +func extend(p []*ff.Element, e int) []*ff.Element { if e == len(p) { return p } - z := arrayOfZeroes(e - len(p)) + z := arrayOfZeroesE(e - len(p)) return append(p, z...) }
M prover/prover.go → prover/prover.go

@@ -6,6 +6,7 @@ "math/big"

bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare" "github.com/iden3/go-circom-prover-verifier/types" + "github.com/iden3/go-iden3-crypto/utils" ) // Proof is the data structure of the Groth16 zkSNARK proof

@@ -123,13 +124,18 @@ for j := range pk.PolsC[i] {

polCT[j] = fAdd(polCT[j], fMul(w[i], pk.PolsC[i][j])) } } - polAS := ifft(polAT) - polBS := ifft(polBT) + polATe := utils.BigIntArrayToElementArray(polAT) + polBTe := utils.BigIntArrayToElementArray(polBT) + polCTe := utils.BigIntArrayToElementArray(polCT) + + polASe := ifft(polATe) + polBSe := ifft(polBTe) + polABSe := polynomialMulE(polASe, polBSe) + + polCSe := ifft(polCTe) - polABS := polynomialMul(polAS, polBS) - polCS := ifft(polCT) - polABCS := polynomialSub(polABS, polCS) + polABCSe := polynomialSubE(polABSe, polCSe) - hS := polABCS[m:] - return hS + hSe := polABCSe[m:] + return ElementArrayToBigIntArray(hSe) }