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 &ethpb.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 &ethpb.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 &ethpb.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] = &ethpb.ValidatorBalance{
   129  			Index:   valContainers[i].Index,
   130  			Balance: valContainers[i].Balance,
   131  		}
   132  	}
   133  	return &ethpb.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 := &ethpb.Committee{
   181  				Index:      index,
   182  				Slot:       slot,
   183  				Validators: committee,
   184  			}
   185  			committees = append(committees, committeeContainer)
   186  		}
   187  	}
   188  	return &ethpb.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] = &ethpb.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, &ethpb.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  }