github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/prysm/v1alpha1/validator/status.go (about) 1 package validator 2 3 import ( 4 "context" 5 "errors" 6 7 types "github.com/prysmaticlabs/eth2-types" 8 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 9 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 10 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 11 "github.com/prysmaticlabs/prysm/shared/bytesutil" 12 "github.com/prysmaticlabs/prysm/shared/depositutil" 13 "github.com/prysmaticlabs/prysm/shared/params" 14 "github.com/prysmaticlabs/prysm/shared/traceutil" 15 "go.opencensus.io/trace" 16 "google.golang.org/grpc/codes" 17 "google.golang.org/grpc/status" 18 ) 19 20 var errPubkeyDoesNotExist = errors.New("pubkey does not exist") 21 var nonExistentIndex = types.ValidatorIndex(^uint64(0)) 22 23 // ValidatorStatus returns the validator status of the current epoch. 24 // The status response can be one of the following: 25 // DEPOSITED - validator's deposit has been recognized by Ethereum 1, not yet recognized by Ethereum. 26 // PENDING - validator is in Ethereum's activation queue. 27 // ACTIVE - validator is active. 28 // EXITING - validator has initiated an an exit request, or has dropped below the ejection balance and is being kicked out. 29 // EXITED - validator is no longer validating. 30 // SLASHING - validator has been kicked out due to meeting a slashing condition. 31 // UNKNOWN_STATUS - validator does not have a known status in the network. 32 func (vs *Server) ValidatorStatus( 33 ctx context.Context, 34 req *ethpb.ValidatorStatusRequest, 35 ) (*ethpb.ValidatorStatusResponse, error) { 36 headState, err := vs.HeadFetcher.HeadState(ctx) 37 if err != nil { 38 return nil, status.Error(codes.Internal, "Could not get head state") 39 } 40 vStatus, _ := vs.validatorStatus(ctx, headState, req.PublicKey) 41 return vStatus, nil 42 } 43 44 // MultipleValidatorStatus is the same as ValidatorStatus. Supports retrieval of multiple 45 // validator statuses. Takes a list of public keys or a list of validator indices. 46 func (vs *Server) MultipleValidatorStatus( 47 ctx context.Context, 48 req *ethpb.MultipleValidatorStatusRequest, 49 ) (*ethpb.MultipleValidatorStatusResponse, error) { 50 if vs.SyncChecker.Syncing() { 51 return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond") 52 } 53 headState, err := vs.HeadFetcher.HeadState(ctx) 54 if err != nil { 55 return nil, status.Error(codes.Internal, "Could not get head state") 56 } 57 responseCap := len(req.PublicKeys) + len(req.Indices) 58 pubKeys := make([][]byte, 0, responseCap) 59 filtered := make(map[[48]byte]bool) 60 filtered[[48]byte{}] = true // Filter out keys with all zeros. 61 // Filter out duplicate public keys. 62 for _, pubKey := range req.PublicKeys { 63 pubkeyBytes := bytesutil.ToBytes48(pubKey) 64 if !filtered[pubkeyBytes] { 65 pubKeys = append(pubKeys, pubKey) 66 filtered[pubkeyBytes] = true 67 } 68 } 69 // Convert indices to public keys. 70 for _, idx := range req.Indices { 71 pubkeyBytes := headState.PubkeyAtIndex(types.ValidatorIndex(idx)) 72 if !filtered[pubkeyBytes] { 73 pubKeys = append(pubKeys, pubkeyBytes[:]) 74 filtered[pubkeyBytes] = true 75 } 76 } 77 // Fetch statuses from beacon state. 78 statuses := make([]*ethpb.ValidatorStatusResponse, len(pubKeys)) 79 indices := make([]types.ValidatorIndex, len(pubKeys)) 80 for i, pubKey := range pubKeys { 81 statuses[i], indices[i] = vs.validatorStatus(ctx, headState, pubKey) 82 } 83 84 return ðpb.MultipleValidatorStatusResponse{ 85 PublicKeys: pubKeys, 86 Statuses: statuses, 87 Indices: indices, 88 }, nil 89 } 90 91 // CheckDoppelGanger checks if the provided keys are currently active in the network. 92 func (vs *Server) CheckDoppelGanger(ctx context.Context, req *ethpb.DoppelGangerRequest) (*ethpb.DoppelGangerResponse, error) { 93 if vs.SyncChecker.Syncing() { 94 return nil, status.Errorf(codes.Unavailable, "Syncing to latest head, not ready to respond") 95 } 96 if req == nil || req.ValidatorRequests == nil || len(req.ValidatorRequests) == 0 { 97 return ðpb.DoppelGangerResponse{ 98 Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}, 99 }, nil 100 } 101 headState, err := vs.HeadFetcher.HeadState(ctx) 102 if err != nil { 103 return nil, status.Error(codes.Internal, "Could not get head state") 104 } 105 // We walk back from the current head state to the state at the beginning of the previous 2 epochs. 106 // Where S_i , i := 0,1,2. i = 0 would signify the current head state in this epoch. 107 currEpoch := helpers.SlotToEpoch(headState.Slot()) 108 previousEpoch, err := currEpoch.SafeSub(1) 109 if err != nil { 110 previousEpoch = currEpoch 111 } 112 olderEpoch, err := previousEpoch.SafeSub(1) 113 if err != nil { 114 olderEpoch = previousEpoch 115 } 116 prevState, err := vs.StateGen.StateBySlot(ctx, params.BeaconConfig().SlotsPerEpoch.Mul(uint64(previousEpoch))) 117 if err != nil { 118 return nil, status.Error(codes.Internal, "Could not get previous state") 119 } 120 olderState, err := vs.StateGen.StateBySlot(ctx, params.BeaconConfig().SlotsPerEpoch.Mul(uint64(olderEpoch))) 121 if err != nil { 122 return nil, status.Error(codes.Internal, "Could not get older state") 123 } 124 resp := ðpb.DoppelGangerResponse{ 125 Responses: []*ethpb.DoppelGangerResponse_ValidatorResponse{}, 126 } 127 for _, v := range req.ValidatorRequests { 128 // If the validator's last recorded epoch was 129 // less than or equal to 2 epochs ago, this method will not 130 // be able to catch duplicates. This is due to how attestation 131 // inclusion works, where an attestation for the current epoch 132 // is able to included in the current or next epoch. Depending 133 // on which epoch it is included the balance change will be 134 // reflected in the following epoch. 135 if v.Epoch+2 >= currEpoch { 136 resp.Responses = append(resp.Responses, 137 ðpb.DoppelGangerResponse_ValidatorResponse{ 138 PublicKey: v.PublicKey, 139 DuplicateExists: false, 140 }) 141 continue 142 } 143 valIndex, ok := olderState.ValidatorIndexByPubkey(bytesutil.ToBytes48(v.PublicKey)) 144 if !ok { 145 // Ignore if validator pubkey doesn't exist. 146 continue 147 } 148 baseBal, err := olderState.BalanceAtIndex(valIndex) 149 if err != nil { 150 return nil, status.Error(codes.Internal, "Could not get validator's balance") 151 } 152 nextBal, err := prevState.BalanceAtIndex(valIndex) 153 if err != nil { 154 return nil, status.Error(codes.Internal, "Could not get validator's balance") 155 } 156 // If the next epoch's balance is higher, we mark it as an existing 157 // duplicate. 158 if nextBal > baseBal { 159 resp.Responses = append(resp.Responses, 160 ðpb.DoppelGangerResponse_ValidatorResponse{ 161 PublicKey: v.PublicKey, 162 DuplicateExists: true, 163 }) 164 continue 165 } 166 currBal, err := headState.BalanceAtIndex(valIndex) 167 if err != nil { 168 return nil, status.Error(codes.Internal, "Could not get validator's balance") 169 } 170 // If the current epoch's balance is higher, we mark it as an existing 171 // duplicate. 172 if currBal > nextBal { 173 resp.Responses = append(resp.Responses, 174 ðpb.DoppelGangerResponse_ValidatorResponse{ 175 PublicKey: v.PublicKey, 176 DuplicateExists: true, 177 }) 178 continue 179 } 180 // Mark the public key as valid. 181 resp.Responses = append(resp.Responses, 182 ðpb.DoppelGangerResponse_ValidatorResponse{ 183 PublicKey: v.PublicKey, 184 DuplicateExists: false, 185 }) 186 } 187 return resp, nil 188 } 189 190 // activationStatus returns the validator status response for the set of validators 191 // requested by their pub keys. 192 func (vs *Server) activationStatus( 193 ctx context.Context, 194 pubKeys [][]byte, 195 ) (bool, []*ethpb.ValidatorActivationResponse_Status, error) { 196 headState, err := vs.HeadFetcher.HeadState(ctx) 197 if err != nil { 198 return false, nil, err 199 } 200 activeValidatorExists := false 201 statusResponses := make([]*ethpb.ValidatorActivationResponse_Status, len(pubKeys)) 202 for i, pubKey := range pubKeys { 203 if ctx.Err() != nil { 204 return false, nil, ctx.Err() 205 } 206 vStatus, idx := vs.validatorStatus(ctx, headState, pubKey) 207 if vStatus == nil { 208 continue 209 } 210 resp := ðpb.ValidatorActivationResponse_Status{ 211 Status: vStatus, 212 PublicKey: pubKey, 213 Index: idx, 214 } 215 statusResponses[i] = resp 216 if vStatus.Status == ethpb.ValidatorStatus_ACTIVE { 217 activeValidatorExists = true 218 } 219 } 220 221 return activeValidatorExists, statusResponses, nil 222 } 223 224 // validatorStatus searches for the requested validator's state and deposit to retrieve its inclusion estimate. Also returns the validators index. 225 func (vs *Server) validatorStatus( 226 ctx context.Context, 227 headState iface.ReadOnlyBeaconState, 228 pubKey []byte, 229 ) (*ethpb.ValidatorStatusResponse, types.ValidatorIndex) { 230 ctx, span := trace.StartSpan(ctx, "ValidatorServer.validatorStatus") 231 defer span.End() 232 233 // Using ^0 as the default value for index, in case the validators index cannot be determined. 234 resp := ðpb.ValidatorStatusResponse{ 235 Status: ethpb.ValidatorStatus_UNKNOWN_STATUS, 236 ActivationEpoch: params.BeaconConfig().FarFutureEpoch, 237 } 238 vStatus, idx, err := statusForPubKey(headState, pubKey) 239 if err != nil && err != errPubkeyDoesNotExist { 240 traceutil.AnnotateError(span, err) 241 return resp, nonExistentIndex 242 } 243 resp.Status = vStatus 244 if err != errPubkeyDoesNotExist { 245 val, err := headState.ValidatorAtIndexReadOnly(idx) 246 if err != nil { 247 traceutil.AnnotateError(span, err) 248 return resp, idx 249 } 250 resp.ActivationEpoch = val.ActivationEpoch() 251 } 252 253 switch resp.Status { 254 // Unknown status means the validator has not been put into the state yet. 255 case ethpb.ValidatorStatus_UNKNOWN_STATUS: 256 // If no connection to ETH1, the deposit block number or position in queue cannot be determined. 257 if !vs.Eth1InfoFetcher.IsConnectedToETH1() { 258 log.Warn("Not connected to ETH1. Cannot determine validator ETH1 deposit block number") 259 return resp, nonExistentIndex 260 } 261 deposit, eth1BlockNumBigInt := vs.DepositFetcher.DepositByPubkey(ctx, pubKey) 262 if eth1BlockNumBigInt == nil { // No deposit found in ETH1. 263 return resp, nonExistentIndex 264 } 265 domain, err := helpers.ComputeDomain( 266 params.BeaconConfig().DomainDeposit, 267 nil, /*forkVersion*/ 268 nil, /*genesisValidatorsRoot*/ 269 ) 270 if err != nil { 271 log.Warn("Could not compute domain") 272 return resp, nonExistentIndex 273 } 274 if err := depositutil.VerifyDepositSignature(deposit.Data, domain); err != nil { 275 resp.Status = ethpb.ValidatorStatus_INVALID 276 log.Warn("Invalid Eth1 deposit") 277 return resp, nonExistentIndex 278 } 279 // Set validator deposit status if their deposit is visible. 280 resp.Status = depositStatus(deposit.Data.Amount) 281 resp.Eth1DepositBlockNumber = eth1BlockNumBigInt.Uint64() 282 283 return resp, nonExistentIndex 284 // Deposited, Pending or Partially Deposited mean the validator has been put into the state. 285 case ethpb.ValidatorStatus_DEPOSITED, ethpb.ValidatorStatus_PENDING, ethpb.ValidatorStatus_PARTIALLY_DEPOSITED: 286 if resp.Status == ethpb.ValidatorStatus_PENDING { 287 if vs.DepositFetcher == nil { 288 log.Warn("Not connected to ETH1. Cannot determine validator ETH1 deposit.") 289 } else { 290 // Check if there was a deposit deposit. 291 deposit, eth1BlockNumBigInt := vs.DepositFetcher.DepositByPubkey(ctx, pubKey) 292 if eth1BlockNumBigInt != nil { 293 resp.Status = depositStatus(deposit.Data.Amount) 294 resp.Eth1DepositBlockNumber = eth1BlockNumBigInt.Uint64() 295 } 296 } 297 } 298 299 var lastActivatedvalidatorIndex types.ValidatorIndex 300 for j := headState.NumValidators() - 1; j >= 0; j-- { 301 val, err := headState.ValidatorAtIndexReadOnly(types.ValidatorIndex(j)) 302 if err != nil { 303 return resp, idx 304 } 305 if helpers.IsActiveValidatorUsingTrie(val, helpers.CurrentEpoch(headState)) { 306 lastActivatedvalidatorIndex = types.ValidatorIndex(j) 307 break 308 } 309 } 310 // Our position in the activation queue is the above index - our validator index. 311 if lastActivatedvalidatorIndex < idx { 312 resp.PositionInActivationQueue = uint64(idx - lastActivatedvalidatorIndex) 313 } 314 return resp, idx 315 default: 316 return resp, idx 317 } 318 } 319 320 func statusForPubKey(headState iface.ReadOnlyBeaconState, pubKey []byte) (ethpb.ValidatorStatus, types.ValidatorIndex, error) { 321 if headState == nil || headState.IsNil() { 322 return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, errors.New("head state does not exist") 323 } 324 idx, ok := headState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey)) 325 if !ok || uint64(idx) >= uint64(headState.NumValidators()) { 326 return ethpb.ValidatorStatus_UNKNOWN_STATUS, 0, errPubkeyDoesNotExist 327 } 328 return assignmentStatus(headState, idx), idx, nil 329 } 330 331 func assignmentStatus(beaconState iface.ReadOnlyBeaconState, validatorIndex types.ValidatorIndex) ethpb.ValidatorStatus { 332 validator, err := beaconState.ValidatorAtIndexReadOnly(validatorIndex) 333 if err != nil { 334 return ethpb.ValidatorStatus_UNKNOWN_STATUS 335 } 336 currentEpoch := helpers.CurrentEpoch(beaconState) 337 farFutureEpoch := params.BeaconConfig().FarFutureEpoch 338 validatorBalance := validator.EffectiveBalance() 339 340 if validator.IsNil() { 341 return ethpb.ValidatorStatus_UNKNOWN_STATUS 342 } 343 if currentEpoch < validator.ActivationEligibilityEpoch() { 344 return depositStatus(validatorBalance) 345 } 346 if currentEpoch < validator.ActivationEpoch() { 347 return ethpb.ValidatorStatus_PENDING 348 } 349 if validator.ExitEpoch() == farFutureEpoch { 350 return ethpb.ValidatorStatus_ACTIVE 351 } 352 if currentEpoch < validator.ExitEpoch() { 353 if validator.Slashed() { 354 return ethpb.ValidatorStatus_SLASHING 355 } 356 return ethpb.ValidatorStatus_EXITING 357 } 358 return ethpb.ValidatorStatus_EXITED 359 } 360 361 func depositStatus(depositOrBalance uint64) ethpb.ValidatorStatus { 362 if depositOrBalance == 0 { 363 return ethpb.ValidatorStatus_PENDING 364 } else if depositOrBalance < params.BeaconConfig().MaxEffectiveBalance { 365 return ethpb.ValidatorStatus_PARTIALLY_DEPOSITED 366 } 367 return ethpb.ValidatorStatus_DEPOSITED 368 }