hermez-node/api/stateapiupdater/stateapiupdater.go
api/stateapiupdater/stateapiupdater.goRaw
1/*
2Package stateapiupdater is responsible for generating and storing the object response of the GET /state endpoint exposed through the api package.
3This object is extensively defined at the OpenAPI spec located at api/swagger.yml.
4
5Deployment considerations: in a setup where multiple processes are used (dedicated api process, separated coord / sync, ...), only one process should care
6of using this package.
7*/
8package stateapiupdater
9
10import (
11 "database/sql"
12 "fmt"
13 "sync"
14
15 "github.com/hermeznetwork/hermez-node/common"
16 "github.com/hermeznetwork/hermez-node/db/historydb"
17 "github.com/hermeznetwork/hermez-node/log"
18 "github.com/hermeznetwork/tracerr"
19)
20
21// Updater is an utility object to facilitate updating the StateAPI
22type Updater struct {
23 hdb *historydb.HistoryDB
24 state historydb.StateAPI
25 config historydb.NodeConfig
26 vars common.SCVariablesPtr
27 consts historydb.Constants
28 rw sync.RWMutex
29 rfp *RecommendedFeePolicy
30}
31
32// RecommendedFeePolicy describes how the recommended fee is calculated
33type RecommendedFeePolicy struct {
34 PolicyType RecommendedFeePolicyType `validate:"required"`
35 StaticValue float64
36}
37
38// RecommendedFeePolicyType describes the different available recommended fee strategies
39type RecommendedFeePolicyType string
40
41const (
42 // RecommendedFeePolicyTypeStatic always give the same StaticValue as recommended fee
43 RecommendedFeePolicyTypeStatic RecommendedFeePolicyType = "Static"
44 // RecommendedFeePolicyTypeAvgLastHour set the recommended fee using the average fee of the last hour
45 RecommendedFeePolicyTypeAvgLastHour RecommendedFeePolicyType = "AvgLastHour"
46)
47
48func (rfp *RecommendedFeePolicy) valid() bool {
49 switch rfp.PolicyType {
50 case RecommendedFeePolicyTypeStatic:
51 if rfp.StaticValue == 0 {
52 log.Warn("RcommendedFee is set to 0 USD, and the policy is static")
53 }
54 return true
55 case RecommendedFeePolicyTypeAvgLastHour:
56 return true
57 default:
58 return false
59 }
60}
61
62// NewUpdater creates a new Updater
63func NewUpdater(hdb *historydb.HistoryDB, config *historydb.NodeConfig, vars *common.SCVariables,
64 consts *historydb.Constants, rfp *RecommendedFeePolicy) (*Updater, error) {
65 if ok := rfp.valid(); !ok {
66 return nil, tracerr.Wrap(fmt.Errorf("Invalid recommended fee policy: %v", rfp.PolicyType))
67 }
68 u := Updater{
69 hdb: hdb,
70 config: *config,
71 consts: *consts,
72 state: historydb.StateAPI{
73 NodePublicInfo: historydb.NodePublicInfo{
74 ForgeDelay: config.ForgeDelay,
75 },
76 },
77 rfp: rfp,
78 }
79 u.SetSCVars(vars.AsPtr())
80 return &u, nil
81}
82
83// Store the State in the HistoryDB
84func (u *Updater) Store() error {
85 u.rw.RLock()
86 defer u.rw.RUnlock()
87 return tracerr.Wrap(u.hdb.SetStateInternalAPI(&u.state))
88}
89
90// SetSCVars sets the smart contract vars (ony updates those that are not nil)
91func (u *Updater) SetSCVars(vars *common.SCVariablesPtr) {
92 u.rw.Lock()
93 defer u.rw.Unlock()
94 if vars.Rollup != nil {
95 u.vars.Rollup = vars.Rollup
96 rollupVars := historydb.NewRollupVariablesAPI(u.vars.Rollup)
97 u.state.Rollup = *rollupVars
98 }
99 if vars.Auction != nil {
100 u.vars.Auction = vars.Auction
101 auctionVars := historydb.NewAuctionVariablesAPI(u.vars.Auction)
102 u.state.Auction = *auctionVars
103 }
104 if vars.WDelayer != nil {
105 u.vars.WDelayer = vars.WDelayer
106 u.state.WithdrawalDelayer = *u.vars.WDelayer
107 }
108}
109
110// UpdateRecommendedFee update Status.RecommendedFee information
111func (u *Updater) UpdateRecommendedFee() error {
112 switch u.rfp.PolicyType {
113 case RecommendedFeePolicyTypeStatic:
114 u.rw.Lock()
115 u.state.RecommendedFee = common.RecommendedFee{
116 ExistingAccount: u.rfp.StaticValue,
117 CreatesAccount: u.rfp.StaticValue,
118 CreatesAccountInternal: u.rfp.StaticValue,
119 }
120 u.rw.Unlock()
121 case RecommendedFeePolicyTypeAvgLastHour:
122 recommendedFee, err := u.hdb.GetRecommendedFee(u.config.MinFeeUSD, u.config.MaxFeeUSD)
123 if err != nil {
124 return tracerr.Wrap(err)
125 }
126 u.rw.Lock()
127 u.state.RecommendedFee = *recommendedFee
128 u.rw.Unlock()
129 default:
130 return tracerr.New("Invalid recommende fee policy")
131 }
132
133 return nil
134}
135
136// UpdateMetrics update Status.Metrics information
137func (u *Updater) UpdateMetrics() error {
138 u.rw.RLock()
139 lastBatch := u.state.Network.LastBatch
140 u.rw.RUnlock()
141 if lastBatch == nil {
142 return nil
143 }
144 lastBatchNum := lastBatch.BatchNum
145 metrics, poolLoad, err := u.hdb.GetMetricsInternalAPI(lastBatchNum)
146 if err != nil {
147 return tracerr.Wrap(err)
148 }
149 u.rw.Lock()
150 u.state.Metrics = *metrics
151 u.state.NodePublicInfo.PoolLoad = poolLoad
152 u.rw.Unlock()
153 return nil
154}
155
156// UpdateNetworkInfoBlock update Status.Network block related information
157func (u *Updater) UpdateNetworkInfoBlock(lastEthBlock, lastSyncBlock common.Block) {
158 u.rw.Lock()
159 u.state.Network.LastSyncBlock = lastSyncBlock.Num
160 u.state.Network.LastEthBlock = lastEthBlock.Num
161 u.rw.Unlock()
162}
163
164// UpdateNetworkInfo update Status.Network information
165func (u *Updater) UpdateNetworkInfo(
166 lastEthBlock, lastSyncBlock common.Block,
167 lastBatchNum common.BatchNum, currentSlot int64,
168) error {
169 // Get last batch in API format
170 lastBatch, err := u.hdb.GetBatchInternalAPI(lastBatchNum)
171 if tracerr.Unwrap(err) == sql.ErrNoRows {
172 lastBatch = nil
173 } else if err != nil {
174 return tracerr.Wrap(err)
175 }
176 u.rw.RLock()
177 auctionVars := u.vars.Auction
178 u.rw.RUnlock()
179 // Get next forgers
180 lastClosedSlot := currentSlot + int64(auctionVars.ClosedAuctionSlots)
181 nextForgers, err := u.hdb.GetNextForgersInternalAPI(auctionVars, &u.consts.Auction,
182 lastSyncBlock, currentSlot, lastClosedSlot)
183 if tracerr.Unwrap(err) == sql.ErrNoRows {
184 nextForgers = nil
185 } else if err != nil {
186 return tracerr.Wrap(err)
187 }
188
189 bucketUpdates, err := u.hdb.GetBucketUpdatesInternalAPI()
190 if err == sql.ErrNoRows {
191 bucketUpdates = nil
192 } else if err != nil {
193 return tracerr.Wrap(err)
194 }
195
196 u.rw.Lock()
197 // Update NodeInfo struct
198 for i, bucketParams := range u.state.Rollup.Buckets {
199 for _, bucketUpdate := range bucketUpdates {
200 if bucketUpdate.NumBucket == i {
201 bucketParams.Withdrawals = bucketUpdate.Withdrawals
202 u.state.Rollup.Buckets[i] = bucketParams
203 break
204 }
205 }
206 }
207 // Update pending L1s
208 pendingL1s, err := u.hdb.GetUnforgedL1UserTxsCount()
209 if err != nil {
210 return tracerr.Wrap(err)
211 }
212 u.state.Network.LastSyncBlock = lastSyncBlock.Num
213 u.state.Network.LastEthBlock = lastEthBlock.Num
214 u.state.Network.LastBatch = lastBatch
215 u.state.Network.CurrentSlot = currentSlot
216 u.state.Network.NextForgers = nextForgers
217 u.state.Network.PendingL1Txs = pendingL1s
218 u.rw.Unlock()
219 return nil
220}