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           []historydb.TokenWithUSD
  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 := []historydb.TokenWithUSD{}
 260	for i, tkn := range tokens {
 261		token := historydb.TokenWithUSD{
 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) historydb.TokenWithUSD {
 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) historydb.TokenWithUSD {
 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}
1074func TestGetConfig(t *testing.T) {
1075	endpoint := apiURL + "config"
1076	var configTest configAPI
1077	assert.NoError(t, doGoodReq("GET", endpoint, nil, &configTest))
1078	assert.Equal(t, config, configTest)
1079	assert.Equal(t, cg, &configTest)
1080}
1081
1082func TestPoolTxs(t *testing.T) {
1083	// POST
1084	endpoint := apiURL + "transactions-pool"
1085	fetchedTxID := common.TxID{}
1086	for _, tx := range tc.poolTxsToSend {
1087		jsonTxBytes, err := json.Marshal(tx)
1088		assert.NoError(t, err)
1089		jsonTxReader := bytes.NewReader(jsonTxBytes)
1090		assert.NoError(
1091			t, doGoodReq(
1092				"POST",
1093				endpoint,
1094				jsonTxReader, &fetchedTxID,
1095			),
1096		)
1097		assert.Equal(t, tx.TxID, fetchedTxID)
1098	}
1099	// 400
1100	// Wrong signature
1101	badTx := tc.poolTxsToSend[0]
1102	badTx.FromIdx = "hez:foo:1000"
1103	jsonTxBytes, err := json.Marshal(badTx)
1104	assert.NoError(t, err)
1105	jsonTxReader := bytes.NewReader(jsonTxBytes)
1106	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1107	assert.NoError(t, err)
1108	// Wrong to
1109	badTx = tc.poolTxsToSend[0]
1110	ethAddr := "hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
1111	badTx.ToEthAddr = &ethAddr
1112	badTx.ToIdx = nil
1113	jsonTxBytes, err = json.Marshal(badTx)
1114	assert.NoError(t, err)
1115	jsonTxReader = bytes.NewReader(jsonTxBytes)
1116	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1117	assert.NoError(t, err)
1118	// Wrong rq
1119	badTx = tc.poolTxsToSend[0]
1120	rqFromIdx := "hez:foo:30"
1121	badTx.RqFromIdx = &rqFromIdx
1122	jsonTxBytes, err = json.Marshal(badTx)
1123	assert.NoError(t, err)
1124	jsonTxReader = bytes.NewReader(jsonTxBytes)
1125	err = doBadReq("POST", endpoint, jsonTxReader, 400)
1126	assert.NoError(t, err)
1127	// GET
1128	endpoint += "/"
1129	for _, tx := range tc.poolTxsToReceive {
1130		fetchedTx := sendPoolTx{}
1131		assert.NoError(
1132			t, doGoodReq(
1133				"GET",
1134				endpoint+tx.TxID.String(),
1135				nil, &fetchedTx,
1136			),
1137		)
1138		assertPoolTx(t, tx, fetchedTx)
1139	}
1140	// 400
1141	err = doBadReq("GET", endpoint+"0xG20000000156660000000090", nil, 400)
1142	assert.NoError(t, err)
1143	// 404
1144	err = doBadReq("GET", endpoint+"0x020000000156660000000090", nil, 404)
1145	assert.NoError(t, err)
1146}
1147
1148func assertPoolTx(t *testing.T, expected, actual sendPoolTx) {
1149	// state should be pending
1150	assert.Equal(t, common.PoolL2TxStatePending, actual.State)
1151	expected.State = actual.State
1152	// timestamp should be very close to now
1153	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
1154	expected.Timestamp = actual.Timestamp
1155	// token timestamp
1156	if expected.Token.USDUpdate == nil {
1157		assert.Equal(t, expected.Token.USDUpdate, actual.Token.USDUpdate)
1158	} else {
1159		assert.Equal(t, expected.Token.USDUpdate.Unix(), actual.Token.USDUpdate.Unix())
1160		expected.Token.USDUpdate = actual.Token.USDUpdate
1161	}
1162	assert.Equal(t, expected, actual)
1163}
1164
1165func TestGetCoordinators(t *testing.T) {
1166	endpoint := apiURL + "coordinators"
1167	fetchedCoordinators := []coordinatorAPI{}
1168
1169	appendIter := func(intr interface{}) {
1170		for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ {
1171			tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i])
1172			if err != nil {
1173				panic(err)
1174			}
1175			fetchedCoordinators = append(fetchedCoordinators, tmp.(coordinatorAPI))
1176		}
1177	}
1178
1179	limit := 5
1180
1181	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1182	err := doGoodReqPaginated(path, historydb.OrderAsc, &coordinatorsAPI{}, appendIter)
1183	assert.NoError(t, err)
1184	assert.Equal(t, tc.coordinators, fetchedCoordinators)
1185
1186	// Reverse Order
1187	reversedCoordinators := []coordinatorAPI{}
1188	appendIter = func(intr interface{}) {
1189		for i := 0; i < len(intr.(*coordinatorsAPI).Coordinators); i++ {
1190			tmp, err := copystructure.Copy(intr.(*coordinatorsAPI).Coordinators[i])
1191			if err != nil {
1192				panic(err)
1193			}
1194			reversedCoordinators = append(reversedCoordinators, tmp.(coordinatorAPI))
1195		}
1196	}
1197	err = doGoodReqPaginated(path, historydb.OrderDesc, &coordinatorsAPI{}, appendIter)
1198	assert.NoError(t, err)
1199	for i := 0; i < len(fetchedCoordinators); i++ {
1200		assert.Equal(t, reversedCoordinators[i], fetchedCoordinators[len(fetchedCoordinators)-1-i])
1201	}
1202
1203	// Test GetCoordinator
1204	path = fmt.Sprintf("%s/%s", endpoint, fetchedCoordinators[2].Forger.String())
1205	coordinator := coordinatorAPI{}
1206	assert.NoError(t, doGoodReq("GET", path, nil, &coordinator))
1207	assert.Equal(t, fetchedCoordinators[2], coordinator)
1208	// 400
1209	path = fmt.Sprintf("%s/0x001", endpoint)
1210	err = doBadReq("GET", path, nil, 400)
1211	assert.NoError(t, err)
1212	// 404
1213	path = fmt.Sprintf("%s/0xaa942cfcd25ad4d90a62358b0dd84f33b398262a", endpoint)
1214	err = doBadReq("GET", path, nil, 404)
1215	assert.NoError(t, err)
1216}
1217func TestAccountCreationAuth(t *testing.T) {
1218	// POST
1219	endpoint := apiURL + "account-creation-authorization"
1220	for _, auth := range tc.auths {
1221		jsonAuthBytes, err := json.Marshal(auth)
1222		assert.NoError(t, err)
1223		jsonAuthReader := bytes.NewReader(jsonAuthBytes)
1224		fmt.Println(string(jsonAuthBytes))
1225		assert.NoError(
1226			t, doGoodReq(
1227				"POST",
1228				endpoint,
1229				jsonAuthReader, nil,
1230			),
1231		)
1232	}
1233	// GET
1234	endpoint += "/"
1235	for _, auth := range tc.auths {
1236		fetchedAuth := accountCreationAuthAPI{}
1237		assert.NoError(
1238			t, doGoodReq(
1239				"GET",
1240				endpoint+auth.EthAddr,
1241				nil, &fetchedAuth,
1242			),
1243		)
1244		assertAuth(t, auth, fetchedAuth)
1245	}
1246	// POST
1247	// 400
1248	// Wrong addr
1249	badAuth := tc.auths[0]
1250	badAuth.EthAddr = ethAddrToHez(ethCommon.BigToAddress(big.NewInt(1)))
1251	jsonAuthBytes, err := json.Marshal(badAuth)
1252	assert.NoError(t, err)
1253	jsonAuthReader := bytes.NewReader(jsonAuthBytes)
1254	err = doBadReq("POST", endpoint, jsonAuthReader, 400)
1255	assert.NoError(t, err)
1256	// Wrong signature
1257	badAuth = tc.auths[0]
1258	badAuth.Signature = badAuth.Signature[:len(badAuth.Signature)-1]
1259	badAuth.Signature += "F"
1260	jsonAuthBytes, err = json.Marshal(badAuth)
1261	assert.NoError(t, err)
1262	jsonAuthReader = bytes.NewReader(jsonAuthBytes)
1263	err = doBadReq("POST", endpoint, jsonAuthReader, 400)
1264	assert.NoError(t, err)
1265	// GET
1266	// 400
1267	err = doBadReq("GET", endpoint+"hez:0xFooBar", nil, 400)
1268	assert.NoError(t, err)
1269	// 404
1270	err = doBadReq("GET", endpoint+"hez:0x0000000000000000000000000000000000000001", nil, 404)
1271	assert.NoError(t, err)
1272}
1273
1274func assertAuth(t *testing.T, expected, actual accountCreationAuthAPI) {
1275	// timestamp should be very close to now
1276	assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
1277	expected.Timestamp = actual.Timestamp
1278	assert.Equal(t, expected, actual)
1279}
1280
1281func doGoodReqPaginated(
1282	path, order string,
1283	iterStruct db.Paginationer,
1284	appendIter func(res interface{}),
1285) error {
1286	next := 0
1287	for {
1288		// Call API to get this iteration items
1289		iterPath := path
1290		if next == 0 && order == historydb.OrderDesc {
1291			// Fetch first item in reverse order
1292			iterPath += "99999"
1293		} else {
1294			// Fetch from next item or 0 if it's ascending order
1295			iterPath += strconv.Itoa(next)
1296		}
1297		if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
1298			return err
1299		}
1300		appendIter(iterStruct)
1301		// Keep iterating?
1302		pag := iterStruct.GetPagination()
1303		if order == historydb.OrderAsc {
1304			if pag.LastReturnedItem == pag.LastItem { // No
1305				break
1306			} else { // Yes
1307				next = pag.LastReturnedItem + 1
1308			}
1309		} else {
1310			if pag.FirstReturnedItem == pag.FirstItem { // No
1311				break
1312			} else { // Yes
1313				next = pag.FirstReturnedItem - 1
1314			}
1315		}
1316	}
1317	return nil
1318}
1319
1320func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
1321	ctx := context.Background()
1322	client := &http.Client{}
1323	httpReq, err := http.NewRequest(method, path, reqBody)
1324	if err != nil {
1325		return err
1326	}
1327	if reqBody != nil {
1328		httpReq.Header.Add("Content-Type", "application/json")
1329	}
1330	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1331	if err != nil {
1332		return err
1333	}
1334	// Validate request against swagger spec
1335	requestValidationInput := &swagger.RequestValidationInput{
1336		Request:    httpReq,
1337		PathParams: pathParams,
1338		Route:      route,
1339	}
1340	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1341		return err
1342	}
1343	// Do API call
1344	resp, err := client.Do(httpReq)
1345	if err != nil {
1346		return err
1347	}
1348	if resp.Body == nil && returnStruct != nil {
1349		return errors.New("Nil body")
1350	}
1351	//nolint
1352	defer resp.Body.Close()
1353	body, err := ioutil.ReadAll(resp.Body)
1354	if err != nil {
1355		return err
1356	}
1357	if resp.StatusCode != 200 {
1358		return fmt.Errorf("%d response. Body: %s", resp.StatusCode, string(body))
1359	}
1360	if returnStruct == nil {
1361		return nil
1362	}
1363	// Unmarshal body into return struct
1364	if err := json.Unmarshal(body, returnStruct); err != nil {
1365		log.Error("invalid json: " + string(body))
1366		return err
1367	}
1368	// Validate response against swagger spec
1369	responseValidationInput := &swagger.ResponseValidationInput{
1370		RequestValidationInput: requestValidationInput,
1371		Status:                 resp.StatusCode,
1372		Header:                 resp.Header,
1373	}
1374	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1375	return swagger.ValidateResponse(ctx, responseValidationInput)
1376}
1377
1378func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
1379	ctx := context.Background()
1380	client := &http.Client{}
1381	httpReq, _ := http.NewRequest(method, path, reqBody)
1382	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1383	if err != nil {
1384		return err
1385	}
1386	// Validate request against swagger spec
1387	requestValidationInput := &swagger.RequestValidationInput{
1388		Request:    httpReq,
1389		PathParams: pathParams,
1390		Route:      route,
1391	}
1392	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1393		if expectedResponseCode != 400 {
1394			return err
1395		}
1396		log.Warn("The request does not match the API spec")
1397	}
1398	// Do API call
1399	resp, err := client.Do(httpReq)
1400	if err != nil {
1401		return err
1402	}
1403	if resp.Body == nil {
1404		return errors.New("Nil body")
1405	}
1406	//nolint
1407	defer resp.Body.Close()
1408	body, err := ioutil.ReadAll(resp.Body)
1409	if err != nil {
1410		return err
1411	}
1412	if resp.StatusCode != expectedResponseCode {
1413		return fmt.Errorf("Unexpected response code: %d. Body: %s", resp.StatusCode, string(body))
1414	}
1415	// Validate response against swagger spec
1416	responseValidationInput := &swagger.ResponseValidationInput{
1417		RequestValidationInput: requestValidationInput,
1418		Status:                 resp.StatusCode,
1419		Header:                 resp.Header,
1420	}
1421	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1422	return swagger.ValidateResponse(ctx, responseValidationInput)
1423}