diff --git a/x/stake/handler.go b/x/stake/handler.go index 1d095f3f4d80..818558a5bd87 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -34,8 +34,16 @@ func NewHandler(k keeper.Keeper) sdk.Handler { func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) { endBlockerTags := sdk.EmptyTags() + // reset the intra-transaction counter + k.SetIntraTxCounter(ctx, 0) + + // calculate validator set changes + ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx) + + // Unbond all mature validators from the unbonding queue k.UnbondAllMatureValidatorQueue(ctx) + // Remove all mature unbonding delegations from the ubd queue matureUnbonds := k.DequeueAllMatureUnbondingQueue(ctx, ctx.BlockHeader().Time) for _, dvPair := range matureUnbonds { err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddr, dvPair.ValidatorAddr) @@ -49,6 +57,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid )) } + // Remove all mature redelegations from the red queue matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time) for _, dvvTriplet := range matureRedelegations { err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr, dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr) @@ -63,11 +72,6 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid )) } - // reset the intra-transaction counter - k.SetIntraTxCounter(ctx, 0) - - // calculate validator set changes - ValidatorUpdates = k.ApplyAndReturnValidatorSetUpdates(ctx) return } diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index c4a558b91609..e8d22572765c 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -495,14 +495,15 @@ func TestMultipleMsgCreateValidator(t *testing.T) { require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime) + + // Jump to finishTime for unbonding period and remove from Unbonding Queue ctx = ctx.WithBlockTime(finishTime) EndBlocker(ctx, keeper) - //Check that the account is unbonded + // Check that the validator is deleted from state validators := keeper.GetValidators(ctx, 100) require.Equal(t, len(validatorAddrs)-(i+1), len(validators), "expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators)) - _, found = keeper.GetValidator(ctx, validatorAddr) require.False(t, found) @@ -1013,7 +1014,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { EndBlocker(ctx, keeper) // validator power should have been reduced to zero - // ergo validator should have been removed from the store - _, found = keeper.GetValidator(ctx, valA) - require.False(t, found) + // validator should be in unbonding state + validator, _ = keeper.GetValidator(ctx, valA) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 5879428f2bec..0d97086c682e 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -108,12 +108,6 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn) k.SetPool(ctx, pool) - // remove validator if it has no more tokens - if validator.DelegatorShares.IsZero() && validator.Status == sdk.Unbonded { - // if not unbonded, we must instead remove validator in EndBlocker once it finishes its unbonding period - k.RemoveValidator(ctx, validator.OperatorAddr) - } - // Log that a slash occurred! logger.Info(fmt.Sprintf( "validator %s slashed by slash factor of %s; burned %v tokens", diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index aab97b811ced..9c23576c34ca 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -348,9 +348,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator // power decreased by 1 again, validator is out of stake - // ergo validator should have been removed from the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // tests Slash at a previous height with a redelegation @@ -450,16 +450,16 @@ func TestSlashWithRedelegation(t *testing.T) { // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator - // validator decreased to zero power, should have been removed from the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator decreased to zero power, should be in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) // slash the validator again, by 100% // no stake remains to be slashed ctx = ctx.WithBlockHeight(12) - // validator no longer in the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // validator still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) // read updating redelegation @@ -472,9 +472,9 @@ func TestSlashWithRedelegation(t *testing.T) { // no more bonded tokens burned require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - // power still zero, still not in the store - _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) - require.False(t, found) + // power still zero, still in unbonding period + validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // tests Slash at a previous height with both an unbonding delegation and a redelegation diff --git a/x/stake/keeper/val_state_change.go b/x/stake/keeper/val_state_change.go index ea057c0fddcf..d6065ae5c209 100644 --- a/x/stake/keeper/val_state_change.go +++ b/x/stake/keeper/val_state_change.go @@ -108,11 +108,6 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // bonded to unbonding k.bondedToUnbonding(ctx, validator) - // remove validator if it has no more tokens - if validator.Tokens.IsZero() { - k.RemoveValidator(ctx, validator.OperatorAddr) - } - // delete from the bonded validator index k.DeleteLastValidatorPower(ctx, sdk.ValAddress(valAddrBytes)) diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 7acf1cc02dae..0f2b8b221114 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -186,9 +186,9 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec()) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) - // validator should have been deleted - _, found := keeper.GetValidator(ctx, addrVals[0]) - require.False(t, found) + // validator should have be unbonding + validator, _ = keeper.GetValidator(ctx, addrVals[0]) + require.Equal(t, validator.GetStatus(), sdk.Unbonding) } // This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator