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