mirror of
https://github.com/arnaucube/go-circom-prover-verifier.git
synced 2026-02-06 19:06:43 +01:00
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
This commit is contained in:
@@ -3,6 +3,8 @@ package prover
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
|
||||
"github.com/iden3/go-iden3-crypto/ff"
|
||||
)
|
||||
|
||||
func arrayOfZeroes(n int) []*big.Int {
|
||||
@@ -12,6 +14,13 @@ func arrayOfZeroes(n int) []*big.Int {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func fAdd(a, b *big.Int) *big.Int {
|
||||
ab := new(big.Int).Add(a, b)
|
||||
@@ -75,6 +84,17 @@ func polynomialSub(a, b []*big.Int) []*big.Int {
|
||||
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 @@ func polynomialMul(a, b []*big.Int) []*big.Int {
|
||||
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 @@ func polynomialDiv(a, b []*big.Int) ([]*big.Int, []*big.Int) {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
66
prover/arithmetic_test.go
Normal file
66
prover/arithmetic_test.go
Normal file
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -3,11 +3,13 @@ package prover
|
||||
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 @@ func newRootsT() rootsT {
|
||||
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 newRootsT() rootsT {
|
||||
|
||||
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 @@ func fft(roots rootsT, pall []*big.Int, bits, offset, step int) []*big.Int {
|
||||
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 @@ func ifft(p []*big.Int) []*big.Int {
|
||||
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...)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
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 @@ func calculateH(pk *types.Pk, w types.Witness) []*big.Int {
|
||||
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)
|
||||
|
||||
polABS := polynomialMul(polAS, polBS)
|
||||
polCS := ifft(polCT)
|
||||
polABCS := polynomialSub(polABS, polCS)
|
||||
polASe := ifft(polATe)
|
||||
polBSe := ifft(polBTe)
|
||||
polABSe := polynomialMulE(polASe, polBSe)
|
||||
|
||||
hS := polABCS[m:]
|
||||
return hS
|
||||
polCSe := ifft(polCTe)
|
||||
|
||||
polABCSe := polynomialSubE(polABSe, polCSe)
|
||||
|
||||
hSe := polABCSe[m:]
|
||||
return ElementArrayToBigIntArray(hSe)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user