-
Notifications
You must be signed in to change notification settings - Fork 162
Expand file tree
/
Copy pathstake-megapool-validator.go
More file actions
272 lines (232 loc) · 7.72 KB
/
stake-megapool-validator.go
File metadata and controls
272 lines (232 loc) · 7.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
package node
import (
"math/big"
"strconv"
"github.com/docker/docker/client"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/rocket-pool/smartnode/bindings/megapool"
"github.com/rocket-pool/smartnode/bindings/rocketpool"
"github.com/rocket-pool/smartnode/bindings/types"
"github.com/rocket-pool/smartnode/bindings/utils/eth"
"github.com/urfave/cli/v3"
"github.com/rocket-pool/smartnode/shared/services"
"github.com/rocket-pool/smartnode/shared/services/beacon"
"github.com/rocket-pool/smartnode/shared/services/config"
rpgas "github.com/rocket-pool/smartnode/shared/services/gas"
"github.com/rocket-pool/smartnode/shared/services/state"
"github.com/rocket-pool/smartnode/shared/services/wallet"
"github.com/rocket-pool/smartnode/shared/types/eth2"
"github.com/rocket-pool/smartnode/shared/utils/api"
"github.com/rocket-pool/smartnode/shared/utils/log"
"github.com/rocket-pool/smartnode/shared/utils/validator"
)
// Stake megapool validator task
type stakeMegapoolValidator struct {
c *cli.Command
log log.ColorLogger
cfg *config.RocketPoolConfig
w wallet.Wallet
rp *rocketpool.RocketPool
bc beacon.Client
d *client.Client
gasThreshold float64
maxFee *big.Int
maxPriorityFee *big.Int
gasLimit uint64
}
// Create stake megapool validator task
func newStakeMegapoolValidator(c *cli.Command, logger log.ColorLogger) (*stakeMegapoolValidator, error) {
// Get services
cfg, err := services.GetConfig(c)
if err != nil {
return nil, err
}
w, err := services.GetWallet(c)
if err != nil {
return nil, err
}
rp, err := services.GetRocketPool(c)
if err != nil {
return nil, err
}
d, err := services.GetDocker(c)
if err != nil {
return nil, err
}
bc, err := services.GetBeaconClient(c)
if err != nil {
return nil, err
}
gasThreshold := cfg.Smartnode.AutoTxGasThreshold.Value.(float64)
// Get the user-requested max fee
maxFeeGwei := cfg.Smartnode.ManualMaxFee.Value.(float64)
var maxFee *big.Int
if maxFeeGwei == 0 {
maxFee = nil
} else {
maxFee = eth.GweiToWei(maxFeeGwei)
}
// Get the user-requested max fee
priorityFeeGwei := cfg.Smartnode.PriorityFee.Value.(float64)
var priorityFee *big.Int
if priorityFeeGwei == 0 {
logger.Printlnf("WARNING: priority fee was missing or 0, setting a default of %.2f.", rpgas.DefaultPriorityFeeGwei)
priorityFee = eth.GweiToWei(rpgas.DefaultPriorityFeeGwei)
} else {
priorityFee = eth.GweiToWei(priorityFeeGwei)
}
// Return task
return &stakeMegapoolValidator{
c: c,
log: logger,
cfg: cfg,
w: w,
rp: rp,
bc: bc,
d: d,
gasThreshold: gasThreshold,
maxFee: maxFee,
maxPriorityFee: priorityFee,
gasLimit: 0,
}, nil
}
// Prestake megapool validator
func (t *stakeMegapoolValidator) run(state *state.NetworkState) error {
// Log
t.log.Println("Checking for megapool validators to stake...")
// Get the latest state
opts := &bind.CallOpts{
BlockNumber: big.NewInt(0).SetUint64(state.ElBlockNumber),
}
// Get node account
nodeAccount, err := t.w.GetNodeAccount()
if err != nil {
return err
}
// Check if the megapool is deployed
deployed, err := megapool.GetMegapoolDeployed(t.rp, nodeAccount.Address, opts)
if err != nil {
return err
}
if !deployed {
return nil
}
// Get the megapool address
megapoolAddress, err := megapool.GetMegapoolExpectedAddress(t.rp, nodeAccount.Address, opts)
if err != nil {
return err
}
// Load the megapool
mp, err := megapool.NewMegaPoolV1(t.rp, megapoolAddress, nil)
if err != nil {
return err
}
// Iterate over megapool validators checking whether they're ready to stake
validatorCount, err := mp.GetValidatorCount(nil)
if err != nil {
return err
}
validatorInfo, err := services.GetMegapoolValidatorDetails(t.rp, t.bc, mp, megapoolAddress, uint32(validatorCount), opts)
if err != nil {
return err
}
// store validators that need to be staked
validatorsToStake := make(map[uint32]types.ValidatorPubkey)
for i := uint32(0); i < uint32(validatorCount); i++ {
if validatorInfo[i].InPrestake && validatorInfo[i].BeaconStatus.Index != "" {
validatorsToStake[validatorInfo[i].ValidatorId] = types.ValidatorPubkey(validatorInfo[i].PubKey)
}
}
// Check if we have any validators to stake
if len(validatorsToStake) == 0 {
return nil
}
// Load the beacon state
beaconState, err := services.GetBeaconState(t.bc)
if err != nil {
return err
}
stakedValidators := 0
// Iterate over validators to stake
for validatorId, validatorPubkey := range validatorsToStake {
// Log
t.log.Printlnf("The validator id %d needs to be staked", validatorId)
// Call Stake
err := t.stakeValidator(t.rp, beaconState, mp, validatorId, state, validatorPubkey, opts)
// dont return if there was an error, just log it so we can continue with the next validator
if err != nil {
t.log.Printlnf("Error staking validator %d: %w", validatorId, err)
continue
}
stakedValidators++
}
if stakedValidators > 0 {
if err := validator.RestartValidator(t.cfg, t.bc, &t.log, t.d); err != nil {
return err
}
}
return nil
}
func (t *stakeMegapoolValidator) stakeValidator(rp *rocketpool.RocketPool, beaconState eth2.BeaconState, mp megapool.Megapool, validatorId uint32, state *state.NetworkState, validatorPubkey types.ValidatorPubkey, callopts *bind.CallOpts) error {
// Get transactor
opts, err := t.w.GetNodeAccountTransactor()
if err != nil {
return err
}
// Check if the validator is included in the finalized beacon state before attempting proof generation
validatorIndexStr, err := t.bc.GetValidatorIndex(validatorPubkey)
if err != nil {
return err
}
validatorIndex, err := strconv.ParseUint(validatorIndexStr, 10, 64)
if err != nil {
return err
}
if validatorIndex >= uint64(len(beaconState.GetValidators())) {
t.log.Printlnf("Validator id %d (beacon index %d) is not yet included in the finalized beacon state. Will retry on next cycle.", validatorId, validatorIndex)
return nil
}
t.log.Printlnf("Crafting a proof that the correct credentials were used on the first beacon chain deposit. This process can take several seconds and is CPU and memory intensive.")
validatorProof, slotTimestamp, slotProof, err := services.GetValidatorProof(t.c, 0, t.w, state.BeaconConfig, mp.GetAddress(), validatorPubkey, beaconState)
if err != nil {
t.log.Printlnf("There was an error during the proof creation process: %w", err)
return err
}
t.log.Printlnf("The beacon state proof has been successfully created.")
// Get the gas limit
gasInfo, err := megapool.EstimateStakeGas(rp, mp.GetAddress(), validatorId, slotTimestamp, validatorProof, slotProof, opts)
if err != nil {
t.log.Printlnf("Could not estimate the gas required to stake megapool validator %d: %w", validatorId, err)
return err
}
gas := big.NewInt(int64(gasInfo.SafeGasLimit))
// Get the max fee
maxFee := t.maxFee
if maxFee == nil || maxFee.Uint64() == 0 {
maxFee, err = rpgas.GetHeadlessMaxFeeWeiWithLatestBlock(t.cfg, t.rp)
if err != nil {
return err
}
}
// Print the gas info
if !api.PrintAndCheckGasInfo(gasInfo, true, t.gasThreshold, &t.log, maxFee, t.gasLimit) {
return nil
}
opts.GasFeeCap = maxFee
opts.GasTipCap = GetPriorityFee(t.maxPriorityFee, maxFee)
opts.GasLimit = gas.Uint64()
// Call stake
tx, err := megapool.Stake(rp, mp.GetAddress(), validatorId, slotTimestamp, validatorProof, slotProof, opts)
if err != nil {
return err
}
// Print TX info and wait for it to be included in a block
err = api.PrintAndWaitForTransaction(t.cfg, tx.Hash(), t.rp.Client, &t.log)
if err != nil {
return err
}
// Log
t.log.Printlnf("Successfully staked validator %d.", validatorId)
// Return
return nil
}