diff --git a/api/account_test.go b/api/account_test.go index e988144..de25355 100644 --- a/api/account_test.go +++ b/api/account_test.go @@ -36,6 +36,8 @@ func (t testAccountsResponse) GetPending() (pendingItems, lastItemID uint64) { func (t *testAccountsResponse) Len() int { return len(t.Accounts) } +func (t testAccountsResponse) New() Pendinger { return &testAccountsResponse{} } + func genTestAccounts(accounts []common.Account, tokens []historydb.TokenWithUSD) []testAccount { tAccounts := []testAccount{} for x, account := range accounts { @@ -98,7 +100,7 @@ func TestGetAccounts(t *testing.T) { assert.LessOrEqual(t, len(fetchedAccounts), len(tc.accounts)) fetchedAccounts = []testAccount{} // Token Ids + bjj - path = fmt.Sprintf("%s?tokenIds=%s&BJJ=%s&limit=%d&fromItem=", endpoint, stringIds, tc.accounts[0].PublicKey, limit) + path = fmt.Sprintf("%s?tokenIds=%s&BJJ=%s&limit=%d&fromItem=", endpoint, stringIds, tc.accounts[10].PublicKey, limit) err = doGoodReqPaginated(path, historydb.OrderAsc, &testAccountsResponse{}, appendIter) assert.NoError(t, err) assert.Greater(t, len(fetchedAccounts), 0) diff --git a/api/api_test.go b/api/api_test.go index ad47d37..224b0f3 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -10,7 +10,6 @@ import ( "math/big" "net/http" "os" - "sort" "strconv" "testing" "time" @@ -25,7 +24,7 @@ import ( "github.com/hermeznetwork/hermez-node/db/statedb" "github.com/hermeznetwork/hermez-node/log" "github.com/hermeznetwork/hermez-node/test" - "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/hermeznetwork/hermez-node/test/til" ) // Pendinger is an interface that allows getting last returned item ID and PendingItems to be used for building fromItem @@ -33,11 +32,132 @@ import ( type Pendinger interface { GetPending() (pendingItems, lastItemID uint64) Len() int + New() Pendinger } const apiPort = ":4010" const apiURL = "http://localhost" + apiPort + "/" +var SetBlockchain = ` + Type: Blockchain + + AddToken(1) + AddToken(2) + AddToken(3) + AddToken(4) + AddToken(5) + AddToken(6) + AddToken(7) + AddToken(8) + > block + + // Coordinator accounts, Idxs: 256, 257 + CreateAccountCoordinator(0) Coord + CreateAccountCoordinator(1) Coord + + // close Block:0, Batch:0 + > batch + + CreateAccountDeposit(0) A: 111111111 + CreateAccountDeposit(1) C: 222222222 + CreateAccountCoordinator(0) C + + // close Block:0, Batch:1 + > batchL1 + // Expected balances: + // Coord(0): 0, Coord(1): 0 + // C(0): 0 + + CreateAccountDeposit(1) A: 333333333 + + // close Block:0, Batch:2 + > batchL1 + + // close Block:0, Batch:3 + > batchL1 + + CreateAccountDepositTransfer(0) B-A: 444444444, 1234444444 // to_eth_addr is NULL + + // close Block:0, Batch:4 + > batchL1 + CreateAccountDeposit(0) D: 555555555 + + // close Block:0, Batch:5 + > batchL1 + + CreateAccountCoordinator(1) B + + Transfer(1) A-B: 111111 (2) // to_eth_addr is NULL + Transfer(0) B-C: 222222 (3) + + // close Block:0, Batch:6 + > batchL1 // forge L1User{1}, forge L1Coord{2}, forge L2{2} + + Deposit(0) C: 666666666 + DepositTransfer(0) C-D: 777777777, 123777777 // to_eth_addr is NULL + + Transfer(0) A-B: 333333 (111) + Transfer(0) C-A: 444444 (222) + Transfer(1) B-C: 555555 (123) + Exit(0) A: 666666 (44) + + ForceTransfer(0) D-B: 777777 // to_eth_addr is NULL + ForceExit(0) B: 888888 + + // close Block:0, Batch:7 + > batchL1 + > block + + Transfer(0) D-A: 999999 (77) + Transfer(0) B-D: 123123 (55) + + // close Block:1, Batch:0 + > batchL1 + + CreateAccountCoordinator(0) F + + CreateAccountCoordinator(0) G + CreateAccountCoordinator(0) H + CreateAccountCoordinator(0) I + CreateAccountCoordinator(0) J + CreateAccountCoordinator(0) K + CreateAccountCoordinator(0) L + CreateAccountCoordinator(0) M + CreateAccountCoordinator(0) N + CreateAccountCoordinator(0) O + CreateAccountCoordinator(0) P + + CreateAccountCoordinator(5) G + CreateAccountCoordinator(5) H + CreateAccountCoordinator(5) I + CreateAccountCoordinator(5) J + CreateAccountCoordinator(5) K + CreateAccountCoordinator(5) L + CreateAccountCoordinator(5) M + CreateAccountCoordinator(5) N + CreateAccountCoordinator(5) O + CreateAccountCoordinator(5) P + + CreateAccountCoordinator(2) G + CreateAccountCoordinator(2) H + CreateAccountCoordinator(2) I + CreateAccountCoordinator(2) J + CreateAccountCoordinator(2) K + CreateAccountCoordinator(2) L + CreateAccountCoordinator(2) M + CreateAccountCoordinator(2) N + CreateAccountCoordinator(2) O + CreateAccountCoordinator(2) P + + + > batch + > block + > batch + > block + > batch + > block +` + type testCommon struct { blocks []common.Block tokens []historydb.TokenWithUSD @@ -45,13 +165,8 @@ type testCommon struct { fullBatches []testFullBatch coordinators []historydb.CoordinatorAPI accounts []testAccount - usrAddr string - usrBjj string - accs []common.Account - usrTxs []testTx - allTxs []testTx + txs []testTx exits []testExit - usrExits []testExit poolTxsToSend []testPoolTxSend poolTxsToReceive []testPoolTxReceive auths []testAuth @@ -71,16 +186,6 @@ var api *API // emulating the task of the synchronizer in order to have data to be returned // by the API endpoints that will be tested func TestMain(m *testing.M) { - /* - til update considerations: - 1. Two instructions sets should be enough (one for L2 another for historydb) - 2. FillBlocksExtra function must be used, there is a coment on top of the function that explains which data is setted - 3. Some data will not be generated by til nor FillBlocksExtra, test.GenXXX will still be required to cover this cases - 4. Most of the historydb inserts should be replaced with nBlocks calls to AddBlockSCData - 5. When defining til instructions, there is no need to have 100s of entries for each table, but it's interesting to - cover all different cases (for instance all tx types) - */ - // Initializations // Swagger router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml") @@ -139,168 +244,117 @@ func TestMain(m *testing.M) { } }() - // Fill HistoryDB and StateDB with fake data - // Gen blocks and add them to DB - const nBlocks = 5 - // TODO: UPDATE with til - blocks := test.GenBlocks(1, nBlocks+1) - err = api.h.AddBlocks(blocks) + // Reset DB + test.WipeDB(api.h.DB()) + + // Genratre blockchain data with til + tcc := til.NewContext(common.RollupConstMaxL1UserTx) + tilCfgExtra := til.ConfigExtra{ + BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"), + CoordUser: "Coord", + } + blocksData, err := tcc.GenerateBlocks(SetBlockchain) + if err != nil { + panic(err) + } + err = tcc.FillBlocksExtra(blocksData, &tilCfgExtra) + if err != nil { + panic(err) + } + AddAditionalInformation(blocksData) + // Generate L2 Txs with til + commonPoolTxs, err := tcc.GeneratePoolL2Txs(til.SetPoolL2MinimumFlow0) if err != nil { panic(err) } - lastBlockNum := blocks[nBlocks-1].EthBlockNum - // Gen tokens and add them to DB - const nTokens = 10 - // TODO: UPDATE with til - tokens, ethToken := test.GenTokens(nTokens, blocks) - err = api.h.AddTokens(tokens) + // Extract til generated data, and add it to HistoryDB + var commonBlocks []common.Block + var commonBatches []common.Batch + var commonAccounts []common.Account + var commonExitTree []common.ExitInfo + var commonL1Txs []common.L1Tx + var commonL2Txs []common.L2Tx + // Add ETH token at the beginning of the array + testTokens := []historydb.TokenWithUSD{} + ethUSD := float64(500) + ethNow := time.Now() + testTokens = append(testTokens, historydb.TokenWithUSD{ + TokenID: test.EthToken.TokenID, + EthBlockNum: test.EthToken.EthBlockNum, + EthAddr: test.EthToken.EthAddr, + Name: test.EthToken.Name, + Symbol: test.EthToken.Symbol, + Decimals: test.EthToken.Decimals, + USD: ðUSD, + USDUpdate: ðNow, + }) + err = api.h.UpdateTokenValue(test.EthToken.Symbol, ethUSD) if err != nil { panic(err) } - tokens = append([]common.Token{ethToken}, tokens...) - // Set token value - tokensUSD := []historydb.TokenWithUSD{} - for i, tkn := range tokens { - token := historydb.TokenWithUSD{ - TokenID: tkn.TokenID, - EthBlockNum: tkn.EthBlockNum, - EthAddr: tkn.EthAddr, - Name: tkn.Name, - Symbol: tkn.Symbol, - Decimals: tkn.Decimals, + for _, block := range blocksData { + // Insert block into HistoryDB + if err := api.h.AddBlockSCData(&block); err != nil { //nolint:gosec block is used as read only in the function + panic(err) } - // Set value of 50% of the tokens - if i%2 != 0 { - value := float64(i) * 1.234567 + // Extract data + commonBlocks = append(commonBlocks, block.Block) + for i, tkn := range block.Rollup.AddedTokens { + token := historydb.TokenWithUSD{ + TokenID: tkn.TokenID, + EthBlockNum: tkn.EthBlockNum, + EthAddr: tkn.EthAddr, + Name: tkn.Name, + Symbol: tkn.Symbol, + Decimals: tkn.Decimals, + } + value := float64(i + 423) now := time.Now().UTC() token.USD = &value token.USDUpdate = &now + // Set value in DB err = api.h.UpdateTokenValue(token.Symbol, value) if err != nil { panic(err) } + testTokens = append(testTokens, token) } - tokensUSD = append(tokensUSD, token) - } - // Gen batches and add them to DB - const nBatches = 10 - // TODO: UPDATE with til - batches := test.GenBatches(nBatches, blocks) - err = api.h.AddBatches(batches) - if err != nil { - panic(err) - } - // Gen accounts and add them to HistoryDB and StateDB - const totalAccounts = 40 - const userAccounts = 4 - usrAddr := ethCommon.BigToAddress(big.NewInt(4896847)) - privK := babyjub.NewRandPrivKey() - usrBjj := privK.Public() - // TODO: UPDATE with til - accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches) - err = api.h.AddAccounts(accs) - if err != nil { - panic(err) - } - for i := 0; i < len(accs); i++ { - if _, err := api.s.CreateAccount(accs[i].Idx, &accs[i]); err != nil { - panic(err) - } - } - // helper to vinculate user related resources - usrIdxs := []string{} - for _, acc := range accs { - if acc.EthAddr == usrAddr || acc.PublicKey == usrBjj { - for _, token := range tokens { - if token.TokenID == acc.TokenID { - usrIdxs = append(usrIdxs, idxToHez(acc.Idx, token.Symbol)) - } - } + // Set USD value for tokens in DB + commonL1Txs = append(commonL1Txs, block.Rollup.L1UserTxs...) + for _, batch := range block.Rollup.Batches { + commonL2Txs = append(commonL2Txs, batch.L2Txs...) + commonAccounts = append(commonAccounts, batch.CreatedAccounts...) + commonBatches = append(commonBatches, batch.Batch) + commonExitTree = append(commonExitTree, batch.ExitTree...) + commonL1Txs = append(commonL1Txs, batch.L1CoordinatorTxs...) } } - // Gen exits and add them to DB - const totalExits = 40 - // TODO: UPDATE with til - exits := test.GenExitTree(totalExits, batches, accs, blocks) - err = api.h.AddExitTree(exits) - if err != nil { - panic(err) - } - // L1 and L2 txs need to be sorted in a combined way - // Gen L1Txs - const totalL1Txs = 40 - const userL1Txs = 4 - // TODO: UPDATE with til - usrL1Txs, othrL1Txs := test.GenL1Txs(256, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches) - // Gen L2Txs - const totalL2Txs = 20 - const userL2Txs = 4 - // TODO: UPDATE with til - usrL2Txs, othrL2Txs := test.GenL2Txs(256+totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches) - // Sort txs - sortedTxs := []txSortFielder{} - for i := 0; i < len(usrL1Txs); i++ { - wL1 := wrappedL1(usrL1Txs[i]) - sortedTxs = append(sortedTxs, &wL1) - } - for i := 0; i < len(othrL1Txs); i++ { - wL1 := wrappedL1(othrL1Txs[i]) - sortedTxs = append(sortedTxs, &wL1) - } - for i := 0; i < len(usrL2Txs); i++ { - wL2 := wrappedL2(usrL2Txs[i]) - sortedTxs = append(sortedTxs, &wL2) - } - for i := 0; i < len(othrL2Txs); i++ { - wL2 := wrappedL2(othrL2Txs[i]) - sortedTxs = append(sortedTxs, &wL2) - } - sort.Sort(txsSort(sortedTxs)) - // Store txs to DB - for _, genericTx := range sortedTxs { - l1 := genericTx.L1() - l2 := genericTx.L2() - if l1 != nil { - err = api.h.AddL1Txs([]common.L1Tx{*l1}) - if err != nil { - panic(err) - } - } else if l2 != nil { - err = api.h.AddL2Txs([]common.L2Tx{*l2}) - if err != nil { - panic(err) - } - } else { - panic("should be l1 or l2") + // lastBlockNum2 := blocksData[len(blocksData)-1].Block.EthBlockNum + + // Add accounts to StateDB + for i := 0; i < len(commonAccounts); i++ { + if _, err := api.s.CreateAccount(commonAccounts[i].Idx, &commonAccounts[i]); err != nil { + panic(err) } } - // Coordinators + // Generate Coordinators and add them to HistoryDB const nCoords = 10 - coords := test.GenCoordinators(nCoords, blocks) - err = api.h.AddCoordinators(coords) - if err != nil { - panic(err) - } - fromItem := uint(0) - limit := uint(99999) - coordinators, _, err := api.h.GetCoordinatorsAPI(&fromItem, &limit, historydb.OrderAsc) - if err != nil { + commonCoords := test.GenCoordinators(nCoords, commonBlocks) + if err := api.h.AddCoordinators(commonCoords); err != nil { panic(err) } - // Bids + // Generate Bids and add them to HistoryDB const nBids = 20 - bids := test.GenBids(nBids, blocks, coords) - err = api.h.AddBids(bids) - if err != nil { + commonBids := test.GenBids(nBids, commonBlocks, commonCoords) + if err = api.h.AddBids(commonBids); err != nil { panic(err) } - testBids := genTestBids(blocks, coordinators, bids) - // Vars + // Generate SC vars and add them to HistoryDB (if needed) var defaultSlotSetBid [6]*big.Int = [6]*big.Int{big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10)} auctionVars := common.AuctionVariables{ EthBlockNum: int64(2), @@ -329,36 +383,36 @@ func TestMain(m *testing.M) { panic(err) } - const nSlots = 20 - - // Set testCommon - usrTxs, allTxs := genTestTxs(sortedTxs, usrIdxs, accs, tokensUSD, blocks) - poolTxsToSend, poolTxsToReceive := genTestPoolTx(accs, []babyjub.PrivateKey{privK}, tokensUSD) // NOTE: pool txs are not inserted to the DB here. In the test they will be posted and getted. - testBatches, fullBatches := genTestBatches(blocks, batches, allTxs) - usrExits, allExits := genTestExits(exits, tokensUSD, accs, usrIdxs) + // Generate test data, as expected to be received/sended from/to the API + testCoords := genTestCoordinators(commonCoords) + testBids := genTestBids(commonBlocks, testCoords, commonBids) + testExits := genTestExits(commonExitTree, testTokens, commonAccounts) + testTxs := genTestTxs(commonL1Txs, commonL2Txs, commonAccounts, testTokens, commonBlocks) + testBatches, testFullBatches := genTestBatches(commonBlocks, commonBatches, testTxs) + poolTxsToSend, poolTxsToReceive := genTestPoolTxs(commonPoolTxs, testTokens, commonAccounts) tc = testCommon{ - blocks: blocks, - tokens: tokensUSD, + blocks: commonBlocks, + tokens: testTokens, batches: testBatches, - fullBatches: fullBatches, - coordinators: coordinators, - accounts: genTestAccounts(accs, tokensUSD), - usrAddr: ethAddrToHez(usrAddr), - usrBjj: bjjToString(usrBjj), - accs: accs, - usrTxs: usrTxs, - allTxs: allTxs, - exits: allExits, - usrExits: usrExits, + fullBatches: testFullBatches, + coordinators: testCoords, + accounts: genTestAccounts(commonAccounts, testTokens), + txs: testTxs, + exits: testExits, poolTxsToSend: poolTxsToSend, poolTxsToReceive: poolTxsToReceive, auths: genTestAuths(test.GenAuths(5)), router: router, bids: testBids, - slots: api.genTestSlots(nSlots, lastBlockNum, testBids, auctionVars), - auctionVars: auctionVars, - rollupVars: rollupVars, - wdelayerVars: wdelayerVars, + slots: api.genTestSlots( + 20, + commonBlocks[len(commonBlocks)-1].EthBlockNum, + testBids, + auctionVars, + ), + auctionVars: auctionVars, + rollupVars: rollupVars, + wdelayerVars: wdelayerVars, } // Fake server @@ -406,7 +460,11 @@ func doGoodReqPaginated( iterPath += strconv.Itoa(int(next)) } // Call API to get this iteration items - if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil { + iterStruct = iterStruct.New() + if err := doGoodReq( + "GET", iterPath+"&order="+order, nil, + iterStruct, + ); err != nil { return err } appendIter(iterStruct) @@ -482,8 +540,10 @@ func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) // Unmarshal body into return struct if err := json.Unmarshal(body, returnStruct); err != nil { log.Error("invalid json: " + string(body)) + log.Error(err) return err } + // log.Info(string(body)) // Validate response against swagger spec responseValidationInput := &swagger.ResponseValidationInput{ RequestValidationInput: requestValidationInput, diff --git a/api/aux_test.go b/api/aux_test.go new file mode 100644 index 0000000..a9139cd --- /dev/null +++ b/api/aux_test.go @@ -0,0 +1,45 @@ +package api + +import ( + "math/big" + "strconv" + "time" + + ethCommon "github.com/ethereum/go-ethereum/common" + "github.com/hermeznetwork/hermez-node/common" + "github.com/iden3/go-merkletree" +) + +func AddAditionalInformation(blocks []common.BlockData) { + for i := range blocks { + blocks[i].Block.Timestamp = time.Now().Add(time.Second * 13).UTC() + blocks[i].Block.Hash = ethCommon.BigToHash(big.NewInt(blocks[i].Block.EthBlockNum)) + for j := range blocks[i].Rollup.AddedTokens { + blocks[i].Rollup.AddedTokens[j].Name = "NAME" + strconv.Itoa(int(blocks[i].Rollup.AddedTokens[j].TokenID)) + blocks[i].Rollup.AddedTokens[j].Symbol = strconv.Itoa(int(blocks[i].Rollup.AddedTokens[j].TokenID)) + blocks[i].Rollup.AddedTokens[j].Decimals = uint64(blocks[i].Rollup.AddedTokens[j].TokenID + 1) + } + for x := range blocks[i].Rollup.Batches { + for q := range blocks[i].Rollup.Batches[x].CreatedAccounts { + blocks[i].Rollup.Batches[x].CreatedAccounts[q].Balance = + big.NewInt(int64(blocks[i].Rollup.Batches[x].CreatedAccounts[q].Idx * 10000000)) + } + for y := range blocks[i].Rollup.Batches[x].ExitTree { + blocks[i].Rollup.Batches[x].ExitTree[y].MerkleProof = + &merkletree.CircomVerifierProof{ + Root: &merkletree.Hash{byte(y), byte(y + 1)}, + Siblings: []*merkletree.Hash{ + merkletree.NewHashFromBigInt(big.NewInt(int64(y) * 10)), + merkletree.NewHashFromBigInt(big.NewInt(int64(y)*100 + 1)), + merkletree.NewHashFromBigInt(big.NewInt(int64(y)*1000 + 2))}, + OldKey: &merkletree.Hash{byte(y * 1), byte(y*1 + 1)}, + OldValue: &merkletree.Hash{byte(y * 2), byte(y*2 + 1)}, + IsOld0: y%2 == 0, + Key: &merkletree.Hash{byte(y * 3), byte(y*3 + 1)}, + Value: &merkletree.Hash{byte(y * 4), byte(y*4 + 1)}, + Fnc: y % 2, + } + } + } + } +} diff --git a/api/batch.go b/api/batch.go index c41f4d5..27e876e 100644 --- a/api/batch.go +++ b/api/batch.go @@ -1,6 +1,7 @@ package api import ( + "database/sql" "errors" "net/http" @@ -109,7 +110,7 @@ func (a *API) getFullBatch(c *gin.Context) { txs, _, err := a.h.GetHistoryTxs( nil, nil, nil, nil, batchNum, nil, nil, &maxTxsPerBatch, historydb.OrderAsc, ) - if err != nil { + if err != nil && err != sql.ErrNoRows { retSQLErr(err, c) return } diff --git a/api/batch_test.go b/api/batch_test.go index 135a4f9..8a615f9 100644 --- a/api/batch_test.go +++ b/api/batch_test.go @@ -43,6 +43,8 @@ func (t testBatchesResponse) Len() int { return len(t.Batches) } +func (t testBatchesResponse) New() Pendinger { return &testBatchesResponse{} } + type testFullBatch struct { Batch testBatch `json:"batch"` Txs []testTx `json:"transactions"` @@ -54,11 +56,11 @@ func genTestBatches( txs []testTx, ) ([]testBatch, []testFullBatch) { tBatches := []testBatch{} - for _, cBatch := range cBatches { + for i := 0; i < len(cBatches); i++ { block := common.Block{} found := false for _, b := range blocks { - if b.EthBlockNum == cBatch.EthBlockNum { + if b.EthBlockNum == cBatches[i].EthBlockNum { block = b found = true break @@ -68,22 +70,22 @@ func genTestBatches( panic("block not found") } collectedFees := make(map[common.TokenID]string) - for k, v := range cBatch.CollectedFees { + for k, v := range cBatches[i].CollectedFees { collectedFees[k] = v.String() } tBatch := testBatch{ - BatchNum: cBatch.BatchNum, - EthBlockNum: cBatch.EthBlockNum, + BatchNum: cBatches[i].BatchNum, + EthBlockNum: cBatches[i].EthBlockNum, EthBlockHash: block.Hash, Timestamp: block.Timestamp, - ForgerAddr: cBatch.ForgerAddr, + ForgerAddr: cBatches[i].ForgerAddr, CollectedFees: collectedFees, - TotalFeesUSD: cBatch.TotalFeesUSD, - StateRoot: cBatch.StateRoot.String(), - NumAccounts: cBatch.NumAccounts, - ExitRoot: cBatch.ExitRoot.String(), - ForgeL1TxsNum: cBatch.ForgeL1TxsNum, - SlotNum: cBatch.SlotNum, + TotalFeesUSD: cBatches[i].TotalFeesUSD, + StateRoot: cBatches[i].StateRoot.String(), + NumAccounts: cBatches[i].NumAccounts, + ExitRoot: cBatches[i].ExitRoot.String(), + ForgeL1TxsNum: cBatches[i].ForgeL1TxsNum, + SlotNum: cBatches[i].SlotNum, } tBatches = append(tBatches, tBatch) } diff --git a/api/bids_test.go b/api/bids_test.go index 7813988..6b94227 100644 --- a/api/bids_test.go +++ b/api/bids_test.go @@ -38,6 +38,8 @@ func (t testBidsResponse) Len() int { return len(t.Bids) } +func (t testBidsResponse) New() Pendinger { return &testBidsResponse{} } + func genTestBids(blocks []common.Block, coordinators []historydb.CoordinatorAPI, bids []common.Bid) []testBid { tBids := []testBid{} for _, bid := range bids { @@ -112,8 +114,8 @@ func TestGetBids(t *testing.T) { // Mixed filters fetchedBids = []testBid{} - bidderAddress = tc.bids[9].Bidder - slotNum = tc.bids[4].SlotNum + bidderAddress = tc.bids[1].Bidder + slotNum = tc.bids[1].SlotNum path = fmt.Sprintf("%s?bidderAddr=%s&slotNum=%d&limit=%d&fromItem=", endpoint, bidderAddress.String(), slotNum, limit) err = doGoodReqPaginated(path, historydb.OrderAsc, &testBidsResponse{}, appendIter) assert.NoError(t, err) diff --git a/api/coordinator_test.go b/api/coordinator_test.go index 822687b..e3258ee 100644 --- a/api/coordinator_test.go +++ b/api/coordinator_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" @@ -22,10 +23,24 @@ func (t testCoordinatorsResponse) GetPending() (pendingItems, lastItemID uint64) func (t *testCoordinatorsResponse) Len() int { return len(t.Coordinators) } +func (t testCoordinatorsResponse) New() Pendinger { return &testCoordinatorsResponse{} } + +func genTestCoordinators(coordinators []common.Coordinator) []historydb.CoordinatorAPI { + testCoords := []historydb.CoordinatorAPI{} + for i := 0; i < len(coordinators); i++ { + testCoords = append(testCoords, historydb.CoordinatorAPI{ + Bidder: coordinators[i].Bidder, + Forger: coordinators[i].Forger, + EthBlockNum: coordinators[i].EthBlockNum, + URL: coordinators[i].URL, + }) + } + return testCoords +} + func TestGetCoordinators(t *testing.T) { endpoint := apiURL + "coordinators" fetchedCoordinators := []historydb.CoordinatorAPI{} - appendIter := func(intr interface{}) { for i := 0; i < len(intr.(*testCoordinatorsResponse).Coordinators); i++ { tmp, err := copystructure.Copy(intr.(*testCoordinatorsResponse).Coordinators[i]) @@ -36,45 +51,30 @@ func TestGetCoordinators(t *testing.T) { } } + // All limit := 5 - path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) err := doGoodReqPaginated(path, historydb.OrderAsc, &testCoordinatorsResponse{}, appendIter) assert.NoError(t, err) - for i := 0; i < len(fetchedCoordinators); i++ { - assert.Equal(t, tc.coordinators[i].ItemID, fetchedCoordinators[i].ItemID) - assert.Equal(t, tc.coordinators[i].Bidder, fetchedCoordinators[i].Bidder) - assert.Equal(t, tc.coordinators[i].Forger, fetchedCoordinators[i].Forger) - assert.Equal(t, tc.coordinators[i].EthBlockNum, fetchedCoordinators[i].EthBlockNum) - assert.Equal(t, tc.coordinators[i].URL, fetchedCoordinators[i].URL) - } + assertCoordinators(t, tc.coordinators, fetchedCoordinators) - // Reverse Order - reversedCoordinators := []historydb.CoordinatorAPI{} - appendIter = func(intr interface{}) { - for i := 0; i < len(intr.(*testCoordinatorsResponse).Coordinators); i++ { - tmp, err := copystructure.Copy(intr.(*testCoordinatorsResponse).Coordinators[i]) - if err != nil { - panic(err) - } - reversedCoordinators = append(reversedCoordinators, tmp.(historydb.CoordinatorAPI)) - } - } + // All in reverse order + fetchedCoordinators = []historydb.CoordinatorAPI{} err = doGoodReqPaginated(path, historydb.OrderDesc, &testCoordinatorsResponse{}, appendIter) assert.NoError(t, err) - for i := 0; i < len(fetchedCoordinators); i++ { - assert.Equal(t, reversedCoordinators[i].ItemID, fetchedCoordinators[len(fetchedCoordinators)-1-i].ItemID) - assert.Equal(t, reversedCoordinators[i].Bidder, fetchedCoordinators[len(fetchedCoordinators)-1-i].Bidder) - assert.Equal(t, reversedCoordinators[i].Forger, fetchedCoordinators[len(fetchedCoordinators)-1-i].Forger) - assert.Equal(t, reversedCoordinators[i].EthBlockNum, fetchedCoordinators[len(fetchedCoordinators)-1-i].EthBlockNum) - assert.Equal(t, reversedCoordinators[i].URL, fetchedCoordinators[len(fetchedCoordinators)-1-i].URL) + reversedCoordinators := []historydb.CoordinatorAPI{} + for i := 0; i < len(tc.coordinators); i++ { + reversedCoordinators = append(reversedCoordinators, tc.coordinators[len(tc.coordinators)-1-i]) } + assertCoordinators(t, reversedCoordinators, fetchedCoordinators) // Test GetCoordinator - path = fmt.Sprintf("%s/%s", endpoint, fetchedCoordinators[2].Forger.String()) - coordinator := historydb.CoordinatorAPI{} - assert.NoError(t, doGoodReq("GET", path, nil, &coordinator)) - assert.Equal(t, fetchedCoordinators[2], coordinator) + for _, coord := range tc.coordinators { + path = fmt.Sprintf("%s/%s", endpoint, coord.Forger.String()) + fetchedCoordinator := historydb.CoordinatorAPI{} + assert.NoError(t, doGoodReq("GET", path, nil, &fetchedCoordinator)) + assertCoordinator(t, coord, fetchedCoordinator) + } // 400 path = fmt.Sprintf("%s/0x001", endpoint) @@ -85,3 +85,15 @@ func TestGetCoordinators(t *testing.T) { err = doBadReq("GET", path, nil, 404) assert.NoError(t, err) } + +func assertCoordinator(t *testing.T, expected, actual historydb.CoordinatorAPI) { + actual.ItemID = 0 + assert.Equal(t, expected, actual) +} + +func assertCoordinators(t *testing.T, expected, actual []historydb.CoordinatorAPI) { + assert.Equal(t, len(expected), len(actual)) + for i := 0; i < len(expected); i++ { + assertCoordinator(t, expected[i], actual[i]) + } +} diff --git a/api/exits_test.go b/api/exits_test.go index e6ef8bb..c36730b 100644 --- a/api/exits_test.go +++ b/api/exits_test.go @@ -45,6 +45,8 @@ func (t testExitsResponse) GetPending() (pendingItems, lastItemID uint64) { return pendingItems, lastItemID } +func (t testExitsResponse) New() Pendinger { return &testExitsResponse{} } + func (t *testExitsResponse) Len() int { return len(t.Exits) } @@ -53,9 +55,8 @@ func genTestExits( commonExits []common.ExitInfo, tokens []historydb.TokenWithUSD, accs []common.Account, - usrIdxs []string, -) (usrExits, allExits []testExit) { - allExits = []testExit{} +) []testExit { + allExits := []testExit{} for _, exit := range commonExits { token := getTokenByIdx(exit.AccountIdx, tokens, accs) siblings := []string{} @@ -82,16 +83,7 @@ func genTestExits( Token: token, }) } - usrExits = []testExit{} - for _, exit := range allExits { - for _, idx := range usrIdxs { - if idx == exit.AccountIdx { - usrExits = append(usrExits, exit) - break - } - } - } - return usrExits, allExits + return allExits } func TestGetExits(t *testing.T) { @@ -116,23 +108,48 @@ func TestGetExits(t *testing.T) { // Get by ethAddr fetchedExits = []testExit{} limit = 7 + var account testAccount + for _, tx := range tc.txs { + found := false + if tx.Type == common.TxTypeExit { + for i := 0; i < len(tc.accounts); i++ { + if tx.FromIdx != nil && string(tc.accounts[i].Idx) == *tx.FromIdx { + account = tc.accounts[i] + break + } + } + } + if found { + break + } + } path = fmt.Sprintf( "%s?hermezEthereumAddress=%s&limit=%d&fromItem=", - endpoint, tc.usrAddr, limit, + endpoint, account.EthAddr, limit, ) err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter) assert.NoError(t, err) - assertExitAPIs(t, tc.usrExits, fetchedExits) + var accountExits []testExit + for i := range tc.exits { + for _, acc := range tc.accounts { + if string(acc.Idx) == tc.exits[i].AccountIdx { + if acc.EthAddr == account.EthAddr { + accountExits = append(accountExits, tc.exits[i]) + } + } + } + } + assertExitAPIs(t, accountExits, fetchedExits) // Get by bjj fetchedExits = []testExit{} limit = 6 path = fmt.Sprintf( "%s?BJJ=%s&limit=%d&fromItem=", - endpoint, tc.usrBjj, limit, + endpoint, account.PublicKey, limit, ) err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter) assert.NoError(t, err) - assertExitAPIs(t, tc.usrExits, fetchedExits) + assertExitAPIs(t, accountExits, fetchedExits) // Get by tokenID fetchedExits = []testExit{} limit = 5 @@ -228,7 +245,7 @@ func TestGetExits(t *testing.T) { // 400 path = fmt.Sprintf( "%s?accountIndex=%s&hermezEthereumAddress=%s", - endpoint, idx, tc.usrAddr, + endpoint, idx, account.EthAddr, ) err = doBadReq("GET", path, nil, 400) assert.NoError(t, err) diff --git a/api/slots.go b/api/slots.go index 3d2a3e2..aaaca51 100644 --- a/api/slots.go +++ b/api/slots.go @@ -17,7 +17,7 @@ type SlotAPI struct { FirstBlock int64 `json:"firstBlock"` LastBlock int64 `json:"lastBlock"` OpenAuction bool `json:"openAuction"` - WinnerBid *historydb.BidAPI `json:"winnerBid"` + WinnerBid *historydb.BidAPI `json:"bestBid"` TotalItems uint64 `json:"-"` FirstItem uint64 `json:"-"` LastItem uint64 `json:"-"` diff --git a/api/slots_test.go b/api/slots_test.go index 54c35e7..4a4209e 100644 --- a/api/slots_test.go +++ b/api/slots_test.go @@ -17,7 +17,7 @@ type testSlot struct { FirstBlock int64 `json:"firstBlock"` LastBlock int64 `json:"lastBlock"` OpenAuction bool `json:"openAuction"` - WinnerBid *testBid `json:"winnerBid"` + WinnerBid *testBid `json:"bestBid"` } type testSlotsResponse struct { @@ -35,6 +35,8 @@ func (t testSlotsResponse) Len() int { return len(t.Slots) } +func (t testSlotsResponse) New() Pendinger { return &testSlotsResponse{} } + func (a *API) genTestSlots(nSlots int, lastBlockNum int64, bids []testBid, auctionVars common.AuctionVariables) []testSlot { tSlots := []testSlot{} bestBids := make(map[int64]testBid) diff --git a/api/state.go b/api/state.go index 9ba8824..dfae473 100644 --- a/api/state.go +++ b/api/state.go @@ -12,10 +12,11 @@ import ( // Network define status of the network type Network struct { - LastBlock int64 `json:"lastBlock"` - LastBatch historydb.BatchAPI `json:"lastBatch"` - CurrentSlot int64 `json:"currentSlot"` - NextForgers []NextForger `json:"nextForgers"` + LastEthBlock int64 `json:"lastEthereumBlock"` + LastSyncBlock int64 `json:"lastSynchedBlock"` + LastBatch historydb.BatchAPI `json:"lastBatch"` + CurrentSlot int64 `json:"currentSlot"` + NextForgers []NextForger `json:"nextForgers"` } // NextForger is a representation of the information of a coordinator and the period will forge @@ -65,8 +66,12 @@ func (a *API) SetAuctionVariables(auctionVariables common.AuctionVariables) { // Network // UpdateNetworkInfo update Status.Network information -func (a *API) UpdateNetworkInfo(lastBlock common.Block, lastBatchNum common.BatchNum, currentSlot int64) error { - a.status.Network.LastBlock = lastBlock.EthBlockNum +func (a *API) UpdateNetworkInfo( + lastEthBlock, lastSyncBlock common.Block, + lastBatchNum common.BatchNum, currentSlot int64, +) error { + a.status.Network.LastSyncBlock = lastSyncBlock.EthBlockNum + a.status.Network.LastEthBlock = lastEthBlock.EthBlockNum lastBatch, err := a.h.GetBatchAPI(lastBatchNum) if err != nil { return err @@ -74,7 +79,7 @@ func (a *API) UpdateNetworkInfo(lastBlock common.Block, lastBatchNum common.Batc a.status.Network.LastBatch = *lastBatch a.status.Network.CurrentSlot = currentSlot lastClosedSlot := currentSlot + int64(a.status.Auction.ClosedAuctionSlots) - nextForgers, err := a.GetNextForgers(lastBlock, currentSlot, lastClosedSlot) + nextForgers, err := a.GetNextForgers(lastSyncBlock, currentSlot, lastClosedSlot) if err != nil { return err } diff --git a/api/state_test.go b/api/state_test.go index fcd8a7f..bcec9cb 100644 --- a/api/state_test.go +++ b/api/state_test.go @@ -21,10 +21,11 @@ type testStatus struct { } type testNetwork struct { - LastBlock int64 `json:"lastBlock"` - LastBatch testBatch `json:"lastBatch"` - CurrentSlot int64 `json:"currentSlot"` - NextForgers []NextForger `json:"nextForgers"` + LastEthBlock int64 `json:"lastEthereumBlock"` + LastSyncBlock int64 `json:"lastSynchedBlock"` + LastBatch testBatch `json:"lastBatch"` + CurrentSlot int64 `json:"currentSlot"` + NextForgers []NextForger `json:"nextForgers"` } func TestSetRollupVariables(t *testing.T) { @@ -80,16 +81,16 @@ func TestNextForgers(t *testing.T) { func TestUpdateNetworkInfo(t *testing.T) { status := &Network{} - assert.Equal(t, status.LastBlock, api.status.Network.LastBlock) + assert.Equal(t, status.LastSyncBlock, api.status.Network.LastSyncBlock) assert.Equal(t, status.LastBatch.BatchNum, api.status.Network.LastBatch.BatchNum) assert.Equal(t, status.CurrentSlot, api.status.Network.CurrentSlot) assert.Equal(t, status.NextForgers, api.status.Network.NextForgers) lastBlock := tc.blocks[3] lastBatchNum := common.BatchNum(3) currentSlotNum := int64(1) - err := api.UpdateNetworkInfo(lastBlock, lastBatchNum, currentSlotNum) + err := api.UpdateNetworkInfo(lastBlock, lastBlock, lastBatchNum, currentSlotNum) assert.NoError(t, err) - assert.Equal(t, lastBlock.EthBlockNum, api.status.Network.LastBlock) + assert.Equal(t, lastBlock.EthBlockNum, api.status.Network.LastSyncBlock) assert.Equal(t, lastBatchNum, api.status.Network.LastBatch.BatchNum) assert.Equal(t, currentSlotNum, api.status.Network.CurrentSlot) assert.Equal(t, int(api.status.Auction.ClosedAuctionSlots)+1, len(api.status.Network.NextForgers)) @@ -101,7 +102,7 @@ func TestUpdateMetrics(t *testing.T) { lastBlock := tc.blocks[3] lastBatchNum := common.BatchNum(3) currentSlotNum := int64(1) - err := api.UpdateNetworkInfo(lastBlock, lastBatchNum, currentSlotNum) + err := api.UpdateNetworkInfo(lastBlock, lastBlock, lastBatchNum, currentSlotNum) assert.NoError(t, err) err = api.UpdateMetrics() @@ -124,14 +125,14 @@ func TestUpdateRecommendedFee(t *testing.T) { api.status.RecommendedFee.ExistingAccount*createAccountInternalExtraFeePercentage) } -func TestGetStatus(t *testing.T) { +func TestGetState(t *testing.T) { lastBlock := tc.blocks[3] lastBatchNum := common.BatchNum(3) currentSlotNum := int64(1) api.SetRollupVariables(tc.rollupVars) api.SetWDelayerVariables(tc.wdelayerVars) api.SetAuctionVariables(tc.auctionVars) - err := api.UpdateNetworkInfo(lastBlock, lastBatchNum, currentSlotNum) + err := api.UpdateNetworkInfo(lastBlock, lastBlock, lastBatchNum, currentSlotNum) assert.NoError(t, err) err = api.UpdateMetrics() assert.NoError(t, err) @@ -145,7 +146,8 @@ func TestGetStatus(t *testing.T) { assert.Equal(t, tc.rollupVars, status.Rollup) assert.Equal(t, tc.auctionVars, status.Auction) assert.Equal(t, tc.wdelayerVars, status.WithdrawalDelayer) - assert.Equal(t, lastBlock.EthBlockNum, status.Network.LastBlock) + assert.Equal(t, lastBlock.EthBlockNum, status.Network.LastEthBlock) + assert.Equal(t, lastBlock.EthBlockNum, status.Network.LastSyncBlock) assert.Equal(t, lastBatchNum, status.Network.LastBatch.BatchNum) assert.Equal(t, currentSlotNum, status.Network.CurrentSlot) assert.Equal(t, int(api.status.Auction.ClosedAuctionSlots)+1, len(status.Network.NextForgers)) diff --git a/api/swagger.yml b/api/swagger.yml index 84b5e42..81486a5 100644 --- a/api/swagger.yml +++ b/api/swagger.yml @@ -1857,6 +1857,7 @@ components: description: List of forged transactions in the batch items: $ref: '#/components/schemas/HistoryTransaction' + nullable: true additionalProperties: false required: - batch @@ -2205,9 +2206,9 @@ components: openAuction: type: boolean description: Whether the auction of the slot is open or not. - winnerBid: + bestBid: type: object - description: The winning bid of the auction. If openAuction == true, is the current winner. If the auction is closed because it has already been finalized, the bid is the final winner. If the winnerBid is null, it is because no coordinator has bid for that slot. + description: The best bid of the auction. If the bestBid is null, it is because no coordinator has bid for that slot. nullable: true properties: itemId: @@ -2245,7 +2246,7 @@ components: - firstBlock - lastBlock - openAuction - - winnerBid + - bestBid Slots: type: object properties: @@ -2328,10 +2329,15 @@ components: type: object description: Gobal statistics of the network properties: - lastBlock: + lastEthereumBlock: + allOf: + - $ref: '#/components/schemas/EthBlockNum' + - description: Current Etherum block. Note that this is the actual last block of Ethereum, not the last synchronized block by the node. + - example: 3457437 + lastSynchedBlock: allOf: - $ref: '#/components/schemas/EthBlockNum' - - description: Last synchronized Etherum block. + - description: Last synchronized Etherum block. Compare with lastEthereumBlock to check the synchronization status of the node. - example: 3457437 lastBatch: $ref: '#/components/schemas/Batch' @@ -2344,7 +2350,8 @@ components: $ref: '#/components/schemas/NextForgers' additionalProperties: false required: - - lastBlock + - lastEthereumBlock + - lastSynchedBlock - lastBatch - currentSlot - nextForgers diff --git a/api/token_test.go b/api/token_test.go index 47ad26b..7d8aff6 100644 --- a/api/token_test.go +++ b/api/token_test.go @@ -26,6 +26,8 @@ func (t *testTokensResponse) Len() int { return len(t.Tokens) } +func (t testTokensResponse) New() Pendinger { return &testTokensResponse{} } + func TestGetToken(t *testing.T) { // Get all txs by their ID endpoint := apiURL + "tokens/" diff --git a/api/txshistory_test.go b/api/txshistory_test.go index 1fd3831..207bcc9 100644 --- a/api/txshistory_test.go +++ b/api/txshistory_test.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "math/big" + "sort" "testing" "time" @@ -51,43 +52,14 @@ type testTx struct { Token historydb.TokenWithUSD `json:"token"` } -type testTxsResponse struct { - Txs []testTx `json:"transactions"` - PendingItems uint64 `json:"pendingItems"` -} - -func (t testTxsResponse) GetPending() (pendingItems, lastItemID uint64) { - pendingItems = t.PendingItems - lastItemID = t.Txs[len(t.Txs)-1].ItemID - return pendingItems, lastItemID -} - -func (t testTxsResponse) Len() int { - return len(t.Txs) -} - -// TxSortFields represents the fields needed to sort L1 and L2 transactions -type txSortFields struct { - BatchNum *common.BatchNum - Position int -} - -// TxSortFielder is a interface that allows sorting L1 and L2 transactions in a combined way -type txSortFielder interface { - SortFields() txSortFields - L1() *common.L1Tx - L2() *common.L2Tx -} - -// TxsSort array of TxSortFielder -type txsSort []txSortFielder +type txsSort []testTx func (t txsSort) Len() int { return len(t) } func (t txsSort) Swap(i, j int) { t[i], t[j] = t[j], t[i] } func (t txsSort) Less(i, j int) bool { // i not forged yet - isf := t[i].SortFields() - jsf := t[j].SortFields() + isf := t[i] + jsf := t[j] if isf.BatchNum == nil { if jsf.BatchNum != nil { // j is already forged return false @@ -108,189 +80,166 @@ func (t txsSort) Less(i, j int) bool { return *isf.BatchNum < *jsf.BatchNum } -type wrappedL1 common.L1Tx - -// SortFields implements TxSortFielder -func (tx *wrappedL1) SortFields() txSortFields { - return txSortFields{ - BatchNum: tx.BatchNum, - Position: tx.Position, - } +type testTxsResponse struct { + Txs []testTx `json:"transactions"` + PendingItems uint64 `json:"pendingItems"` } -// L1 implements TxSortFielder -func (tx *wrappedL1) L1() *common.L1Tx { - l1tx := common.L1Tx(*tx) - return &l1tx +func (t testTxsResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Txs[len(t.Txs)-1].ItemID + return pendingItems, lastItemID } -// L2 implements TxSortFielder -func (tx *wrappedL1) L2() *common.L2Tx { return nil } - -type wrappedL2 common.L2Tx - -// SortFields implements TxSortFielder -func (tx *wrappedL2) SortFields() txSortFields { - return txSortFields{ - BatchNum: &tx.BatchNum, - Position: tx.Position, - } +func (t testTxsResponse) Len() int { + return len(t.Txs) } -// L1 implements TxSortFielder -func (tx *wrappedL2) L1() *common.L1Tx { return nil } - -// L2 implements TxSortFielder -func (tx *wrappedL2) L2() *common.L2Tx { - l2tx := common.L2Tx(*tx) - return &l2tx -} +func (t testTxsResponse) New() Pendinger { return &testTxsResponse{} } func genTestTxs( - genericTxs []txSortFielder, - usrIdxs []string, + l1s []common.L1Tx, + l2s []common.L2Tx, accs []common.Account, tokens []historydb.TokenWithUSD, blocks []common.Block, -) (usrTxs []testTx, allTxs []testTx) { - usrTxs = []testTx{} - allTxs = []testTx{} - isUsrTx := func(tx testTx) bool { - for _, idx := range usrIdxs { - if tx.FromIdx != nil && *tx.FromIdx == idx { - return true - } - if tx.ToIdx == idx { - return true - } +) []testTx { + txs := []testTx{} + // common.L1Tx ==> testTx + for _, l1 := range l1s { + token := getTokenByID(l1.TokenID, tokens) + // l1.FromEthAddr and l1.FromBJJ can't be nil + fromEthAddr := string(apitypes.NewHezEthAddr(l1.FromEthAddr)) + fromBJJ := string(apitypes.NewHezBJJ(l1.FromBJJ)) + tx := testTx{ + IsL1: "L1", + TxID: l1.TxID, + Type: l1.Type, + Position: l1.Position, + FromEthAddr: &fromEthAddr, + FromBJJ: &fromBJJ, + ToIdx: idxToHez(l1.ToIdx, token.Symbol), + Amount: l1.Amount.String(), + BatchNum: l1.BatchNum, + Timestamp: getTimestamp(l1.EthBlockNum, blocks), + L1Info: &testL1Info{ + ToForgeL1TxsNum: l1.ToForgeL1TxsNum, + UserOrigin: l1.UserOrigin, + LoadAmount: l1.LoadAmount.String(), + EthBlockNum: l1.EthBlockNum, + }, + Token: token, } - return false - } - for _, genericTx := range genericTxs { - l1 := genericTx.L1() - l2 := genericTx.L2() - if l1 != nil { // L1Tx to testTx - token := getTokenByID(l1.TokenID, tokens) - // l1.FromEthAddr and l1.FromBJJ can't be nil - fromEthAddr := string(apitypes.NewHezEthAddr(l1.FromEthAddr)) - fromBJJ := string(apitypes.NewHezBJJ(l1.FromBJJ)) - tx := testTx{ - IsL1: "L1", - TxID: l1.TxID, - Type: l1.Type, - Position: l1.Position, - FromEthAddr: &fromEthAddr, - FromBJJ: &fromBJJ, - ToIdx: idxToHez(l1.ToIdx, token.Symbol), - Amount: l1.Amount.String(), - BatchNum: l1.BatchNum, - Timestamp: getTimestamp(l1.EthBlockNum, blocks), - L1Info: &testL1Info{ - ToForgeL1TxsNum: l1.ToForgeL1TxsNum, - UserOrigin: l1.UserOrigin, - LoadAmount: l1.LoadAmount.String(), - EthBlockNum: l1.EthBlockNum, - }, - Token: token, - } - // If FromIdx is not nil - if l1.FromIdx != 0 { - idxStr := idxToHez(l1.FromIdx, token.Symbol) - tx.FromIdx = &idxStr - } - // If tx has a normal ToIdx (>255), set FromEthAddr and FromBJJ - if l1.ToIdx >= common.UserThreshold { - // find account - for _, acc := range accs { - if l1.ToIdx == acc.Idx { - toEthAddr := string(apitypes.NewHezEthAddr(acc.EthAddr)) - tx.ToEthAddr = &toEthAddr - toBJJ := string(apitypes.NewHezBJJ(acc.PublicKey)) - tx.ToBJJ = &toBJJ - break - } + // set BatchNum for user txs + if tx.L1Info.ToForgeL1TxsNum != nil { + // WARNING: this is an asumption, and the test input data can brake it easily + bn := common.BatchNum(*tx.L1Info.ToForgeL1TxsNum + 2) + tx.BatchNum = &bn + } + // If FromIdx is not nil + idxStr := idxToHez(l1.FromIdx, token.Symbol) + tx.FromIdx = &idxStr + // If tx has a normal ToIdx (>255), set FromEthAddr and FromBJJ + if l1.ToIdx >= common.UserThreshold { + // find account + for _, acc := range accs { + if l1.ToIdx == acc.Idx { + toEthAddr := string(apitypes.NewHezEthAddr(acc.EthAddr)) + tx.ToEthAddr = &toEthAddr + toBJJ := string(apitypes.NewHezBJJ(acc.PublicKey)) + tx.ToBJJ = &toBJJ + break } } - // If the token has USD value setted - if token.USD != nil { - af := new(big.Float).SetInt(l1.Amount) - amountFloat, _ := af.Float64() - usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals)) + } + // If the token has USD value setted + if token.USD != nil { + af := new(big.Float).SetInt(l1.Amount) + amountFloat, _ := af.Float64() + usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals)) + if usd != 0 { tx.HistoricUSD = &usd - laf := new(big.Float).SetInt(l1.LoadAmount) - loadAmountFloat, _ := laf.Float64() - loadUSD := *token.USD * loadAmountFloat / math.Pow(10, float64(token.Decimals)) - tx.L1Info.HistoricLoadAmountUSD = &loadUSD - } - allTxs = append(allTxs, tx) - if isUsrTx(tx) { - usrTxs = append(usrTxs, tx) } - } else { // L2Tx to testTx - token := getTokenByIdx(l2.FromIdx, tokens, accs) - // l1.FromIdx can't be nil - fromIdx := idxToHez(l2.FromIdx, token.Symbol) - tx := testTx{ - IsL1: "L2", - TxID: l2.TxID, - Type: l2.Type, - Position: l2.Position, - ToIdx: idxToHez(l2.ToIdx, token.Symbol), - FromIdx: &fromIdx, - Amount: l2.Amount.String(), - BatchNum: &l2.BatchNum, - Timestamp: getTimestamp(l2.EthBlockNum, blocks), - L2Info: &testL2Info{ - Nonce: l2.Nonce, - Fee: l2.Fee, - }, - Token: token, + laf := new(big.Float).SetInt(l1.LoadAmount) + loadAmountFloat, _ := laf.Float64() + loadUSD := *token.USD * loadAmountFloat / math.Pow(10, float64(token.Decimals)) + if loadAmountFloat != 0 { + tx.L1Info.HistoricLoadAmountUSD = &loadUSD } - // If FromIdx is not nil - if l2.FromIdx != 0 { - idxStr := idxToHez(l2.FromIdx, token.Symbol) - tx.FromIdx = &idxStr + } + txs = append(txs, tx) + } + + // common.L2Tx ==> testTx + for i := 0; i < len(l2s); i++ { + token := getTokenByIdx(l2s[i].FromIdx, tokens, accs) + // l1.FromIdx can't be nil + fromIdx := idxToHez(l2s[i].FromIdx, token.Symbol) + tx := testTx{ + IsL1: "L2", + TxID: l2s[i].TxID, + Type: l2s[i].Type, + Position: l2s[i].Position, + ToIdx: idxToHez(l2s[i].ToIdx, token.Symbol), + FromIdx: &fromIdx, + Amount: l2s[i].Amount.String(), + BatchNum: &l2s[i].BatchNum, + Timestamp: getTimestamp(l2s[i].EthBlockNum, blocks), + L2Info: &testL2Info{ + Nonce: l2s[i].Nonce, + Fee: l2s[i].Fee, + }, + Token: token, + } + // If FromIdx is not nil + if l2s[i].FromIdx != 0 { + idxStr := idxToHez(l2s[i].FromIdx, token.Symbol) + tx.FromIdx = &idxStr + } + // Set FromEthAddr and FromBJJ (FromIdx it's always >255) + for _, acc := range accs { + if l2s[i].FromIdx == acc.Idx { + fromEthAddr := string(apitypes.NewHezEthAddr(acc.EthAddr)) + tx.FromEthAddr = &fromEthAddr + fromBJJ := string(apitypes.NewHezBJJ(acc.PublicKey)) + tx.FromBJJ = &fromBJJ + break } - // Set FromEthAddr and FromBJJ (FromIdx it's always >255) + } + // If tx has a normal ToIdx (>255), set FromEthAddr and FromBJJ + if l2s[i].ToIdx >= common.UserThreshold { + // find account for _, acc := range accs { - if l2.ToIdx == acc.Idx { - fromEthAddr := string(apitypes.NewHezEthAddr(acc.EthAddr)) - tx.FromEthAddr = &fromEthAddr - fromBJJ := string(apitypes.NewHezBJJ(acc.PublicKey)) - tx.FromBJJ = &fromBJJ + if l2s[i].ToIdx == acc.Idx { + toEthAddr := string(apitypes.NewHezEthAddr(acc.EthAddr)) + tx.ToEthAddr = &toEthAddr + toBJJ := string(apitypes.NewHezBJJ(acc.PublicKey)) + tx.ToBJJ = &toBJJ break } } - // If tx has a normal ToIdx (>255), set FromEthAddr and FromBJJ - if l2.ToIdx >= common.UserThreshold { - // find account - for _, acc := range accs { - if l2.ToIdx == acc.Idx { - toEthAddr := string(apitypes.NewHezEthAddr(acc.EthAddr)) - tx.ToEthAddr = &toEthAddr - toBJJ := string(apitypes.NewHezBJJ(acc.PublicKey)) - tx.ToBJJ = &toBJJ - break - } - } - } - // If the token has USD value setted - if token.USD != nil { - af := new(big.Float).SetInt(l2.Amount) - amountFloat, _ := af.Float64() - usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals)) - tx.HistoricUSD = &usd - feeUSD := usd * l2.Fee.Percentage() + } + // If the token has USD value setted + if token.USD != nil { + af := new(big.Float).SetInt(l2s[i].Amount) + amountFloat, _ := af.Float64() + usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals)) + if usd != 0 { tx.HistoricUSD = &usd - tx.L2Info.HistoricFeeUSD = &feeUSD - } - allTxs = append(allTxs, tx) - if isUsrTx(tx) { - usrTxs = append(usrTxs, tx) + feeUSD := usd * l2s[i].Fee.Percentage() + if feeUSD != 0 { + tx.L2Info.HistoricFeeUSD = &feeUSD + } } } + txs = append(txs, tx) } - return usrTxs, allTxs + + // Sort txs + sortedTxs := txsSort(txs) + sort.Sort(sortedTxs) + + return []testTx(sortedTxs) } func TestGetHistoryTxs(t *testing.T) { @@ -310,32 +259,44 @@ func TestGetHistoryTxs(t *testing.T) { path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) err := doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) assert.NoError(t, err) - assertTxs(t, tc.allTxs, fetchedTxs) - // Uncomment once tx generation for tests is fixed - // // Get by ethAddr - // fetchedTxs = []testTx{} - // limit = 7 - // path = fmt.Sprintf( - // "%s?hermezEthereumAddress=%s&limit=%d&fromItem=", - // endpoint, tc.usrAddr, limit, - // ) - // err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) - // assert.NoError(t, err) - // assertTxs(t, tc.usrTxs, fetchedTxs) - // // Get by bjj - // fetchedTxs = []testTx{} - // limit = 6 - // path = fmt.Sprintf( - // "%s?BJJ=%s&limit=%d&fromItem=", - // endpoint, tc.usrBjj, limit, - // ) - // err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) - // assert.NoError(t, err) - // assertTxs(t, tc.usrTxs, fetchedTxs) + assertTxs(t, tc.txs, fetchedTxs) + // Get by ethAddr + account := tc.accounts[2] + fetchedTxs = []testTx{} + limit = 7 + path = fmt.Sprintf( + "%s?hermezEthereumAddress=%s&limit=%d&fromItem=", + endpoint, account.EthAddr, limit, + ) + err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) + assert.NoError(t, err) + accountTxs := []testTx{} + for i := 0; i < len(tc.txs); i++ { + tx := tc.txs[i] + if (tx.FromIdx != nil && *tx.FromIdx == string(account.Idx)) || + tx.ToIdx == string(account.Idx) || + (tx.FromEthAddr != nil && *tx.FromEthAddr == string(account.EthAddr)) || + (tx.ToEthAddr != nil && *tx.ToEthAddr == string(account.EthAddr)) || + (tx.FromBJJ != nil && *tx.FromBJJ == string(account.PublicKey)) || + (tx.ToBJJ != nil && *tx.ToBJJ == string(account.PublicKey)) { + accountTxs = append(accountTxs, tx) + } + } + assertTxs(t, accountTxs, fetchedTxs) + // Get by bjj + fetchedTxs = []testTx{} + limit = 6 + path = fmt.Sprintf( + "%s?BJJ=%s&limit=%d&fromItem=", + endpoint, account.PublicKey, limit, + ) + err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) + assert.NoError(t, err) + assertTxs(t, accountTxs, fetchedTxs) // Get by tokenID fetchedTxs = []testTx{} limit = 5 - tokenID := tc.allTxs[0].Token.TokenID + tokenID := tc.txs[0].Token.TokenID path = fmt.Sprintf( "%s?tokenId=%d&limit=%d&fromItem=", endpoint, tokenID, limit, @@ -343,34 +304,46 @@ func TestGetHistoryTxs(t *testing.T) { err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) assert.NoError(t, err) tokenIDTxs := []testTx{} - for i := 0; i < len(tc.allTxs); i++ { - if tc.allTxs[i].Token.TokenID == tokenID { - tokenIDTxs = append(tokenIDTxs, tc.allTxs[i]) + for i := 0; i < len(tc.txs); i++ { + if tc.txs[i].Token.TokenID == tokenID { + tokenIDTxs = append(tokenIDTxs, tc.txs[i]) } } assertTxs(t, tokenIDTxs, fetchedTxs) - // // idx - // fetchedTxs = []testTx{} - // limit = 4 - idx := tc.allTxs[0].ToIdx - // path = fmt.Sprintf( - // "%s?accountIndex=%s&limit=%d&fromItem=", - // endpoint, idx, limit, - // ) - // err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) - // assert.NoError(t, err) - // idxTxs := []testTx{} - // for i := 0; i < len(tc.allTxs); i++ { - // if (tc.allTxs[i].FromIdx != nil && (*tc.allTxs[i].FromIdx)[6:] == idx[6:]) || - // tc.allTxs[i].ToIdx[6:] == idx[6:] { - // idxTxs = append(idxTxs, tc.allTxs[i]) - // } - // } - // assertHistoryTxAPIs(t, idxTxs, fetchedTxs) + // idx + fetchedTxs = []testTx{} + limit = 4 + idxStr := tc.txs[0].ToIdx + idx, err := stringToIdx(idxStr, "") + assert.NoError(t, err) + path = fmt.Sprintf( + "%s?accountIndex=%s&limit=%d&fromItem=", + endpoint, idxStr, limit, + ) + err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) + assert.NoError(t, err) + idxTxs := []testTx{} + for i := 0; i < len(tc.txs); i++ { + var fromIdx *common.Idx + if tc.txs[i].FromIdx != nil { + fromIdx, err = stringToIdx(*tc.txs[i].FromIdx, "") + assert.NoError(t, err) + if *fromIdx == *idx { + idxTxs = append(idxTxs, tc.txs[i]) + continue + } + } + toIdx, err := stringToIdx((tc.txs[i].ToIdx), "") + assert.NoError(t, err) + if *toIdx == *idx { + idxTxs = append(idxTxs, tc.txs[i]) + } + } + assertTxs(t, idxTxs, fetchedTxs) // batchNum fetchedTxs = []testTx{} limit = 3 - batchNum := tc.allTxs[0].BatchNum + batchNum := tc.txs[0].BatchNum path = fmt.Sprintf( "%s?batchNum=%d&limit=%d&fromItem=", endpoint, *batchNum, limit, @@ -378,26 +351,24 @@ func TestGetHistoryTxs(t *testing.T) { err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) assert.NoError(t, err) batchNumTxs := []testTx{} - for i := 0; i < len(tc.allTxs); i++ { - if tc.allTxs[i].BatchNum != nil && - *tc.allTxs[i].BatchNum == *batchNum { - batchNumTxs = append(batchNumTxs, tc.allTxs[i]) + for i := 0; i < len(tc.txs); i++ { + if tc.txs[i].BatchNum != nil && + *tc.txs[i].BatchNum == *batchNum { + batchNumTxs = append(batchNumTxs, tc.txs[i]) } } assertTxs(t, batchNumTxs, fetchedTxs) // type txTypes := []common.TxType{ // Uncomment once test gen is fixed - // common.TxTypeExit, - // common.TxTypeTransfer, - // common.TxTypeDeposit, + common.TxTypeExit, + common.TxTypeTransfer, + common.TxTypeDeposit, common.TxTypeCreateAccountDeposit, - // common.TxTypeCreateAccountDepositTransfer, - // common.TxTypeDepositTransfer, + common.TxTypeCreateAccountDepositTransfer, + common.TxTypeDepositTransfer, common.TxTypeForceTransfer, - // common.TxTypeForceExit, - // common.TxTypeTransferToEthAddr, - // common.TxTypeTransferToBJJ, + common.TxTypeForceExit, } for _, txType := range txTypes { fetchedTxs = []testTx{} @@ -409,9 +380,9 @@ func TestGetHistoryTxs(t *testing.T) { err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) assert.NoError(t, err) txTypeTxs := []testTx{} - for i := 0; i < len(tc.allTxs); i++ { - if tc.allTxs[i].Type == txType { - txTypeTxs = append(txTypeTxs, tc.allTxs[i]) + for i := 0; i < len(tc.txs); i++ { + if tc.txs[i].Type == txType { + txTypeTxs = append(txTypeTxs, tc.txs[i]) } } assertTxs(t, txTypeTxs, fetchedTxs) @@ -426,10 +397,10 @@ func TestGetHistoryTxs(t *testing.T) { err = doGoodReqPaginated(path, historydb.OrderAsc, &testTxsResponse{}, appendIter) assert.NoError(t, err) mixedTxs := []testTx{} - for i := 0; i < len(tc.allTxs); i++ { - if tc.allTxs[i].BatchNum != nil { - if *tc.allTxs[i].BatchNum == *batchNum && tc.allTxs[i].Token.TokenID == tokenID { - mixedTxs = append(mixedTxs, tc.allTxs[i]) + for i := 0; i < len(tc.txs); i++ { + if tc.txs[i].BatchNum != nil { + if *tc.txs[i].BatchNum == *batchNum && tc.txs[i].Token.TokenID == tokenID { + mixedTxs = append(mixedTxs, tc.txs[i]) } } } @@ -441,14 +412,14 @@ func TestGetHistoryTxs(t *testing.T) { err = doGoodReqPaginated(path, historydb.OrderDesc, &testTxsResponse{}, appendIter) assert.NoError(t, err) flipedTxs := []testTx{} - for i := 0; i < len(tc.allTxs); i++ { - flipedTxs = append(flipedTxs, tc.allTxs[len(tc.allTxs)-1-i]) + for i := 0; i < len(tc.txs); i++ { + flipedTxs = append(flipedTxs, tc.txs[len(tc.txs)-1-i]) } assertTxs(t, flipedTxs, fetchedTxs) // 400 path = fmt.Sprintf( "%s?accountIndex=%s&hermezEthereumAddress=%s", - endpoint, idx, tc.usrAddr, + endpoint, idx, account.EthAddr, ) err = doBadReq("GET", path, nil, 400) assert.NoError(t, err) @@ -468,13 +439,13 @@ func TestGetHistoryTx(t *testing.T) { // Get all txs by their ID endpoint := apiURL + "transactions-history/" fetchedTxs := []testTx{} - for _, tx := range tc.allTxs { + for _, tx := range tc.txs { fetchedTx := testTx{} err := doGoodReq("GET", endpoint+tx.TxID.String(), nil, &fetchedTx) assert.NoError(t, err) fetchedTxs = append(fetchedTxs, fetchedTx) } - assertTxs(t, tc.allTxs, fetchedTxs) + assertTxs(t, tc.txs, fetchedTxs) // 400 err := doBadReq("GET", endpoint+"0x001", nil, 400) assert.NoError(t, err) @@ -486,12 +457,15 @@ func TestGetHistoryTx(t *testing.T) { func assertTxs(t *testing.T, expected, actual []testTx) { require.Equal(t, len(expected), len(actual)) for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop + assert.Equal(t, expected[i].BatchNum, actual[i].BatchNum) + assert.Equal(t, expected[i].Position, actual[i].Position) actual[i].ItemID = 0 actual[i].Token.ItemID = 0 assert.Equal(t, expected[i].Timestamp.Unix(), actual[i].Timestamp.Unix()) expected[i].Timestamp = actual[i].Timestamp if expected[i].Token.USDUpdate == nil { assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate) + expected[i].Token.USDUpdate = actual[i].Token.USDUpdate } else { assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix()) expected[i].Token.USDUpdate = actual[i].Token.USDUpdate diff --git a/api/txspool_test.go b/api/txspool_test.go index fd2d2f3..b02609a 100644 --- a/api/txspool_test.go +++ b/api/txspool_test.go @@ -3,7 +3,6 @@ package api import ( "bytes" "encoding/json" - "math/big" "testing" "time" @@ -66,32 +65,11 @@ type testPoolTxSend struct { RqNonce *common.Nonce `json:"requestNonce"` } -func genTestPoolTx(accs []common.Account, privKs []babyjub.PrivateKey, tokens []historydb.TokenWithUSD) (poolTxsToSend []testPoolTxSend, poolTxsToReceive []testPoolTxReceive) { - // Generate common.PoolL2Tx - // WARNING: this should be replaced once til is ready - poolTxs := []common.PoolL2Tx{} - amount := new(big.Int) - amount, ok := amount.SetString("100000000000000", 10) - if !ok { - panic("bad amount") - } - poolTx := common.PoolL2Tx{ - FromIdx: accs[0].Idx, - ToIdx: accs[1].Idx, - Amount: amount, - TokenID: accs[0].TokenID, - Nonce: 6, - } - if _, err := common.NewPoolL2Tx(&poolTx); err != nil { - panic(err) - } - h, err := poolTx.HashToSign() - if err != nil { - panic(err) - } - poolTx.Signature = privKs[0].SignPoseidon(h).Compress() - poolTxs = append(poolTxs, poolTx) - // Transform to API formats +func genTestPoolTxs( + poolTxs []common.PoolL2Tx, + tokens []historydb.TokenWithUSD, + accs []common.Account, +) (poolTxsToSend []testPoolTxSend, poolTxsToReceive []testPoolTxReceive) { poolTxsToSend = []testPoolTxSend{} poolTxsToReceive = []testPoolTxReceive{} for _, poolTx := range poolTxs { @@ -125,7 +103,7 @@ func genTestPoolTx(accs []common.Account, privKs []babyjub.PrivateKey, tokens [] RqNonce: &poolTx.RqNonce, Token: token, } - fromAcc := getAccountByIdx(poolTx.ToIdx, accs) + fromAcc := getAccountByIdx(poolTx.FromIdx, accs) fromAddr := ethAddrToHez(fromAcc.EthAddr) genReceiveTx.FromEthAddr = &fromAddr fromBjj := bjjToString(fromAcc.PublicKey) diff --git a/apitypes/apitypes.go b/apitypes/apitypes.go index 65d6499..db4b624 100644 --- a/apitypes/apitypes.go +++ b/apitypes/apitypes.go @@ -95,12 +95,12 @@ func (c *CollectedFees) UnmarshalJSON(text []byte) error { if err := json.Unmarshal(text, &bigIntMap); err != nil { return err } - bStrMap := make(map[common.TokenID]BigIntStr) + *c = CollectedFees(make(map[common.TokenID]BigIntStr)) for k, v := range bigIntMap { bStr := NewBigIntStr(v) - bStrMap[k] = *bStr + (CollectedFees(*c)[k]) = *bStr } - *c = CollectedFees(bStrMap) + // *c = CollectedFees(bStrMap) return nil } diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index edc17ba..f9c9eaa 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -894,17 +894,13 @@ func (hdb *HistoryDB) GetHistoryTxs( nextIsAnd := false // ethAddr filter if ethAddr != nil { - queryStr = `WITH acc AS - (select idx from account where eth_addr = ?) ` + queryStr - queryStr += ", acc WHERE (tx.from_idx IN(acc.idx) OR tx.to_idx IN(acc.idx)) " + queryStr += "WHERE (tx.from_eth_addr = ? OR tx.to_eth_addr = ?) " nextIsAnd = true - args = append(args, ethAddr) + args = append(args, ethAddr, ethAddr) } else if bjj != nil { // bjj filter - queryStr = `WITH acc AS - (select idx from account where bjj = ?) ` + queryStr - queryStr += ", acc WHERE (tx.from_idx IN(acc.idx) OR tx.to_idx IN(acc.idx)) " + queryStr += "WHERE (tx.from_bjj = ? OR tx.to_bjj = ?) " nextIsAnd = true - args = append(args, bjj) + args = append(args, bjj, bjj) } // tokenID filter if tokenID != nil { @@ -1302,10 +1298,30 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) { } } - // Add l1 Txs - if len(blockData.Rollup.L1UserTxs) > 0 { - if err := hdb.addL1Txs(txn, blockData.Rollup.L1UserTxs); err != nil { - return err + // Prepare user L1 txs to be added. + // They must be added before the batch that will forge them (which can be in the same block) + // and after the account that will be sent to (also can be in the same block). + // Note: insert order is not relevant since item_id will be updated by a DB trigger when + // the batch that forges those txs is inserted + userL1s := make(map[common.BatchNum][]common.L1Tx) + for i := range blockData.Rollup.L1UserTxs { + batchThatForgesIsInTheBlock := false + for _, batch := range blockData.Rollup.Batches { + if batch.Batch.ForgeL1TxsNum != nil && + *batch.Batch.ForgeL1TxsNum == *blockData.Rollup.L1UserTxs[i].ToForgeL1TxsNum { + // Tx is forged in this block. It's guaranteed that: + // * the first batch of the block won't forge user L1 txs that have been added in this block + // * batch nums are sequential therefore it's safe to add the tx at batch.BatchNum -1 + batchThatForgesIsInTheBlock = true + addAtBatchNum := batch.Batch.BatchNum - 1 + userL1s[addAtBatchNum] = append(userL1s[addAtBatchNum], blockData.Rollup.L1UserTxs[i]) + break + } + } + if !batchThatForgesIsInTheBlock { + // User artificial batchNum 0 to add txs that are not forge in this block + // after all the accounts of the block have been added + userL1s[0] = append(userL1s[0], blockData.Rollup.L1UserTxs[i]) } } @@ -1318,12 +1334,17 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) { return err } - // Add unforged l1 Txs - if batch.L1Batch { - if len(batch.L1CoordinatorTxs) > 0 { - if err := hdb.addL1Txs(txn, batch.L1CoordinatorTxs); err != nil { - return err - } + // Add accounts + if len(batch.CreatedAccounts) > 0 { + if err := hdb.addAccounts(txn, batch.CreatedAccounts); err != nil { + return err + } + } + + // Add forged l1 coordinator Txs + if len(batch.L1CoordinatorTxs) > 0 { + if err := hdb.addL1Txs(txn, batch.L1CoordinatorTxs); err != nil { + return err } } @@ -1334,9 +1355,9 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) { } } - // Add accounts - if len(batch.CreatedAccounts) > 0 { - if err := hdb.addAccounts(txn, batch.CreatedAccounts); err != nil { + // Add user L1 txs that will be forged in next batch + if userlL1s, ok := userL1s[batch.Batch.BatchNum]; ok { + if err := hdb.addL1Txs(txn, userlL1s); err != nil { return err } } @@ -1348,6 +1369,12 @@ func (hdb *HistoryDB) AddBlockSCData(blockData *common.BlockData) (err error) { } } } + // Add user L1 txs that won't be forged in this block + if userL1sNotForgedInThisBlock, ok := userL1s[0]; ok { + if err := hdb.addL1Txs(txn, userL1sNotForgedInThisBlock); err != nil { + return err + } + } if blockData.Rollup.Vars != nil { if err := hdb.setRollupVars(txn, blockData.Rollup.Vars); err != nil { return err diff --git a/db/migrations/0001.sql b/db/migrations/0001.sql index 1f1c4bf..93d0f85 100644 --- a/db/migrations/0001.sql +++ b/db/migrations/0001.sql @@ -448,7 +448,7 @@ DECLARE _tx_timestamp TIMESTAMP; BEGIN IF NEW.is_l1 THEN - -- Validate + -- Validate L1 Tx IF NEW.user_origin IS NULL OR NEW.from_eth_addr IS NULL OR NEW.from_bjj IS NULL OR @@ -458,7 +458,7 @@ BEGIN RAISE EXCEPTION 'Invalid L1 tx: %', NEW; END IF; ELSE - -- Validate + -- Validate L2 Tx IF NEW.batch_num IS NULL OR NEW.nonce IS NULL THEN RAISE EXCEPTION 'Invalid L2 tx: %', NEW; END IF; @@ -474,17 +474,22 @@ BEGIN -- Set USD related SELECT INTO _value, _usd_update, _tx_timestamp usd / POWER(10, decimals), usd_update, timestamp FROM token INNER JOIN block on token.eth_block_num = block.eth_block_num WHERE token_id = NEW.token_id; - IF _tx_timestamp - interval '24 hours' < _usd_update AND _tx_timestamp + interval '24 hours' > _usd_update THEN - NEW."amount_usd" = (SELECT _value * NEW.amount_f); - IF NOT NEW.is_l1 THEN - NEW."fee_usd" = (SELECT NEW."amount_usd" * fee_percentage(NEW.fee::NUMERIC)); - ELSE - NEW."load_amount_usd" = (SELECT _value * NEW.load_amount_f); + IF _usd_update - interval '24 hours' < _usd_update AND _usd_update + interval '24 hours' > _usd_update THEN + IF _value > 0.0 THEN + IF NEW."amount_f" > 0.0 THEN + NEW."amount_usd" = (SELECT _value * NEW."amount_f"); + IF NOT NEW."is_l1" AND NEW."fee" > 0 THEN + NEW."fee_usd" = (SELECT NEW."amount_usd" * fee_percentage(NEW.fee::NUMERIC)); + END IF; + END IF; + IF NEW."is_l1" AND NEW."load_amount_f" > 0.0 THEN + NEW."load_amount_usd" = (SELECT _value * NEW.load_amount_f); + END IF; END IF; END IF; -- Set to_{eth_addr,bjj} - IF NEW.to_idx > 255 THEN - SELECT INTO NEW."to_eth_addr", NEW."to_bjj" eth_addr, bjj FROM account WHERE idx = NEW.to_idx; + IF NEW."to_idx" > 255 THEN + SELECT INTO NEW."to_eth_addr", NEW."to_bjj" eth_addr, bjj FROM account WHERE idx = NEW."to_idx"; END IF; RETURN NEW; END; diff --git a/go.sum b/go.sum index 1b9b0cc..30dd8bb 100644 --- a/go.sum +++ b/go.sum @@ -174,6 +174,7 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x github.com/getkin/kin-openapi v0.22.0 h1:J5IFyKd/5yuB6AZAgwK0CMBKnabWcmkowtsl6bRkz4s= github.com/getkin/kin-openapi v0.22.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw= github.com/getkin/kin-openapi v0.23.0 h1:RKtVNKk8kxcTIWEswgZ3Olvn1RxWOJ0zz8cP3d9aHIA= +github.com/getkin/kin-openapi v0.29.0 h1:YAJ8s2UFkhAfJ/bpDcBiUPD4spfVa1ur0FqhBuubaRw= github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs=