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.

328 lines
9.0 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
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
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
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
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. "database/sql"
  4. "errors"
  5. "net/http"
  6. "github.com/gin-gonic/gin"
  7. "github.com/hermeznetwork/hermez-node/common"
  8. "github.com/hermeznetwork/hermez-node/db/historydb"
  9. "github.com/hermeznetwork/tracerr"
  10. )
  11. // SlotAPI is a representation of a slot information
  12. type SlotAPI 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 *historydb.BidAPI `json:"bestBid"`
  19. }
  20. func (a *API) getFirstLastBlock(slotNum int64) (int64, int64) {
  21. genesisBlock := a.cg.AuctionConstants.GenesisBlockNum
  22. blocksPerSlot := int64(a.cg.AuctionConstants.BlocksPerSlot)
  23. firstBlock := slotNum*blocksPerSlot + genesisBlock
  24. lastBlock := (slotNum+1)*blocksPerSlot + genesisBlock - 1
  25. return firstBlock, lastBlock
  26. }
  27. func (a *API) getCurrentSlot(currentBlock int64) int64 {
  28. genesisBlock := a.cg.AuctionConstants.GenesisBlockNum
  29. blocksPerSlot := int64(a.cg.AuctionConstants.BlocksPerSlot)
  30. currentSlot := (currentBlock - genesisBlock) / blocksPerSlot
  31. return currentSlot
  32. }
  33. func (a *API) isOpenAuction(currentBlock, slotNum int64, auctionVars common.AuctionVariables) bool {
  34. currentSlot := a.getCurrentSlot(currentBlock)
  35. closedAuctionSlots := currentSlot + int64(auctionVars.ClosedAuctionSlots)
  36. openAuctionSlots := int64(auctionVars.OpenAuctionSlots)
  37. if slotNum > closedAuctionSlots && slotNum <= (closedAuctionSlots+openAuctionSlots) {
  38. return true
  39. }
  40. return false
  41. }
  42. func (a *API) newSlotAPI(slotNum, currentBlockNum int64, bid *historydb.BidAPI, auctionVars *common.AuctionVariables) SlotAPI {
  43. firstBlock, lastBlock := a.getFirstLastBlock(slotNum)
  44. openAuction := a.isOpenAuction(currentBlockNum, slotNum, *auctionVars)
  45. slot := SlotAPI{
  46. ItemID: uint64(slotNum),
  47. SlotNum: slotNum,
  48. FirstBlock: firstBlock,
  49. LastBlock: lastBlock,
  50. OpenAuction: openAuction,
  51. WinnerBid: bid,
  52. }
  53. return slot
  54. }
  55. func (a *API) newSlotsAPIFromWinnerBids(fromItem *uint, order string, bids []historydb.BidAPI, currentBlockNum int64, auctionVars *common.AuctionVariables) (slots []SlotAPI) {
  56. for i := range bids {
  57. slotNum := bids[i].SlotNum
  58. slot := a.newSlotAPI(slotNum, currentBlockNum, &bids[i], auctionVars)
  59. if order == historydb.OrderAsc {
  60. if fromItem == nil || slot.ItemID >= uint64(*fromItem) {
  61. slots = append(slots, slot)
  62. }
  63. } else {
  64. if fromItem == nil || slot.ItemID <= uint64(*fromItem) {
  65. slots = append(slots, slot)
  66. }
  67. }
  68. }
  69. return slots
  70. }
  71. func (a *API) addEmptySlot(slots []SlotAPI, slotNum int64, currentBlockNum int64, auctionVars *common.AuctionVariables, fromItem *uint, order string) ([]SlotAPI, error) {
  72. emptySlot := a.newSlotAPI(slotNum, currentBlockNum, nil, auctionVars)
  73. if order == historydb.OrderAsc {
  74. if fromItem == nil || emptySlot.ItemID >= uint64(*fromItem) {
  75. slots = append(slots, emptySlot)
  76. }
  77. } else {
  78. if fromItem == nil || emptySlot.ItemID <= uint64(*fromItem) {
  79. slots = append([]SlotAPI{emptySlot}, slots...)
  80. }
  81. }
  82. return slots, nil
  83. }
  84. func (a *API) getSlot(c *gin.Context) {
  85. slotNumUint, err := parseParamUint("slotNum", nil, 0, maxUint32, c)
  86. if err != nil {
  87. retBadReq(err, c)
  88. return
  89. }
  90. currentBlock, err := a.h.GetLastBlockAPI()
  91. if err != nil {
  92. retBadReq(err, c)
  93. return
  94. }
  95. auctionVars, err := a.h.GetAuctionVarsAPI()
  96. if err != nil {
  97. retBadReq(err, c)
  98. return
  99. }
  100. slotNum := int64(*slotNumUint)
  101. bid, err := a.h.GetBestBidAPI(&slotNum)
  102. if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
  103. retSQLErr(err, c)
  104. return
  105. }
  106. var slot SlotAPI
  107. if tracerr.Unwrap(err) == sql.ErrNoRows {
  108. slot = a.newSlotAPI(slotNum, currentBlock.Num, nil, auctionVars)
  109. } else {
  110. slot = a.newSlotAPI(bid.SlotNum, currentBlock.Num, &bid, auctionVars)
  111. }
  112. // JSON response
  113. c.JSON(http.StatusOK, slot)
  114. }
  115. func getLimits(
  116. minSlotNum, maxSlotNum int64, fromItem, limit *uint, order string,
  117. ) (minLimit, maxLimit int64, pendingItems uint64) {
  118. if order == historydb.OrderAsc {
  119. if fromItem != nil && int64(*fromItem) > minSlotNum {
  120. minLimit = int64(*fromItem)
  121. } else {
  122. minLimit = minSlotNum
  123. }
  124. if limit != nil && (minLimit+int64(*limit-1)) < maxSlotNum {
  125. maxLimit = minLimit + int64(*limit-1)
  126. } else {
  127. maxLimit = maxSlotNum
  128. }
  129. pendingItems = uint64(maxSlotNum - maxLimit)
  130. } else {
  131. if fromItem != nil && int64(*fromItem) < maxSlotNum {
  132. maxLimit = int64(*fromItem)
  133. } else {
  134. maxLimit = maxSlotNum
  135. }
  136. if limit != nil && (maxLimit-int64(*limit-1)) < minSlotNum {
  137. minLimit = minSlotNum
  138. } else {
  139. minLimit = maxLimit - int64(*limit-1)
  140. }
  141. pendingItems = uint64(-(minSlotNum - minLimit))
  142. }
  143. return minLimit, maxLimit, pendingItems
  144. }
  145. func getLimitsWithAddr(minSlotNum, maxSlotNum *int64, fromItem, limit *uint, order string) (int64, int64) {
  146. var minLim, maxLim int64
  147. if fromItem != nil {
  148. if order == historydb.OrderAsc {
  149. maxLim = *maxSlotNum
  150. if int64(*fromItem) > *minSlotNum {
  151. minLim = int64(*fromItem)
  152. } else {
  153. minLim = *minSlotNum
  154. }
  155. } else {
  156. minLim = *minSlotNum
  157. if int64(*fromItem) < *maxSlotNum {
  158. maxLim = int64(*fromItem)
  159. } else {
  160. maxLim = *maxSlotNum
  161. }
  162. }
  163. } else {
  164. maxLim = *maxSlotNum
  165. minLim = *minSlotNum
  166. }
  167. return minLim, maxLim
  168. }
  169. func (a *API) getSlots(c *gin.Context) {
  170. var slots []SlotAPI
  171. minSlotNumDflt := int64(0)
  172. // Get filters
  173. minSlotNum, maxSlotNum, wonByEthereumAddress, finishedAuction, err := parseSlotFilters(c)
  174. if err != nil {
  175. retBadReq(err, c)
  176. return
  177. }
  178. // Pagination
  179. fromItem, order, limit, err := parsePagination(c)
  180. if err != nil {
  181. retBadReq(err, c)
  182. return
  183. }
  184. currentBlock, err := a.h.GetLastBlockAPI()
  185. if err != nil {
  186. retBadReq(err, c)
  187. return
  188. }
  189. auctionVars, err := a.h.GetAuctionVarsAPI()
  190. if err != nil {
  191. retBadReq(err, c)
  192. return
  193. }
  194. // Check filters
  195. if maxSlotNum == nil && finishedAuction == nil {
  196. retBadReq(errors.New("It is necessary to add maxSlotNum filter"), c)
  197. return
  198. } else if finishedAuction != nil {
  199. if maxSlotNum == nil && !*finishedAuction {
  200. retBadReq(errors.New("It is necessary to add maxSlotNum filter"), c)
  201. return
  202. } else if *finishedAuction {
  203. currentBlock, err := a.h.GetLastBlockAPI()
  204. if err != nil {
  205. retBadReq(err, c)
  206. return
  207. }
  208. currentSlot := a.getCurrentSlot(currentBlock.Num)
  209. auctionVars, err := a.h.GetAuctionVarsAPI()
  210. if err != nil {
  211. retBadReq(err, c)
  212. return
  213. }
  214. closedAuctionSlots := currentSlot + int64(auctionVars.ClosedAuctionSlots)
  215. if maxSlotNum == nil {
  216. maxSlotNum = &closedAuctionSlots
  217. } else if closedAuctionSlots < *maxSlotNum {
  218. maxSlotNum = &closedAuctionSlots
  219. }
  220. }
  221. } else if maxSlotNum != nil && minSlotNum != nil {
  222. if *minSlotNum > *maxSlotNum {
  223. retBadReq(errors.New("It is necessary to add valid filter (minSlotNum <= maxSlotNum)"), c)
  224. return
  225. }
  226. }
  227. if minSlotNum == nil {
  228. minSlotNum = &minSlotNumDflt
  229. }
  230. // Get bids and pagination according to filters
  231. var slotMinLim, slotMaxLim int64
  232. var bids []historydb.BidAPI
  233. var pendingItems uint64
  234. if wonByEthereumAddress == nil {
  235. slotMinLim, slotMaxLim, pendingItems = getLimits(*minSlotNum, *maxSlotNum, fromItem, limit, order)
  236. // Get best bids in range maxSlotNum - minSlotNum
  237. bids, _, err = a.h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, nil, order)
  238. if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
  239. retSQLErr(err, c)
  240. return
  241. }
  242. } else {
  243. slotMinLim, slotMaxLim = getLimitsWithAddr(minSlotNum, maxSlotNum, fromItem, limit, order)
  244. bids, pendingItems, err = a.h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, limit, order)
  245. if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
  246. retSQLErr(err, c)
  247. return
  248. }
  249. }
  250. // Build the slot information with previous bids
  251. var slotsBids []SlotAPI
  252. if len(bids) > 0 {
  253. slotsBids = a.newSlotsAPIFromWinnerBids(fromItem, order, bids, currentBlock.Num, auctionVars)
  254. if err != nil {
  255. retBadReq(err, c)
  256. return
  257. }
  258. }
  259. // Build the other slots
  260. if wonByEthereumAddress == nil {
  261. // Build hte information of the slots with bids or not
  262. for i := slotMinLim; i <= slotMaxLim; i++ {
  263. found := false
  264. for j := range slotsBids {
  265. if slotsBids[j].SlotNum == i {
  266. found = true
  267. if order == historydb.OrderAsc {
  268. if fromItem == nil || slotsBids[j].ItemID >= uint64(*fromItem) {
  269. slots = append(slots, slotsBids[j])
  270. }
  271. } else {
  272. if fromItem == nil || slotsBids[j].ItemID <= uint64(*fromItem) {
  273. slots = append([]SlotAPI{slotsBids[j]}, slots...)
  274. }
  275. }
  276. break
  277. }
  278. }
  279. if !found {
  280. slots, err = a.addEmptySlot(slots, i, currentBlock.Num, auctionVars, fromItem, order)
  281. if err != nil {
  282. retBadReq(err, c)
  283. return
  284. }
  285. }
  286. }
  287. } else if len(slotsBids) > 0 {
  288. slots = slotsBids
  289. }
  290. if len(slots) == 0 {
  291. retSQLErr(sql.ErrNoRows, c)
  292. return
  293. }
  294. // Build successful response
  295. type slotsResponse struct {
  296. Slots []SlotAPI `json:"slots"`
  297. PendingItems uint64 `json:"pendingItems"`
  298. }
  299. c.JSON(http.StatusOK, &slotsResponse{
  300. Slots: slots,
  301. PendingItems: pendingItems,
  302. })
  303. }