diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index a557e7f5d..99cee4070 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -116,6 +116,25 @@ func createIncrementalAccounts(accNum int) []sdk.AccAddress { return addresses } +func AddTestAddrsFromPubKeys(app *SimApp, ctx sdk.Context, pubKeys []crypto.PubKey, accAmt sdk.Int) { + initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) + totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt.MulRaw(int64(len(pubKeys))))) + prevSupply := app.SupplyKeeper.GetSupply(ctx) + app.SupplyKeeper.SetSupply(ctx, supply.NewSupply(prevSupply.GetTotal().Add(totalSupply...))) + + // fill all the addresses with some coins, set the loose pool tokens simultaneously + for _, pubKey := range pubKeys { + acc := app.AccountKeeper.NewAccountWithAddress(ctx, sdk.AccAddress(pubKey.Address())) + app.AccountKeeper.SetAccount(ctx, acc) + + _, err := app.BankKeeper.AddCoins(ctx, sdk.AccAddress(pubKey.Address()), initCoins) + if err != nil { + panic(err) + } + } + return +} + // AddTestAddrs constructs and returns accNum amount of accounts with an // initial balance of accAmt in random order func AddTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sdk.AccAddress { diff --git a/x/slashing/keeper/keeper_test.go b/x/slashing/keeper/keeper_test.go index b1b3e5eea..ce28a5607 100644 --- a/x/slashing/keeper/keeper_test.go +++ b/x/slashing/keeper/keeper_test.go @@ -117,3 +117,125 @@ func TestHandleAlreadyJailed(t *testing.T) { validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, resultingTokens, validator.GetTokens()) } + +// Test a validator dipping in and out of the validator set +// Ensure that missed blocks are tracked correctly and that +// the start height of the signing info is reset correctly +func TestValidatorDippingInAndOut(t *testing.T) { + + // initial setup + // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + app.SlashingKeeper.SetParams(ctx, keeper.TestParams()) + + params := app.StakingKeeper.GetParams(ctx) + params.MaxValidators = 1 + app.StakingKeeper.SetParams(ctx, params) + power := int64(100) + + pks := simapp.CreateTestPubKeys(3) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + + amt := sdk.TokensFromConsensusPower(power) + addr, val := pks[0].Address(), pks[0] + consAddr := sdk.ConsAddress(addr) + sh := staking.NewHandler(app.StakingKeeper) + res, err := sh(ctx, keeper.NewTestMsgCreateValidator(sdk.ValAddress(addr), val, amt)) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, app.StakingKeeper) + + // 100 first blocks OK + height := int64(0) + for ; height < int64(100); height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) + } + + // kick first validator out of validator set + newAmt := sdk.TokensFromConsensusPower(101) + res, err = sh(ctx, keeper.NewTestMsgCreateValidator(sdk.ValAddress(pks[1].Address()), pks[1], newAmt)) + require.NoError(t, err) + require.NotNil(t, res) + + validatorUpdates := staking.EndBlocker(ctx, app.StakingKeeper) + require.Equal(t, 2, len(validatorUpdates)) + validator, _ := app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addr)) + require.Equal(t, sdk.Unbonding, validator.Status) + + // 600 more blocks happened + height = int64(700) + ctx = ctx.WithBlockHeight(height) + + // validator added back in + delTokens := sdk.TokensFromConsensusPower(50) + res, err = sh(ctx, keeper.NewTestMsgDelegate(sdk.AccAddress(pks[2].Address()), sdk.ValAddress(pks[0].Address()), delTokens)) + require.NoError(t, err) + require.NotNil(t, res) + + validatorUpdates = staking.EndBlocker(ctx, app.StakingKeeper) + require.Equal(t, 2, len(validatorUpdates)) + validator, _ = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addr)) + require.Equal(t, sdk.Bonded, validator.Status) + newPower := int64(150) + + // validator misses a block + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + height++ + + // shouldn't be jailed/kicked yet + validator, _ = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addr)) + require.Equal(t, sdk.Bonded, validator.Status) + + // validator misses 500 more blocks, 501 total + latest := height + for ; height < latest+500; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + } + + // should now be jailed & kicked + staking.EndBlocker(ctx, app.StakingKeeper) + validator, _ = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addr)) + require.Equal(t, sdk.Unbonding, validator.Status) + + // check all the signing information + signInfo, found := app.SlashingKeeper.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 < app.SlashingKeeper.SignedBlocksWindow(ctx); offset++ { + missed := app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, consAddr, offset) + require.False(t, missed) + } + + // some blocks pass + height = int64(5000) + ctx = ctx.WithBlockHeight(height) + + // validator rejoins and starts signing again + app.StakingKeeper.Unjail(ctx, consAddr) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) + height++ + + // validator should not be kicked since we reset counter/array when it was jailed + staking.EndBlocker(ctx, app.StakingKeeper) + validator, _ = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addr)) + require.Equal(t, sdk.Bonded, validator.Status) + + // validator misses 501 blocks + latest = height + for ; height < latest+501; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) + } + + // validator should now be jailed & kicked + staking.EndBlocker(ctx, app.StakingKeeper) + validator, _ = app.StakingKeeper.GetValidator(ctx, sdk.ValAddress(addr)) + require.Equal(t, sdk.Unbonding, validator.Status) + +} diff --git a/x/slashing/keeper/old_keeper_test.go b/x/slashing/keeper/old_keeper_test.go index 6deb48393..b55569d4a 100644 --- a/x/slashing/keeper/old_keeper_test.go +++ b/x/slashing/keeper/old_keeper_test.go @@ -1,124 +1 @@ package keeper - -import ( - "github.com/stretchr/testify/require" - "testing" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -// Test a validator dipping in and out of the validator set -// Ensure that missed blocks are tracked correctly and that -// the start height of the signing info is reset correctly -func TestValidatorDippingInAndOut(t *testing.T) { - - // initial setup - // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 - ctx, _, sk, _, keeper := CreateTestInput(t, TestParams()) - params := sk.GetParams(ctx) - params.MaxValidators = 1 - sk.SetParams(ctx, params) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := Addrs[0], Pks[0] - consAddr := sdk.ConsAddress(addr) - sh := staking.NewHandler(sk) - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - // 100 first blocks OK - height := int64(0) - for ; height < int64(100); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - - // kick first validator out of validator set - newAmt := sdk.TokensFromConsensusPower(101) - res, err = sh(ctx, NewTestMsgCreateValidator(Addrs[1], Pks[1], newAmt)) - require.NoError(t, err) - require.NotNil(t, res) - - validatorUpdates := staking.EndBlocker(ctx, sk) - require.Equal(t, 2, len(validatorUpdates)) - validator, _ := sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Unbonding, validator.Status) - - // 600 more blocks happened - height = int64(700) - ctx = ctx.WithBlockHeight(height) - - // validator added back in - delTokens := sdk.TokensFromConsensusPower(50) - res, err = sh(ctx, NewTestMsgDelegate(sdk.AccAddress(Addrs[2]), Addrs[0], delTokens)) - require.NoError(t, err) - require.NotNil(t, res) - - validatorUpdates = staking.EndBlocker(ctx, sk) - require.Equal(t, 2, len(validatorUpdates)) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Bonded, validator.Status) - newPower := int64(150) - - // validator misses a block - keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) - height++ - - // shouldn't be jailed/kicked yet - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Bonded, validator.Status) - - // validator misses 500 more blocks, 501 total - latest := height - for ; height < latest+500; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) - } - - // should now be jailed & kicked - staking.EndBlocker(ctx, sk) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Unbonding, validator.Status) - - // check all the signing information - 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(), newPower, true) - height++ - - // validator should not be kicked since we reset counter/array when it was jailed - staking.EndBlocker(ctx, sk) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Bonded, validator.Status) - - // validator misses 501 blocks - latest = height - for ; height < latest+501; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) - } - - // validator should now be jailed & kicked - staking.EndBlocker(ctx, sk) - validator, _ = sk.GetValidator(ctx, addr) - require.Equal(t, sdk.Unbonding, validator.Status) - -}