package l2db import ( "database/sql" "fmt" "os" "testing" "time" ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" dbUtils "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/hermeznetwork/hermez-node/log" "github.com/hermeznetwork/hermez-node/test" "github.com/hermeznetwork/hermez-node/test/til" "github.com/hermeznetwork/tracerr" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var decimals = uint64(3) var tokenValue = 1.0 // The price update gives a value of 1.0 USD to the token var l2DB *L2DB var l2DBWithACC *L2DB var historyDB *historydb.HistoryDB var tc *til.Context var tokens map[common.TokenID]historydb.TokenWithUSD var accs map[common.Idx]common.Account func TestMain(m *testing.M) { // init DB pass := os.Getenv("POSTGRES_PASS") db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez") if err != nil { panic(err) } l2DB = NewL2DB(db, db, 10, 1000, 0.0, 1000.0, 24*time.Hour, nil) apiConnCon := dbUtils.NewAPIConnectionController(1, time.Second) l2DBWithACC = NewL2DB(db, db, 10, 1000, 0.0, 1000.0, 24*time.Hour, apiConnCon) test.WipeDB(l2DB.DB()) historyDB = historydb.NewHistoryDB(db, db, nil) // Run tests result := m.Run() // Close DB if err := db.Close(); err != nil { log.Error("Error closing the history DB:", err) } os.Exit(result) } func prepareHistoryDB(historyDB *historydb.HistoryDB) error { // Reset DB test.WipeDB(l2DB.DB()) // Generate pool txs using til setBlockchain := ` Type: Blockchain AddToken(1) AddToken(2) CreateAccountDeposit(1) A: 20000 CreateAccountDeposit(2) A: 20000 CreateAccountDeposit(1) B: 10000 CreateAccountDeposit(2) B: 10000 > batchL1 > batchL1 > block > block ` tc = til.NewContext(uint16(0), common.RollupConstMaxL1UserTx) tilCfgExtra := til.ConfigExtra{ BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"), CoordUser: "A", } blocks, err := tc.GenerateBlocks(setBlockchain) if err != nil { return tracerr.Wrap(err) } err = tc.FillBlocksExtra(blocks, &tilCfgExtra) if err != nil { return tracerr.Wrap(err) } for i := range blocks { block := &blocks[i] for j := range block.Rollup.AddedTokens { token := &block.Rollup.AddedTokens[j] token.Name = fmt.Sprintf("Token %d", token.TokenID) token.Symbol = fmt.Sprintf("TK%d", token.TokenID) token.Decimals = decimals } } tokens = make(map[common.TokenID]historydb.TokenWithUSD) // tokensValue = make(map[common.TokenID]float64) accs = make(map[common.Idx]common.Account) now := time.Now().UTC() // Add all blocks except for the last one for i := range blocks[:len(blocks)-1] { if err := historyDB.AddBlockSCData(&blocks[i]); err != nil { return tracerr.Wrap(err) } for _, batch := range blocks[i].Rollup.Batches { for _, account := range batch.CreatedAccounts { accs[account.Idx] = account } } for _, token := range blocks[i].Rollup.AddedTokens { readToken := historydb.TokenWithUSD{ TokenID: token.TokenID, EthBlockNum: token.EthBlockNum, EthAddr: token.EthAddr, Name: token.Name, Symbol: token.Symbol, Decimals: token.Decimals, USD: &tokenValue, USDUpdate: &now, } tokens[token.TokenID] = readToken // Set value to the tokens err := historyDB.UpdateTokenValue(readToken.EthAddr, *readToken.USD) if err != nil { return tracerr.Wrap(err) } } } return nil } func generatePoolL2Txs() ([]common.PoolL2Tx, error) { // Fee = 126 corresponds to ~10% setPool := ` Type: PoolL2 PoolTransfer(1) A-B: 6000 (126) PoolTransfer(2) A-B: 3000 (126) PoolTransfer(1) B-A: 5000 (126) PoolTransfer(2) B-A: 10000 (126) PoolTransfer(1) A-B: 7000 (126) PoolTransfer(2) A-B: 2000 (126) PoolTransfer(1) B-A: 8000 (126) PoolTransfer(2) B-A: 1000 (126) PoolTransfer(1) A-B: 3000 (126) PoolTransferToEthAddr(2) B-A: 5000 (126) PoolTransferToBJJ(2) B-A: 5000 (126) PoolExit(1) A: 5000 (126) PoolExit(2) B: 3000 (126) ` poolL2Txs, err := tc.GeneratePoolL2Txs(setPool) if err != nil { return nil, tracerr.Wrap(err) } return poolL2Txs, nil } func TestAddTxTest(t *testing.T) { err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) fetchedTx, err := l2DB.GetTx(poolL2Txs[i].TxID) require.NoError(t, err) assertTx(t, &poolL2Txs[i], fetchedTx) nameZone, offset := fetchedTx.Timestamp.Zone() assert.Equal(t, "UTC", nameZone) assert.Equal(t, 0, offset) } } func TestAddTxAPI(t *testing.T) { err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } oldMaxTxs := l2DBWithACC.maxTxs // set max number of pending txs that can be kept in the pool to 5 l2DBWithACC.maxTxs = 5 poolL2Txs, err := generatePoolL2Txs() txs := make([]*PoolL2TxWrite, len(poolL2Txs)) for i := range poolL2Txs { txs[i] = NewPoolL2TxWriteFromPoolL2Tx(&poolL2Txs[i]) } require.NoError(t, err) require.GreaterOrEqual(t, len(poolL2Txs), 8) for i := range txs[:5] { err := l2DBWithACC.AddTxAPI(txs[i]) require.NoError(t, err) fetchedTx, err := l2DB.GetTx(poolL2Txs[i].TxID) require.NoError(t, err) assertTx(t, &poolL2Txs[i], fetchedTx) nameZone, offset := fetchedTx.Timestamp.Zone() assert.Equal(t, "UTC", nameZone) assert.Equal(t, 0, offset) } err = l2DBWithACC.AddTxAPI(txs[5]) assert.Equal(t, errPoolFull, tracerr.Unwrap(err)) // reset maxTxs to original value l2DBWithACC.maxTxs = oldMaxTxs // set minFeeUSD to a high value than the tx feeUSD to test the error // of inserting a tx with lower than min fee oldMinFeeUSD := l2DBWithACC.minFeeUSD tx := txs[5] feeAmount, err := common.CalcFeeAmount(tx.Amount, tx.Fee) require.NoError(t, err) feeAmountUSD := common.TokensToUSD(feeAmount, decimals, tokenValue) // set minFeeUSD higher than the tx fee to trigger the error l2DBWithACC.minFeeUSD = feeAmountUSD + 1 err = l2DBWithACC.AddTxAPI(tx) require.Error(t, err) assert.Regexp(t, "tx.feeUSD (.*) < minFeeUSD (.*)", err.Error()) // reset minFeeUSD to original value l2DBWithACC.minFeeUSD = oldMinFeeUSD } func TestUpdateTxsInfo(t *testing.T) { err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) // once added, change the Info parameter poolL2Txs[i].Info = "test" } // update the txs err = l2DB.UpdateTxsInfo(poolL2Txs) require.NoError(t, err) for i := range poolL2Txs { fetchedTx, err := l2DB.GetTx(poolL2Txs[i].TxID) require.NoError(t, err) assert.Equal(t, "test", fetchedTx.Info) } } func assertTx(t *testing.T, expected, actual *common.PoolL2Tx) { // Check that timestamp has been set within the last 3 seconds assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix()) assert.GreaterOrEqual(t, time.Now().UTC().Unix(), actual.Timestamp.Unix()) expected.Timestamp = actual.Timestamp // Check absolute fee // find token token := tokens[expected.TokenID] // If the token has value in USD setted if token.USDUpdate != nil { assert.Less(t, token.USDUpdate.Unix()-3, actual.AbsoluteFeeUpdate.Unix()) expected.AbsoluteFeeUpdate = actual.AbsoluteFeeUpdate // Set expected fee amountUSD := common.TokensToUSD(expected.Amount, token.Decimals, *token.USD) expected.AbsoluteFee = amountUSD * expected.Fee.Percentage() test.AssertUSD(t, &expected.AbsoluteFee, &actual.AbsoluteFee) } assert.Equal(t, expected, actual) } // NO UPDATE: benchmarks will be done after impl is finished // func BenchmarkAddTxTest(b *testing.B) { // const nInserts = 20 // test.WipeDB(l2DB.DB()) // txs := test.GenPoolTxs(nInserts, tokens) // now := time.Now() // for _, tx := range txs { // _ = l2DB.AddTxTest(tx) // } // elapsedTime := time.Since(now) // log.Info("Time to insert 2048 txs:", elapsedTime) // } func TestGetPending(t *testing.T) { err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) var pendingTxs []*common.PoolL2Tx for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) pendingTxs = append(pendingTxs, &poolL2Txs[i]) } fetchedTxs, err := l2DB.GetPendingTxs() require.NoError(t, err) assert.Equal(t, len(pendingTxs), len(fetchedTxs)) for i := range fetchedTxs { assertTx(t, pendingTxs[i], &fetchedTxs[i]) } // Check AbsoluteFee amount for i := range fetchedTxs { tx := &fetchedTxs[i] feeAmount, err := common.CalcFeeAmount(tx.Amount, tx.Fee) require.NoError(t, err) feeAmountUSD := common.TokensToUSD(feeAmount, tokens[tx.TokenID].Decimals, *tokens[tx.TokenID].USD) assert.InEpsilon(t, feeAmountUSD, tx.AbsoluteFee, 0.01) } } func TestL2DB_GetPoolTxs(t *testing.T) { err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() state := common.PoolL2TxState("pend") idx := common.Idx(256) var pendingTxs []*common.PoolL2Tx for i := range poolL2Txs { if poolL2Txs[i].FromIdx == idx || poolL2Txs[i].ToIdx == idx { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) pendingTxs = append(pendingTxs, &poolL2Txs[i]) } } fetchedTxs, err := l2DBWithACC.GetPoolTxs(&idx, &state) require.NoError(t, err) assert.Equal(t, len(pendingTxs), len(fetchedTxs)) } func TestStartForging(t *testing.T) { // Generate txs var fakeBatchNum common.BatchNum = 33 err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) var startForgingTxIDs []common.TxID randomizer := 0 // Add txs to DB for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 { startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID) } randomizer++ } // Start forging txs err = l2DB.StartForging(startForgingTxIDs, fakeBatchNum) require.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range startForgingTxIDs { fetchedTx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State) assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum) } } func TestDoneForging(t *testing.T) { // Generate txs var fakeBatchNum common.BatchNum = 33 err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) var startForgingTxIDs []common.TxID randomizer := 0 // Add txs to DB for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 { startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID) } randomizer++ } // Start forging txs err = l2DB.StartForging(startForgingTxIDs, fakeBatchNum) require.NoError(t, err) var doneForgingTxIDs []common.TxID randomizer = 0 for _, txID := range startForgingTxIDs { if randomizer%2 == 0 { doneForgingTxIDs = append(doneForgingTxIDs, txID) } randomizer++ } // Done forging txs err = l2DB.DoneForging(doneForgingTxIDs, fakeBatchNum) require.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range doneForgingTxIDs { fetchedTx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State) assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum) } } func TestInvalidate(t *testing.T) { // Generate txs var fakeBatchNum common.BatchNum = 33 err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) var invalidTxIDs []common.TxID randomizer := 0 // Add txs to DB for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) if poolL2Txs[i].State != common.PoolL2TxStateInvalid && randomizer%2 == 0 { randomizer++ invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID) } } // Invalidate txs err = l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum) require.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range invalidTxIDs { fetchedTx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum) } } func TestInvalidateOldNonces(t *testing.T) { // Generate txs var fakeBatchNum common.BatchNum = 33 err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) // Update Accounts currentNonce var updateAccounts []common.IdxNonce var currentNonce = common.Nonce(1) for i := range accs { updateAccounts = append(updateAccounts, common.IdxNonce{ Idx: accs[i].Idx, Nonce: common.Nonce(currentNonce), }) } // Add txs to DB var invalidTxIDs []common.TxID for i := range poolL2Txs { if poolL2Txs[i].Nonce < currentNonce { invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID) } err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) } // sanity check require.Greater(t, len(invalidTxIDs), 0) err = l2DB.InvalidateOldNonces(updateAccounts, fakeBatchNum) require.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range invalidTxIDs { fetchedTx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) assert.Equal(t, &fakeBatchNum, fetchedTx.BatchNum) } } // TestReorg: first part of the test with reorg // With invalidated transactions BEFORE reorgBatch // And forged transactions in reorgBatch func TestReorg(t *testing.T) { // Generate txs const lastValidBatch common.BatchNum = 20 const reorgBatch common.BatchNum = lastValidBatch + 1 err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) reorgedTxIDs := []common.TxID{} nonReorgedTxIDs := []common.TxID{} var startForgingTxIDs []common.TxID var invalidTxIDs []common.TxID var allTxRandomize []common.TxID randomizer := 0 // Add txs to DB for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 { startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID) allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID) } else if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%3 == 0 { invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID) allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID) } randomizer++ } // Start forging txs err = l2DB.StartForging(startForgingTxIDs, lastValidBatch) require.NoError(t, err) var doneForgingTxIDs []common.TxID randomizer = 0 for _, txID := range allTxRandomize { invalidTx := false for i := range invalidTxIDs { if invalidTxIDs[i] == txID { invalidTx = true nonReorgedTxIDs = append(nonReorgedTxIDs, txID) } } if !invalidTx { if randomizer%2 == 0 { doneForgingTxIDs = append(doneForgingTxIDs, txID) reorgedTxIDs = append(reorgedTxIDs, txID) } else { nonReorgedTxIDs = append(nonReorgedTxIDs, txID) } randomizer++ } } // Invalidate txs BEFORE reorgBatch --> nonReorg err = l2DB.InvalidateTxs(invalidTxIDs, lastValidBatch) require.NoError(t, err) // Done forging txs in reorgBatch --> Reorg err = l2DB.DoneForging(doneForgingTxIDs, reorgBatch) require.NoError(t, err) err = l2DB.Reorg(lastValidBatch) require.NoError(t, err) for _, id := range reorgedTxIDs { tx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Nil(t, tx.BatchNum) assert.Equal(t, common.PoolL2TxStatePending, tx.State) } for _, id := range nonReorgedTxIDs { fetchedTx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Equal(t, lastValidBatch, *fetchedTx.BatchNum) } } // TestReorg: second part of test with reorg // With invalidated transactions in reorgBatch // And forged transactions BEFORE reorgBatch func TestReorg2(t *testing.T) { // Generate txs const lastValidBatch common.BatchNum = 20 const reorgBatch common.BatchNum = lastValidBatch + 1 err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) reorgedTxIDs := []common.TxID{} nonReorgedTxIDs := []common.TxID{} var startForgingTxIDs []common.TxID var invalidTxIDs []common.TxID var allTxRandomize []common.TxID randomizer := 0 // Add txs to DB for i := range poolL2Txs { err := l2DB.AddTxTest(&poolL2Txs[i]) require.NoError(t, err) if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%2 == 0 { startForgingTxIDs = append(startForgingTxIDs, poolL2Txs[i].TxID) allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID) } else if poolL2Txs[i].State == common.PoolL2TxStatePending && randomizer%3 == 0 { invalidTxIDs = append(invalidTxIDs, poolL2Txs[i].TxID) allTxRandomize = append(allTxRandomize, poolL2Txs[i].TxID) } randomizer++ } // Start forging txs err = l2DB.StartForging(startForgingTxIDs, lastValidBatch) require.NoError(t, err) var doneForgingTxIDs []common.TxID randomizer = 0 for _, txID := range allTxRandomize { invalidTx := false for i := range invalidTxIDs { if invalidTxIDs[i] == txID { invalidTx = true reorgedTxIDs = append(reorgedTxIDs, txID) } } if !invalidTx { if randomizer%2 == 0 { doneForgingTxIDs = append(doneForgingTxIDs, txID) } nonReorgedTxIDs = append(nonReorgedTxIDs, txID) randomizer++ } } // Done forging txs BEFORE reorgBatch --> nonReorg err = l2DB.DoneForging(doneForgingTxIDs, lastValidBatch) require.NoError(t, err) // Invalidate txs in reorgBatch --> Reorg err = l2DB.InvalidateTxs(invalidTxIDs, reorgBatch) require.NoError(t, err) err = l2DB.Reorg(lastValidBatch) require.NoError(t, err) for _, id := range reorgedTxIDs { tx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Nil(t, tx.BatchNum) assert.Equal(t, common.PoolL2TxStatePending, tx.State) } for _, id := range nonReorgedTxIDs { fetchedTx, err := l2DBWithACC.GetTxAPI(id) require.NoError(t, err) assert.Equal(t, lastValidBatch, *fetchedTx.BatchNum) } } func TestPurge(t *testing.T) { // Generate txs err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } // generatePoolL2Txs generateTx := int(l2DB.maxTxs/8 + 1) var poolL2Tx []common.PoolL2Tx for i := 0; i < generateTx; i++ { poolL2TxAux, err := generatePoolL2Txs() require.NoError(t, err) poolL2Tx = append(poolL2Tx, poolL2TxAux...) } afterTTLIDs := []common.TxID{} keepedIDs := []common.TxID{} var deletedIDs []common.TxID var invalidTxIDs []common.TxID var doneForgingTxIDs []common.TxID const toDeleteBatchNum common.BatchNum = 30 safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1 // Add txs to the DB for i := 0; i < len(poolL2Tx); i++ { tx := poolL2Tx[i] if i%2 == 0 { // keep tx keepedIDs = append(keepedIDs, tx.TxID) } else { // delete after safety period if i%3 == 0 { doneForgingTxIDs = append(doneForgingTxIDs, tx.TxID) } else if i%5 == 0 { invalidTxIDs = append(invalidTxIDs, tx.TxID) } else { afterTTLIDs = append(afterTTLIDs, tx.TxID) } deletedIDs = append(deletedIDs, poolL2Tx[i].TxID) } err := l2DB.AddTxTest(&tx) require.NoError(t, err) } // Set batchNum keeped txs for i := range keepedIDs { _, err = l2DB.dbWrite.Exec( "UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;", safeBatchNum, keepedIDs[i], ) require.NoError(t, err) } // Start forging txs and set batchNum err = l2DB.StartForging(doneForgingTxIDs, toDeleteBatchNum) require.NoError(t, err) // Done forging txs and set batchNum err = l2DB.DoneForging(doneForgingTxIDs, toDeleteBatchNum) require.NoError(t, err) // Invalidate txs and set batchNum err = l2DB.InvalidateTxs(invalidTxIDs, toDeleteBatchNum) require.NoError(t, err) // Update timestamp of afterTTL txs deleteTimestamp := time.Unix(time.Now().UTC().Unix()-int64(l2DB.ttl.Seconds()+float64(4*time.Second)), 0) for _, id := range afterTTLIDs { // Set timestamp _, err = l2DB.dbWrite.Exec( "UPDATE tx_pool SET timestamp = $1, state = $2 WHERE tx_id = $3;", deleteTimestamp, common.PoolL2TxStatePending, id, ) require.NoError(t, err) } // Purge txs err = l2DB.Purge(safeBatchNum) require.NoError(t, err) // Check results for _, id := range deletedIDs { _, err := l2DB.GetTx(id) assert.Error(t, err) } for _, id := range keepedIDs { _, err := l2DB.GetTx(id) require.NoError(t, err) } } func TestAuth(t *testing.T) { test.WipeDB(l2DB.DB()) const nAuths = 5 chainID := uint16(0) hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") // Generate authorizations auths := test.GenAuths(nAuths, chainID, hermezContractAddr) for i := 0; i < len(auths); i++ { // Add to the DB err := l2DB.AddAccountCreationAuth(auths[i]) require.NoError(t, err) // Fetch from DB auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr) require.NoError(t, err) // Check fetched vs generated assert.Equal(t, auths[i].EthAddr, auth.EthAddr) assert.Equal(t, auths[i].BJJ, auth.BJJ) assert.Equal(t, auths[i].Signature, auth.Signature) assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix()) nameZone, offset := auths[i].Timestamp.Zone() assert.Equal(t, "UTC", nameZone) assert.Equal(t, 0, offset) } } func TestManyAuth(t *testing.T) { test.WipeDB(l2DB.DB()) const nAuths = 5 chainID := uint16(0) hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") // Generate authorizations genAuths := test.GenAuths(nAuths, chainID, hermezContractAddr) auths := make([]common.AccountCreationAuth, len(genAuths)) // Convert to a non-pointer slice for i := 0; i < len(genAuths); i++ { auths[i] = *genAuths[i] } // Add a duplicate one to check the not exist condition err := l2DB.AddAccountCreationAuth(genAuths[0]) require.NoError(t, err) // Add to the DB err = l2DB.AddManyAccountCreationAuth(auths) require.NoError(t, err) // Assert the result for i := 0; i < len(auths); i++ { // Fetch from DB auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr) require.NoError(t, err) // Check fetched vs generated assert.Equal(t, auths[i].EthAddr, auth.EthAddr) assert.Equal(t, auths[i].BJJ, auth.BJJ) assert.Equal(t, auths[i].Signature, auth.Signature) assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix()) nameZone, offset := auths[i].Timestamp.Zone() assert.Equal(t, "UTC", nameZone) assert.Equal(t, 0, offset) } } func TestAddGet(t *testing.T) { err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } poolL2Txs, err := generatePoolL2Txs() require.NoError(t, err) // We will work with only 3 txs require.GreaterOrEqual(t, len(poolL2Txs), 3) txs := poolL2Txs[:3] // NOTE: By changing the tx fields, the signature will no longer be // valid, but we are not checking the signautre here so it's OK. // 0. Has ToIdx >= 256 && ToEthAddr == 0 && ToBJJ == 0 require.GreaterOrEqual(t, int(txs[0].ToIdx), 256) txs[0].ToEthAddr = ethCommon.Address{} txs[0].ToBJJ = babyjub.PublicKeyComp{} // 1. Has ToIdx >= 256 && ToEthAddr != 0 && ToBJJ != 0 require.GreaterOrEqual(t, int(txs[1].ToIdx), 256) require.NotEqual(t, txs[1].ToEthAddr, ethCommon.Address{}) require.NotEqual(t, txs[1].ToBJJ, babyjub.PublicKeyComp{}) // 2. Has ToIdx == 0 && ToEthAddr != 0 && ToBJJ != 0 txs[2].ToIdx = 0 require.NotEqual(t, txs[2].ToEthAddr, ethCommon.Address{}) require.NotEqual(t, txs[2].ToBJJ, babyjub.PublicKeyComp{}) for i := 0; i < len(txs); i++ { require.NoError(t, txs[i].SetID()) require.NoError(t, l2DB.AddTxTest(&txs[i])) } // Verify that the inserts haven't altered any field (specially // ToEthAddr and ToBJJ) for i := 0; i < len(txs); i++ { dbTx, err := l2DB.GetTx(txs[i].TxID) require.NoError(t, err) // Ignore Timestamp, AbsoluteFee, AbsoluteFeeUpdate txs[i].Timestamp = dbTx.Timestamp txs[i].AbsoluteFee = dbTx.AbsoluteFee txs[i].AbsoluteFeeUpdate = dbTx.AbsoluteFeeUpdate assert.Equal(t, txs[i], *dbTx) } } func TestPurgeByExternalDelete(t *testing.T) { err := prepareHistoryDB(historyDB) if err != nil { log.Error("Error prepare historyDB", err) } txs, err := generatePoolL2Txs() require.NoError(t, err) // We will work with 8 txs require.GreaterOrEqual(t, len(txs), 8) txs = txs[:8] for i := range txs { require.NoError(t, l2DB.AddTxTest(&txs[i])) } // We will recreate this scenario: // tx index, status , external_delete // 0 , pending, false // 1 , pending, false // 2 , pending, true // will be deleted // 3 , pending, true // will be deleted // 4 , fging , false // 5 , fging , false // 6 , fging , true // 7 , fging , true require.NoError(t, l2DB.StartForging( []common.TxID{txs[4].TxID, txs[5].TxID, txs[6].TxID, txs[7].TxID}, 1)) _, err = l2DB.dbWrite.Exec( `UPDATE tx_pool SET external_delete = true WHERE tx_id IN ($1, $2, $3, $4) ;`, txs[2].TxID, txs[3].TxID, txs[6].TxID, txs[7].TxID, ) require.NoError(t, err) require.NoError(t, l2DB.PurgeByExternalDelete()) // Query txs that are have been not deleted for _, i := range []int{0, 1, 4, 5, 6, 7} { txID := txs[i].TxID _, err := l2DB.GetTx(txID) require.NoError(t, err) } // Query txs that have been deleted for _, i := range []int{2, 3} { txID := txs[i].TxID _, err := l2DB.GetTx(txID) require.Equal(t, sql.ErrNoRows, tracerr.Unwrap(err)) } }