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		BJJ:     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	// Close PebbleDB before creating a new StateDB
 69	err = sdb.db.DB().Pebble().Close()
 70	require.NoError(t, err)
 71
 72	// call NewStateDB which should get the db at the last checkpoint state
 73	// executing a Reset (discarding the last 'testkey0'&'testvalue0' data)
 74	sdb, err = NewStateDB(dir, 128, TypeTxSelector, 0)
 75	require.NoError(t, err)
 76	v, err = sdb.db.DB().Get(k0)
 77	assert.NotNil(t, err)
 78	assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
 79	assert.Nil(t, v)
 80
 81	// store the same data from the beginning that has ben lost since last NewStateDB
 82	tx, err = sdb.db.DB().NewTx()
 83	assert.NoError(t, err)
 84	err = tx.Put(k0, v0)
 85	assert.NoError(t, err)
 86	err = tx.Commit()
 87	assert.NoError(t, err)
 88	v, err = sdb.db.DB().Get(k0)
 89	assert.NoError(t, err)
 90	assert.Equal(t, v0, v)
 91
 92	// make checkpoints with the current state
 93	bn, err := sdb.db.GetCurrentBatch()
 94	assert.NoError(t, err)
 95	assert.Equal(t, common.BatchNum(0), bn)
 96	err = sdb.db.MakeCheckpoint()
 97	assert.NoError(t, err)
 98	bn, err = sdb.db.GetCurrentBatch()
 99	assert.NoError(t, err)
