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