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