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.

851 lines
25 KiB

  1. package api
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "io/ioutil"
  10. "math/big"
  11. "net/http"
  12. "os"
  13. "sort"
  14. "strconv"
  15. "testing"
  16. "time"
  17. ethCommon "github.com/ethereum/go-ethereum/common"
  18. swagger "github.com/getkin/kin-openapi/openapi3filter"
  19. "github.com/gin-gonic/gin"
  20. "github.com/hermeznetwork/hermez-node/common"
  21. "github.com/hermeznetwork/hermez-node/db"
  22. "github.com/hermeznetwork/hermez-node/db/historydb"
  23. "github.com/hermeznetwork/hermez-node/db/l2db"
  24. "github.com/hermeznetwork/hermez-node/db/statedb"
  25. "github.com/hermeznetwork/hermez-node/eth"
  26. "github.com/hermeznetwork/hermez-node/log"
  27. "github.com/hermeznetwork/hermez-node/test"
  28. "github.com/iden3/go-iden3-crypto/babyjub"
  29. "github.com/mitchellh/copystructure"
  30. "github.com/stretchr/testify/assert"
  31. "github.com/stretchr/testify/require"
  32. )
  33. const apiPort = ":4010"
  34. const apiURL = "http://localhost" + apiPort + "/"
  35. type testCommon struct {
  36. blocks []common.Block
  37. tokens []historydb.TokenWithUSD
  38. batches []testBatch
  39. fullBatches []testFullBatch
  40. coordinators []historydb.CoordinatorAPI
  41. usrAddr string
  42. usrBjj string
  43. accs []common.Account
  44. usrTxs []testTx
  45. allTxs []testTx
  46. exits []exitAPI
  47. usrExits []exitAPI
  48. poolTxsToSend []testPoolTxSend
  49. poolTxsToReceive []testPoolTxReceive
  50. auths []accountCreationAuthAPI
  51. router *swagger.Router
  52. bids []testBid
  53. }
  54. var tc testCommon
  55. var config configAPI
  56. // TestMain initializes the API server, and fill HistoryDB and StateDB with fake data,
  57. // emulating the task of the synchronizer in order to have data to be returned
  58. // by the API endpoints that will be tested
  59. func TestMain(m *testing.M) {
  60. // Initializations
  61. // Swagger
  62. router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
  63. // HistoryDB
  64. pass := os.Getenv("POSTGRES_PASS")
  65. database, err := db.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
  66. if err != nil {
  67. panic(err)
  68. }
  69. hdb := historydb.NewHistoryDB(database)
  70. err = hdb.Reorg(-1)
  71. if err != nil {
  72. panic(err)
  73. }
  74. // StateDB
  75. dir, err := ioutil.TempDir("", "tmpdb")
  76. if err != nil {
  77. panic(err)
  78. }
  79. defer func() {
  80. if err := os.RemoveAll(dir); err != nil {
  81. panic(err)
  82. }
  83. }()
  84. sdb, err := statedb.NewStateDB(dir, statedb.TypeTxSelector, 0)
  85. if err != nil {
  86. panic(err)
  87. }
  88. // L2DB
  89. l2DB := l2db.NewL2DB(database, 10, 100, 24*time.Hour)
  90. test.CleanL2DB(l2DB.DB())
  91. // Config (smart contract constants)
  92. config.RollupConstants.ExchangeMultiplier = eth.RollupConstExchangeMultiplier
  93. config.RollupConstants.ExitIdx = eth.RollupConstExitIDx
  94. config.RollupConstants.ReservedIdx = eth.RollupConstReservedIDx
  95. config.RollupConstants.LimitLoadAmount, _ = new(big.Int).SetString("340282366920938463463374607431768211456", 10)
  96. config.RollupConstants.LimitL2TransferAmount, _ = new(big.Int).SetString("6277101735386680763835789423207666416102355444464034512896", 10)
  97. config.RollupConstants.LimitTokens = eth.RollupConstLimitTokens
  98. config.RollupConstants.L1CoordinatorTotalBytes = eth.RollupConstL1CoordinatorTotalBytes
  99. config.RollupConstants.L1UserTotalBytes = eth.RollupConstL1UserTotalBytes
  100. config.RollupConstants.MaxL1UserTx = eth.RollupConstMaxL1UserTx
  101. config.RollupConstants.MaxL1Tx = eth.RollupConstMaxL1Tx
  102. config.RollupConstants.InputSHAConstantBytes = eth.RollupConstInputSHAConstantBytes
  103. config.RollupConstants.NumBuckets = eth.RollupConstNumBuckets
  104. config.RollupConstants.MaxWithdrawalDelay = eth.RollupConstMaxWithdrawalDelay
  105. var rollupPublicConstants eth.RollupPublicConstants
  106. rollupPublicConstants.AbsoluteMaxL1L2BatchTimeout = 240
  107. rollupPublicConstants.HermezAuctionContract = ethCommon.HexToAddress("0x500D1d6A4c7D8Ae28240b47c8FCde034D827fD5e")
  108. rollupPublicConstants.HermezGovernanceDAOAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
  109. rollupPublicConstants.SafetyAddress = ethCommon.HexToAddress("0xE5904695748fe4A84b40b3fc79De2277660BD1D3")
  110. rollupPublicConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
  111. rollupPublicConstants.WithdrawDelayerContract = ethCommon.HexToAddress("0xD6C850aeBFDC46D7F4c207e445cC0d6B0919BDBe")
  112. var verifier eth.RollupVerifierStruct
  113. verifier.MaxTx = 512
  114. verifier.NLevels = 32
  115. rollupPublicConstants.Verifiers = append(rollupPublicConstants.Verifiers, verifier)
  116. var auctionConstants eth.AuctionConstants
  117. auctionConstants.BlocksPerSlot = 40
  118. auctionConstants.GenesisBlockNum = 100
  119. auctionConstants.GovernanceAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
  120. auctionConstants.InitialMinimalBidding, _ = new(big.Int).SetString("10000000000000000000", 10)
  121. auctionConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
  122. auctionConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
  123. var wdelayerConstants eth.WDelayerConstants
  124. wdelayerConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
  125. wdelayerConstants.MaxEmergencyModeTime = uint64(1000000)
  126. wdelayerConstants.MaxWithdrawalDelay = uint64(10000000)
  127. config.RollupConstants.PublicConstants = rollupPublicConstants
  128. config.AuctionConstants = auctionConstants
  129. config.WDelayerConstants = wdelayerConstants
  130. // API
  131. api := gin.Default()
  132. if err := SetAPIEndpoints(
  133. true,
  134. true,
  135. api,
  136. hdb,
  137. sdb,
  138. l2DB,
  139. &config,
  140. ); err != nil {
  141. panic(err)
  142. }
  143. // Start server
  144. server := &http.Server{Addr: apiPort, Handler: api}
  145. go func() {
  146. if err := server.ListenAndServe(); err != nil &&
  147. err != http.ErrServerClosed {
  148. panic(err)
  149. }
  150. }()
  151. // Fill HistoryDB and StateDB with fake data
  152. // Clean DB
  153. err = h.Reorg(0)
  154. if err != nil {
  155. panic(err)
  156. }
  157. // Gen blocks and add them to DB
  158. const nBlocks = 5
  159. blocks := test.GenBlocks(1, nBlocks+1)
  160. err = h.AddBlocks(blocks)
  161. if err != nil {
  162. panic(err)
  163. }
  164. // Gen tokens and add them to DB
  165. const nTokens = 10
  166. tokens := test.GenTokens(nTokens, blocks)
  167. err = h.AddTokens(tokens)
  168. if err != nil {
  169. panic(err)
  170. }
  171. // Set token value
  172. tokensUSD := []historydb.TokenWithUSD{}
  173. for i, tkn := range tokens {
  174. token := historydb.TokenWithUSD{
  175. TokenID: tkn.TokenID,
  176. EthBlockNum: tkn.EthBlockNum,
  177. EthAddr: tkn.EthAddr,
  178. Name: tkn.Name,
  179. Symbol: tkn.Symbol,
  180. Decimals: tkn.Decimals,
  181. }
  182. // Set value of 50% of the tokens
  183. if i%2 != 0 {
  184. value := float64(i) * 1.234567
  185. now := time.Now().UTC()
  186. token.USD = &value
  187. token.USDUpdate = &now
  188. err = h.UpdateTokenValue(token.Symbol, value)
  189. if err != nil {
  190. panic(err)
  191. }
  192. }
  193. tokensUSD = append(tokensUSD, token)
  194. }
  195. // Gen batches and add them to DB
  196. const nBatches = 10
  197. batches := test.GenBatches(nBatches, blocks)
  198. err = h.AddBatches(batches)
  199. if err != nil {
  200. panic(err)
  201. }
  202. // Gen accounts and add them to HistoryDB and StateDB
  203. const totalAccounts = 40
  204. const userAccounts = 4
  205. usrAddr := ethCommon.BigToAddress(big.NewInt(4896847))
  206. privK := babyjub.NewRandPrivKey()
  207. usrBjj := privK.Public()
  208. accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches)
  209. err = h.AddAccounts(accs)
  210. if err != nil {
  211. panic(err)
  212. }
  213. for i := 0; i < len(accs); i++ {
  214. if _, err := s.CreateAccount(accs[i].Idx, &accs[i]); err != nil {
  215. panic(err)
  216. }
  217. }
  218. // helper to vinculate user related resources
  219. usrIdxs := []string{}
  220. for _, acc := range accs {
  221. if acc.EthAddr == usrAddr || acc.PublicKey == usrBjj {
  222. for _, token := range tokens {
  223. if token.TokenID == acc.TokenID {
  224. usrIdxs = append(usrIdxs, idxToHez(acc.Idx, token.Symbol))
  225. }
  226. }
  227. }
  228. }
  229. // Gen exits and add them to DB
  230. const totalExits = 40
  231. exits := test.GenExitTree(totalExits, batches, accs)
  232. err = h.AddExitTree(exits)
  233. if err != nil {
  234. panic(err)
  235. }
  236. // L1 and L2 txs need to be sorted in a combined way
  237. // Gen L1Txs
  238. const totalL1Txs = 40
  239. const userL1Txs = 4
  240. usrL1Txs, othrL1Txs := test.GenL1Txs(256, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches)
  241. // Gen L2Txs
  242. const totalL2Txs = 20
  243. const userL2Txs = 4
  244. usrL2Txs, othrL2Txs := test.GenL2Txs(256+totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches)
  245. // Sort txs
  246. sortedTxs := []txSortFielder{}
  247. for i := 0; i < len(usrL1Txs); i++ {
  248. wL1 := wrappedL1(usrL1Txs[i])
  249. sortedTxs = append(sortedTxs, &wL1)
  250. }
  251. for i := 0; i < len(othrL1Txs); i++ {
  252. wL1 := wrappedL1(othrL1Txs[i])
  253. sortedTxs = append(sortedTxs, &wL1)
  254. }
  255. for i := 0; i < len(usrL2Txs); i++ {
  256. wL2 := wrappedL2(usrL2Txs[i])
  257. sortedTxs = append(sortedTxs, &wL2)
  258. }
  259. for i := 0; i < len(othrL2Txs); i++ {
  260. wL2 := wrappedL2(othrL2Txs[i])
  261. sortedTxs = append(sortedTxs, &wL2)
  262. }
  263. sort.Sort(txsSort(sortedTxs))
  264. // Store txs to DB
  265. for _, genericTx := range sortedTxs {
  266. l1 := genericTx.L1()
  267. l2 := genericTx.L2()
  268. if l1 != nil {
  269. err = h.AddL1Txs([]common.L1Tx{*l1})
  270. if err != nil {
  271. panic(err)
  272. }
  273. } else if l2 != nil {
  274. err = h.AddL2Txs([]common.L2Tx{*l2})
  275. if err != nil {
  276. panic(err)
  277. }
  278. } else {
  279. panic("should be l1 or l2")
  280. }
  281. }
  282. // Transform exits to API
  283. exitsToAPIExits := func(exits []common.ExitInfo, accs []common.Account, tokens []common.Token) []exitAPI {
  284. historyExits := []historydb.HistoryExit{}
  285. for _, exit := range exits {
  286. token := getTokenByIdx(exit.AccountIdx, tokensUSD, accs)
  287. historyExits = append(historyExits, historydb.HistoryExit{
  288. BatchNum: exit.BatchNum,
  289. AccountIdx: exit.AccountIdx,
  290. MerkleProof: exit.MerkleProof,
  291. Balance: exit.Balance,
  292. InstantWithdrawn: exit.InstantWithdrawn,
  293. DelayedWithdrawRequest: exit.DelayedWithdrawRequest,
  294. DelayedWithdrawn: exit.DelayedWithdrawn,
  295. TokenID: token.TokenID,
  296. TokenEthBlockNum: token.EthBlockNum,
  297. TokenEthAddr: token.EthAddr,
  298. TokenName: token.Name,
  299. TokenSymbol: token.Symbol,
  300. TokenDecimals: token.Decimals,
  301. TokenUSD: token.USD,
  302. TokenUSDUpdate: token.USDUpdate,
  303. })
  304. }
  305. return historyExitsToAPI(historyExits)
  306. }
  307. apiExits := exitsToAPIExits(exits, accs, tokens)
  308. // sort.Sort(apiExits)
  309. usrExits := []exitAPI{}
  310. for _, exit := range apiExits {
  311. for _, idx := range usrIdxs {
  312. if idx == exit.AccountIdx {
  313. usrExits = append(usrExits, exit)
  314. }
  315. }
  316. }
  317. // Coordinators
  318. const nCoords = 10
  319. coords := test.GenCoordinators(nCoords, blocks)
  320. err = hdb.AddCoordinators(coords)
  321. if err != nil {
  322. panic(err)
  323. }
  324. fromItem := uint(0)
  325. limit := uint(99999)
  326. coordinators, _, err := hdb.GetCoordinatorsAPI(&fromItem, &limit, historydb.OrderAsc)
  327. if err != nil {
  328. panic(err)
  329. }
  330. // Account creation auth
  331. const nAuths = 5
  332. auths := test.GenAuths(nAuths)
  333. // Transform auths to API format
  334. apiAuths := []accountCreationAuthAPI{}
  335. for _, auth := range auths {
  336. apiAuth := accountCreationAuthToAPI(auth)
  337. apiAuths = append(apiAuths, *apiAuth)
  338. }
  339. // Bids
  340. const nBids = 10
  341. bids := test.GenBids(nBids, blocks, coords)
  342. err = hdb.AddBids(bids)
  343. if err != nil {
  344. panic(err)
  345. }
  346. // Set testCommon
  347. usrTxs, allTxs := genTestTxs(sortedTxs, usrIdxs, accs, tokensUSD, blocks)
  348. poolTxsToSend, poolTxsToReceive := genTestPoolTx(accs, []babyjub.PrivateKey{privK}, tokensUSD) // NOTE: pool txs are not inserted to the DB here. In the test they will be posted and getted.
  349. testBatches, fullBatches := genTestBatches(blocks, batches, allTxs)
  350. tc = testCommon{
  351. blocks: blocks,
  352. tokens: tokensUSD,
  353. batches: testBatches,
  354. fullBatches: fullBatches,
  355. coordinators: coordinators,
  356. usrAddr: ethAddrToHez(usrAddr),
  357. usrBjj: bjjToString(usrBjj),
  358. accs: accs,
  359. usrTxs: usrTxs,
  360. allTxs: allTxs,
  361. exits: apiExits,
  362. usrExits: usrExits,
  363. poolTxsToSend: poolTxsToSend,
  364. poolTxsToReceive: poolTxsToReceive,
  365. auths: apiAuths,
  366. router: router,
  367. bids: genTestBids(blocks, coordinators, bids),
  368. }
  369. // Fake server
  370. if os.Getenv("FAKE_SERVER") == "yes" {
  371. for {
  372. log.Info("Running fake server at " + apiURL + " until ^C is received")
  373. time.Sleep(30 * time.Second)
  374. }
  375. }
  376. // Run tests
  377. result := m.Run()
  378. // Stop server
  379. if err := server.Shutdown(context.Background()); err != nil {
  380. panic(err)
  381. }
  382. if err := database.Close(); err != nil {
  383. panic(err)
  384. }
  385. if err := os.RemoveAll(dir); err != nil {
  386. panic(err)
  387. }
  388. os.Exit(result)
  389. }
  390. func TestGetExits(t *testing.T) {
  391. endpoint := apiURL + "exits"
  392. fetchedExits := []exitAPI{}
  393. appendIter := func(intr interface{}) {
  394. for i := 0; i < len(intr.(*exitsAPI).Exits); i++ {
  395. tmp, err := copystructure.Copy(intr.(*exitsAPI).Exits[i])
  396. if err != nil {
  397. panic(err)
  398. }
  399. fetchedExits = append(fetchedExits, tmp.(exitAPI))
  400. }
  401. }
  402. // Get all (no filters)
  403. limit := 8
  404. path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
  405. err := doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
  406. assert.NoError(t, err)
  407. assertExitAPIs(t, tc.exits, fetchedExits)
  408. // Get by ethAddr
  409. fetchedExits = []exitAPI{}
  410. limit = 7
  411. path = fmt.Sprintf(
  412. "%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
  413. endpoint, tc.usrAddr, limit,
  414. )
  415. err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
  416. assert.NoError(t, err)
  417. assertExitAPIs(t, tc.usrExits, fetchedExits)
  418. // Get by bjj
  419. fetchedExits = []exitAPI{}
  420. limit = 6
  421. path = fmt.Sprintf(
  422. "%s?BJJ=%s&limit=%d&fromItem=",
  423. endpoint, tc.usrBjj, limit,
  424. )
  425. err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
  426. assert.NoError(t, err)
  427. assertExitAPIs(t, tc.usrExits, fetchedExits)
  428. // Get by tokenID
  429. fetchedExits = []exitAPI{}
  430. limit = 5
  431. tokenID := tc.exits[0].Token.TokenID
  432. path = fmt.Sprintf(
  433. "%s?tokenId=%d&limit=%d&fromItem=",
  434. endpoint, tokenID, limit,
  435. )
  436. err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
  437. assert.NoError(t, err)
  438. tokenIDExits := []exitAPI{}
  439. for i := 0; i < len(tc.exits); i++ {
  440. if tc.exits[i].Token.TokenID == tokenID {
  441. tokenIDExits = append(tokenIDExits, tc.exits[i])
  442. }
  443. }
  444. assertExitAPIs(t, tokenIDExits, fetchedExits)
  445. // idx
  446. fetchedExits = []exitAPI{}
  447. limit = 4
  448. idx := tc.exits[0].AccountIdx
  449. path = fmt.Sprintf(
  450. "%s?accountIndex=%s&limit=%d&fromItem=",
  451. endpoint, idx, limit,
  452. )
  453. err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
  454. assert.NoError(t, err)
  455. idxExits := []exitAPI{}
  456. for i := 0; i < len(tc.exits); i++ {
  457. if tc.exits[i].AccountIdx[6:] == idx[6:] {
  458. idxExits = append(idxExits, tc.exits[i])
  459. }
  460. }
  461. assertExitAPIs(t, idxExits, fetchedExits)
  462. // batchNum
  463. fetchedExits = []exitAPI{}
  464. limit = 3
  465. batchNum := tc.exits[0].BatchNum
  466. path = fmt.Sprintf(
  467. "%s?batchNum=%d&limit=%d&fromItem=",
  468. endpoint, batchNum, limit,
  469. )
  470. err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
  471. assert.NoError(t, err)
  472. batchNumExits := []exitAPI{}
  473. for i := 0; i < len(tc.exits); i++ {
  474. if tc.exits[i].BatchNum == batchNum {
  475. batchNumExits = append(batchNumExits, tc.exits[i])
  476. }
  477. }
  478. assertExitAPIs(t, batchNumExits, fetchedExits)
  479. // Multiple filters
  480. fetchedExits = []exitAPI{}
  481. limit = 1
  482. path = fmt.Sprintf(
  483. "%s?batchNum=%d&tokeId=%d&limit=%d&fromItem=",
  484. endpoint, batchNum, tokenID, limit,
  485. )
  486. err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
  487. assert.NoError(t, err)
  488. mixedExits := []exitAPI{}
  489. flipedExits := []exitAPI{}
  490. for i := 0; i < len(tc.exits); i++ {
  491. if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
  492. mixedExits = append(mixedExits, tc.exits[i])
  493. }
  494. flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
  495. }
  496. assertExitAPIs(t, mixedExits, fetchedExits)
  497. // All, in reverse order
  498. fetchedExits = []exitAPI{}
  499. limit = 5
  500. path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
  501. err = doGoodReqPaginated(path, historydb.OrderDesc, &exitsAPI{}, appendIter)
  502. assert.NoError(t, err)
  503. assertExitAPIs(t, flipedExits, fetchedExits)
  504. // 400
  505. path = fmt.Sprintf(
  506. "%s?accountIndex=%s&hermezEthereumAddress=%s",
  507. endpoint, idx, tc.usrAddr,
  508. )
  509. err = doBadReq("GET", path, nil, 400)
  510. assert.NoError(t, err)
  511. path = fmt.Sprintf("%s?tokenId=X", endpoint)
  512. err = doBadReq("GET", path, nil, 400)
  513. assert.NoError(t, err)
  514. // 404
  515. path = fmt.Sprintf("%s?batchNum=999999", endpoint)
  516. err = doBadReq("GET", path, nil, 404)
  517. assert.NoError(t, err)
  518. path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
  519. err = doBadReq("GET", path, nil, 404)
  520. assert.NoError(t, err)
  521. }
  522. func TestGetExit(t *testing.T) {
  523. // Get all txs by their ID
  524. endpoint := apiURL + "exits/"
  525. fetchedExits := []exitAPI{}
  526. for _, exit := range tc.exits {
  527. fetchedExit := exitAPI{}
  528. assert.NoError(
  529. t, doGoodReq(
  530. "GET",
  531. fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
  532. nil, &fetchedExit,
  533. ),
  534. )
  535. fetchedExits = append(fetchedExits, fetchedExit)
  536. }
  537. assertExitAPIs(t, tc.exits, fetchedExits)
  538. // 400
  539. err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
  540. assert.NoError(t, err)
  541. err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
  542. assert.NoError(t, err)
  543. // 404
  544. err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
  545. assert.NoError(t, err)
  546. }
  547. func assertExitAPIs(t *testing.T, expected, actual []exitAPI) {
  548. require.Equal(t, len(expected), len(actual))
  549. for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
  550. actual[i].ItemID = 0
  551. if expected[i].Token.USDUpdate == nil {
  552. assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
  553. } else {
  554. assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
  555. expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
  556. }
  557. assert.Equal(t, expected[i], actual[i])
  558. }
  559. }
  560. func TestGetConfig(t *testing.T) {
  561. endpoint := apiURL + "config"
  562. var configTest configAPI
  563. assert.NoError(t, doGoodReq("GET", endpoint, nil, &configTest))
  564. assert.Equal(t, config, configTest)
  565. assert.Equal(t, cg, &configTest)
  566. }
  567. func TestAccountCreationAuth(t *testing.T) {
  568. // POST
  569. endpoint := apiURL + "account-creation-authorization"
  570. for _, auth := range tc.auths {
  571. jsonAuthBytes, err := json.Marshal(auth)
  572. assert.NoError(t, err)
  573. jsonAuthReader := bytes.NewReader(jsonAuthBytes)
  574. fmt.Println(string(jsonAuthBytes))
  575. assert.NoError(
  576. t, doGoodReq(
  577. "POST",
  578. endpoint,
  579. jsonAuthReader, nil,
  580. ),
  581. )
  582. }
  583. // GET
  584. endpoint += "/"
  585. for _, auth := range tc.auths {
  586. fetchedAuth := accountCreationAuthAPI{}
  587. assert.NoError(
  588. t, doGoodReq(
  589. "GET",
  590. endpoint+auth.EthAddr,
  591. nil, &fetchedAuth,
  592. ),
  593. )
  594. assertAuth(t, auth, fetchedAuth)
  595. }
  596. // POST
  597. // 400
  598. // Wrong addr
  599. badAuth := tc.auths[0]
  600. badAuth.EthAddr = ethAddrToHez(ethCommon.BigToAddress(big.NewInt(1)))
  601. jsonAuthBytes, err := json.Marshal(badAuth)
  602. assert.NoError(t, err)
  603. jsonAuthReader := bytes.NewReader(jsonAuthBytes)
  604. err = doBadReq("POST", endpoint, jsonAuthReader, 400)
  605. assert.NoError(t, err)
  606. // Wrong signature
  607. badAuth = tc.auths[0]
  608. badAuth.Signature = badAuth.Signature[:len(badAuth.Signature)-1]
  609. badAuth.Signature += "F"
  610. jsonAuthBytes, err = json.Marshal(badAuth)
  611. assert.NoError(t, err)
  612. jsonAuthReader = bytes.NewReader(jsonAuthBytes)
  613. err = doBadReq("POST", endpoint, jsonAuthReader, 400)
  614. assert.NoError(t, err)
  615. // GET
  616. // 400
  617. err = doBadReq("GET", endpoint+"hez:0xFooBar", nil, 400)
  618. assert.NoError(t, err)
  619. // 404
  620. err = doBadReq("GET", endpoint+"hez:0x0000000000000000000000000000000000000001", nil, 404)
  621. assert.NoError(t, err)
  622. }
  623. func assertAuth(t *testing.T, expected, actual accountCreationAuthAPI) {
  624. // timestamp should be very close to now
  625. assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
  626. expected.Timestamp = actual.Timestamp
  627. assert.Equal(t, expected, actual)
  628. }
  629. func doGoodReqPaginated(
  630. path, order string,
  631. iterStruct db.Paginationer,
  632. appendIter func(res interface{}),
  633. ) error {
  634. next := 0
  635. for {
  636. // Call API to get this iteration items
  637. iterPath := path
  638. if next == 0 && order == historydb.OrderDesc {
  639. // Fetch first item in reverse order
  640. iterPath += "99999"
  641. } else {
  642. // Fetch from next item or 0 if it's ascending order
  643. iterPath += strconv.Itoa(next)
  644. }
  645. if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
  646. return err
  647. }
  648. appendIter(iterStruct)
  649. // Keep iterating?
  650. pag := iterStruct.GetPagination()
  651. if order == historydb.OrderAsc {
  652. if pag.LastReturnedItem == pag.LastItem { // No
  653. break
  654. } else { // Yes
  655. next = pag.LastReturnedItem + 1
  656. }
  657. } else {
  658. if pag.FirstReturnedItem == pag.FirstItem { // No
  659. break
  660. } else { // Yes
  661. next = pag.FirstReturnedItem - 1
  662. }
  663. }
  664. }
  665. return nil
  666. }
  667. func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
  668. ctx := context.Background()
  669. client := &http.Client{}
  670. httpReq, err := http.NewRequest(method, path, reqBody)
  671. if err != nil {
  672. return err
  673. }
  674. if reqBody != nil {
  675. httpReq.Header.Add("Content-Type", "application/json")
  676. }
  677. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  678. if err != nil {
  679. return err
  680. }
  681. // Validate request against swagger spec
  682. requestValidationInput := &swagger.RequestValidationInput{
  683. Request: httpReq,
  684. PathParams: pathParams,
  685. Route: route,
  686. }
  687. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  688. return err
  689. }
  690. // Do API call
  691. resp, err := client.Do(httpReq)
  692. if err != nil {
  693. return err
  694. }
  695. if resp.Body == nil && returnStruct != nil {
  696. return errors.New("Nil body")
  697. }
  698. //nolint
  699. defer resp.Body.Close()
  700. body, err := ioutil.ReadAll(resp.Body)
  701. if err != nil {
  702. return err
  703. }
  704. if resp.StatusCode != 200 {
  705. return fmt.Errorf("%d response. Body: %s", resp.StatusCode, string(body))
  706. }
  707. if returnStruct == nil {
  708. return nil
  709. }
  710. // Unmarshal body into return struct
  711. if err := json.Unmarshal(body, returnStruct); err != nil {
  712. log.Error("invalid json: " + string(body))
  713. return err
  714. }
  715. // Validate response against swagger spec
  716. responseValidationInput := &swagger.ResponseValidationInput{
  717. RequestValidationInput: requestValidationInput,
  718. Status: resp.StatusCode,
  719. Header: resp.Header,
  720. }
  721. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  722. return swagger.ValidateResponse(ctx, responseValidationInput)
  723. }
  724. func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
  725. ctx := context.Background()
  726. client := &http.Client{}
  727. httpReq, _ := http.NewRequest(method, path, reqBody)
  728. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  729. if err != nil {
  730. return err
  731. }
  732. // Validate request against swagger spec
  733. requestValidationInput := &swagger.RequestValidationInput{
  734. Request: httpReq,
  735. PathParams: pathParams,
  736. Route: route,
  737. }
  738. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  739. if expectedResponseCode != 400 {
  740. return err
  741. }
  742. log.Warn("The request does not match the API spec")
  743. }
  744. // Do API call
  745. resp, err := client.Do(httpReq)
  746. if err != nil {
  747. return err
  748. }
  749. if resp.Body == nil {
  750. return errors.New("Nil body")
  751. }
  752. //nolint
  753. defer resp.Body.Close()
  754. body, err := ioutil.ReadAll(resp.Body)
  755. if err != nil {
  756. return err
  757. }
  758. if resp.StatusCode != expectedResponseCode {
  759. return fmt.Errorf("Unexpected response code: %d. Body: %s", resp.StatusCode, string(body))
  760. }
  761. // Validate response against swagger spec
  762. responseValidationInput := &swagger.ResponseValidationInput{
  763. RequestValidationInput: requestValidationInput,
  764. Status: resp.StatusCode,
  765. Header: resp.Header,
  766. }
  767. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  768. return swagger.ValidateResponse(ctx, responseValidationInput)
  769. }
  770. // test helpers
  771. func getTimestamp(blockNum int64, blocks []common.Block) time.Time {
  772. for i := 0; i < len(blocks); i++ {
  773. if blocks[i].EthBlockNum == blockNum {
  774. return blocks[i].Timestamp
  775. }
  776. }
  777. panic("timesamp not found")
  778. }
  779. func getTokenByID(id common.TokenID, tokens []historydb.TokenWithUSD) historydb.TokenWithUSD {
  780. for i := 0; i < len(tokens); i++ {
  781. if tokens[i].TokenID == id {
  782. return tokens[i]
  783. }
  784. }
  785. panic("token not found")
  786. }
  787. func getTokenByIdx(idx common.Idx, tokens []historydb.TokenWithUSD, accs []common.Account) historydb.TokenWithUSD {
  788. for _, acc := range accs {
  789. if idx == acc.Idx {
  790. return getTokenByID(acc.TokenID, tokens)
  791. }
  792. }
  793. panic("token not found")
  794. }
  795. func getAccountByIdx(idx common.Idx, accs []common.Account) *common.Account {
  796. for _, acc := range accs {
  797. if acc.Idx == idx {
  798. return &acc
  799. }
  800. }
  801. panic("account not found")
  802. }
  803. func getBlockByNum(ethBlockNum int64, blocks []common.Block) common.Block {
  804. for _, b := range blocks {
  805. if b.EthBlockNum == ethBlockNum {
  806. return b
  807. }
  808. }
  809. panic("block not found")
  810. }
  811. func getCoordinatorByBidder(bidder ethCommon.Address, coordinators []historydb.CoordinatorAPI) historydb.CoordinatorAPI {
  812. for _, c := range coordinators {
  813. if c.Bidder == bidder {
  814. return c
  815. }
  816. }
  817. panic("coordinator not found")
  818. }