From 72bbc33a65ac5571bc71159463ad4ee279ba8427 Mon Sep 17 00:00:00 2001 From: Nishant Das Date: Thu, 13 Aug 2020 22:37:38 +0800 Subject: [PATCH] Fix Validator Exits (#6986) * add change * Update beacon-chain/core/blocks/exit.go Co-authored-by: Shay Zluf * Merge refs/heads/master into fixExits --- beacon-chain/core/blocks/exit.go | 52 ++++++++++++++++++++++----- beacon-chain/core/blocks/exit_test.go | 20 +++++++++++ 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/beacon-chain/core/blocks/exit.go b/beacon-chain/core/blocks/exit.go index 0baef68bc..3b7d0804b 100644 --- a/beacon-chain/core/blocks/exit.go +++ b/beacon-chain/core/blocks/exit.go @@ -67,13 +67,24 @@ func ProcessVoluntaryExitsNoVerify( beaconState *stateTrie.BeaconState, body *ethpb.BeaconBlockBody, ) (*stateTrie.BeaconState, error) { - var err error exits := body.VoluntaryExits for idx, exit := range exits { if exit == nil || exit.Exit == nil { return nil, errors.New("nil exit") } + val, err := beaconState.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex) + if err != nil { + return nil, err + } + if err := verifyExitConditions(val, beaconState.Slot(), exit.Exit); err != nil { + return nil, err + } + // Validate that fork and genesis root are valid. + _, err = helpers.Domain(beaconState.Fork(), exit.Exit.Epoch, params.BeaconConfig().DomainVoluntaryExit, beaconState.GenesisValidatorRoot()) + if err != nil { + return nil, err + } beaconState, err = v.InitiateValidatorExit(beaconState, exit.Exit.ValidatorIndex) if err != nil { return nil, errors.Wrapf(err, "failed to process voluntary exit at index %d", idx) @@ -107,6 +118,37 @@ func VerifyExit(validator *stateTrie.ReadOnlyValidator, currentSlot uint64, fork } exit := signed.Exit + if err := verifyExitConditions(validator, currentSlot, exit); err != nil { + return err + } + domain, err := helpers.Domain(fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit, genesisRoot) + if err != nil { + return err + } + valPubKey := validator.PublicKey() + if err := helpers.VerifySigningRoot(exit, valPubKey[:], signed.Signature, domain); err != nil { + return helpers.ErrSigFailedToVerify + } + return nil +} + +// verifyExitConditions implements the spec defined validation for voluntary exits(excluding signatures). +// +// Spec pseudocode definition: +// def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: +// """ +// Process ``VoluntaryExit`` operation. +// """ +// validator = state.validator_registry[exit.validator_index] +// # Verify the validator is active +// assert is_active_validator(validator, get_current_epoch(state)) +// # Verify the validator has not yet exited +// assert validator.exit_epoch == FAR_FUTURE_EPOCH +// # Exits must specify an epoch when they become valid; they are not valid before then +// assert get_current_epoch(state) >= exit.epoch +// # Verify the validator has been active long enough +// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD +func verifyExitConditions(validator *stateTrie.ReadOnlyValidator, currentSlot uint64, exit *ethpb.VoluntaryExit) error { currentEpoch := helpers.SlotToEpoch(currentSlot) // Verify the validator is active. if !helpers.IsActiveValidatorUsingTrie(validator, currentEpoch) { @@ -128,13 +170,5 @@ func VerifyExit(validator *stateTrie.ReadOnlyValidator, currentSlot uint64, fork validator.ActivationEpoch()+params.BeaconConfig().ShardCommitteePeriod, ) } - domain, err := helpers.Domain(fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit, genesisRoot) - if err != nil { - return err - } - valPubKey := validator.PublicKey() - if err := helpers.VerifySigningRoot(exit, valPubKey[:], signed.Signature, domain); err != nil { - return helpers.ErrSigFailedToVerify - } return nil } diff --git a/beacon-chain/core/blocks/exit_test.go b/beacon-chain/core/blocks/exit_test.go index 427640450..4d0283c32 100644 --- a/beacon-chain/core/blocks/exit_test.go +++ b/beacon-chain/core/blocks/exit_test.go @@ -41,6 +41,10 @@ func TestProcessVoluntaryExits_ValidatorNotActive(t *testing.T) { want := "non-active validator cannot exit" _, err = blocks.ProcessVoluntaryExits(context.Background(), state, block.Body) assert.ErrorContains(t, want, err) + + // Check conformance of no verify method. + _, err = blocks.ProcessVoluntaryExitsNoVerify(state, block.Body) + assert.ErrorContains(t, want, err) } func TestProcessVoluntaryExits_InvalidExitEpoch(t *testing.T) { @@ -70,6 +74,11 @@ func TestProcessVoluntaryExits_InvalidExitEpoch(t *testing.T) { want := "expected current epoch >= exit epoch" _, err = blocks.ProcessVoluntaryExits(context.Background(), state, block.Body) assert.ErrorContains(t, want, err) + + // Check conformance of no verify method. + _, err = blocks.ProcessVoluntaryExitsNoVerify(state, block.Body) + assert.ErrorContains(t, want, err) + } func TestProcessVoluntaryExits_NotActiveLongEnoughToExit(t *testing.T) { @@ -143,6 +152,7 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) { }, } + stateCopy := state.Copy() newState, err := blocks.ProcessVoluntaryExits(context.Background(), state, block.Body) require.NoError(t, err, "Could not process exits") newRegistry := newState.Validators() @@ -150,4 +160,14 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) { t.Errorf("Expected validator exit epoch to be %d, got %d", helpers.ActivationExitEpoch(state.Slot()/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch) } + + // Check conformance with NoVerify Exit Method. + newState, err = blocks.ProcessVoluntaryExitsNoVerify(stateCopy, block.Body) + require.NoError(t, err, "Could not process exits") + newRegistry = newState.Validators() + if newRegistry[0].ExitEpoch != helpers.ActivationExitEpoch(stateCopy.Slot()/params.BeaconConfig().SlotsPerEpoch) { + t.Errorf("Expected validator exit epoch to be %d, got %d", + helpers.ActivationExitEpoch(stateCopy.Slot()/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch) + } + }