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 chainID := uint16(0)
49 sdb, err := NewStateDB(dir, TypeTxSelector, 0, chainID)
50 assert.NoError(t, err)
51
52 // test values
53 k0 := []byte("testkey0")
54 k1 := []byte("testkey1")
55 v0 := []byte("testvalue0")
56 v1 := []byte("testvalue1")
57
58 // store some data
59 tx, err := sdb.db.NewTx()
60 assert.NoError(t, err)
61 err = tx.Put(k0, v0)
62 assert.NoError(t, err)
63 err = tx.Commit()
64 assert.NoError(t, err)
65 v, err := sdb.db.Get(k0)
66 assert.NoError(t, err)
67 assert.Equal(t, v0, v)
68
69 // call NewStateDB which should get the db at the last checkpoint state
70 // executing a Reset (discarding the last 'testkey0'&'testvalue0' data)
71 sdb, err = NewStateDB(dir, TypeTxSelector, 0, chainID)
72 assert.NoError(t, err)
73 v, err = sdb.db.Get(k0)
74 assert.NotNil(t, err)
75 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
76 assert.Nil(t, v)
77
78 // store the same data from the beginning that has ben lost since last NewStateDB
79 tx, err = sdb.db.NewTx()
80 assert.NoError(t, err)
81 err = tx.Put(k0, v0)
82 assert.NoError(t, err)
83 err = tx.Commit()
84 assert.NoError(t, err)
85 v, err = sdb.db.Get(k0)
86 assert.NoError(t, err)
87 assert.Equal(t, v0, v)
88
89 // make checkpoints with the current state
90 bn, err := sdb.GetCurrentBatch()
91 assert.NoError(t, err)
92 assert.Equal(t, common.BatchNum(0), bn)
93 err = sdb.MakeCheckpoint()
94 assert.NoError(t, err)
95 bn, err = sdb.GetCurrentBatch()
96 assert.NoError(t, err)
97 assert.Equal(t, common.BatchNum(1), bn)
98
99 // write more data
100 tx, err = sdb.db.NewTx()
101 assert.NoError(t, err)
102 err = tx.Put(k1, v1)
103 assert.NoError(t, err)
104 err = tx.Commit()
105 assert.NoError(t, err)
106
107 v, err = sdb.db.Get(k1)
108 assert.NoError(t, err)
109 assert.Equal(t, v1, v)
110
111 // call NewStateDB which should get the db at the last checkpoint state
112 // executing a Reset (discarding the last 'testkey1'&'testvalue1' data)
113 sdb, err = NewStateDB(dir, TypeTxSelector, 0, chainID)
114 assert.NoError(t, err)
115
116 v, err = sdb.db.Get(k0)
117 assert.NoError(t, err)
118 assert.Equal(t, v0, v)
119
120 v, err = sdb.db.Get(k1)
121 assert.NotNil(t, err)
122 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
123 assert.Nil(t, v)
124}
125
126func TestStateDBWithoutMT(t *testing.T) {
127 dir, err := ioutil.TempDir("", "tmpdb")
128 require.NoError(t, err)
129 defer assert.NoError(t, os.RemoveAll(dir))
130
131 chainID := uint16(0)
132 sdb, err := NewStateDB(dir, TypeTxSelector, 0, chainID)
133 assert.NoError(t, err)
134
135 // create test accounts
136 var accounts []*common.Account
137 for i := 0; i < 4; i++ {
138 accounts = append(accounts, newAccount(t, i))
139 }
140
141 // get non-existing account, expecting an error
142 unexistingAccount := common.Idx(1)
143 _, err = sdb.GetAccount(unexistingAccount)
144 assert.NotNil(t, err)
145 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
146
147 // add test accounts
148 for i := 0; i < len(accounts); i++ {
149 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
150 assert.NoError(t, err)
151 }
152
153 for i := 0; i < len(accounts); i++ {
154 existingAccount := accounts[i].Idx
155 accGetted, err := sdb.GetAccount(existingAccount)
156 assert.NoError(t, err)
157 assert.Equal(t, accounts[i], accGetted)
158 }
159
160 // try already existing idx and get error
161 existingAccount := common.Idx(256)
162 _, err = sdb.GetAccount(existingAccount) // check that exist
163 assert.NoError(t, err)
164 _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
165 assert.NotNil(t, err)
166 assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
167
168 // update accounts
169 for i := 0; i < len(accounts); i++ {
170 accounts[i].Nonce = accounts[i].Nonce + 1
171 existingAccount = common.Idx(i)
172 _, err = sdb.UpdateAccount(existingAccount, accounts[i])
173 assert.NoError(t, err)
174 }
175
176 _, err = sdb.MTGetProof(common.Idx(1))
177 assert.NotNil(t, err)
178 assert.Equal(t, ErrStateDBWithoutMT, tracerr.Unwrap(err))
179}
180
181func TestStateDBWithMT(t *testing.T) {
182 dir, err := ioutil.TempDir("", "tmpdb")
183 require.NoError(t, err)
184 defer assert.NoError(t, os.RemoveAll(dir))
185
186 chainID := uint16(0)
187 sdb, err := NewStateDB(dir, TypeSynchronizer, 32, chainID)
188 assert.NoError(t, err)
189
190 // create test accounts
191 var accounts []*common.Account
192 for i := 0; i < 20; i++ {
193 accounts = append(accounts, newAccount(t, i))
194 }
195
196 // get non-existing account, expecting an error
197 _, err = sdb.GetAccount(common.Idx(1))
198 assert.NotNil(t, err)
199 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
200
201 // add test accounts
202 for i := 0; i < len(accounts); i++ {
203 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
204 assert.NoError(t, err)
205 }
206
207 for i := 0; i < len(accounts); i++ {
208 accGetted, err := sdb.GetAccount(accounts[i].Idx)
209 assert.NoError(t, err)
210 assert.Equal(t, accounts[i], accGetted)
211 }
212
213 // try already existing idx and get error
214 _, err = sdb.GetAccount(common.Idx(256)) // check that exist
215 assert.NoError(t, err)
216 _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
217 assert.NotNil(t, err)
218 assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
219
220 _, err = sdb.MTGetProof(common.Idx(256))
221 assert.NoError(t, err)
222
223 // update accounts
224 for i := 0; i < len(accounts); i++ {
225 accounts[i].Nonce = accounts[i].Nonce + 1
226 _, err = sdb.UpdateAccount(accounts[i].Idx, accounts[i])
227 assert.NoError(t, err)
228 }
229 a, err := sdb.GetAccount(common.Idx(256)) // check that account value has been updated
230 assert.NoError(t, err)
231 assert.Equal(t, accounts[0].Nonce, a.Nonce)
232}
233
234func TestCheckpoints(t *testing.T) {
235 dir, err := ioutil.TempDir("", "sdb")
236 require.NoError(t, err)
237 defer assert.NoError(t, os.RemoveAll(dir))
238
239 chainID := uint16(0)
240 sdb, err := NewStateDB(dir, TypeSynchronizer, 32, chainID)
241 assert.NoError(t, err)
242
243 // create test accounts
244 var accounts []*common.Account
245 for i := 0; i < 10; i++ {
246 accounts = append(accounts, newAccount(t, i))
247 }
248
249 // add test accounts
250 for i := 0; i < len(accounts); i++ {
251 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
252 assert.NoError(t, err)
253 }
254
255 // do checkpoints and check that currentBatch is correct
256 err = sdb.MakeCheckpoint()
257 assert.NoError(t, err)
258 cb, err := sdb.GetCurrentBatch()
259 assert.NoError(t, err)
260 assert.Equal(t, common.BatchNum(1), cb)
261
262 for i := 1; i < 10; i++ {
263 err = sdb.MakeCheckpoint()
264 assert.NoError(t, err)
265
266 cb, err = sdb.GetCurrentBatch()
267 assert.NoError(t, err)
268 assert.Equal(t, common.BatchNum(i+1), cb)
269 }
270
271 // printCheckpoints(t, sdb.path)
272
273 // reset checkpoint
274 err = sdb.Reset(3)
275 assert.NoError(t, err)
276
277 // check that reset can be repeated (as there exist the 'current' and
278 // 'BatchNum3', from where the 'current' is a copy)
279 err = sdb.Reset(3)
280 require.NoError(t, err)
281
282 // check that currentBatch is as expected after Reset
283 cb, err = sdb.GetCurrentBatch()
284 assert.NoError(t, err)
285 assert.Equal(t, common.BatchNum(3), cb)
286
287 // advance one checkpoint and check that currentBatch is fine
288 err = sdb.MakeCheckpoint()
289 assert.NoError(t, err)
290 cb, err = sdb.GetCurrentBatch()
291 assert.NoError(t, err)
292 assert.Equal(t, common.BatchNum(4), cb)
293
294 err = sdb.DeleteCheckpoint(common.BatchNum(9))
295 assert.NoError(t, err)
296 err = sdb.DeleteCheckpoint(common.BatchNum(10))
297 assert.NoError(t, err)
298 err = sdb.DeleteCheckpoint(common.BatchNum(9)) // does not exist, should return err
299 assert.NotNil(t, err)
300 err = sdb.DeleteCheckpoint(common.BatchNum(11)) // does not exist, should return err
301 assert.NotNil(t, err)
302
303 // Create a LocalStateDB from the initial StateDB
304 dirLocal, err := ioutil.TempDir("", "ldb")
305 require.NoError(t, err)
306 defer assert.NoError(t, os.RemoveAll(dirLocal))
307 ldb, err := NewLocalStateDB(dirLocal, sdb, TypeBatchBuilder, 32)
308 assert.NoError(t, err)
309
310 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
311 err = ldb.Reset(4, true)
312 assert.NoError(t, err)
313 // check that currentBatch is 4 after the Reset
314 cb, err = ldb.GetCurrentBatch()
315 assert.NoError(t, err)
316 assert.Equal(t, common.BatchNum(4), cb)
317 // advance one checkpoint in ldb
318 err = ldb.MakeCheckpoint()
319 assert.NoError(t, err)
320 cb, err = ldb.GetCurrentBatch()
321 assert.NoError(t, err)
322 assert.Equal(t, common.BatchNum(5), cb)
323
324 // Create a 2nd LocalStateDB from the initial StateDB
325 dirLocal2, err := ioutil.TempDir("", "ldb2")
326 require.NoError(t, err)
327 defer assert.NoError(t, os.RemoveAll(dirLocal2))
328 ldb2, err := NewLocalStateDB(dirLocal2, sdb, TypeBatchBuilder, 32)
329 assert.NoError(t, err)
330
331 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
332 err = ldb2.Reset(4, true)
333 assert.NoError(t, err)
334 // check that currentBatch is 4 after the Reset
335 cb, err = ldb2.GetCurrentBatch()
336 assert.NoError(t, err)
337 assert.Equal(t, common.BatchNum(4), cb)
338 // advance one checkpoint in ldb2
339 err = ldb2.MakeCheckpoint()
340 assert.NoError(t, err)
341 cb, err = ldb2.GetCurrentBatch()
342 assert.NoError(t, err)
343 assert.Equal(t, common.BatchNum(5), cb)
344
345 debug := false
346 if debug {
347 printCheckpoints(t, sdb.path)
348 printCheckpoints(t, ldb.path)
349 printCheckpoints(t, ldb2.path)
350 }
351}
352
353func TestStateDBGetAccounts(t *testing.T) {
354 dir, err := ioutil.TempDir("", "tmpdb")
355 require.NoError(t, err)
356
357 chainID := uint16(0)
358 sdb, err := NewStateDB(dir, TypeTxSelector, 0, chainID)
359 assert.NoError(t, err)
360
361 // create test accounts
362 var accounts []common.Account
363 for i := 0; i < 16; i++ {
364 account := newAccount(t, i)
365 accounts = append(accounts, *account)
366 }
367
368 // add test accounts
369 for i := range accounts {
370 _, err = sdb.CreateAccount(accounts[i].Idx, &accounts[i])
371 require.NoError(t, err)
372 }
373
374 dbAccounts, err := sdb.GetAccounts()
375 require.NoError(t, err)
376 assert.Equal(t, accounts, dbAccounts)
377}
378
379func printCheckpoints(t *testing.T, path string) {
380 files, err := ioutil.ReadDir(path)
381 assert.NoError(t, err)
382
383 fmt.Println(path)
384 for _, f := range files {
385 fmt.Println(" " + f.Name())
386 }
387}
388
389func bigFromStr(h string, u int) *big.Int {
390 if u == 16 {
391 h = strings.TrimPrefix(h, "0x")
392 }
393 b, ok := new(big.Int).SetString(h, u)
394 if !ok {
395 panic("bigFromStr err")
396 }
397 return b
398}
399
400func TestCheckAccountsTreeTestVectors(t *testing.T) {
401 dir, err := ioutil.TempDir("", "tmpdb")
402 require.NoError(t, err)
403 defer assert.NoError(t, os.RemoveAll(dir))
404
405 chainID := uint16(0)
406 sdb, err := NewStateDB(dir, TypeSynchronizer, 32, chainID)
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}