arbo/addbatch_test.go
addbatch_test.goRaw
1package arbo
2
3import (
4 "crypto/rand"
5 "encoding/hex"
6 "fmt"
7 "math/big"
8 "runtime"
9 "sort"
10 "testing"
11 "time"
12
13 qt "github.com/frankban/quicktest"
14 "go.vocdoni.io/dvote/db/badgerdb"
15)
16
17var debug = true
18
19func printTestContext(prefix string, nLeafs int, hashName, dbName string) {
20 if debug {
21 fmt.Printf("%snCPU: %d, nLeafs: %d, hash: %s, db: %s\n",
22 prefix, runtime.NumCPU(), nLeafs, hashName, dbName)
23 }
24}
25
26func printRes(name string, duration time.Duration) {
27 if debug {
28 fmt.Printf("%s: %s \n", name, duration)
29 }
30}
31
32func debugTime(descr string, time1, time2 time.Duration) {
33 if debug {
34 fmt.Printf("%s was %.02fx times faster than without AddBatch\n",
35 descr, float64(time1)/float64(time2))
36 }
37}
38
39func testInit(c *qt.C, n int) (*Tree, *Tree) {
40 database1, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
41 c.Assert(err, qt.IsNil)
42 tree1, err := NewTree(database1, 100, HashFunctionPoseidon)
43 c.Assert(err, qt.IsNil)
44
45 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
46 c.Assert(err, qt.IsNil)
47 tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
48 c.Assert(err, qt.IsNil)
49
50 bLen := HashFunctionPoseidon.Len()
51 // add the initial leafs to fill a bit the trees before calling the
52 // AddBatch method
53 for i := 0; i < n; i++ {
54 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
55 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
56 if err := tree1.Add(k, v); err != nil {
57 c.Fatal(err)
58 }
59 if err := tree2.Add(k, v); err != nil {
60 c.Fatal(err)
61 }
62 }
63 return tree1, tree2
64}
65
66func TestAddBatchTreeEmpty(t *testing.T) {
67 c := qt.New(t)
68
69 nLeafs := 1024
70
71 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
72 c.Assert(err, qt.IsNil)
73 tree, err := NewTree(database, 100, HashFunctionPoseidon)
74 c.Assert(err, qt.IsNil)
75 defer tree.db.Close() //nolint:errcheck
76
77 bLen := tree.HashFunction().Len()
78 var keys, values [][]byte
79 for i := 0; i < nLeafs; i++ {
80 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
81 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
82 keys = append(keys, k)
83 values = append(values, v)
84 }
85
86 start := time.Now()
87 for i := 0; i < nLeafs; i++ {
88 if err := tree.Add(keys[i], values[i]); err != nil {
89 t.Fatal(err)
90 }
91 }
92 time1 := time.Since(start)
93
94 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
95 c.Assert(err, qt.IsNil)
96 tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
97 c.Assert(err, qt.IsNil)
98 defer tree2.db.Close() //nolint:errcheck
99 tree2.dbgInit()
100
101 start = time.Now()
102 indexes, err := tree2.AddBatch(keys, values)
103 c.Assert(err, qt.IsNil)
104 time2 := time.Since(start)
105 if debug {
106 debugTime("Case tree empty, AddBatch", time1, time2)
107 printTestContext(" ", nLeafs, "Poseidon", "memory")
108 tree2.dbg.print(" ")
109 }
110 c.Check(len(indexes), qt.Equals, 0)
111
112 // check that both trees roots are equal
113 checkRoots(c, tree, tree2)
114}
115
116func TestAddBatchTreeEmptyNotPowerOf2(t *testing.T) {
117 c := qt.New(t)
118
119 nLeafs := 1027
120
121 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
122 c.Assert(err, qt.IsNil)
123 tree, err := NewTree(database, 100, HashFunctionPoseidon)
124 c.Assert(err, qt.IsNil)
125 defer tree.db.Close() //nolint:errcheck
126
127 bLen := tree.HashFunction().Len()
128 for i := 0; i < nLeafs; i++ {
129 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
130 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
131 if err := tree.Add(k, v); err != nil {
132 t.Fatal(err)
133 }
134 }
135
136 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
137 c.Assert(err, qt.IsNil)
138 tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
139 c.Assert(err, qt.IsNil)
140 defer tree2.db.Close() //nolint:errcheck
141
142 var keys, values [][]byte
143 for i := 0; i < nLeafs; i++ {
144 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
145 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
146 keys = append(keys, k)
147 values = append(values, v)
148 }
149 indexes, err := tree2.AddBatch(keys, values)
150 c.Assert(err, qt.IsNil)
151 c.Check(len(indexes), qt.Equals, 0)
152
153 // check that both trees roots are equal
154 checkRoots(c, tree, tree2)
155}
156
157func randomBytes(n int) []byte {
158 b := make([]byte, n)
159 _, err := rand.Read(b)
160 if err != nil {
161 panic(err)
162 }
163 return b
164}
165
166func TestAddBatchTestVector1(t *testing.T) {
167 c := qt.New(t)
168 database1, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
169 c.Assert(err, qt.IsNil)
170 tree1, err := NewTree(database1, 100, HashFunctionBlake2b)
171 c.Assert(err, qt.IsNil)
172 defer tree1.db.Close() //nolint:errcheck
173
174 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
175 c.Assert(err, qt.IsNil)
176 tree2, err := NewTree(database2, 100, HashFunctionBlake2b)
177 c.Assert(err, qt.IsNil)
178 defer tree2.db.Close() //nolint:errcheck
179
180 // leafs in 2nd level subtrees: [ 6, 0, 1, 1]
181 testvectorKeys := []string{
182 "1c7c2265e368314ca58ed2e1f33a326f1220e234a566d55c3605439dbe411642",
183 "2c9f0a578afff5bfa4e0992a43066460faaab9e8e500db0b16647c701cdb16bf",
184 "1c45cb31f2fa39ec7b9ebf0fad40e0b8296016b5ce8844ae06ff77226379d9a5",
185 "d8af98bbbb585129798ae54d5eabbc9d0561d583faf1663b3a3724d15bda4ec7",
186 }
187 var keys, values [][]byte
188 for i := 0; i < len(testvectorKeys); i++ {
189 key, err := hex.DecodeString(testvectorKeys[i])
190 c.Assert(err, qt.IsNil)
191 keys = append(keys, key)
192 values = append(values, []byte{0})
193 }
194
195 for i := 0; i < len(keys); i++ {
196 if err := tree1.Add(keys[i], values[i]); err != nil {
197 t.Fatal(err)
198 }
199 }
200
201 indexes, err := tree2.AddBatch(keys, values)
202 c.Assert(err, qt.IsNil)
203 c.Check(len(indexes), qt.Equals, 0)
204 // check that both trees roots are equal
205 checkRoots(c, tree1, tree2)
206
207 // 2nd test vectors
208 database1, err = badgerdb.New(badgerdb.Options{Path: c.TempDir()})
209 c.Assert(err, qt.IsNil)
210 tree1, err = NewTree(database1, 100, HashFunctionBlake2b)
211 c.Assert(err, qt.IsNil)
212 defer tree1.db.Close() //nolint:errcheck
213
214 database2, err = badgerdb.New(badgerdb.Options{Path: c.TempDir()})
215 c.Assert(err, qt.IsNil)
216 tree2, err = NewTree(database2, 100, HashFunctionBlake2b)
217 c.Assert(err, qt.IsNil)
218 defer tree2.db.Close() //nolint:errcheck
219
220 testvectorKeys = []string{
221 "1c7c2265e368314ca58ed2e1f33a326f1220e234a566d55c3605439dbe411642",
222 "2c9f0a578afff5bfa4e0992a43066460faaab9e8e500db0b16647c701cdb16bf",
223 "9cb87ec67e875c61390edcd1ab517f443591047709a4d4e45b0f9ed980857b8e",
224 "9b4e9e92e974a589f426ceeb4cb291dc24893513fecf8e8460992dcf52621d4d",
225 "1c45cb31f2fa39ec7b9ebf0fad40e0b8296016b5ce8844ae06ff77226379d9a5",
226 "d8af98bbbb585129798ae54d5eabbc9d0561d583faf1663b3a3724d15bda4ec7",
227 "3cd55dbfb8f975f20a0925dfbdabe79fa2d51dd0268afbb8ba6b01de9dfcdd3c",
228 "5d0a9d6d9f197c091bf054fac9cb60e11ec723d6610ed8578e617b4d46cb43d5",
229 }
230 keys = [][]byte{}
231 values = [][]byte{}
232 for i := 0; i < len(testvectorKeys); i++ {
233 key, err := hex.DecodeString(testvectorKeys[i])
234 c.Assert(err, qt.IsNil)
235 keys = append(keys, key)
236 values = append(values, []byte{0})
237 }
238
239 for i := 0; i < len(keys); i++ {
240 if err := tree1.Add(keys[i], values[i]); err != nil {
241 t.Fatal(err)
242 }
243 }
244
245 indexes, err = tree2.AddBatch(keys, values)
246 c.Assert(err, qt.IsNil)
247 c.Check(len(indexes), qt.Equals, 0)
248 // check that both trees roots are equal
249 checkRoots(c, tree1, tree2)
250}
251
252func TestAddBatchTestVector2(t *testing.T) {
253 // test vector with unbalanced tree
254 c := qt.New(t)
255
256 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
257 c.Assert(err, qt.IsNil)
258 tree1, err := NewTree(database, 100, HashFunctionPoseidon)
259 c.Assert(err, qt.IsNil)
260 defer tree1.db.Close() //nolint:errcheck
261
262 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
263 c.Assert(err, qt.IsNil)
264 tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
265 c.Assert(err, qt.IsNil)
266 defer tree2.db.Close() //nolint:errcheck
267
268 bLen := tree1.HashFunction().Len()
269 var keys, values [][]byte
270 // 1
271 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(1))))
272 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(1))))
273 // 2
274 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(2))))
275 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(2))))
276 // 3
277 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(3))))
278 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(3))))
279 // 5
280 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(5))))
281 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(5))))
282
283 for i := 0; i < len(keys); i++ {
284 if err := tree1.Add(keys[i], values[i]); err != nil {
285 t.Fatal(err)
286 }
287 }
288
289 indexes, err := tree2.AddBatch(keys, values)
290 c.Assert(err, qt.IsNil)
291 c.Check(len(indexes), qt.Equals, 0)
292
293 // check that both trees roots are equal
294 checkRoots(c, tree1, tree2)
295}
296
297func TestAddBatchTestVector3(t *testing.T) {
298 // test vector with unbalanced tree
299 c := qt.New(t)
300
301 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
302 c.Assert(err, qt.IsNil)
303 tree1, err := NewTree(database, 100, HashFunctionPoseidon)
304 c.Assert(err, qt.IsNil)
305 defer tree1.db.Close() //nolint:errcheck
306
307 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
308 c.Assert(err, qt.IsNil)
309 tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
310 c.Assert(err, qt.IsNil)
311 defer tree2.db.Close() //nolint:errcheck
312
313 bLen := tree1.HashFunction().Len()
314 var keys, values [][]byte
315 // 0
316 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(0))))
317 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(0))))
318 // 3
319 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(3))))
320 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(3))))
321 // 7
322 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(7))))
323 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(7))))
324 // 135
325 keys = append(keys, BigIntToBytes(bLen, big.NewInt(int64(135))))
326 values = append(values, BigIntToBytes(bLen, big.NewInt(int64(135))))
327
328 for i := 0; i < len(keys); i++ {
329 if err := tree1.Add(keys[i], values[i]); err != nil {
330 t.Fatal(err)
331 }
332 }
333
334 indexes, err := tree2.AddBatch(keys, values)
335 c.Assert(err, qt.IsNil)
336 c.Check(len(indexes), qt.Equals, 0)
337
338 // check that both trees roots are equal
339 checkRoots(c, tree1, tree2)
340 //
341 // tree1.PrintGraphvizFirstNLevels(nil, 100)
342 // tree2.PrintGraphvizFirstNLevels(nil, 100)
343}
344
345func TestAddBatchTreeEmptyRandomKeys(t *testing.T) {
346 c := qt.New(t)
347
348 nLeafs := 8
349
350 database1, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
351 c.Assert(err, qt.IsNil)
352 tree1, err := NewTree(database1, 100, HashFunctionBlake2b)
353 c.Assert(err, qt.IsNil)
354 defer tree1.db.Close() //nolint:errcheck
355
356 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
357 c.Assert(err, qt.IsNil)
358 tree2, err := NewTree(database2, 100, HashFunctionBlake2b)
359 c.Assert(err, qt.IsNil)
360 defer tree2.db.Close() //nolint:errcheck
361
362 var keys, values [][]byte
363 for i := 0; i < nLeafs; i++ {
364 keys = append(keys, randomBytes(32))
365 values = append(values, randomBytes(32))
366 }
367
368 for i := 0; i < len(keys); i++ {
369 if err := tree1.Add(keys[i], values[i]); err != nil {
370 t.Fatal(err)
371 }
372 }
373
374 indexes, err := tree2.AddBatch(keys, values)
375 c.Assert(err, qt.IsNil)
376 c.Check(len(indexes), qt.Equals, 0)
377 // check that both trees roots are equal
378 checkRoots(c, tree1, tree2)
379}
380
381func TestAddBatchTreeNotEmptyFewLeafs(t *testing.T) {
382 c := qt.New(t)
383
384 nLeafs := 1024
385 initialNLeafs := 99
386
387 tree1, tree2 := testInit(c, initialNLeafs)
388 tree2.dbgInit()
389
390 bLen := tree1.HashFunction().Len()
391 start := time.Now()
392 for i := initialNLeafs; i < nLeafs; i++ {
393 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
394 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
395 if err := tree1.Add(k, v); err != nil {
396 t.Fatal(err)
397 }
398 }
399 time1 := time.Since(start)
400
401 // prepare the key-values to be added
402 var keys, values [][]byte
403 for i := initialNLeafs; i < nLeafs; i++ {
404 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
405 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
406 keys = append(keys, k)
407 values = append(values, v)
408 }
409 start = time.Now()
410 indexes, err := tree2.AddBatch(keys, values)
411 c.Assert(err, qt.IsNil)
412 time2 := time.Since(start)
413 if debug {
414 debugTime("Case tree not empty w/ few leafs, AddBatch", time1, time2)
415 printTestContext(" ", nLeafs, "Poseidon", "memory")
416 tree2.dbg.print(" ")
417 }
418 c.Check(len(indexes), qt.Equals, 0)
419
420 // check that both trees roots are equal
421 checkRoots(c, tree1, tree2)
422}
423
424func TestAddBatchTreeNotEmptyEnoughLeafs(t *testing.T) {
425 c := qt.New(t)
426
427 nLeafs := 1024
428 initialNLeafs := 500
429
430 tree1, tree2 := testInit(c, initialNLeafs)
431 tree2.dbgInit()
432
433 bLen := tree1.HashFunction().Len()
434 start := time.Now()
435 for i := initialNLeafs; i < nLeafs; i++ {
436 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
437 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
438 if err := tree1.Add(k, v); err != nil {
439 t.Fatal(err)
440 }
441 }
442 time1 := time.Since(start)
443
444 // prepare the key-values to be added
445 var keys, values [][]byte
446 for i := initialNLeafs; i < nLeafs; i++ {
447 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
448 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
449 keys = append(keys, k)
450 values = append(values, v)
451 }
452 start = time.Now()
453 indexes, err := tree2.AddBatch(keys, values)
454 c.Assert(err, qt.IsNil)
455 time2 := time.Since(start)
456 if debug {
457 debugTime("Case tree not empty w/ enough leafs, AddBatch", time1, time2)
458 printTestContext(" ", nLeafs, "Poseidon", "memory")
459 tree2.dbg.print(" ")
460 }
461 c.Check(len(indexes), qt.Equals, 0)
462 // check that both trees roots are equal
463 checkRoots(c, tree1, tree2)
464}
465
466func TestAddBatchTreeEmptyRepeatedLeafs(t *testing.T) {
467 c := qt.New(t)
468
469 nLeafs := 1024
470 nRepeatedKeys := 99
471
472 tree1, tree2 := testInit(c, 0)
473
474 bLen := tree1.HashFunction().Len()
475 // prepare the key-values to be added
476 var keys, values [][]byte
477 for i := 0; i < nLeafs; i++ {
478 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
479 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
480 keys = append(keys, k)
481 values = append(values, v)
482 }
483 // add repeated key-values
484 for i := 0; i < nRepeatedKeys; i++ {
485 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
486 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
487 keys = append(keys, k)
488 values = append(values, v)
489 }
490
491 // add the non-repeated key-values in tree1 with .Add loop
492 for i := 0; i < nLeafs; i++ {
493 if err := tree1.Add(keys[i], values[i]); err != nil {
494 t.Fatal(err)
495 }
496 }
497
498 indexes, err := tree2.AddBatch(keys, values)
499 c.Assert(err, qt.IsNil)
500 c.Check(len(indexes), qt.Equals, nRepeatedKeys)
501 // check that both trees roots are equal
502 checkRoots(c, tree1, tree2)
503}
504
505func TestAddBatchTreeNotEmptyFewLeafsRepeatedLeafs(t *testing.T) {
506 c := qt.New(t)
507
508 nLeafs := 1024
509 initialNLeafs := 99
510
511 tree1, tree2 := testInit(c, initialNLeafs)
512
513 bLen := tree1.HashFunction().Len()
514 // prepare the key-values to be added
515 var keys, values [][]byte
516 for i := 0; i < nLeafs; i++ {
517 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
518 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
519 keys = append(keys, k)
520 values = append(values, v)
521 }
522
523 // add the keys that will be existing when AddBatch is called
524 for i := initialNLeafs; i < nLeafs; i++ {
525 if err := tree1.Add(keys[i], values[i]); err != nil {
526 t.Fatal(err)
527 }
528 }
529
530 indexes, err := tree2.AddBatch(keys, values)
531 c.Assert(err, qt.IsNil)
532 c.Check(len(indexes), qt.Equals, initialNLeafs)
533 // check that both trees roots are equal
534 checkRoots(c, tree1, tree2)
535}
536
537func TestSplitInBuckets(t *testing.T) {
538 c := qt.New(t)
539
540 bLen := HashFunctionPoseidon.Len()
541 nLeafs := 16
542 kvs := make([]kv, nLeafs)
543 for i := 0; i < nLeafs; i++ {
544 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
545 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
546 keyPath := make([]byte, 32)
547 copy(keyPath[:], k)
548 kvs[i].pos = i
549 kvs[i].keyPath = k
550 kvs[i].k = k
551 kvs[i].v = v
552 }
553
554 // check keyToBucket results for 4 buckets & 8 keys
555 c.Assert(keyToBucket(kvs[0].k, 4), qt.Equals, 0)
556 c.Assert(keyToBucket(kvs[1].k, 4), qt.Equals, 2)
557 c.Assert(keyToBucket(kvs[2].k, 4), qt.Equals, 1)
558 c.Assert(keyToBucket(kvs[3].k, 4), qt.Equals, 3)
559 c.Assert(keyToBucket(kvs[4].k, 4), qt.Equals, 0)
560 c.Assert(keyToBucket(kvs[5].k, 4), qt.Equals, 2)
561 c.Assert(keyToBucket(kvs[6].k, 4), qt.Equals, 1)
562 c.Assert(keyToBucket(kvs[7].k, 4), qt.Equals, 3)
563
564 // check keyToBucket results for 8 buckets & 8 keys
565 c.Assert(keyToBucket(kvs[0].k, 8), qt.Equals, 0)
566 c.Assert(keyToBucket(kvs[1].k, 8), qt.Equals, 4)
567 c.Assert(keyToBucket(kvs[2].k, 8), qt.Equals, 2)
568 c.Assert(keyToBucket(kvs[3].k, 8), qt.Equals, 6)
569 c.Assert(keyToBucket(kvs[4].k, 8), qt.Equals, 1)
570 c.Assert(keyToBucket(kvs[5].k, 8), qt.Equals, 5)
571 c.Assert(keyToBucket(kvs[6].k, 8), qt.Equals, 3)
572 c.Assert(keyToBucket(kvs[7].k, 8), qt.Equals, 7)
573
574 buckets := splitInBuckets(kvs, 4)
575
576 expected := [][]string{
577 {
578 "00000000", // bucket 0
579 "08000000",
580 "04000000",
581 "0c000000",
582 },
583 {
584 "02000000", // bucket 1
585 "0a000000",
586 "06000000",
587 "0e000000",
588 },
589 {
590 "01000000", // bucket 2
591 "09000000",
592 "05000000",
593 "0d000000",
594 },
595 {
596 "03000000", // bucket 3
597 "0b000000",
598 "07000000",
599 "0f000000",
600 },
601 }
602
603 for i := 0; i < len(buckets); i++ {
604 sortKvs(buckets[i])
605 c.Assert(len(buckets[i]), qt.Equals, len(expected[i]))
606 for j := 0; j < len(buckets[i]); j++ {
607 c.Check(hex.EncodeToString(buckets[i][j].k[:4]),
608 qt.Equals, expected[i][j])
609 }
610 }
611}
612
613// compareBytes compares byte slices where the bytes are compared from left to
614// right and each byte is compared by bit from right to left
615func compareBytes(a, b []byte) bool {
616 // WIP
617 for i := 0; i < len(a); i++ {
618 for j := 0; j < 8; j++ {
619 aBit := a[i] & (1 << j)
620 bBit := b[i] & (1 << j)
621 if aBit > bBit {
622 return false
623 } else if aBit < bBit {
624 return true
625 }
626 }
627 }
628 return false
629}
630
631// sortKvs sorts the kv by path
632func sortKvs(kvs []kv) {
633 sort.Slice(kvs, func(i, j int) bool {
634 return compareBytes(kvs[i].keyPath, kvs[j].keyPath)
635 })
636}
637
638func TestAddBatchTreeNotEmpty(t *testing.T) {
639 c := qt.New(t)
640
641 nLeafs := 4096
642 initialNLeafs := 900
643
644 tree1, tree2 := testInit(c, initialNLeafs)
645 tree2.dbgInit()
646
647 bLen := tree1.HashFunction().Len()
648 start := time.Now()
649 for i := initialNLeafs; i < nLeafs; i++ {
650 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
651 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
652 if err := tree1.Add(k, v); err != nil {
653 t.Fatal(err)
654 }
655 }
656 time1 := time.Since(start)
657
658 // prepare the key-values to be added
659 var keys, values [][]byte
660 for i := initialNLeafs; i < nLeafs; i++ {
661 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
662 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
663 keys = append(keys, k)
664 values = append(values, v)
665 }
666 start = time.Now()
667 indexes, err := tree2.AddBatch(keys, values)
668 c.Assert(err, qt.IsNil)
669 time2 := time.Since(start)
670 if debug {
671 debugTime("Case tree not empty, AddBatch", time1, time2)
672 printTestContext(" ", nLeafs, "Poseidon", "memory")
673 tree2.dbg.print(" ")
674 }
675 c.Check(len(indexes), qt.Equals, 0)
676
677 // check that both trees roots are equal
678 checkRoots(c, tree1, tree2)
679}
680
681func TestAddBatchNotEmptyUnbalanced(t *testing.T) {
682 c := qt.New(t)
683
684 nLeafs := 4096
685 initialNLeafs := 900
686
687 tree1, _ := testInit(c, initialNLeafs)
688 bLen := tree1.HashFunction().Len()
689
690 start := time.Now()
691 for i := initialNLeafs; i < nLeafs; i++ {
692 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
693 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
694 if err := tree1.Add(k, v); err != nil {
695 t.Fatal(err)
696 }
697 }
698 time1 := time.Since(start)
699
700 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
701 c.Assert(err, qt.IsNil)
702 tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
703 c.Assert(err, qt.IsNil)
704 defer tree2.db.Close() //nolint:errcheck
705 tree2.dbgInit()
706
707 var keys, values [][]byte
708 // add the initial leafs to fill a bit the tree before calling the
709 // AddBatch method
710 for i := 0; i < initialNLeafs; i++ {
711 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
712 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
713 // use only the keys of one bucket, store the not used ones for
714 // later
715 if i%4 != 0 {
716 keys = append(keys, k)
717 values = append(values, v)
718 continue
719 }
720 if err := tree2.Add(k, v); err != nil {
721 t.Fatal(err)
722 }
723 }
724
725 for i := initialNLeafs; i < nLeafs; i++ {
726 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
727 v := BigIntToBytes(bLen, big.NewInt(int64(i*2)))
728 keys = append(keys, k)
729 values = append(values, v)
730 }
731 start = time.Now()
732 indexes, err := tree2.AddBatch(keys, values)
733 c.Assert(err, qt.IsNil)
734 time2 := time.Since(start)
735 if debug {
736 debugTime("Case tree not empty & unbalanced, AddBatch", time1, time2)
737 printTestContext(" ", nLeafs, "Poseidon", "memory")
738 tree2.dbg.print(" ")
739 }
740 c.Check(len(indexes), qt.Equals, 0)
741
742 // check that both trees roots are equal
743 checkRoots(c, tree1, tree2)
744}
745
746func TestFlp2(t *testing.T) {
747 c := qt.New(t)
748 c.Assert(flp2(31), qt.Equals, 16)
749 c.Assert(flp2(32), qt.Equals, 32)
750 c.Assert(flp2(33), qt.Equals, 32)
751 c.Assert(flp2(63), qt.Equals, 32)
752 c.Assert(flp2(64), qt.Equals, 64)
753 c.Assert(flp2(9000), qt.Equals, 8192)
754}
755
756func TestAddBatchBench(t *testing.T) {
757 nLeafs := 50_000
758 printTestContext("TestAddBatchBench: ", nLeafs, "Blake2b", "badgerdb")
759
760 // prepare inputs
761 var ks, vs [][]byte
762 for i := 0; i < nLeafs; i++ {
763 k := randomBytes(32)
764 v := randomBytes(32)
765 ks = append(ks, k)
766 vs = append(vs, v)
767 }
768
769 benchAdd(t, ks, vs)
770
771 benchAddBatch(t, ks, vs)
772}
773
774func benchAdd(t *testing.T, ks, vs [][]byte) {
775 c := qt.New(t)
776
777 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
778 c.Assert(err, qt.IsNil)
779 tree, err := NewTree(database, 140, HashFunctionBlake2b)
780 c.Assert(err, qt.IsNil)
781 defer tree.db.Close() //nolint:errcheck
782
783 start := time.Now()
784 for i := 0; i < len(ks); i++ {
785 err = tree.Add(ks[i], vs[i])
786 c.Assert(err, qt.IsNil)
787 }
788 if debug {
789 printRes(" Add loop", time.Since(start))
790 tree.dbg.print(" ")
791 }
792}
793
794func benchAddBatch(t *testing.T, ks, vs [][]byte) {
795 c := qt.New(t)
796
797 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
798 c.Assert(err, qt.IsNil)
799 tree, err := NewTree(database, 140, HashFunctionBlake2b)
800 c.Assert(err, qt.IsNil)
801 defer tree.db.Close() //nolint:errcheck
802
803 tree.dbgInit()
804
805 start := time.Now()
806 invalids, err := tree.AddBatch(ks, vs)
807 if debug {
808 printRes(" AddBatch", time.Since(start))
809 tree.dbg.print(" ")
810 }
811 c.Assert(err, qt.IsNil)
812 c.Assert(len(invalids), qt.Equals, 0)
813}
814
815func TestDbgStats(t *testing.T) {
816 c := qt.New(t)
817
818 nLeafs := 10_000
819
820 // prepare inputs
821 var ks, vs [][]byte
822 for i := 0; i < nLeafs; i++ {
823 k := randomBytes(32)
824 v := randomBytes(32)
825 ks = append(ks, k)
826 vs = append(vs, v)
827 }
828
829 // 1
830 database1, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
831 c.Assert(err, qt.IsNil)
832 tree1, err := NewTree(database1, 100, HashFunctionBlake2b)
833 c.Assert(err, qt.IsNil)
834 defer tree1.db.Close() //nolint:errcheck
835
836 tree1.dbgInit()
837
838 for i := 0; i < len(ks); i++ {
839 err = tree1.Add(ks[i], vs[i])
840 c.Assert(err, qt.IsNil)
841 }
842
843 // 2
844 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
845 c.Assert(err, qt.IsNil)
846 tree2, err := NewTree(database2, 100, HashFunctionBlake2b)
847 c.Assert(err, qt.IsNil)
848 defer tree2.db.Close() //nolint:errcheck
849
850 tree2.dbgInit()
851
852 invalids, err := tree2.AddBatch(ks, vs)
853 c.Assert(err, qt.IsNil)
854 c.Assert(len(invalids), qt.Equals, 0)
855
856 // 3
857 database3, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
858 c.Assert(err, qt.IsNil)
859 tree3, err := NewTree(database3, 100, HashFunctionBlake2b)
860 c.Assert(err, qt.IsNil)
861 defer tree3.db.Close() //nolint:errcheck
862
863 tree3.dbgInit()
864
865 // add few key-values
866 // invalids, err = tree3.AddBatch(ks[:], vs[:])
867 invalids, err = tree3.AddBatch(ks[:1000], vs[:1000])
868 c.Assert(err, qt.IsNil)
869 c.Assert(len(invalids), qt.Equals, 0)
870
871 // add the rest of key-values
872 invalids, err = tree3.AddBatch(ks[1000:], vs[1000:])
873 c.Assert(err, qt.IsNil)
874 c.Assert(len(invalids), qt.Equals, 0)
875
876 checkRoots(c, tree1, tree2)
877 checkRoots(c, tree1, tree3)
878
879 if debug {
880 fmt.Println("TestDbgStats")
881 tree1.dbg.print(" add in loop in emptyTree ")
882 tree2.dbg.print(" addbatch caseEmptyTree ")
883 tree3.dbg.print(" addbatch caseNotEmptyTree ")
884 }
885}
886
887func TestLoadVT(t *testing.T) {
888 c := qt.New(t)
889
890 nLeafs := 1024
891
892 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
893 c.Assert(err, qt.IsNil)
894 tree, err := NewTree(database, 100, HashFunctionPoseidon)
895 c.Assert(err, qt.IsNil)
896 defer tree.db.Close() //nolint:errcheck
897
898 var keys, values [][]byte
899 for i := 0; i < nLeafs; i++ {
900 k := randomBytes(31)
901 v := randomBytes(31)
902 keys = append(keys, k)
903 values = append(values, v)
904 }
905 indexes, err := tree.AddBatch(keys, values)
906 c.Assert(err, qt.IsNil)
907 c.Check(len(indexes), qt.Equals, 0)
908
909 rTx := tree.db.ReadTx()
910 defer rTx.Discard()
911 vt, err := tree.loadVT(rTx)
912 c.Assert(err, qt.IsNil)
913 _, err = vt.computeHashes()
914 c.Assert(err, qt.IsNil)
915
916 // check that tree & vt roots are equal
917 c.Check(tree.Root(), qt.DeepEquals, vt.root.h)
918}
919
920// TestAddKeysWithEmptyValues calls AddBatch giving an array of empty values
921func TestAddKeysWithEmptyValues(t *testing.T) {
922 c := qt.New(t)
923
924 nLeafs := 1024
925
926 database, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
927 c.Assert(err, qt.IsNil)
928 tree, err := NewTree(database, 100, HashFunctionPoseidon)
929 c.Assert(err, qt.IsNil)
930 defer tree.db.Close() //nolint:errcheck
931
932 bLen := tree.HashFunction().Len()
933 var keys, values [][]byte
934 for i := 0; i < nLeafs; i++ {
935 k := BigIntToBytes(bLen, big.NewInt(int64(i)))
936 v := []byte{}
937 keys = append(keys, k)
938 values = append(values, v)
939 }
940
941 for i := 0; i < nLeafs; i++ {
942 if err := tree.Add(keys[i], values[i]); err != nil {
943 t.Fatal(err)
944 }
945 }
946
947 database2, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
948 c.Assert(err, qt.IsNil)
949 tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
950 c.Assert(err, qt.IsNil)
951 defer tree2.db.Close() //nolint:errcheck
952 tree2.dbgInit()
953
954 indexes, err := tree2.AddBatch(keys, values)
955 c.Assert(err, qt.IsNil)
956 c.Check(len(indexes), qt.Equals, 0)
957 // check that both trees roots are equal
958 checkRoots(c, tree, tree2)
959
960 // use tree3 to add nil value array
961 database3, err := badgerdb.New(badgerdb.Options{Path: c.TempDir()})
962 c.Assert(err, qt.IsNil)
963 tree3, err := NewTree(database3, 100, HashFunctionPoseidon)
964 c.Assert(err, qt.IsNil)
965 defer tree3.db.Close() //nolint:errcheck
966
967 indexes, err = tree3.AddBatch(keys, nil)
968 c.Assert(err, qt.IsNil)
969 c.Check(len(indexes), qt.Equals, 0)
970 checkRoots(c, tree, tree3)
971
972 kAux, proofV, siblings, existence, err := tree2.GenProof(keys[9])
973 c.Assert(err, qt.IsNil)
974 c.Assert(proofV, qt.DeepEquals, values[9])
975 c.Assert(keys[9], qt.DeepEquals, kAux)
976 c.Assert(existence, qt.IsTrue)
977
978 // check with empty array
979 verif, err := CheckProof(tree.hashFunction, keys[9], []byte{}, tree.Root(), siblings)
980 c.Assert(err, qt.IsNil)
981 c.Check(verif, qt.IsTrue)
982
983 // check with array with only 1 zero
984 verif, err = CheckProof(tree.hashFunction, keys[9], []byte{0}, tree.Root(), siblings)
985 c.Assert(err, qt.IsNil)
986 c.Check(verif, qt.IsTrue)
987
988 // check with array with 32 zeroes
989 e32 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
990 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
991 c.Assert(len(e32), qt.Equals, 32)
992 verif, err = CheckProof(tree.hashFunction, keys[9], e32, tree.Root(), siblings)
993 c.Assert(err, qt.IsNil)
994 c.Check(verif, qt.IsTrue)
995
996 // check with array with value!=0 returns false at verification
997 verif, err = CheckProof(tree.hashFunction, keys[9], []byte{0, 1}, tree.Root(), siblings)
998 c.Assert(err, qt.IsNil)
999 c.Check(verif, qt.IsFalse)
1000}
1001
1002// TODO test adding batch with multiple invalid keys
1003// TODO for tests of AddBatch, if the root does not match the Add root, bulk
1004// all the leafs of both trees into a log file to later be able to debug and
1005// recreate the case