Circom Verifier Proofs allow to verify through a zkSNARK proof the inclusion/exclusion of a leaf in a tree. This commit adds the needed code in go to generate the circuit inputs for a CircomVerifierProof.master
@ -0,0 +1,86 @@ |
|||||
|
package arbo |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
) |
||||
|
|
||||
|
// CircomVerifierProof contains the needed data to check a Circom Verifier Proof
|
||||
|
// inside a circom circuit. CircomVerifierProof allow to verify through a
|
||||
|
// zkSNARK proof the inclusion/exclusion of a leaf in a tree.
|
||||
|
type CircomVerifierProof struct { |
||||
|
Root []byte `json:"root"` |
||||
|
Siblings [][]byte `json:"siblings"` |
||||
|
OldKey []byte `json:"oldKey"` |
||||
|
OldValue []byte `json:"oldValue"` |
||||
|
IsOld0 bool `json:"isOld0"` |
||||
|
Key []byte `json:"key"` |
||||
|
Value []byte `json:"value"` |
||||
|
Fnc int `json:"fnc"` // 0: inclusion, 1: non inclusion
|
||||
|
} |
||||
|
|
||||
|
// MarshalJSON implements the JSON marshaler
|
||||
|
func (cvp CircomVerifierProof) MarshalJSON() ([]byte, error) { |
||||
|
m := make(map[string]interface{}) |
||||
|
|
||||
|
m["root"] = BytesToBigInt(cvp.Root).String() |
||||
|
m["siblings"] = siblingsToStringArray(cvp.Siblings) |
||||
|
m["oldKey"] = BytesToBigInt(cvp.OldKey).String() |
||||
|
m["oldValue"] = BytesToBigInt(cvp.OldValue).String() |
||||
|
if cvp.IsOld0 { |
||||
|
m["isOld0"] = "1" |
||||
|
} else { |
||||
|
m["isOld0"] = "0" |
||||
|
} |
||||
|
m["key"] = BytesToBigInt(cvp.Key).String() |
||||
|
m["value"] = BytesToBigInt(cvp.Value).String() |
||||
|
m["fnc"] = cvp.Fnc |
||||
|
|
||||
|
return json.Marshal(m) |
||||
|
} |
||||
|
|
||||
|
func siblingsToStringArray(s [][]byte) []string { |
||||
|
var r []string |
||||
|
for i := 0; i < len(s); i++ { |
||||
|
r = append(r, BytesToBigInt(s[i]).String()) |
||||
|
} |
||||
|
return r |
||||
|
} |
||||
|
|
||||
|
func (t *Tree) fillMissingEmptySiblings(s [][]byte) [][]byte { |
||||
|
for i := len(s); i < t.maxLevels; i++ { |
||||
|
s = append(s, emptyValue) |
||||
|
} |
||||
|
return s |
||||
|
} |
||||
|
|
||||
|
// GenerateCircomVerifierProof generates a CircomVerifierProof for a given key
|
||||
|
// in the Tree
|
||||
|
func (t *Tree) GenerateCircomVerifierProof(k []byte) (*CircomVerifierProof, error) { |
||||
|
kAux, v, siblings, existence, err := t.GenProof(k) |
||||
|
if err != nil && err != ErrKeyNotFound { |
||||
|
return nil, err |
||||
|
} |
||||
|
var cp CircomVerifierProof |
||||
|
cp.Root = t.Root() |
||||
|
s, err := UnpackSiblings(t.hashFunction, siblings) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
cp.Siblings = t.fillMissingEmptySiblings(s) |
||||
|
if !existence { |
||||
|
cp.OldKey = kAux |
||||
|
cp.OldValue = v |
||||
|
} else { |
||||
|
cp.OldKey = emptyValue |
||||
|
cp.OldValue = emptyValue |
||||
|
} |
||||
|
cp.Key = k |
||||
|
cp.Value = v |
||||
|
if existence { |
||||
|
cp.Fnc = 0 // inclusion
|
||||
|
} else { |
||||
|
cp.Fnc = 1 // non inclusion
|
||||
|
} |
||||
|
|
||||
|
return &cp, nil |
||||
|
} |
@ -0,0 +1,63 @@ |
|||||
|
package arbo |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"math/big" |
||||
|
"testing" |
||||
|
|
||||
|
qt "github.com/frankban/quicktest" |
||||
|
"go.vocdoni.io/dvote/db" |
||||
|
) |
||||
|
|
||||
|
func TestCircomVerifierProof(t *testing.T) { |
||||
|
c := qt.New(t) |
||||
|
database, err := db.NewBadgerDB(c.TempDir()) |
||||
|
c.Assert(err, qt.IsNil) |
||||
|
tree, err := NewTree(database, 4, HashFunctionPoseidon) |
||||
|
c.Assert(err, qt.IsNil) |
||||
|
defer tree.db.Close() //nolint:errcheck
|
||||
|
|
||||
|
bLen := tree.HashFunction().Len() |
||||
|
|
||||
|
testVector := [][]int64{ |
||||
|
{1, 11}, |
||||
|
{2, 22}, |
||||
|
{3, 33}, |
||||
|
{4, 44}, |
||||
|
} |
||||
|
for i := 0; i < len(testVector); i++ { |
||||
|
k := BigIntToBytes(bLen, big.NewInt(testVector[i][0])) |
||||
|
v := BigIntToBytes(bLen, big.NewInt(testVector[i][1])) |
||||
|
if err := tree.Add(k, v); err != nil { |
||||
|
t.Fatal(err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// proof of existence
|
||||
|
k := BigIntToBytes(bLen, big.NewInt(int64(2))) |
||||
|
cvp, err := tree.GenerateCircomVerifierProof(k) |
||||
|
c.Assert(err, qt.IsNil) |
||||
|
jCvp, err := json.Marshal(cvp) |
||||
|
c.Assert(err, qt.IsNil) |
||||
|
// test vector checked with a circom circuit (arbo/testvectors/circom)
|
||||
|
c.Assert(string(jCvp), qt.Equals, `{"fnc":0,"isOld0":"0","key":"2","oldK`+ |
||||
|
`ey":"0","oldValue":"0","root":"1355816845522055904274785395894906304622`+ |
||||
|
`6645447188878859760119761585093422436","siblings":["1162013050763544193`+ |
||||
|
`2056895853942898236773847390796721536119314875877874016518","5158240518`+ |
||||
|
`874928563648144881543092238925265313977134167935552944620041388700","0"`+ |
||||
|
`,"0"],"value":"22"}`) |
||||
|
|
||||
|
// proof of non-existence
|
||||
|
k = BigIntToBytes(bLen, big.NewInt(int64(5))) |
||||
|
cvp, err = tree.GenerateCircomVerifierProof(k) |
||||
|
c.Assert(err, qt.IsNil) |
||||
|
jCvp, err = json.Marshal(cvp) |
||||
|
c.Assert(err, qt.IsNil) |
||||
|
// test vector checked with a circom circuit (arbo/testvectors/circom)
|
||||
|
c.Assert(string(jCvp), qt.Equals, `{"fnc":1,"isOld0":"0","key":"5","oldK`+ |
||||
|
`ey":"1","oldValue":"11","root":"135581684552205590427478539589490630462`+ |
||||
|
`26645447188878859760119761585093422436","siblings":["756056982086999933`+ |
||||
|
`1905412009838015295115276841209205575174464206730109811365","1276103081`+ |
||||
|
`3800436751877086580591648324911598798716611088294049841213649313596","0`+ |
||||
|
`","0"],"value":"11"}`) |
||||
|
} |