arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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}