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.

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