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.

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