package l2db import ( "math/big" "os" "testing" "time" "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/jmoiron/sqlx" "github.com/stretchr/testify/assert" ) var l2DB *L2DB var tokens []common.Token var tokensUSD []historydb.TokenWithUSD 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, 10, 100, 24*time.Hour) test.WipeDB(l2DB.DB()) tokens, tokensUSD = prepareHistoryDB(db) // 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(db *sqlx.DB) ([]common.Token, []historydb.TokenWithUSD) { historyDB := historydb.NewHistoryDB(db) const fromBlock int64 = 1 const toBlock int64 = 5 // Store blocks to historyDB blocks := test.GenBlocks(fromBlock, toBlock) if err := historyDB.AddBlocks(blocks); err != nil { panic(err) } // Store tokens to historyDB const nTokens = 5 tokens, ethToken := test.GenTokens(nTokens, blocks) if err := historyDB.AddTokens(tokens); err != nil { panic(err) } tokens = append([]common.Token{ethToken}, tokens...) readTokens := []historydb.TokenWithUSD{} for i, token := range tokens { readToken := historydb.TokenWithUSD{ TokenID: token.TokenID, EthBlockNum: token.EthBlockNum, EthAddr: token.EthAddr, Name: token.Name, Symbol: token.Symbol, Decimals: token.Decimals, } if i%2 != 0 { value := float64(i) * 5.4321 if err := historyDB.UpdateTokenValue(token.Symbol, value); err != nil { panic(err) } now := time.Now().UTC() readToken.USDUpdate = &now readToken.USD = &value } readTokens = append(readTokens, readToken) } return tokens, readTokens } func TestAddTxTest(t *testing.T) { // Gen poolTxs const nInserts = 20 test.WipeDB(l2DB.DB()) txs := test.GenPoolTxs(nInserts, tokens) for _, tx := range txs { // TODO: UPDATE with til err := l2DB.AddTxTest(tx) assert.NoError(t, err) fetchedTx, err := l2DB.GetTx(tx.TxID) assert.NoError(t, err) // assertReadTx(t, commonToRead(tx, tokens), fetchedTx) assertTx(t, tx, fetchedTx) nameZone, offset := fetchedTx.Timestamp.Zone() assert.Equal(t, "UTC", nameZone) assert.Equal(t, 0, offset) } } 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 := historydb.TokenWithUSD{} for _, tkn := range tokensUSD { if expected.TokenID == tkn.TokenID { token = tkn break } } // If the token has value in USD setted if token.USDUpdate != nil { assert.Equal(t, token.USDUpdate.Unix(), actual.AbsoluteFeeUpdate.Unix()) expected.AbsoluteFeeUpdate = actual.AbsoluteFeeUpdate // Set expected fee f := new(big.Float).SetInt(expected.Amount) amountF, _ := f.Float64() expected.AbsoluteFee = *token.USD * amountF * 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) { const nInserts = 20 test.WipeDB(l2DB.DB()) // TODO: UPDATE with til txs := test.GenPoolTxs(nInserts, tokens) var pendingTxs []*common.PoolL2Tx for _, tx := range txs { err := l2DB.AddTxTest(tx) assert.NoError(t, err) if tx.State == common.PoolL2TxStatePending { pendingTxs = append(pendingTxs, tx) } } fetchedTxs, err := l2DB.GetPendingTxs() assert.NoError(t, err) assert.Equal(t, len(pendingTxs), len(fetchedTxs)) for i := range fetchedTxs { assertTx(t, pendingTxs[i], &fetchedTxs[i]) } } /* TODO: update with til func TestStartForging(t *testing.T) { // Generate txs const nInserts = 60 const fakeBatchNum common.BatchNum = 33 test.WipeDB(l2DB.DB()) txs := test.GenPoolTxs(nInserts, tokens) var startForgingTxIDs []common.TxID randomizer := 0 // Add txs to DB for _, tx := range txs { err := l2DB.AddTxTest(tx) assert.NoError(t, err) if tx.State == common.PoolL2TxStatePending && randomizer%2 == 0 { randomizer++ startForgingTxIDs = append(startForgingTxIDs, tx.TxID) } } // Start forging txs err := l2DB.StartForging(startForgingTxIDs, fakeBatchNum) assert.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range startForgingTxIDs { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State) assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } */ /* TODO: update with til func TestDoneForging(t *testing.T) { // Generate txs const nInserts = 60 const fakeBatchNum common.BatchNum = 33 test.WipeDB(l2DB.DB()) txs := test.GenPoolTxs(nInserts, tokens) var doneForgingTxIDs []common.TxID randomizer := 0 // Add txs to DB for _, tx := range txs { err := l2DB.AddTxTest(tx) assert.NoError(t, err) if tx.State == common.PoolL2TxStateForging && randomizer%2 == 0 { randomizer++ doneForgingTxIDs = append(doneForgingTxIDs, tx.TxID) } } // Start forging txs err := l2DB.DoneForging(doneForgingTxIDs, fakeBatchNum) assert.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range doneForgingTxIDs { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State) assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } */ /* TODO: update with til func TestInvalidate(t *testing.T) { // Generate txs const nInserts = 60 const fakeBatchNum common.BatchNum = 33 test.WipeDB(l2DB.DB()) txs := test.GenPoolTxs(nInserts, tokens) var invalidTxIDs []common.TxID randomizer := 0 // Add txs to DB for _, tx := range txs { err := l2DB.AddTxTest(tx) assert.NoError(t, err) if tx.State != common.PoolL2TxStateInvalid && randomizer%2 == 0 { randomizer++ invalidTxIDs = append(invalidTxIDs, tx.TxID) } } // Start forging txs err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum) assert.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range invalidTxIDs { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } */ /* TODO: update with til func TestCheckNonces(t *testing.T) { // Generate txs const nInserts = 60 const fakeBatchNum common.BatchNum = 33 test.WipeDB(l2DB.DB()) txs := test.GenPoolTxs(nInserts, tokens) var invalidTxIDs []common.TxID // Generate accounts const nAccoutns = 2 const currentNonce = 2 accs := []common.Account{} for i := 0; i < nAccoutns; i++ { accs = append(accs, common.Account{ Idx: common.Idx(i), Nonce: currentNonce, }) } // Add txs to DB for i := 0; i < len(txs); i++ { if txs[i].State != common.PoolL2TxStateInvalid { if i%2 == 0 { // Ensure transaction will be marked as invalid due to old nonce txs[i].Nonce = accs[i%len(accs)].Nonce txs[i].FromIdx = accs[i%len(accs)].Idx invalidTxIDs = append(invalidTxIDs, txs[i].TxID) } else { // Ensure transaction will NOT be marked as invalid due to old nonce txs[i].Nonce = currentNonce + 1 } } err := l2DB.AddTxTest(txs[i]) assert.NoError(t, err) } // Start forging txs err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum) assert.NoError(t, err) // Fetch txs and check that they've been updated correctly for _, id := range invalidTxIDs { fetchedTx, err := l2DB.GetTx(id) assert.NoError(t, err) assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State) assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum) } } */ func TestReorg(t *testing.T) { // Generate txs const nInserts = 20 const lastValidBatch common.BatchNum = 20 const reorgBatch common.BatchNum = lastValidBatch + 1 test.WipeDB(l2DB.DB()) // TODO: update with til txs := test.GenPoolTxs(nInserts, tokens) // Add txs to the DB reorgedTxIDs := []common.TxID{} nonReorgedTxIDs := []common.TxID{} for i := 0; i < len(txs); i++ { err := l2DB.AddTxTest(txs[i]) assert.NoError(t, err) var batchNum common.BatchNum if txs[i].State == common.PoolL2TxStateForged || txs[i].State == common.PoolL2TxStateInvalid { reorgedTxIDs = append(reorgedTxIDs, txs[i].TxID) batchNum = reorgBatch } else { nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID) batchNum = lastValidBatch } _, err = l2DB.db.Exec( "UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;", batchNum, txs[i].TxID, ) assert.NoError(t, err) } err := l2DB.Reorg(lastValidBatch) assert.NoError(t, err) for _, id := range reorgedTxIDs { tx, err := l2DB.GetTxAPI(id) assert.NoError(t, err) assert.Nil(t, tx.BatchNum) assert.Equal(t, common.PoolL2TxStatePending, tx.State) } for _, id := range nonReorgedTxIDs { fetchedTx, err := l2DB.GetTxAPI(id) assert.NoError(t, err) assert.Equal(t, lastValidBatch, *fetchedTx.BatchNum) } } func TestPurge(t *testing.T) { /* TODO: update with til WARNING: this should be fixed once transaktio is ready // Generate txs nInserts := l2DB.maxTxs + 20 test.WipeDB(l2DB.DB()) txs := test.GenPoolTxs(int(nInserts), tokens) deletedIDs := []common.TxID{} keepedIDs := []common.TxID{} const toDeleteBatchNum common.BatchNum = 30 safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1 // Add txs to the DB for i := 0; i < int(l2DB.maxTxs); i++ { var batchNum common.BatchNum if i%2 == 0 { // keep tx batchNum = safeBatchNum keepedIDs = append(keepedIDs, txs[i].TxID) } else { // delete after safety period batchNum = toDeleteBatchNum if i%3 == 0 { txs[i].State = common.PoolL2TxStateForged } else { txs[i].State = common.PoolL2TxStateInvalid } deletedIDs = append(deletedIDs, txs[i].TxID) } err := l2DB.AddTxTest(txs[i]) assert.NoError(t, err) // Set batchNum _, err = l2DB.db.Exec( "UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;", batchNum, txs[i].TxID, ) assert.NoError(t, err) } for i := int(l2DB.maxTxs); i < len(txs); i++ { // Delete after TTL deletedIDs = append(deletedIDs, txs[i].TxID) err := l2DB.AddTxTest(txs[i]) assert.NoError(t, err) // Set timestamp deleteTimestamp := time.Unix(time.Now().UTC().Unix()-int64(l2DB.ttl.Seconds()+float64(4*time.Second)), 0) _, err = l2DB.db.Exec( "UPDATE tx_pool SET timestamp = $1 WHERE tx_id = $2;", deleteTimestamp, txs[i].TxID, ) assert.NoError(t, err) } // Purge txs err := l2DB.Purge(safeBatchNum) assert.NoError(t, err) // Check results for _, id := range deletedIDs { tx, err := l2DB.GetTx(id) if err == nil { log.Debug(tx) } assert.Error(t, err) } for _, id := range keepedIDs { _, err := l2DB.GetTx(id) assert.NoError(t, err) } */ } func TestAuth(t *testing.T) { test.WipeDB(l2DB.DB()) const nAuths = 5 // Generate authorizations auths := test.GenAuths(nAuths) for i := 0; i < len(auths); i++ { // Add to the DB err := l2DB.AddAccountCreationAuth(auths[i]) assert.NoError(t, err) // Fetch from DB auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr) assert.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) } }