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.

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