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

     1  package blocks
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/pkg/errors"
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    10  	v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
    11  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    12  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    13  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    14  	"github.com/prysmaticlabs/prysm/shared/params"
    15  )
    16  
    17  // ValidatorAlreadyExitedMsg defines a message saying that a validator has already exited.
    18  var ValidatorAlreadyExitedMsg = "has already submitted an exit, which will take place at epoch"
    19  
    20  // ValidatorCannotExitYetMsg defines a message saying that a validator cannot exit
    21  // because it has not been active long enough.
    22  var ValidatorCannotExitYetMsg = "validator has not been active long enough to exit"
    23  
    24  // ProcessVoluntaryExits is one of the operations performed
    25  // on each processed beacon block to determine which validators
    26  // should exit the state's validator registry.
    27  //
    28  // Spec pseudocode definition:
    29  //   def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
    30  //    voluntary_exit = signed_voluntary_exit.message
    31  //    validator = state.validators[voluntary_exit.validator_index]
    32  //    # Verify the validator is active
    33  //    assert is_active_validator(validator, get_current_epoch(state))
    34  //    # Verify exit has not been initiated
    35  //    assert validator.exit_epoch == FAR_FUTURE_EPOCH
    36  //    # Exits must specify an epoch when they become valid; they are not valid before then
    37  //    assert get_current_epoch(state) >= voluntary_exit.epoch
    38  //    # Verify the validator has been active long enough
    39  //    assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
    40  //    # Verify signature
    41  //    domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
    42  //    signing_root = compute_signing_root(voluntary_exit, domain)
    43  //    assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
    44  //    # Initiate exit
    45  //    initiate_validator_exit(state, voluntary_exit.validator_index)
    46  func ProcessVoluntaryExits(
    47  	_ context.Context,
    48  	beaconState iface.BeaconState,
    49  	exits []*ethpb.SignedVoluntaryExit,
    50  ) (iface.BeaconState, error) {
    51  	for idx, exit := range exits {
    52  		if exit == nil || exit.Exit == nil {
    53  			return nil, errors.New("nil voluntary exit in block body")
    54  		}
    55  		val, err := beaconState.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex)
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  		if err := VerifyExitAndSignature(val, beaconState.Slot(), beaconState.Fork(), exit, beaconState.GenesisValidatorRoot()); err != nil {
    60  			return nil, errors.Wrapf(err, "could not verify exit %d", idx)
    61  		}
    62  		beaconState, err = v.InitiateValidatorExit(beaconState, exit.Exit.ValidatorIndex)
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  	}
    67  	return beaconState, nil
    68  }
    69  
    70  // VerifyExitAndSignature implements the spec defined validation for voluntary exits.
    71  //
    72  // Spec pseudocode definition:
    73  //   def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
    74  //    voluntary_exit = signed_voluntary_exit.message
    75  //    validator = state.validators[voluntary_exit.validator_index]
    76  //    # Verify the validator is active
    77  //    assert is_active_validator(validator, get_current_epoch(state))
    78  //    # Verify exit has not been initiated
    79  //    assert validator.exit_epoch == FAR_FUTURE_EPOCH
    80  //    # Exits must specify an epoch when they become valid; they are not valid before then
    81  //    assert get_current_epoch(state) >= voluntary_exit.epoch
    82  //    # Verify the validator has been active long enough
    83  //    assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
    84  //    # Verify signature
    85  //    domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
    86  //    signing_root = compute_signing_root(voluntary_exit, domain)
    87  //    assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
    88  //    # Initiate exit
    89  //    initiate_validator_exit(state, voluntary_exit.validator_index)
    90  func VerifyExitAndSignature(
    91  	validator iface.ReadOnlyValidator,
    92  	currentSlot types.Slot,
    93  	fork *pb.Fork,
    94  	signed *ethpb.SignedVoluntaryExit,
    95  	genesisRoot []byte,
    96  ) error {
    97  	if signed == nil || signed.Exit == nil {
    98  		return errors.New("nil exit")
    99  	}
   100  
   101  	exit := signed.Exit
   102  	if err := verifyExitConditions(validator, currentSlot, exit); err != nil {
   103  		return err
   104  	}
   105  	domain, err := helpers.Domain(fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit, genesisRoot)
   106  	if err != nil {
   107  		return err
   108  	}
   109  	valPubKey := validator.PublicKey()
   110  	if err := helpers.VerifySigningRoot(exit, valPubKey[:], signed.Signature, domain); err != nil {
   111  		return helpers.ErrSigFailedToVerify
   112  	}
   113  	return nil
   114  }
   115  
   116  // verifyExitConditions implements the spec defined validation for voluntary exits(excluding signatures).
   117  //
   118  // Spec pseudocode definition:
   119  //   def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVoluntaryExit) -> None:
   120  //    voluntary_exit = signed_voluntary_exit.message
   121  //    validator = state.validators[voluntary_exit.validator_index]
   122  //    # Verify the validator is active
   123  //    assert is_active_validator(validator, get_current_epoch(state))
   124  //    # Verify exit has not been initiated
   125  //    assert validator.exit_epoch == FAR_FUTURE_EPOCH
   126  //    # Exits must specify an epoch when they become valid; they are not valid before then
   127  //    assert get_current_epoch(state) >= voluntary_exit.epoch
   128  //    # Verify the validator has been active long enough
   129  //    assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
   130  //    # Verify signature
   131  //    domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
   132  //    signing_root = compute_signing_root(voluntary_exit, domain)
   133  //    assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
   134  //    # Initiate exit
   135  //    initiate_validator_exit(state, voluntary_exit.validator_index)
   136  func verifyExitConditions(validator iface.ReadOnlyValidator, currentSlot types.Slot, exit *ethpb.VoluntaryExit) error {
   137  	currentEpoch := helpers.SlotToEpoch(currentSlot)
   138  	// Verify the validator is active.
   139  	if !helpers.IsActiveValidatorUsingTrie(validator, currentEpoch) {
   140  		return errors.New("non-active validator cannot exit")
   141  	}
   142  	// Verify the validator has not yet submitted an exit.
   143  	if validator.ExitEpoch() != params.BeaconConfig().FarFutureEpoch {
   144  		return fmt.Errorf("validator with index %d %s: %v", exit.ValidatorIndex, ValidatorAlreadyExitedMsg, validator.ExitEpoch())
   145  	}
   146  	// Exits must specify an epoch when they become valid; they are not valid before then.
   147  	if currentEpoch < exit.Epoch {
   148  		return fmt.Errorf("expected current epoch >= exit epoch, received %d < %d", currentEpoch, exit.Epoch)
   149  	}
   150  	// Verify the validator has been active long enough.
   151  	if currentEpoch < validator.ActivationEpoch()+params.BeaconConfig().ShardCommitteePeriod {
   152  		return fmt.Errorf(
   153  			"%s: %d of %d epochs. Validator will be eligible for exit at epoch %d",
   154  			ValidatorCannotExitYetMsg,
   155  			currentEpoch-validator.ActivationEpoch(),
   156  			params.BeaconConfig().ShardCommitteePeriod,
   157  			validator.ActivationEpoch()+params.BeaconConfig().ShardCommitteePeriod,
   158  		)
   159  	}
   160  	return nil
   161  }