github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/rpc/eth/v1/beacon/validator.go (about) 1 package beacon 2 3 import ( 4 "context" 5 "strconv" 6 7 "github.com/pkg/errors" 8 types "github.com/prysmaticlabs/eth2-types" 9 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 10 "github.com/prysmaticlabs/prysm/beacon-chain/rpc/statefetcher" 11 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 12 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1" 13 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1" 14 "github.com/prysmaticlabs/prysm/proto/migration" 15 "github.com/prysmaticlabs/prysm/shared/bytesutil" 16 "github.com/prysmaticlabs/prysm/shared/params" 17 "google.golang.org/grpc/codes" 18 "google.golang.org/grpc/status" 19 ) 20 21 // invalidValidatorIdError represents an error scenario where a validator's ID is invalid. 22 type invalidValidatorIdError struct { 23 message string 24 } 25 26 // newInvalidValidatorIdError creates a new error instance. 27 func newInvalidValidatorIdError(validatorId []byte, reason error) invalidValidatorIdError { 28 return invalidValidatorIdError{ 29 message: errors.Wrapf(reason, "could not decode validator id '%s'", string(validatorId)).Error(), 30 } 31 } 32 33 // Error returns the underlying error message. 34 func (e *invalidValidatorIdError) Error() string { 35 return e.message 36 } 37 38 // GetValidator returns a validator specified by state and id or public key along with status and balance. 39 func (bs *Server) GetValidator(ctx context.Context, req *ethpb.StateValidatorRequest) (*ethpb.StateValidatorResponse, error) { 40 state, err := bs.StateFetcher.State(ctx, req.StateId) 41 if err != nil { 42 if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok { 43 return nil, status.Errorf(codes.NotFound, "could not get state: %v", stateNotFoundErr) 44 } else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok { 45 return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr) 46 } 47 return nil, status.Errorf(codes.Internal, "State not found: %v", err) 48 } 49 if len(req.ValidatorId) == 0 { 50 return nil, status.Error(codes.InvalidArgument, "Validator ID is required") 51 } 52 valContainer, err := valContainersByRequestIds(state, [][]byte{req.ValidatorId}) 53 if err != nil { 54 return nil, handleValContainerErr(err) 55 } 56 if len(valContainer) == 0 { 57 return nil, status.Error(codes.NotFound, "Could not find validator") 58 } 59 return ðpb.StateValidatorResponse{Data: valContainer[0]}, nil 60 } 61 62 // ListValidators returns filterable list of validators with their balance, status and index. 63 func (bs *Server) ListValidators(ctx context.Context, req *ethpb.StateValidatorsRequest) (*ethpb.StateValidatorsResponse, error) { 64 state, err := bs.StateFetcher.State(ctx, req.StateId) 65 if err != nil { 66 if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok { 67 return nil, status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr) 68 } else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok { 69 return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr) 70 } 71 return nil, status.Errorf(codes.Internal, "Could not get state: %v", err) 72 } 73 74 valContainers, err := valContainersByRequestIds(state, req.Id) 75 if err != nil { 76 return nil, handleValContainerErr(err) 77 } 78 79 // Exit early if no matching validators we found or we don't want to further filter validators by status. 80 if len(valContainers) == 0 || len(req.Status) == 0 { 81 return ðpb.StateValidatorsResponse{Data: valContainers}, nil 82 } 83 84 filterStatus := make(map[ethpb.ValidatorStatus]bool, len(req.Status)) 85 const lastValidStatusValue = ethpb.ValidatorStatus(12) 86 for _, ss := range req.Status { 87 if ss > lastValidStatusValue { 88 return nil, status.Errorf(codes.InvalidArgument, "Invalid status "+ss.String()) 89 } 90 filterStatus[ss] = true 91 } 92 epoch := helpers.SlotToEpoch(state.Slot()) 93 filteredVals := make([]*ethpb.ValidatorContainer, 0, len(valContainers)) 94 for _, vc := range valContainers { 95 valStatus, err := validatorStatus(vc.Validator, epoch) 96 if err != nil { 97 return nil, status.Errorf(codes.Internal, "Could not get validator status: %v", err) 98 } 99 valSubStatus, err := validatorSubStatus(vc.Validator, epoch) 100 if err != nil { 101 return nil, status.Errorf(codes.Internal, "Could not get validator sub status: %v", err) 102 } 103 if filterStatus[valStatus] || filterStatus[valSubStatus] { 104 filteredVals = append(filteredVals, vc) 105 } 106 } 107 return ðpb.StateValidatorsResponse{Data: filteredVals}, nil 108 } 109 110 // ListValidatorBalances returns a filterable list of validator balances. 111 func (bs *Server) ListValidatorBalances(ctx context.Context, req *ethpb.ValidatorBalancesRequest) (*ethpb.ValidatorBalancesResponse, error) { 112 state, err := bs.StateFetcher.State(ctx, req.StateId) 113 if err != nil { 114 if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok { 115 return nil, status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr) 116 } else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok { 117 return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr) 118 } 119 return nil, status.Errorf(codes.Internal, "Could not get state: %v", err) 120 } 121 122 valContainers, err := valContainersByRequestIds(state, req.Id) 123 if err != nil { 124 return nil, handleValContainerErr(err) 125 } 126 valBalances := make([]*ethpb.ValidatorBalance, len(valContainers)) 127 for i := 0; i < len(valContainers); i++ { 128 valBalances[i] = ðpb.ValidatorBalance{ 129 Index: valContainers[i].Index, 130 Balance: valContainers[i].Balance, 131 } 132 } 133 return ðpb.ValidatorBalancesResponse{Data: valBalances}, nil 134 } 135 136 // ListCommittees retrieves the committees for the given state at the given epoch. 137 // If the requested slot and index are defined, only those committees are returned. 138 func (bs *Server) ListCommittees(ctx context.Context, req *ethpb.StateCommitteesRequest) (*ethpb.StateCommitteesResponse, error) { 139 state, err := bs.StateFetcher.State(ctx, req.StateId) 140 if err != nil { 141 if stateNotFoundErr, ok := err.(*statefetcher.StateNotFoundError); ok { 142 return nil, status.Errorf(codes.NotFound, "State not found: %v", stateNotFoundErr) 143 } else if parseErr, ok := err.(*statefetcher.StateIdParseError); ok { 144 return nil, status.Errorf(codes.InvalidArgument, "Invalid state ID: %v", parseErr) 145 } 146 return nil, status.Errorf(codes.Internal, "Could not get state: %v", err) 147 } 148 149 epoch := helpers.SlotToEpoch(state.Slot()) 150 if req.Epoch != nil { 151 epoch = *req.Epoch 152 } 153 activeCount, err := helpers.ActiveValidatorCount(state, epoch) 154 if err != nil { 155 return nil, status.Errorf(codes.Internal, "Could not get active validator count: %v", err) 156 } 157 158 startSlot, err := helpers.StartSlot(epoch) 159 if err != nil { 160 return nil, status.Errorf(codes.InvalidArgument, "Invalid epoch: %v", err) 161 } 162 endSlot, err := helpers.EndSlot(epoch) 163 if err != nil { 164 return nil, status.Errorf(codes.InvalidArgument, "Invalid epoch: %v", err) 165 } 166 committeesPerSlot := helpers.SlotCommitteeCount(activeCount) 167 committees := make([]*ethpb.Committee, 0) 168 for slot := startSlot; slot <= endSlot; slot++ { 169 if req.Slot != nil && slot != *req.Slot { 170 continue 171 } 172 for index := types.CommitteeIndex(0); index < types.CommitteeIndex(committeesPerSlot); index++ { 173 if req.Index != nil && index != *req.Index { 174 continue 175 } 176 committee, err := helpers.BeaconCommitteeFromState(state, slot, index) 177 if err != nil { 178 return nil, status.Errorf(codes.Internal, "Could not get committee: %v", err) 179 } 180 committeeContainer := ðpb.Committee{ 181 Index: index, 182 Slot: slot, 183 Validators: committee, 184 } 185 committees = append(committees, committeeContainer) 186 } 187 } 188 return ðpb.StateCommitteesResponse{Data: committees}, nil 189 } 190 191 // This function returns the validator object based on the passed in ID. The validator ID could be its public key, 192 // or its index. 193 func valContainersByRequestIds(state iface.BeaconState, validatorIds [][]byte) ([]*ethpb.ValidatorContainer, error) { 194 epoch := helpers.SlotToEpoch(state.Slot()) 195 var valContainers []*ethpb.ValidatorContainer 196 if len(validatorIds) == 0 { 197 allValidators := state.Validators() 198 allBalances := state.Balances() 199 valContainers = make([]*ethpb.ValidatorContainer, len(allValidators)) 200 for i, validator := range allValidators { 201 v1Validator := migration.V1Alpha1ValidatorToV1(validator) 202 subStatus, err := validatorSubStatus(v1Validator, epoch) 203 if err != nil { 204 return nil, errors.Wrap(err, "could not get validator sub status") 205 } 206 valContainers[i] = ðpb.ValidatorContainer{ 207 Index: types.ValidatorIndex(i), 208 Balance: allBalances[i], 209 Status: subStatus, 210 Validator: v1Validator, 211 } 212 } 213 } else { 214 valContainers = make([]*ethpb.ValidatorContainer, 0, len(validatorIds)) 215 for _, validatorId := range validatorIds { 216 var valIndex types.ValidatorIndex 217 if len(validatorId) == params.BeaconConfig().BLSPubkeyLength { 218 var ok bool 219 valIndex, ok = state.ValidatorIndexByPubkey(bytesutil.ToBytes48(validatorId)) 220 if !ok { 221 // Ignore well-formed yet unknown public keys. 222 continue 223 } 224 } else { 225 index, err := strconv.ParseUint(string(validatorId), 10, 64) 226 if err != nil { 227 e := newInvalidValidatorIdError(validatorId, err) 228 return nil, &e 229 } 230 valIndex = types.ValidatorIndex(index) 231 } 232 validator, err := state.ValidatorAtIndex(valIndex) 233 if _, ok := err.(*v1.ValidatorIndexOutOfRangeError); ok { 234 // Ignore well-formed yet unknown indexes. 235 continue 236 } 237 if err != nil { 238 return nil, errors.Wrap(err, "could not get validator") 239 } 240 v1Validator := migration.V1Alpha1ValidatorToV1(validator) 241 subStatus, err := validatorSubStatus(v1Validator, epoch) 242 if err != nil { 243 return nil, errors.Wrap(err, "could not get validator sub status") 244 } 245 valContainers = append(valContainers, ðpb.ValidatorContainer{ 246 Index: valIndex, 247 Balance: v1Validator.EffectiveBalance, 248 Status: subStatus, 249 Validator: v1Validator, 250 }) 251 } 252 } 253 254 return valContainers, nil 255 } 256 257 func validatorStatus(validator *ethpb.Validator, epoch types.Epoch) (ethpb.ValidatorStatus, error) { 258 valStatus, err := validatorSubStatus(validator, epoch) 259 if err != nil { 260 return 0, errors.Wrap(err, "could not get sub status") 261 } 262 switch valStatus { 263 case ethpb.ValidatorStatus_PENDING_INITIALIZED, ethpb.ValidatorStatus_PENDING_QUEUED: 264 return ethpb.ValidatorStatus_PENDING, nil 265 case ethpb.ValidatorStatus_ACTIVE_ONGOING, ethpb.ValidatorStatus_ACTIVE_SLASHED, ethpb.ValidatorStatus_ACTIVE_EXITING: 266 return ethpb.ValidatorStatus_ACTIVE, nil 267 case ethpb.ValidatorStatus_EXITED_UNSLASHED, ethpb.ValidatorStatus_EXITED_SLASHED: 268 return ethpb.ValidatorStatus_EXITED, nil 269 case ethpb.ValidatorStatus_WITHDRAWAL_POSSIBLE, ethpb.ValidatorStatus_WITHDRAWAL_DONE: 270 return ethpb.ValidatorStatus_WITHDRAWAL, nil 271 } 272 return 0, errors.New("invalid validator state") 273 } 274 275 func validatorSubStatus(validator *ethpb.Validator, epoch types.Epoch) (ethpb.ValidatorStatus, error) { 276 farFutureEpoch := params.BeaconConfig().FarFutureEpoch 277 278 // Pending. 279 if validator.ActivationEpoch > epoch { 280 if validator.ActivationEligibilityEpoch == farFutureEpoch { 281 return ethpb.ValidatorStatus_PENDING_INITIALIZED, nil 282 } else if validator.ActivationEligibilityEpoch < farFutureEpoch { 283 return ethpb.ValidatorStatus_PENDING_QUEUED, nil 284 } 285 } 286 287 // Active. 288 if validator.ActivationEpoch <= epoch && epoch < validator.ExitEpoch { 289 if validator.ExitEpoch == farFutureEpoch { 290 return ethpb.ValidatorStatus_ACTIVE_ONGOING, nil 291 } else if validator.ExitEpoch < farFutureEpoch { 292 if validator.Slashed { 293 return ethpb.ValidatorStatus_ACTIVE_SLASHED, nil 294 } 295 return ethpb.ValidatorStatus_ACTIVE_EXITING, nil 296 } 297 } 298 299 // Exited. 300 if validator.ExitEpoch <= epoch && epoch < validator.WithdrawableEpoch { 301 if validator.Slashed { 302 return ethpb.ValidatorStatus_EXITED_SLASHED, nil 303 } 304 return ethpb.ValidatorStatus_EXITED_UNSLASHED, nil 305 } 306 307 if validator.WithdrawableEpoch <= epoch { 308 if validator.EffectiveBalance != 0 { 309 return ethpb.ValidatorStatus_WITHDRAWAL_POSSIBLE, nil 310 } else { 311 return ethpb.ValidatorStatus_WITHDRAWAL_DONE, nil 312 } 313 } 314 315 return 0, errors.New("invalid validator state") 316 } 317 318 func handleValContainerErr(err error) error { 319 if outOfRangeErr, ok := err.(*v1.ValidatorIndexOutOfRangeError); ok { 320 return status.Errorf(codes.InvalidArgument, "Invalid validator ID: %v", outOfRangeErr) 321 } 322 if invalidIdErr, ok := err.(*invalidValidatorIdError); ok { 323 return status.Errorf(codes.InvalidArgument, "Invalid validator ID: %v", invalidIdErr) 324 } 325 return status.Errorf(codes.Internal, "Could not get validator container: %v", err) 326 }