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.

561 lines
16 KiB

  1. package api
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "math/big"
  10. "net/http"
  11. "os"
  12. "sort"
  13. "strconv"
  14. "testing"
  15. "time"
  16. ethCommon "github.com/ethereum/go-ethereum/common"
  17. swagger "github.com/getkin/kin-openapi/openapi3filter"
  18. "github.com/gin-gonic/gin"
  19. "github.com/hermeznetwork/hermez-node/common"
  20. "github.com/hermeznetwork/hermez-node/db"
  21. "github.com/hermeznetwork/hermez-node/db/historydb"
  22. "github.com/hermeznetwork/hermez-node/db/l2db"
  23. "github.com/hermeznetwork/hermez-node/db/statedb"
  24. "github.com/hermeznetwork/hermez-node/log"
  25. "github.com/hermeznetwork/hermez-node/test"
  26. "github.com/iden3/go-iden3-crypto/babyjub"
  27. "github.com/stretchr/testify/assert"
  28. )
  29. const apiPort = ":4010"
  30. const apiURL = "http://localhost" + apiPort + "/"
  31. type testCommon struct {
  32. blocks []common.Block
  33. tokens []historydb.TokenWithUSD
  34. batches []testBatch
  35. fullBatches []testFullBatch
  36. coordinators []historydb.CoordinatorAPI
  37. usrAddr string
  38. usrBjj string
  39. accs []common.Account
  40. usrTxs []testTx
  41. allTxs []testTx
  42. exits []testExit
  43. usrExits []testExit
  44. poolTxsToSend []testPoolTxSend
  45. poolTxsToReceive []testPoolTxReceive
  46. auths []testAuth
  47. router *swagger.Router
  48. bids []testBid
  49. }
  50. var tc testCommon
  51. var config configAPI
  52. // TestMain initializes the API server, and fill HistoryDB and StateDB with fake data,
  53. // emulating the task of the synchronizer in order to have data to be returned
  54. // by the API endpoints that will be tested
  55. func TestMain(m *testing.M) {
  56. // Initializations
  57. // Swagger
  58. router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
  59. // HistoryDB
  60. pass := os.Getenv("POSTGRES_PASS")
  61. database, err := db.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
  62. if err != nil {
  63. panic(err)
  64. }
  65. hdb := historydb.NewHistoryDB(database)
  66. if err != nil {
  67. panic(err)
  68. }
  69. // StateDB
  70. dir, err := ioutil.TempDir("", "tmpdb")
  71. if err != nil {
  72. panic(err)
  73. }
  74. defer func() {
  75. if err := os.RemoveAll(dir); err != nil {
  76. panic(err)
  77. }
  78. }()
  79. sdb, err := statedb.NewStateDB(dir, statedb.TypeTxSelector, 0)
  80. if err != nil {
  81. panic(err)
  82. }
  83. // L2DB
  84. l2DB := l2db.NewL2DB(database, 10, 100, 24*time.Hour)
  85. test.WipeDB(l2DB.DB()) // this will clean HistoryDB and L2DB
  86. // Config (smart contract constants)
  87. config.RollupConstants.ExchangeMultiplier = common.RollupConstExchangeMultiplier
  88. config.RollupConstants.ExitIdx = common.RollupConstExitIDx
  89. config.RollupConstants.ReservedIdx = common.RollupConstReservedIDx
  90. config.RollupConstants.LimitLoadAmount, _ = new(big.Int).SetString("340282366920938463463374607431768211456", 10)
  91. config.RollupConstants.LimitL2TransferAmount, _ = new(big.Int).SetString("6277101735386680763835789423207666416102355444464034512896", 10)
  92. config.RollupConstants.LimitTokens = common.RollupConstLimitTokens
  93. config.RollupConstants.L1CoordinatorTotalBytes = common.RollupConstL1CoordinatorTotalBytes
  94. config.RollupConstants.L1UserTotalBytes = common.RollupConstL1UserTotalBytes
  95. config.RollupConstants.MaxL1UserTx = common.RollupConstMaxL1UserTx
  96. config.RollupConstants.MaxL1Tx = common.RollupConstMaxL1Tx
  97. config.RollupConstants.InputSHAConstantBytes = common.RollupConstInputSHAConstantBytes
  98. config.RollupConstants.NumBuckets = common.RollupConstNumBuckets
  99. config.RollupConstants.MaxWithdrawalDelay = common.RollupConstMaxWithdrawalDelay
  100. var rollupPublicConstants common.RollupConstants
  101. rollupPublicConstants.AbsoluteMaxL1L2BatchTimeout = 240
  102. rollupPublicConstants.HermezAuctionContract = ethCommon.HexToAddress("0x500D1d6A4c7D8Ae28240b47c8FCde034D827fD5e")
  103. rollupPublicConstants.HermezGovernanceDAOAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
  104. rollupPublicConstants.SafetyAddress = ethCommon.HexToAddress("0xE5904695748fe4A84b40b3fc79De2277660BD1D3")
  105. rollupPublicConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
  106. rollupPublicConstants.WithdrawDelayerContract = ethCommon.HexToAddress("0xD6C850aeBFDC46D7F4c207e445cC0d6B0919BDBe")
  107. var verifier common.RollupVerifierStruct
  108. verifier.MaxTx = 512
  109. verifier.NLevels = 32
  110. rollupPublicConstants.Verifiers = append(rollupPublicConstants.Verifiers, verifier)
  111. var auctionConstants common.AuctionConstants
  112. auctionConstants.BlocksPerSlot = 40
  113. auctionConstants.GenesisBlockNum = 100
  114. auctionConstants.GovernanceAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
  115. auctionConstants.InitialMinimalBidding, _ = new(big.Int).SetString("10000000000000000000", 10)
  116. auctionConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
  117. auctionConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
  118. var wdelayerConstants common.WDelayerConstants
  119. wdelayerConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
  120. wdelayerConstants.MaxEmergencyModeTime = uint64(1000000)
  121. wdelayerConstants.MaxWithdrawalDelay = uint64(10000000)
  122. config.RollupConstants.PublicConstants = rollupPublicConstants
  123. config.AuctionConstants = auctionConstants
  124. config.WDelayerConstants = wdelayerConstants
  125. // API
  126. api := gin.Default()
  127. if err := SetAPIEndpoints(
  128. true,
  129. true,
  130. api,
  131. hdb,
  132. sdb,
  133. l2DB,
  134. &config,
  135. ); err != nil {
  136. panic(err)
  137. }
  138. // Start server
  139. server := &http.Server{Addr: apiPort, Handler: api}
  140. go func() {
  141. if err := server.ListenAndServe(); err != nil &&
  142. err != http.ErrServerClosed {
  143. panic(err)
  144. }
  145. }()
  146. // Fill HistoryDB and StateDB with fake data
  147. // Gen blocks and add them to DB
  148. const nBlocks = 5
  149. blocks := test.GenBlocks(1, nBlocks+1)
  150. err = h.AddBlocks(blocks)
  151. if err != nil {
  152. panic(err)
  153. }
  154. // Gen tokens and add them to DB
  155. const nTokens = 10
  156. tokens, ethToken := test.GenTokens(nTokens, blocks)
  157. err = h.AddTokens(tokens)
  158. if err != nil {
  159. panic(err)
  160. }
  161. tokens = append([]common.Token{ethToken}, tokens...)
  162. // Set token value
  163. tokensUSD := []historydb.TokenWithUSD{}
  164. for i, tkn := range tokens {
  165. token := historydb.TokenWithUSD{
  166. TokenID: tkn.TokenID,
  167. EthBlockNum: tkn.EthBlockNum,
  168. EthAddr: tkn.EthAddr,
  169. Name: tkn.Name,
  170. Symbol: tkn.Symbol,
  171. Decimals: tkn.Decimals,
  172. }
  173. // Set value of 50% of the tokens
  174. if i%2 != 0 {
  175. value := float64(i) * 1.234567
  176. now := time.Now().UTC()
  177. token.USD = &value
  178. token.USDUpdate = &now
  179. err = h.UpdateTokenValue(token.Symbol, value)
  180. if err != nil {
  181. panic(err)
  182. }
  183. }
  184. tokensUSD = append(tokensUSD, token)
  185. }
  186. // Gen batches and add them to DB
  187. const nBatches = 10
  188. batches := test.GenBatches(nBatches, blocks)
  189. err = h.AddBatches(batches)
  190. if err != nil {
  191. panic(err)
  192. }
  193. // Gen accounts and add them to HistoryDB and StateDB
  194. const totalAccounts = 40
  195. const userAccounts = 4
  196. usrAddr := ethCommon.BigToAddress(big.NewInt(4896847))
  197. privK := babyjub.NewRandPrivKey()
  198. usrBjj := privK.Public()
  199. accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches)
  200. err = h.AddAccounts(accs)
  201. if err != nil {
  202. panic(err)
  203. }
  204. for i := 0; i < len(accs); i++ {
  205. if _, err := s.CreateAccount(accs[i].Idx, &accs[i]); err != nil {
  206. panic(err)
  207. }
  208. }
  209. // helper to vinculate user related resources
  210. usrIdxs := []string{}
  211. for _, acc := range accs {
  212. if acc.EthAddr == usrAddr || acc.PublicKey == usrBjj {
  213. for _, token := range tokens {
  214. if token.TokenID == acc.TokenID {
  215. usrIdxs = append(usrIdxs, idxToHez(acc.Idx, token.Symbol))
  216. }
  217. }
  218. }
  219. }
  220. // Gen exits and add them to DB
  221. const totalExits = 40
  222. exits := test.GenExitTree(totalExits, batches, accs)
  223. err = h.AddExitTree(exits)
  224. if err != nil {
  225. panic(err)
  226. }
  227. // L1 and L2 txs need to be sorted in a combined way
  228. // Gen L1Txs
  229. const totalL1Txs = 40
  230. const userL1Txs = 4
  231. usrL1Txs, othrL1Txs := test.GenL1Txs(256, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches)
  232. // Gen L2Txs
  233. const totalL2Txs = 20
  234. const userL2Txs = 4
  235. usrL2Txs, othrL2Txs := test.GenL2Txs(256+totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches)
  236. // Sort txs
  237. sortedTxs := []txSortFielder{}
  238. for i := 0; i < len(usrL1Txs); i++ {
  239. wL1 := wrappedL1(usrL1Txs[i])
  240. sortedTxs = append(sortedTxs, &wL1)
  241. }
  242. for i := 0; i < len(othrL1Txs); i++ {
  243. wL1 := wrappedL1(othrL1Txs[i])
  244. sortedTxs = append(sortedTxs, &wL1)
  245. }
  246. for i := 0; i < len(usrL2Txs); i++ {
  247. wL2 := wrappedL2(usrL2Txs[i])
  248. sortedTxs = append(sortedTxs, &wL2)
  249. }
  250. for i := 0; i < len(othrL2Txs); i++ {
  251. wL2 := wrappedL2(othrL2Txs[i])
  252. sortedTxs = append(sortedTxs, &wL2)
  253. }
  254. sort.Sort(txsSort(sortedTxs))
  255. // Store txs to DB
  256. for _, genericTx := range sortedTxs {
  257. l1 := genericTx.L1()
  258. l2 := genericTx.L2()
  259. if l1 != nil {
  260. err = h.AddL1Txs([]common.L1Tx{*l1})
  261. if err != nil {
  262. panic(err)
  263. }
  264. } else if l2 != nil {
  265. err = h.AddL2Txs([]common.L2Tx{*l2})
  266. if err != nil {
  267. panic(err)
  268. }
  269. } else {
  270. panic("should be l1 or l2")
  271. }
  272. }
  273. // Coordinators
  274. const nCoords = 10
  275. coords := test.GenCoordinators(nCoords, blocks)
  276. err = hdb.AddCoordinators(coords)
  277. if err != nil {
  278. panic(err)
  279. }
  280. fromItem := uint(0)
  281. limit := uint(99999)
  282. coordinators, _, err := hdb.GetCoordinatorsAPI(&fromItem, &limit, historydb.OrderAsc)
  283. if err != nil {
  284. panic(err)
  285. }
  286. // Bids
  287. const nBids = 10
  288. bids := test.GenBids(nBids, blocks, coords)
  289. err = hdb.AddBids(bids)
  290. if err != nil {
  291. panic(err)
  292. }
  293. // Set testCommon
  294. usrTxs, allTxs := genTestTxs(sortedTxs, usrIdxs, accs, tokensUSD, blocks)
  295. 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.
  296. testBatches, fullBatches := genTestBatches(blocks, batches, allTxs)
  297. usrExits, allExits := genTestExits(exits, tokensUSD, accs, usrIdxs)
  298. tc = testCommon{
  299. blocks: blocks,
  300. tokens: tokensUSD,
  301. batches: testBatches,
  302. fullBatches: fullBatches,
  303. coordinators: coordinators,
  304. usrAddr: ethAddrToHez(usrAddr),
  305. usrBjj: bjjToString(usrBjj),
  306. accs: accs,
  307. usrTxs: usrTxs,
  308. allTxs: allTxs,
  309. exits: allExits,
  310. usrExits: usrExits,
  311. poolTxsToSend: poolTxsToSend,
  312. poolTxsToReceive: poolTxsToReceive,
  313. auths: genTestAuths(test.GenAuths(5)),
  314. router: router,
  315. bids: genTestBids(blocks, coordinators, bids),
  316. }
  317. // Fake server
  318. if os.Getenv("FAKE_SERVER") == "yes" {
  319. for {
  320. log.Info("Running fake server at " + apiURL + " until ^C is received")
  321. time.Sleep(30 * time.Second)
  322. }
  323. }
  324. // Run tests
  325. result := m.Run()
  326. // Stop server
  327. if err := server.Shutdown(context.Background()); err != nil {
  328. panic(err)
  329. }
  330. if err := database.Close(); err != nil {
  331. panic(err)
  332. }
  333. if err := os.RemoveAll(dir); err != nil {
  334. panic(err)
  335. }
  336. os.Exit(result)
  337. }
  338. func TestGetConfig(t *testing.T) {
  339. endpoint := apiURL + "config"
  340. var configTest configAPI
  341. assert.NoError(t, doGoodReq("GET", endpoint, nil, &configTest))
  342. assert.Equal(t, config, configTest)
  343. assert.Equal(t, cg, &configTest)
  344. }
  345. func doGoodReqPaginated(
  346. path, order string,
  347. iterStruct db.Paginationer,
  348. appendIter func(res interface{}),
  349. ) error {
  350. next := 0
  351. for {
  352. // Call API to get this iteration items
  353. iterPath := path
  354. if next == 0 && order == historydb.OrderDesc {
  355. // Fetch first item in reverse order
  356. iterPath += "99999"
  357. } else {
  358. // Fetch from next item or 0 if it's ascending order
  359. iterPath += strconv.Itoa(next)
  360. }
  361. if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
  362. return err
  363. }
  364. appendIter(iterStruct)
  365. // Keep iterating?
  366. pag := iterStruct.GetPagination()
  367. if order == historydb.OrderAsc {
  368. if pag.LastReturnedItem == pag.LastItem { // No
  369. break
  370. } else { // Yes
  371. next = pag.LastReturnedItem + 1
  372. }
  373. } else {
  374. if pag.FirstReturnedItem == pag.FirstItem { // No
  375. break
  376. } else { // Yes
  377. next = pag.FirstReturnedItem - 1
  378. }
  379. }
  380. }
  381. return nil
  382. }
  383. func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
  384. ctx := context.Background()
  385. client := &http.Client{}
  386. httpReq, err := http.NewRequest(method, path, reqBody)
  387. if err != nil {
  388. return err
  389. }
  390. if reqBody != nil {
  391. httpReq.Header.Add("Content-Type", "application/json")
  392. }
  393. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  394. if err != nil {
  395. return err
  396. }
  397. // Validate request against swagger spec
  398. requestValidationInput := &swagger.RequestValidationInput{
  399. Request: httpReq,
  400. PathParams: pathParams,
  401. Route: route,
  402. }
  403. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  404. return err
  405. }
  406. // Do API call
  407. resp, err := client.Do(httpReq)
  408. if err != nil {
  409. return err
  410. }
  411. if resp.Body == nil && returnStruct != nil {
  412. return errors.New("Nil body")
  413. }
  414. //nolint
  415. defer resp.Body.Close()
  416. body, err := ioutil.ReadAll(resp.Body)
  417. if err != nil {
  418. return err
  419. }
  420. if resp.StatusCode != 200 {
  421. return fmt.Errorf("%d response. Body: %s", resp.StatusCode, string(body))
  422. }
  423. if returnStruct == nil {
  424. return nil
  425. }
  426. // Unmarshal body into return struct
  427. if err := json.Unmarshal(body, returnStruct); err != nil {
  428. log.Error("invalid json: " + string(body))
  429. return err
  430. }
  431. // Validate response against swagger spec
  432. responseValidationInput := &swagger.ResponseValidationInput{
  433. RequestValidationInput: requestValidationInput,
  434. Status: resp.StatusCode,
  435. Header: resp.Header,
  436. }
  437. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  438. return swagger.ValidateResponse(ctx, responseValidationInput)
  439. }
  440. func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
  441. ctx := context.Background()
  442. client := &http.Client{}
  443. httpReq, _ := http.NewRequest(method, path, reqBody)
  444. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  445. if err != nil {
  446. return err
  447. }
  448. // Validate request against swagger spec
  449. requestValidationInput := &swagger.RequestValidationInput{
  450. Request: httpReq,
  451. PathParams: pathParams,
  452. Route: route,
  453. }
  454. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  455. if expectedResponseCode != 400 {
  456. return err
  457. }
  458. log.Warn("The request does not match the API spec")
  459. }
  460. // Do API call
  461. resp, err := client.Do(httpReq)
  462. if err != nil {
  463. return err
  464. }
  465. if resp.Body == nil {
  466. return errors.New("Nil body")
  467. }
  468. //nolint
  469. defer resp.Body.Close()
  470. body, err := ioutil.ReadAll(resp.Body)
  471. if err != nil {
  472. return err
  473. }
  474. if resp.StatusCode != expectedResponseCode {
  475. return fmt.Errorf("Unexpected response code: %d. Body: %s", resp.StatusCode, string(body))
  476. }
  477. // Validate response against swagger spec
  478. responseValidationInput := &swagger.ResponseValidationInput{
  479. RequestValidationInput: requestValidationInput,
  480. Status: resp.StatusCode,
  481. Header: resp.Header,
  482. }
  483. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  484. return swagger.ValidateResponse(ctx, responseValidationInput)
  485. }
  486. // test helpers
  487. func getTimestamp(blockNum int64, blocks []common.Block) time.Time {
  488. for i := 0; i < len(blocks); i++ {
  489. if blocks[i].EthBlockNum == blockNum {
  490. return blocks[i].Timestamp
  491. }
  492. }
  493. panic("timesamp not found")
  494. }
  495. func getTokenByID(id common.TokenID, tokens []historydb.TokenWithUSD) historydb.TokenWithUSD {
  496. for i := 0; i < len(tokens); i++ {
  497. if tokens[i].TokenID == id {
  498. return tokens[i]
  499. }
  500. }
  501. panic("token not found")
  502. }
  503. func getTokenByIdx(idx common.Idx, tokens []historydb.TokenWithUSD, accs []common.Account) historydb.TokenWithUSD {
  504. for _, acc := range accs {
  505. if idx == acc.Idx {
  506. return getTokenByID(acc.TokenID, tokens)
  507. }
  508. }
  509. panic("token not found")
  510. }
  511. func getAccountByIdx(idx common.Idx, accs []common.Account) *common.Account {
  512. for _, acc := range accs {
  513. if acc.Idx == idx {
  514. return &acc
  515. }
  516. }
  517. panic("account not found")
  518. }
  519. func getBlockByNum(ethBlockNum int64, blocks []common.Block) common.Block {
  520. for _, b := range blocks {
  521. if b.EthBlockNum == ethBlockNum {
  522. return b
  523. }
  524. }
  525. panic("block not found")
  526. }
  527. func getCoordinatorByBidder(bidder ethCommon.Address, coordinators []historydb.CoordinatorAPI) historydb.CoordinatorAPI {
  528. for _, c := range coordinators {
  529. if c.Bidder == bidder {
  530. return c
  531. }
  532. }
  533. panic("coordinator not found")
  534. }