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	chainID := uint16(0)
 49	sdb, err := NewStateDB(dir, 128, 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, 128, 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, 128, 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, 128, 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, 128, 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, 128, 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(1))
295	assert.NoError(t, err)
296	err = sdb.DeleteCheckpoint(common.BatchNum(2))
297	assert.NoError(t, err)
298	err = sdb.DeleteCheckpoint(common.BatchNum(1)) // does not exist, should return err
299	assert.NotNil(t, err)
300	err = sdb.DeleteCheckpoint(common.BatchNum(2)) // 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, 128, 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, 128, 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, 128, 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, 128, 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}
472
473func TestListCheckpoints(t *testing.T) {
474	dir, err := ioutil.TempDir("", "tmpdb")
475	require.NoError(t, err)
476	defer assert.NoError(t, os.RemoveAll(dir))
477
478	chainID := uint16(0)
479	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32, chainID)
480	require.NoError(t, err)
481
482	numCheckpoints := 16
483	// do checkpoints
484	for i := 0; i < numCheckpoints; i++ {
485		err = sdb.MakeCheckpoint()
486		require.NoError(t, err)
487	}
488	list, err := sdb.listCheckpoints()
489	require.NoError(t, err)
490	assert.Equal(t, numCheckpoints, len(list))
491	assert.Equal(t, 1, list[0])
492	assert.Equal(t, numCheckpoints, list[len(list)-1])
493
494	numReset := 10
495	err = sdb.Reset(common.BatchNum(numReset))
496	require.NoError(t, err)
497	list, err = sdb.listCheckpoints()
498	require.NoError(t, err)
499	assert.Equal(t, numReset, len(list))
500	assert.Equal(t, 1, list[0])
501	assert.Equal(t, numReset, list[len(list)-1])
502}
503
504func TestDeleteOldCheckpoints(t *testing.T) {
505	dir, err := ioutil.TempDir("", "tmpdb")
506	require.NoError(t, err)
507	defer assert.NoError(t, os.RemoveAll(dir))
508
509	chainID := uint16(0)
510	keep := 16
511	sdb, err := NewStateDB(dir, keep, TypeSynchronizer, 32, chainID)
512	require.NoError(t, err)
513
514	numCheckpoints := 32
515	// do checkpoints and check that we never have more than `keep`
516	// checkpoints
517	for i := 0; i < numCheckpoints; i++ {
518		err = sdb.MakeCheckpoint()
519		require.NoError(t, err)
520		checkpoints, err := sdb.listCheckpoints()
521		require.NoError(t, err)
522		assert.LessOrEqual(t, len(checkpoints), keep)
523	}
524}