arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/db/l2db/l2db_test.go
db/l2db/l2db_test.goRaw
  1package l2db
  2
  3import (
  4	"database/sql"
  5	"fmt"
  6	"os"
  7	"testing"
  8	"time"
  9
 10	ethCommon "github.com/ethereum/go-ethereum/common"
 11	"github.com/hermeznetwork/hermez-node/common"
 12	dbUtils "github.com/hermeznetwork/hermez-node/db"
 13	"github.com/hermeznetwork/hermez-node/db/historydb"
 14	"github.com/hermeznetwork/hermez-node/log"
 15	"github.com/hermeznetwork/hermez-node/test"
 16	"github.com/hermeznetwork/hermez-node/test/til"
 17	"github.com/hermeznetwork/tracerr"
 18	"github.com/iden3/go-iden3-crypto/babyjub"
 19	"github.com/stretchr/testify/assert"
 20	"github.com/stretchr/testify/require"
 21)
 22
 23var decimals = uint64(3)
 24var tokenValue = 1.0 // The price update gives a value of 1.0 USD to the token
 25var l2DB *L2DB
 26var l2DBWithACC *L2DB
 27var historyDB *historydb.HistoryDB
 28var tc *til.Context
 29var tokens map[common.TokenID]historydb.TokenWithUSD
 30
 31var accs map[common.Idx]common.Account
 32
 33func TestMain(m *testing.M) {
 34	// init DB
 35	pass := os.Getenv("POSTGRES_PASS")
 36	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 37	if err != nil {
 38		panic(err)
 39	}
 40	l2DB = NewL2DB(db, db, 10, 1000, 0.0, 24*time.Hour, nil)
 41	apiConnCon := dbUtils.NewAPIConnectionController(1, time.Second)
 42	l2DBWithACC = NewL2DB(db, db, 10, 1000, 0.0, 24*time.Hour, apiConnCon)
 43	test.WipeDB(l2DB.DB())
 44	historyDB = historydb.NewHistoryDB(db, db, nil)
 45	// Run tests
 46	result := m.Run()
 47	// Close DB
 48	if err := db.Close(); err != nil {
 49		log.Error("Error closing the history DB:", err)
 50	}
 51	os.Exit(result)
 52}
 53
 54func prepareHistoryDB(historyDB *historydb.HistoryDB) error {
 55	// Reset DB
 56	test.WipeDB(l2DB.DB())
 57	// Generate pool txs using til
 58	setBlockchain := `
 59			Type: Blockchain
 60
 61			AddToken(1)
 62			AddToken(2)
 63			CreateAccountDeposit(1) A: 20000
 64			CreateAccountDeposit(2) A: 20000
 65			CreateAccountDeposit(1) B: 10000
 66			CreateAccountDeposit(2) B: 10000
 67			> batchL1 
 68			> batchL1
 69			> block
 70			> block
 71			`
 72
 73	tc = til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
 74	tilCfgExtra := til.ConfigExtra{
 75		BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
 76		CoordUser:     "A",
 77	}
 78	blocks, err := tc.GenerateBlocks(setBlockchain)
 79	if err != nil {
 80		return tracerr.Wrap(err)
 81	}
 82
 83	err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
 84	if err != nil {
 85		return tracerr.Wrap(err)
 86	}
 87	for i := range blocks {
 88		block := &blocks[i]
 89		for j := range block.Rollup.AddedTokens {
 90			token := &block.Rollup.AddedTokens[j]
 91			token.Name = fmt.Sprintf("Token %d", token.TokenID)
 92			token.Symbol = fmt.Sprintf("TK%d", token.TokenID)
 93			token.Decimals = decimals
 94		}
 95	}
 96
 97	tokens = make(map[common.TokenID]historydb.TokenWithUSD)
 98	// tokensValue = make(map[common.TokenID]float64)
 99	accs = make(map[common.Idx]common.Account)
100	now := time.Now().UTC()
101	// Add all blocks except for the last one
102	for i := range blocks[:len(blocks)-1] {
103		if err := historyDB.AddBlockSCData(&blocks[i]); err != nil {
104			return tracerr.Wrap(err)
105		}
106		for _, batch := range blocks[i].Rollup.Batches {
107			for _, account := range batch.CreatedAccounts {
108				accs[account.Idx] = account
109			}
110		}
111		for _, token := range blocks[i].Rollup.AddedTokens {
112			readToken := historydb.TokenWithUSD{
113				TokenID:     token.TokenID,
114				EthBlockNum: token.EthBlockNum,
115				EthAddr:     token.EthAddr,
116				Name:        token.Name,
117				Symbol:      token.Symbol,
118				Decimals:    token.Decimals,
119				USD:         &tokenValue,
120				USDUpdate:   &now,
121			}
122			tokens[token.TokenID] = readToken
123			// Set value to the tokens
124			err := historyDB.UpdateTokenValue(readToken.EthAddr, *readToken.USD)
125			if err != nil {
126				return tracerr.Wrap(err)
127			}
128		}
129	}
130	return nil
131}
132
133func generatePoolL2Txs() ([]common.PoolL2Tx, error) {
134	// Fee = 126 corresponds to ~10%
135	setPool := `
136			Type: PoolL2
137			PoolTransfer(1) A-B: 6000 (126)
138			PoolTransfer(2) A-B: 3000 (126)
139			PoolTransfer(1) B-A: 5000 (126)
140			PoolTransfer(2) B-A: 10000 (126)
141			PoolTransfer(1) A-B: 7000 (126)
142			PoolTransfer(2) A-B: 2000 (126)
143			PoolTransfer(1) B-A: 8000 (126)
144			PoolTransfer(2) B-A: 1000 (126)
145			PoolTransfer(1) A-B: 3000 (126)
146			PoolTransferToEthAddr(2) B-A: 5000 (126)
147			PoolTransferToBJJ(2) B-A: 5000 (126)
148
149			PoolExit(1) A: 5000 (126)
150			PoolExit(2) B: 3000 (126)
151		`
152	poolL2Txs, err := tc.GeneratePoolL2Txs(setPool)
153	if err != nil {
154		return nil, tracerr.Wrap(err)
155	}
156	return poolL2Txs, nil
157}
158
159func TestAddTxTest(t *testing.T) {
160	err := prepareHistoryDB(historyDB)
161	if err != nil {
162		log.Error("Error prepare historyDB", err)
163	}
164	poolL2Txs, err := generatePoolL2Txs()
165	require.NoError(t, err)
166	for i := range poolL2Txs {
167		err := l2DB.AddTxTest(&poolL2Txs[i])
168		require.NoError(t, err)
169		fetchedTx, err := l2DB.GetTx(poolL2Txs[i].TxID)
170		require.NoError(t, err)
171		assertTx(t, &poolL2Txs[i], fetchedTx)
172		nameZone, offset := fetchedTx.Timestamp.Zone()
173		assert.Equal(t, "UTC", nameZone)
174		assert.Equal(t, 0, offset)
175	}
176}
177
178func TestAddTxAPI(t *testing.T) {
179	err := prepareHistoryDB(historyDB)
180	if err != nil {
181		log.Error("Error prepare historyDB", err)
182	}
183
184	oldMaxTxs := l2DBWithACC.maxTxs
185	// set max number of pending txs that can be kept in the pool to 5
186	l2DBWithACC.maxTxs = 5
187
188	poolL2Txs, err := generatePoolL2Txs()
189	txs := make([]*PoolL2TxWrite, len(poolL2Txs))
190	for i := range poolL2Txs {
191		txs[i] = NewPoolL2TxWriteFromPoolL2Tx(&poolL2Txs[i])
192	}
193	require.NoError(t, err)
194	require.GreaterOrEqual(t, len(poolL2Txs), 8)
195	for i := range txs[:5] {
196		err := l2DBWithACC.AddTxAPI(txs[i])
197		require.NoError(t, err)
198		fetchedTx, err := l2DB.GetTx(poolL2Txs[i].TxID)
199		require.NoError(t, err)
200		assertTx(t, &poolL2Txs[i], fetchedTx)
201		nameZone, offset := fetchedTx.Timestamp.Zone()
202		assert.Equal(t, "UTC", nameZone)
203		assert.Equal(t, 0, offset)
204	}
205	err = l2DBWithACC.AddTxAPI(txs[5])
206	assert.Equal(t, errPoolFull, tracerr.Unwrap(err))
207	// reset maxTxs to original value
208	l2DBWithACC.maxTxs = oldMaxTxs
209
210	// set minFeeUSD to a high value than the tx feeUSD to test the error
211	// of inserting a tx with lower than min fee
212	oldMinFeeUSD := l2DBWithACC.minFeeUSD
213	tx := txs[5]
214	feeAmount, err := common.CalcFeeAmount(tx.Amount, tx.Fee)
215	require.NoError(t, err)
216	feeAmountUSD := common.TokensToUSD(feeAmount, decimals, tokenValue)
217	// set minFeeUSD higher than the tx fee to trigger the error
218	l2DBWithACC.minFeeUSD = feeAmountUSD + 1
219	err = l2DBWithACC.AddTxAPI(tx)
220	require.Error(t, err)
221	assert.Regexp(t, "tx.feeUSD (.*) < minFeeUSD (.*)", err.Error())
222	// reset minFeeUSD to original value
223	l2DBWithACC.minFeeUSD = oldMinFeeUSD
224}
225
226func TestUpdateTxsInfo(t *testing.T) {
227	err := prepareHistoryDB(historyDB)
228	if err != nil {
229		log.Error("Error prepare historyDB", err)
230	}
231	poolL2Txs, err := generatePoolL2Txs()
232	require.NoError(t, err)
233	for i := range poolL2Txs {
234		err := l2DB.AddTxTest(&poolL2Txs[i])
235		require.NoError(t, err)
236
237		// once added, change the Info parameter
238		poolL2Txs[i].Info = "test"
239	}
240	// update the txs
241	err = l2DB.UpdateTxsInfo(poolL2Txs)
242	require.NoError(t, err)
243
244	for i := range poolL2Txs {
245		fetchedTx, err := l2DB.GetTx(poolL2Txs[i].TxID)
246		require.NoError(t, err)
247		assert.Equal(t, "test", fetchedTx.Info)
248	}
249}
250
251func assertTx(t *testing.T, expected, actual *common.PoolL2Tx) {
252	// Check that timestamp has been set within the last 3 seconds
253	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
254	assert.GreaterOrEqual(t, time.Now().UTC().Unix(), actual.Timestamp.Unix())
255	expected.Timestamp = actual.Timestamp
256	// Check absolute fee
257	// find token
258	token := tokens[expected.TokenID]
259	// If the token has value in USD setted
260	if token.USDUpdate != nil {
261		assert.Less(t, token.USDUpdate.Unix()-3, actual.AbsoluteFeeUpdate.Unix())
262		expected.AbsoluteFeeUpdate = actual.AbsoluteFeeUpdate
263		// Set expected fee
264		amountUSD := common.TokensToUSD(expected.Amount, token.Decimals, *token.USD)
265		expected.AbsoluteFee = amountUSD * expected.Fee.Percentage()
266		test.AssertUSD(t, &expected.AbsoluteFee, &actual.AbsoluteFee)
267	}
268	assert.Equal(t, expected, actual)
269}
270
271// NO UPDATE: benchmarks will be done after impl is finished
272// func BenchmarkAddTxTest(b *testing.B) {
273// 	const nInserts = 20
274// 	test.WipeDB(l2DB.DB())
275// 	txs := test.GenPoolTxs(nInserts, tokens)
276// 	now := time.Now()
277// 	for _, tx := range txs {
278// 		_ = l2DB.AddTxTest(tx)
279// 	}
280// 	elapsedTime := time.Since(now)
281// 	log.Info("Time to insert 2048 txs:", elapsedTime)
282// }
283
284func TestGetPending(t *testing.T) {
285	err := prepareHistoryDB(historyDB)
286	if err != nil {
287		log.Error("Error prepare historyDB", err)
288	}
289	poolL2Txs, err := generatePoolL2Txs()
290	require.NoError(t, err)
291	var pendingTxs []*common.PoolL2Tx
292	for i := range poolL2Txs {
293		err := l2DB.AddTxTest(&poolL2Txs[i])
294		require.NoError(t, err)
295		pendingTxs = append(pendingTxs, &poolL2Txs[i])
296	}
297	fetchedTxs, err := l2DB.GetPendingTxs()
298	require.NoError(t, err)
299	assert.Equal(t, len(pendingTxs), len(fetchedTxs))
300	for i := range fetchedTxs {
301		assertTx(t, pendingTxs[i], &fetchedTxs[i])
302	}
303	// Check AbsoluteFee amount
304	for i := range fetchedTxs {
305		tx := &fetchedTxs[i]
306		feeAmount, err := common.CalcFeeAmount(tx.Amount, tx.Fee)
307		require.NoError(t, err)
308		feeAmountUSD := common.TokensToUSD(feeAmount,
309			tokens[tx.TokenID].Decimals, *tokens[tx.TokenID].USD)
310		assert.InEpsilon(t, feeAmountUSD, tx.AbsoluteFee, 0.01)
311	}
312}
313
314func TestStartForging(t *testing.T) {
315	// Generate txs
316	var fakeBatchNum common.BatchNum = 33
317	err := prepareHistoryDB(historyDB)
318	if err != nil {
319		log.Error("Error prepare historyDB", err)
320	}
321	poolL2Txs, err := generatePoolL2Txs()
322	require.NoError(t, err)
323	var startForgingTxIDs []common.TxID
324	randomizer := 0
325	// Add txs to DB
326	for i := range poolL2Txs {
327		err := l2DB.AddTxTest(&poolL2Txs[i])
328		require.NoError(t, err)
329		if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 {
330			startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID)
331		}
332		randomizer++
333	}
334	// Start forging txs
335	err = l2DB.StartForging(startForgingTxIDs, fakeBatchNum)
336	require.NoError(t, err)
337	// Fetch txs and check that they've been updated correctly
338	for _, id := range startForgingTxIDs {
339		fetchedTx, err := l2DBWithACC.GetTxAPI(id)
340		require.NoError(t, err)
341		assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State)
342		assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum)
343	}
344}
345
346func TestDoneForging(t *testing.T) {
347	// Generate txs
348	var fakeBatchNum common.BatchNum = 33
349	err := prepareHistoryDB(historyDB)
350	if err != nil {
351		log.Error("Error prepare historyDB", err)
352	}
353	poolL2Txs, err := generatePoolL2Txs()
354	require.NoError(t, err)
355	var startForgingTxIDs []common.TxID
356	randomizer := 0
357	// Add txs to DB
358	for i := range poolL2Txs {
359		err := l2DB.AddTxTest(&poolL2Txs[i])
360		require.NoError(t, err)
361		if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 {
362			startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID)
363		}
364		randomizer++
365	}
366	// Start forging txs
367	err = l2DB.StartForging(startForgingTxIDs, fakeBatchNum)
368	require.NoError(t, err)
369
370	var doneForgingTxIDs []common.TxID
371	randomizer = 0
372	for _, txID := range startForgingTxIDs {
373		if randomizer%2 == 0 {
374			doneForgingTxIDs = append(doneForgingTxIDs, txID)
375		}
376		randomizer++
377	}
378	// Done forging txs
379	err = l2DB.DoneForging(doneForgingTxIDs, fakeBatchNum)
380	require.NoError(t, err)
381
382	// Fetch txs and check that they've been updated correctly
383	for _, id := range doneForgingTxIDs {
384		fetchedTx, err := l2DBWithACC.GetTxAPI(id)
385		require.NoError(t, err)
386		assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State)
387		assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum)
388	}
389}
390
391func TestInvalidate(t *testing.T) {
392	// Generate txs
393	var fakeBatchNum common.BatchNum = 33
394	err := prepareHistoryDB(historyDB)
395	if err != nil {
396		log.Error("Error prepare historyDB", err)
397	}
398	poolL2Txs, err := generatePoolL2Txs()
399	require.NoError(t, err)
400	var invalidTxIDs []common.TxID
401	randomizer := 0
402	// Add txs to DB
403	for i := range poolL2Txs {
404		err := l2DB.AddTxTest(&poolL2Txs[i])
405		require.NoError(t, err)
406		if poolL2Txs[i].State != common.PoolL2TxStateInvalid && randomizer%2 == 0 {
407			randomizer++
408			invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID)
409		}
410	}
411	// Invalidate txs
412	err = l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
413	require.NoError(t, err)
414	// Fetch txs and check that they've been updated correctly
415	for _, id := range invalidTxIDs {
416		fetchedTx, err := l2DBWithACC.GetTxAPI(id)
417		require.NoError(t, err)
418		assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
419		assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum)
420	}
421}
422
423func TestInvalidateOldNonces(t *testing.T) {
424	// Generate txs
425	var fakeBatchNum common.BatchNum = 33
426	err := prepareHistoryDB(historyDB)
427	if err != nil {
428		log.Error("Error prepare historyDB", err)
429	}
430	poolL2Txs, err := generatePoolL2Txs()
431	require.NoError(t, err)
432	// Update Accounts currentNonce
433	var updateAccounts []common.IdxNonce
434	var currentNonce = common.Nonce(1)
435	for i := range accs {
436		updateAccounts = append(updateAccounts, common.IdxNonce{
437			Idx:   accs[i].Idx,
438			Nonce: common.Nonce(currentNonce),
439		})
440	}
441	// Add txs to DB
442	var invalidTxIDs []common.TxID
443	for i := range poolL2Txs {
444		if poolL2Txs[i].Nonce < currentNonce {
445			invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID)
446		}
447		err := l2DB.AddTxTest(&poolL2Txs[i])
448		require.NoError(t, err)
449	}
450	// sanity check
451	require.Greater(t, len(invalidTxIDs), 0)
452
453	err = l2DB.InvalidateOldNonces(updateAccounts, fakeBatchNum)
454	require.NoError(t, err)
455	// Fetch txs and check that they've been updated correctly
456	for _, id := range invalidTxIDs {
457		fetchedTx, err := l2DBWithACC.GetTxAPI(id)
458		require.NoError(t, err)
459		assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
460		assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum)
461	}
462}
463
464// TestReorg: first part of the test with reorg
465// With invalidated transactions BEFORE reorgBatch
466// And forged transactions in reorgBatch
467func TestReorg(t *testing.T) {
468	// Generate txs
469	const lastValidBatch common.BatchNum = 20
470	const reorgBatch common.BatchNum = lastValidBatch + 1
471	err := prepareHistoryDB(historyDB)
472	if err != nil {
473		log.Error("Error prepare historyDB", err)
474	}
475	poolL2Txs, err := generatePoolL2Txs()
476	require.NoError(t, err)
477
478	reorgedTxIDs := []common.TxID{}
479	nonReorgedTxIDs := []common.TxID{}
480	var startForgingTxIDs []common.TxID
481	var invalidTxIDs []common.TxID
482	var allTxRandomize []common.TxID
483	randomizer := 0
484	// Add txs to DB
485	for i := range poolL2Txs {
486		err := l2DB.AddTxTest(&poolL2Txs[i])
487		require.NoError(t, err)
488		if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 {
489			startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID)
490			allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID)
491		} else if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%3 == 0 {
492			invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID)
493			allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID)
494		}
495		randomizer++
496	}
497	// Start forging txs
498	err = l2DB.StartForging(startForgingTxIDs, lastValidBatch)
499	require.NoError(t, err)
500
501	var doneForgingTxIDs []common.TxID
502	randomizer = 0
503	for _, txID := range allTxRandomize {
504		invalidTx := false
505		for i := range invalidTxIDs {
506			if invalidTxIDs[i] == txID {
507				invalidTx = true
508				nonReorgedTxIDs = append(nonReorgedTxIDs, txID)
509			}
510		}
511		if !invalidTx {
512			if randomizer%2 == 0 {
513				doneForgingTxIDs = append(doneForgingTxIDs, txID)
514				reorgedTxIDs = append(reorgedTxIDs, txID)
515			} else {
516				nonReorgedTxIDs = append(nonReorgedTxIDs, txID)
517			}
518			randomizer++
519		}
520	}
521
522	// Invalidate txs BEFORE reorgBatch --> nonReorg
523	err = l2DB.InvalidateTxs(invalidTxIDs, lastValidBatch)
524	require.NoError(t, err)
525	// Done forging txs in reorgBatch --> Reorg
526	err = l2DB.DoneForging(doneForgingTxIDs, reorgBatch)
527	require.NoError(t, err)
528
529	err = l2DB.Reorg(lastValidBatch)
530	require.NoError(t, err)
531	for _, id := range reorgedTxIDs {
532		tx, err := l2DBWithACC.GetTxAPI(id)
533		require.NoError(t, err)
534		assert.Nil(t, tx.BatchNum)
535		assert.Equal(t, common.PoolL2TxStatePending, tx.State)
536	}
537	for _, id := range nonReorgedTxIDs {
538		fetchedTx, err := l2DBWithACC.GetTxAPI(id)
539		require.NoError(t, err)
540		assert.Equal(t, lastValidBatch, *fetchedTx.BatchNum)
541	}
542}
543
544// TestReorg: second part of test with reorg
545// With invalidated transactions in reorgBatch
546// And forged transactions BEFORE reorgBatch
547func TestReorg2(t *testing.T) {
548	// Generate txs
549	const lastValidBatch common.BatchNum = 20
550	const reorgBatch common.BatchNum = lastValidBatch + 1
551	err := prepareHistoryDB(historyDB)
552	if err != nil {
553		log.Error("Error prepare historyDB", err)
554	}
555	poolL2Txs, err := generatePoolL2Txs()
556	require.NoError(t, err)
557
558	reorgedTxIDs := []common.TxID{}
559	nonReorgedTxIDs := []common.TxID{}
560	var startForgingTxIDs []common.TxID
561	var invalidTxIDs []common.TxID
562	var allTxRandomize []common.TxID
563	randomizer := 0
564	// Add txs to DB
565	for i := range poolL2Txs {
566		err := l2DB.AddTxTest(&poolL2Txs[i])
567		require.NoError(t, err)
568		if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 {
569			startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID)
570			allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID)
571		} else if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%3 == 0 {
572			invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID)
573			allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID)
574		}
575		randomizer++
576	}
577	// Start forging txs
578	err = l2DB.StartForging(startForgingTxIDs, lastValidBatch)
579	require.NoError(t, err)
580
581	var doneForgingTxIDs []common.TxID
582	randomizer = 0
583	for _, txID := range allTxRandomize {
584		invalidTx := false
585		for i := range invalidTxIDs {
586			if invalidTxIDs[i] == txID {
587				invalidTx = true
588				reorgedTxIDs = append(reorgedTxIDs, txID)
589			}
590		}
591		if !invalidTx {
592			if randomizer%2 == 0 {
593				doneForgingTxIDs = append(doneForgingTxIDs, txID)
594			}
595			nonReorgedTxIDs = append(nonReorgedTxIDs, txID)
596			randomizer++
597		}
598	}
599	// Done forging txs BEFORE reorgBatch --> nonReorg
600	err = l2DB.DoneForging(doneForgingTxIDs, lastValidBatch)
601	require.NoError(t, err)
602	// Invalidate txs in reorgBatch --> Reorg
603	err = l2DB.InvalidateTxs(invalidTxIDs, reorgBatch)
604	require.NoError(t, err)
605
606	err = l2DB.Reorg(lastValidBatch)
607	require.NoError(t, err)
608	for _, id := range reorgedTxIDs {
609		tx, err := l2DBWithACC.GetTxAPI(id)
610		require.NoError(t, err)
611		assert.Nil(t, tx.BatchNum)
612		assert.Equal(t, common.PoolL2TxStatePending, tx.State)
613	}
614	for _, id := range nonReorgedTxIDs {
615		fetchedTx, err := l2DBWithACC.GetTxAPI(id)
616		require.NoError(t, err)
617		assert.Equal(t, lastValidBatch, *fetchedTx.BatchNum)
618	}
619}
620
621func TestPurge(t *testing.T) {
622	// Generate txs
623	err := prepareHistoryDB(historyDB)
624	if err != nil {
625		log.Error("Error prepare historyDB", err)
626	}
627	// generatePoolL2Txs
628	generateTx := int(l2DB.maxTxs/8 + 1)
629	var poolL2Tx []common.PoolL2Tx
630	for i := 0; i < generateTx; i++ {
631		poolL2TxAux, err := generatePoolL2Txs()
632		require.NoError(t, err)
633		poolL2Tx = append(poolL2Tx, poolL2TxAux...)
634	}
635
636	afterTTLIDs := []common.TxID{}
637	keepedIDs := []common.TxID{}
638	var deletedIDs []common.TxID
639	var invalidTxIDs []common.TxID
640	var doneForgingTxIDs []common.TxID
641	const toDeleteBatchNum common.BatchNum = 30
642	safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1
643	// Add txs to the DB
644	for i := 0; i < len(poolL2Tx); i++ {
645		tx := poolL2Tx[i]
646		if i%2 == 0 { // keep tx
647			keepedIDs = append(keepedIDs, tx.TxID)
648		} else { // delete after safety period
649			if i%3 == 0 {
650				doneForgingTxIDs = append(doneForgingTxIDs, tx.TxID)
651			} else if i%5 == 0 {
652				invalidTxIDs = append(invalidTxIDs, tx.TxID)
653			} else {
654				afterTTLIDs = append(afterTTLIDs, tx.TxID)
655			}
656			deletedIDs = append(deletedIDs, poolL2Tx[i].TxID)
657		}
658		err := l2DB.AddTxTest(&tx)
659		require.NoError(t, err)
660	}
661	// Set batchNum keeped txs
662	for i := range keepedIDs {
663		_, err = l2DB.dbWrite.Exec(
664			"UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;",
665			safeBatchNum, keepedIDs[i],
666		)
667		require.NoError(t, err)
668	}
669	// Start forging txs and set batchNum
670	err = l2DB.StartForging(doneForgingTxIDs, toDeleteBatchNum)
671	require.NoError(t, err)
672	// Done forging txs and set batchNum
673	err = l2DB.DoneForging(doneForgingTxIDs, toDeleteBatchNum)
674	require.NoError(t, err)
675	// Invalidate txs and set batchNum
676	err = l2DB.InvalidateTxs(invalidTxIDs, toDeleteBatchNum)
677	require.NoError(t, err)
678	// Update timestamp of afterTTL txs
679	deleteTimestamp := time.Unix(time.Now().UTC().Unix()-int64(l2DB.ttl.Seconds()+float64(4*time.Second)), 0)
680	for _, id := range afterTTLIDs {
681		// Set timestamp
682		_, err = l2DB.dbWrite.Exec(
683			"UPDATE tx_pool SET timestamp = $1, state = $2 WHERE tx_id = $3;",
684			deleteTimestamp, common.PoolL2TxStatePending, id,
685		)
686		require.NoError(t, err)
687	}
688
689	// Purge txs
690	err = l2DB.Purge(safeBatchNum)
691	require.NoError(t, err)
692	// Check results
693	for _, id := range deletedIDs {
694		_, err := l2DB.GetTx(id)
695		assert.Error(t, err)
696	}
697	for _, id := range keepedIDs {
698		_, err := l2DB.GetTx(id)
699		require.NoError(t, err)
700	}
701}
702
703func TestAuth(t *testing.T) {
704	test.WipeDB(l2DB.DB())
705	const nAuths = 5
706	chainID := uint16(0)
707	hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
708	// Generate authorizations
709	auths := test.GenAuths(nAuths, chainID, hermezContractAddr)
710	for i := 0; i < len(auths); i++ {
711		// Add to the DB
712		err := l2DB.AddAccountCreationAuth(auths[i])
713		require.NoError(t, err)
714		// Fetch from DB
715		auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr)
716		require.NoError(t, err)
717		// Check fetched vs generated
718		assert.Equal(t, auths[i].EthAddr, auth.EthAddr)
719		assert.Equal(t, auths[i].BJJ, auth.BJJ)
720		assert.Equal(t, auths[i].Signature, auth.Signature)
721		assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix())
722		nameZone, offset := auths[i].Timestamp.Zone()
723		assert.Equal(t, "UTC", nameZone)
724		assert.Equal(t, 0, offset)
725	}
726}
727
728func TestManyAuth(t *testing.T) {
729	test.WipeDB(l2DB.DB())
730	const nAuths = 5
731	chainID := uint16(0)
732	hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
733	// Generate authorizations
734	genAuths := test.GenAuths(nAuths, chainID, hermezContractAddr)
735	auths := make([]common.AccountCreationAuth, len(genAuths))
736	// Convert to a non-pointer slice
737	for i := 0; i < len(genAuths); i++ {
738		auths[i] = *genAuths[i]
739	}
740
741	// Add a duplicate one to check the not exist condition
742	err := l2DB.AddAccountCreationAuth(genAuths[0])
743	require.NoError(t, err)
744
745	// Add to the DB
746	err = l2DB.AddManyAccountCreationAuth(auths)
747	require.NoError(t, err)
748
749	// Assert the result
750	for i := 0; i < len(auths); i++ {
751		// Fetch from DB
752		auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr)
753		require.NoError(t, err)
754		// Check fetched vs generated
755		assert.Equal(t, auths[i].EthAddr, auth.EthAddr)
756		assert.Equal(t, auths[i].BJJ, auth.BJJ)
757		assert.Equal(t, auths[i].Signature, auth.Signature)
758		assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix())
759		nameZone, offset := auths[i].Timestamp.Zone()
760		assert.Equal(t, "UTC", nameZone)
761		assert.Equal(t, 0, offset)
762	}
763}
764
765func TestAddGet(t *testing.T) {
766	err := prepareHistoryDB(historyDB)
767	if err != nil {
768		log.Error("Error prepare historyDB", err)
769	}
770	poolL2Txs, err := generatePoolL2Txs()
771	require.NoError(t, err)
772
773	// We will work with only 3 txs
774	require.GreaterOrEqual(t, len(poolL2Txs), 3)
775	txs := poolL2Txs[:3]
776	// NOTE: By changing the tx fields, the signature will no longer be
777	// valid, but we are not checking the signautre here so it's OK.
778	// 0. Has ToIdx >= 256 && ToEthAddr == 0 && ToBJJ == 0
779	require.GreaterOrEqual(t, int(txs[0].ToIdx), 256)
780	txs[0].ToEthAddr = ethCommon.Address{}
781	txs[0].ToBJJ = babyjub.PublicKeyComp{}
782	// 1. Has ToIdx >= 256 && ToEthAddr != 0 && ToBJJ != 0
783	require.GreaterOrEqual(t, int(txs[1].ToIdx), 256)
784	require.NotEqual(t, txs[1].ToEthAddr, ethCommon.Address{})
785	require.NotEqual(t, txs[1].ToBJJ, babyjub.PublicKeyComp{})
786	// 2. Has ToIdx == 0 && ToEthAddr != 0 && ToBJJ != 0
787	txs[2].ToIdx = 0
788	require.NotEqual(t, txs[2].ToEthAddr, ethCommon.Address{})
789	require.NotEqual(t, txs[2].ToBJJ, babyjub.PublicKeyComp{})
790
791	for i := 0; i < len(txs); i++ {
792		require.NoError(t, txs[i].SetID())
793		require.NoError(t, l2DB.AddTxTest(&txs[i]))
794	}
795	// Verify that the inserts haven't altered any field (specially
796	// ToEthAddr and ToBJJ)
797	for i := 0; i < len(txs); i++ {
798		dbTx, err := l2DB.GetTx(txs[i].TxID)
799		require.NoError(t, err)
800		// Ignore Timestamp, AbsoluteFee, AbsoluteFeeUpdate
801		txs[i].Timestamp = dbTx.Timestamp
802		txs[i].AbsoluteFee = dbTx.AbsoluteFee
803		txs[i].AbsoluteFeeUpdate = dbTx.AbsoluteFeeUpdate
804		assert.Equal(t, txs[i], *dbTx)
805	}
806}
807
808func TestPurgeByExternalDelete(t *testing.T) {
809	err := prepareHistoryDB(historyDB)
810	if err != nil {
811		log.Error("Error prepare historyDB", err)
812	}
813	txs, err := generatePoolL2Txs()
814	require.NoError(t, err)
815
816	// We will work with 8 txs
817	require.GreaterOrEqual(t, len(txs), 8)
818	txs = txs[:8]
819	for i := range txs {
820		require.NoError(t, l2DB.AddTxTest(&txs[i]))
821	}
822
823	// We will recreate this scenario:
824	// tx index, status , external_delete
825	// 0       , pending, false
826	// 1       , pending, false
827	// 2       , pending, true // will be deleted
828	// 3       , pending, true // will be deleted
829	// 4       , fging  , false
830	// 5       , fging  , false
831	// 6       , fging  , true
832	// 7       , fging  , true
833
834	require.NoError(t, l2DB.StartForging(
835		[]common.TxID{txs[4].TxID, txs[5].TxID, txs[6].TxID, txs[7].TxID},
836		1))
837	_, err = l2DB.dbWrite.Exec(
838		`UPDATE tx_pool SET external_delete = true WHERE
839			tx_id IN ($1, $2, $3, $4)
840		;`,
841		txs[2].TxID, txs[3].TxID, txs[6].TxID, txs[7].TxID,
842	)
843	require.NoError(t, err)
844	require.NoError(t, l2DB.PurgeByExternalDelete())
845
846	// Query txs that are have been not deleted
847	for _, i := range []int{0, 1, 4, 5, 6, 7} {
848		txID := txs[i].TxID
849		_, err := l2DB.GetTx(txID)
850		require.NoError(t, err)
851	}
852
853	// Query txs that have been deleted
854	for _, i := range []int{2, 3} {
855		txID := txs[i].TxID
856		_, err := l2DB.GetTx(txID)
857		require.Equal(t, sql.ErrNoRows, tracerr.Unwrap(err))
858	}
859}