arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/l2db/l2db_test.go
db/l2db/l2db_test.goRaw
  1package l2db
  2
  3import (
  4	"math/big"
  5	"os"
  6	"testing"
  7	"time"
  8
  9	ethCommon "github.com/ethereum/go-ethereum/common"
 10	"github.com/hermeznetwork/hermez-node/common"
 11	dbUtils "github.com/hermeznetwork/hermez-node/db"
 12	"github.com/hermeznetwork/hermez-node/db/historydb"
 13	"github.com/hermeznetwork/hermez-node/log"
 14	"github.com/hermeznetwork/hermez-node/test"
 15	"github.com/jmoiron/sqlx"
 16	"github.com/stretchr/testify/assert"
 17)
 18
 19var l2DB *L2DB
 20var tokens []common.Token
 21var tokensUSD []historydb.TokenWithUSD
 22
 23func TestMain(m *testing.M) {
 24	// init DB
 25	pass := os.Getenv("POSTGRES_PASS")
 26	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 27	if err != nil {
 28		panic(err)
 29	}
 30	l2DB = NewL2DB(db, 10, 100, 24*time.Hour)
 31	tokens, tokensUSD = prepareHistoryDB(db)
 32	if err != nil {
 33		panic(err)
 34	}
 35	// Run tests
 36	result := m.Run()
 37	// Close DB
 38	if err := db.Close(); err != nil {
 39		log.Error("Error closing the history DB:", err)
 40	}
 41	os.Exit(result)
 42}
 43
 44func prepareHistoryDB(db *sqlx.DB) ([]common.Token, []historydb.TokenWithUSD) {
 45	historyDB := historydb.NewHistoryDB(db)
 46	const fromBlock int64 = 1
 47	const toBlock int64 = 5
 48	// Clean historyDB
 49	if err := historyDB.Reorg(-1); err != nil {
 50		panic(err)
 51	}
 52	// Store blocks to historyDB
 53	blocks := test.GenBlocks(fromBlock, toBlock)
 54	if err := historyDB.AddBlocks(blocks); err != nil {
 55		panic(err)
 56	}
 57	// Store tokens to historyDB
 58	const nTokens = 5
 59	tokens := test.GenTokens(nTokens, blocks)
 60	if err := historyDB.AddTokens(tokens); err != nil {
 61		panic(err)
 62	}
 63	readTokens := []historydb.TokenWithUSD{}
 64	for i, token := range tokens {
 65		readToken := historydb.TokenWithUSD{
 66			TokenID:     token.TokenID,
 67			EthBlockNum: token.EthBlockNum,
 68			EthAddr:     token.EthAddr,
 69			Name:        token.Name,
 70			Symbol:      token.Symbol,
 71			Decimals:    token.Decimals,
 72		}
 73		if i%2 != 0 {
 74			value := float64(i) * 5.4321
 75			if err := historyDB.UpdateTokenValue(token.Symbol, value); err != nil {
 76				panic(err)
 77			}
 78			now := time.Now().UTC()
 79			readToken.USDUpdate = &now
 80			readToken.USD = &value
 81		}
 82		readTokens = append(readTokens, readToken)
 83	}
 84	return tokens, readTokens
 85}
 86
 87func TestAddTxTest(t *testing.T) {
 88	// Gen poolTxs
 89	const nInserts = 20
 90	test.CleanL2DB(l2DB.DB())
 91	txs := test.GenPoolTxs(nInserts, tokens)
 92	for _, tx := range txs {
 93		err := l2DB.AddTxTest(tx)
 94		assert.NoError(t, err)
 95		fetchedTx, err := l2DB.GetTx(tx.TxID)
 96		assert.NoError(t, err)
 97		assertReadTx(t, commonToRead(tx, tokens), fetchedTx)
 98	}
 99}
