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 }