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.

314 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
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. "fmt"
  5. "math"
  6. "math/big"
  7. "net/http"
  8. "time"
  9. "github.com/gin-gonic/gin"
  10. "github.com/hermeznetwork/hermez-node/apitypes"
  11. "github.com/hermeznetwork/hermez-node/common"
  12. "github.com/hermeznetwork/hermez-node/db/historydb"
  13. "github.com/hermeznetwork/tracerr"
  14. )
  15. // Network define status of the network
  16. type Network struct {
  17. LastEthBlock int64 `json:"lastEthereumBlock"`
  18. LastSyncBlock int64 `json:"lastSynchedBlock"`
  19. LastBatch *historydb.BatchAPI `json:"lastBatch"`
  20. CurrentSlot int64 `json:"currentSlot"`
  21. NextForgers []NextForger `json:"nextForgers"`
  22. }
  23. // NextForger is a representation of the information of a coordinator and the period will forge
  24. type NextForger struct {
  25. Coordinator historydb.CoordinatorAPI `json:"coordinator"`
  26. Period Period `json:"period"`
  27. }
  28. // Period is a representation of a period
  29. type Period struct {
  30. SlotNum int64 `json:"slotNum"`
  31. FromBlock int64 `json:"fromBlock"`
  32. ToBlock int64 `json:"toBlock"`
  33. FromTimestamp time.Time `json:"fromTimestamp"`
  34. ToTimestamp time.Time `json:"toTimestamp"`
  35. }
  36. func (a *API) getState(c *gin.Context) {
  37. // TODO: There are no events for the buckets information, so now this information will be 0
  38. a.status.RLock()
  39. status := a.status //nolint
  40. a.status.RUnlock()
  41. c.JSON(http.StatusOK, status) //nolint
  42. }
  43. // SC Vars
  44. // SetRollupVariables set Status.Rollup variables
  45. func (a *API) SetRollupVariables(rollupVariables common.RollupVariables) {
  46. a.status.Lock()
  47. var rollupVAPI historydb.RollupVariablesAPI
  48. rollupVAPI.EthBlockNum = rollupVariables.EthBlockNum
  49. rollupVAPI.FeeAddToken = apitypes.NewBigIntStr(rollupVariables.FeeAddToken)
  50. rollupVAPI.ForgeL1L2BatchTimeout = rollupVariables.ForgeL1L2BatchTimeout
  51. rollupVAPI.WithdrawalDelay = rollupVariables.WithdrawalDelay
  52. for i, bucket := range rollupVariables.Buckets {
  53. var apiBucket historydb.BucketParamsAPI
  54. apiBucket.CeilUSD = apitypes.NewBigIntStr(bucket.CeilUSD)
  55. apiBucket.Withdrawals = apitypes.NewBigIntStr(bucket.Withdrawals)
  56. apiBucket.BlockWithdrawalRate = apitypes.NewBigIntStr(bucket.BlockWithdrawalRate)
  57. apiBucket.MaxWithdrawals = apitypes.NewBigIntStr(bucket.MaxWithdrawals)
  58. rollupVAPI.Buckets[i] = apiBucket
  59. }
  60. rollupVAPI.SafeMode = rollupVariables.SafeMode
  61. a.status.Rollup = rollupVAPI
  62. a.status.Unlock()
  63. }
  64. // SetWDelayerVariables set Status.WithdrawalDelayer variables
  65. func (a *API) SetWDelayerVariables(wDelayerVariables common.WDelayerVariables) {
  66. a.status.Lock()
  67. a.status.WithdrawalDelayer = wDelayerVariables
  68. a.status.Unlock()
  69. }
  70. // SetAuctionVariables set Status.Auction variables
  71. func (a *API) SetAuctionVariables(auctionVariables common.AuctionVariables) {
  72. a.status.Lock()
  73. var auctionAPI historydb.AuctionVariablesAPI
  74. auctionAPI.EthBlockNum = auctionVariables.EthBlockNum
  75. auctionAPI.DonationAddress = auctionVariables.DonationAddress
  76. auctionAPI.BootCoordinator = auctionVariables.BootCoordinator
  77. auctionAPI.BootCoordinatorURL = auctionVariables.BootCoordinatorURL
  78. auctionAPI.DefaultSlotSetBidSlotNum = auctionVariables.DefaultSlotSetBidSlotNum
  79. auctionAPI.ClosedAuctionSlots = auctionVariables.ClosedAuctionSlots
  80. auctionAPI.OpenAuctionSlots = auctionVariables.OpenAuctionSlots
  81. auctionAPI.Outbidding = auctionVariables.Outbidding
  82. auctionAPI.SlotDeadline = auctionVariables.SlotDeadline
  83. for i, slot := range auctionVariables.DefaultSlotSetBid {
  84. auctionAPI.DefaultSlotSetBid[i] = apitypes.NewBigIntStr(slot)
  85. }
  86. for i, ratio := range auctionVariables.AllocationRatio {
  87. auctionAPI.AllocationRatio[i] = ratio
  88. }
  89. a.status.Auction = auctionAPI
  90. a.status.Unlock()
  91. }
  92. // Network
  93. // UpdateNetworkInfoBlock update Status.Network block related information
  94. func (a *API) UpdateNetworkInfoBlock(
  95. lastEthBlock, lastSyncBlock common.Block,
  96. ) {
  97. a.status.Network.LastSyncBlock = lastSyncBlock.Num
  98. a.status.Network.LastEthBlock = lastEthBlock.Num
  99. }
  100. // UpdateNetworkInfo update Status.Network information
  101. func (a *API) UpdateNetworkInfo(
  102. lastEthBlock, lastSyncBlock common.Block,
  103. lastBatchNum common.BatchNum, currentSlot int64,
  104. ) error {
  105. lastBatch, err := a.h.GetBatchAPI(lastBatchNum)
  106. if tracerr.Unwrap(err) == sql.ErrNoRows {
  107. lastBatch = nil
  108. } else if err != nil {
  109. return tracerr.Wrap(err)
  110. }
  111. lastClosedSlot := currentSlot + int64(a.status.Auction.ClosedAuctionSlots)
  112. nextForgers, err := a.getNextForgers(lastSyncBlock, currentSlot, lastClosedSlot)
  113. if tracerr.Unwrap(err) == sql.ErrNoRows {
  114. nextForgers = nil
  115. } else if err != nil {
  116. return tracerr.Wrap(err)
  117. }
  118. a.status.Lock()
  119. a.status.Network.LastSyncBlock = lastSyncBlock.Num
  120. a.status.Network.LastEthBlock = lastEthBlock.Num
  121. a.status.Network.LastBatch = lastBatch
  122. a.status.Network.CurrentSlot = currentSlot
  123. a.status.Network.NextForgers = nextForgers
  124. // Update buckets withdrawals
  125. bucketsUpdate, err := a.h.GetBucketUpdatesAPI()
  126. if tracerr.Unwrap(err) == sql.ErrNoRows {
  127. bucketsUpdate = nil
  128. } else if err != nil {
  129. return tracerr.Wrap(err)
  130. }
  131. for i, bucketParams := range a.status.Rollup.Buckets {
  132. for _, bucketUpdate := range bucketsUpdate {
  133. if bucketUpdate.NumBucket == i {
  134. bucketParams.Withdrawals = bucketUpdate.Withdrawals
  135. a.status.Rollup.Buckets[i] = bucketParams
  136. break
  137. }
  138. }
  139. }
  140. a.status.Unlock()
  141. return nil
  142. }
  143. // apiSlotToBigInts converts from [6]*apitypes.BigIntStr to [6]*big.Int
  144. func apiSlotToBigInts(defaultSlotSetBid [6]*apitypes.BigIntStr) ([6]*big.Int, error) {
  145. var slots [6]*big.Int
  146. for i, slot := range defaultSlotSetBid {
  147. bigInt, ok := new(big.Int).SetString(string(*slot), 10)
  148. if !ok {
  149. return slots, tracerr.Wrap(fmt.Errorf("can't convert %T into big.Int", slot))
  150. }
  151. slots[i] = bigInt
  152. }
  153. return slots, nil
  154. }
  155. // getNextForgers returns next forgers
  156. func (a *API) getNextForgers(lastBlock common.Block, currentSlot, lastClosedSlot int64) ([]NextForger, error) {
  157. secondsPerBlock := int64(15) //nolint:gomnd
  158. // currentSlot and lastClosedSlot included
  159. limit := uint(lastClosedSlot - currentSlot + 1)
  160. bids, _, err := a.h.GetBestBidsAPI(&currentSlot, &lastClosedSlot, nil, &limit, "ASC")
  161. if err != nil && tracerr.Unwrap(err) != sql.ErrNoRows {
  162. return nil, tracerr.Wrap(err)
  163. }
  164. nextForgers := []NextForger{}
  165. // Get min bid info
  166. var minBidInfo []historydb.MinBidInfo
  167. if currentSlot >= a.status.Auction.DefaultSlotSetBidSlotNum {
  168. // All min bids can be calculated with the last update of AuctionVariables
  169. bigIntSlots, err := apiSlotToBigInts(a.status.Auction.DefaultSlotSetBid)
  170. if err != nil {
  171. return nil, tracerr.Wrap(err)
  172. }
  173. minBidInfo = []historydb.MinBidInfo{{
  174. DefaultSlotSetBid: bigIntSlots,
  175. DefaultSlotSetBidSlotNum: a.status.Auction.DefaultSlotSetBidSlotNum,
  176. }}
  177. } else {
  178. // Get all the relevant updates from the DB
  179. minBidInfo, err = a.h.GetAuctionVarsUntilSetSlotNumAPI(lastClosedSlot, int(lastClosedSlot-currentSlot)+1)
  180. if err != nil {
  181. return nil, tracerr.Wrap(err)
  182. }
  183. }
  184. // Create nextForger for each slot
  185. for i := currentSlot; i <= lastClosedSlot; i++ {
  186. fromBlock := i*int64(a.cg.AuctionConstants.BlocksPerSlot) + a.cg.AuctionConstants.GenesisBlockNum
  187. toBlock := (i+1)*int64(a.cg.AuctionConstants.BlocksPerSlot) + a.cg.AuctionConstants.GenesisBlockNum - 1
  188. nextForger := NextForger{
  189. Period: Period{
  190. SlotNum: i,
  191. FromBlock: fromBlock,
  192. ToBlock: toBlock,
  193. FromTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(fromBlock-lastBlock.Num))),
  194. ToTimestamp: lastBlock.Timestamp.Add(time.Second * time.Duration(secondsPerBlock*(toBlock-lastBlock.Num))),
  195. },
  196. }
  197. foundForger := false
  198. // If there is a bid for a slot, get forger (coordinator)
  199. for j := range bids {
  200. slotNum := bids[j].SlotNum
  201. if slotNum == i {
  202. // There's a bid for the slot
  203. // Check if the bid is greater than the minimum required
  204. for i := 0; i < len(minBidInfo); i++ {
  205. // Find the most recent update
  206. if slotNum >= minBidInfo[i].DefaultSlotSetBidSlotNum {
  207. // Get min bid
  208. minBidSelector := slotNum % int64(len(a.status.Auction.DefaultSlotSetBid))
  209. minBid := minBidInfo[i].DefaultSlotSetBid[minBidSelector]
  210. // Check if the bid has beaten the minimum
  211. bid, ok := new(big.Int).SetString(string(bids[j].BidValue), 10)
  212. if !ok {
  213. return nil, tracerr.New("Wrong bid value, error parsing it as big.Int")
  214. }
  215. if minBid.Cmp(bid) == 1 {
  216. // Min bid is greater than bid, the slot will be forged by boot coordinator
  217. break
  218. }
  219. foundForger = true
  220. break
  221. }
  222. }
  223. if !foundForger { // There is no bid or it's smaller than the minimum
  224. break
  225. }
  226. coordinator, err := a.h.GetCoordinatorAPI(bids[j].Bidder)
  227. if err != nil {
  228. return nil, tracerr.Wrap(err)
  229. }
  230. nextForger.Coordinator = *coordinator
  231. break
  232. }
  233. }
  234. // If there is no bid, the coordinator that will forge is boot coordinator
  235. if !foundForger {
  236. nextForger.Coordinator = historydb.CoordinatorAPI{
  237. Forger: a.status.Auction.BootCoordinator,
  238. URL: a.status.Auction.BootCoordinatorURL,
  239. }
  240. }
  241. nextForgers = append(nextForgers, nextForger)
  242. }
  243. return nextForgers, nil
  244. }
  245. // Metrics
  246. // UpdateMetrics update Status.Metrics information
  247. func (a *API) UpdateMetrics() error {
  248. a.status.RLock()
  249. if a.status.Network.LastBatch == nil {
  250. a.status.RUnlock()
  251. return nil
  252. }
  253. batchNum := a.status.Network.LastBatch.BatchNum
  254. a.status.RUnlock()
  255. metrics, err := a.h.GetMetricsAPI(batchNum)
  256. if err != nil {
  257. return tracerr.Wrap(err)
  258. }
  259. a.status.Lock()
  260. a.status.Metrics = *metrics
  261. a.status.Unlock()
  262. return nil
  263. }
  264. // Recommended fee
  265. // UpdateRecommendedFee update Status.RecommendedFee information
  266. func (a *API) UpdateRecommendedFee() error {
  267. feeExistingAccount, err := a.h.GetAvgTxFeeAPI()
  268. if err != nil {
  269. return tracerr.Wrap(err)
  270. }
  271. var minFeeUSD float64
  272. if a.l2 != nil {
  273. minFeeUSD = a.l2.MinFeeUSD()
  274. }
  275. a.status.Lock()
  276. a.status.RecommendedFee.ExistingAccount =
  277. math.Max(feeExistingAccount, minFeeUSD)
  278. a.status.RecommendedFee.CreatesAccount =
  279. math.Max(createAccountExtraFeePercentage*feeExistingAccount, minFeeUSD)
  280. a.status.RecommendedFee.CreatesAccountAndRegister =
  281. math.Max(createAccountInternalExtraFeePercentage*feeExistingAccount, minFeeUSD)
  282. a.status.Unlock()
  283. return nil
  284. }