arnaucube
arnaucube/arboPublic Self-hosted repository
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"
 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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 = db.NewBadgerDB(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 = db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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 := db.NewBadgerDB(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	vt, err := tree.loadVT()
910	c.Assert(err, qt.IsNil)
911	_, err = vt.computeHashes()
912	c.Assert(err, qt.IsNil)
913
914	// check that tree & vt roots are equal
915	c.Check(tree.Root(), qt.DeepEquals, vt.root.h)
916}
917
918// TestAddKeysWithEmptyValues calls AddBatch giving an array of empty values
919func TestAddKeysWithEmptyValues(t *testing.T) {
920	c := qt.New(t)
921
922	nLeafs := 1024
923
924	database, err := db.NewBadgerDB(c.TempDir())
925	c.Assert(err, qt.IsNil)
926	tree, err := NewTree(database, 100, HashFunctionPoseidon)
927	c.Assert(err, qt.IsNil)
928	defer tree.db.Close() //nolint:errcheck
929
930	bLen := tree.HashFunction().Len()
931	var keys, values [][]byte
932	for i := 0; i < nLeafs; i++ {
933		k := BigIntToBytes(bLen, big.NewInt(int64(i)))
934		v := []byte{}
935		keys = append(keys, k)
936		values = append(values, v)
937	}
938
939	for i := 0; i < nLeafs; i++ {
940		if err := tree.Add(keys[i], values[i]); err != nil {
941			t.Fatal(err)
942		}
943	}
944
945	database2, err := db.NewBadgerDB(c.TempDir())
946	c.Assert(err, qt.IsNil)
947	tree2, err := NewTree(database2, 100, HashFunctionPoseidon)
948	c.Assert(err, qt.IsNil)
949	defer tree2.db.Close() //nolint:errcheck
950	tree2.dbgInit()
951
952	indexes, err := tree2.AddBatch(keys, values)
953	c.Assert(err, qt.IsNil)
954	c.Check(len(indexes), qt.Equals, 0)
955
956	// check that both trees roots are equal
957	checkRoots(c, tree, tree2)
958
959	kAux, proofV, siblings, existence, err := tree2.GenProof(keys[9])
960	c.Assert(err, qt.IsNil)
961	c.Assert(proofV, qt.DeepEquals, values[9])
962	c.Assert(keys[9], qt.DeepEquals, kAux)
963	c.Assert(existence, qt.IsTrue)
964
965	// check with empty array
966	verif, err := CheckProof(tree.hashFunction, keys[9], []byte{}, tree.Root(), siblings)
967	c.Assert(err, qt.IsNil)
968	c.Check(verif, qt.IsTrue)
969
970	// check with array with only 1 zero
971	verif, err = CheckProof(tree.hashFunction, keys[9], []byte{0}, tree.Root(), siblings)
972	c.Assert(err, qt.IsNil)
973	c.Check(verif, qt.IsTrue)
974
975	// check with array with 32 zeroes
976	e32 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
977		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
978	c.Assert(len(e32), qt.Equals, 32)
979	verif, err = CheckProof(tree.hashFunction, keys[9], e32, tree.Root(), siblings)
980	c.Assert(err, qt.IsNil)
981	c.Check(verif, qt.IsTrue)
982
983	// check with array with value!=0 returns false at verification
984	verif, err = CheckProof(tree.hashFunction, keys[9], []byte{0, 1}, tree.Root(), siblings)
985	c.Assert(err, qt.IsNil)
986	c.Check(verif, qt.IsFalse)
987}
988
989// TODO test adding batch with multiple invalid keys
990// TODO for tests of AddBatch, if the root does not match the Add root, bulk
991// all the leafs of both trees into a log file to later be able to debug and
992// recreate the case