100
101func commonToRead(commonTx *common.PoolL2Tx, tokens []common.Token) *PoolL2TxRead {
102	readTx := &PoolL2TxRead{
103		TxID:      commonTx.TxID,
104		FromIdx:   commonTx.FromIdx,
105		ToBJJ:     commonTx.ToBJJ,
106		Amount:    commonTx.Amount,
107		Fee:       commonTx.Fee,
108		Nonce:     commonTx.Nonce,
109		State:     commonTx.State,
110		Signature: commonTx.Signature,
111		RqToBJJ:   commonTx.RqToBJJ,
112		RqAmount:  commonTx.RqAmount,
113		Type:      commonTx.Type,
114		Timestamp: commonTx.Timestamp,
115		TokenID:   commonTx.TokenID,
116	}
117	// token related fields
118	// find token
119	token := historydb.TokenWithUSD{}
120	for _, tkn := range tokensUSD {
121		if tkn.TokenID == readTx.TokenID {
122			token = tkn
123			break
124		}
125	}
126	// set token related fields
127	readTx.TokenEthBlockNum = token.EthBlockNum
128	readTx.TokenEthAddr = token.EthAddr
129	readTx.TokenName = token.Name
130	readTx.TokenSymbol = token.Symbol
131	readTx.TokenDecimals = token.Decimals
132	readTx.TokenUSD = token.USD
133	readTx.TokenUSDUpdate = token.USDUpdate
134	// nullable fields
135	if commonTx.ToIdx != 0 {
136		readTx.ToIdx = &commonTx.ToIdx
137	}
138	nilAddr := ethCommon.BigToAddress(big.NewInt(0))
139	if commonTx.ToEthAddr != nilAddr {
140		readTx.ToEthAddr = &commonTx.ToEthAddr
141	}
142	if commonTx.RqFromIdx != 0 {
143		readTx.RqFromIdx = &commonTx.RqFromIdx
144	}
145	if commonTx.RqToIdx != 0 { // if true, all Rq... fields must be different to nil
146		readTx.RqToIdx = &commonTx.RqToIdx
147		readTx.RqTokenID = &commonTx.RqTokenID
148		readTx.RqFee = &commonTx.RqFee
149		readTx.RqNonce = &commonTx.RqNonce
150	}
151	if commonTx.RqToEthAddr != nilAddr {
152		readTx.RqToEthAddr = &commonTx.RqToEthAddr
153	}
154	return readTx
155}
156
157func assertReadTx(t *testing.T, expected, actual *PoolL2TxRead) {
158	// Check that timestamp has been set within the last 3 seconds
159	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
160	assert.GreaterOrEqual(t, time.Now().UTC().Unix(), actual.Timestamp.Unix())
161	expected.Timestamp = actual.Timestamp
162	// Check token related stuff
163	if expected.TokenUSDUpdate != nil {
164		// Check that TokenUSDUpdate has been set within the last 3 seconds
165		assert.Less(t, time.Now().UTC().Unix()-3, actual.TokenUSDUpdate.Unix())
166		assert.GreaterOrEqual(t, time.Now().UTC().Unix(), actual.TokenUSDUpdate.Unix())
167		expected.TokenUSDUpdate = actual.TokenUSDUpdate
168	}
169	assert.Equal(t, expected, actual)
170}
171
172func assertTx(t *testing.T, expected, actual *common.PoolL2Tx) {
173	// Check that timestamp has been set within the last 3 seconds
174	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
175	assert.GreaterOrEqual(t, time.Now().UTC().Unix(), actual.Timestamp.Unix())
176	expected.Timestamp = actual.Timestamp
177	// Check absolute fee
178	// find token
179	token := historydb.TokenWithUSD{}
180	for _, tkn := range tokensUSD {
181		if expected.TokenID == tkn.TokenID {
182			token = tkn
183			break
184		}
185	}
186	// If the token has value in USD setted
187	if token.USDUpdate != nil {
188		assert.Equal(t, token.USDUpdate.Unix(), actual.AbsoluteFeeUpdate.Unix())
189		expected.AbsoluteFeeUpdate = actual.AbsoluteFeeUpdate
190		// Set expected fee
191		f := new(big.Float).SetInt(expected.Amount)
192		amountF, _ := f.Float64()
193		expected.AbsoluteFee = *token.USD * amountF * expected.Fee.Percentage()
194		test.AssertUSD(t, &expected.AbsoluteFee, &actual.AbsoluteFee)
195	}
196	assert.Equal(t, expected, actual)
197}
198
199func BenchmarkAddTxTest(b *testing.B) {
200	const nInserts = 20
201	test.CleanL2DB(l2DB.DB())
202	txs := test.GenPoolTxs(nInserts, tokens)
203	now := time.Now()
204	for _, tx := range txs {
205		_ = l2DB.AddTxTest(tx)
206	}
207	elapsedTime := time.Since(now)
208	log.Info("Time to insert 2048 txs:", elapsedTime)
209}
210
211func TestGetPending(t *testing.T) {
212	const nInserts = 20
213	test.CleanL2DB(l2DB.DB())
214	txs := test.GenPoolTxs(nInserts, tokens)
215	var pendingTxs []*common.PoolL2Tx
216	for _, tx := range txs {
217		err := l2DB.AddTxTest(tx)
218		assert.NoError(t, err)
219		if tx.State == common.PoolL2TxStatePending {
220			pendingTxs = append(pendingTxs, tx)
221		}
222	}
223	fetchedTxs, err := l2DB.GetPendingTxs()
224	assert.NoError(t, err)
225	assert.Equal(t, len(pendingTxs), len(fetchedTxs))
226	for i := range fetchedTxs {
227		assertTx(t, pendingTxs[i], &fetchedTxs[i])
228	}
229}
230
231/*
232	WARNING: this should be fixed once transaktio is ready
233func TestStartForging(t *testing.T) {
234	// Generate txs
235	const nInserts = 60
236	const fakeBatchNum common.BatchNum = 33
237	test.CleanL2DB(l2DB.DB())
238	txs := test.GenPoolTxs(nInserts, tokens)
239	var startForgingTxIDs []common.TxID
240	randomizer := 0
241	// Add txs to DB
242	for _, tx := range txs {
243		err := l2DB.AddTxTest(tx)
244		assert.NoError(t, err)
245		if tx.State == common.PoolL2TxStatePending && randomizer%2 == 0 {
246			randomizer++
247			startForgingTxIDs = append(startForgingTxIDs, tx.TxID)
248		}
249	}
250	// Start forging txs
251	err := l2DB.StartForging(startForgingTxIDs, fakeBatchNum)
252	assert.NoError(t, err)
253	// Fetch txs and check that they've been updated correctly
254	for _, id := range startForgingTxIDs {
255		fetchedTx, err := l2DB.GetTx(id)
256		assert.NoError(t, err)
257		assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State)
258		assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
259	}
260}
261*/
262
263/*
264	WARNING: this should be fixed once transaktio is ready
265func TestDoneForging(t *testing.T) {
266	// Generate txs
267	const nInserts = 60
268	const fakeBatchNum common.BatchNum = 33
269	test.CleanL2DB(l2DB.DB())
270	txs := test.GenPoolTxs(nInserts, tokens)
271	var doneForgingTxIDs []common.TxID
272	randomizer := 0
273	// Add txs to DB
274	for _, tx := range txs {
275		err := l2DB.AddTxTest(tx)
276		assert.NoError(t, err)
277		if tx.State == common.PoolL2TxStateForging && randomizer%2 == 0 {
278			randomizer++
279			doneForgingTxIDs = append(doneForgingTxIDs, tx.TxID)
280		}
281	}
282	// Start forging txs
283	err := l2DB.DoneForging(doneForgingTxIDs, fakeBatchNum)
284	assert.NoError(t, err)
285	// Fetch txs and check that they've been updated correctly
286	for _, id := range doneForgingTxIDs {
287		fetchedTx, err := l2DB.GetTx(id)
288		assert.NoError(t, err)
289		assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State)
290		assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
291	}
292}
293*/
294
295/*
296	WARNING: this should be fixed once transaktio is ready
297func TestInvalidate(t *testing.T) {
298	// Generate txs
299	const nInserts = 60
300	const fakeBatchNum common.BatchNum = 33
301	test.CleanL2DB(l2DB.DB())
302	txs := test.GenPoolTxs(nInserts, tokens)
303	var invalidTxIDs []common.TxID
304	randomizer := 0
305	// Add txs to DB
306	for _, tx := range txs {
307		err := l2DB.AddTxTest(tx)
308		assert.NoError(t, err)
309		if tx.State != common.PoolL2TxStateInvalid && randomizer%2 == 0 {
310			randomizer++
311			invalidTxIDs = append(invalidTxIDs, tx.TxID)
312		}
313	}
314	// Start forging txs
315	err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
316	assert.NoError(t, err)
317	// Fetch txs and check that they've been updated correctly
318	for _, id := range invalidTxIDs {
319		fetchedTx, err := l2DB.GetTx(id)
320		assert.NoError(t, err)
321		assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
322		assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
323	}
324}
325*/
326
327/*
328	WARNING: this should be fixed once transaktio is ready
329func TestCheckNonces(t *testing.T) {
330	// Generate txs
331	const nInserts = 60
332	const fakeBatchNum common.BatchNum = 33
333	test.CleanL2DB(l2DB.DB())
334	txs := test.GenPoolTxs(nInserts, tokens)
335	var invalidTxIDs []common.TxID
336	// Generate accounts
337	const nAccoutns = 2
338	const currentNonce = 2
339	accs := []common.Account{}
340	for i := 0; i < nAccoutns; i++ {
341		accs = append(accs, common.Account{
342			Idx:   common.Idx(i),
343			Nonce: currentNonce,
344		})
345	}
346	// Add txs to DB
347	for i := 0; i < len(txs); i++ {
348		if txs[i].State != common.PoolL2TxStateInvalid {
349			if i%2 == 0 { // Ensure transaction will be marked as invalid due to old nonce
350				txs[i].Nonce = accs[i%len(accs)].Nonce
351				txs[i].FromIdx = accs[i%len(accs)].Idx
352				invalidTxIDs = append(invalidTxIDs, txs[i].TxID)
353			} else { // Ensure transaction will NOT be marked as invalid due to old nonce
354				txs[i].Nonce = currentNonce + 1
355			}
356		}
357		err := l2DB.AddTxTest(txs[i])
358		assert.NoError(t, err)
359	}
360	// Start forging txs
361	err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
362	assert.NoError(t, err)
363	// Fetch txs and check that they've been updated correctly
364	for _, id := range invalidTxIDs {
365		fetchedTx, err := l2DB.GetTx(id)
366		assert.NoError(t, err)
367		assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
368		assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
369	}
370}
371*/
372
373func TestReorg(t *testing.T) {
374	// Generate txs
375	const nInserts = 20
376	const lastValidBatch common.BatchNum = 20
377	const reorgBatch common.BatchNum = lastValidBatch + 1
378	test.CleanL2DB(l2DB.DB())
379	txs := test.GenPoolTxs(nInserts, tokens)
380	// Add txs to the DB
381	reorgedTxIDs := []common.TxID{}
382	nonReorgedTxIDs := []common.TxID{}
383	for i := 0; i < len(txs); i++ {
384		err := l2DB.AddTxTest(txs[i])
385		assert.NoError(t, err)
386		var batchNum common.BatchNum
387		if txs[i].State == common.PoolL2TxStateForged || txs[i].State == common.PoolL2TxStateInvalid {
388			reorgedTxIDs = append(reorgedTxIDs, txs[i].TxID)
389			batchNum = reorgBatch
390		} else {
391			nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID)
392			batchNum = lastValidBatch
393		}
394		_, err = l2DB.db.Exec(
395			"UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;",
396			batchNum, txs[i].TxID,
397		)
398		assert.NoError(t, err)
399	}
400	err := l2DB.Reorg(lastValidBatch)
401	assert.NoError(t, err)
402	for _, id := range reorgedTxIDs {
403		tx, err := l2DB.GetTx(id)
404		assert.NoError(t, err)
405		assert.Nil(t, tx.BatchNum)
406		assert.Equal(t, common.PoolL2TxStatePending, tx.State)
407	}
408	for _, id := range nonReorgedTxIDs {
409		fetchedTx, err := l2DB.GetTx(id)
410		assert.NoError(t, err)
411		assert.Equal(t, lastValidBatch, *fetchedTx.BatchNum)
412	}
413}
414
415func TestPurge(t *testing.T) {
416	/*
417		WARNING: this should be fixed once transaktio is ready
418		// Generate txs
419		nInserts := l2DB.maxTxs + 20
420		test.CleanL2DB(l2DB.DB())
421		txs := test.GenPoolTxs(int(nInserts), tokens)
422		deletedIDs := []common.TxID{}
423		keepedIDs := []common.TxID{}
424		const toDeleteBatchNum common.BatchNum = 30
425		safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1
426		// Add txs to the DB
427		for i := 0; i < int(l2DB.maxTxs); i++ {
428			var batchNum common.BatchNum
429			if i%2 == 0 { // keep tx
430				batchNum = safeBatchNum
431				keepedIDs = append(keepedIDs, txs[i].TxID)
432			} else { // delete after safety period
433				batchNum = toDeleteBatchNum
434				if i%3 == 0 {
435					txs[i].State = common.PoolL2TxStateForged
436				} else {
437					txs[i].State = common.PoolL2TxStateInvalid
438				}
439				deletedIDs = append(deletedIDs, txs[i].TxID)
440			}
441			err := l2DB.AddTxTest(txs[i])
442			assert.NoError(t, err)
443			// Set batchNum
444			_, err = l2DB.db.Exec(
445				"UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;",
446				batchNum, txs[i].TxID,
447			)
448			assert.NoError(t, err)
449		}
450		for i := int(l2DB.maxTxs); i < len(txs); i++ {
451			// Delete after TTL
452			deletedIDs = append(deletedIDs, txs[i].TxID)
453			err := l2DB.AddTxTest(txs[i])
454			assert.NoError(t, err)
455			// Set timestamp
456			deleteTimestamp := time.Unix(time.Now().UTC().Unix()-int64(l2DB.ttl.Seconds()+float64(4*time.Second)), 0)
457			_, err = l2DB.db.Exec(
458				"UPDATE tx_pool SET timestamp = $1 WHERE tx_id = $2;",
459				deleteTimestamp, txs[i].TxID,
460			)
461			assert.NoError(t, err)
462		}
463		// Purge txs
464		err := l2DB.Purge(safeBatchNum)
465		assert.NoError(t, err)
466		// Check results
467		for _, id := range deletedIDs {
468			tx, err := l2DB.GetTx(id)
469			if err == nil {
470				log.Debug(tx)
471			}
472			assert.Error(t, err)
473		}
474		for _, id := range keepedIDs {
475			_, err := l2DB.GetTx(id)
476			assert.NoError(t, err)
477		}
478	*/
479}
480
481func TestAuth(t *testing.T) {
482	test.CleanL2DB(l2DB.DB())
483	const nAuths = 5
484	// Generate authorizations
485	auths := test.GenAuths(nAuths)
486	for i := 0; i < len(auths); i++ {
487		// Add to the DB
488		err := l2DB.AddAccountCreationAuth(auths[i])
489		assert.NoError(t, err)
490		// Fetch from DB
491		auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr)
492		assert.NoError(t, err)
493		// Check fetched vs generated
494		assert.Equal(t, auths[i].EthAddr, auth.EthAddr)
495		assert.Equal(t, auths[i].BJJ, auth.BJJ)
496		assert.Equal(t, auths[i].Signature, auth.Signature)
497		assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix())
498	}
499}