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.

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