github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/validators/validator.go (about)

     1  // Package validators contains libraries to shuffle validators
     2  // and retrieve active validator indices from a given slot
     3  // or an attestation. It also provides helper functions to locate
     4  // validator based on pubic key.
     5  package validators
     6  
     7  import (
     8  	"github.com/pkg/errors"
     9  	types "github.com/prysmaticlabs/eth2-types"
    10  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    11  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    12  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    13  	"github.com/prysmaticlabs/prysm/shared/params"
    14  )
    15  
    16  // InitiateValidatorExit takes in validator index and updates
    17  // validator with correct voluntary exit parameters.
    18  //
    19  // Spec pseudocode definition:
    20  //  def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:
    21  //    """
    22  //    Initiate the exit of the validator with index ``index``.
    23  //    """
    24  //    # Return if validator already initiated exit
    25  //    validator = state.validators[index]
    26  //    if validator.exit_epoch != FAR_FUTURE_EPOCH:
    27  //        return
    28  //
    29  //    # Compute exit queue epoch
    30  //    exit_epochs = [v.exit_epoch for v in state.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
    31  //    exit_queue_epoch = max(exit_epochs + [compute_activation_exit_epoch(get_current_epoch(state))])
    32  //    exit_queue_churn = len([v for v in state.validators if v.exit_epoch == exit_queue_epoch])
    33  //    if exit_queue_churn >= get_validator_churn_limit(state):
    34  //        exit_queue_epoch += Epoch(1)
    35  //
    36  //    # Set validator exit epoch and withdrawable epoch
    37  //    validator.exit_epoch = exit_queue_epoch
    38  //    validator.withdrawable_epoch = Epoch(validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY)
    39  func InitiateValidatorExit(state iface.BeaconState, idx types.ValidatorIndex) (iface.BeaconState, error) {
    40  	validator, err := state.ValidatorAtIndex(idx)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	if validator.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
    45  		return state, nil
    46  	}
    47  	var exitEpochs []types.Epoch
    48  	err = state.ReadFromEveryValidator(func(idx int, val iface.ReadOnlyValidator) error {
    49  		if val.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
    50  			exitEpochs = append(exitEpochs, val.ExitEpoch())
    51  		}
    52  		return nil
    53  	})
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	exitEpochs = append(exitEpochs, helpers.ActivationExitEpoch(helpers.CurrentEpoch(state)))
    58  
    59  	// Obtain the exit queue epoch as the maximum number in the exit epochs array.
    60  	exitQueueEpoch := types.Epoch(0)
    61  	for _, i := range exitEpochs {
    62  		if exitQueueEpoch < i {
    63  			exitQueueEpoch = i
    64  		}
    65  	}
    66  
    67  	// We use the exit queue churn to determine if we have passed a churn limit.
    68  	exitQueueChurn := uint64(0)
    69  	err = state.ReadFromEveryValidator(func(idx int, val iface.ReadOnlyValidator) error {
    70  		if val.ExitEpoch() == exitQueueEpoch {
    71  			exitQueueChurn++
    72  		}
    73  		return nil
    74  	})
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	activeValidatorCount, err := helpers.ActiveValidatorCount(state, helpers.CurrentEpoch(state))
    79  	if err != nil {
    80  		return nil, errors.Wrap(err, "could not get active validator count")
    81  	}
    82  	churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
    83  	if err != nil {
    84  		return nil, errors.Wrap(err, "could not get churn limit")
    85  	}
    86  
    87  	if exitQueueChurn >= churn {
    88  		exitQueueEpoch++
    89  	}
    90  	validator.ExitEpoch = exitQueueEpoch
    91  	validator.WithdrawableEpoch = exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
    92  	if err := state.UpdateValidatorAtIndex(idx, validator); err != nil {
    93  		return nil, err
    94  	}
    95  	return state, nil
    96  }
    97  
    98  // SlashValidator slashes the malicious validator's balance and awards
    99  // the whistleblower's balance.
   100  //
   101  // Spec pseudocode definition:
   102  //  def slash_validator(state: BeaconState,
   103  //                    slashed_index: ValidatorIndex,
   104  //                    whistleblower_index: ValidatorIndex=None) -> None:
   105  //    """
   106  //    Slash the validator with index ``slashed_index``.
   107  //    """
   108  //    epoch = get_current_epoch(state)
   109  //    initiate_validator_exit(state, slashed_index)
   110  //    validator = state.validators[slashed_index]
   111  //    validator.slashed = True
   112  //    validator.withdrawable_epoch = max(validator.withdrawable_epoch, Epoch(epoch + EPOCHS_PER_SLASHINGS_VECTOR))
   113  //    state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
   114  //    decrease_balance(state, slashed_index, validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT)
   115  //
   116  //    # Apply proposer and whistleblower rewards
   117  //    proposer_index = get_beacon_proposer_index(state)
   118  //    if whistleblower_index is None:
   119  //        whistleblower_index = proposer_index
   120  //    whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
   121  //    proposer_reward = Gwei(whistleblower_reward // PROPOSER_REWARD_QUOTIENT)
   122  //    increase_balance(state, proposer_index, proposer_reward)
   123  //    increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
   124  func SlashValidator(state iface.BeaconState, slashedIdx types.ValidatorIndex) (iface.BeaconState, error) {
   125  	state, err := InitiateValidatorExit(state, slashedIdx)
   126  	if err != nil {
   127  		return nil, errors.Wrapf(err, "could not initiate validator %d exit", slashedIdx)
   128  	}
   129  	currentEpoch := helpers.SlotToEpoch(state.Slot())
   130  	validator, err := state.ValidatorAtIndex(slashedIdx)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	validator.Slashed = true
   135  	maxWithdrawableEpoch := types.MaxEpoch(validator.WithdrawableEpoch, currentEpoch+params.BeaconConfig().EpochsPerSlashingsVector)
   136  	validator.WithdrawableEpoch = maxWithdrawableEpoch
   137  
   138  	if err := state.UpdateValidatorAtIndex(slashedIdx, validator); err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	// The slashing amount is represented by epochs per slashing vector. The validator's effective balance is then applied to that amount.
   143  	slashings := state.Slashings()
   144  	currentSlashing := slashings[currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector]
   145  	if err := state.UpdateSlashingsAtIndex(
   146  		uint64(currentEpoch%params.BeaconConfig().EpochsPerSlashingsVector),
   147  		currentSlashing+validator.EffectiveBalance,
   148  	); err != nil {
   149  		return nil, err
   150  	}
   151  	if err := helpers.DecreaseBalance(state, slashedIdx, validator.EffectiveBalance/params.BeaconConfig().MinSlashingPenaltyQuotient); err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	proposerIdx, err := helpers.BeaconProposerIndex(state)
   156  	if err != nil {
   157  		return nil, errors.Wrap(err, "could not get proposer idx")
   158  	}
   159  	// In phase 0, the proposer is the whistleblower.
   160  	whistleBlowerIdx := proposerIdx
   161  	whistleblowerReward := validator.EffectiveBalance / params.BeaconConfig().WhistleBlowerRewardQuotient
   162  	proposerReward := whistleblowerReward / params.BeaconConfig().ProposerRewardQuotient
   163  	err = helpers.IncreaseBalance(state, proposerIdx, proposerReward)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	err = helpers.IncreaseBalance(state, whistleBlowerIdx, whistleblowerReward-proposerReward)
   168  	if err != nil {
   169  		return nil, err
   170  	}
   171  	return state, nil
   172  }
   173  
   174  // ActivatedValidatorIndices determines the indices activated during the given epoch.
   175  func ActivatedValidatorIndices(epoch types.Epoch, validators []*ethpb.Validator) []types.ValidatorIndex {
   176  	activations := make([]types.ValidatorIndex, 0)
   177  	for i := 0; i < len(validators); i++ {
   178  		val := validators[i]
   179  		if val.ActivationEpoch <= epoch && epoch < val.ExitEpoch {
   180  			activations = append(activations, types.ValidatorIndex(i))
   181  		}
   182  	}
   183  	return activations
   184  }
   185  
   186  // SlashedValidatorIndices determines the indices slashed during the given epoch.
   187  func SlashedValidatorIndices(epoch types.Epoch, validators []*ethpb.Validator) []types.ValidatorIndex {
   188  	slashed := make([]types.ValidatorIndex, 0)
   189  	for i := 0; i < len(validators); i++ {
   190  		val := validators[i]
   191  		maxWithdrawableEpoch := types.MaxEpoch(val.WithdrawableEpoch, epoch+params.BeaconConfig().EpochsPerSlashingsVector)
   192  		if val.WithdrawableEpoch == maxWithdrawableEpoch && val.Slashed {
   193  			slashed = append(slashed, types.ValidatorIndex(i))
   194  		}
   195  	}
   196  	return slashed
   197  }
   198  
   199  // ExitedValidatorIndices determines the indices exited during the current epoch.
   200  func ExitedValidatorIndices(epoch types.Epoch, validators []*ethpb.Validator, activeValidatorCount uint64) ([]types.ValidatorIndex, error) {
   201  	exited := make([]types.ValidatorIndex, 0)
   202  	exitEpochs := make([]types.Epoch, 0)
   203  	for i := 0; i < len(validators); i++ {
   204  		val := validators[i]
   205  		if val.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
   206  			exitEpochs = append(exitEpochs, val.ExitEpoch)
   207  		}
   208  	}
   209  	exitQueueEpoch := types.Epoch(0)
   210  	for _, i := range exitEpochs {
   211  		if exitQueueEpoch < i {
   212  			exitQueueEpoch = i
   213  		}
   214  	}
   215  
   216  	// We use the exit queue churn to determine if we have passed a churn limit.
   217  	exitQueueChurn := uint64(0)
   218  	for _, val := range validators {
   219  		if val.ExitEpoch == exitQueueEpoch {
   220  			exitQueueChurn++
   221  		}
   222  	}
   223  	churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
   224  	if err != nil {
   225  		return nil, errors.Wrap(err, "could not get churn limit")
   226  	}
   227  	if churn < exitQueueChurn {
   228  		exitQueueEpoch++
   229  	}
   230  	withdrawableEpoch := exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
   231  	for i, val := range validators {
   232  		if val.ExitEpoch == epoch && val.WithdrawableEpoch == withdrawableEpoch &&
   233  			val.EffectiveBalance > params.BeaconConfig().EjectionBalance {
   234  			exited = append(exited, types.ValidatorIndex(i))
   235  		}
   236  	}
   237  	return exited, nil
   238  }
   239  
   240  // EjectedValidatorIndices determines the indices ejected during the given epoch.
   241  func EjectedValidatorIndices(epoch types.Epoch, validators []*ethpb.Validator, activeValidatorCount uint64) ([]types.ValidatorIndex, error) {
   242  	ejected := make([]types.ValidatorIndex, 0)
   243  	exitEpochs := make([]types.Epoch, 0)
   244  	for i := 0; i < len(validators); i++ {
   245  		val := validators[i]
   246  		if val.ExitEpoch != params.BeaconConfig().FarFutureEpoch {
   247  			exitEpochs = append(exitEpochs, val.ExitEpoch)
   248  		}
   249  	}
   250  	exitQueueEpoch := types.Epoch(0)
   251  	for _, i := range exitEpochs {
   252  		if exitQueueEpoch < i {
   253  			exitQueueEpoch = i
   254  		}
   255  	}
   256  
   257  	// We use the exit queue churn to determine if we have passed a churn limit.
   258  	exitQueueChurn := uint64(0)
   259  	for _, val := range validators {
   260  		if val.ExitEpoch == exitQueueEpoch {
   261  			exitQueueChurn++
   262  		}
   263  	}
   264  	churn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
   265  	if err != nil {
   266  		return nil, errors.Wrap(err, "could not get churn limit")
   267  	}
   268  	if churn < exitQueueChurn {
   269  		exitQueueEpoch++
   270  	}
   271  	withdrawableEpoch := exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
   272  	for i, val := range validators {
   273  		if val.ExitEpoch == epoch && val.WithdrawableEpoch == withdrawableEpoch &&
   274  			val.EffectiveBalance <= params.BeaconConfig().EjectionBalance {
   275  			ejected = append(ejected, types.ValidatorIndex(i))
   276  		}
   277  	}
   278  	return ejected, nil
   279  }