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