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.

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