hermez-node/db/statedb/statedb_test.go
db/statedb/statedb_test.goRaw
1package statedb
2
3import (
4 "encoding/hex"
5 "fmt"
6 "io/ioutil"
7 "math/big"
8 "os"
9 "strings"
10 "testing"
11
12 ethCommon "github.com/ethereum/go-ethereum/common"
13 ethCrypto "github.com/ethereum/go-ethereum/crypto"
14 "github.com/hermeznetwork/hermez-node/common"
15 "github.com/hermeznetwork/hermez-node/log"
16 "github.com/hermeznetwork/tracerr"
17 "github.com/iden3/go-iden3-crypto/babyjub"
18 "github.com/iden3/go-merkletree/db"
19 "github.com/stretchr/testify/assert"
20 "github.com/stretchr/testify/require"
21)
22
23func newAccount(t *testing.T, i int) *common.Account {
24 var sk babyjub.PrivateKey
25 _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
26 require.NoError(t, err)
27 pk := sk.Public()
28
29 key, err := ethCrypto.GenerateKey()
30 require.NoError(t, err)
31 address := ethCrypto.PubkeyToAddress(key.PublicKey)
32
33 return &common.Account{
34 Idx: common.Idx(256 + i),
35 TokenID: common.TokenID(i),
36 Nonce: common.Nonce(i),
37 Balance: big.NewInt(1000),
38 BJJ: pk.Compress(),
39 EthAddr: address,
40 }
41}
42
43func TestNewStateDBIntermediateState(t *testing.T) {
44 dir, err := ioutil.TempDir("", "tmpdb")
45 require.NoError(t, err)
46 defer require.NoError(t, os.RemoveAll(dir))
47
48 sdb, err := NewStateDB(dir, 128, TypeTxSelector, 0)
49 require.NoError(t, err)
50
51 // test values
52 k0 := []byte("testkey0")
53 k1 := []byte("testkey1")
54 v0 := []byte("testvalue0")
55 v1 := []byte("testvalue1")
56
57 // store some data
58 tx, err := sdb.db.DB().NewTx()
59 require.NoError(t, err)
60 err = tx.Put(k0, v0)
61 require.NoError(t, err)
62 err = tx.Commit()
63 require.NoError(t, err)
64 v, err := sdb.db.DB().Get(k0)
65 require.NoError(t, err)
66 assert.Equal(t, v0, v)
67
68 // k0 not yet in last
69 err = sdb.LastRead(func(sdb *Last) error {
70 _, err := sdb.DB().Get(k0)
71 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
72 return nil
73 })
74 require.NoError(t, err)
75
76 // Close PebbleDB before creating a new StateDB
77 sdb.Close()
78
79 // call NewStateDB which should get the db at the last checkpoint state
80 // executing a Reset (discarding the last 'testkey0'&'testvalue0' data)
81 sdb, err = NewStateDB(dir, 128, TypeTxSelector, 0)
82 require.NoError(t, err)
83 v, err = sdb.db.DB().Get(k0)
84 assert.NotNil(t, err)
85 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
86 assert.Nil(t, v)
87
88 // k0 not in last
89 err = sdb.LastRead(func(sdb *Last) error {
90 _, err := sdb.DB().Get(k0)
91 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
92 return nil
93 })
94 require.NoError(t, err)
95
96 // store the same data from the beginning that has ben lost since last NewStateDB
97 tx, err = sdb.db.DB().NewTx()
98 require.NoError(t, err)
99 err = tx.Put(k0, v0)
100 require.NoError(t, err)
101 err = tx.Commit()
102 require.NoError(t, err)
103 v, err = sdb.db.DB().Get(k0)
104 require.NoError(t, err)
105 assert.Equal(t, v0, v)
106
107 // k0 yet not in last
108 err = sdb.LastRead(func(sdb *Last) error {
109 _, err := sdb.DB().Get(k0)
110 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
111 return nil
112 })
113 require.NoError(t, err)
114
115 // make checkpoints with the current state
116 bn, err := sdb.getCurrentBatch()
117 require.NoError(t, err)
118 assert.Equal(t, common.BatchNum(0), bn)
119 err = sdb.db.MakeCheckpoint()
120 require.NoError(t, err)
121 bn, err = sdb.getCurrentBatch()
122 require.NoError(t, err)
123 assert.Equal(t, common.BatchNum(1), bn)
124
125 // k0 in last
126 err = sdb.LastRead(func(sdb *Last) error {
127 v, err := sdb.DB().Get(k0)
128 require.NoError(t, err)
129 assert.Equal(t, v0, v)
130 return nil
131 })
132 require.NoError(t, err)
133
134 // write more data
135 tx, err = sdb.db.DB().NewTx()
136 require.NoError(t, err)
137 err = tx.Put(k1, v1)
138 require.NoError(t, err)
139 err = tx.Put(k0, v1) // overwrite k0 with v1
140 require.NoError(t, err)
141 err = tx.Commit()
142 require.NoError(t, err)
143
144 v, err = sdb.db.DB().Get(k1)
145 require.NoError(t, err)
146 assert.Equal(t, v1, v)
147
148 err = sdb.LastRead(func(sdb *Last) error {
149 v, err := sdb.DB().Get(k0)
150 require.NoError(t, err)
151 assert.Equal(t, v0, v)
152 return nil
153 })
154 require.NoError(t, err)
155
156 // Close PebbleDB before creating a new StateDB
157 sdb.Close()
158
159 // call NewStateDB which should get the db at the last checkpoint state
160 // executing a Reset (discarding the last 'testkey1'&'testvalue1' data)
161 sdb, err = NewStateDB(dir, 128, TypeTxSelector, 0)
162 require.NoError(t, err)
163
164 bn, err = sdb.getCurrentBatch()
165 require.NoError(t, err)
166 assert.Equal(t, common.BatchNum(1), bn)
167
168 // we closed the db without doing a checkpoint after overwriting k0, so
169 // it's back to v0
170 v, err = sdb.db.DB().Get(k0)
171 require.NoError(t, err)
172 assert.Equal(t, v0, v)
173
174 v, err = sdb.db.DB().Get(k1)
175 assert.NotNil(t, err)
176 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
177 assert.Nil(t, v)
178}
179
180func TestStateDBWithoutMT(t *testing.T) {
181 dir, err := ioutil.TempDir("", "tmpdb")
182 require.NoError(t, err)
183 defer require.NoError(t, os.RemoveAll(dir))
184
185 sdb, err := NewStateDB(dir, 128, TypeTxSelector, 0)
186 require.NoError(t, err)
187
188 // create test accounts
189 var accounts []*common.Account
190 for i := 0; i < 4; i++ {
191 accounts = append(accounts, newAccount(t, i))
192 }
193
194 // get non-existing account, expecting an error
195 unexistingAccount := common.Idx(1)
196 _, err = sdb.GetAccount(unexistingAccount)
197 assert.NotNil(t, err)
198 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
199
200 // add test accounts
201 for i := 0; i < len(accounts); i++ {
202 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
203 require.NoError(t, err)
204 }
205
206 for i := 0; i < len(accounts); i++ {
207 existingAccount := accounts[i].Idx
208 accGetted, err := sdb.GetAccount(existingAccount)
209 require.NoError(t, err)
210 assert.Equal(t, accounts[i], accGetted)
211 }
212
213 // try already existing idx and get error
214 existingAccount := common.Idx(256)
215 _, err = sdb.GetAccount(existingAccount) // check that exist
216 require.NoError(t, err)
217 _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
218 assert.NotNil(t, err)
219 assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
220
221 // update accounts
222 for i := 0; i < len(accounts); i++ {
223 accounts[i].Nonce = accounts[i].Nonce + 1
224 existingAccount = common.Idx(i)
225 _, err = sdb.UpdateAccount(existingAccount, accounts[i])
226 require.NoError(t, err)
227 }
228
229 _, err = sdb.MTGetProof(common.Idx(1))
230 assert.NotNil(t, err)
231 assert.Equal(t, ErrStateDBWithoutMT, tracerr.Unwrap(err))
232}
233
234func TestStateDBWithMT(t *testing.T) {
235 dir, err := ioutil.TempDir("", "tmpdb")
236 require.NoError(t, err)
237 defer require.NoError(t, os.RemoveAll(dir))
238
239 sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
240 require.NoError(t, err)
241
242 // create test accounts
243 var accounts []*common.Account
244 for i := 0; i < 20; i++ {
245 accounts = append(accounts, newAccount(t, i))
246 }
247
248 // get non-existing account, expecting an error
249 _, err = sdb.GetAccount(common.Idx(1))
250 assert.NotNil(t, err)
251 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
252
253 // add test accounts
254 for i := 0; i < len(accounts); i++ {
255 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
256 require.NoError(t, err)
257 }
258
259 for i := 0; i < len(accounts); i++ {
260 accGetted, err := sdb.GetAccount(accounts[i].Idx)
261 require.NoError(t, err)
262 assert.Equal(t, accounts[i], accGetted)
263 }
264
265 // try already existing idx and get error
266 _, err = sdb.GetAccount(common.Idx(256)) // check that exist
267 require.NoError(t, err)
268 _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
269 assert.NotNil(t, err)
270 assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
271
272 _, err = sdb.MTGetProof(common.Idx(256))
273 require.NoError(t, err)
274
275 // update accounts
276 for i := 0; i < len(accounts); i++ {
277 accounts[i].Nonce = accounts[i].Nonce + 1
278 _, err = sdb.UpdateAccount(accounts[i].Idx, accounts[i])
279 require.NoError(t, err)
280 }
281 a, err := sdb.GetAccount(common.Idx(256)) // check that account value has been updated
282 require.NoError(t, err)
283 assert.Equal(t, accounts[0].Nonce, a.Nonce)
284}
285
286// TestCheckpoints performs almost the same test than kvdb/kvdb_test.go
287// TestCheckpoints, but over the StateDB
288func TestCheckpoints(t *testing.T) {
289 dir, err := ioutil.TempDir("", "sdb")
290 require.NoError(t, err)
291 defer require.NoError(t, os.RemoveAll(dir))
292
293 sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
294 require.NoError(t, err)
295
296 err = sdb.Reset(0)
297 require.NoError(t, err)
298
299 // create test accounts
300 var accounts []*common.Account
301 for i := 0; i < 10; i++ {
302 accounts = append(accounts, newAccount(t, i))
303 }
304
305 // add test accounts
306 for i := 0; i < len(accounts); i++ {
307 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
308 require.NoError(t, err)
309 }
310 // account doesn't exist in Last checkpoint
311 _, err = sdb.LastGetAccount(accounts[0].Idx)
312 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
313
314 // do checkpoints and check that currentBatch is correct
315 err = sdb.MakeCheckpoint()
316 require.NoError(t, err)
317 cb, err := sdb.getCurrentBatch()
318 require.NoError(t, err)
319 assert.Equal(t, common.BatchNum(1), cb)
320
321 // account exists in Last checkpoint
322 accCur, err := sdb.GetAccount(accounts[0].Idx)
323 require.NoError(t, err)
324 accLast, err := sdb.LastGetAccount(accounts[0].Idx)
325 require.NoError(t, err)
326 assert.Equal(t, accounts[0], accLast)
327 assert.Equal(t, accCur, accLast)
328
329 for i := 1; i < 10; i++ {
330 err = sdb.MakeCheckpoint()
331 require.NoError(t, err)
332
333 cb, err = sdb.getCurrentBatch()
334 require.NoError(t, err)
335 assert.Equal(t, common.BatchNum(i+1), cb)
336 }
337
338 // printCheckpoints(t, sdb.path)
339
340 // reset checkpoint
341 err = sdb.Reset(3)
342 require.NoError(t, err)
343
344 // check that reset can be repeated (as there exist the 'current' and
345 // 'BatchNum3', from where the 'current' is a copy)
346 err = sdb.Reset(3)
347 require.NoError(t, err)
348
349 // check that currentBatch is as expected after Reset
350 cb, err = sdb.getCurrentBatch()
351 require.NoError(t, err)
352 assert.Equal(t, common.BatchNum(3), cb)
353
354 // advance one checkpoint and check that currentBatch is fine
355 err = sdb.MakeCheckpoint()
356 require.NoError(t, err)
357 cb, err = sdb.getCurrentBatch()
358 require.NoError(t, err)
359 assert.Equal(t, common.BatchNum(4), cb)
360
361 err = sdb.db.DeleteCheckpoint(common.BatchNum(1))
362 require.NoError(t, err)
363 err = sdb.db.DeleteCheckpoint(common.BatchNum(2))
364 require.NoError(t, err)
365 err = sdb.db.DeleteCheckpoint(common.BatchNum(1)) // does not exist, should return err
366 assert.NotNil(t, err)
367 err = sdb.db.DeleteCheckpoint(common.BatchNum(2)) // does not exist, should return err
368 assert.NotNil(t, err)
369
370 // Create a LocalStateDB from the initial StateDB
371 dirLocal, err := ioutil.TempDir("", "ldb")
372 require.NoError(t, err)
373 defer require.NoError(t, os.RemoveAll(dirLocal))
374 ldb, err := NewLocalStateDB(dirLocal, 128, sdb, TypeBatchBuilder, 32)
375 require.NoError(t, err)
376
377 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
378 err = ldb.Reset(4, true)
379 require.NoError(t, err)
380 // check that currentBatch is 4 after the Reset
381 cb, err = ldb.getCurrentBatch()
382 require.NoError(t, err)
383 assert.Equal(t, common.BatchNum(4), cb)
384 // advance one checkpoint in ldb
385 err = ldb.MakeCheckpoint()
386 require.NoError(t, err)
387 cb, err = ldb.getCurrentBatch()
388 require.NoError(t, err)
389 assert.Equal(t, common.BatchNum(5), cb)
390
391 // Create a 2nd LocalStateDB from the initial StateDB
392 dirLocal2, err := ioutil.TempDir("", "ldb2")
393 require.NoError(t, err)
394 defer require.NoError(t, os.RemoveAll(dirLocal2))
395 ldb2, err := NewLocalStateDB(dirLocal2, 128, sdb, TypeBatchBuilder, 32)
396 require.NoError(t, err)
397
398 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
399 err = ldb2.Reset(4, true)
400 require.NoError(t, err)
401 // check that currentBatch is 4 after the Reset
402 cb, err = ldb2.db.GetCurrentBatch()
403 require.NoError(t, err)
404 assert.Equal(t, common.BatchNum(4), cb)
405 // advance one checkpoint in ldb2
406 err = ldb2.db.MakeCheckpoint()
407 require.NoError(t, err)
408 cb, err = ldb2.db.GetCurrentBatch()
409 require.NoError(t, err)
410 assert.Equal(t, common.BatchNum(5), cb)
411
412 debug := false
413 if debug {
414 printCheckpoints(t, sdb.path)
415 printCheckpoints(t, ldb.path)
416 printCheckpoints(t, ldb2.path)
417 }
418}
419
420func TestStateDBGetAccounts(t *testing.T) {
421 dir, err := ioutil.TempDir("", "tmpdb")
422 require.NoError(t, err)
423
424 sdb, err := NewStateDB(dir, 128, TypeTxSelector, 0)
425 require.NoError(t, err)
426
427 // create test accounts
428 var accounts []common.Account
429 for i := 0; i < 16; i++ {
430 account := newAccount(t, i)
431 accounts = append(accounts, *account)
432 }
433
434 // add test accounts
435 for i := range accounts {
436 _, err = sdb.CreateAccount(accounts[i].Idx, &accounts[i])
437 require.NoError(t, err)
438 }
439
440 dbAccounts, err := sdb.TestGetAccounts()
441 require.NoError(t, err)
442 assert.Equal(t, accounts, dbAccounts)
443}
444
445func printCheckpoints(t *testing.T, path string) {
446 files, err := ioutil.ReadDir(path)
447 require.NoError(t, err)
448
449 fmt.Println(path)
450 for _, f := range files {
451 fmt.Println(" " + f.Name())
452 }
453}
454
455func bigFromStr(h string, u int) *big.Int {
456 if u == 16 {
457 h = strings.TrimPrefix(h, "0x")
458 }
459 b, ok := new(big.Int).SetString(h, u)
460 if !ok {
461 panic("bigFromStr err")
462 }
463 return b
464}
465
466func TestCheckAccountsTreeTestVectors(t *testing.T) {
467 dir, err := ioutil.TempDir("", "tmpdb")
468 require.NoError(t, err)
469 defer require.NoError(t, os.RemoveAll(dir))
470
471 sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
472 require.NoError(t, err)
473
474 ay0 := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(253), nil), big.NewInt(1))
475 // test value from js version (compatibility-canary)
476 assert.Equal(t, "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", (hex.EncodeToString(ay0.Bytes())))
477 bjjPoint0Comp := babyjub.PackSignY(true, ay0)
478 bjj0 := babyjub.PublicKeyComp(bjjPoint0Comp)
479
480 ay1 := bigFromStr("00", 16)
481 bjjPoint1Comp := babyjub.PackSignY(false, ay1)
482 bjj1 := babyjub.PublicKeyComp(bjjPoint1Comp)
483 ay2 := bigFromStr("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7", 16)
484 bjjPoint2Comp := babyjub.PackSignY(false, ay2)
485 bjj2 := babyjub.PublicKeyComp(bjjPoint2Comp)
486
487 ay3 := bigFromStr("0x10", 16) // 0x10=16
488 bjjPoint3Comp := babyjub.PackSignY(false, ay3)
489 require.NoError(t, err)
490 bjj3 := babyjub.PublicKeyComp(bjjPoint3Comp)
491 accounts := []*common.Account{
492 {
493 Idx: 1,
494 TokenID: 0xFFFFFFFF,
495 BJJ: bjj0,
496 EthAddr: ethCommon.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
497 Nonce: common.Nonce(0xFFFFFFFFFF),
498 Balance: bigFromStr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16),
499 },
500 {
501 Idx: 100,
502 TokenID: 0,
503 BJJ: bjj1,
504 EthAddr: ethCommon.HexToAddress("0x00"),
505 Nonce: common.Nonce(0),
506 Balance: bigFromStr("0", 10),
507 },
508 {
509 Idx: 0xFFFFFFFFFFFF,
510 TokenID: 3,
511 BJJ: bjj2,
512 EthAddr: ethCommon.HexToAddress("0xA3C88ac39A76789437AED31B9608da72e1bbfBF9"),
513 Nonce: common.Nonce(129),
514 Balance: bigFromStr("42000000000000000000", 10),
515 },
516 {
517 Idx: 10000,
518 TokenID: 1000,
519 BJJ: bjj3,
520 EthAddr: ethCommon.HexToAddress("0x64"),
521 Nonce: common.Nonce(1900),
522 Balance: bigFromStr("14000000000000000000", 10),
523 },
524 }
525 for i := 0; i < len(accounts); i++ {
526 _, err = accounts[i].HashValue()
527 require.NoError(t, err)
528 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
529 if err != nil {
530 log.Error(err)
531 }
532 require.NoError(t, err)
533 }
534 // root value generated by js version:
535 assert.Equal(t, "17298264051379321456969039521810887093935433569451713402227686942080129181291", sdb.MT.Root().BigInt().String())
536}
537
538// TestListCheckpoints performs almost the same test than kvdb/kvdb_test.go
539// TestListCheckpoints, but over the StateDB
540func TestListCheckpoints(t *testing.T) {
541 dir, err := ioutil.TempDir("", "tmpdb")
542 require.NoError(t, err)
543 defer require.NoError(t, os.RemoveAll(dir))
544
545 sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
546 require.NoError(t, err)
547
548 numCheckpoints := 16
549 // do checkpoints
550 for i := 0; i < numCheckpoints; i++ {
551 err = sdb.MakeCheckpoint()
552 require.NoError(t, err)
553 }
554 list, err := sdb.db.ListCheckpoints()
555 require.NoError(t, err)
556 assert.Equal(t, numCheckpoints, len(list))
557 assert.Equal(t, 1, list[0])
558 assert.Equal(t, numCheckpoints, list[len(list)-1])
559
560 numReset := 10
561 err = sdb.Reset(common.BatchNum(numReset))
562 require.NoError(t, err)
563 list, err = sdb.db.ListCheckpoints()
564 require.NoError(t, err)
565 assert.Equal(t, numReset, len(list))
566 assert.Equal(t, 1, list[0])
567 assert.Equal(t, numReset, list[len(list)-1])
568}
569
570// TestDeleteOldCheckpoints performs almost the same test than
571// kvdb/kvdb_test.go TestDeleteOldCheckpoints, but over the StateDB
572func TestDeleteOldCheckpoints(t *testing.T) {
573 dir, err := ioutil.TempDir("", "tmpdb")
574 require.NoError(t, err)
575 defer require.NoError(t, os.RemoveAll(dir))
576
577 keep := 16
578 sdb, err := NewStateDB(dir, keep, TypeSynchronizer, 32)
579 require.NoError(t, err)
580
581 numCheckpoints := 32
582 // do checkpoints and check that we never have more than `keep`
583 // checkpoints
584 for i := 0; i < numCheckpoints; i++ {
585 err = sdb.MakeCheckpoint()
586 require.NoError(t, err)
587 checkpoints, err := sdb.db.ListCheckpoints()
588 require.NoError(t, err)
589 assert.LessOrEqual(t, len(checkpoints), keep)
590 }
591}
592
593func TestCurrentIdx(t *testing.T) {
594 dir, err := ioutil.TempDir("", "tmpdb")
595 require.NoError(t, err)
596 defer require.NoError(t, os.RemoveAll(dir))
597
598 keep := 16
599 sdb, err := NewStateDB(dir, keep, TypeSynchronizer, 32)
600 require.NoError(t, err)
601
602 idx := sdb.CurrentIdx()
603 assert.Equal(t, common.Idx(255), idx)
604
605 sdb.Close()
606
607 sdb, err = NewStateDB(dir, keep, TypeSynchronizer, 32)
608 require.NoError(t, err)
609
610 idx = sdb.CurrentIdx()
611 assert.Equal(t, common.Idx(255), idx)
612
613 err = sdb.MakeCheckpoint()
614 require.NoError(t, err)
615
616 idx = sdb.CurrentIdx()
617 assert.Equal(t, common.Idx(255), idx)
618
619 sdb.Close()
620
621 sdb, err = NewStateDB(dir, keep, TypeSynchronizer, 32)
622 require.NoError(t, err)
623
624 idx = sdb.CurrentIdx()
625 assert.Equal(t, common.Idx(255), idx)
626}