diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index 3a5ddd6fdc..3710e4072d 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -9,9 +9,7 @@ import ( func (k Keeper) onValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { // Update the signing info start height or create a new signing info signingInfo, found := k.getValidatorSigningInfo(ctx, address) - if found { - signingInfo.StartHeight = ctx.BlockHeight() - } else { + if !found { signingInfo = ValidatorSigningInfo{ StartHeight: ctx.BlockHeight(), IndexOffset: 0, diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 68cbd90b04..73734198d2 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -144,6 +144,10 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p k.validatorSet.Slash(ctx, consAddr, distributionHeight, power, k.SlashFractionDowntime(ctx)) k.validatorSet.Jail(ctx, consAddr) signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx)) + // We need to reset the counter & array so that the validator won't be immediately slashed for downtime upon rebonding. + signInfo.MissedBlocksCounter = 0 + signInfo.IndexOffset = 0 + k.clearValidatorMissedBlockBitArray(ctx, consAddr) } else { // Validator was (a) not found or (b) already jailed, don't slash logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed", diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 52defda8d1..4299bb7ccc 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -380,6 +380,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { sk.SetParams(ctx, params) amtInt := int64(100) addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) + consAddr := sdk.ConsAddress(addr) sh := stake.NewHandler(sk) got := sh(ctx, newTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) @@ -425,31 +426,48 @@ func TestValidatorDippingInAndOut(t *testing.T) { require.Equal(t, sdk.Bonded, validator.Status) // validator misses 501 blocks - for ; height < int64(1203); height++ { + for ; height < int64(1201); height++ { ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false) } - // should not yet be jailed & kicked + // should now be jailed & kicked + stake.EndBlocker(ctx, sk) + validator, _ = sk.GetValidator(ctx, addr) + require.Equal(t, sdk.Unbonding, validator.Status) + + signInfo, found := keeper.getValidatorSigningInfo(ctx, consAddr) + require.True(t, found) + require.Equal(t, int64(0), signInfo.MissedBlocksCounter) + require.Equal(t, int64(0), signInfo.IndexOffset) + // array should be cleared + for offset := int64(0); offset < keeper.SignedBlocksWindow(ctx); offset++ { + missed := keeper.getValidatorMissedBlockBitArray(ctx, consAddr, offset) + require.False(t, missed) + } + + // some blocks pass + height = int64(5000) + ctx = ctx.WithBlockHeight(height) + + // validator rejoins and starts signing again + sk.Unjail(ctx, consAddr) + keeper.handleValidatorSignature(ctx, val.Address(), newAmt, true) + + // validator should not be kicked stake.EndBlocker(ctx, sk) validator, _ = sk.GetValidator(ctx, addr) require.Equal(t, sdk.Bonded, validator.Status) - // validator signs 500 blocks - for ; height < int64(1702); height++ { + // validator misses 501 blocks + for height = int64(5001); height < int64(5503); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), newAmt, true) + keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false) } - // should have exceeded threshold - signingInfo, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(700), signingInfo.StartHeight) - require.Equal(t, int64(1102), signingInfo.IndexOffset) - require.Equal(t, int64(501), signingInfo.MissedBlocksCounter) - - // should be jailed & kicked + // validator should now be jailed & kicked stake.EndBlocker(ctx, sk) validator, _ = sk.GetValidator(ctx, addr) require.Equal(t, sdk.Unbonding, validator.Status) + }