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/tracerr"
16 "github.com/iden3/go-iden3-crypto/babyjub"
17 "github.com/iden3/go-merkletree/db"
18 "github.com/stretchr/testify/assert"
19 "github.com/stretchr/testify/require"
20)
21
22func newAccount(t *testing.T, i int) *common.Account {
23 var sk babyjub.PrivateKey
24 _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
25 require.Nil(t, err)
26 pk := sk.Public()
27
28 key, err := ethCrypto.GenerateKey()
29 require.Nil(t, err)
30 address := ethCrypto.PubkeyToAddress(key.PublicKey)
31
32 return &common.Account{
33 Idx: common.Idx(256 + i),
34 TokenID: common.TokenID(i),
35 Nonce: common.Nonce(i),
36 Balance: big.NewInt(1000),
37 PublicKey: pk,
38 EthAddr: address,
39 }
40}
41
42func TestNewStateDBIntermediateState(t *testing.T) {
43 dir, err := ioutil.TempDir("", "tmpdb")
44 require.Nil(t, err)
45 defer assert.Nil(t, os.RemoveAll(dir))
46
47 sdb, err := NewStateDB(dir, TypeTxSelector, 0)
48 assert.Nil(t, err)
49
50 // test values
51 k0 := []byte("testkey0")
52 k1 := []byte("testkey1")
53 v0 := []byte("testvalue0")
54 v1 := []byte("testvalue1")
55
56 // store some data
57 tx, err := sdb.db.NewTx()
58 assert.Nil(t, err)
59 err = tx.Put(k0, v0)
60 assert.Nil(t, err)
61 err = tx.Commit()
62 assert.Nil(t, err)
63 v, err := sdb.db.Get(k0)
64 assert.Nil(t, err)
65 assert.Equal(t, v0, v)
66
67 // call NewStateDB which should get the db at the last checkpoint state
68 // executing a Reset (discarding the last 'testkey0'&'testvalue0' data)
69 sdb, err = NewStateDB(dir, TypeTxSelector, 0)
70 assert.Nil(t, err)
71 v, err = sdb.db.Get(k0)
72 assert.NotNil(t, err)
73 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
74 assert.Nil(t, v)
75
76 // store the same data from the beginning that has ben lost since last NewStateDB
77 tx, err = sdb.db.NewTx()
78 assert.Nil(t, err)
79 err = tx.Put(k0, v0)
80 assert.Nil(t, err)
81 err = tx.Commit()
82 assert.Nil(t, err)
83 v, err = sdb.db.Get(k0)
84 assert.Nil(t, err)
85 assert.Equal(t, v0, v)
86
87 // make checkpoints with the current state
88 bn, err := sdb.GetCurrentBatch()
89 assert.Nil(t, err)
90 assert.Equal(t, common.BatchNum(0), bn)
91 err = sdb.MakeCheckpoint()
92 assert.Nil(t, err)
93 bn, err = sdb.GetCurrentBatch()
94 assert.Nil(t, err)
95 assert.Equal(t, common.BatchNum(1), bn)
96
97 // write more data
98 tx, err = sdb.db.NewTx()
99 assert.Nil(t, err)
100 err = tx.Put(k1, v1)
101 assert.Nil(t, err)
102 err = tx.Commit()
103 assert.Nil(t, err)
104
105 v, err = sdb.db.Get(k1)
106 assert.Nil(t, err)
107 assert.Equal(t, v1, v)
108
109 // call NewStateDB which should get the db at the last checkpoint state
110 // executing a Reset (discarding the last 'testkey1'&'testvalue1' data)
111 sdb, err = NewStateDB(dir, TypeTxSelector, 0)
112 assert.Nil(t, err)
113
114 v, err = sdb.db.Get(k0)
115 assert.Nil(t, err)
116 assert.Equal(t, v0, v)
117
118 v, err = sdb.db.Get(k1)
119 assert.NotNil(t, err)
120 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
121 assert.Nil(t, v)
122}
123
124func TestStateDBWithoutMT(t *testing.T) {
125 dir, err := ioutil.TempDir("", "tmpdb")
126 require.Nil(t, err)
127 defer assert.Nil(t, os.RemoveAll(dir))
128
129 sdb, err := NewStateDB(dir, TypeTxSelector, 0)
130 assert.Nil(t, err)
131
132 // create test accounts
133 var accounts []*common.Account
134 for i := 0; i < 4; i++ {
135 accounts = append(accounts, newAccount(t, i))
136 }
137
138 // get non-existing account, expecting an error
139 unexistingAccount := common.Idx(1)
140 _, err = sdb.GetAccount(unexistingAccount)
141 assert.NotNil(t, err)
142 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
143
144 // add test accounts
145 for i := 0; i < len(accounts); i++ {
146 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
147 assert.Nil(t, err)
148 }
149
150 for i := 0; i < len(accounts); i++ {
151 existingAccount := accounts[i].Idx
152 accGetted, err := sdb.GetAccount(existingAccount)
153 assert.Nil(t, err)
154 assert.Equal(t, accounts[i], accGetted)
155 }
156
157 // try already existing idx and get error
158 existingAccount := common.Idx(256)
159 _, err = sdb.GetAccount(existingAccount) // check that exist
160 assert.Nil(t, err)
161 _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
162 assert.NotNil(t, err)
163 assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
164
165 // update accounts
166 for i := 0; i < len(accounts); i++ {
167 accounts[i].Nonce = accounts[i].Nonce + 1
168 existingAccount = common.Idx(i)
169 _, err = sdb.UpdateAccount(existingAccount, accounts[i])
170 assert.Nil(t, err)
171 }
172
173 _, err = sdb.MTGetProof(common.Idx(1))
174 assert.NotNil(t, err)
175 assert.Equal(t, ErrStateDBWithoutMT, tracerr.Unwrap(err))
176}
177
178func TestStateDBWithMT(t *testing.T) {
179 dir, err := ioutil.TempDir("", "tmpdb")
180 require.Nil(t, err)
181 defer assert.Nil(t, os.RemoveAll(dir))
182
183 sdb, err := NewStateDB(dir, TypeSynchronizer, 32)
184 assert.Nil(t, err)
185
186 // create test accounts
187 var accounts []*common.Account
188 for i := 0; i < 20; i++ {
189 accounts = append(accounts, newAccount(t, i))
190 }
191
192 // get non-existing account, expecting an error
193 _, err = sdb.GetAccount(common.Idx(1))
194 assert.NotNil(t, err)
195 assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
196
197 // add test accounts
198 for i := 0; i < len(accounts); i++ {
199 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
200 assert.Nil(t, err)
201 }
202
203 for i := 0; i < len(accounts); i++ {
204 accGetted, err := sdb.GetAccount(accounts[i].Idx)
205 assert.Nil(t, err)
206 assert.Equal(t, accounts[i], accGetted)
207 }
208
209 // try already existing idx and get error
210 _, err = sdb.GetAccount(common.Idx(256)) // check that exist
211 assert.Nil(t, err)
212 _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
213 assert.NotNil(t, err)
214 assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
215
216 _, err = sdb.MTGetProof(common.Idx(256))
217 assert.Nil(t, err)
218
219 // update accounts
220 for i := 0; i < len(accounts); i++ {
221 accounts[i].Nonce = accounts[i].Nonce + 1
222 _, err = sdb.UpdateAccount(accounts[i].Idx, accounts[i])
223 assert.Nil(t, err)
224 }
225 a, err := sdb.GetAccount(common.Idx(256)) // check that account value has been updated
226 assert.Nil(t, err)
227 assert.Equal(t, accounts[0].Nonce, a.Nonce)
228}
229
230func TestCheckpoints(t *testing.T) {
231 dir, err := ioutil.TempDir("", "sdb")
232 require.Nil(t, err)
233 defer assert.Nil(t, os.RemoveAll(dir))
234
235 sdb, err := NewStateDB(dir, TypeSynchronizer, 32)
236 assert.Nil(t, err)
237
238 // create test accounts
239 var accounts []*common.Account
240 for i := 0; i < 10; i++ {
241 accounts = append(accounts, newAccount(t, i))
242 }
243
244 // add test accounts
245 for i := 0; i < len(accounts); i++ {
246 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
247 assert.Nil(t, err)
248 }
249
250 // do checkpoints and check that currentBatch is correct
251 err = sdb.MakeCheckpoint()
252 assert.Nil(t, err)
253 cb, err := sdb.GetCurrentBatch()
254 assert.Nil(t, err)
255 assert.Equal(t, common.BatchNum(1), cb)
256
257 for i := 1; i < 10; i++ {
258 err = sdb.MakeCheckpoint()
259 assert.Nil(t, err)
260
261 cb, err = sdb.GetCurrentBatch()
262 assert.Nil(t, err)
263 assert.Equal(t, common.BatchNum(i+1), cb)
264 }
265
266 // printCheckpoints(t, sdb.path)
267
268 // reset checkpoint
269 err = sdb.Reset(3)
270 assert.Nil(t, err)
271
272 // check that reset can be repeated (as there exist the 'current' and
273 // 'BatchNum3', from where the 'current' is a copy)
274 err = sdb.Reset(3)
275 require.Nil(t, err)
276
277 // check that currentBatch is as expected after Reset
278 cb, err = sdb.GetCurrentBatch()
279 assert.Nil(t, err)
280 assert.Equal(t, common.BatchNum(3), cb)
281
282 // advance one checkpoint and check that currentBatch is fine
283 err = sdb.MakeCheckpoint()
284 assert.Nil(t, err)
285 cb, err = sdb.GetCurrentBatch()
286 assert.Nil(t, err)
287 assert.Equal(t, common.BatchNum(4), cb)
288
289 err = sdb.DeleteCheckpoint(common.BatchNum(9))
290 assert.Nil(t, err)
291 err = sdb.DeleteCheckpoint(common.BatchNum(10))
292 assert.Nil(t, err)
293 err = sdb.DeleteCheckpoint(common.BatchNum(9)) // does not exist, should return err
294 assert.NotNil(t, err)
295 err = sdb.DeleteCheckpoint(common.BatchNum(11)) // does not exist, should return err
296 assert.NotNil(t, err)
297
298 // Create a LocalStateDB from the initial StateDB
299 dirLocal, err := ioutil.TempDir("", "ldb")
300 require.Nil(t, err)
301 defer assert.Nil(t, os.RemoveAll(dirLocal))
302 ldb, err := NewLocalStateDB(dirLocal, sdb, TypeBatchBuilder, 32)
303 assert.Nil(t, err)
304
305 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
306 err = ldb.Reset(4, true)
307 assert.Nil(t, err)
308 // check that currentBatch is 4 after the Reset
309 cb, err = ldb.GetCurrentBatch()
310 assert.Nil(t, err)
311 assert.Equal(t, common.BatchNum(4), cb)
312 // advance one checkpoint in ldb
313 err = ldb.MakeCheckpoint()
314 assert.Nil(t, err)
315 cb, err = ldb.GetCurrentBatch()
316 assert.Nil(t, err)
317 assert.Equal(t, common.BatchNum(5), cb)
318
319 // Create a 2nd LocalStateDB from the initial StateDB
320 dirLocal2, err := ioutil.TempDir("", "ldb2")
321 require.Nil(t, err)
322 defer assert.Nil(t, os.RemoveAll(dirLocal2))
323 ldb2, err := NewLocalStateDB(dirLocal2, sdb, TypeBatchBuilder, 32)
324 assert.Nil(t, err)
325
326 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
327 err = ldb2.Reset(4, true)
328 assert.Nil(t, err)
329 // check that currentBatch is 4 after the Reset
330 cb, err = ldb2.GetCurrentBatch()
331 assert.Nil(t, err)
332 assert.Equal(t, common.BatchNum(4), cb)
333 // advance one checkpoint in ldb2
334 err = ldb2.MakeCheckpoint()
335 assert.Nil(t, err)
336 cb, err = ldb2.GetCurrentBatch()
337 assert.Nil(t, err)
338 assert.Equal(t, common.BatchNum(5), cb)
339
340 debug := false
341 if debug {
342 printCheckpoints(t, sdb.path)
343 printCheckpoints(t, ldb.path)
344 printCheckpoints(t, ldb2.path)
345 }
346}
347
348func TestStateDBGetAccounts(t *testing.T) {
349 dir, err := ioutil.TempDir("", "tmpdb")
350 require.Nil(t, err)
351
352 sdb, err := NewStateDB(dir, TypeTxSelector, 0)
353 assert.Nil(t, err)
354
355 // create test accounts
356 var accounts []common.Account
357 for i := 0; i < 16; i++ {
358 account := newAccount(t, i)
359 accounts = append(accounts, *account)
360 }
361
362 // add test accounts
363 for i := range accounts {
364 _, err = sdb.CreateAccount(accounts[i].Idx, &accounts[i])
365 require.Nil(t, err)
366 }
367
368 dbAccounts, err := sdb.GetAccounts()
369 require.Nil(t, err)
370 assert.Equal(t, accounts, dbAccounts)
371}
372
373func printCheckpoints(t *testing.T, path string) {
374 files, err := ioutil.ReadDir(path)
375 assert.Nil(t, err)
376
377 fmt.Println(path)
378 for _, f := range files {
379 fmt.Println(" " + f.Name())
380 }
381}
382
383func bigFromStr(h string, u int) *big.Int {
384 if u == 16 {
385 h = strings.TrimPrefix(h, "0x")
386 }
387 b, ok := new(big.Int).SetString(h, u)
388 if !ok {
389 panic("bigFromStr err")
390 }
391 return b
392}
393
394func TestCheckAccountsTreeTestVectors(t *testing.T) {
395 dir, err := ioutil.TempDir("", "tmpdb")
396 require.Nil(t, err)
397 defer assert.Nil(t, os.RemoveAll(dir))
398
399 sdb, err := NewStateDB(dir, TypeSynchronizer, 32)
400 require.Nil(t, err)
401
402 ay0 := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(253), nil), big.NewInt(1))
403 // test value from js version (compatibility-canary)
404 assert.Equal(t, "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", (hex.EncodeToString(ay0.Bytes())))
405 bjj0, err := babyjub.PointFromSignAndY(true, ay0)
406 require.Nil(t, err)
407
408 ay1 := bigFromStr("00", 16)
409 bjj1, err := babyjub.PointFromSignAndY(false, ay1)
410 require.Nil(t, err)
411 ay2 := bigFromStr("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7", 16)
412 bjj2, err := babyjub.PointFromSignAndY(false, ay2)
413 require.Nil(t, err)
414
415 ay3 := bigFromStr("0x10", 16) // 0x10=16
416 bjj3, err := babyjub.PointFromSignAndY(false, ay3)
417 require.Nil(t, err)
418 accounts := []*common.Account{
419 {
420 Idx: 1,
421 TokenID: 0xFFFFFFFF,
422 PublicKey: (*babyjub.PublicKey)(bjj0),
423 EthAddr: ethCommon.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
424 Nonce: common.Nonce(0xFFFFFFFFFF),
425 Balance: bigFromStr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16),
426 },
427 {
428 Idx: 100,
429 TokenID: 0,
430 PublicKey: (*babyjub.PublicKey)(bjj1),
431 EthAddr: ethCommon.HexToAddress("0x00"),
432 Nonce: common.Nonce(0),
433 Balance: bigFromStr("0", 10),
434 },
435 {
436 Idx: 0xFFFFFFFFFFFF,
437 TokenID: 3,
438 PublicKey: (*babyjub.PublicKey)(bjj2),
439 EthAddr: ethCommon.HexToAddress("0xA3C88ac39A76789437AED31B9608da72e1bbfBF9"),
440 Nonce: common.Nonce(129),
441 Balance: bigFromStr("42000000000000000000", 10),
442 },
443 {
444 Idx: 10000,
445 TokenID: 1000,
446 PublicKey: (*babyjub.PublicKey)(bjj3),
447 EthAddr: ethCommon.HexToAddress("0x64"),
448 Nonce: common.Nonce(1900),
449 Balance: bigFromStr("14000000000000000000", 10),
450 },
451 }
452 for i := 0; i < len(accounts); i++ {
453 _, err = accounts[i].HashValue()
454 require.Nil(t, err)
455 _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
456 require.Nil(t, err)
457 }
458 // root value generated by js version:
459 assert.Equal(t, "17298264051379321456969039521810887093935433569451713402227686942080129181291", sdb.mt.Root().BigInt().String())
460}