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	// Fake server
 658	if os.Getenv("FAKE_SERVER") == "yes" {
 659		for {
 660			log.Info("Running fake server until ^C is received")
 661			time.Sleep(10 * time.Second)
 662		}
 663	}
 664	// Run tests
 665	result := m.Run()
 666	// Stop server
 667	if err := server.Shutdown(context.Background()); err != nil {
 668		panic(err)
 669	}
 670	if err := database.Close(); err != nil {
 671		panic(err)
 672	}
 673	if err := os.RemoveAll(dir); err != nil {
 674		panic(err)
 675	}
 676	os.Exit(result)
 677}
 678
 679func TestGetHistoryTxs(t *testing.T) {
 680	endpoint := apiURL + "transactions-history"
 681	fetchedTxs := []historyTxAPI{}
 682	appendIter := func(intr interface{}) {
 683		for i := 0; i < len(intr.(*historyTxsAPI).Txs); i++ {
 684			tmp, err := copystructure.Copy(intr.(*historyTxsAPI).Txs[i])
 685			if err != nil {
 686				panic(err)
 687			}
 688			fetchedTxs = append(fetchedTxs, tmp.(historyTxAPI))
 689		}
 690	}
 691	// Get all (no filters)
 692	limit := 8
 693	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 694	err := doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 695	assert.NoError(t, err)
 696	assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
 697	// Uncomment once tx generation for tests is fixed
 698	// // Get by ethAddr
 699	// fetchedTxs = []historyTxAPI{}
 700	// limit = 7
 701	// path = fmt.Sprintf(
 702	// 	"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
 703	// 	endpoint, tc.usrAddr, limit,
 704	// )
 705	// err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 706	// assert.NoError(t, err)
 707	// assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
 708	// // Get by bjj
 709	// fetchedTxs = []historyTxAPI{}
 710	// limit = 6
 711	// path = fmt.Sprintf(
 712	// 	"%s?BJJ=%s&limit=%d&fromItem=",
 713	// 	endpoint, tc.usrBjj, limit,
 714	// )
 715	// err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 716	// assert.NoError(t, err)
 717	// assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
 718	// Get by tokenID
 719	fetchedTxs = []historyTxAPI{}
 720	limit = 5
 721	tokenID := tc.allTxs[0].Token.TokenID
 722	path = fmt.Sprintf(
 723		"%s?tokenId=%d&limit=%d&fromItem=",
 724		endpoint, tokenID, limit,
 725	)
 726	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 727	assert.NoError(t, err)
 728	tokenIDTxs := []historyTxAPI{}
 729	for i := 0; i < len(tc.allTxs); i++ {
 730		if tc.allTxs[i].Token.TokenID == tokenID {
 731			tokenIDTxs = append(tokenIDTxs, tc.allTxs[i])
 732		}
 733	}
 734	assertHistoryTxAPIs(t, tokenIDTxs, fetchedTxs)
 735	// idx
 736	fetchedTxs = []historyTxAPI{}
 737	limit = 4
 738	idx := tc.allTxs[0].ToIdx
 739	path = fmt.Sprintf(
 740		"%s?accountIndex=%s&limit=%d&fromItem=",
 741		endpoint, idx, limit,
 742	)
 743	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 744	assert.NoError(t, err)
 745	idxTxs := []historyTxAPI{}
 746	for i := 0; i < len(tc.allTxs); i++ {
 747		if (tc.allTxs[i].FromIdx != nil && (*tc.allTxs[i].FromIdx)[6:] == idx[6:]) ||
 748			tc.allTxs[i].ToIdx[6:] == idx[6:] {
 749			idxTxs = append(idxTxs, tc.allTxs[i])
 750		}
 751	}
 752	assertHistoryTxAPIs(t, idxTxs, fetchedTxs)
 753	// batchNum
 754	fetchedTxs = []historyTxAPI{}
 755	limit = 3
 756	batchNum := tc.allTxs[0].BatchNum
 757	path = fmt.Sprintf(
 758		"%s?batchNum=%d&limit=%d&fromItem=",
 759		endpoint, *batchNum, limit,
 760	)
 761	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 762	assert.NoError(t, err)
 763	batchNumTxs := []historyTxAPI{}
 764	for i := 0; i < len(tc.allTxs); i++ {
 765		if tc.allTxs[i].BatchNum != nil &&
 766			*tc.allTxs[i].BatchNum == *batchNum {
 767			batchNumTxs = append(batchNumTxs, tc.allTxs[i])
 768		}
 769	}
 770	assertHistoryTxAPIs(t, batchNumTxs, fetchedTxs)
 771	// type
 772	txTypes := []common.TxType{
 773		// Uncomment once test gen is fixed
 774		// common.TxTypeExit,
 775		// common.TxTypeTransfer,
 776		// common.TxTypeDeposit,
 777		common.TxTypeCreateAccountDeposit,
 778		// common.TxTypeCreateAccountDepositTransfer,
 779		// common.TxTypeDepositTransfer,
 780		common.TxTypeForceTransfer,
 781		// common.TxTypeForceExit,
 782		// common.TxTypeTransferToEthAddr,
 783		// common.TxTypeTransferToBJJ,
 784	}
 785	for _, txType := range txTypes {
 786		fetchedTxs = []historyTxAPI{}
 787		limit = 2
 788		path = fmt.Sprintf(
 789			"%s?type=%s&limit=%d&fromItem=",
 790			endpoint, txType, limit,
 791		)
 792		err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 793		assert.NoError(t, err)
 794		txTypeTxs := []historyTxAPI{}
 795		for i := 0; i < len(tc.allTxs); i++ {
 796			if tc.allTxs[i].Type == txType {
 797				txTypeTxs = append(txTypeTxs, tc.allTxs[i])
 798			}
 799		}
 800		assertHistoryTxAPIs(t, txTypeTxs, fetchedTxs)
 801	}
 802	// Multiple filters
 803	fetchedTxs = []historyTxAPI{}
 804	limit = 1
 805	path = fmt.Sprintf(
 806		"%s?batchNum=%d&tokenId=%d&limit=%d&fromItem=",
 807		endpoint, *batchNum, tokenID, limit,
 808	)
 809	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 810	assert.NoError(t, err)
 811	mixedTxs := []historyTxAPI{}
 812	for i := 0; i < len(tc.allTxs); i++ {
 813		if tc.allTxs[i].BatchNum != nil {
 814			if *tc.allTxs[i].BatchNum == *batchNum && tc.allTxs[i].Token.TokenID == tokenID {
 815				mixedTxs = append(mixedTxs, tc.allTxs[i])
 816			}
 817		}
 818	}
 819	assertHistoryTxAPIs(t, mixedTxs, fetchedTxs)
 820	// All, in reverse order
 821	fetchedTxs = []historyTxAPI{}
 822	limit = 5
 823	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 824	err = doGoodReqPaginated(path, historydb.OrderDesc, &historyTxsAPI{}, appendIter)
 825	assert.NoError(t, err)
 826	flipedTxs := []historyTxAPI{}
 827	for i := 0; i < len(tc.allTxs); i++ {
 828		flipedTxs = append(flipedTxs, tc.allTxs[len(tc.allTxs)-1-i])
 829	}
 830	assertHistoryTxAPIs(t, flipedTxs, fetchedTxs)
 831	// 400
 832	path = fmt.Sprintf(
 833		"%s?accountIndex=%s&hermezEthereumAddress=%s",
 834		endpoint, idx, tc.usrAddr,
 835	)
 836	err = doBadReq("GET", path, nil, 400)
 837	assert.NoError(t, err)
 838	path = fmt.Sprintf("%s?tokenId=X", endpoint)
 839	err = doBadReq("GET", path, nil, 400)
 840	assert.NoError(t, err)
 841	// 404
 842	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
 843	err = doBadReq("GET", path, nil, 404)
 844	assert.NoError(t, err)
 845	path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
 846	err = doBadReq("GET", path, nil, 404)
 847	assert.NoError(t, err)
 848}
 849
 850func TestGetHistoryTx(t *testing.T) {
 851	// Get all txs by their ID
 852	endpoint := apiURL + "transactions-history/"
 853	fetchedTxs := []historyTxAPI{}
 854	for _, tx := range tc.allTxs {
 855		fetchedTx := historyTxAPI{}
 856		assert.NoError(t, doGoodReq("GET", endpoint+tx.TxID.String(), nil, &fetchedTx))
 857		fetchedTxs = append(fetchedTxs, fetchedTx)
 858	}
 859	assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
 860	// 400
 861	err := doBadReq("GET", endpoint+"0x001", nil, 400)
 862	assert.NoError(t, err)
 863	// 404
 864	err = doBadReq("GET", endpoint+"0x00000000000001e240004700", nil, 404)
 865	assert.NoError(t, err)
 866}
 867
 868func assertHistoryTxAPIs(t *testing.T, expected, actual []historyTxAPI) {
 869	require.Equal(t, len(expected), len(actual))
 870	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
 871		actual[i].ItemID = 0
 872		assert.Equal(t, expected[i].Timestamp.Unix(), actual[i].Timestamp.Unix())
 873		expected[i].Timestamp = actual[i].Timestamp
 874		if expected[i].Token.USDUpdate == nil {
 875			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
 876		} else {
 877			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
 878			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
 879		}
 880		test.AssertUSD(t, expected[i].HistoricUSD, actual[i].HistoricUSD)
 881		if expected[i].L2Info != nil {
 882			test.AssertUSD(t, expected[i].L2Info.HistoricFeeUSD, actual[i].L2Info.HistoricFeeUSD)
 883		} else {
 884			test.AssertUSD(t, expected[i].L1Info.HistoricLoadAmountUSD, actual[i].L1Info.HistoricLoadAmountUSD)
 885		}
 886		assert.Equal(t, expected[i], actual[i])
 887	}
 888}
 889
 890func TestGetExits(t *testing.T) {
 891	endpoint := apiURL + "exits"
 892	fetchedExits := []exitAPI{}
 893	appendIter := func(intr interface{}) {
 894		for i := 0; i < len(intr.(*exitsAPI).Exits); i++ {
 895			tmp, err := copystructure.Copy(intr.(*exitsAPI).Exits[i])
 896			if err != nil {
 897				panic(err)
 898			}
 899			fetchedExits = append(fetchedExits, tmp.(exitAPI))
 900		}
 901	}
 902	// Get all (no filters)
 903	limit := 8
 904	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 905	err := doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 906	assert.NoError(t, err)
 907	assertExitAPIs(t, tc.exits, fetchedExits)
 908
 909	// Get by ethAddr
 910	fetchedExits = []exitAPI{}
 911	limit = 7
 912	path = fmt.Sprintf(
 913		"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
 914		endpoint, tc.usrAddr, limit,
 915	)
 916	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 917	assert.NoError(t, err)
 918	assertExitAPIs(t, tc.usrExits, fetchedExits)
 919	// Get by bjj
 920	fetchedExits = []exitAPI{}
 921	limit = 6
 922	path = fmt.Sprintf(
 923		"%s?BJJ=%s&limit=%d&fromItem=",
 924		endpoint, tc.usrBjj, limit,
 925	)
 926	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 927	assert.NoError(t, err)
 928	assertExitAPIs(t, tc.usrExits, fetchedExits)
 929	// Get by tokenID
 930	fetchedExits = []exitAPI{}
 931	limit = 5
 932	tokenID := tc.exits[0].Token.TokenID
 933	path = fmt.Sprintf(
 934		"%s?tokenId=%d&limit=%d&fromItem=",
 935		endpoint, tokenID, limit,
 936	)
 937	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 938	assert.NoError(t, err)
 939	tokenIDExits := []exitAPI{}
 940	for i := 0; i < len(tc.exits); i++ {
 941		if tc.exits[i].Token.TokenID == tokenID {
 942			tokenIDExits = append(tokenIDExits, tc.exits[i])
 943		}
 944	}
 945	assertExitAPIs(t, tokenIDExits, fetchedExits)
 946	// idx
 947	fetchedExits = []exitAPI{}
 948	limit = 4
 949	idx := tc.exits[0].AccountIdx
 950	path = fmt.Sprintf(
 951		"%s?accountIndex=%s&limit=%d&fromItem=",
 952		endpoint, idx, limit,
 953	)
 954	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 955	assert.NoError(t, err)
 956	idxExits := []exitAPI{}
 957	for i := 0; i < len(tc.exits); i++ {
 958		if tc.exits[i].AccountIdx[6:] == idx[6:] {
 959			idxExits = append(idxExits, tc.exits[i])
 960		}
 961	}
 962	assertExitAPIs(t, idxExits, fetchedExits)
 963	// batchNum
 964	fetchedExits = []exitAPI{}
 965	limit = 3
 966	batchNum := tc.exits[0].BatchNum
 967	path = fmt.Sprintf(
 968		"%s?batchNum=%d&limit=%d&fromItem=",
 969		endpoint, batchNum, limit,
 970	)
 971	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 972	assert.NoError(t, err)
 973	batchNumExits := []exitAPI{}
 974	for i := 0; i < len(tc.exits); i++ {
 975		if tc.exits[i].BatchNum == batchNum {
 976			batchNumExits = append(batchNumExits, tc.exits[i])
 977		}
 978	}
 979	assertExitAPIs(t, batchNumExits, fetchedExits)
 980	// Multiple filters
 981	fetchedExits = []exitAPI{}
 982	limit = 1
 983	path = fmt.Sprintf(
 984		"%s?batchNum=%d&tokeId=%d&limit=%d&fromItem=",
 985		endpoint, batchNum, tokenID, limit,
 986	)
 987	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 988	assert.NoError(t, err)
 989	mixedExits := []exitAPI{}
 990	flipedExits := []exitAPI{}
 991	for i := 0; i < len(tc.exits); i++ {
 992		if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
 993			mixedExits = append(mixedExits, tc.exits[i])
 994		}
 995		flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
 996	}
 997	assertExitAPIs(t, mixedExits, fetchedExits)
 998	// All, in reverse order
 999	fetchedExits = []exitAPI{}
