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