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.

301 lines
9.9 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.
3 years ago
  1. package api
  2. import (
  3. "fmt"
  4. "strconv"
  5. "testing"
  6. "github.com/hermeznetwork/hermez-node/common"
  7. "github.com/hermeznetwork/hermez-node/db/historydb"
  8. "github.com/mitchellh/copystructure"
  9. "github.com/stretchr/testify/assert"
  10. )
  11. type testSlot struct {
  12. ItemID uint64 `json:"itemId"`
  13. SlotNum int64 `json:"slotNum"`
  14. FirstBlock int64 `json:"firstBlock"`
  15. LastBlock int64 `json:"lastBlock"`
  16. OpenAuction bool `json:"openAuction"`
  17. WinnerBid *testBid `json:"bestBid"`
  18. }
  19. type testSlotsResponse struct {
  20. Slots []testSlot `json:"slots"`
  21. PendingItems uint64 `json:"pendingItems"`
  22. }
  23. func (t testSlotsResponse) GetPending() (pendingItems, lastItemID uint64) {
  24. pendingItems = t.PendingItems
  25. lastItemID = t.Slots[len(t.Slots)-1].ItemID
  26. return pendingItems, lastItemID
  27. }
  28. func (t testSlotsResponse) Len() int {
  29. return len(t.Slots)
  30. }
  31. func (t testSlotsResponse) New() Pendinger { return &testSlotsResponse{} }
  32. func (a *API) genTestSlots(nSlots int, lastBlockNum int64, bids []testBid, auctionVars common.AuctionVariables) []testSlot {
  33. tSlots := []testSlot{}
  34. bestBids := make(map[int64]testBid)
  35. // It's assumed that bids for each slot will be received in increasing order
  36. for i := range bids {
  37. bestBids[bids[i].SlotNum] = bids[i]
  38. }
  39. for i := int64(0); i < int64(nSlots); i++ {
  40. bid, ok := bestBids[i]
  41. firstBlock, lastBlock := a.getFirstLastBlock(int64(i))
  42. tSlot := testSlot{
  43. SlotNum: int64(i),
  44. FirstBlock: firstBlock,
  45. LastBlock: lastBlock,
  46. OpenAuction: a.isOpenAuction(lastBlockNum, int64(i), auctionVars),
  47. }
  48. if ok {
  49. tSlot.WinnerBid = &bid
  50. }
  51. tSlots = append(tSlots, tSlot)
  52. }
  53. return tSlots
  54. }
  55. func (a *API) getEmptyTestSlot(slotNum int64) testSlot {
  56. firstBlock, lastBlock := a.getFirstLastBlock(slotNum)
  57. slot := testSlot{
  58. SlotNum: slotNum,
  59. FirstBlock: firstBlock,
  60. LastBlock: lastBlock,
  61. OpenAuction: false,
  62. WinnerBid: nil,
  63. }
  64. return slot
  65. }
  66. func TestGetSlot(t *testing.T) {
  67. endpoint := apiURL + "slots/"
  68. for _, slot := range tc.slots {
  69. fetchedSlot := testSlot{}
  70. assert.NoError(
  71. t, doGoodReq(
  72. "GET",
  73. endpoint+strconv.Itoa(int(slot.SlotNum)),
  74. nil, &fetchedSlot,
  75. ),
  76. )
  77. assertSlot(t, slot, fetchedSlot)
  78. }
  79. // Slot with WinnerBid == nil
  80. slotNum := int64(15)
  81. fetchedSlot := testSlot{}
  82. assert.NoError(
  83. t, doGoodReq(
  84. "GET",
  85. endpoint+strconv.Itoa(int(slotNum)),
  86. nil, &fetchedSlot,
  87. ),
  88. )
  89. emptySlot := api.getEmptyTestSlot(slotNum)
  90. assertSlot(t, emptySlot, fetchedSlot)
  91. // Invalid slotNum
  92. path := endpoint + strconv.Itoa(-2)
  93. err := doBadReq("GET", path, nil, 400)
  94. assert.NoError(t, err)
  95. }
  96. func TestGetSlots(t *testing.T) {
  97. endpoint := apiURL + "slots"
  98. fetchedSlots := []testSlot{}
  99. appendIter := func(intr interface{}) {
  100. for i := 0; i < len(intr.(*testSlotsResponse).Slots); i++ {
  101. tmp, err := copystructure.Copy(intr.(*testSlotsResponse).Slots[i])
  102. if err != nil {
  103. panic(err)
  104. }
  105. fetchedSlots = append(fetchedSlots, tmp.(testSlot))
  106. }
  107. }
  108. // All slots with maxSlotNum filter
  109. maxSlotNum := tc.slots[len(tc.slots)-1].SlotNum + 5
  110. limit := 1
  111. path := fmt.Sprintf("%s?maxSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, limit)
  112. err := doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  113. assert.NoError(t, err)
  114. allSlots := tc.slots
  115. for i := tc.slots[len(tc.slots)-1].SlotNum; i < maxSlotNum; i++ {
  116. emptySlot := api.getEmptyTestSlot(i + 1)
  117. allSlots = append(allSlots, emptySlot)
  118. }
  119. assertSlots(t, allSlots, fetchedSlots)
  120. // All slots with maxSlotNum filter, in reverse order
  121. fetchedSlots = []testSlot{}
  122. limit = 3
  123. path = fmt.Sprintf("%s?maxSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, limit)
  124. err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
  125. assert.NoError(t, err)
  126. flippedAllSlots := []testSlot{}
  127. for i := len(allSlots) - 1; i >= 0; i-- {
  128. flippedAllSlots = append(flippedAllSlots, allSlots[i])
  129. }
  130. assertSlots(t, flippedAllSlots, fetchedSlots)
  131. // maxSlotNum & wonByEthereumAddress
  132. fetchedSlots = []testSlot{}
  133. limit = 1
  134. bidderAddr := tc.coordinators[2].Bidder
  135. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, bidderAddr.String(), limit)
  136. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  137. assert.NoError(t, err)
  138. bidderAddressSlots := []testSlot{}
  139. for i := 0; i < len(tc.slots); i++ {
  140. if tc.slots[i].WinnerBid != nil {
  141. if tc.slots[i].WinnerBid.Bidder == bidderAddr {
  142. bidderAddressSlots = append(bidderAddressSlots, tc.slots[i])
  143. }
  144. }
  145. }
  146. assertSlots(t, bidderAddressSlots, fetchedSlots)
  147. // maxSlotNum & wonByEthereumAddress, in reverse order
  148. fetchedSlots = []testSlot{}
  149. limit = 1
  150. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, bidderAddr.String(), limit)
  151. err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
  152. assert.NoError(t, err)
  153. flippedBidderAddressSlots := []testSlot{}
  154. for i := len(bidderAddressSlots) - 1; i >= 0; i-- {
  155. flippedBidderAddressSlots = append(flippedBidderAddressSlots, bidderAddressSlots[i])
  156. }
  157. assertSlots(t, flippedBidderAddressSlots, fetchedSlots)
  158. // finishedAuction
  159. fetchedSlots = []testSlot{}
  160. limit = 15
  161. path = fmt.Sprintf("%s?finishedAuction=%t&limit=%d&fromItem=", endpoint, true, limit)
  162. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  163. assert.NoError(t, err)
  164. currentSlot := api.getCurrentSlot(tc.blocks[len(tc.blocks)-1].Num)
  165. finishedAuctionSlots := []testSlot{}
  166. for i := 0; i < len(tc.slots); i++ {
  167. finishAuction := currentSlot + int64(tc.auctionVars.ClosedAuctionSlots)
  168. if tc.slots[i].SlotNum <= finishAuction {
  169. finishedAuctionSlots = append(finishedAuctionSlots, tc.slots[i])
  170. } else {
  171. break
  172. }
  173. }
  174. assertSlots(t, finishedAuctionSlots, fetchedSlots)
  175. //minSlot + maxSlot
  176. limit = 10
  177. minSlotNum := tc.slots[3].SlotNum
  178. maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum - 1
  179. fetchedSlots = []testSlot{}
  180. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  181. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  182. assert.NoError(t, err)
  183. minMaxBatchNumSlots := []testSlot{}
  184. for i := 0; i < len(tc.slots); i++ {
  185. if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum {
  186. minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i])
  187. }
  188. }
  189. assertSlots(t, minMaxBatchNumSlots, fetchedSlots)
  190. //minSlot + maxSlot
  191. limit = 15
  192. minSlotNum = tc.slots[0].SlotNum
  193. maxSlotNum = tc.slots[0].SlotNum
  194. fetchedSlots = []testSlot{}
  195. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  196. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  197. assert.NoError(t, err)
  198. minMaxBatchNumSlots = []testSlot{}
  199. for i := 0; i < len(tc.slots); i++ {
  200. if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum {
  201. minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i])
  202. }
  203. }
  204. assertSlots(t, minMaxBatchNumSlots, fetchedSlots)
  205. // Only empty Slots
  206. limit = 2
  207. minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1
  208. maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5
  209. fetchedSlots = []testSlot{}
  210. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  211. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  212. assert.NoError(t, err)
  213. emptySlots := []testSlot{}
  214. for i := 0; i < len(allSlots); i++ {
  215. if allSlots[i].SlotNum >= minSlotNum && allSlots[i].SlotNum <= maxSlotNum {
  216. emptySlots = append(emptySlots, allSlots[i])
  217. }
  218. }
  219. assertSlots(t, emptySlots, fetchedSlots)
  220. // Only empty Slots, in reverse order
  221. limit = 4
  222. minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1
  223. maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5
  224. fetchedSlots = []testSlot{}
  225. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  226. err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
  227. assert.NoError(t, err)
  228. flippedEmptySlots := []testSlot{}
  229. for i := 0; i < len(flippedAllSlots); i++ {
  230. if flippedAllSlots[i].SlotNum >= minSlotNum && flippedAllSlots[i].SlotNum <= maxSlotNum {
  231. flippedEmptySlots = append(flippedEmptySlots, flippedAllSlots[i])
  232. }
  233. }
  234. assertSlots(t, flippedEmptySlots, fetchedSlots)
  235. // 400
  236. // No filters
  237. path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
  238. err = doBadReq("GET", path, nil, 400)
  239. assert.NoError(t, err)
  240. // Invalid maxSlotNum
  241. path = fmt.Sprintf("%s?maxSlotNum=%d", endpoint, -2)
  242. err = doBadReq("GET", path, nil, 400)
  243. assert.NoError(t, err)
  244. // Invalid wonByEthereumAddress
  245. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s", endpoint, maxSlotNum, "0xG0000001")
  246. err = doBadReq("GET", path, nil, 400)
  247. assert.NoError(t, err)
  248. // Invalid minSlotNum / maxSlotNum (minSlotNum > maxSlotNum)
  249. maxSlotNum = tc.slots[1].SlotNum
  250. minSlotNum = tc.slots[4].SlotNum
  251. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  252. err = doBadReq("GET", path, nil, 400)
  253. assert.NoError(t, err)
  254. // 404
  255. maxSlotNum = tc.slots[1].SlotNum
  256. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, tc.coordinators[3].Bidder.String(), limit)
  257. err = doBadReq("GET", path, nil, 404)
  258. assert.NoError(t, err)
  259. }
  260. func assertSlots(t *testing.T, expected, actual []testSlot) {
  261. assert.Equal(t, len(expected), len(actual))
  262. for i := 0; i < len(expected); i++ {
  263. assertSlot(t, expected[i], actual[i])
  264. }
  265. }
  266. func assertSlot(t *testing.T, expected, actual testSlot) {
  267. if actual.WinnerBid != nil {
  268. assert.Equal(t, expected.WinnerBid.Timestamp.Unix(), actual.WinnerBid.Timestamp.Unix())
  269. expected.WinnerBid.Timestamp = actual.WinnerBid.Timestamp
  270. actual.WinnerBid.ItemID = expected.WinnerBid.ItemID
  271. }
  272. actual.ItemID = expected.ItemID
  273. assert.Equal(t, expected, actual)
  274. }