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