arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/api_test.go
api/api_test.goRaw
   1package api
   2
   3import (
   4	"bytes"
   5	"context"
   6	"encoding/json"
   7	"errors"
   8	"fmt"
   9	"io"
  10	"io/ioutil"
  11	"math"
  12	"math/big"
  13	"net/http"
  14	"os"
  15	"sort"
  16	"strconv"
  17	"testing"
  18	"time"
  19
  20	ethCommon "github.com/ethereum/go-ethereum/common"
  21	swagger "github.com/getkin/kin-openapi/openapi3filter"
  22	"github.com/gin-gonic/gin"
  23	"github.com/hermeznetwork/hermez-node/common"
  24	"github.com/hermeznetwork/hermez-node/db"
  25	"github.com/hermeznetwork/hermez-node/db/historydb"
  26	"github.com/hermeznetwork/hermez-node/db/l2db"
  27	"github.com/hermeznetwork/hermez-node/db/statedb"
  28	"github.com/hermeznetwork/hermez-node/eth"
  29	"github.com/hermeznetwork/hermez-node/log"
  30	"github.com/hermeznetwork/hermez-node/test"
  31	"github.com/iden3/go-iden3-crypto/babyjub"
  32	"github.com/mitchellh/copystructure"
  33	"github.com/stretchr/testify/assert"
  34	"github.com/stretchr/testify/require"
  35)
  36
  37const apiPort = ":4010"
  38const apiURL = "http://localhost" + apiPort + "/"
  39
  40type testCommon struct {
  41	blocks           []common.Block
  42	tokens           []tokenAPI
  43	batches          []common.Batch
  44	coordinators     []coordinatorAPI
  45	usrAddr          string
  46	usrBjj           string
  47	accs             []common.Account
  48	usrTxs           []historyTxAPI
  49	allTxs           []historyTxAPI
  50	exits            []exitAPI
  51	usrExits         []exitAPI
  52	poolTxsToSend    []receivedPoolTx
  53	poolTxsToReceive []sendPoolTx
  54	router           *swagger.Router
  55}
  56
  57// TxSortFields represents the fields needed to sort L1 and L2 transactions
  58type txSortFields struct {
  59	BatchNum *common.BatchNum
  60	Position int
  61}
  62
  63// TxSortFielder is a interface that allows sorting L1 and L2 transactions in a combined way
  64type txSortFielder interface {
  65	SortFields() txSortFields
  66	L1() *common.L1Tx
  67	L2() *common.L2Tx
  68}
  69
  70// TxsSort array of TxSortFielder
  71type txsSort []txSortFielder
  72
  73func (t txsSort) Len() int      { return len(t) }
  74func (t txsSort) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
  75func (t txsSort) Less(i, j int) bool {
  76	// i not forged yet
  77	isf := t[i].SortFields()
  78	jsf := t[j].SortFields()
  79	if isf.BatchNum == nil {
  80		if jsf.BatchNum != nil { // j is already forged
  81			return false
  82		}
  83		// Both aren't forged, is i in a smaller position?
  84		return isf.Position < jsf.Position
  85	}
  86	// i is forged
  87	if jsf.BatchNum == nil {
  88		return false // j is not forged
  89	}
  90	// Both are forged
  91	if *isf.BatchNum == *jsf.BatchNum {
  92		// At the same batch,  is i in a smaller position?
  93		return isf.Position < jsf.Position
  94	}
  95	// At different batches, is i in a smaller batch?
  96	return *isf.BatchNum < *jsf.BatchNum
  97}
  98
  99type wrappedL1 common.L1Tx
 100
 101// SortFields implements TxSortFielder
 102func (tx *wrappedL1) SortFields() txSortFields {
 103	return txSortFields{
 104		BatchNum: tx.BatchNum,
 105		Position: tx.Position,
 106	}
 107}
 108
 109// L1 implements TxSortFielder
 110func (tx *wrappedL1) L1() *common.L1Tx {
 111	l1tx := common.L1Tx(*tx)
 112	return &l1tx
 113}
 114
 115// L2 implements TxSortFielder
 116func (tx *wrappedL1) L2() *common.L2Tx { return nil }
 117
 118type wrappedL2 common.L2Tx
 119
 120// SortFields implements TxSortFielder
 121func (tx *wrappedL2) SortFields() txSortFields {
 122	return txSortFields{
 123		BatchNum: &tx.BatchNum,
 124		Position: tx.Position,
 125	}
 126}
 127
 128// L1 implements TxSortFielder
 129func (tx *wrappedL2) L1() *common.L1Tx { return nil }
 130
 131// L2 implements TxSortFielder
 132func (tx *wrappedL2) L2() *common.L2Tx {
 133	l2tx := common.L2Tx(*tx)
 134	return &l2tx
 135}
 136
 137var tc testCommon
 138var config configAPI
 139
 140func TestMain(m *testing.M) {
 141	// Init swagger
 142	router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
 143	// Init DBs
 144	// HistoryDB
 145	pass := os.Getenv("POSTGRES_PASS")
 146	database, err := db.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 147	if err != nil {
 148		panic(err)
 149	}
 150	hdb := historydb.NewHistoryDB(database)
 151	err = hdb.Reorg(-1)
 152	if err != nil {
 153		panic(err)
 154	}
 155	// StateDB
 156	dir, err := ioutil.TempDir("", "tmpdb")
 157	if err != nil {
 158		panic(err)
 159	}
 160	defer func() {
 161		if err := os.RemoveAll(dir); err != nil {
 162			panic(err)
 163		}
 164	}()
 165	sdb, err := statedb.NewStateDB(dir, statedb.TypeTxSelector, 0)
 166	if err != nil {
 167		panic(err)
 168	}
 169	// L2DB
 170	l2DB := l2db.NewL2DB(database, 10, 100, 24*time.Hour)
 171	test.CleanL2DB(l2DB.DB())
 172
 173	config.RollupConstants.ExchangeMultiplier = eth.RollupConstExchangeMultiplier
 174	config.RollupConstants.ExitIdx = eth.RollupConstExitIDx
 175	config.RollupConstants.ReservedIdx = eth.RollupConstReservedIDx
 176	config.RollupConstants.LimitLoadAmount, _ = new(big.Int).SetString("340282366920938463463374607431768211456", 10)
 177	config.RollupConstants.LimitL2TransferAmount, _ = new(big.Int).SetString("6277101735386680763835789423207666416102355444464034512896", 10)
 178	config.RollupConstants.LimitTokens = eth.RollupConstLimitTokens
 179	config.RollupConstants.L1CoordinatorTotalBytes = eth.RollupConstL1CoordinatorTotalBytes
 180	config.RollupConstants.L1UserTotalBytes = eth.RollupConstL1UserTotalBytes
 181	config.RollupConstants.MaxL1UserTx = eth.RollupConstMaxL1UserTx
 182	config.RollupConstants.MaxL1Tx = eth.RollupConstMaxL1Tx
 183	config.RollupConstants.InputSHAConstantBytes = eth.RollupConstInputSHAConstantBytes
 184	config.RollupConstants.NumBuckets = eth.RollupConstNumBuckets
 185	config.RollupConstants.MaxWithdrawalDelay = eth.RollupConstMaxWithdrawalDelay
 186	var rollupPublicConstants eth.RollupPublicConstants
 187	rollupPublicConstants.AbsoluteMaxL1L2BatchTimeout = 240
 188	rollupPublicConstants.HermezAuctionContract = ethCommon.HexToAddress("0x500D1d6A4c7D8Ae28240b47c8FCde034D827fD5e")
 189	rollupPublicConstants.HermezGovernanceDAOAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
 190	rollupPublicConstants.SafetyAddress = ethCommon.HexToAddress("0xE5904695748fe4A84b40b3fc79De2277660BD1D3")
 191	rollupPublicConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
 192	rollupPublicConstants.WithdrawDelayerContract = ethCommon.HexToAddress("0xD6C850aeBFDC46D7F4c207e445cC0d6B0919BDBe")
 193	var verifier eth.RollupVerifierStruct
 194	verifier.MaxTx = 512
 195	verifier.NLevels = 32
 196	rollupPublicConstants.Verifiers = append(rollupPublicConstants.Verifiers, verifier)
 197
 198	var auctionConstants eth.AuctionConstants
 199	auctionConstants.BlocksPerSlot = 40
 200	auctionConstants.GenesisBlockNum = 100
 201	auctionConstants.GovernanceAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
 202	auctionConstants.InitialMinimalBidding, _ = new(big.Int).SetString("10000000000000000000", 10)
 203	auctionConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
 204	auctionConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
 205
 206	var wdelayerConstants eth.WDelayerConstants
 207	wdelayerConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
 208	wdelayerConstants.MaxEmergencyModeTime = uint64(1000000)
 209	wdelayerConstants.MaxWithdrawalDelay = uint64(10000000)
 210
 211	config.RollupConstants.PublicConstants = rollupPublicConstants
 212	config.AuctionConstants = auctionConstants
 213	config.WDelayerConstants = wdelayerConstants
 214
 215	// Init API
 216	api := gin.Default()
 217	if err := SetAPIEndpoints(
 218		true,
 219		true,
 220		api,
 221		hdb,
 222		sdb,
 223		l2DB,
 224		&config,
 225	); err != nil {
 226		panic(err)
 227	}
 228	// Start server
 229	server := &http.Server{Addr: apiPort, Handler: api}
 230	go func() {
 231		if err := server.ListenAndServe(); err != nil &&
 232			err != http.ErrServerClosed {
 233			panic(err)
 234		}
 235	}()
 236
 237	// Populate DBs
 238	// Clean DB
 239	err = h.Reorg(0)
 240	if err != nil {
 241		panic(err)
 242	}
 243	// Gen blocks and add them to DB
 244	const nBlocks = 5
 245	blocks := test.GenBlocks(1, nBlocks+1)
 246	err = h.AddBlocks(blocks)
 247	if err != nil {
 248		panic(err)
 249	}
 250	// Gen tokens and add them to DB
 251	const nTokens = 10
 252	tokens := test.GenTokens(nTokens, blocks)
 253	err = h.AddTokens(tokens)
 254	if err != nil {
 255		panic(err)
 256	}
 257	// Set token value
 258	tokensUSD := []tokenAPI{}
 259	for i, tkn := range tokens {
 260		token := tokenAPI{
 261			TokenID:     tkn.TokenID,
 262			EthBlockNum: tkn.EthBlockNum,
 263			EthAddr:     tkn.EthAddr,
 264			Name:        tkn.Name,
 265			Symbol:      tkn.Symbol,
 266			Decimals:    tkn.Decimals,
 267		}
 268		// Set value of 50% of the tokens
 269		if i%2 != 0 {
 270			value := float64(i) * 1.234567
 271			now := time.Now().UTC()
 272			token.USD = &value
 273			token.USDUpdate = &now
 274			err = h.UpdateTokenValue(token.Symbol, value)
 275			if err != nil {
 276				panic(err)
 277			}
 278		}
 279		tokensUSD = append(tokensUSD, token)
 280	}
 281	// Gen batches and add them to DB
 282	const nBatches = 10
 283	batches := test.GenBatches(nBatches, blocks)
 284	err = h.AddBatches(batches)
 285	if err != nil {
 286		panic(err)
 287	}
 288	// Gen accounts and add them to HistoryDB and StateDB
 289	const totalAccounts = 40
 290	const userAccounts = 4
 291	usrAddr := ethCommon.BigToAddress(big.NewInt(4896847))
 292	privK := babyjub.NewRandPrivKey()
 293	usrBjj := privK.Public()
 294	accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches)
 295	err = h.AddAccounts(accs)
 296	if err != nil {
 297		panic(err)
 298	}
 299	for i := 0; i < len(accs); i++ {
 300		if _, err := s.CreateAccount(accs[i].Idx, &accs[i]); err != nil {
 301			panic(err)
 302		}
 303	}
 304	// Gen exits and add them to DB
 305	const totalExits = 40
 306	exits := test.GenExitTree(totalExits, batches, accs)
 307	err = h.AddExitTree(exits)
 308	if err != nil {
 309		panic(err)
 310	}
 311	// Gen L1Txs and add them to DB
 312	const totalL1Txs = 40
 313	const userL1Txs = 4
 314	usrL1Txs, othrL1Txs := test.GenL1Txs(256, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches)
 315	// Gen L2Txs and add them to DB
 316	const totalL2Txs = 20
 317	const userL2Txs = 4
 318	usrL2Txs, othrL2Txs := test.GenL2Txs(256+totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches)
 319	// Order txs
 320	sortedTxs := []txSortFielder{}
 321	for i := 0; i < len(usrL1Txs); i++ {
 322		wL1 := wrappedL1(usrL1Txs[i])
 323		sortedTxs = append(sortedTxs, &wL1)
 324	}
 325	for i := 0; i < len(othrL1Txs); i++ {
 326		wL1 := wrappedL1(othrL1Txs[i])
 327		sortedTxs = append(sortedTxs, &wL1)
 328	}
 329	for i := 0; i < len(usrL2Txs); i++ {
 330		wL2 := wrappedL2(usrL2Txs[i])
 331		sortedTxs = append(sortedTxs, &wL2)
 332	}
 333	for i := 0; i < len(othrL2Txs); i++ {
 334		wL2 := wrappedL2(othrL2Txs[i])
 335		sortedTxs = append(sortedTxs, &wL2)
 336	}
 337	sort.Sort(txsSort(sortedTxs))
 338	// Add txs to DB and prepare them for test commons
 339	usrTxs := []historyTxAPI{}
 340	allTxs := []historyTxAPI{}
 341	getTimestamp := func(blockNum int64) time.Time {
 342		for i := 0; i < len(blocks); i++ {
 343			if blocks[i].EthBlockNum == blockNum {
 344				return blocks[i].Timestamp
 345			}
 346		}
 347		panic("timesamp not found")
 348	}
 349	getToken := func(id common.TokenID) tokenAPI {
 350		for i := 0; i < len(tokensUSD); i++ {
 351			if tokensUSD[i].TokenID == id {
 352				return tokensUSD[i]
 353			}
 354		}
 355		panic("token not found")
 356	}
 357	getTokenByIdx := func(idx common.Idx) tokenAPI {
 358		for _, acc := range accs {
 359			if idx == acc.Idx {
 360				return getToken(acc.TokenID)
 361			}
 362		}
 363		panic("token not found")
 364	}
 365	usrIdxs := []string{}
 366	for _, acc := range accs {
 367		if acc.EthAddr == usrAddr || acc.PublicKey == usrBjj {
 368			for _, token := range tokens {
 369				if token.TokenID == acc.TokenID {
 370					usrIdxs = append(usrIdxs, idxToHez(acc.Idx, token.Symbol))
 371				}
 372			}
 373		}
 374	}
 375	isUsrTx := func(tx historyTxAPI) bool {
 376		for _, idx := range usrIdxs {
 377			if tx.FromIdx != nil && *tx.FromIdx == idx {
 378				return true
 379			}
 380			if tx.ToIdx == idx {
 381				return true
 382			}
 383		}
 384		return false
 385	}
 386	for _, genericTx := range sortedTxs {
 387		l1 := genericTx.L1()
 388		l2 := genericTx.L2()
 389		if l1 != nil {
 390			// Add L1 tx to DB
 391			err = h.AddL1Txs([]common.L1Tx{*l1})
 392			if err != nil {
 393				panic(err)
 394			}
 395			// L1Tx ==> historyTxAPI
 396			token := getToken(l1.TokenID)
 397			tx := historyTxAPI{
 398				IsL1:      "L1",
 399				TxID:      l1.TxID,
 400				Type:      l1.Type,
 401				Position:  l1.Position,
 402				ToIdx:     idxToHez(l1.ToIdx, token.Symbol),
 403				Amount:    l1.Amount.String(),
 404				BatchNum:  l1.BatchNum,
 405				Timestamp: getTimestamp(l1.EthBlockNum),
 406				L1Info: &l1Info{
 407					ToForgeL1TxsNum: l1.ToForgeL1TxsNum,
 408					UserOrigin:      l1.UserOrigin,
 409					FromEthAddr:     ethAddrToHez(l1.FromEthAddr),
 410					FromBJJ:         bjjToString(l1.FromBJJ),
 411					LoadAmount:      l1.LoadAmount.String(),
 412					EthBlockNum:     l1.EthBlockNum,
 413				},
 414				Token: token,
 415			}
 416			if l1.FromIdx != 0 {
 417				idxStr := idxToHez(l1.FromIdx, token.Symbol)
 418				tx.FromIdx = &idxStr
 419			}
 420			if token.USD != nil {
 421				af := new(big.Float).SetInt(l1.Amount)
 422				amountFloat, _ := af.Float64()
 423				usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals))
 424				tx.HistoricUSD = &usd
 425				laf := new(big.Float).SetInt(l1.LoadAmount)
 426				loadAmountFloat, _ := laf.Float64()
 427				loadUSD := *token.USD * loadAmountFloat / math.Pow(10, float64(token.Decimals))
 428				tx.L1Info.HistoricLoadAmountUSD = &loadUSD
 429			}
 430			allTxs = append(allTxs, tx)
 431			if isUsrTx(tx) {
 432				usrTxs = append(usrTxs, tx)
 433			}
 434		} else {
 435			// Add L2 tx to DB
 436			err = h.AddL2Txs([]common.L2Tx{*l2})
 437			if err != nil {
 438				panic(err)
 439			}
 440			// L2Tx ==> historyTxAPI
 441			var tokenID common.TokenID
 442			found := false
 443			for _, acc := range accs {
 444				if acc.Idx == l2.FromIdx {
 445					found = true
 446					tokenID = acc.TokenID
 447					break
 448				}
 449			}
 450			if !found {
 451				panic("tokenID not found")
 452			}
 453			token := getToken(tokenID)
 454			tx := historyTxAPI{
 455				IsL1:      "L2",
 456				TxID:      l2.TxID,
 457				Type:      l2.Type,
 458				Position:  l2.Position,
 459				ToIdx:     idxToHez(l2.ToIdx, token.Symbol),
 460				Amount:    l2.Amount.String(),
 461				BatchNum:  &l2.BatchNum,
 462				Timestamp: getTimestamp(l2.EthBlockNum),
 463				L2Info: &l2Info{
 464					Nonce: l2.Nonce,
 465					Fee:   l2.Fee,
 466				},
 467				Token: token,
 468			}
 469			if l2.FromIdx != 0 {
 470				idxStr := idxToHez(l2.FromIdx, token.Symbol)
 471				tx.FromIdx = &idxStr
 472			}
 473			if token.USD != nil {
 474				af := new(big.Float).SetInt(l2.Amount)
 475				amountFloat, _ := af.Float64()
 476				usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals))
 477				tx.HistoricUSD = &usd
 478				feeUSD := usd * l2.Fee.Percentage()
 479				tx.HistoricUSD = &usd
 480				tx.L2Info.HistoricFeeUSD = &feeUSD
 481			}
 482			allTxs = append(allTxs, tx)
 483			if isUsrTx(tx) {
 484				usrTxs = append(usrTxs, tx)
 485			}
 486		}
 487	}
 488	// Transform exits to API
 489	exitsToAPIExits := func(exits []common.ExitInfo, accs []common.Account, tokens []common.Token) []exitAPI {
 490		historyExits := []historydb.HistoryExit{}
 491		for _, exit := range exits {
 492			token := getTokenByIdx(exit.AccountIdx)
 493			historyExits = append(historyExits, historydb.HistoryExit{
 494				BatchNum:               exit.BatchNum,
 495				AccountIdx:             exit.AccountIdx,
 496				MerkleProof:            exit.MerkleProof,
 497				Balance:                exit.Balance,
 498				InstantWithdrawn:       exit.InstantWithdrawn,
 499				DelayedWithdrawRequest: exit.DelayedWithdrawRequest,
 500				DelayedWithdrawn:       exit.DelayedWithdrawn,
 501				TokenID:                token.TokenID,
 502				TokenEthBlockNum:       token.EthBlockNum,
 503				TokenEthAddr:           token.EthAddr,
 504				TokenName:              token.Name,
 505				TokenSymbol:            token.Symbol,
 506				TokenDecimals:          token.Decimals,
 507				TokenUSD:               token.USD,
 508				TokenUSDUpdate:         token.USDUpdate,
 509			})
 510		}
 511		return historyExitsToAPI(historyExits)
 512	}
 513	apiExits := exitsToAPIExits(exits, accs, tokens)
 514	// sort.Sort(apiExits)
 515	usrExits := []exitAPI{}
 516	for _, exit := range apiExits {
 517		for _, idx := range usrIdxs {
 518			if idx == exit.AccountIdx {
 519				usrExits = append(usrExits, exit)
 520			}
 521		}
 522	}
 523	// Prepare pool Txs
 524	// Generate common.PoolL2Tx
 525	// WARNING: this should be replaced once transakcio is ready
 526	poolTxs := []common.PoolL2Tx{}
 527	amount := new(big.Int)
 528	amount, ok := amount.SetString("100000000000000", 10)
 529	if !ok {
 530		panic("bad amount")
 531	}
 532	poolTx := common.PoolL2Tx{
 533		FromIdx: accs[0].Idx,
 534		ToIdx:   accs[1].Idx,
 535		Amount:  amount,
 536		TokenID: accs[0].TokenID,
 537		Nonce:   6,
 538	}
 539	if _, err := common.NewPoolL2Tx(&poolTx); err != nil {
 540		panic(err)
 541	}
 542	h, err := poolTx.HashToSign()
 543	if err != nil {
 544		panic(err)
 545	}
 546	poolTx.Signature = privK.SignPoseidon(h).Compress()
 547	poolTxs = append(poolTxs, poolTx)
 548	// Transform to API formats
 549	poolTxsToSend := []receivedPoolTx{}
 550	poolTxsToReceive := []sendPoolTx{}
 551	for _, poolTx := range poolTxs {
 552		// common.PoolL2Tx ==> receivedPoolTx
 553		token := getToken(poolTx.TokenID)
 554		genSendTx := receivedPoolTx{
 555			TxID:      poolTx.TxID,
 556			Type:      poolTx.Type,
 557			TokenID:   poolTx.TokenID,
 558			FromIdx:   idxToHez(poolTx.FromIdx, token.Symbol),
 559			Amount:    poolTx.Amount.String(),
 560			Fee:       poolTx.Fee,
 561			Nonce:     poolTx.Nonce,
 562			Signature: poolTx.Signature,
 563			RqFee:     &poolTx.RqFee,
 564			RqNonce:   &poolTx.RqNonce,
 565		}
 566		// common.PoolL2Tx ==> receivedPoolTx
 567		genReceiveTx := sendPoolTx{
 568			TxID:      poolTx.TxID,
 569			Type:      poolTx.Type,
 570			FromIdx:   idxToHez(poolTx.FromIdx, token.Symbol),
 571			Amount:    poolTx.Amount.String(),
 572			Fee:       poolTx.Fee,
 573			Nonce:     poolTx.Nonce,
 574			State:     poolTx.State,
 575			Signature: poolTx.Signature,
 576			Timestamp: poolTx.Timestamp,
 577			// BatchNum:    poolTx.BatchNum,
 578			RqFee:   &poolTx.RqFee,
 579			RqNonce: &poolTx.RqNonce,
 580			Token:   token,
 581		}
 582		if poolTx.ToIdx != 0 {
 583			toIdx := idxToHez(poolTx.ToIdx, token.Symbol)
 584			genSendTx.ToIdx = &toIdx
 585			genReceiveTx.ToIdx = &toIdx
 586		}
 587		if poolTx.ToEthAddr != common.EmptyAddr {
 588			toEth := ethAddrToHez(poolTx.ToEthAddr)
 589			genSendTx.ToEthAddr = &toEth
 590			genReceiveTx.ToEthAddr = &toEth
 591		}
 592		if poolTx.ToBJJ != nil {
 593			toBJJ := bjjToString(poolTx.ToBJJ)
 594			genSendTx.ToBJJ = &toBJJ
 595			genReceiveTx.ToBJJ = &toBJJ
 596		}
 597		if poolTx.RqFromIdx != 0 {
 598			rqFromIdx := idxToHez(poolTx.RqFromIdx, token.Symbol)
 599			genSendTx.RqFromIdx = &rqFromIdx
 600			genReceiveTx.RqFromIdx = &rqFromIdx
 601			genSendTx.RqTokenID = &token.TokenID
 602			genReceiveTx.RqTokenID = &token.TokenID
 603			rqAmount := poolTx.RqAmount.String()
 604			genSendTx.RqAmount = &rqAmount
 605			genReceiveTx.RqAmount = &rqAmount
 606
 607			if poolTx.RqToIdx != 0 {
 608				rqToIdx := idxToHez(poolTx.RqToIdx, token.Symbol)
 609				genSendTx.RqToIdx = &rqToIdx
 610				genReceiveTx.RqToIdx = &rqToIdx
 611			}
 612			if poolTx.RqToEthAddr != common.EmptyAddr {
 613				rqToEth := ethAddrToHez(poolTx.RqToEthAddr)
 614				genSendTx.RqToEthAddr = &rqToEth
 615				genReceiveTx.RqToEthAddr = &rqToEth
 616			}
 617			if poolTx.RqToBJJ != nil {
 618				rqToBJJ := bjjToString(poolTx.RqToBJJ)
 619				genSendTx.RqToBJJ = &rqToBJJ
 620				genReceiveTx.RqToBJJ = &rqToBJJ
 621			}
 622		}
 623		poolTxsToSend = append(poolTxsToSend, genSendTx)
 624		poolTxsToReceive = append(poolTxsToReceive, genReceiveTx)
 625	}
 626	// Coordinators
 627	const nCoords = 10
 628	coords := test.GenCoordinators(nCoords, blocks)
 629	err = hdb.AddCoordinators(coords)
 630	if err != nil {
 631		panic(err)
 632	}
 633	fromItem := uint(0)
 634	limit := uint(99999)
 635	coordinators, _, err := hdb.GetCoordinators(&fromItem, &limit, historydb.OrderAsc)
 636	apiCoordinators := coordinatorsToAPI(coordinators)
 637	if err != nil {
 638		panic(err)
 639	}
 640	// Set testCommon
 641	tc = testCommon{
 642		blocks:           blocks,
 643		tokens:           tokensUSD,
 644		batches:          batches,
 645		coordinators:     apiCoordinators,
 646		usrAddr:          ethAddrToHez(usrAddr),
 647		usrBjj:           bjjToString(usrBjj),
 648		accs:             accs,
 649		usrTxs:           usrTxs,
 650		allTxs:           allTxs,
 651		exits:            apiExits,
 652		usrExits:         usrExits,
 653		poolTxsToSend:    poolTxsToSend,
 654		poolTxsToReceive: poolTxsToReceive,
 655		router:           router,
 656	}
 657	// Run tests
 658	result := m.Run()
 659	// Stop server
 660	if err := server.Shutdown(context.Background()); err != nil {
 661		panic(err)
 662	}
 663	if err := database.Close(); err != nil {
 664		panic(err)
 665	}
 666	if err := os.RemoveAll(dir); err != nil {
 667		panic(err)
 668	}
 669	os.Exit(result)
 670}
 671
 672func TestGetHistoryTxs(t *testing.T) {
 673	endpoint := apiURL + "transactions-history"
 674	fetchedTxs := []historyTxAPI{}
 675	appendIter := func(intr interface{}) {
 676		for i := 0; i < len(intr.(*historyTxsAPI).Txs); i++ {
 677			tmp, err := copystructure.Copy(intr.(*historyTxsAPI).Txs[i])
 678			if err != nil {
 679				panic(err)
 680			}
 681			fetchedTxs = append(fetchedTxs, tmp.(historyTxAPI))
 682		}
 683	}
 684	// Get all (no filters)
 685	limit := 8
 686	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 687	err := doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 688	assert.NoError(t, err)
 689	assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
 690	// Uncomment once tx generation for tests is fixed
 691	// // Get by ethAddr
 692	// fetchedTxs = []historyTxAPI{}
 693	// limit = 7
 694	// path = fmt.Sprintf(
 695	// 	"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
 696	// 	endpoint, tc.usrAddr, limit,
 697	// )
 698	// err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 699	// assert.NoError(t, err)
 700	// assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
 701	// // Get by bjj
 702	// fetchedTxs = []historyTxAPI{}
 703	// limit = 6
 704	// path = fmt.Sprintf(
 705	// 	"%s?BJJ=%s&limit=%d&fromItem=",
 706	// 	endpoint, tc.usrBjj, limit,
 707	// )
 708	// err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 709	// assert.NoError(t, err)
 710	// assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
 711	// Get by tokenID
 712	fetchedTxs = []historyTxAPI{}
 713	limit = 5
 714	tokenID := tc.allTxs[0].Token.TokenID
 715	path = fmt.Sprintf(
 716		"%s?tokenId=%d&limit=%d&fromItem=",
 717		endpoint, tokenID, limit,
 718	)
 719	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 720	assert.NoError(t, err)
 721	tokenIDTxs := []historyTxAPI{}
 722	for i := 0; i < len(tc.allTxs); i++ {
 723		if tc.allTxs[i].Token.TokenID == tokenID {
 724			tokenIDTxs = append(tokenIDTxs, tc.allTxs[i])
 725		}
 726	}
 727	assertHistoryTxAPIs(t, tokenIDTxs, fetchedTxs)
 728	// idx
 729	fetchedTxs = []historyTxAPI{}
 730	limit = 4
 731	idx := tc.allTxs[0].ToIdx
 732	path = fmt.Sprintf(
 733		"%s?accountIndex=%s&limit=%d&fromItem=",
 734		endpoint, idx, limit,
 735	)
 736	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 737	assert.NoError(t, err)
 738	idxTxs := []historyTxAPI{}
 739	for i := 0; i < len(tc.allTxs); i++ {
 740		if (tc.allTxs[i].FromIdx != nil && (*tc.allTxs[i].FromIdx)[6:] == idx[6:]) ||
 741			tc.allTxs[i].ToIdx[6:] == idx[6:] {
 742			idxTxs = append(idxTxs, tc.allTxs[i])
 743		}
 744	}
 745	assertHistoryTxAPIs(t, idxTxs, fetchedTxs)
 746	// batchNum
 747	fetchedTxs = []historyTxAPI{}
 748	limit = 3
 749	batchNum := tc.allTxs[0].BatchNum
 750	path = fmt.Sprintf(
 751		"%s?batchNum=%d&limit=%d&fromItem=",
 752		endpoint, *batchNum, limit,
 753	)
 754	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 755	assert.NoError(t, err)
 756	batchNumTxs := []historyTxAPI{}
 757	for i := 0; i < len(tc.allTxs); i++ {
 758		if tc.allTxs[i].BatchNum != nil &&
 759			*tc.allTxs[i].BatchNum == *batchNum {
 760			batchNumTxs = append(batchNumTxs, tc.allTxs[i])
 761		}
 762	}
 763	assertHistoryTxAPIs(t, batchNumTxs, fetchedTxs)
 764	// type
 765	txTypes := []common.TxType{
 766		// Uncomment once test gen is fixed
 767		// common.TxTypeExit,
 768		// common.TxTypeTransfer,
 769		// common.TxTypeDeposit,
 770		common.TxTypeCreateAccountDeposit,
 771		// common.TxTypeCreateAccountDepositTransfer,
 772		// common.TxTypeDepositTransfer,
 773		common.TxTypeForceTransfer,
 774		// common.TxTypeForceExit,
 775		// common.TxTypeTransferToEthAddr,
 776		// common.TxTypeTransferToBJJ,
 777	}
 778	for _, txType := range txTypes {
 779		fetchedTxs = []historyTxAPI{}
 780		limit = 2
 781		path = fmt.Sprintf(
 782			"%s?type=%s&limit=%d&fromItem=",
 783			endpoint, txType, limit,
 784		)
 785		err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 786		assert.NoError(t, err)
 787		txTypeTxs := []historyTxAPI{}
 788		for i := 0; i < len(tc.allTxs); i++ {
 789			if tc.allTxs[i].Type == txType {
 790				txTypeTxs = append(txTypeTxs, tc.allTxs[i])
 791			}
 792		}
 793		assertHistoryTxAPIs(t, txTypeTxs, fetchedTxs)
 794	}
 795	// Multiple filters
 796	fetchedTxs = []historyTxAPI{}
 797	limit = 1
 798	path = fmt.Sprintf(
 799		"%s?batchNum=%d&tokenId=%d&limit=%d&fromItem=",
 800		endpoint, *batchNum, tokenID, limit,
 801	)
 802	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 803	assert.NoError(t, err)
 804	mixedTxs := []historyTxAPI{}
 805	for i := 0; i < len(tc.allTxs); i++ {
 806		if tc.allTxs[i].BatchNum != nil {
 807			if *tc.allTxs[i].BatchNum == *batchNum && tc.allTxs[i].Token.TokenID == tokenID {
 808				mixedTxs = append(mixedTxs, tc.allTxs[i])
 809			}
 810		}
 811	}
 812	assertHistoryTxAPIs(t, mixedTxs, fetchedTxs)
 813	// All, in reverse order
 814	fetchedTxs = []historyTxAPI{}
 815	limit = 5
 816	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 817	err = doGoodReqPaginated(path, historydb.OrderDesc, &historyTxsAPI{}, appendIter)
 818	assert.NoError(t, err)
 819	flipedTxs := []historyTxAPI{}
 820	for i := 0; i < len(tc.allTxs); i++ {
 821		flipedTxs = append(flipedTxs, tc.allTxs[len(tc.allTxs)-1-i])
 822	}
 823	assertHistoryTxAPIs(t, flipedTxs, fetchedTxs)
 824	// 400
 825	path = fmt.Sprintf(
 826		"%s?accountIndex=%s&hermezEthereumAddress=%s",
 827		endpoint, idx, tc.usrAddr,
 828	)
 829	err = doBadReq("GET", path, nil, 400)
 830	assert.NoError(t, err)
 831	path = fmt.Sprintf("%s?tokenId=X", endpoint)
 832	err = doBadReq("GET", path, nil, 400)
 833	assert.NoError(t, err)
 834	// 404
 835	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
 836	err = doBadReq("GET", path, nil, 404)
 837	assert.NoError(t, err)
 838	path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
 839	err = doBadReq("GET", path, nil, 404)
 840	assert.NoError(t, err)
 841}
 842
 843func TestGetHistoryTx(t *testing.T) {
 844	// Get all txs by their ID
 845	endpoint := apiURL + "transactions-history/"
 846	fetchedTxs := []historyTxAPI{}
 847	for _, tx := range tc.allTxs {
 848		fetchedTx := historyTxAPI{}
 849		assert.NoError(t, doGoodReq("GET", endpoint+tx.TxID.String(), nil, &fetchedTx))
 850		fetchedTxs = append(fetchedTxs, fetchedTx)
 851	}
 852	assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
 853	// 400
 854	err := doBadReq("GET", endpoint+"0x001", nil, 400)
 855	assert.NoError(t, err)
 856	// 404
 857	err = doBadReq("GET", endpoint+"0x00000000000001e240004700", nil, 404)
 858	assert.NoError(t, err)
 859}
 860
 861func assertHistoryTxAPIs(t *testing.T, expected, actual []historyTxAPI) {
 862	require.Equal(t, len(expected), len(actual))
 863	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
 864		actual[i].ItemID = 0
 865		assert.Equal(t, expected[i].Timestamp.Unix(), actual[i].Timestamp.Unix())
 866		expected[i].Timestamp = actual[i].Timestamp
 867		if expected[i].Token.USDUpdate == nil {
 868			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
 869		} else {
 870			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
 871			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
 872		}
 873		test.AssertUSD(t, expected[i].HistoricUSD, actual[i].HistoricUSD)
 874		if expected[i].L2Info != nil {
 875			test.AssertUSD(t, expected[i].L2Info.HistoricFeeUSD, actual[i].L2Info.HistoricFeeUSD)
 876		} else {
 877			test.AssertUSD(t, expected[i].L1Info.HistoricLoadAmountUSD, actual[i].L1Info.HistoricLoadAmountUSD)
 878		}
 879		assert.Equal(t, expected[i], actual[i])
 880	}
 881}
 882
 883func TestGetExits(t *testing.T) {
 884	endpoint := apiURL + "exits"
 885	fetchedExits := []exitAPI{}
 886	appendIter := func(intr interface{}) {
 887		for i := 0; i < len(intr.(*exitsAPI).Exits); i++ {
 888			tmp, err := copystructure.Copy(intr.(*exitsAPI).Exits[i])
 889			if err != nil {
 890				panic(err)
 891			}
 892			fetchedExits = append(fetchedExits, tmp.(exitAPI))
 893		}
 894	}
 895	// Get all (no filters)
 896	limit := 8
 897	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 898	err := doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 899	assert.NoError(t, err)
 900	assertExitAPIs(t, tc.exits, fetchedExits)
 901
 902	// Get by ethAddr
 903	fetchedExits = []exitAPI{}
 904	limit = 7
 905	path = fmt.Sprintf(
 906		"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
 907		endpoint, tc.usrAddr, limit,
 908	)
 909	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 910	assert.NoError(t, err)
 911	assertExitAPIs(t, tc.usrExits, fetchedExits)
 912	// Get by bjj
 913	fetchedExits = []exitAPI{}
 914	limit = 6
 915	path = fmt.Sprintf(
 916		"%s?BJJ=%s&limit=%d&fromItem=",
 917		endpoint, tc.usrBjj, limit,
 918	)
 919	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 920	assert.NoError(t, err)
 921	assertExitAPIs(t, tc.usrExits, fetchedExits)
 922	// Get by tokenID
 923	fetchedExits = []exitAPI{}
 924	limit = 5
 925	tokenID := tc.exits[0].Token.TokenID
 926	path = fmt.Sprintf(
 927		"%s?tokenId=%d&limit=%d&fromItem=",
 928		endpoint, tokenID, limit,
 929	)
 930	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 931	assert.NoError(t, err)
 932	tokenIDExits := []exitAPI{}
 933	for i := 0; i < len(tc.exits); i++ {
 934		if tc.exits[i].Token.TokenID == tokenID {
 935			tokenIDExits = append(tokenIDExits, tc.exits[i])
 936		}
 937	}
 938	assertExitAPIs(t, tokenIDExits, fetchedExits)
 939	// idx
 940	fetchedExits = []exitAPI{}
 941	limit = 4
 942	idx := tc.exits[0].AccountIdx
 943	path = fmt.Sprintf(
 944		"%s?accountIndex=%s&limit=%d&fromItem=",
 945		endpoint, idx, limit,
 946	)
 947	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 948	assert.NoError(t, err)
 949	idxExits := []exitAPI{}
 950	for i := 0; i < len(tc.exits); i++ {
 951		if tc.exits[i].AccountIdx[6:] == idx[6:] {
 952			idxExits = append(idxExits, tc.exits[i])
 953		}
 954	}
 955	assertExitAPIs(t, idxExits, fetchedExits)
 956	// batchNum
 957	fetchedExits = []exitAPI{}
 958	limit = 3
 959	batchNum := tc.exits[0].BatchNum
 960	path = fmt.Sprintf(
 961		"%s?batchNum=%d&limit=%d&fromItem=",
 962		endpoint, batchNum, limit,
 963	)
 964	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 965	assert.NoError(t, err)
 966	batchNumExits := []exitAPI{}
 967	for i := 0; i < len(tc.exits); i++ {
 968		if tc.exits[i].BatchNum == batchNum {
 969			batchNumExits = append(batchNumExits, tc.exits[i])
 970		}
 971	}
 972	assertExitAPIs(t, batchNumExits, fetchedExits)
 973	// Multiple filters
 974	fetchedExits = []exitAPI{}
 975	limit = 1
 976	path = fmt.Sprintf(
 977		"%s?batchNum=%d&tokeId=%d&limit=%d&fromItem=",
 978		endpoint, batchNum, tokenID, limit,
 979	)
 980	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 981	assert.NoError(t, err)
 982	mixedExits := []exitAPI{}
 983	flipedExits := []exitAPI{}
 984	for i := 0; i < len(tc.exits); i++ {
 985		if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
 986			mixedExits = append(mixedExits, tc.exits[i])
 987		}
 988		flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
 989	}
 990	assertExitAPIs(t, mixedExits, fetchedExits)
 991	// All, in reverse order
 992	fetchedExits = []exitAPI{}
 993	limit = 5
 994	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 995	err = doGoodReqPaginated(path, historydb.OrderDesc, &exitsAPI{}, appendIter)
 996	assert.NoError(t, err)
 997	assertExitAPIs(t, flipedExits, fetchedExits)
 998	// 400
 999	path = fmt.Sprintf(
1000		"%s?accountIndex=%s&hermezEthereumAddress=%s",
1001		endpoint, idx, tc.usrAddr,
1002	)
1003	err = doBadReq("GET", path, nil, 400)
1004	assert.NoError(t, err)
1005	path = fmt.Sprintf("%s?tokenId=X", endpoint)
1006	err = doBadReq("GET", path, nil, 400)
1007	assert.NoError(t, err)
1008	// 404
1009	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
1010	err = doBadReq("GET", path, nil, 404)
1011	assert.NoError(t, err)
1012	path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
1013	err = doBadReq("GET", path, nil, 404)
1014	assert.NoError(t, err)
1015}
1016
1017func TestGetExit(t *testing.T) {
1018	// Get all txs by their ID
1019	endpoint := apiURL + "exits/"
1020	fetchedExits := []exitAPI{}
1021	for _, exit := range tc.exits {
1022		fetchedExit := exitAPI{}
1023		assert.NoError(
1024			t, doGoodReq(
1025				"GET",
1026				fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
1027				nil, &fetchedExit,
1028			),
1029		)
1030		fetchedExits = append(fetchedExits, fetchedExit)
1031	}
1032	assertExitAPIs(t, tc.exits, fetchedExits)
1033	// 400
1034	err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
1035	assert.NoError(t, err)
1036	err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
1037	assert.NoError(t, err)
1038	// 404
1039	err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
1040	assert.NoError(t, err)
1041}
1042
1043func assertExitAPIs(t *testing.T, expected, actual []exitAPI) {
1044	require.Equal(t, len(expected), len(actual))
1045	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
1046		actual[i].ItemID = 0
1047		if expected[i].Token.USDUpdate == nil {
1048			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
1049		} else {
1050			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
1051			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
1052		}
1053		assert.Equal(t, expected[i], actual[i])
1054	}
1055}
1056
1057func TestGetToken(t *testing.T) {
1058	// Get all txs by their ID
1059	endpoint := apiURL + "tokens/"
1060	fetchedTokens := []tokenAPI{}
1061	for _, token := range tc.tokens {
1062		fetchedToken := tokenAPI{}
1063		assert.NoError(t, doGoodReq("GET", endpoint+strconv.Itoa(int(token.TokenID)), nil, &fetchedToken))
1064		fetchedTokens = append(fetchedTokens, fetchedToken)
1065	}
1066	assertTokensAPIs(t, tc.tokens, fetchedTokens)
1067}
1068
1069func TestGetTokens(t *testing.T) {
1070	endpoint := apiURL + "tokens"
1071	fetchedTokens := []tokenAPI{}
1072	appendIter := func(intr interface{}) {
1073		for i := 0; i < len(intr.(*tokensAPI).Tokens); i++ {
1074			tmp, err := copystructure.Copy(intr.(*tokensAPI).Tokens[i])
1075			if err != nil {
1076				panic(err)
1077			}
1078			fetchedTokens = append(fetchedTokens, tmp.(tokenAPI))
1079		}
1080	}
1081	// Get all (no filters)
1082	limit := 8
1083	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1084	err := doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1085	assert.NoError(t, err)
1086	assertTokensAPIs(t, tc.tokens, fetchedTokens)
1087
1088	// Get by tokenIds
1089	fetchedTokens = []tokenAPI{}
1090	limit = 7
1091	stringIds := strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID))
1092	path = fmt.Sprintf(
1093		"%s?ids=%s&limit=%d&fromItem=",
1094		endpoint, stringIds, limit,
1095	)
1096	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1097	assert.NoError(t, err)
1098	var tokensFiltered []tokenAPI
1099	tokensFiltered = append(tokensFiltered, tc.tokens[2])
1100	tokensFiltered = append(tokensFiltered, tc.tokens[5])
1101	tokensFiltered = append(tokensFiltered, tc.tokens[6])
1102	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1103
1104	// Get by symbols
1105	fetchedTokens = []tokenAPI{}
1106	limit = 7
1107	stringSymbols := tc.tokens[1].Symbol + "," + tc.tokens[3].Symbol
1108	path = fmt.Sprintf(
1109		"%s?symbols=%s&limit=%d&fromItem=",
1110		endpoint, stringSymbols, limit,
1111	)
1112	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1113	assert.NoError(t, err)
1114	tokensFiltered = nil
1115	tokensFiltered = append(tokensFiltered, tc.tokens[1])
1116	tokensFiltered = append(tokensFiltered, tc.tokens[3])
1117	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1118
1119	// Get by name
1120	fetchedTokens = []tokenAPI{}
1121	limit = 5
1122	stringName := tc.tokens[8].Name[4:5]
1123	path = fmt.Sprintf(
1124		"%s?name=%s&limit=%d&fromItem=",
1125		endpoint, stringName, limit,
1126	)
1127	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1128	assert.NoError(t, err)
1129	tokensFiltered = nil
1130	tokensFiltered = append(tokensFiltered, tc.tokens[8])
1131	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1132
1133	// Multiple filters
1134	fetchedTokens = []tokenAPI{}
1135	limit = 5
1136	stringSymbols = tc.tokens[2].Symbol + "," + tc.tokens[6].Symbol
1137	stringIds = strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID))
1138	path = fmt.Sprintf(
1139		"%s?symbols=%s&ids=%s&limit=%d&fromItem=",
1140		endpoint, stringSymbols, stringIds, limit,
1141	)
1142	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1143	assert.NoError(t, err)
1144
1145	tokensFiltered = nil
1146	tokensFiltered = append(tokensFiltered, tc.tokens[2])
1147	tokensFiltered = append(tokensFiltered, tc.tokens[6])
1148	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1149
1150	// All, in reverse order
1151	fetchedTokens = []tokenAPI{}
1152	limit = 5
1153	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1154	err = doGoodReqPaginated(path, historydb.OrderDesc, &tokensAPI{}, appendIter)
1155	assert.NoError(t, err)
1156	flipedTokens := []tokenAPI{}
1157	for i := 0; i < len(tc.tokens); i++ {
1158		flipedTokens = append(flipedTokens, tc.tokens[len(tc.tokens)-1-i])
1159	}
1160	assertTokensAPIs(t, flipedTokens, fetchedTokens)
1161}
1162
1163func assertTokensAPIs(t *testing.T, expected, actual []tokenAPI) {
1164	require.Equal(t, len(expected), len(actual))
1165	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
1166		actual[i].ItemID = 0
1167		if expected[i].USDUpdate == nil {
1168			assert.Equal(t, expected[i].USDUpdate, actual[i].USDUpdate)
1169		} else {
1170			assert.Equal(t, expected[i].USDUpdate.Unix(), actual[i].USDUpdate.Unix())
1171			expected[i].USDUpdate = actual[i].USDUpdate
1172		}
1173		assert.Equal(t, expected[i], actual[i])
1174	}
1175}
1176
1177func TestGetConfig(t *testing.T) {
1178	endpoint := apiURL + "config"
1179	var configTest configAPI
1180	assert.NoError(t, doGoodReq("GET", endpoint, nil, &configTest))
1181	assert.Equal(t, config, configTest)
1182	assert.Equal(t, cg, &configTest)
1183}
1184
1185func TestPoolTxs(t *testing.T) {
1186	// POST
1187	endpoint := apiURL + "transactions-pool"
1188	fetchedTxID := common.TxID{}
1189	for _, tx := range tc.poolTxsToSend {
1190		jsonTxBytes, err := json.Marshal(tx)
1191		assert.NoError(t, err)
1192		jsonTxReader := bytes.NewReader(jsonTxBytes)
1193		fmt.Println(string(jsonTxBytes))
1194		assert.NoError(
1195			t, doGoodReq(
1196				"POST",
1197				endpoint,
1198				jsonTxReader, &fetchedTxID,
1199			),
1200		)
1201		assert.Equal(t, tx.TxID, fetchedTxID)
1202	}
1203	// 400
1204	// Wrong signature
1205	badTx := tc.poolTxsToSend[0]
1206	badTx.FromIdx = "hez:foo:1000"
1207	jsonTxBytes, err := json.Marshal(badTx)
1208	assert.NoError(t, err)
1209	jsonTxReader := bytes.NewReader(jsonTxBytes)
1210	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1211	assert.NoError(t, err)
1212	// Wrong to
1213	badTx = tc.poolTxsToSend[0]
1214	ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
1215	badTx.ToEthAddr = &ethAddr
1216	badTx.ToIdx = nil
1217	jsonTxBytes, err = json.Marshal(badTx)
1218	assert.NoError(t, err)
1219	jsonTxReader = bytes.NewReader(jsonTxBytes)
1220	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1221	assert.NoError(t, err)
1222	// Wrong rq
1223	badTx = tc.poolTxsToSend[0]
1224	rqFromIdx := "hez:foo:30"
1225	badTx.RqFromIdx = &rqFromIdx
1226	jsonTxBytes, err = json.Marshal(badTx)
1227	assert.NoError(t, err)
1228	jsonTxReader = bytes.NewReader(jsonTxBytes)
1229	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1230	assert.NoError(t, err)
1231	// GET
1232	endpoint += "/"
1233	for _, tx := range tc.poolTxsToReceive {
1234		fetchedTx := sendPoolTx{}
1235		assert.NoError(
1236			t, doGoodReq(
1237				"GET",
1238				endpoint+tx.TxID.String(),
1239				nil, &fetchedTx,
1240			),
1241		)
1242		assertPoolTx(t, tx, fetchedTx)
1243	}
1244	// 400
1245	err = doBadReq("GET", endpoint+"0xG20000000156660000000090", nil, 400)
1246	assert.NoError(t, err)
1247	// 404
1248	err = doBadReq("GET", endpoint+"0x020000000156660000000090", nil, 404)
1249	assert.NoError(t, err)
1250}
1251
1252func assertPoolTx(t *testing.T, expected, actual sendPoolTx) {
1253	// state should be pending
1254	assert.Equal(t, common.PoolL2TxStatePending, actual.State)
1255	expected.State = actual.State
1256	// timestamp should be very close to now
1257	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
1258	expected.Timestamp = actual.Timestamp
1259	// token timestamp
1260	if expected.Token.USDUpdate == nil {
1261		assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
1262	} else {
1263		assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
1264		expected.Token.USDUpdate = actual.Token.USDUpdate
1265	}
1266	assert.Equal(t, expected, actual)
1267}
1268
1269func TestGetCoordinators(t *testing.T) {
1270	endpoint := apiURL + "coordinators"
1271	fetchedCoordinators := []coordinatorAPI{}
1272
1273	appendIter := func(intr interface{}) {
1274		for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ {
1275			tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i])
1276			if err != nil {
1277				panic(err)
1278			}
1279			fetchedCoordinators = append(fetchedCoordinators, tmp.(coordinatorAPI))
1280		}
1281	}
1282
1283	limit := 5
1284
1285	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1286	err := doGoodReqPaginated(path, historydb.OrderAsc, &coordinatorsAPI{}, appendIter)
1287	assert.NoError(t, err)
1288	assert.Equal(t, tc.coordinators, fetchedCoordinators)
1289
1290	// Reverse Order
1291	reversedCoordinators := []coordinatorAPI{}
1292	appendIter = func(intr interface{}) {
1293		for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ {
1294			tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i])
1295			if err != nil {
1296				panic(err)
1297			}
1298			reversedCoordinators = append(reversedCoordinators, tmp.(coordinatorAPI))
1299		}
1300	}
1301	err = doGoodReqPaginated(path, historydb.OrderDesc, &coordinatorsAPI{}, appendIter)
1302	assert.NoError(t, err)
1303	for i := 0; i < len(fetchedCoordinators); i++ {
1304		assert.Equal(t, reversedCoordinators[i], fetchedCoordinators[len(fetchedCoordinators)-1-i])
1305	}
1306
1307	// Test GetCoordinator
1308	path = fmt.Sprintf("%s/%s", endpoint, fetchedCoordinators[2].Forger.String())
1309	coordinator := coordinatorAPI{}
1310	assert.NoError(t, doGoodReq("GET", path, nil, &coordinator))
1311	assert.Equal(t, fetchedCoordinators[2], coordinator)
1312	// 400
1313	path = fmt.Sprintf("%s/0x001", endpoint)
1314	err = doBadReq("GET", path, nil, 400)
1315	assert.NoError(t, err)
1316	// 404
1317	path = fmt.Sprintf("%s/0xaa942cfcd25ad4d90a62358b0dd84f33b398262a", endpoint)
1318	err = doBadReq("GET", path, nil, 404)
1319	assert.NoError(t, err)
1320}
1321
1322func doGoodReqPaginated(
1323	path, order string,
1324	iterStruct db.Paginationer,
1325	appendIter func(res interface{}),
1326) error {
1327	next := 0
1328	for {
1329		// Call API to get this iteration items
1330		iterPath := path
1331		if next == 0 && order == historydb.OrderDesc {
1332			// Fetch first item in reverse order
1333			iterPath += "99999"
1334		} else {
1335			// Fetch from next item or 0 if it's ascending order
1336			iterPath += strconv.Itoa(next)
1337		}
1338		if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
1339			return err
1340		}
1341		appendIter(iterStruct)
1342		// Keep iterating?
1343		pag := iterStruct.GetPagination()
1344		if order == historydb.OrderAsc {
1345			if pag.LastReturnedItem == pag.LastItem { // No
1346				break
1347			} else { // Yes
1348				next = pag.LastReturnedItem + 1
1349			}
1350		} else {
1351			if pag.FirstReturnedItem == pag.FirstItem { // No
1352				break
1353			} else { // Yes
1354				next = pag.FirstReturnedItem - 1
1355			}
1356		}
1357	}
1358	return nil
1359}
1360
1361func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
1362	ctx := context.Background()
1363	client := &http.Client{}
1364	httpReq, err := http.NewRequest(method, path, reqBody)
1365	if err != nil {
1366		return err
1367	}
1368	if reqBody != nil {
1369		httpReq.Header.Add("Content-Type", "application/json")
1370	}
1371	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1372	if err != nil {
1373		return err
1374	}
1375	// Validate request against swagger spec
1376	requestValidationInput := &swagger.RequestValidationInput{
1377		Request:    httpReq,
1378		PathParams: pathParams,
1379		Route:      route,
1380	}
1381	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1382		return err
1383	}
1384	// Do API call
1385	resp, err := client.Do(httpReq)
1386	if err != nil {
1387		return err
1388	}
1389	if resp.Body == nil {
1390		return errors.New("Nil body")
1391	}
1392	//nolint
1393	defer resp.Body.Close()
1394	body, err := ioutil.ReadAll(resp.Body)
1395	if err != nil {
1396		return err
1397	}
1398	if resp.StatusCode != 200 {
1399		return fmt.Errorf("%d response: %s", resp.StatusCode, string(body))
1400	}
1401	// Unmarshal body into return struct
1402	if err := json.Unmarshal(body, returnStruct); err != nil {
1403		return err
1404	}
1405	// Validate response against swagger spec
1406	responseValidationInput := &swagger.ResponseValidationInput{
1407		RequestValidationInput: requestValidationInput,
1408		Status:                 resp.StatusCode,
1409		Header:                 resp.Header,
1410	}
1411	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1412	return swagger.ValidateResponse(ctx, responseValidationInput)
1413}
1414
1415func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
1416	ctx := context.Background()
1417	client := &http.Client{}
1418	httpReq, _ := http.NewRequest(method, path, reqBody)
1419	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1420	if err != nil {
1421		return err
1422	}
1423	// Validate request against swagger spec
1424	requestValidationInput := &swagger.RequestValidationInput{
1425		Request:    httpReq,
1426		PathParams: pathParams,
1427		Route:      route,
1428	}
1429	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1430		if expectedResponseCode != 400 {
1431			return err
1432		}
1433		log.Warn("The request does not match the API spec")
1434	}
1435	// Do API call
1436	resp, err := client.Do(httpReq)
1437	if err != nil {
1438		return err
1439	}
1440	if resp.Body == nil {
1441		return errors.New("Nil body")
1442	}
1443	//nolint
1444	defer resp.Body.Close()
1445	body, err := ioutil.ReadAll(resp.Body)
1446	if err != nil {
1447		return err
1448	}
1449	if resp.StatusCode != expectedResponseCode {
1450		return fmt.Errorf("Unexpected response code: %d. Body: %s", resp.StatusCode, string(body))
1451	}
1452	// Validate response against swagger spec
1453	responseValidationInput := &swagger.ResponseValidationInput{
1454		RequestValidationInput: requestValidationInput,
1455		Status:                 resp.StatusCode,
1456		Header:                 resp.Header,
1457	}
1458	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1459	return swagger.ValidateResponse(ctx, responseValidationInput)
1460}