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