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