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