You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1391 lines
41 KiB

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