1000	limit = 5
1001	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1002	err = doGoodReqPaginated(path, historydb.OrderDesc, &exitsAPI{}, appendIter)
1003	assert.NoError(t, err)
1004	assertExitAPIs(t, flipedExits, fetchedExits)
1005	// 400
1006	path = fmt.Sprintf(
1007		"%s?accountIndex=%s&hermezEthereumAddress=%s",
1008		endpoint, idx, tc.usrAddr,
1009	)
1010	err = doBadReq("GET", path, nil, 400)
1011	assert.NoError(t, err)
1012	path = fmt.Sprintf("%s?tokenId=X", endpoint)
1013	err = doBadReq("GET", path, nil, 400)
1014	assert.NoError(t, err)
1015	// 404
1016	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
1017	err = doBadReq("GET", path, nil, 404)
1018	assert.NoError(t, err)
1019	path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
1020	err = doBadReq("GET", path, nil, 404)
1021	assert.NoError(t, err)
1022}
1023
1024func TestGetExit(t *testing.T) {
1025	// Get all txs by their ID
1026	endpoint := apiURL + "exits/"
1027	fetchedExits := []exitAPI{}
1028	for _, exit := range tc.exits {
1029		fetchedExit := exitAPI{}
1030		assert.NoError(
1031			t, doGoodReq(
1032				"GET",
1033				fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
1034				nil, &fetchedExit,
1035			),
1036		)
1037		fetchedExits = append(fetchedExits, fetchedExit)
1038	}
1039	assertExitAPIs(t, tc.exits, fetchedExits)
1040	// 400
1041	err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
1042	assert.NoError(t, err)
1043	err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
1044	assert.NoError(t, err)
1045	// 404
1046	err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
1047	assert.NoError(t, err)
1048}
1049
1050func assertExitAPIs(t *testing.T, expected, actual []exitAPI) {
1051	require.Equal(t, len(expected), len(actual))
1052	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
1053		actual[i].ItemID = 0
1054		if expected[i].Token.USDUpdate == nil {
1055			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
1056		} else {
1057			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
1058			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
1059		}
1060		assert.Equal(t, expected[i], actual[i])
1061	}
1062}
1063
1064func TestGetToken(t *testing.T) {
1065	// Get all txs by their ID
1066	endpoint := apiURL + "tokens/"
1067	fetchedTokens := []tokenAPI{}
1068	for _, token := range tc.tokens {
1069		fetchedToken := tokenAPI{}
1070		assert.NoError(t, doGoodReq("GET", endpoint+strconv.Itoa(int(token.TokenID)), nil, &fetchedToken))
1071		fetchedTokens = append(fetchedTokens, fetchedToken)
1072	}
1073	assertTokensAPIs(t, tc.tokens, fetchedTokens)
1074}
1075
1076func TestGetTokens(t *testing.T) {
1077	endpoint := apiURL + "tokens"
1078	fetchedTokens := []tokenAPI{}
1079	appendIter := func(intr interface{}) {
1080		for i := 0; i < len(intr.(*tokensAPI).Tokens); i++ {
1081			tmp, err := copystructure.Copy(intr.(*tokensAPI).Tokens[i])
1082			if err != nil {
1083				panic(err)
1084			}
1085			fetchedTokens = append(fetchedTokens, tmp.(tokenAPI))
1086		}
1087	}
1088	// Get all (no filters)
1089	limit := 8
1090	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1091	err := doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1092	assert.NoError(t, err)
1093	assertTokensAPIs(t, tc.tokens, fetchedTokens)
1094
1095	// Get by tokenIds
1096	fetchedTokens = []tokenAPI{}
1097	limit = 7
1098	stringIds := strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID))
1099	path = fmt.Sprintf(
1100		"%s?ids=%s&limit=%d&fromItem=",
1101		endpoint, stringIds, limit,
1102	)
1103	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1104	assert.NoError(t, err)
1105	var tokensFiltered []tokenAPI
1106	tokensFiltered = append(tokensFiltered, tc.tokens[2])
1107	tokensFiltered = append(tokensFiltered, tc.tokens[5])
1108	tokensFiltered = append(tokensFiltered, tc.tokens[6])
1109	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1110
1111	// Get by symbols
1112	fetchedTokens = []tokenAPI{}
1113	limit = 7
1114	stringSymbols := tc.tokens[1].Symbol + "," + tc.tokens[3].Symbol
1115	path = fmt.Sprintf(
1116		"%s?symbols=%s&limit=%d&fromItem=",
1117		endpoint, stringSymbols, limit,
1118	)
1119	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1120	assert.NoError(t, err)
1121	tokensFiltered = nil
1122	tokensFiltered = append(tokensFiltered, tc.tokens[1])
1123	tokensFiltered = append(tokensFiltered, tc.tokens[3])
1124	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1125
1126	// Get by name
1127	fetchedTokens = []tokenAPI{}
1128	limit = 5
1129	stringName := tc.tokens[8].Name[4:5]
1130	path = fmt.Sprintf(
1131		"%s?name=%s&limit=%d&fromItem=",
1132		endpoint, stringName, limit,
1133	)
1134	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1135	assert.NoError(t, err)
1136	tokensFiltered = nil
1137	tokensFiltered = append(tokensFiltered, tc.tokens[8])
1138	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1139
1140	// Multiple filters
1141	fetchedTokens = []tokenAPI{}
1142	limit = 5
1143	stringSymbols = tc.tokens[2].Symbol + "," + tc.tokens[6].Symbol
1144	stringIds = strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID))
1145	path = fmt.Sprintf(
1146		"%s?symbols=%s&ids=%s&limit=%d&fromItem=",
1147		endpoint, stringSymbols, stringIds, limit,
1148	)
1149	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1150	assert.NoError(t, err)
1151
1152	tokensFiltered = nil
1153	tokensFiltered = append(tokensFiltered, tc.tokens[2])
1154	tokensFiltered = append(tokensFiltered, tc.tokens[6])
1155	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1156
1157	// All, in reverse order
1158	fetchedTokens = []tokenAPI{}
1159	limit = 5
1160	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1161	err = doGoodReqPaginated(path, historydb.OrderDesc, &tokensAPI{}, appendIter)
1162	assert.NoError(t, err)
1163	flipedTokens := []tokenAPI{}
1164	for i := 0; i < len(tc.tokens); i++ {
1165		flipedTokens = append(flipedTokens, tc.tokens[len(tc.tokens)-1-i])
1166	}
1167	assertTokensAPIs(t, flipedTokens, fetchedTokens)
1168}
1169
1170func assertTokensAPIs(t *testing.T, expected, actual []tokenAPI) {
1171	require.Equal(t, len(expected), len(actual))
1172	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
1173		actual[i].ItemID = 0
1174		if expected[i].USDUpdate == nil {
1175			assert.Equal(t, expected[i].USDUpdate, actual[i].USDUpdate)
1176		} else {
1177			assert.Equal(t, expected[i].USDUpdate.Unix(), actual[i].USDUpdate.Unix())
1178			expected[i].USDUpdate = actual[i].USDUpdate
1179		}
1180		assert.Equal(t, expected[i], actual[i])
1181	}
1182}
1183
1184func TestGetConfig(t *testing.T) {
1185	endpoint := apiURL + "config"
1186	var configTest configAPI
1187	assert.NoError(t, doGoodReq("GET", endpoint, nil, &configTest))
1188	assert.Equal(t, config, configTest)
1189	assert.Equal(t, cg, &configTest)
1190}
1191
1192func TestPoolTxs(t *testing.T) {
1193	// POST
1194	endpoint := apiURL + "transactions-pool"
1195	fetchedTxID := common.TxID{}
1196	for _, tx := range tc.poolTxsToSend {
1197		jsonTxBytes, err := json.Marshal(tx)
1198		assert.NoError(t, err)
1199		jsonTxReader := bytes.NewReader(jsonTxBytes)
1200		fmt.Println(string(jsonTxBytes))
1201		assert.NoError(
1202			t, doGoodReq(
1203				"POST",
1204				endpoint,
1205				jsonTxReader, &fetchedTxID,
1206			),
1207		)
1208		assert.Equal(t, tx.TxID, fetchedTxID)
1209	}
1210	// 400
1211	// Wrong signature
1212	badTx := tc.poolTxsToSend[0]
1213	badTx.FromIdx = "hez:foo:1000"
1214	jsonTxBytes, err := json.Marshal(badTx)
1215	assert.NoError(t, err)
1216	jsonTxReader := bytes.NewReader(jsonTxBytes)
1217	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1218	assert.NoError(t, err)
1219	// Wrong to
1220	badTx = tc.poolTxsToSend[0]
1221	ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
1222	badTx.ToEthAddr = &ethAddr
1223	badTx.ToIdx = nil
1224	jsonTxBytes, err = json.Marshal(badTx)
1225	assert.NoError(t, err)
1226	jsonTxReader = bytes.NewReader(jsonTxBytes)
1227	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1228	assert.NoError(t, err)
1229	// Wrong rq
1230	badTx = tc.poolTxsToSend[0]
1231	rqFromIdx := "hez:foo:30"
1232	badTx.RqFromIdx = &rqFromIdx
1233	jsonTxBytes, err = json.Marshal(badTx)
1234	assert.NoError(t, err)
1235	jsonTxReader = bytes.NewReader(jsonTxBytes)
1236	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1237	assert.NoError(t, err)
1238	// GET
1239	endpoint += "/"
1240	for _, tx := range tc.poolTxsToReceive {
1241		fetchedTx := sendPoolTx{}
1242		assert.NoError(
1243			t, doGoodReq(
1244				"GET",
1245				endpoint+tx.TxID.String(),
1246				nil, &fetchedTx,
1247			),
1248		)
1249		assertPoolTx(t, tx, fetchedTx)
1250	}
1251	// 400
1252	err = doBadReq("GET", endpoint+"0xG20000000156660000000090", nil, 400)
1253	assert.NoError(t, err)
1254	// 404
1255	err = doBadReq("GET", endpoint+"0x020000000156660000000090", nil, 404)
1256	assert.NoError(t, err)
1257}
1258
1259func assertPoolTx(t *testing.T, expected, actual sendPoolTx) {
1260	// state should be pending
1261	assert.Equal(t, common.PoolL2TxStatePending, actual.State)
1262	expected.State = actual.State
1263	// timestamp should be very close to now
1264	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
1265	expected.Timestamp = actual.Timestamp
1266	// token timestamp
1267	if expected.Token.USDUpdate == nil {
1268		assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
1269	} else {
1270		assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
1271		expected.Token.USDUpdate = actual.Token.USDUpdate
1272	}
1273	assert.Equal(t, expected, actual)
1274}
1275
1276func TestGetCoordinators(t *testing.T) {
1277	endpoint := apiURL + "coordinators"
1278	fetchedCoordinators := []coordinatorAPI{}
1279
1280	appendIter := func(intr interface{}) {
1281		for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ {
1282			tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i])
1283			if err != nil {
1284				panic(err)
1285			}
1286			fetchedCoordinators = append(fetchedCoordinators, tmp.(coordinatorAPI))
1287		}
1288	}
1289
1290	limit := 5
1291
1292	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1293	err := doGoodReqPaginated(path, historydb.OrderAsc, &coordinatorsAPI{}, appendIter)
1294	assert.NoError(t, err)
1295	assert.Equal(t, tc.coordinators, fetchedCoordinators)
1296
1297	// Reverse Order
1298	reversedCoordinators := []coordinatorAPI{}
1299	appendIter = func(intr interface{}) {
1300		for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ {
1301			tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i])
1302			if err != nil {
1303				panic(err)
1304			}
1305			reversedCoordinators = append(reversedCoordinators, tmp.(coordinatorAPI))
1306		}
1307	}
1308	err = doGoodReqPaginated(path, historydb.OrderDesc, &coordinatorsAPI{}, appendIter)
1309	assert.NoError(t, err)
1310	for i := 0; i < len(fetchedCoordinators); i++ {
1311		assert.Equal(t, reversedCoordinators[i], fetchedCoordinators[len(fetchedCoordinators)-1-i])
1312	}
1313
1314	// Test GetCoordinator
1315	path = fmt.Sprintf("%s/%s", endpoint, fetchedCoordinators[2].Forger.String())
1316	coordinator := coordinatorAPI{}
1317	assert.NoError(t, doGoodReq("GET", path, nil, &coordinator))
1318	assert.Equal(t, fetchedCoordinators[2], coordinator)
1319	// 400
1320	path = fmt.Sprintf("%s/0x001", endpoint)
1321	err = doBadReq("GET", path, nil, 400)
1322	assert.NoError(t, err)
1323	// 404
1324	path = fmt.Sprintf("%s/0xaa942cfcd25ad4d90a62358b0dd84f33b398262a", endpoint)
1325	err = doBadReq("GET", path, nil, 404)
1326	assert.NoError(t, err)
1327}
1328
1329func doGoodReqPaginated(
1330	path, order string,
1331	iterStruct db.Paginationer,
1332	appendIter func(res interface{}),
1333) error {
1334	next := 0
1335	for {
1336		// Call API to get this iteration items
1337		iterPath := path
1338		if next == 0 && order == historydb.OrderDesc {
1339			// Fetch first item in reverse order
1340			iterPath += "99999"
1341		} else {
1342			// Fetch from next item or 0 if it's ascending order
1343			iterPath += strconv.Itoa(next)
1344		}
1345		if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
1346			return err
1347		}
1348		appendIter(iterStruct)
1349		// Keep iterating?
1350		pag := iterStruct.GetPagination()
1351		if order == historydb.OrderAsc {
1352			if pag.LastReturnedItem == pag.LastItem { // No
1353				break
1354			} else { // Yes
1355				next = pag.LastReturnedItem + 1
1356			}
1357		} else {
1358			if pag.FirstReturnedItem == pag.FirstItem { // No
1359				break
1360			} else { // Yes
1361				next = pag.FirstReturnedItem - 1
1362			}
1363		}
1364	}
1365	return nil
1366}
1367
1368func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
1369	ctx := context.Background()
1370	client := &http.Client{}
1371	httpReq, err := http.NewRequest(method, path, reqBody)
1372	if err != nil {
1373		return err
1374	}
1375	if reqBody != nil {
1376		httpReq.Header.Add("Content-Type", "application/json")
1377	}
1378	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1379	if err != nil {
1380		return err
1381	}
1382	// Validate request against swagger spec
1383	requestValidationInput := &swagger.RequestValidationInput{
1384		Request:    httpReq,
1385		PathParams: pathParams,
1386		Route:      route,
1387	}
1388	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1389		return err
1390	}
1391	// Do API call
1392	resp, err := client.Do(httpReq)
1393	if err != nil {
1394		return err
1395	}
1396	if resp.Body == nil {
1397		return errors.New("Nil body")
1398	}
1399	//nolint
1400	defer resp.Body.Close()
1401	body, err := ioutil.ReadAll(resp.Body)
1402	if err != nil {
1403		return err
1404	}
1405	if resp.StatusCode != 200 {
1406		return fmt.Errorf("%d response: %s", resp.StatusCode, string(body))
1407	}
1408	// Unmarshal body into return struct
1409	if err := json.Unmarshal(body, returnStruct); err != nil {
1410		return err
1411	}
1412	// Validate response against swagger spec
1413	responseValidationInput := &swagger.ResponseValidationInput{
1414		RequestValidationInput: requestValidationInput,
1415		Status:                 resp.StatusCode,
1416		Header:                 resp.Header,
1417	}
1418	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1419	return swagger.ValidateResponse(ctx, responseValidationInput)
1420}
1421
1422func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
1423	ctx := context.Background()
1424	client := &http.Client{}
1425	httpReq, _ := http.NewRequest(method, path, reqBody)
1426	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1427	if err != nil {
1428		return err
1429	}
1430	// Validate request against swagger spec
1431	requestValidationInput := &swagger.RequestValidationInput{
1432		Request:    httpReq,
1433		PathParams: pathParams,
1434		Route:      route,
1435	}
1436	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1437		if expectedResponseCode != 400 {
1438			return err
1439		}
1440		log.Warn("The request does not match the API spec")
1441	}
1442	// Do API call
1443	resp, err := client.Do(httpReq)
1444	if err != nil {
1445		return err
1446	}
1447	if resp.Body == nil {
1448		return errors.New("Nil body")
1449	}
1450	//nolint
1451	defer resp.Body.Close()
1452	body, err := ioutil.ReadAll(resp.Body)
1453	if err != nil {
1454		return err
1455	}
1456	if resp.StatusCode != expectedResponseCode {
1457		return fmt.Errorf("Unexpected response code: %d. Body: %s", resp.StatusCode, string(body))
1458	}
1459	// Validate response against swagger spec
1460	responseValidationInput := &swagger.ResponseValidationInput{
1461		RequestValidationInput: requestValidationInput,
1462		Status:                 resp.StatusCode,
1463		Header:                 resp.Header,
1464	}
1465	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1466	return swagger.ValidateResponse(ctx, responseValidationInput)
1467}