From b0a3c5aaad282a2b4019f17ffff8cbb1e108057f Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 23 Apr 2019 15:02:14 -0700 Subject: [PATCH 1/8] Implemented DelayedActivationExitEpoch and ChurnLimit --- beacon-chain/core/blocks/block_operations.go | 2 +- beacon-chain/core/helpers/validators.go | 26 +++++++++++++--- beacon-chain/core/helpers/validators_test.go | 31 ++++++++++++++++++- beacon-chain/core/validators/validator.go | 4 +-- .../core/validators/validator_test.go | 6 ++-- 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/beacon-chain/core/blocks/block_operations.go b/beacon-chain/core/blocks/block_operations.go index b5ac5aaee1d3..f5f6729d6344 100644 --- a/beacon-chain/core/blocks/block_operations.go +++ b/beacon-chain/core/blocks/block_operations.go @@ -728,7 +728,7 @@ func ProcessValidatorExits( func verifyExit(beaconState *pb.BeaconState, exit *pb.VoluntaryExit, verifySignatures bool) error { validator := beaconState.ValidatorRegistry[exit.ValidatorIndex] currentEpoch := helpers.CurrentEpoch(beaconState) - entryExitEffectEpoch := helpers.EntryExitEffectEpoch(currentEpoch) + entryExitEffectEpoch := helpers.DelayedActivationExitEpoch(currentEpoch) if validator.ExitEpoch <= entryExitEffectEpoch { return fmt.Errorf( "validator exit epoch should be > entry_exit_effect_epoch, received %d <= %d", diff --git a/beacon-chain/core/helpers/validators.go b/beacon-chain/core/helpers/validators.go index 3b8abe80d7be..4d60209a8bab 100644 --- a/beacon-chain/core/helpers/validators.go +++ b/beacon-chain/core/helpers/validators.go @@ -41,20 +41,36 @@ func ActiveValidatorIndices(validators []*pb.Validator, epoch uint64) []uint64 { return indices } -// EntryExitEffectEpoch takes in epoch number and returns when +// DelayedActivationExitEpoch takes in epoch number and returns when // the validator is eligible for activation and exit. // // Spec pseudocode definition: -// def get_entry_exit_effect_epoch(epoch: Epoch) -> Epoch: +// def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: // """ -// An entry or exit triggered in the ``epoch`` given by the input takes effect at -// the epoch given by the output. +// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. // """ // return epoch + 1 + ACTIVATION_EXIT_DELAY -func EntryExitEffectEpoch(epoch uint64) uint64 { +func DelayedActivationExitEpoch(epoch uint64) uint64 { return epoch + 1 + params.BeaconConfig().ActivationExitDelay } +// ChurnLimit returns then number of validators that are allowed to +// enter and exit validator pool for an epoch. +// +// Spec pseudocode definition: +// def get_churn_limit(state: BeaconState) -> int: +// return max( +// MIN_PER_EPOCH_CHURN_LIMIT, +// len(get_active_validator_indices(state, get_current_epoch(state))) // CHURN_LIMIT_QUOTIENT +// ) +func ChurnLimit(state *pb.BeaconState) uint64 { + validatorCount := uint64(len(ActiveValidatorIndices(state.ValidatorRegistry, CurrentEpoch(state)))) + if validatorCount/params.BeaconConfig().ChurnLimitQuotient > params.BeaconConfig().MinPerEpochChurnLimit { + return validatorCount / params.BeaconConfig().ChurnLimitQuotient + } + return params.BeaconConfig().MinPerEpochChurnLimit +} + // BeaconProposerIndex returns the index of the proposer of the block at a // given slot. // diff --git a/beacon-chain/core/helpers/validators_test.go b/beacon-chain/core/helpers/validators_test.go index 2c8361d70e55..7d8d615df326 100644 --- a/beacon-chain/core/helpers/validators_test.go +++ b/beacon-chain/core/helpers/validators_test.go @@ -97,9 +97,38 @@ func TestBeaconProposerIndex_EmptyCommittee(t *testing.T) { func TestEntryExitEffectEpoch_OK(t *testing.T) { epoch := uint64(9999) - got := EntryExitEffectEpoch(epoch) + got := DelayedActivationExitEpoch(epoch) wanted := epoch + 1 + params.BeaconConfig().ActivationExitDelay if wanted != got { t.Errorf("Wanted: %d, received: %d", wanted, got) } } + +func TestChurnLimit_OK(t *testing.T) { + tests := []struct { + validatorCount int + wantedChurn uint64 + }{ + {validatorCount: 1000, wantedChurn: 4}, + {validatorCount: 100000, wantedChurn: 4}, + {validatorCount: 1000000, wantedChurn: 15 /* validatorCount/churnLimitQuotient */}, + {validatorCount: 2000000, wantedChurn: 30 /* validatorCount/churnLimitQuotient */}, + } + for _, test := range tests { + validators := make([]*pb.Validator, test.validatorCount) + for i := 0; i < len(validators); i++ { + validators[i] = &pb.Validator{ + ExitEpoch: params.BeaconConfig().FarFutureEpoch, + } + } + + resultChurn := ChurnLimit(&pb.BeaconState{ + Slot: 1, + ValidatorRegistry: validators, + }) + if resultChurn != test.wantedChurn { + t.Errorf("ChurnLimit(%d) = %d, want = %d", + test.validatorCount, resultChurn, test.wantedChurn) + } + } +} diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index c2e4f2284504..50c2f8c350eb 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -161,7 +161,7 @@ func ActivateValidator(state *pb.BeaconState, idx uint64, genesis bool) (*pb.Bea if genesis { validator.ActivationEpoch = params.BeaconConfig().GenesisEpoch } else { - validator.ActivationEpoch = helpers.EntryExitEffectEpoch(helpers.CurrentEpoch(state)) + validator.ActivationEpoch = helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state)) } state.ValidatorRegistry[idx] = validator @@ -305,7 +305,7 @@ func SlashValidator(state *pb.BeaconState, idx uint64) (*pb.BeaconState, error) // state.validator_registry_update_epoch = current_epoch func UpdateRegistry(state *pb.BeaconState) (*pb.BeaconState, error) { currentEpoch := helpers.CurrentEpoch(state) - updatedEpoch := helpers.EntryExitEffectEpoch(currentEpoch) + updatedEpoch := helpers.DelayedActivationExitEpoch(currentEpoch) activeValidatorIndices := helpers.ActiveValidatorIndices( state.ValidatorRegistry, currentEpoch) diff --git a/beacon-chain/core/validators/validator_test.go b/beacon-chain/core/validators/validator_test.go index cc066ac97172..f115a8860f20 100644 --- a/beacon-chain/core/validators/validator_test.go +++ b/beacon-chain/core/validators/validator_test.go @@ -390,7 +390,7 @@ func TestActivateValidator_OK(t *testing.T) { t.Fatalf("could not execute activateValidator:%v", err) } currentEpoch := helpers.CurrentEpoch(state) - wantedEpoch := helpers.EntryExitEffectEpoch(currentEpoch) + wantedEpoch := helpers.DelayedActivationExitEpoch(currentEpoch) if newState.ValidatorRegistry[0].ActivationEpoch != wantedEpoch { t.Errorf("Wanted activation slot = %d, got %d", wantedEpoch, @@ -420,7 +420,7 @@ func TestExitValidator_OK(t *testing.T) { newState := ExitValidator(state, 0) currentEpoch := helpers.CurrentEpoch(state) - wantedEpoch := helpers.EntryExitEffectEpoch(currentEpoch) + wantedEpoch := helpers.DelayedActivationExitEpoch(currentEpoch) if newState.ValidatorRegistry[0].ExitEpoch != wantedEpoch { t.Errorf("Wanted exit slot %d, got %d", wantedEpoch, @@ -587,7 +587,7 @@ func TestUpdateRegistry_Activations(t *testing.T) { func TestUpdateRegistry_Exits(t *testing.T) { epoch := uint64(5) - exitEpoch := helpers.EntryExitEffectEpoch(epoch) + exitEpoch := helpers.DelayedActivationExitEpoch(epoch) state := &pb.BeaconState{ Slot: epoch * params.BeaconConfig().SlotsPerEpoch, ValidatorRegistry: []*pb.Validator{ From 17c92a258ba8b97640a29194e99e971074755773 Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 23 Apr 2019 17:58:57 -0700 Subject: [PATCH 2/8] starting InitiateValidatorExit --- beacon-chain/core/validators/validator.go | 47 +++++++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index c2e4f2284504..829f397e5e63 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -179,12 +179,51 @@ func ActivateValidator(state *pb.BeaconState, idx uint64, genesis bool) (*pb.Bea // // Spec pseudocode definition: // def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: +// """ +// Initiate the validator of the given ``index``. +// Note that this function mutates ``state``. +// """ +// # Return if validator already initiated exit // validator = state.validator_registry[index] -// validator.status_flags |= INITIATED_EXIT +// if validator.exit_epoch != FAR_FUTURE_EPOCH: +// return +// +// # Compute exit queue epoch +// exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH] +// exit_queue_epoch = max(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))]) +// exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) +// if exit_queue_churn >= get_churn_limit(state): +// exit_queue_epoch += 1 +// +// # Set validator exit epoch and withdrawable epoch +// validator.exit_epoch = exit_queue_epoch +// validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY func InitiateValidatorExit(state *pb.BeaconState, idx uint64) *pb.BeaconState { - state.ValidatorRegistry[idx].StatusFlags |= - pb.Validator_INITIATED_EXIT - return state + v := state.ValidatorRegistry[idx] + + if v.ExitEpoch != params.BeaconConfig().FarFutureEpoch { + return state + } + + var lastExitedEpoch uint64 + for i := 0; i < len(state.ValidatorRegistry); i++ { + if state.ValidatorRegistry[i].ExitEpoch != params.BeaconConfig().FarFutureEpoch { + lastExitedEpoch = state.ValidatorRegistry[i].ExitEpoch + } + } + if helpers.EntryExitEffectEpoch(helpers.CurrentEpoch(state)) > lastExitedEpoch { + lastExitedEpoch = helpers.EntryExitEffectEpoch(helpers.CurrentEpoch(state)) + } + + var currentExitQueueLength uint64 + for i := 0; i < len(state.ValidatorRegistry); i++ { + if state.ValidatorRegistry[i].ExitEpoch == lastExitedEpoch { + currentExitQueueLength++ + } + } + + if currentExitQueueLength >= helpers. + } // ExitValidator takes in validator index and does house From 6fcaa42752befd1feb50046e333e68327e2f3082 Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 23 Apr 2019 18:08:54 -0700 Subject: [PATCH 3/8] finished InitiateValidatorExit --- beacon-chain/core/validators/validator.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 0fb343c3faee..1cf8140129ee 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -201,29 +201,42 @@ func ActivateValidator(state *pb.BeaconState, idx uint64, genesis bool) (*pb.Bea func InitiateValidatorExit(state *pb.BeaconState, idx uint64) *pb.BeaconState { v := state.ValidatorRegistry[idx] + // Return if validator already initiated exit. + // According to the spec, this is not an assert condition and + // shouldn't fail beacon block state transition. if v.ExitEpoch != params.BeaconConfig().FarFutureEpoch { return state } + // Find the last initiated exited epoch and add exit delay to it. var lastExitedEpoch uint64 for i := 0; i < len(state.ValidatorRegistry); i++ { if state.ValidatorRegistry[i].ExitEpoch != params.BeaconConfig().FarFutureEpoch { lastExitedEpoch = state.ValidatorRegistry[i].ExitEpoch } } - if helpers.EntryExitEffectEpoch(helpers.CurrentEpoch(state)) > lastExitedEpoch { - lastExitedEpoch = helpers.EntryExitEffectEpoch(helpers.CurrentEpoch(state)) + if helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state)) > lastExitedEpoch { + lastExitedEpoch = helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state)) } + // Find the total number of validators exiting same epoch as + // input validator. If the number is greater than churn limit, postpone + // exit epoch to the next epoch. var currentExitQueueLength uint64 for i := 0; i < len(state.ValidatorRegistry); i++ { if state.ValidatorRegistry[i].ExitEpoch == lastExitedEpoch { currentExitQueueLength++ } } + if currentExitQueueLength >= helpers.ChurnLimit(state) { + lastExitedEpoch++ + } - if currentExitQueueLength >= helpers. + v.ExitEpoch = lastExitedEpoch + v.WithdrawableEpoch = v.ExitEpoch + params.BeaconConfig().MinValidatorWithdrawalDelay + state.ValidatorRegistry[idx] = v + return state } // ExitValidator takes in validator index and does house From 61ad3e8709a27c1807302719baa202255fccca1a Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 23 Apr 2019 18:26:44 -0700 Subject: [PATCH 4/8] fixed existing tests --- .../tests/state-tests/block-processing.yaml | 5 +---- beacon-chain/core/blocks/block_operations_test.go | 15 ++++++++------- beacon-chain/core/validators/validator.go | 2 +- beacon-chain/core/validators/validator_test.go | 11 ----------- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/beacon-chain/chaintest/tests/state-tests/block-processing.yaml b/beacon-chain/chaintest/tests/state-tests/block-processing.yaml index 0d20bad98169..3641b75e176a 100644 --- a/beacon-chain/chaintest/tests/state-tests/block-processing.yaml +++ b/beacon-chain/chaintest/tests/state-tests/block-processing.yaml @@ -52,14 +52,11 @@ test_cases: slashable_attestation_1_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51] slashable_attestation_2_custody_bitfield: !binary "F" slashable_attestation_2_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51] - validator_exits: - - epoch: 144115188075855872 - validator_index: 45 # At slot 9223372036854775868, validator at index 45 triggers a voluntary exit + TODO(2307): Validator rotation logic is getting revamped piece by piece, we'll add the Exit test caes back when state transtion aligns spec v0.6. results: slot: 9223372036854775872 num_validators: 67 penalized_validators: [50, 51] # We test that the validators at indices were indeed penalized - exited_validators: [45] # We confirm the indices of validators that willingly exited the registry # TODO(1387): Waiting for spec to stable to proceed with this test case # - config: # skip_slots: [10, 20] diff --git a/beacon-chain/core/blocks/block_operations_test.go b/beacon-chain/core/blocks/block_operations_test.go index 72189ce73d26..47855515f748 100644 --- a/beacon-chain/core/blocks/block_operations_test.go +++ b/beacon-chain/core/blocks/block_operations_test.go @@ -364,7 +364,7 @@ func TestProcessProposerSlashings_AppliesCorrectStatus(t *testing.T) { beaconState := &pb.BeaconState{ ValidatorRegistry: validators, Slot: currentSlot, - Balances: validatorBalances, + Balances: validatorBalances, LatestSlashedBalances: []uint64{0}, } block := &pb.BeaconBlock{ @@ -642,7 +642,7 @@ func TestProcessAttesterSlashings_AppliesCorrectStatus(t *testing.T) { beaconState := &pb.BeaconState{ ValidatorRegistry: validators, Slot: currentSlot, - Balances: validatorBalances, + Balances: validatorBalances, LatestSlashedBalances: make([]uint64, params.BeaconConfig().LatestSlashedExitLength), } block := &pb.BeaconBlock{ @@ -1172,7 +1172,7 @@ func TestProcessValidatorDeposits_ProcessDepositHelperFuncFails(t *testing.T) { root := depositTrie.Root() beaconState := &pb.BeaconState{ ValidatorRegistry: registry, - Balances: balances, + Balances: balances, LatestEth1Data: &pb.Eth1Data{ DepositRootHash32: root[:], BlockHash32: root[:], @@ -1245,7 +1245,7 @@ func TestProcessValidatorDeposits_IncorrectMerkleIndex(t *testing.T) { balances := []uint64{0} beaconState := &pb.BeaconState{ ValidatorRegistry: registry, - Balances: balances, + Balances: balances, Slot: currentSlot, GenesisTime: uint64(genesisTime), } @@ -1325,7 +1325,7 @@ func TestProcessValidatorDeposits_ProcessCorrectly(t *testing.T) { root := depositTrie.Root() beaconState := &pb.BeaconState{ ValidatorRegistry: registry, - Balances: balances, + Balances: balances, LatestEth1Data: &pb.Eth1Data{ DepositRootHash32: root[:], BlockHash32: root[:], @@ -1502,7 +1502,8 @@ func TestProcessValidatorExits_AppliesCorrectStatus(t *testing.T) { t.Fatalf("Could not process exits: %v", err) } newRegistry := newState.ValidatorRegistry - if newRegistry[0].StatusFlags == pb.Validator_INITIAL { - t.Error("Expected validator status to change, remained INITIAL") + if newRegistry[0].ExitEpoch != helpers.DelayedActivationExitEpoch(state.Slot/params.BeaconConfig().SlotsPerEpoch) { + t.Errorf("Expected validator exit epoch to be %d, got %d", + helpers.DelayedActivationExitEpoch(state.Slot/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch) } } diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 1cf8140129ee..1be445c1851c 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -208,7 +208,7 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) *pb.BeaconState { return state } - // Find the last initiated exited epoch and add exit delay to it. + // Find the last initiated exited epoch and add an exit delay to it. var lastExitedEpoch uint64 for i := 0; i < len(state.ValidatorRegistry); i++ { if state.ValidatorRegistry[i].ExitEpoch != params.BeaconConfig().FarFutureEpoch { diff --git a/beacon-chain/core/validators/validator_test.go b/beacon-chain/core/validators/validator_test.go index f115a8860f20..d599b9bacbb8 100644 --- a/beacon-chain/core/validators/validator_test.go +++ b/beacon-chain/core/validators/validator_test.go @@ -398,17 +398,6 @@ func TestActivateValidator_OK(t *testing.T) { } } -func TestInitiateValidatorExit_OK(t *testing.T) { - state := &pb.BeaconState{ValidatorRegistry: []*pb.Validator{{}, {}, {}}} - newState := InitiateValidatorExit(state, 2) - if newState.ValidatorRegistry[0].StatusFlags != pb.Validator_INITIAL { - t.Errorf("Wanted flag INITIAL, got %v", newState.ValidatorRegistry[0].StatusFlags) - } - if newState.ValidatorRegistry[2].StatusFlags != pb.Validator_INITIATED_EXIT { - t.Errorf("Wanted flag ACTIVE_PENDING_EXIT, got %v", newState.ValidatorRegistry[0].StatusFlags) - } -} - func TestExitValidator_OK(t *testing.T) { state := &pb.BeaconState{ Slot: 100, // epoch 2 From ac9cedfa0c5d6695fb62f149b14a511bb4903eef Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 23 Apr 2019 18:57:26 -0700 Subject: [PATCH 5/8] fixed a bug, i hate python --- beacon-chain/core/validators/validator.go | 22 ++++++++++++------- .../core/validators/validator_test.go | 12 ++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 1be445c1851c..48a6866b14a8 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -208,15 +208,21 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) *pb.BeaconState { return state } - // Find the last initiated exited epoch and add an exit delay to it. - var lastExitedEpoch uint64 + // Aggregate all the exit epochs from the last. + var exitedEpochs []uint64 for i := 0; i < len(state.ValidatorRegistry); i++ { if state.ValidatorRegistry[i].ExitEpoch != params.BeaconConfig().FarFutureEpoch { - lastExitedEpoch = state.ValidatorRegistry[i].ExitEpoch + exitedEpochs = append(exitedEpochs, state.ValidatorRegistry[i].ExitEpoch) } } - if helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state)) > lastExitedEpoch { - lastExitedEpoch = helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state)) + + // Find the highest exit epoch between the aggregated list and current epoch plus delay. + exitedEpochs = append(exitedEpochs, helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state))) + highestExitEpoch := exitedEpochs[0] + for _, e := range exitedEpochs { + if e > highestExitEpoch { + highestExitEpoch = e + } } // Find the total number of validators exiting same epoch as @@ -224,15 +230,15 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) *pb.BeaconState { // exit epoch to the next epoch. var currentExitQueueLength uint64 for i := 0; i < len(state.ValidatorRegistry); i++ { - if state.ValidatorRegistry[i].ExitEpoch == lastExitedEpoch { + if state.ValidatorRegistry[i].ExitEpoch == highestExitEpoch { currentExitQueueLength++ } } if currentExitQueueLength >= helpers.ChurnLimit(state) { - lastExitedEpoch++ + highestExitEpoch++ } - v.ExitEpoch = lastExitedEpoch + v.ExitEpoch = highestExitEpoch v.WithdrawableEpoch = v.ExitEpoch + params.BeaconConfig().MinValidatorWithdrawalDelay state.ValidatorRegistry[idx] = v diff --git a/beacon-chain/core/validators/validator_test.go b/beacon-chain/core/validators/validator_test.go index d599b9bacbb8..504822eb2600 100644 --- a/beacon-chain/core/validators/validator_test.go +++ b/beacon-chain/core/validators/validator_test.go @@ -398,6 +398,18 @@ func TestActivateValidator_OK(t *testing.T) { } } +func TestInitiateValidatorExit_AlreadyExited(t *testing.T) { + exitEpoch := uint64(199) + state := &pb.BeaconState{ValidatorRegistry: []*pb.Validator{{ + ExitEpoch: exitEpoch}, + }} + newState := InitiateValidatorExit(state, 0) + if newState.ValidatorRegistry[0].ExitEpoch != exitEpoch { + t.Errorf("Already exited, wanted exit epoch %d, got %d", + exitEpoch, newState.ValidatorRegistry[0].ExitEpoch) + } +} + func TestExitValidator_OK(t *testing.T) { state := &pb.BeaconState{ Slot: 100, // epoch 2 From 39bad8c427821c9f35f7af3924ed33b921912fe0 Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 23 Apr 2019 19:07:58 -0700 Subject: [PATCH 6/8] 100% test coverage, I win! --- beacon-chain/core/validators/validator.go | 1 + .../core/validators/validator_test.go | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 48a6866b14a8..8b7275a5e22d 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -234,6 +234,7 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) *pb.BeaconState { currentExitQueueLength++ } } + if currentExitQueueLength >= helpers.ChurnLimit(state) { highestExitEpoch++ } diff --git a/beacon-chain/core/validators/validator_test.go b/beacon-chain/core/validators/validator_test.go index 504822eb2600..8a63d5bdfc6b 100644 --- a/beacon-chain/core/validators/validator_test.go +++ b/beacon-chain/core/validators/validator_test.go @@ -410,6 +410,44 @@ func TestInitiateValidatorExit_AlreadyExited(t *testing.T) { } } +func TestInitiateValidatorExit_ProperExit(t *testing.T) { + exitedEpoch := uint64(100) + idx := uint64(3) + state := &pb.BeaconState{ValidatorRegistry: []*pb.Validator{ + {ExitEpoch: exitedEpoch}, + {ExitEpoch: exitedEpoch + 1}, + {ExitEpoch: exitedEpoch + 2}, + {ExitEpoch: params.BeaconConfig().FarFutureEpoch}, + }} + newState := InitiateValidatorExit(state, idx) + if newState.ValidatorRegistry[idx].ExitEpoch != exitedEpoch+2 { + t.Errorf("Exit epoch was not the highest, wanted exit epoch %d, got %d", + exitedEpoch+2, newState.ValidatorRegistry[idx].ExitEpoch) + } +} + +func TestInitiateValidatorExit_ChurnOverflow(t *testing.T) { + exitedEpoch := uint64(100) + idx := uint64(4) + state := &pb.BeaconState{ValidatorRegistry: []*pb.Validator{ + {ExitEpoch: exitedEpoch + 2}, + {ExitEpoch: exitedEpoch + 2}, + {ExitEpoch: exitedEpoch + 2}, + {ExitEpoch: exitedEpoch + 2}, //over flow here + {ExitEpoch: params.BeaconConfig().FarFutureEpoch}, + }} + newState := InitiateValidatorExit(state, idx) + + // Because of exit queue overflow, + // validator who init exited has to wait one more epoch. + wantedEpoch := state.ValidatorRegistry[0].ExitEpoch + 1 + + if newState.ValidatorRegistry[idx].ExitEpoch != wantedEpoch { + t.Errorf("Exit epoch did not cover overflow case, wanted exit epoch %d, got %d", + wantedEpoch, newState.ValidatorRegistry[idx].ExitEpoch) + } +} + func TestExitValidator_OK(t *testing.T) { state := &pb.BeaconState{ Slot: 100, // epoch 2 From f341fcbb9dfbc887c33ad7971a1f6cf2fc978702 Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Tue, 23 Apr 2019 19:16:44 -0700 Subject: [PATCH 7/8] fixed comment --- beacon-chain/chaintest/tests/state-tests/block-processing.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beacon-chain/chaintest/tests/state-tests/block-processing.yaml b/beacon-chain/chaintest/tests/state-tests/block-processing.yaml index 3641b75e176a..6c647819e3cd 100644 --- a/beacon-chain/chaintest/tests/state-tests/block-processing.yaml +++ b/beacon-chain/chaintest/tests/state-tests/block-processing.yaml @@ -52,7 +52,8 @@ test_cases: slashable_attestation_1_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51] slashable_attestation_2_custody_bitfield: !binary "F" slashable_attestation_2_validator_indices: [1, 2, 3, 4, 5, 6, 7, 51] - TODO(2307): Validator rotation logic is getting revamped piece by piece, we'll add the Exit test caes back when state transtion aligns spec v0.6. + # TODO(2307): Validator rotation logic is getting revamped piece by piece, + # we'll add the Exit test case back when state transition aligns spec v0.6. results: slot: 9223372036854775872 num_validators: 67 From 785dd7c3ed3ccfbad8f7c82f0428b48dda43fa6e Mon Sep 17 00:00:00 2001 From: Terence Tsao Date: Thu, 25 Apr 2019 13:21:42 -0700 Subject: [PATCH 8/8] track highest slot --- beacon-chain/core/validators/validator.go | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/beacon-chain/core/validators/validator.go b/beacon-chain/core/validators/validator.go index 987845d3f2d8..8baa39192813 100644 --- a/beacon-chain/core/validators/validator.go +++ b/beacon-chain/core/validators/validator.go @@ -208,20 +208,13 @@ func InitiateValidatorExit(state *pb.BeaconState, idx uint64) *pb.BeaconState { return state } - // Aggregate all the exit epochs from the last. - var exitedEpochs []uint64 + // Find the highest exit epoch among exited validators. + highestExitEpoch := helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state)) for i := 0; i < len(state.ValidatorRegistry); i++ { if state.ValidatorRegistry[i].ExitEpoch != params.BeaconConfig().FarFutureEpoch { - exitedEpochs = append(exitedEpochs, state.ValidatorRegistry[i].ExitEpoch) - } - } - - // Find the highest exit epoch between the aggregated list and current epoch plus delay. - exitedEpochs = append(exitedEpochs, helpers.DelayedActivationExitEpoch(helpers.CurrentEpoch(state))) - highestExitEpoch := exitedEpochs[0] - for _, e := range exitedEpochs { - if e > highestExitEpoch { - highestExitEpoch = e + if highestExitEpoch < state.ValidatorRegistry[i].ExitEpoch { + highestExitEpoch = state.ValidatorRegistry[i].ExitEpoch + } } }