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