Remove Already Exited Validators From Queue (#4695)

* added regression test

* fixed the regression test units

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Raul Jordan 2020-01-30 17:57:42 -08:00 committed by GitHub
parent 85a38e6053
commit f97ac5f0d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 1 deletions

View File

@ -540,7 +540,9 @@ func (bs *Server) GetValidatorQueue(
minEpoch := exitQueueEpoch + params.BeaconConfig().MinValidatorWithdrawabilityDelay
exitQueueIndices := make([]uint64, 0)
for _, valIdx := range awaitingExit {
if headState.Validators[valIdx].WithdrawableEpoch < minEpoch {
val := headState.Validators[valIdx]
// Ensure the validator has not yet exited before adding its index to the exit queue.
if val.WithdrawableEpoch < minEpoch && !validatorHasExited(val, helpers.CurrentEpoch(headState)) {
exitQueueIndices = append(exitQueueIndices, valIdx)
}
}
@ -611,3 +613,24 @@ func (bs *Server) GetValidatorPerformance(
TotalActiveValidators: activeCount,
}, nil
}
// Determines whether a validator has already exited.
func validatorHasExited(validator *ethpb.Validator, currentEpoch uint64) bool {
farFutureEpoch := params.BeaconConfig().FarFutureEpoch
if currentEpoch < validator.ActivationEligibilityEpoch {
return false
}
if currentEpoch < validator.ActivationEpoch {
return false
}
if validator.ExitEpoch == farFutureEpoch {
return false
}
if currentEpoch < validator.ExitEpoch {
if validator.Slashed {
return false
}
return false
}
return true
}

View File

@ -1222,6 +1222,67 @@ func TestServer_GetValidatorQueue_PendingActivation(t *testing.T) {
}
}
func TestServer_GetValidatorQueue_ExitedValidatorLeavesQueue(t *testing.T) {
headState := &pbp2p.BeaconState{
Validators: []*ethpb.Validator{
{
ActivationEpoch: 0,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
PublicKey: []byte("1"),
},
{
ActivationEpoch: 0,
ExitEpoch: 4,
WithdrawableEpoch: 6,
PublicKey: []byte("2"),
},
},
FinalizedCheckpoint: &ethpb.Checkpoint{
Epoch: 0,
},
}
bs := &Server{
HeadFetcher: &mock.ChainService{
State: headState,
},
}
// First we check if validator with index 1 is in the exit queue.
res, err := bs.GetValidatorQueue(context.Background(), &ptypes.Empty{})
if err != nil {
t.Fatal(err)
}
wanted := [][]byte{
[]byte("2"),
}
activeValidatorCount, err := helpers.ActiveValidatorCount(headState, helpers.CurrentEpoch(headState))
if err != nil {
t.Fatal(err)
}
wantChurn, err := helpers.ValidatorChurnLimit(activeValidatorCount)
if err != nil {
t.Fatal(err)
}
if res.ChurnLimit != wantChurn {
t.Errorf("Wanted churn %d, received %d", wantChurn, res.ChurnLimit)
}
if !reflect.DeepEqual(res.ExitPublicKeys, wanted) {
t.Errorf("Wanted %v, received %v", wanted, res.ExitPublicKeys)
}
// Now, we move the state.slot past the exit epoch of the validator, and now
// the validator should no longer exist in the queue.
headState.Slot = helpers.StartSlot(headState.Validators[1].ExitEpoch + 1)
res, err = bs.GetValidatorQueue(context.Background(), &ptypes.Empty{})
if err != nil {
t.Fatal(err)
}
if len(res.ExitPublicKeys) != 0 {
t.Errorf("Wanted empty exit queue, received %v", res.ExitPublicKeys)
}
}
func TestServer_GetValidatorQueue_PendingExit(t *testing.T) {
headState := &pbp2p.BeaconState{
Validators: []*ethpb.Validator{