100	assert.Equal(t, common.BatchNum(1), bn)
101
102	// write more data
103	tx, err = sdb.db.DB().NewTx()
104	assert.NoError(t, err)
105	err = tx.Put(k1, v1)
106	assert.NoError(t, err)
107	err = tx.Commit()
108	assert.NoError(t, err)
109
110	v, err = sdb.db.DB().Get(k1)
111	assert.NoError(t, err)
112	assert.Equal(t, v1, v)
113
114	// Close PebbleDB before creating a new StateDB
115	err = sdb.db.DB().Pebble().Close()
116	require.NoError(t, err)
117
118	// call NewStateDB which should get the db at the last checkpoint state
119	// executing a Reset (discarding the last 'testkey1'&'testvalue1' data)
120	sdb, err = NewStateDB(dir, 128, TypeTxSelector, 0)
121	require.NoError(t, err)
122
123	bn, err = sdb.db.GetCurrentBatch()
124	assert.NoError(t, err)
125	assert.Equal(t, common.BatchNum(1), bn)
126
127	v, err = sdb.db.DB().Get(k0)
128	assert.NoError(t, err)
129	assert.Equal(t, v0, v)
130
131	v, err = sdb.db.DB().Get(k1)
132	assert.NotNil(t, err)
133	assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
134	assert.Nil(t, v)
135}
136
137func TestStateDBWithoutMT(t *testing.T) {
138	dir, err := ioutil.TempDir("", "tmpdb")
139	require.NoError(t, err)
140	defer assert.NoError(t, os.RemoveAll(dir))
141
142	sdb, err := NewStateDB(dir, 128, TypeTxSelector, 0)
143	assert.NoError(t, err)
144
145	// create test accounts
146	var accounts []*common.Account
147	for i := 0; i < 4; i++ {
148		accounts = append(accounts, newAccount(t, i))
149	}
150
151	// get non-existing account, expecting an error
152	unexistingAccount := common.Idx(1)
153	_, err = sdb.GetAccount(unexistingAccount)
154	assert.NotNil(t, err)
155	assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
156
157	// add test accounts
158	for i := 0; i < len(accounts); i++ {
159		_, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
160		assert.NoError(t, err)
161	}
162
163	for i := 0; i < len(accounts); i++ {
164		existingAccount := accounts[i].Idx
165		accGetted, err := sdb.GetAccount(existingAccount)
166		assert.NoError(t, err)
167		assert.Equal(t, accounts[i], accGetted)
168	}
169
170	// try already existing idx and get error
171	existingAccount := common.Idx(256)
172	_, err = sdb.GetAccount(existingAccount) // check that exist
173	assert.NoError(t, err)
174	_, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
175	assert.NotNil(t, err)
176	assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
177
178	// update accounts
179	for i := 0; i < len(accounts); i++ {
180		accounts[i].Nonce = accounts[i].Nonce + 1
181		existingAccount = common.Idx(i)
182		_, err = sdb.UpdateAccount(existingAccount, accounts[i])
183		assert.NoError(t, err)
184	}
185
186	_, err = sdb.MTGetProof(common.Idx(1))
187	assert.NotNil(t, err)
188	assert.Equal(t, ErrStateDBWithoutMT, tracerr.Unwrap(err))
189}
190
191func TestStateDBWithMT(t *testing.T) {
192	dir, err := ioutil.TempDir("", "tmpdb")
193	require.NoError(t, err)
194	defer assert.NoError(t, os.RemoveAll(dir))
195
196	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
197	assert.NoError(t, err)
198
199	// create test accounts
200	var accounts []*common.Account
201	for i := 0; i < 20; i++ {
202		accounts = append(accounts, newAccount(t, i))
203	}
204
205	// get non-existing account, expecting an error
206	_, err = sdb.GetAccount(common.Idx(1))
207	assert.NotNil(t, err)
208	assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
209
210	// add test accounts
211	for i := 0; i < len(accounts); i++ {
212		_, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
213		assert.NoError(t, err)
214	}
215
216	for i := 0; i < len(accounts); i++ {
217		accGetted, err := sdb.GetAccount(accounts[i].Idx)
218		assert.NoError(t, err)
219		assert.Equal(t, accounts[i], accGetted)
220	}
221
222	// try already existing idx and get error
223	_, err = sdb.GetAccount(common.Idx(256)) // check that exist
224	assert.NoError(t, err)
225	_, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
226	assert.NotNil(t, err)
227	assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
228
229	_, err = sdb.MTGetProof(common.Idx(256))
230	assert.NoError(t, err)
231
232	// update accounts
233	for i := 0; i < len(accounts); i++ {
234		accounts[i].Nonce = accounts[i].Nonce + 1
235		_, err = sdb.UpdateAccount(accounts[i].Idx, accounts[i])
236		assert.NoError(t, err)
237	}
238	a, err := sdb.GetAccount(common.Idx(256)) // check that account value has been updated
239	assert.NoError(t, err)
240	assert.Equal(t, accounts[0].Nonce, a.Nonce)
241}
242
243// TestCheckpoints performs almost the same test than kvdb/kvdb_test.go
244// TestCheckpoints, but over the StateDB
245func TestCheckpoints(t *testing.T) {
246	dir, err := ioutil.TempDir("", "sdb")
247	require.NoError(t, err)
248	defer assert.NoError(t, os.RemoveAll(dir))
249
250	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
251	assert.NoError(t, err)
252
253	// create test accounts
254	var accounts []*common.Account
255	for i := 0; i < 10; i++ {
256		accounts = append(accounts, newAccount(t, i))
257	}
258
259	// add test accounts
260	for i := 0; i < len(accounts); i++ {
261		_, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
262		assert.NoError(t, err)
263	}
264
265	// do checkpoints and check that currentBatch is correct
266	err = sdb.db.MakeCheckpoint()
267	assert.NoError(t, err)
268	cb, err := sdb.db.GetCurrentBatch()
269	assert.NoError(t, err)
270	assert.Equal(t, common.BatchNum(1), cb)
271
272	for i := 1; i < 10; i++ {
273		err = sdb.db.MakeCheckpoint()
274		assert.NoError(t, err)
275
276		cb, err = sdb.db.GetCurrentBatch()
277		assert.NoError(t, err)
278		assert.Equal(t, common.BatchNum(i+1), cb)
279	}
280
281	// printCheckpoints(t, sdb.path)
282
283	// reset checkpoint
284	err = sdb.Reset(3)
285	assert.NoError(t, err)
286
287	// check that reset can be repeated (as there exist the 'current' and
288	// 'BatchNum3', from where the 'current' is a copy)
289	err = sdb.Reset(3)
290	require.NoError(t, err)
291
292	// check that currentBatch is as expected after Reset
293	cb, err = sdb.db.GetCurrentBatch()
294	assert.NoError(t, err)
295	assert.Equal(t, common.BatchNum(3), cb)
296
297	// advance one checkpoint and check that currentBatch is fine
298	err = sdb.db.MakeCheckpoint()
299	assert.NoError(t, err)
300	cb, err = sdb.db.GetCurrentBatch()
301	assert.NoError(t, err)
302	assert.Equal(t, common.BatchNum(4), cb)
303
304	err = sdb.db.DeleteCheckpoint(common.BatchNum(1))
305	assert.NoError(t, err)
306	err = sdb.db.DeleteCheckpoint(common.BatchNum(2))
307	assert.NoError(t, err)
308	err = sdb.db.DeleteCheckpoint(common.BatchNum(1)) // does not exist, should return err
309	assert.NotNil(t, err)
310	err = sdb.db.DeleteCheckpoint(common.BatchNum(2)) // does not exist, should return err
311	assert.NotNil(t, err)
312
313	// Create a LocalStateDB from the initial StateDB
314	dirLocal, err := ioutil.TempDir("", "ldb")
315	require.NoError(t, err)
316	defer assert.NoError(t, os.RemoveAll(dirLocal))
317	ldb, err := NewLocalStateDB(dirLocal, 128, sdb, TypeBatchBuilder, 32)
318	assert.NoError(t, err)
319
320	// get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
321	err = ldb.Reset(4, true)
322	assert.NoError(t, err)
323	// check that currentBatch is 4 after the Reset
324	cb, err = ldb.db.GetCurrentBatch()
325	assert.NoError(t, err)
326	assert.Equal(t, common.BatchNum(4), cb)
327	// advance one checkpoint in ldb
328	err = ldb.db.MakeCheckpoint()
329	assert.NoError(t, err)
330	cb, err = ldb.db.GetCurrentBatch()
331	assert.NoError(t, err)
332	assert.Equal(t, common.BatchNum(5), cb)
333
334	// Create a 2nd LocalStateDB from the initial StateDB
335	dirLocal2, err := ioutil.TempDir("", "ldb2")
336	require.NoError(t, err)
337	defer assert.NoError(t, os.RemoveAll(dirLocal2))
338	ldb2, err := NewLocalStateDB(dirLocal2, 128, sdb, TypeBatchBuilder, 32)
339	assert.NoError(t, err)
340
341	// get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
342	err = ldb2.Reset(4, true)
343	assert.NoError(t, err)
344	// check that currentBatch is 4 after the Reset
345	cb, err = ldb2.db.GetCurrentBatch()
346	assert.NoError(t, err)
347	assert.Equal(t, common.BatchNum(4), cb)
348	// advance one checkpoint in ldb2
349	err = ldb2.db.MakeCheckpoint()
350	assert.NoError(t, err)
351	cb, err = ldb2.db.GetCurrentBatch()
352	assert.NoError(t, err)
353	assert.Equal(t, common.BatchNum(5), cb)
354
355	debug := false
356	if debug {
357		printCheckpoints(t, sdb.path)
358		printCheckpoints(t, ldb.path)
359		printCheckpoints(t, ldb2.path)
360	}
361}
362
363func TestStateDBGetAccounts(t *testing.T) {
364	dir, err := ioutil.TempDir("", "tmpdb")
365	require.NoError(t, err)
366
367	sdb, err := NewStateDB(dir, 128, TypeTxSelector, 0)
368	assert.NoError(t, err)
369
370	// create test accounts
371	var accounts []common.Account
372	for i := 0; i < 16; i++ {
373		account := newAccount(t, i)
374		accounts = append(accounts, *account)
375	}
376
377	// add test accounts
378	for i := range accounts {
379		_, err = sdb.CreateAccount(accounts[i].Idx, &accounts[i])
380		require.NoError(t, err)
381	}
382
383	dbAccounts, err := sdb.GetAccounts()
384	require.NoError(t, err)
385	assert.Equal(t, accounts, dbAccounts)
386}
387
388func printCheckpoints(t *testing.T, path string) {
389	files, err := ioutil.ReadDir(path)
390	assert.NoError(t, err)
391
392	fmt.Println(path)
393	for _, f := range files {
394		fmt.Println("	" + f.Name())
395	}
396}
397
398func bigFromStr(h string, u int) *big.Int {
399	if u == 16 {
400		h = strings.TrimPrefix(h, "0x")
401	}
402	b, ok := new(big.Int).SetString(h, u)
403	if !ok {
404		panic("bigFromStr err")
405	}
406	return b
407}
408
409func TestCheckAccountsTreeTestVectors(t *testing.T) {
410	dir, err := ioutil.TempDir("", "tmpdb")
411	require.NoError(t, err)
412	defer assert.NoError(t, os.RemoveAll(dir))
413
414	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
415	require.NoError(t, err)
416
417	ay0 := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(253), nil), big.NewInt(1))
418	// test value from js version (compatibility-canary)
419	assert.Equal(t, "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", (hex.EncodeToString(ay0.Bytes())))
420	bjjPoint0Comp := babyjub.PackSignY(true, ay0)
421	bjj0 := babyjub.PublicKeyComp(bjjPoint0Comp)
422
423	ay1 := bigFromStr("00", 16)
424	bjjPoint1Comp := babyjub.PackSignY(false, ay1)
425	bjj1 := babyjub.PublicKeyComp(bjjPoint1Comp)
426	ay2 := bigFromStr("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7", 16)
427	bjjPoint2Comp := babyjub.PackSignY(false, ay2)
428	bjj2 := babyjub.PublicKeyComp(bjjPoint2Comp)
429
430	ay3 := bigFromStr("0x10", 16) // 0x10=16
431	bjjPoint3Comp := babyjub.PackSignY(false, ay3)
432	require.NoError(t, err)
433	bjj3 := babyjub.PublicKeyComp(bjjPoint3Comp)
434	accounts := []*common.Account{
435		{
436			Idx:     1,
437			TokenID: 0xFFFFFFFF,
438			BJJ:     bjj0,
439			EthAddr: ethCommon.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
440			Nonce:   common.Nonce(0xFFFFFFFFFF),
441			Balance: bigFromStr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16),
442		},
443		{
444			Idx:     100,
445			TokenID: 0,
446			BJJ:     bjj1,
447			EthAddr: ethCommon.HexToAddress("0x00"),
448			Nonce:   common.Nonce(0),
449			Balance: bigFromStr("0", 10),
450		},
451		{
452			Idx:     0xFFFFFFFFFFFF,
453			TokenID: 3,
454			BJJ:     bjj2,
455			EthAddr: ethCommon.HexToAddress("0xA3C88ac39A76789437AED31B9608da72e1bbfBF9"),
456			Nonce:   common.Nonce(129),
457			Balance: bigFromStr("42000000000000000000", 10),
458		},
459		{
460			Idx:     10000,
461			TokenID: 1000,
462			BJJ:     bjj3,
463			EthAddr: ethCommon.HexToAddress("0x64"),
464			Nonce:   common.Nonce(1900),
465			Balance: bigFromStr("14000000000000000000", 10),
466		},
467	}
468	for i := 0; i < len(accounts); i++ {
469		_, err = accounts[i].HashValue()
470		require.NoError(t, err)
471		_, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
472		if err != nil {
473			log.Error(err)
474		}
475		require.NoError(t, err)
476	}
477	// root value generated by js version:
478	assert.Equal(t, "17298264051379321456969039521810887093935433569451713402227686942080129181291", sdb.MT.Root().BigInt().String())
479}
480
481// TestListCheckpoints performs almost the same test than kvdb/kvdb_test.go
482// TestListCheckpoints, but over the StateDB
483func TestListCheckpoints(t *testing.T) {
484	dir, err := ioutil.TempDir("", "tmpdb")
485	require.NoError(t, err)
486	defer assert.NoError(t, os.RemoveAll(dir))
487
488	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
489	require.NoError(t, err)
490
491	numCheckpoints := 16
492	// do checkpoints
493	for i := 0; i < numCheckpoints; i++ {
494		err = sdb.db.MakeCheckpoint()
495		require.NoError(t, err)
496	}
497	list, err := sdb.db.ListCheckpoints()
498	require.NoError(t, err)
499	assert.Equal(t, numCheckpoints, len(list))
500	assert.Equal(t, 1, list[0])
501	assert.Equal(t, numCheckpoints, list[len(list)-1])
502
503	numReset := 10
504	err = sdb.Reset(common.BatchNum(numReset))
505	require.NoError(t, err)
506	list, err = sdb.db.ListCheckpoints()
507	require.NoError(t, err)
508	assert.Equal(t, numReset, len(list))
509	assert.Equal(t, 1, list[0])
510	assert.Equal(t, numReset, list[len(list)-1])
511}
512
513// TestDeleteOldCheckpoints performs almost the same test than
514// kvdb/kvdb_test.go TestDeleteOldCheckpoints, but over the StateDB
515func TestDeleteOldCheckpoints(t *testing.T) {
516	dir, err := ioutil.TempDir("", "tmpdb")
517	require.NoError(t, err)
518	defer assert.NoError(t, os.RemoveAll(dir))
519
520	keep := 16
521	sdb, err := NewStateDB(dir, keep, TypeSynchronizer, 32)
522	require.NoError(t, err)
523
524	numCheckpoints := 32
525	// do checkpoints and check that we never have more than `keep`
526	// checkpoints
527	for i := 0; i < numCheckpoints; i++ {
528		err = sdb.db.MakeCheckpoint()
529		require.NoError(t, err)
530		checkpoints, err := sdb.db.ListCheckpoints()
531		require.NoError(t, err)
532		assert.LessOrEqual(t, len(checkpoints), keep)
533	}
534}
535
536func TestCurrentIdx(t *testing.T) {
537	dir, err := ioutil.TempDir("", "tmpdb")
538	require.NoError(t, err)
539	defer require.NoError(t, os.RemoveAll(dir))
540
541	keep := 16
542	sdb, err := NewStateDB(dir, keep, TypeSynchronizer, 32)
543	require.NoError(t, err)
544
545	idx := sdb.CurrentIdx()
546	assert.Equal(t, common.Idx(255), idx)
547
548	sdb.Close()
549
550	sdb, err = NewStateDB(dir, keep, TypeSynchronizer, 32)
551	require.NoError(t, err)
552
553	idx = sdb.CurrentIdx()
554	assert.Equal(t, common.Idx(255), idx)
555
556	err = sdb.MakeCheckpoint()
557	require.NoError(t, err)
558
559	idx = sdb.CurrentIdx()
560	assert.Equal(t, common.Idx(255), idx)
561
562	sdb.Close()
563
564	sdb, err = NewStateDB(dir, keep, TypeSynchronizer, 32)
565	require.NoError(t, err)
566
567	idx = sdb.CurrentIdx()
568	assert.Equal(t, common.Idx(255), idx)
569}