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 &ethpb.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 &ethpb.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 := &ethpb.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  				&ethpb.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  				&ethpb.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  				&ethpb.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  			&ethpb.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 := &ethpb.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 := &ethpb.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  }