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