simple test fails going down in the tree

This commit is contained in:
Lucas Menendez
2025-03-21 13:38:00 +01:00
parent 7a328a8e0c
commit b09faf8bdb
8 changed files with 2381 additions and 2446 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,64 +1,64 @@
package arbo
import (
"encoding/json"
"math/big"
"testing"
// import (
// "encoding/json"
// "math/big"
// "testing"
qt "github.com/frankban/quicktest"
"go.vocdoni.io/dvote/db"
"go.vocdoni.io/dvote/db/pebbledb"
)
// qt "github.com/frankban/quicktest"
// "go.vocdoni.io/dvote/db"
// "go.vocdoni.io/dvote/db/pebbledb"
// )
func TestCircomVerifierProof(t *testing.T) {
c := qt.New(t)
database, err := pebbledb.New(db.Options{Path: c.TempDir()})
c.Assert(err, qt.IsNil)
tree, err := NewTree(Config{Database: database, MaxLevels: 4,
HashFunction: HashFunctionPoseidon})
c.Assert(err, qt.IsNil)
defer tree.db.Close() //nolint:errcheck
// func TestCircomVerifierProof(t *testing.T) {
// c := qt.New(t)
// database, err := pebbledb.New(db.Options{Path: c.TempDir()})
// c.Assert(err, qt.IsNil)
// tree, err := NewTree(Config{Database: database, MaxLevels: 4,
// HashFunction: HashFunctionPoseidon})
// c.Assert(err, qt.IsNil)
// defer tree.db.Close() //nolint:errcheck
testVector := [][]int64{
{1, 11},
{2, 22},
{3, 33},
{4, 44},
}
bLen := 1
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)
}
}
// testVector := [][]int64{
// {1, 11},
// {2, 22},
// {3, 33},
// {4, 44},
// }
// bLen := 1
// 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 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"}`)
}
// // 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"}`)
// }

View File

