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