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.

289 lines
9.1 KiB

Update coordinator, call all api update functions - Common: - Rename Block.EthBlockNum to Block.Num to avoid unneeded repetition - API: - Add UpdateNetworkInfoBlock to update just block information, to be used when the node is not yet synchronized - Node: - Call API.UpdateMetrics and UpdateRecommendedFee in a loop, with configurable time intervals - Synchronizer: - When mapping events by TxHash, use an array to support the possibility of multiple calls of the same function happening in the same transaction (for example, a smart contract in a single transaction could call withdraw with delay twice, which would generate 2 withdraw events, and 2 deposit events). - In Stats, keep entire LastBlock instead of just the blockNum - In Stats, add lastL1BatchBlock - Test Stats and SCVars - Coordinator: - Enable writing the BatchInfo in every step of the pipeline to disk (with JSON text files) for debugging purposes. - Move the Pipeline functionality from the Coordinator to its own struct (Pipeline) - Implement shouldL1lL2Batch - In TxManager, implement logic to perform several attempts when doing ethereum node RPC calls before considering the error. (Both for calls to forgeBatch and transaction receipt) - In TxManager, reorganize the flow and note the specific points in which actions are made when err != nil - HistoryDB: - Implement GetLastL1BatchBlockNum: returns the blockNum of the latest forged l1Batch, to help the coordinator decide when to forge an L1Batch. - EthereumClient and test.Client: - Update EthBlockByNumber to return the last block when the passed number is -1.
4 years ago
  1. package api
  2. import (
  3. "fmt"
  4. "strconv"
  5. "testing"
  6. "time"
  7. ethCommon "github.com/ethereum/go-ethereum/common"
  8. "github.com/hermeznetwork/hermez-node/common"
  9. "github.com/hermeznetwork/hermez-node/db/historydb"
  10. "github.com/mitchellh/copystructure"
  11. "github.com/stretchr/testify/assert"
  12. )
  13. type testBatch struct {
  14. ItemID uint64 `json:"itemId"`
  15. BatchNum common.BatchNum `json:"batchNum"`
  16. EthBlockNum int64 `json:"ethereumBlockNum"`
  17. EthBlockHash ethCommon.Hash `json:"ethereumBlockHash"`
  18. Timestamp time.Time `json:"timestamp"`
  19. ForgerAddr ethCommon.Address `json:"forgerAddr"`
  20. CollectedFees map[common.TokenID]string `json:"collectedFees"`
  21. TotalFeesUSD *float64 `json:"historicTotalCollectedFeesUSD"`
  22. StateRoot string `json:"stateRoot"`
  23. NumAccounts int `json:"numAccounts"`
  24. ExitRoot string `json:"exitRoot"`
  25. ForgeL1TxsNum *int64 `json:"forgeL1TransactionsNum"`
  26. SlotNum int64 `json:"slotNum"`
  27. ForgedTxs int `json:"forgedTransactions"`
  28. }
  29. type testBatchesResponse struct {
  30. Batches []testBatch `json:"batches"`
  31. PendingItems uint64 `json:"pendingItems"`
  32. }
  33. func (t testBatchesResponse) GetPending() (pendingItems, lastItemID uint64) {
  34. pendingItems = t.PendingItems
  35. lastItemID = t.Batches[len(t.Batches)-1].ItemID
  36. return pendingItems, lastItemID
  37. }
  38. func (t testBatchesResponse) Len() int {
  39. return len(t.Batches)
  40. }
  41. func (t testBatchesResponse) New() Pendinger { return &testBatchesResponse{} }
  42. type testFullBatch struct {
  43. Batch testBatch `json:"batch"`
  44. Txs []testTx `json:"transactions"`
  45. }
  46. func genTestBatches(
  47. blocks []common.Block,
  48. cBatches []common.Batch,
  49. txs []testTx,
  50. ) ([]testBatch, []testFullBatch) {
  51. tBatches := []testBatch{}
  52. for i := 0; i < len(cBatches); i++ {
  53. block := common.Block{}
  54. found := false
  55. for _, b := range blocks {
  56. if b.Num == cBatches[i].EthBlockNum {
  57. block = b
  58. found = true
  59. break
  60. }
  61. }
  62. if !found {
  63. panic("block not found")
  64. }
  65. collectedFees := make(map[common.TokenID]string)
  66. for k, v := range cBatches[i].CollectedFees {
  67. collectedFees[k] = v.String()
  68. }
  69. forgedTxs := 0
  70. for _, tx := range txs {
  71. if tx.BatchNum != nil && *tx.BatchNum == cBatches[i].BatchNum {
  72. forgedTxs++
  73. }
  74. }
  75. tBatch := testBatch{
  76. BatchNum: cBatches[i].BatchNum,
  77. EthBlockNum: cBatches[i].EthBlockNum,
  78. EthBlockHash: block.Hash,
  79. Timestamp: block.Timestamp,
  80. ForgerAddr: cBatches[i].ForgerAddr,
  81. CollectedFees: collectedFees,
  82. TotalFeesUSD: cBatches[i].TotalFeesUSD,
  83. StateRoot: cBatches[i].StateRoot.String(),
  84. NumAccounts: cBatches[i].NumAccounts,
  85. ExitRoot: cBatches[i].ExitRoot.String(),
  86. ForgeL1TxsNum: cBatches[i].ForgeL1TxsNum,
  87. SlotNum: cBatches[i].SlotNum,
  88. ForgedTxs: forgedTxs,
  89. }
  90. tBatches = append(tBatches, tBatch)
  91. }
  92. fullBatches := []testFullBatch{}
  93. for i := 0; i < len(tBatches); i++ {
  94. forgedTxs := []testTx{}
  95. for j := 0; j < len(txs); j++ {
  96. if txs[j].BatchNum != nil && *txs[j].BatchNum == tBatches[i].BatchNum {
  97. forgedTxs = append(forgedTxs, txs[j])
  98. }
  99. }
  100. fullBatches = append(fullBatches, testFullBatch{
  101. Batch: tBatches[i],
  102. Txs: forgedTxs,
  103. })
  104. }
  105. return tBatches, fullBatches
  106. }
  107. func TestGetBatches(t *testing.T) {
  108. endpoint := apiURL + "batches"
  109. fetchedBatches := []testBatch{}
  110. appendIter := func(intr interface{}) {
  111. for i := 0; i < len(intr.(*testBatchesResponse).Batches); i++ {
  112. tmp, err := copystructure.Copy(intr.(*testBatchesResponse).Batches[i])
  113. if err != nil {
  114. panic(err)
  115. }
  116. fetchedBatches = append(fetchedBatches, tmp.(testBatch))
  117. }
  118. }
  119. // Get all (no filters)
  120. limit := 3
  121. path := fmt.Sprintf("%s?limit=%d", endpoint, limit)
  122. err := doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
  123. assert.NoError(t, err)
  124. assertBatches(t, tc.batches, fetchedBatches)
  125. // minBatchNum
  126. fetchedBatches = []testBatch{}
  127. limit = 2
  128. minBatchNum := tc.batches[len(tc.batches)/2].BatchNum
  129. path = fmt.Sprintf("%s?minBatchNum=%d&limit=%d", endpoint, minBatchNum, limit)
  130. err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
  131. assert.NoError(t, err)
  132. minBatchNumBatches := []testBatch{}
  133. for i := 0; i < len(tc.batches); i++ {
  134. if tc.batches[i].BatchNum > minBatchNum {
  135. minBatchNumBatches = append(minBatchNumBatches, tc.batches[i])
  136. }
  137. }
  138. assertBatches(t, minBatchNumBatches, fetchedBatches)
  139. // maxBatchNum
  140. fetchedBatches = []testBatch{}
  141. limit = 1
  142. maxBatchNum := tc.batches[len(tc.batches)/2].BatchNum
  143. path = fmt.Sprintf("%s?maxBatchNum=%d&limit=%d", endpoint, maxBatchNum, limit)
  144. err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
  145. assert.NoError(t, err)
  146. maxBatchNumBatches := []testBatch{}
  147. for i := 0; i < len(tc.batches); i++ {
  148. if tc.batches[i].BatchNum < maxBatchNum {
  149. maxBatchNumBatches = append(maxBatchNumBatches, tc.batches[i])
  150. }
  151. }
  152. assertBatches(t, maxBatchNumBatches, fetchedBatches)
  153. // slotNum
  154. fetchedBatches = []testBatch{}
  155. limit = 5
  156. slotNum := tc.batches[len(tc.batches)/2].SlotNum
  157. path = fmt.Sprintf("%s?slotNum=%d&limit=%d", endpoint, slotNum, limit)
  158. err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
  159. assert.NoError(t, err)
  160. slotNumBatches := []testBatch{}
  161. for i := 0; i < len(tc.batches); i++ {
  162. if tc.batches[i].SlotNum == slotNum {
  163. slotNumBatches = append(slotNumBatches, tc.batches[i])
  164. }
  165. }
  166. assertBatches(t, slotNumBatches, fetchedBatches)
  167. // forgerAddr
  168. fetchedBatches = []testBatch{}
  169. limit = 10
  170. forgerAddr := tc.batches[len(tc.batches)/2].ForgerAddr
  171. path = fmt.Sprintf("%s?forgerAddr=%s&limit=%d", endpoint, forgerAddr.String(), limit)
  172. err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
  173. assert.NoError(t, err)
  174. forgerAddrBatches := []testBatch{}
  175. for i := 0; i < len(tc.batches); i++ {
  176. if tc.batches[i].ForgerAddr == forgerAddr {
  177. forgerAddrBatches = append(forgerAddrBatches, tc.batches[i])
  178. }
  179. }
  180. assertBatches(t, forgerAddrBatches, fetchedBatches)
  181. // All, in reverse order
  182. fetchedBatches = []testBatch{}
  183. limit = 6
  184. path = fmt.Sprintf("%s?limit=%d", endpoint, limit)
  185. err = doGoodReqPaginated(path, historydb.OrderDesc, &testBatchesResponse{}, appendIter)
  186. assert.NoError(t, err)
  187. flippedBatches := []testBatch{}
  188. for i := len(tc.batches) - 1; i >= 0; i-- {
  189. flippedBatches = append(flippedBatches, tc.batches[i])
  190. }
  191. assertBatches(t, flippedBatches, fetchedBatches)
  192. // Mixed filters
  193. fetchedBatches = []testBatch{}
  194. limit = 1
  195. maxBatchNum = tc.batches[len(tc.batches)-len(tc.batches)/4].BatchNum
  196. minBatchNum = tc.batches[len(tc.batches)/4].BatchNum
  197. path = fmt.Sprintf("%s?minBatchNum=%d&maxBatchNum=%d&limit=%d", endpoint, minBatchNum, maxBatchNum, limit)
  198. err = doGoodReqPaginated(path, historydb.OrderAsc, &testBatchesResponse{}, appendIter)
  199. assert.NoError(t, err)
  200. minMaxBatchNumBatches := []testBatch{}
  201. for i := 0; i < len(tc.batches); i++ {
  202. if tc.batches[i].BatchNum < maxBatchNum && tc.batches[i].BatchNum > minBatchNum {
  203. minMaxBatchNumBatches = append(minMaxBatchNumBatches, tc.batches[i])
  204. }
  205. }
  206. assertBatches(t, minMaxBatchNumBatches, fetchedBatches)
  207. // 400
  208. // Invalid minBatchNum
  209. path = fmt.Sprintf("%s?minBatchNum=%d", endpoint, -2)
  210. err = doBadReq("GET", path, nil, 400)
  211. assert.NoError(t, err)
  212. // Invalid forgerAddr
  213. path = fmt.Sprintf("%s?forgerAddr=%s", endpoint, "0xG0000001")
  214. err = doBadReq("GET", path, nil, 400)
  215. assert.NoError(t, err)
  216. // 404
  217. path = fmt.Sprintf("%s?slotNum=%d&minBatchNum=%d", endpoint, 1, 25)
  218. err = doBadReq("GET", path, nil, 404)
  219. assert.NoError(t, err)
  220. }
  221. func TestGetBatch(t *testing.T) {
  222. endpoint := apiURL + "batches/"
  223. for _, batch := range tc.batches {
  224. fetchedBatch := testBatch{}
  225. assert.NoError(
  226. t, doGoodReq(
  227. "GET",
  228. endpoint+strconv.Itoa(int(batch.BatchNum)),
  229. nil, &fetchedBatch,
  230. ),
  231. )
  232. assertBatch(t, batch, fetchedBatch)
  233. }
  234. // 400
  235. assert.NoError(t, doBadReq("GET", endpoint+"foo", nil, 400))
  236. // 404
  237. assert.NoError(t, doBadReq("GET", endpoint+"99999", nil, 404))
  238. }
  239. func TestGetFullBatch(t *testing.T) {
  240. endpoint := apiURL + "full-batches/"
  241. for _, fullBatch := range tc.fullBatches {
  242. fetchedFullBatch := testFullBatch{}
  243. assert.NoError(
  244. t, doGoodReq(
  245. "GET",
  246. endpoint+strconv.Itoa(int(fullBatch.Batch.BatchNum)),
  247. nil, &fetchedFullBatch,
  248. ),
  249. )
  250. assertBatch(t, fullBatch.Batch, fetchedFullBatch.Batch)
  251. assertTxs(t, fullBatch.Txs, fetchedFullBatch.Txs)
  252. }
  253. // 400
  254. assert.NoError(t, doBadReq("GET", endpoint+"foo", nil, 400))
  255. // 404
  256. assert.NoError(t, doBadReq("GET", endpoint+"99999", nil, 404))
  257. }
  258. func assertBatches(t *testing.T, expected, actual []testBatch) {
  259. assert.Equal(t, len(expected), len(actual))
  260. for i := 0; i < len(expected); i++ {
  261. assertBatch(t, expected[i], actual[i])
  262. }
  263. }
  264. func assertBatch(t *testing.T, expected, actual testBatch) {
  265. assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
  266. expected.Timestamp = actual.Timestamp
  267. actual.ItemID = expected.ItemID
  268. assert.Equal(t, expected, actual)
  269. }