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.

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