@@ -64,7 +64,11 @@ func (f HashPoseidon) Len() int {
// expects the byte arrays to be little-endian representations of big.Int
// values.
func (f HashPoseidon) Hash(b ...*big.Int) (*big.Int, error) {
return poseidon.Hash(b)
var toHash []*big.Int
for _, i := range b {
toHash = append(toHash, BigToFF(BN254BaseField, BytesToBigInt(i.Bytes())))
}
return poseidon.Hash(toHash)
}
type HashMiMC7 struct{}

View File

@@ -1,121 +1,121 @@
package arbo
import (
"bytes"
"io"
"os"
"testing"
"time"
// import (
// "bytes"
// "io"
// "os"
// "testing"
// "time"
qt "github.com/frankban/quicktest"
"go.vocdoni.io/dvote/db"
"go.vocdoni.io/dvote/db/pebbledb"
)
// qt "github.com/frankban/quicktest"
// "go.vocdoni.io/dvote/db"
// "go.vocdoni.io/dvote/db/pebbledb"
// )
func checkRoots(c *qt.C, tree1, tree2 *Tree) {
root1, err := tree1.Root()
c.Assert(err, qt.IsNil)
root2, err := tree2.Root()
c.Assert(err, qt.IsNil)
if !bytes.Equal(root2, root1) {
dir := "err-dump"
if _, err := os.Stat(dir); os.IsNotExist(err) {
err := os.Mkdir(dir, os.ModePerm)
c.Assert(err, qt.IsNil)
}
// store tree1
storeTree(c, tree1, dir+"/tree1")
// func checkRoots(c *qt.C, tree1, tree2 *Tree) {
// root1, err := tree1.Root()
// c.Assert(err, qt.IsNil)
// root2, err := tree2.Root()
// c.Assert(err, qt.IsNil)
// if !bytes.Equal(root2, root1) {
// dir := "err-dump"
// if _, err := os.Stat(dir); os.IsNotExist(err) {
// err := os.Mkdir(dir, os.ModePerm)
// c.Assert(err, qt.IsNil)
// }
// // store tree1
// storeTree(c, tree1, dir+"/tree1")
// store tree2
storeTree(c, tree2, dir+"/tree2")
// // store tree2
// storeTree(c, tree2, dir+"/tree2")
root1, err := tree1.Root()
c.Assert(err, qt.IsNil)
root2, err := tree2.Root()
c.Assert(err, qt.IsNil)
c.Check(root2, qt.DeepEquals, root1)
}
}
// root1, err := tree1.Root()
// c.Assert(err, qt.IsNil)
// root2, err := tree2.Root()
// c.Assert(err, qt.IsNil)
// c.Check(root2, qt.DeepEquals, root1)
// }
// }
func storeTree(c *qt.C, tree *Tree, path string) {
dump, err := tree.Dump(nil)
c.Assert(err, qt.IsNil)
err = os.WriteFile(path+"-"+time.Now().String()+".debug", dump, 0600)
c.Assert(err, qt.IsNil)
}
// func storeTree(c *qt.C, tree *Tree, path string) {
// dump, err := tree.Dump(nil)
// c.Assert(err, qt.IsNil)
// err = os.WriteFile(path+"-"+time.Now().String()+".debug", dump, 0600)
// c.Assert(err, qt.IsNil)
// }
// nolint:unused
func readTree(c *qt.C, tree *Tree, path string) {
b, err := os.ReadFile(path) //nolint:gosec
c.Assert(err, qt.IsNil)
err = tree.ImportDump(b)
c.Assert(err, qt.IsNil)
}
// // nolint:unused
// func readTree(c *qt.C, tree *Tree, path string) {
// b, err := os.ReadFile(path) //nolint:gosec
// c.Assert(err, qt.IsNil)
// err = tree.ImportDump(b)
// c.Assert(err, qt.IsNil)
// }
// nolint:unused
func importDumpLoopAdd(tree *Tree, b []byte) error {
r := bytes.NewReader(b)
var err error
for {
l := make([]byte, 2)
_, err = io.ReadFull(r, l)
if err == io.EOF {
break
} else if err != nil {
return err
}
k := make([]byte, l[0])
_, err = io.ReadFull(r, k)
if err != nil {
return err
}
v := make([]byte, l[1])
_, err = io.ReadFull(r, v)
if err != nil {
return err
}
err = tree.Add(k, v)
if err != nil {
return err
}
}
return nil
}
// // nolint:unused
// func importDumpLoopAdd(tree *Tree, b []byte) error {
// r := bytes.NewReader(b)
// var err error
// for {
// l := make([]byte, 2)
// _, err = io.ReadFull(r, l)
// if err == io.EOF {
// break
// } else if err != nil {
// return err
// }
// k := make([]byte, l[0])
// _, err = io.ReadFull(r, k)
// if err != nil {
// return err
// }
// v := make([]byte, l[1])
// _, err = io.ReadFull(r, v)
// if err != nil {
// return err
// }
// err = tree.Add(k, v)
// if err != nil {
// return err
// }
// }
// return nil
// }
func TestReadTreeDBG(t *testing.T) {
t.Skip() // test just for debugging purposes, disabled by default
// func TestReadTreeDBG(t *testing.T) {
// t.Skip() // test just for debugging purposes, disabled by default
c := qt.New(t)
// c := qt.New(t)
database1, err := pebbledb.New(db.Options{Path: c.TempDir()})
c.Assert(err, qt.IsNil)
tree1, err := NewTree(Config{Database: database1, MaxLevels: 100,
HashFunction: HashFunctionMiMC_BN254})
c.Assert(err, qt.IsNil)
// database1, err := pebbledb.New(db.Options{Path: c.TempDir()})
// c.Assert(err, qt.IsNil)
// tree1, err := NewTree(Config{Database: database1, MaxLevels: 100,
// HashFunction: HashFunctionMiMC_BN254})
// c.Assert(err, qt.IsNil)
database2, err := pebbledb.New(db.Options{Path: c.TempDir()})
c.Assert(err, qt.IsNil)
tree2, err := NewTree(Config{Database: database2, MaxLevels: 100,
HashFunction: HashFunctionMiMC_BN254})
c.Assert(err, qt.IsNil)
// database2, err := pebbledb.New(db.Options{Path: c.TempDir()})
// c.Assert(err, qt.IsNil)
// tree2, err := NewTree(Config{Database: database2, MaxLevels: 100,
// HashFunction: HashFunctionMiMC_BN254})
// c.Assert(err, qt.IsNil)
// tree1 is generated by a loop of .Add
path := "err-dump/tree1-2021-06-03 16:45:54.104449306 +0200 CEST m=+0.073874545.debug"
b, err := os.ReadFile(path)
c.Assert(err, qt.IsNil)
err = importDumpLoopAdd(tree1, b)
c.Assert(err, qt.IsNil)
// // tree1 is generated by a loop of .Add
// path := "err-dump/tree1-2021-06-03 16:45:54.104449306 +0200 CEST m=+0.073874545.debug"
// b, err := os.ReadFile(path)
// c.Assert(err, qt.IsNil)
// err = importDumpLoopAdd(tree1, b)
// c.Assert(err, qt.IsNil)
// tree2 is generated by .AddBatch
path = "err-dump/tree2-2021-06-03 16:45:54.104525519 +0200 CEST m=+0.073950756.debug"
readTree(c, tree2, path)
// // tree2 is generated by .AddBatch
// path = "err-dump/tree2-2021-06-03 16:45:54.104525519 +0200 CEST m=+0.073950756.debug"
// readTree(c, tree2, path)
// tree1.PrintGraphvizFirstNLevels(nil, 6)
// tree2.PrintGraphvizFirstNLevels(nil, 6)
// // tree1.PrintGraphvizFirstNLevels(nil, 6)
// // tree2.PrintGraphvizFirstNLevels(nil, 6)
root1, err := tree1.Root()
c.Assert(err, qt.IsNil)
root2, err := tree2.Root()
c.Assert(err, qt.IsNil)
c.Check(root2, qt.DeepEquals, root1)
}
// root1, err := tree1.Root()
// c.Assert(err, qt.IsNil)
// root2, err := tree2.Root()
// c.Assert(err, qt.IsNil)
// c.Check(root2, qt.DeepEquals, root1)
// }

View File

@@ -8,16 +8,16 @@ import (
qt "github.com/frankban/quicktest"
"github.com/vocdoni/arbo"
"go.vocdoni.io/dvote/db"
"go.vocdoni.io/dvote/db/pebbledb"
"github.com/vocdoni/arbo/memdb"
)
func TestGenerator(t *testing.T) {
c := qt.New(t)
database, err := pebbledb.New(db.Options{Path: c.TempDir()})
c.Assert(err, qt.IsNil)
tree, err := arbo.NewTree(arbo.Config{Database: database, MaxLevels: 4,
HashFunction: arbo.HashFunctionPoseidon})
tree, err := arbo.NewTree(arbo.Config{
Database: memdb.New(),
MaxLevels: 4,
HashFunction: arbo.HashFunctionPoseidon,
})
c.Assert(err, qt.IsNil)
testVector := [][]int64{
@@ -26,17 +26,12 @@ func TestGenerator(t *testing.T) {
{3, 33},
{4, 44},
}
bLen := 1
for i := 0; i < len(testVector); i++ {
k := arbo.BigIntToBytes(bLen, big.NewInt(testVector[i][0]))
v := arbo.BigIntToBytes(bLen, big.NewInt(testVector[i][1]))
if err := tree.Add(k, v); err != nil {
t.Fatal(err)
}
for i := range testVector {
c.Assert(tree.Add(big.NewInt(testVector[i][0]), big.NewInt(testVector[i][1])), qt.IsNil)
}
// proof of existence
k := arbo.BigIntToBytes(bLen, big.NewInt(int64(2)))
k := big.NewInt(int64(2))
cvp, err := tree.GenerateCircomVerifierProof(k)
c.Assert(err, qt.IsNil)
jCvp, err := json.Marshal(cvp)
@@ -46,7 +41,7 @@ func TestGenerator(t *testing.T) {
c.Assert(err, qt.IsNil)
// proof of non-existence
k = arbo.BigIntToBytes(bLen, big.NewInt(int64(5)))
k = big.NewInt(int64(5))
cvp, err = tree.GenerateCircomVerifierProof(k)
c.Assert(err, qt.IsNil)
jCvp, err = json.Marshal(cvp)

69
tree.go
View File

@@ -15,6 +15,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"log"
"math"
"math/big"
"runtime"
@@ -138,12 +139,14 @@ func NewTreeWithTx(wTx db.WriteTx, cfg Config) (*Tree, error) {
if cfg.ThresholdNLeafs == 0 {
cfg.ThresholdNLeafs = DefaultThresholdNLeafs
}
t := Tree{db: cfg.Database, maxLevels: cfg.MaxLevels,
thresholdNLeafs: cfg.ThresholdNLeafs, hashFunction: cfg.HashFunction}
t.emptyHash = big.NewInt(0) // empty
_, err := wTx.Get(dbKeyRoot)
if err == db.ErrKeyNotFound {
t := Tree{
db: cfg.Database,
maxLevels: cfg.MaxLevels,
thresholdNLeafs: cfg.ThresholdNLeafs,
hashFunction: cfg.HashFunction,
emptyHash: zero, // empty
}
if _, err := wTx.Get(dbKeyRoot); err == db.ErrKeyNotFound {
// store new root 0 (empty)
if err = wTx.Set(dbKeyRoot, t.emptyHash.Bytes()); err != nil {
return nil, err
@@ -226,12 +229,11 @@ func (t *Tree) AddBatchWithTx(wTx db.WriteTx, keys []*big.Int, values [][]*big.I
return nil, ErrSnapshotNotEditable
}
e := big.NewInt(0)
// equal the number of keys & values
if len(keys) > len(values) {
// add missing values
for i := len(values); i < len(keys); i++ {
values = append(values, []*big.Int{e})
values = append(values, []*big.Int{zero})
}
} else if len(keys) < len(values) {
// crop extra values
@@ -252,7 +254,7 @@ func (t *Tree) addBatchInDisk(wTx db.WriteTx, keys []*big.Int, values [][]*big.I
nCPU := flp2(runtime.NumCPU())
if nCPU == 1 || len(keys) < nCPU {
var invalids []Invalid
for i := 0; i < len(keys); i++ {
for i := range keys {
if err := t.addWithTx(wTx, keys[i], values[i]...); err != nil {
invalids = append(invalids, Invalid{i, err})
}
@@ -281,12 +283,12 @@ func (t *Tree) addBatchInDisk(wTx db.WriteTx, keys []*big.Int, values [][]*big.I
// Already populated Tree but Unbalanced.
// add one key at each bucket, and then continue with the flow
for i := 0; i < len(buckets); i++ {
for i := range buckets {
// add one leaf of the bucket, if there is an error when
// adding the k-v, try to add the next one of the bucket
// (until one is added)
inserted := -1
for j := 0; j < len(buckets[i]); j++ {
for j := range buckets[i] {
if newRoot, err := t.add(wTx, root, 0,
buckets[i][j].k, buckets[i][j].v...); err == nil {
inserted = j
@@ -315,7 +317,7 @@ func (t *Tree) addBatchInDisk(wTx db.WriteTx, keys []*big.Int, values [][]*big.I
invalidsInBucket := make([][]Invalid, nCPU)
txs := make([]db.WriteTx, nCPU)
for i := 0; i < nCPU; i++ {
for i := range nCPU {
txs[i] = t.db.WriteTx()
err := txs[i].Apply(wTx)
if err != nil {
@@ -325,7 +327,7 @@ func (t *Tree) addBatchInDisk(wTx db.WriteTx, keys []*big.Int, values [][]*big.I
var wg sync.WaitGroup
wg.Add(nCPU)
for i := 0; i < nCPU; i++ {
for i := range nCPU {
go func(cpu int) {
// use different wTx for each cpu, after once all
// are done, iter over the cpuWTxs and copy their
@@ -346,14 +348,14 @@ func (t *Tree) addBatchInDisk(wTx db.WriteTx, keys []*big.Int, values [][]*big.I
}
wg.Wait()
for i := 0; i < nCPU; i++ {
for i := range nCPU {
if err := wTx.Apply(txs[i]); err != nil {
return nil, err
}
txs[i].Discard()
}
for i := 0; i < len(invalidsInBucket); i++ {
for i := range invalidsInBucket {
invalids = append(invalids, invalidsInBucket[i]...)
}
@@ -386,7 +388,7 @@ func (t *Tree) upFromSubRoots(wTx db.WriteTx, subRoots []*big.Int) (*big.Int, er
}
// get the subRoots values to know the node types of each subRoot
nodeTypes := make([]byte, len(subRoots))
for i := 0; i < len(subRoots); i++ {
for i := range subRoots {
if subRoots[i].Cmp(t.emptyHash) == 0 {
nodeTypes[i] = PrefixValueEmpty
continue
@@ -470,7 +472,7 @@ func (t *Tree) addBatchInMemory(wTx db.WriteTx, keys []*big.Int, values [][]*big
}
// store pairs in db
for i := 0; i < len(pairs); i++ {
for i := range pairs {
if err := wTx.Set(pairs[i][0], pairs[i][1]); err != nil {
return nil, err
}
@@ -604,28 +606,34 @@ func checkKeyValueLen(k *big.Int, v ...*big.Int) error {
func (t *Tree) add(wTx db.WriteTx, root *big.Int, fromLvl int, k *big.Int, v ...*big.Int) (*big.Int, error) {
if err := checkKeyValueLen(k, v...); err != nil {
log.Println("checkKeyValueLen error:", err)
return nil, err
}
keyPath, err := keyPathFromKey(t.maxLevels, k)
if err != nil {
log.Println("keyPathFromKey error:", err)
return nil, err
}
path := getPath(t.maxLevels, keyPath)
// go down to the leaf
var siblings []*big.Int
log.Println("down", k.String(), root.String())
_, _, siblings, err = t.down(wTx, k, root, siblings, path, fromLvl, false)
if err != nil {
log.Println("down error:", err)
return nil, err
}
leafKey, leafValue, err := t.newLeafValue(k, v...)
if err != nil {
log.Println("newLeafValue error:", err)
return nil, err
}
if err := wTx.Set(leafKey.Bytes(), leafValue); err != nil {
log.Println("wTx.Set error:", err)
return nil, err
}
@@ -636,9 +644,10 @@ func (t *Tree) add(wTx db.WriteTx, root *big.Int, fromLvl int, k *big.Int, v ...
}
root, err = t.up(wTx, leafKey, siblings, path, len(siblings)-1, fromLvl)
if err != nil {
log.Println("up error:", err)
return nil, err
}
log.Println("new root", root.String())
return root, nil
}
@@ -660,6 +669,7 @@ func (t *Tree) down(rTx db.Reader, newKey, currKey *big.Int, siblings []*big.Int
}
currValue, err = rTx.Get(currKey.Bytes())
if err != nil {
log.Println("rTx.Get error:", err)
return nil, nil, nil, err
}
@@ -685,6 +695,7 @@ func (t *Tree) down(rTx db.Reader, newKey, currKey *big.Int, siblings []*big.Int
oldLeafKeyFull, err := keyPathFromKey(t.maxLevels, oldLeafKey)
if err != nil {
log.Println("keyPathFromKey error:", err)
return nil, nil, nil, err
}
@@ -692,6 +703,7 @@ func (t *Tree) down(rTx db.Reader, newKey, currKey *big.Int, siblings []*big.Int
oldPath := getPath(t.maxLevels, oldLeafKeyFull)
siblings, err = t.downVirtually(siblings, currKey, newKey, oldPath, path, currLvl)
if err != nil {
log.Println("downVirtually error:", err)
return nil, nil, nil, err
}
}
@@ -811,7 +823,7 @@ func WriteLeafValue(k *big.Int, v ...*big.Int) ([]byte, error) {
leafValue = append(leafValue, byte(PrefixValueLeaf))
leafValue = append(leafValue, byte(len(k.Bytes())))
leafValue = append(leafValue, k.Bytes()...)
for i := 0; i < len(v); i++ {
for i := range v {
leafValue = append(leafValue, byte(len(v[i].Bytes())))
leafValue = append(leafValue, v[i].Bytes()...)
}
@@ -892,7 +904,7 @@ func ReadIntermediateChilds(b []byte) (*big.Int, *big.Int) {
func getPath(numLevels int, k []byte) []bool {
path := make([]bool, numLevels)
for n := 0; n < numLevels; n++ {
for n := range numLevels {
path[n] = k[n/8]&(1<<(n%8)) != 0
}
return path
@@ -1019,7 +1031,7 @@ func (t *Tree) GenProofWithTx(rTx db.Reader, k *big.Int) (*big.Int, []*big.Int,
func PackSiblings(hashFunc HashFunction, siblings []*big.Int) ([]byte, error) {
var b []byte
var bitmap []bool
for i := 0; i < len(siblings); i++ {
for i := range siblings {
if siblings[i].Cmp(zero) == 0 {
bitmap = append(bitmap, false)
} else {
@@ -1062,7 +1074,7 @@ func UnpackSiblings(hashFunc HashFunction, b []byte) ([]*big.Int, error) {
iSibl := 0
emptySibl := big.NewInt(0)
var siblings []*big.Int
for i := 0; i < len(bitmap); i++ {
for i := range bitmap {
if iSibl >= len(siblingsBytes) {
break
}
@@ -1080,7 +1092,7 @@ func UnpackSiblings(hashFunc HashFunction, b []byte) ([]*big.Int, error) {
func bitmapToBytes(bitmap []bool) []byte {
bitmapBytesLen := int(math.Ceil(float64(len(bitmap)) / 8)) //nolint:gomnd
b := make([]byte, bitmapBytesLen)
for i := 0; i < len(bitmap); i++ {
for i := range bitmap {
if bitmap[i] {
b[i/8] |= 1 << (i % 8)
}
@@ -1090,8 +1102,8 @@ func bitmapToBytes(bitmap []bool) []byte {
func bytesToBitmap(b []byte) []bool {
var bitmap []bool
for i := 0; i < len(b); i++ {
for j := 0; j < 8; j++ {
for i := range b {
for j := range 8 {
bitmap = append(bitmap, b[i]&(1<<j) > 0)
}
}
@@ -1138,7 +1150,7 @@ func (t *Tree) GetWithTx(rTx db.Reader, k *big.Int) (*big.Int, []*big.Int, error
// CheckProof verifies the given proof. The proof verification depends on the
// HashFunction passed as parameter.
func CheckProof(hashFunc HashFunction, root, packedSiblings []byte, k *big.Int, v ...*big.Int) (bool, error) {
func CheckProof(hashFunc HashFunction, root *big.Int, packedSiblings []byte, k *big.Int, v ...*big.Int) (bool, error) {
siblings, err := UnpackSiblings(hashFunc, packedSiblings)
if err != nil {
return false, err
@@ -1166,10 +1178,7 @@ func CheckProof(hashFunc HashFunction, root, packedSiblings []byte, k *big.Int,
}
}
}
if bytes.Equal(key.Bytes(), root) {
return true, nil
}
return false, nil
return key.Cmp(root) == 0, nil
}
func (t *Tree) incNLeafs(wTx db.WriteTx, nLeafs int) error {

File diff suppressed because it is too large Load Diff

View File

@@ -1,336 +1,336 @@
package arbo
import (
"encoding/hex"
"math"
"math/big"
"testing"
// import (
// "encoding/hex"
// "math"
// "math/big"
// "testing"
qt "github.com/frankban/quicktest"
"go.vocdoni.io/dvote/db"
"go.vocdoni.io/dvote/db/pebbledb"
)
// qt "github.com/frankban/quicktest"
// "go.vocdoni.io/dvote/db"
// "go.vocdoni.io/dvote/db/pebbledb"
// )
// testVirtualTree adds the given key-values and tests the vt root against the
// Tree
func testVirtualTree(c *qt.C, maxLevels int, keys, values [][]byte) {
c.Assert(len(keys), qt.Equals, len(values))
// // testVirtualTree adds the given key-values and tests the vt root against the
// // Tree
// func testVirtualTree(c *qt.C, maxLevels int, keys, values [][]byte) {
// c.Assert(len(keys), qt.Equals, len(values))
// normal tree, to have an expected root value
database, err := pebbledb.New(db.Options{Path: c.TempDir()})
c.Assert(err, qt.IsNil)
tree, err := NewTree(Config{Database: database, MaxLevels: maxLevels,
HashFunction: HashFunctionPoseidon})
c.Assert(err, qt.IsNil)
for i := 0; i < len(keys); i++ {
err := tree.Add(keys[i], values[i])
c.Assert(err, qt.IsNil)
}
// // normal tree, to have an expected root value
// database, err := pebbledb.New(db.Options{Path: c.TempDir()})
// c.Assert(err, qt.IsNil)
// tree, err := NewTree(Config{Database: database, MaxLevels: maxLevels,
// HashFunction: HashFunctionPoseidon})
// c.Assert(err, qt.IsNil)
// for i := 0; i < len(keys); i++ {
// err := tree.Add(keys[i], values[i])
// c.Assert(err, qt.IsNil)
// }
// virtual tree
vTree := newVT(maxLevels, HashFunctionPoseidon)
// // virtual tree
// vTree := newVT(maxLevels, HashFunctionPoseidon)
c.Assert(vTree.root, qt.IsNil)
// c.Assert(vTree.root, qt.IsNil)
for i := 0; i < len(keys); i++ {
err := vTree.add(0, keys[i], values[i])
c.Assert(err, qt.IsNil)
}
// for i := 0; i < len(keys); i++ {
// err := vTree.add(0, keys[i], values[i])
// c.Assert(err, qt.IsNil)
// }
// compute hashes, and check Root
_, err = vTree.computeHashes()
c.Assert(err, qt.IsNil)
root, err := tree.Root()
c.Assert(err, qt.IsNil)
c.Assert(vTree.root.h, qt.DeepEquals, root)
}
// // compute hashes, and check Root
// _, err = vTree.computeHashes()
// c.Assert(err, qt.IsNil)
// root, err := tree.Root()
// c.Assert(err, qt.IsNil)
// c.Assert(vTree.root.h, qt.DeepEquals, root)
// }
func TestVirtualTreeTestVectors(t *testing.T) {
c := qt.New(t)
// func TestVirtualTreeTestVectors(t *testing.T) {
// c := qt.New(t)
maxLevels := 32
keyLen := int(math.Ceil(float64(maxLevels) / float64(8))) //nolint:gomnd
keys := [][]byte{
BigIntToBytes(keyLen, big.NewInt(1)),
BigIntToBytes(keyLen, big.NewInt(33)),
BigIntToBytes(keyLen, big.NewInt(1234)),
BigIntToBytes(keyLen, big.NewInt(123456789)),
}
values := [][]byte{
BigIntToBytes(keyLen, big.NewInt(2)),
BigIntToBytes(keyLen, big.NewInt(44)),
BigIntToBytes(keyLen, big.NewInt(9876)),
BigIntToBytes(keyLen, big.NewInt(987654321)),
}
// maxLevels := 32
// keyLen := int(math.Ceil(float64(maxLevels) / float64(8))) //nolint:gomnd
// keys := [][]byte{
// BigIntToBytes(keyLen, big.NewInt(1)),
// BigIntToBytes(keyLen, big.NewInt(33)),
// BigIntToBytes(keyLen, big.NewInt(1234)),
// BigIntToBytes(keyLen, big.NewInt(123456789)),
// }
// values := [][]byte{
// BigIntToBytes(keyLen, big.NewInt(2)),
// BigIntToBytes(keyLen, big.NewInt(44)),
// BigIntToBytes(keyLen, big.NewInt(9876)),
// BigIntToBytes(keyLen, big.NewInt(987654321)),
// }
// check the root for different batches of leafs
testVirtualTree(c, maxLevels, keys[:1], values[:1])
testVirtualTree(c, maxLevels, keys[:2], values[:2])
testVirtualTree(c, maxLevels, keys[:3], values[:3])
testVirtualTree(c, maxLevels, keys[:4], values[:4])
// // check the root for different batches of leafs
// testVirtualTree(c, maxLevels, keys[:1], values[:1])
// testVirtualTree(c, maxLevels, keys[:2], values[:2])
// testVirtualTree(c, maxLevels, keys[:3], values[:3])
// testVirtualTree(c, maxLevels, keys[:4], values[:4])
// test with hardcoded values
testvectorKeys := []string{
"1c7c2265e368314ca58ed2e1f33a326f1220e234a566d55c3605439dbe411642",
"2c9f0a578afff5bfa4e0992a43066460faaab9e8e500db0b16647c701cdb16bf",
"9cb87ec67e875c61390edcd1ab517f443591047709a4d4e45b0f9ed980857b8e",
"9b4e9e92e974a589f426ceeb4cb291dc24893513fecf8e8460992dcf52621d4d",
"1c45cb31f2fa39ec7b9ebf0fad40e0b8296016b5ce8844ae06ff77226379d9a5",
"d8af98bbbb585129798ae54d5eabbc9d0561d583faf1663b3a3724d15bda4ec7",
"3cd55dbfb8f975f20a0925dfbdabe79fa2d51dd0268afbb8ba6b01de9dfcdd3c",
"5d0a9d6d9f197c091bf054fac9cb60e11ec723d6610ed8578e617b4d46cb43d5",
}
keys = [][]byte{}
values = [][]byte{}
for i := 0; i < len(testvectorKeys); i++ {
key, err := hex.DecodeString(testvectorKeys[i])
c.Assert(err, qt.IsNil)
keys = append(keys, key)
values = append(values, []byte{0})
}
// // test with hardcoded values
// testvectorKeys := []string{
// "1c7c2265e368314ca58ed2e1f33a326f1220e234a566d55c3605439dbe411642",
// "2c9f0a578afff5bfa4e0992a43066460faaab9e8e500db0b16647c701cdb16bf",
// "9cb87ec67e875c61390edcd1ab517f443591047709a4d4e45b0f9ed980857b8e",
// "9b4e9e92e974a589f426ceeb4cb291dc24893513fecf8e8460992dcf52621d4d",
// "1c45cb31f2fa39ec7b9ebf0fad40e0b8296016b5ce8844ae06ff77226379d9a5",
// "d8af98bbbb585129798ae54d5eabbc9d0561d583faf1663b3a3724d15bda4ec7",
// "3cd55dbfb8f975f20a0925dfbdabe79fa2d51dd0268afbb8ba6b01de9dfcdd3c",
// "5d0a9d6d9f197c091bf054fac9cb60e11ec723d6610ed8578e617b4d46cb43d5",
// }
// keys = [][]byte{}
// values = [][]byte{}
// for i := 0; i < len(testvectorKeys); i++ {
// key, err := hex.DecodeString(testvectorKeys[i])
// c.Assert(err, qt.IsNil)
// keys = append(keys, key)
// values = append(values, []byte{0})
// }
// check the root for different batches of leafs
testVirtualTree(c, 256, keys[:1], values[:1])
testVirtualTree(c, 256, keys, values)
}
// // check the root for different batches of leafs
// testVirtualTree(c, 256, keys[:1], values[:1])
// testVirtualTree(c, 256, keys, values)
// }
func TestVirtualTreeRandomKeys(t *testing.T) {
c := qt.New(t)
// func TestVirtualTreeRandomKeys(t *testing.T) {
// c := qt.New(t)
// test with random values
nLeafs := 1024
keys := make([][]byte, nLeafs)
values := make([][]byte, nLeafs)
for i := 0; i < nLeafs; i++ {
keys[i] = randomBytes(32)
values[i] = randomBytes(32)
}
// // test with random values
// nLeafs := 1024
// keys := make([][]byte, nLeafs)
// values := make([][]byte, nLeafs)
// for i := 0; i < nLeafs; i++ {
// keys[i] = randomBytes(32)
// values[i] = randomBytes(32)
// }
testVirtualTree(c, 256, keys, values)
}
// testVirtualTree(c, 256, keys, values)
// }
func TestVirtualTreeAddBatch(t *testing.T) {
c := qt.New(t)
// func TestVirtualTreeAddBatch(t *testing.T) {
// c := qt.New(t)
nLeafs := 2000
maxLevels := 256
// nLeafs := 2000
// maxLevels := 256
keys := make([][]byte, nLeafs)
values := make([][]byte, nLeafs)
for i := 0; i < nLeafs; i++ {
keys[i] = randomBytes(32)
values[i] = randomBytes(32)
}
// keys := make([][]byte, nLeafs)
// values := make([][]byte, nLeafs)
// for i := 0; i < nLeafs; i++ {
// keys[i] = randomBytes(32)
// values[i] = randomBytes(32)
// }
// normal tree, to have an expected root value
database, err := pebbledb.New(db.Options{Path: c.TempDir()})
c.Assert(err, qt.IsNil)
tree, err := NewTree(Config{Database: database, MaxLevels: maxLevels,
HashFunction: HashFunctionPoseidon})
c.Assert(err, qt.IsNil)
for i := 0; i < len(keys); i++ {
err := tree.Add(keys[i], values[i])
c.Assert(err, qt.IsNil)
}
// // normal tree, to have an expected root value
// database, err := pebbledb.New(db.Options{Path: c.TempDir()})
// c.Assert(err, qt.IsNil)
// tree, err := NewTree(Config{Database: database, MaxLevels: maxLevels,
// HashFunction: HashFunctionPoseidon})
// c.Assert(err, qt.IsNil)
// for i := 0; i < len(keys); i++ {
// err := tree.Add(keys[i], values[i])
// c.Assert(err, qt.IsNil)
// }
// virtual tree
vTree := newVT(maxLevels, HashFunctionPoseidon)
// // virtual tree
// vTree := newVT(maxLevels, HashFunctionPoseidon)
c.Assert(vTree.root, qt.IsNil)
// c.Assert(vTree.root, qt.IsNil)
invalids, err := vTree.addBatch(keys, values)
c.Assert(err, qt.IsNil)
c.Assert(len(invalids), qt.Equals, 0)
// invalids, err := vTree.addBatch(keys, values)
// c.Assert(err, qt.IsNil)
// c.Assert(len(invalids), qt.Equals, 0)
// compute hashes, and check Root
_, err = vTree.computeHashes()
c.Assert(err, qt.IsNil)
root, err := tree.Root()
c.Assert(err, qt.IsNil)
c.Assert(vTree.root.h, qt.DeepEquals, root)
}
// // compute hashes, and check Root
// _, err = vTree.computeHashes()
// c.Assert(err, qt.IsNil)
// root, err := tree.Root()
// c.Assert(err, qt.IsNil)
// c.Assert(vTree.root.h, qt.DeepEquals, root)
// }
func TestVirtualTreeAddBatchFullyUsed(t *testing.T) {
c := qt.New(t)
// func TestVirtualTreeAddBatchFullyUsed(t *testing.T) {
// c := qt.New(t)
vTree1 := newVT(7, HashFunctionPoseidon) // used for add one by one
vTree2 := newVT(7, HashFunctionPoseidon) // used for addBatch
// vTree1 := newVT(7, HashFunctionPoseidon) // used for add one by one
// vTree2 := newVT(7, HashFunctionPoseidon) // used for addBatch
var keys, values [][]byte
for i := 0; i < 128; i++ {
k := BigIntToBytes(1, big.NewInt(int64(i)))
v := k
// var keys, values [][]byte
// for i := 0; i < 128; i++ {
// k := BigIntToBytes(1, big.NewInt(int64(i)))
// v := k
keys = append(keys, k)
values = append(values, v)
// keys = append(keys, k)
// values = append(values, v)
// add one by one expecting no error
err := vTree1.add(0, k, v)
c.Assert(err, qt.IsNil)
}
// // add one by one expecting no error
// err := vTree1.add(0, k, v)
// c.Assert(err, qt.IsNil)
// }
invalids, err := vTree2.addBatch(keys, values)
c.Assert(err, qt.IsNil)
c.Assert(0, qt.Equals, len(invalids))
}
// invalids, err := vTree2.addBatch(keys, values)
// c.Assert(err, qt.IsNil)
// c.Assert(0, qt.Equals, len(invalids))
// }
func TestGetNodesAtLevel(t *testing.T) {
c := qt.New(t)
// func TestGetNodesAtLevel(t *testing.T) {
// c := qt.New(t)
tree0 := vt{
params: &params{
maxLevels: 100,
hashFunction: HashFunctionPoseidon,
emptyHash: make([]byte, HashFunctionPoseidon.Len()),
},
root: nil,
}
// tree0 := vt{
// params: &params{
// maxLevels: 100,
// hashFunction: HashFunctionPoseidon,
// emptyHash: make([]byte, HashFunctionPoseidon.Len()),
// },
// root: nil,
// }
tree1 := vt{
params: &params{
maxLevels: 100,
hashFunction: HashFunctionPoseidon,
emptyHash: make([]byte, HashFunctionPoseidon.Len()),
},
root: &node{
l: &node{
l: &node{
k: []byte{0, 0, 0, 0},
v: []byte{0, 0, 0, 0},
},
r: &node{
k: []byte{0, 0, 0, 1},
v: []byte{0, 0, 0, 1},
},
},
r: &node{
l: &node{
k: []byte{0, 0, 0, 2},
v: []byte{0, 0, 0, 2},
},
r: &node{
k: []byte{0, 0, 0, 3},
v: []byte{0, 0, 0, 3},
},
},
},
}
// tree1.printGraphviz()
// tree1 := vt{
// params: &params{
// maxLevels: 100,
// hashFunction: HashFunctionPoseidon,
// emptyHash: make([]byte, HashFunctionPoseidon.Len()),
// },
// root: &node{
// l: &node{
// l: &node{
// k: []byte{0, 0, 0, 0},
// v: []byte{0, 0, 0, 0},
// },
// r: &node{
// k: []byte{0, 0, 0, 1},
// v: []byte{0, 0, 0, 1},
// },
// },
// r: &node{
// l: &node{
// k: []byte{0, 0, 0, 2},
// v: []byte{0, 0, 0, 2},
// },
// r: &node{
// k: []byte{0, 0, 0, 3},
// v: []byte{0, 0, 0, 3},
// },
// },
// },
// }
// // tree1.printGraphviz()
tree2 := vt{
params: &params{
maxLevels: 100,
hashFunction: HashFunctionPoseidon,
emptyHash: make([]byte, HashFunctionPoseidon.Len()),
},
root: &node{
l: nil,
r: &node{
l: &node{
l: &node{
l: &node{
k: []byte{0, 0, 0, 0},
v: []byte{0, 0, 0, 0},
},
r: &node{
k: []byte{0, 0, 0, 1},
v: []byte{0, 0, 0, 1},
},
},
r: &node{
k: []byte{0, 0, 0, 2},
v: []byte{0, 0, 0, 2},
},
},
r: &node{
k: []byte{0, 0, 0, 3},
v: []byte{0, 0, 0, 3},
},
},
},
}
// tree2.printGraphviz()
// tree2 := vt{
// params: &params{
// maxLevels: 100,
// hashFunction: HashFunctionPoseidon,
// emptyHash: make([]byte, HashFunctionPoseidon.Len()),
// },
// root: &node{
// l: nil,
// r: &node{
// l: &node{
// l: &node{
// l: &node{
// k: []byte{0, 0, 0, 0},
// v: []byte{0, 0, 0, 0},
// },
// r: &node{
// k: []byte{0, 0, 0, 1},
// v: []byte{0, 0, 0, 1},
// },
// },
// r: &node{
// k: []byte{0, 0, 0, 2},
// v: []byte{0, 0, 0, 2},
// },
// },
// r: &node{
// k: []byte{0, 0, 0, 3},
// v: []byte{0, 0, 0, 3},
// },
// },
// },
// }
// // tree2.printGraphviz()
tree3 := vt{
params: &params{
maxLevels: 100,
hashFunction: HashFunctionPoseidon,
emptyHash: make([]byte, HashFunctionPoseidon.Len()),
},
root: &node{
l: nil,
r: &node{
l: &node{
l: &node{
l: &node{
k: []byte{0, 0, 0, 0},
v: []byte{0, 0, 0, 0},
},
r: &node{
k: []byte{0, 0, 0, 1},
v: []byte{0, 0, 0, 1},
},
},
r: &node{
k: []byte{0, 0, 0, 2},
v: []byte{0, 0, 0, 2},
},
},
r: nil,
},
},
}
// tree3.printGraphviz()
// tree3 := vt{
// params: &params{
// maxLevels: 100,
// hashFunction: HashFunctionPoseidon,
// emptyHash: make([]byte, HashFunctionPoseidon.Len()),
// },
// root: &node{
// l: nil,
// r: &node{
// l: &node{
// l: &node{
// l: &node{
// k: []byte{0, 0, 0, 0},
// v: []byte{0, 0, 0, 0},
// },
// r: &node{
// k: []byte{0, 0, 0, 1},
// v: []byte{0, 0, 0, 1},
// },
// },
// r: &node{
// k: []byte{0, 0, 0, 2},
// v: []byte{0, 0, 0, 2},
// },
// },
// r: nil,
// },
// },
// }
// // tree3.printGraphviz()
nodes0, err := tree0.getNodesAtLevel(2)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes0), qt.DeepEquals, 4)
c.Assert("0000", qt.DeepEquals, getNotNils(nodes0))
// nodes0, err := tree0.getNodesAtLevel(2)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes0), qt.DeepEquals, 4)
// c.Assert("0000", qt.DeepEquals, getNotNils(nodes0))
nodes1, err := tree1.getNodesAtLevel(2)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes1), qt.DeepEquals, 4)
c.Assert("1111", qt.DeepEquals, getNotNils(nodes1))
// nodes1, err := tree1.getNodesAtLevel(2)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes1), qt.DeepEquals, 4)
// c.Assert("1111", qt.DeepEquals, getNotNils(nodes1))
nodes1, err = tree1.getNodesAtLevel(3)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes1), qt.DeepEquals, 8)
c.Assert("00000000", qt.DeepEquals, getNotNils(nodes1))
// nodes1, err = tree1.getNodesAtLevel(3)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes1), qt.DeepEquals, 8)
// c.Assert("00000000", qt.DeepEquals, getNotNils(nodes1))
nodes2, err := tree2.getNodesAtLevel(2)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes2), qt.DeepEquals, 4)
c.Assert("0011", qt.DeepEquals, getNotNils(nodes2))
// nodes2, err := tree2.getNodesAtLevel(2)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes2), qt.DeepEquals, 4)
// c.Assert("0011", qt.DeepEquals, getNotNils(nodes2))
nodes2, err = tree2.getNodesAtLevel(3)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes2), qt.DeepEquals, 8)
c.Assert("00001100", qt.DeepEquals, getNotNils(nodes2))
// nodes2, err = tree2.getNodesAtLevel(3)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes2), qt.DeepEquals, 8)
// c.Assert("00001100", qt.DeepEquals, getNotNils(nodes2))
nodes3, err := tree3.getNodesAtLevel(2)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes3), qt.DeepEquals, 4)
c.Assert("0010", qt.DeepEquals, getNotNils(nodes3))
// nodes3, err := tree3.getNodesAtLevel(2)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes3), qt.DeepEquals, 4)
// c.Assert("0010", qt.DeepEquals, getNotNils(nodes3))
nodes3, err = tree3.getNodesAtLevel(3)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes3), qt.DeepEquals, 8)
c.Assert("00001100", qt.DeepEquals, getNotNils(nodes3))
// nodes3, err = tree3.getNodesAtLevel(3)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes3), qt.DeepEquals, 8)
// c.Assert("00001100", qt.DeepEquals, getNotNils(nodes3))
nodes3, err = tree3.getNodesAtLevel(4)
c.Assert(err, qt.IsNil)
c.Assert(len(nodes3), qt.DeepEquals, 16)
c.Assert("0000000011000000", qt.DeepEquals, getNotNils(nodes3))
}
// nodes3, err = tree3.getNodesAtLevel(4)
// c.Assert(err, qt.IsNil)
// c.Assert(len(nodes3), qt.DeepEquals, 16)
// c.Assert("0000000011000000", qt.DeepEquals, getNotNils(nodes3))
// }
func getNotNils(nodes []*node) string {
s := ""
for i := 0; i < len(nodes); i++ {
if nodes[i] == nil {
s += "0"
} else {
s += "1"
}
}
return s
}
// func getNotNils(nodes []*node) string {
// s := ""
// for i := 0; i < len(nodes); i++ {
// if nodes[i] == nil {
// s += "0"
// } else {
// s += "1"
// }
// }
// return s
// }