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