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.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.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.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.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.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.GetCurrentBatch()
 90	assert.NoError(t, err)
 91	assert.Equal(t, common.BatchNum(0), bn)
 92	err = sdb.MakeCheckpoint()
 93	assert.NoError(t, err)
 94	bn, err = sdb.GetCurrentBatch()
 95	assert.NoError(t, err)
 96	assert.Equal(t, common.BatchNum(1), bn)
 97
 98	// write more data
 99	tx, err = sdb.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.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.Get(k0)
116	assert.NoError(t, err)
117	assert.Equal(t, v0, v)
118
119	v, err = sdb.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
231func TestCheckpoints(t *testing.T) {
232	dir, err := ioutil.TempDir("", "sdb")
233	require.NoError(t, err)
234	defer assert.NoError(t, os.RemoveAll(dir))
235
236	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
237	assert.NoError(t, err)
238
239	// create test accounts
240	var accounts []*common.Account
241	for i := 0; i < 10; i++ {
242		accounts = append(accounts, newAccount(t, i))
243	}
244
245	// add test accounts
246	for i := 0; i < len(accounts); i++ {
247		_, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
248		assert.NoError(t, err)
249	}
250
251	// do checkpoints and check that currentBatch is correct
252	err = sdb.MakeCheckpoint()
253	assert.NoError(t, err)
254	cb, err := sdb.GetCurrentBatch()
255	assert.NoError(t, err)
256	assert.Equal(t, common.BatchNum(1), cb)
257
258	for i := 1; i < 10; i++ {
259		err = sdb.MakeCheckpoint()
260		assert.NoError(t, err)
261
262		cb, err = sdb.GetCurrentBatch()
263		assert.NoError(t, err)
264		assert.Equal(t, common.BatchNum(i+1), cb)
265	}
266
267	// printCheckpoints(t, sdb.path)
268
269	// reset checkpoint
270	err = sdb.Reset(3)
271	assert.NoError(t, err)
272
273	// check that reset can be repeated (as there exist the 'current' and
274	// 'BatchNum3', from where the 'current' is a copy)
275	err = sdb.Reset(3)
276	require.NoError(t, err)
277
278	// check that currentBatch is as expected after Reset
279	cb, err = sdb.GetCurrentBatch()
280	assert.NoError(t, err)
281	assert.Equal(t, common.BatchNum(3), cb)
282
283	// advance one checkpoint and check that currentBatch is fine
284	err = sdb.MakeCheckpoint()
285	assert.NoError(t, err)
286	cb, err = sdb.GetCurrentBatch()
287	assert.NoError(t, err)
288	assert.Equal(t, common.BatchNum(4), cb)
289
290	err = sdb.DeleteCheckpoint(common.BatchNum(1))
291	assert.NoError(t, err)
292	err = sdb.DeleteCheckpoint(common.BatchNum(2))
293	assert.NoError(t, err)
294	err = sdb.DeleteCheckpoint(common.BatchNum(1)) // does not exist, should return err
295	assert.NotNil(t, err)
296	err = sdb.DeleteCheckpoint(common.BatchNum(2)) // does not exist, should return err
297	assert.NotNil(t, err)
298
299	// Create a LocalStateDB from the initial StateDB
300	dirLocal, err := ioutil.TempDir("", "ldb")
301	require.NoError(t, err)
302	defer assert.NoError(t, os.RemoveAll(dirLocal))
303	ldb, err := NewLocalStateDB(dirLocal, 128, sdb, TypeBatchBuilder, 32)
304	assert.NoError(t, err)
305
306	// get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
307	err = ldb.Reset(4, true)
308	assert.NoError(t, err)
309	// check that currentBatch is 4 after the Reset
310	cb, err = ldb.GetCurrentBatch()
311	assert.NoError(t, err)
312	assert.Equal(t, common.BatchNum(4), cb)
313	// advance one checkpoint in ldb
314	err = ldb.MakeCheckpoint()
315	assert.NoError(t, err)
316	cb, err = ldb.GetCurrentBatch()
317	assert.NoError(t, err)
318	assert.Equal(t, common.BatchNum(5), cb)
319
320	// Create a 2nd LocalStateDB from the initial StateDB
321	dirLocal2, err := ioutil.TempDir("", "ldb2")
322	require.NoError(t, err)
323	defer assert.NoError(t, os.RemoveAll(dirLocal2))
324	ldb2, err := NewLocalStateDB(dirLocal2, 128, sdb, TypeBatchBuilder, 32)
325	assert.NoError(t, err)
326
327	// get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
328	err = ldb2.Reset(4, true)
329	assert.NoError(t, err)
330	// check that currentBatch is 4 after the Reset
331	cb, err = ldb2.GetCurrentBatch()
332	assert.NoError(t, err)
333	assert.Equal(t, common.BatchNum(4), cb)
334	// advance one checkpoint in ldb2
335	err = ldb2.MakeCheckpoint()
336	assert.NoError(t, err)
337	cb, err = ldb2.GetCurrentBatch()
338	assert.NoError(t, err)
339	assert.Equal(t, common.BatchNum(5), cb)
340
341	debug := false
342	if debug {
343		printCheckpoints(t, sdb.path)
344		printCheckpoints(t, ldb.path)
345		printCheckpoints(t, ldb2.path)
346	}
347}
348
349func TestStateDBGetAccounts(t *testing.T) {
350	dir, err := ioutil.TempDir("", "tmpdb")
351	require.NoError(t, err)
352
353	sdb, err := NewStateDB(dir, 128, TypeTxSelector, 0)
354	assert.NoError(t, err)
355
356	// create test accounts
357	var accounts []common.Account
358	for i := 0; i < 16; i++ {
359		account := newAccount(t, i)
360		accounts = append(accounts, *account)
361	}
362
363	// add test accounts
364	for i := range accounts {
365		_, err = sdb.CreateAccount(accounts[i].Idx, &accounts[i])
366		require.NoError(t, err)
367	}
368
369	dbAccounts, err := sdb.GetAccounts()
370	require.NoError(t, err)
371	assert.Equal(t, accounts, dbAccounts)
372}
373
374func printCheckpoints(t *testing.T, path string) {
375	files, err := ioutil.ReadDir(path)
376	assert.NoError(t, err)
377
378	fmt.Println(path)
379	for _, f := range files {
380		fmt.Println("	" + f.Name())
381	}
382}
383
384func bigFromStr(h string, u int) *big.Int {
385	if u == 16 {
386		h = strings.TrimPrefix(h, "0x")
387	}
388	b, ok := new(big.Int).SetString(h, u)
389	if !ok {
390		panic("bigFromStr err")
391	}
392	return b
393}
394
395func TestCheckAccountsTreeTestVectors(t *testing.T) {
396	dir, err := ioutil.TempDir("", "tmpdb")
397	require.NoError(t, err)
398	defer assert.NoError(t, os.RemoveAll(dir))
399
400	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
401	require.NoError(t, err)
402
403	ay0 := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(253), nil), big.NewInt(1))
404	// test value from js version (compatibility-canary)
405	assert.Equal(t, "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", (hex.EncodeToString(ay0.Bytes())))
406	bjjPoint0Comp := babyjub.PackSignY(true, ay0)
407	bjj0 := babyjub.PublicKeyComp(bjjPoint0Comp)
408
409	ay1 := bigFromStr("00", 16)
410	bjjPoint1Comp := babyjub.PackSignY(false, ay1)
411	bjj1 := babyjub.PublicKeyComp(bjjPoint1Comp)
412	ay2 := bigFromStr("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7", 16)
413	bjjPoint2Comp := babyjub.PackSignY(false, ay2)
414	bjj2 := babyjub.PublicKeyComp(bjjPoint2Comp)
415
416	ay3 := bigFromStr("0x10", 16) // 0x10=16
417	bjjPoint3Comp := babyjub.PackSignY(false, ay3)
418	require.NoError(t, err)
419	bjj3 := babyjub.PublicKeyComp(bjjPoint3Comp)
420	accounts := []*common.Account{
421		{
422			Idx:       1,
423			TokenID:   0xFFFFFFFF,
424			PublicKey: bjj0,
425			EthAddr:   ethCommon.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
426			Nonce:     common.Nonce(0xFFFFFFFFFF),
427			Balance:   bigFromStr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16),
428		},
429		{
430			Idx:       100,
431			TokenID:   0,
432			PublicKey: bjj1,
433			EthAddr:   ethCommon.HexToAddress("0x00"),
434			Nonce:     common.Nonce(0),
435			Balance:   bigFromStr("0", 10),
436		},
437		{
438			Idx:       0xFFFFFFFFFFFF,
439			TokenID:   3,
440			PublicKey: bjj2,
441			EthAddr:   ethCommon.HexToAddress("0xA3C88ac39A76789437AED31B9608da72e1bbfBF9"),
442			Nonce:     common.Nonce(129),
443			Balance:   bigFromStr("42000000000000000000", 10),
444		},
445		{
446			Idx:       10000,
447			TokenID:   1000,
448			PublicKey: bjj3,
449			EthAddr:   ethCommon.HexToAddress("0x64"),
450			Nonce:     common.Nonce(1900),
451			Balance:   bigFromStr("14000000000000000000", 10),
452		},
453	}
454	for i := 0; i < len(accounts); i++ {
455		_, err = accounts[i].HashValue()
456		require.NoError(t, err)
457		_, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
458		if err != nil {
459			log.Error(err)
460		}
461		require.NoError(t, err)
462	}
463	// root value generated by js version:
464	assert.Equal(t, "17298264051379321456969039521810887093935433569451713402227686942080129181291", sdb.MT.Root().BigInt().String())
465}
466
467func TestListCheckpoints(t *testing.T) {
468	dir, err := ioutil.TempDir("", "tmpdb")
469	require.NoError(t, err)
470	defer assert.NoError(t, os.RemoveAll(dir))
471
472	sdb, err := NewStateDB(dir, 128, TypeSynchronizer, 32)
473	require.NoError(t, err)
474
475	numCheckpoints := 16
476	// do checkpoints
477	for i := 0; i < numCheckpoints; i++ {
478		err = sdb.MakeCheckpoint()
479		require.NoError(t, err)
480	}
481	list, err := sdb.listCheckpoints()
482	require.NoError(t, err)
483	assert.Equal(t, numCheckpoints, len(list))
484	assert.Equal(t, 1, list[0])
485	assert.Equal(t, numCheckpoints, list[len(list)-1])
486
487	numReset := 10
488	err = sdb.Reset(common.BatchNum(numReset))
489	require.NoError(t, err)
490	list, err = sdb.listCheckpoints()
491	require.NoError(t, err)
492	assert.Equal(t, numReset, len(list))
493	assert.Equal(t, 1, list[0])
494	assert.Equal(t, numReset, list[len(list)-1])
495}
496
497func TestDeleteOldCheckpoints(t *testing.T) {
498	dir, err := ioutil.TempDir("", "tmpdb")
499	require.NoError(t, err)
500	defer assert.NoError(t, os.RemoveAll(dir))
501
502	keep := 16
503	sdb, err := NewStateDB(dir, keep, TypeSynchronizer, 32)
504	require.NoError(t, err)
505
506	numCheckpoints := 32
507	// do checkpoints and check that we never have more than `keep`
508	// checkpoints
509	for i := 0; i < numCheckpoints; i++ {
510		err = sdb.MakeCheckpoint()
511		require.NoError(t, err)
512		checkpoints, err := sdb.listCheckpoints()
513		require.NoError(t, err)
514		assert.LessOrEqual(t, len(checkpoints), keep)
515	}
516}