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.

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