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