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