From a016524468000c4eb9696ff6f69314b987e6d1dc Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Mon, 2 Mar 2020 21:19:46 +0100 Subject: [PATCH 01/13] migrate TestHandleNewValidator to new simapp --- x/slashing/keeper/common_test.go | 7 + x/slashing/keeper/keeper_test.go | 216 ++++---------------------- x/slashing/keeper/old_keeper_test.go | 223 +++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 187 deletions(-) create mode 100644 x/slashing/keeper/common_test.go create mode 100644 x/slashing/keeper/old_keeper_test.go diff --git a/x/slashing/keeper/common_test.go b/x/slashing/keeper/common_test.go new file mode 100644 index 000000000..940d995ca --- /dev/null +++ b/x/slashing/keeper/common_test.go @@ -0,0 +1,7 @@ +package keeper_test + +import sdk "github.com/cosmos/cosmos-sdk/types" + +var ( + InitTokens = sdk.TokensFromConsensusPower(200) +) diff --git a/x/slashing/keeper/keeper_test.go b/x/slashing/keeper/keeper_test.go index 4f4f555bb..785682995 100644 --- a/x/slashing/keeper/keeper_test.go +++ b/x/slashing/keeper/keeper_test.go @@ -1,13 +1,16 @@ -package keeper +package keeper_test import ( "testing" "time" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/cosmos/cosmos-sdk/x/staking" ) @@ -15,209 +18,48 @@ import ( // Ensure that SigningInfo.StartHeight is set correctly // and that they are not immediately jailed func TestHandleNewValidator(t *testing.T) { - // initial setup - ctx, bk, sk, _, keeper := CreateTestInput(t, TestParams()) - addr, val := Addrs[0], Pks[0] - amt := sdk.TokensFromConsensusPower(100) - sh := staking.NewHandler(sk) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) - // 1000 first blocks not a validator - ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) + pks := simapp.CreateTestPubKeys(1) + + addr, val := valAddrs[0], pks[0] + amt := sdk.TokensFromConsensusPower(100) + sh := staking.NewHandler(app.StakingKeeper) + + ctx = ctx.WithBlockHeight(app.SlashingKeeper.SignedBlocksWindow(ctx) + 1) // Validator created - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) + res, err := sh(ctx, keeper.NewTestMsgCreateValidator(addr, val, amt)) require.NoError(t, err) require.NotNil(t, res) - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) // Now a validator, for two blocks - keeper.HandleValidatorSignature(ctx, val.Address(), 100, true) - ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2) - keeper.HandleValidatorSignature(ctx, val.Address(), 100, false) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, true) + ctx = ctx.WithBlockHeight(app.SlashingKeeper.SignedBlocksWindow(ctx) + 2) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, false) - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) - require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight) + require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)+1, info.StartHeight) require.Equal(t, int64(2), info.IndexOffset) require.Equal(t, int64(1), info.MissedBlocksCounter) require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) // validator should be bonded still, should not have been jailed or slashed - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Bonded, validator.GetStatus()) - bondPool := sk.GetBondedPool(ctx) + bondPool := app.StakingKeeper.GetBondedPool(ctx) expTokens := sdk.TokensFromConsensusPower(100) - require.Equal(t, expTokens.Int64(), bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount.Int64()) -} - -// Test a jailed validator being "down" twice -// Ensure that they're only slashed once -func TestHandleAlreadyJailed(t *testing.T) { - - // initial setup - ctx, _, sk, _, keeper := CreateTestInput(t, types.DefaultParams()) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := Addrs[0], Pks[0] - sh := staking.NewHandler(sk) - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - // 1000 first blocks OK - height := int64(0) - for ; height < keeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - - // 501 blocks missed - for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, sk) - - // validator should have been jailed and slashed - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) - - // validator should have been slashed - resultingTokens := amt.Sub(sdk.TokensFromConsensusPower(1)) - require.Equal(t, resultingTokens, validator.GetTokens()) - - // another block missed - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - - // validator should not have been slashed twice - validator, _ = sk.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 - 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) - + require.Equal(t, expTokens.Int64(), app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount.Int64()) } diff --git a/x/slashing/keeper/old_keeper_test.go b/x/slashing/keeper/old_keeper_test.go new file mode 100644 index 000000000..4f4f555bb --- /dev/null +++ b/x/slashing/keeper/old_keeper_test.go @@ -0,0 +1,223 @@ +package keeper + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +// Test a new validator entering the validator set +// Ensure that SigningInfo.StartHeight is set correctly +// and that they are not immediately jailed +func TestHandleNewValidator(t *testing.T) { + // initial setup + ctx, bk, sk, _, keeper := CreateTestInput(t, TestParams()) + addr, val := Addrs[0], Pks[0] + amt := sdk.TokensFromConsensusPower(100) + sh := staking.NewHandler(sk) + + // 1000 first blocks not a validator + ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) + + // Validator created + res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, sk) + + require.Equal( + t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), + ) + require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + + // Now a validator, for two blocks + keeper.HandleValidatorSignature(ctx, val.Address(), 100, true) + ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2) + keeper.HandleValidatorSignature(ctx, val.Address(), 100, false) + + info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight) + require.Equal(t, int64(2), info.IndexOffset) + require.Equal(t, int64(1), info.MissedBlocksCounter) + require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) + + // validator should be bonded still, should not have been jailed or slashed + validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + bondPool := sk.GetBondedPool(ctx) + expTokens := sdk.TokensFromConsensusPower(100) + require.Equal(t, expTokens.Int64(), bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount.Int64()) +} + +// Test a jailed validator being "down" twice +// Ensure that they're only slashed once +func TestHandleAlreadyJailed(t *testing.T) { + + // initial setup + ctx, _, sk, _, keeper := CreateTestInput(t, types.DefaultParams()) + power := int64(100) + amt := sdk.TokensFromConsensusPower(power) + addr, val := Addrs[0], Pks[0] + sh := staking.NewHandler(sk) + res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, sk) + + // 1000 first blocks OK + height := int64(0) + for ; height < keeper.SignedBlocksWindow(ctx); height++ { + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, true) + } + + // 501 blocks missed + for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ { + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + + // end block + staking.EndBlocker(ctx, sk) + + // validator should have been jailed and slashed + validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Unbonding, validator.GetStatus()) + + // validator should have been slashed + resultingTokens := amt.Sub(sdk.TokensFromConsensusPower(1)) + require.Equal(t, resultingTokens, validator.GetTokens()) + + // another block missed + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + + // validator should not have been slashed twice + validator, _ = sk.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 + 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) + +} From 2706854efffcbce5180871a78240c77ddae80ca4 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Mon, 2 Mar 2020 22:54:38 +0100 Subject: [PATCH 02/13] refactor TestHandleAlreadyJailed to simapp --- x/slashing/keeper/keeper_test.go | 54 ++++++++++++++ x/slashing/keeper/old_keeper_test.go | 101 +-------------------------- 2 files changed, 55 insertions(+), 100 deletions(-) diff --git a/x/slashing/keeper/keeper_test.go b/x/slashing/keeper/keeper_test.go index 785682995..b1b3e5eea 100644 --- a/x/slashing/keeper/keeper_test.go +++ b/x/slashing/keeper/keeper_test.go @@ -63,3 +63,57 @@ func TestHandleNewValidator(t *testing.T) { expTokens := sdk.TokensFromConsensusPower(100) require.Equal(t, expTokens.Int64(), app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount.Int64()) } + +// Test a jailed validator being "down" twice +// Ensure that they're only slashed once +func TestHandleAlreadyJailed(t *testing.T) { + // initial setup + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + power := int64(100) + + amt := sdk.TokensFromConsensusPower(power) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) + pks := simapp.CreateTestPubKeys(1) + + addr, val := valAddrs[0], pks[0] + sh := staking.NewHandler(app.StakingKeeper) + res, err := sh(ctx, keeper.NewTestMsgCreateValidator(addr, val, amt)) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, app.StakingKeeper) + + // 1000 first blocks OK + height := int64(0) + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) + } + + // 501 blocks missed + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx))+1; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + // validator should have been jailed and slashed + validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Unbonding, validator.GetStatus()) + + // validator should have been slashed + resultingTokens := amt.Sub(sdk.TokensFromConsensusPower(1)) + require.Equal(t, resultingTokens, validator.GetTokens()) + + // another block missed + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + + // validator should not have been slashed twice + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, resultingTokens, validator.GetTokens()) +} diff --git a/x/slashing/keeper/old_keeper_test.go b/x/slashing/keeper/old_keeper_test.go index 4f4f555bb..6deb48393 100644 --- a/x/slashing/keeper/old_keeper_test.go +++ b/x/slashing/keeper/old_keeper_test.go @@ -1,112 +1,13 @@ package keeper import ( - "testing" - "time" - "github.com/stretchr/testify/require" + "testing" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" ) -// Test a new validator entering the validator set -// Ensure that SigningInfo.StartHeight is set correctly -// and that they are not immediately jailed -func TestHandleNewValidator(t *testing.T) { - // initial setup - ctx, bk, sk, _, keeper := CreateTestInput(t, TestParams()) - addr, val := Addrs[0], Pks[0] - amt := sdk.TokensFromConsensusPower(100) - sh := staking.NewHandler(sk) - - // 1000 first blocks not a validator - ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) - - // Validator created - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), - ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) - - // Now a validator, for two blocks - keeper.HandleValidatorSignature(ctx, val.Address(), 100, true) - ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2) - keeper.HandleValidatorSignature(ctx, val.Address(), 100, false) - - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight) - require.Equal(t, int64(2), info.IndexOffset) - require.Equal(t, int64(1), info.MissedBlocksCounter) - require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - - // validator should be bonded still, should not have been jailed or slashed - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - bondPool := sk.GetBondedPool(ctx) - expTokens := sdk.TokensFromConsensusPower(100) - require.Equal(t, expTokens.Int64(), bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount.Int64()) -} - -// Test a jailed validator being "down" twice -// Ensure that they're only slashed once -func TestHandleAlreadyJailed(t *testing.T) { - - // initial setup - ctx, _, sk, _, keeper := CreateTestInput(t, types.DefaultParams()) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := Addrs[0], Pks[0] - sh := staking.NewHandler(sk) - res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - // 1000 first blocks OK - height := int64(0) - for ; height < keeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - - // 501 blocks missed - for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, sk) - - // validator should have been jailed and slashed - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) - - // validator should have been slashed - resultingTokens := amt.Sub(sdk.TokensFromConsensusPower(1)) - require.Equal(t, resultingTokens, validator.GetTokens()) - - // another block missed - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - - // validator should not have been slashed twice - validator, _ = sk.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 From 863dc1484e1fdee09f57f30d946236ed1630c5e9 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 10:51:06 +0100 Subject: [PATCH 03/13] make test pass --- simapp/test_helpers.go | 19 +++++ x/slashing/keeper/keeper_test.go | 122 ++++++++++++++++++++++++++ x/slashing/keeper/old_keeper_test.go | 123 --------------------------- 3 files changed, 141 insertions(+), 123 deletions(-) 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) - -} From 0a5f64b64f083447e757a2cea104897b3ba4897f Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 11:05:20 +0100 Subject: [PATCH 04/13] clean some code --- simapp/test_helpers.go | 45 +++++++++++++++------------- x/slashing/keeper/old_keeper_test.go | 1 - 2 files changed, 24 insertions(+), 22 deletions(-) delete mode 100644 x/slashing/keeper/old_keeper_test.go diff --git a/simapp/test_helpers.go b/simapp/test_helpers.go index 99cee4070..300235fe1 100644 --- a/simapp/test_helpers.go +++ b/simapp/test_helpers.go @@ -116,23 +116,23 @@ func createIncrementalAccounts(accNum int) []sdk.AccAddress { return addresses } +// AddTestAddrsFromPubKeys adds the addresses into the SimApp providing only the public keys. 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...))) + + setTotalSupply(app, ctx, accAmt, len(pubKeys)) // 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) - } + saveAccount(app, ctx, sdk.AccAddress(pubKey.Address()), initCoins) } - return +} + +// setTotalSupply provides the total supply based on accAmt * totalAccounts. +func setTotalSupply(app *SimApp, ctx sdk.Context, accAmt sdk.Int, totalAccounts int) { + totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt.MulRaw(int64(totalAccounts)))) + prevSupply := app.SupplyKeeper.GetSupply(ctx) + app.SupplyKeeper.SetSupply(ctx, supply.NewSupply(prevSupply.GetTotal().Add(totalSupply...))) } // AddTestAddrs constructs and returns accNum amount of accounts with an @@ -151,23 +151,26 @@ func addTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int, stra testAddrs := strategy(accNum) initCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt)) - totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), accAmt.MulRaw(int64(len(testAddrs))))) - prevSupply := app.SupplyKeeper.GetSupply(ctx) - app.SupplyKeeper.SetSupply(ctx, supply.NewSupply(prevSupply.GetTotal().Add(totalSupply...))) + setTotalSupply(app, ctx, accAmt, accNum) // fill all the addresses with some coins, set the loose pool tokens simultaneously for _, addr := range testAddrs { - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - app.AccountKeeper.SetAccount(ctx, acc) - - _, err := app.BankKeeper.AddCoins(ctx, addr, initCoins) - if err != nil { - panic(err) - } + saveAccount(app, ctx, addr, initCoins) } + return testAddrs } +// saveAccount saves the provided account into the simapp with balance based on initCoins. +func saveAccount(app *SimApp, ctx sdk.Context, addr sdk.AccAddress, initCoins sdk.Coins) { + acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) + app.AccountKeeper.SetAccount(ctx, acc) + _, err := app.BankKeeper.AddCoins(ctx, addr, initCoins) + if err != nil { + panic(err) + } +} + // ConvertAddrsToValAddrs converts the provided addresses to ValAddress. func ConvertAddrsToValAddrs(addrs []sdk.AccAddress) []sdk.ValAddress { valAddrs := make([]sdk.ValAddress, len(addrs)) diff --git a/x/slashing/keeper/old_keeper_test.go b/x/slashing/keeper/old_keeper_test.go deleted file mode 100644 index b55569d4a..000000000 --- a/x/slashing/keeper/old_keeper_test.go +++ /dev/null @@ -1 +0,0 @@ -package keeper From 416f0147f707e5a90b9ac11d570465cda4368581 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 11:14:22 +0100 Subject: [PATCH 05/13] refactor querier test --- x/slashing/keeper/querier_test.go | 35 ++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/x/slashing/keeper/querier_test.go b/x/slashing/keeper/querier_test.go index fd924bc8e..5413ba599 100644 --- a/x/slashing/keeper/querier_test.go +++ b/x/slashing/keeper/querier_test.go @@ -1,38 +1,53 @@ -package keeper +package keeper_test import ( "testing" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) func TestNewQuerier(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, TestParams()) - querier := NewQuerier(keeper) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + app.SlashingKeeper.SetParams(ctx, keeper.TestParams()) + + querier := keeper.NewQuerier(app.SlashingKeeper) query := abci.RequestQuery{ Path: "", Data: []byte{}, } - _, err := querier(ctx, []string{"parameters"}, query) + _, err := querier(ctx, []string{types.QueryParameters}, query) require.NoError(t, err) } func TestQueryParams(t *testing.T) { cdc := codec.New() - ctx, _, _, _, keeper := CreateTestInput(t, TestParams()) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + app.SlashingKeeper.SetParams(ctx, keeper.TestParams()) + + querier := keeper.NewQuerier(app.SlashingKeeper) + + query := abci.RequestQuery{ + Path: "", + Data: []byte{}, + } var params types.Params - res, errRes := queryParams(ctx, keeper) - require.NoError(t, errRes) - - err := cdc.UnmarshalJSON(res, ¶ms) + res, err := querier(ctx, []string{types.QueryParameters}, query) require.NoError(t, err) - require.Equal(t, keeper.GetParams(ctx), params) + + err = cdc.UnmarshalJSON(res, ¶ms) + require.NoError(t, err) + require.Equal(t, app.SlashingKeeper.GetParams(ctx), params) } From c2cc84950b5f617a9c6f6167ba9d801718814894 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 11:16:31 +0100 Subject: [PATCH 06/13] refactor TestGetSetValidatorSigningInfo test --- x/slashing/keeper/old_signing_info_test.go | 61 +++++++++++++++++++ x/slashing/keeper/signing_info_test.go | 68 ++++------------------ 2 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 x/slashing/keeper/old_signing_info_test.go diff --git a/x/slashing/keeper/old_signing_info_test.go b/x/slashing/keeper/old_signing_info_test.go new file mode 100644 index 000000000..1e4347b22 --- /dev/null +++ b/x/slashing/keeper/old_signing_info_test.go @@ -0,0 +1,61 @@ +package keeper + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing/types" +) + +func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { + ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) + missed := keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) + require.False(t, missed) // treat empty key as not missed + keeper.SetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0, true) + missed = keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) + require.True(t, missed) // now should be missed +} + +func TestTombstoned(t *testing.T) { + ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) + require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) + require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) + + newInfo := types.NewValidatorSigningInfo( + sdk.ConsAddress(Addrs[0]), + int64(4), + int64(3), + time.Unix(2, 0), + false, + int64(10), + ) + keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) + + require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) + keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) + require.True(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) + require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) +} + +func TestJailUntil(t *testing.T) { + ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) + require.Panics(t, func() { keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Now()) }) + + newInfo := types.NewValidatorSigningInfo( + sdk.ConsAddress(Addrs[0]), + int64(4), + int64(3), + time.Unix(2, 0), + false, + int64(10), + ) + keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) + keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Unix(253402300799, 0).UTC()) + + info, ok := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) + require.True(t, ok) + require.Equal(t, time.Unix(253402300799, 0).UTC(), info.JailedUntil) +} diff --git a/x/slashing/keeper/signing_info_test.go b/x/slashing/keeper/signing_info_test.go index ac8c50752..c58f4f900 100644 --- a/x/slashing/keeper/signing_info_test.go +++ b/x/slashing/keeper/signing_info_test.go @@ -1,4 +1,4 @@ -package keeper +package keeper_test import ( "testing" @@ -6,77 +6,33 @@ import ( "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing/types" ) func TestGetSetValidatorSigningInfo(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) require.False(t, found) newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), + sdk.ConsAddress(addrDels[0]), int64(4), int64(3), time.Unix(2, 0), false, int64(10), ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), newInfo) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) require.True(t, found) require.Equal(t, info.StartHeight, int64(4)) require.Equal(t, info.IndexOffset, int64(3)) require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC()) require.Equal(t, info.MissedBlocksCounter, int64(10)) } - -func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - missed := keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) - require.False(t, missed) // treat empty key as not missed - keeper.SetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0, true) - missed = keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) - require.True(t, missed) // now should be missed -} - -func TestTombstoned(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) - require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - - newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), - int64(4), - int64(3), - time.Unix(2, 0), - false, - int64(10), - ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - - require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) - require.True(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) -} - -func TestJailUntil(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - require.Panics(t, func() { keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Now()) }) - - newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), - int64(4), - int64(3), - time.Unix(2, 0), - false, - int64(10), - ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Unix(253402300799, 0).UTC()) - - info, ok := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) - require.True(t, ok) - require.Equal(t, time.Unix(253402300799, 0).UTC(), info.JailedUntil) -} From 2b95d5317e3a244af7ffcd57e80be190fb438edf Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 11:20:18 +0100 Subject: [PATCH 07/13] refactor signing info test --- x/slashing/keeper/old_signing_info_test.go | 61 ---------------------- x/slashing/keeper/signing_info_test.go | 59 +++++++++++++++++++++ 2 files changed, 59 insertions(+), 61 deletions(-) delete mode 100644 x/slashing/keeper/old_signing_info_test.go diff --git a/x/slashing/keeper/old_signing_info_test.go b/x/slashing/keeper/old_signing_info_test.go deleted file mode 100644 index 1e4347b22..000000000 --- a/x/slashing/keeper/old_signing_info_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package keeper - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing/types" -) - -func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - missed := keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) - require.False(t, missed) // treat empty key as not missed - keeper.SetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0, true) - missed = keeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(Addrs[0]), 0) - require.True(t, missed) // now should be missed -} - -func TestTombstoned(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) - require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - - newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), - int64(4), - int64(3), - time.Unix(2, 0), - false, - int64(10), - ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - - require.False(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) - require.True(t, keeper.IsTombstoned(ctx, sdk.ConsAddress(Addrs[0]))) - require.Panics(t, func() { keeper.Tombstone(ctx, sdk.ConsAddress(Addrs[0])) }) -} - -func TestJailUntil(t *testing.T) { - ctx, _, _, _, keeper := CreateTestInput(t, types.DefaultParams()) - require.Panics(t, func() { keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Now()) }) - - newInfo := types.NewValidatorSigningInfo( - sdk.ConsAddress(Addrs[0]), - int64(4), - int64(3), - time.Unix(2, 0), - false, - int64(10), - ) - keeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0]), newInfo) - keeper.JailUntil(ctx, sdk.ConsAddress(Addrs[0]), time.Unix(253402300799, 0).UTC()) - - info, ok := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(Addrs[0])) - require.True(t, ok) - require.Equal(t, time.Unix(253402300799, 0).UTC(), info.JailedUntil) -} diff --git a/x/slashing/keeper/signing_info_test.go b/x/slashing/keeper/signing_info_test.go index c58f4f900..e10df926c 100644 --- a/x/slashing/keeper/signing_info_test.go +++ b/x/slashing/keeper/signing_info_test.go @@ -36,3 +36,62 @@ func TestGetSetValidatorSigningInfo(t *testing.T) { require.Equal(t, info.JailedUntil, time.Unix(2, 0).UTC()) require.Equal(t, info.MissedBlocksCounter, int64(10)) } + +func TestGetSetValidatorMissedBlockBitArray(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + missed := app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrDels[0]), 0) + require.False(t, missed) // treat empty key as not missed + app.SlashingKeeper.SetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrDels[0]), 0, true) + missed = app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, sdk.ConsAddress(addrDels[0]), 0) + require.True(t, missed) // now should be missed +} + +func TestTombstoned(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + require.Panics(t, func() { app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[0])) }) + require.False(t, app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0]))) + + newInfo := types.NewValidatorSigningInfo( + sdk.ConsAddress(addrDels[0]), + int64(4), + int64(3), + time.Unix(2, 0), + false, + int64(10), + ) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), newInfo) + + require.False(t, app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0]))) + app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[0])) + require.True(t, app.SlashingKeeper.IsTombstoned(ctx, sdk.ConsAddress(addrDels[0]))) + require.Panics(t, func() { app.SlashingKeeper.Tombstone(ctx, sdk.ConsAddress(addrDels[0])) }) +} + +func TestJailUntil(t *testing.T) { + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, sdk.TokensFromConsensusPower(200)) + + require.Panics(t, func() { app.SlashingKeeper.JailUntil(ctx, sdk.ConsAddress(addrDels[0]), time.Now()) }) + + newInfo := types.NewValidatorSigningInfo( + sdk.ConsAddress(addrDels[0]), + int64(4), + int64(3), + time.Unix(2, 0), + false, + int64(10), + ) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0]), newInfo) + app.SlashingKeeper.JailUntil(ctx, sdk.ConsAddress(addrDels[0]), time.Unix(253402300799, 0).UTC()) + + info, ok := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(addrDels[0])) + require.True(t, ok) + require.Equal(t, time.Unix(253402300799, 0).UTC(), info.JailedUntil) +} From 24d25fa207841bd87e786a8bc7f5bae72edfbc1e Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 11:25:32 +0100 Subject: [PATCH 08/13] refactor abci test to use simapp --- x/slashing/abci_test.go | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/x/slashing/abci_test.go b/x/slashing/abci_test.go index c1b0f62ff..cd9efc553 100644 --- a/x/slashing/abci_test.go +++ b/x/slashing/abci_test.go @@ -4,32 +4,37 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - + "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" ) func TestBeginBlocker(t *testing.T) { - ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + power := int64(100) amt := sdk.TokensFromConsensusPower(power) - addr, pk := slashingkeeper.Addrs[2], slashingkeeper.Pks[2] + addr, pk := sdk.ValAddress(pks[0].Address()), pks[0] // bond the validator - res, err := staking.NewHandler(sk)(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, pk, amt)) + res, err := staking.NewHandler(app.StakingKeeper)(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, pk, amt)) require.NoError(t, err) require.NotNil(t, res) - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) val := abci.Validator{ Address: pk.Address(), @@ -46,9 +51,9 @@ func TestBeginBlocker(t *testing.T) { }, } - slashing.BeginBlocker(ctx, req, keeper) + slashing.BeginBlocker(ctx, req, app.SlashingKeeper) - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(pk.Address())) + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(pk.Address())) require.True(t, found) require.Equal(t, ctx.BlockHeight(), info.StartHeight) require.Equal(t, int64(1), info.IndexOffset) @@ -58,7 +63,7 @@ func TestBeginBlocker(t *testing.T) { height := int64(0) // for 1000 blocks, mark the validator as having signed - for ; height < keeper.SignedBlocksWindow(ctx); height++ { + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ LastCommitInfo: abci.LastCommitInfo{ @@ -69,11 +74,11 @@ func TestBeginBlocker(t *testing.T) { }, } - slashing.BeginBlocker(ctx, req, keeper) + slashing.BeginBlocker(ctx, req, app.SlashingKeeper) } // for 500 blocks, mark the validator as having not signed - for ; height < ((keeper.SignedBlocksWindow(ctx) * 2) - keeper.MinSignedPerWindow(ctx) + 1); height++ { + for ; height < ((app.SlashingKeeper.SignedBlocksWindow(ctx) * 2) - app.SlashingKeeper.MinSignedPerWindow(ctx) + 1); height++ { ctx = ctx.WithBlockHeight(height) req = abci.RequestBeginBlock{ LastCommitInfo: abci.LastCommitInfo{ @@ -84,14 +89,14 @@ func TestBeginBlocker(t *testing.T) { }, } - slashing.BeginBlocker(ctx, req, keeper) + slashing.BeginBlocker(ctx, req, app.SlashingKeeper) } // end block - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) // validator should be jailed - validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) + validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) require.True(t, found) require.Equal(t, sdk.Unbonding, validator.GetStatus()) } From 1da6f3c86e097b0ed7c375dd025923ccf45d27f7 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 11:28:22 +0100 Subject: [PATCH 09/13] refactor TestCannotUnjailUnlessJailed to use simapp --- x/slashing/handler_test.go | 290 ++------------------------------- x/slashing/old_handler_test.go | 280 +++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 275 deletions(-) create mode 100644 x/slashing/old_handler_test.go diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index abd5e5255..f4538a2e2 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -2,39 +2,42 @@ package slashing_test import ( "errors" - "strings" "testing" - "time" - "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/simapp" abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/stretchr/testify/require" ) func TestCannotUnjailUnlessJailed(t *testing.T) { // initial setup - ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) - slh := slashing.NewHandler(keeper) + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + + slh := slashing.NewHandler(app.SlashingKeeper) amt := sdk.TokensFromConsensusPower(100) - addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0] + addr, val := sdk.ValAddress(pks[0].Address()), pks[0] msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt) - res, err := staking.NewHandler(sk)(ctx, msg) + res, err := staking.NewHandler(app.StakingKeeper)(ctx, msg) require.NoError(t, err) require.NotNil(t, res) - staking.EndBlocker(ctx, sk) + staking.EndBlocker(ctx, app.StakingKeeper) require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) // assert non-jailed validator can't be unjailed res, err = slh(ctx, slashing.NewMsgUnjail(addr)) @@ -42,266 +45,3 @@ func TestCannotUnjailUnlessJailed(t *testing.T) { require.Nil(t, res) require.True(t, errors.Is(slashing.ErrValidatorNotJailed, err)) } - -func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { - // initial setup - ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) - slh := slashing.NewHandler(keeper) - amtInt := int64(100) - addr, val, amt := slashingkeeper.Addrs[0], slashingkeeper.Pks[0], sdk.TokensFromConsensusPower(amtInt) - msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt) - msg.MinSelfDelegation = amt - - res, err := staking.NewHandler(sk)(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, - ) - - unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt()) - undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, unbondAmt) - res, err = staking.NewHandler(sk)(ctx, undelegateMsg) - require.NoError(t, err) - require.NotNil(t, res) - - require.True(t, sk.Validator(ctx, addr).IsJailed()) - - // assert non-jailed validator can't be unjailed - res, err = slh(ctx, slashing.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - require.True(t, errors.Is(slashing.ErrSelfDelegationTooLowToUnjail, err)) -} - -func TestJailedValidatorDelegations(t *testing.T) { - ctx, _, stakingKeeper, _, slashingKeeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) - - stakingParams := stakingKeeper.GetParams(ctx) - stakingKeeper.SetParams(ctx, stakingParams) - - // create a validator - bondAmount := sdk.TokensFromConsensusPower(10) - valPubKey := slashingkeeper.Pks[0] - valAddr, consAddr := slashingkeeper.Addrs[1], sdk.ConsAddress(slashingkeeper.Addrs[0]) - - msgCreateVal := slashingkeeper.NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount) - res, err := staking.NewHandler(stakingKeeper)(ctx, msgCreateVal) - require.NoError(t, err) - require.NotNil(t, res) - - // end block - staking.EndBlocker(ctx, stakingKeeper) - - // set dummy signing info - newInfo := slashing.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) - slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) - - // delegate tokens to the validator - delAddr := sdk.AccAddress(slashingkeeper.Addrs[2]) - msgDelegate := slashingkeeper.NewTestMsgDelegate(delAddr, valAddr, bondAmount) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgDelegate) - require.NoError(t, err) - require.NotNil(t, res) - - unbondAmt := sdk.NewCoin(stakingKeeper.GetParams(ctx).BondDenom, bondAmount) - - // unbond validator total self-delegations (which should jail the validator) - msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgUndelegate) - require.NoError(t, err) - require.NotNil(t, res) - - err = stakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) - require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err) - - // verify validator still exists and is jailed - validator, found := stakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.True(t, validator.IsJailed()) - - // verify the validator cannot unjail itself - res, err = slashing.NewHandler(slashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) - require.Error(t, err) - require.Nil(t, res) - - // self-delegate to validator - msgSelfDelegate := slashingkeeper.NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgSelfDelegate) - require.NoError(t, err) - require.NotNil(t, res) - - // verify the validator can now unjail itself - res, err = slashing.NewHandler(slashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) - require.NoError(t, err) - require.NotNil(t, res) -} - -func TestInvalidMsg(t *testing.T) { - k := slashing.Keeper{} - h := slashing.NewHandler(k) - - res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) - require.Error(t, err) - require.Nil(t, res) - require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) -} - -// Test a validator through uptime, downtime, revocation, -// unrevocation, starting height reset, and revocation again -func TestHandleAbsentValidator(t *testing.T) { - // initial setup - ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashingkeeper.TestParams()) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0] - sh := staking.NewHandler(sk) - slh := slashing.NewHandler(keeper) - - res, err := sh(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), - ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) - - // will exist since the validator has been bonded - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.IndexOffset) - require.Equal(t, int64(0), info.MissedBlocksCounter) - require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - height := int64(0) - - // 1000 first blocks OK - for ; height < keeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // 500 blocks missed - for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) - - // validator should be bonded still - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - - bondPool := sk.GetBondedPool(ctx) - require.True(sdk.IntEq(t, amt, bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount)) - - // 501st block missed - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // counter now reset to zero - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, sk) - - // validator should have been jailed - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) - - slashAmt := amt.ToDec().Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() - - // validator should have been slashed - require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) - - // 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator) - height++ - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(1), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, sk) - - // validator should not have been slashed any more, since it was already jailed - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) - - // unrevocation should fail prior to jail expiration - res, err = slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - - // unrevocation should succeed after jail expiration - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeJailDuration(ctx))}) - res, err = slh(ctx, types.NewMsgUnjail(addr)) - require.NoError(t, err) - require.NotNil(t, res) - - // end block - staking.EndBlocker(ctx, sk) - - // validator should be rebonded now - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - - // validator should have been slashed - require.Equal(t, amt.Int64()-slashAmt, bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount.Int64()) - - // Validator start height should not have been changed - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1 - require.Equal(t, int64(1), info.MissedBlocksCounter) - - // validator should not be immediately jailed again - height++ - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - - // 500 signed blocks - nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1 - for ; height < nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, sk) - - // validator should be jailed again after 500 unsigned blocks - nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 - for ; height <= nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, sk) - - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) -} diff --git a/x/slashing/old_handler_test.go b/x/slashing/old_handler_test.go new file mode 100644 index 000000000..be126e6a4 --- /dev/null +++ b/x/slashing/old_handler_test.go @@ -0,0 +1,280 @@ +package slashing_test + +import ( + "errors" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { + // initial setup + ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) + slh := slashing.NewHandler(keeper) + amtInt := int64(100) + addr, val, amt := slashingkeeper.Addrs[0], slashingkeeper.Pks[0], sdk.TokensFromConsensusPower(amtInt) + msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt) + msg.MinSelfDelegation = amt + + res, err := staking.NewHandler(sk)(ctx, msg) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, sk) + + require.Equal( + t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, + ) + + unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt()) + undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, unbondAmt) + res, err = staking.NewHandler(sk)(ctx, undelegateMsg) + require.NoError(t, err) + require.NotNil(t, res) + + require.True(t, sk.Validator(ctx, addr).IsJailed()) + + // assert non-jailed validator can't be unjailed + res, err = slh(ctx, slashing.NewMsgUnjail(addr)) + require.Error(t, err) + require.Nil(t, res) + require.True(t, errors.Is(slashing.ErrSelfDelegationTooLowToUnjail, err)) +} + +func TestJailedValidatorDelegations(t *testing.T) { + ctx, _, stakingKeeper, _, slashingKeeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) + + stakingParams := stakingKeeper.GetParams(ctx) + stakingKeeper.SetParams(ctx, stakingParams) + + // create a validator + bondAmount := sdk.TokensFromConsensusPower(10) + valPubKey := slashingkeeper.Pks[0] + valAddr, consAddr := slashingkeeper.Addrs[1], sdk.ConsAddress(slashingkeeper.Addrs[0]) + + msgCreateVal := slashingkeeper.NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount) + res, err := staking.NewHandler(stakingKeeper)(ctx, msgCreateVal) + require.NoError(t, err) + require.NotNil(t, res) + + // end block + staking.EndBlocker(ctx, stakingKeeper) + + // set dummy signing info + newInfo := slashing.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) + slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) + + // delegate tokens to the validator + delAddr := sdk.AccAddress(slashingkeeper.Addrs[2]) + msgDelegate := slashingkeeper.NewTestMsgDelegate(delAddr, valAddr, bondAmount) + res, err = staking.NewHandler(stakingKeeper)(ctx, msgDelegate) + require.NoError(t, err) + require.NotNil(t, res) + + unbondAmt := sdk.NewCoin(stakingKeeper.GetParams(ctx).BondDenom, bondAmount) + + // unbond validator total self-delegations (which should jail the validator) + msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt) + res, err = staking.NewHandler(stakingKeeper)(ctx, msgUndelegate) + require.NoError(t, err) + require.NotNil(t, res) + + err = stakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) + require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err) + + // verify validator still exists and is jailed + validator, found := stakingKeeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.True(t, validator.IsJailed()) + + // verify the validator cannot unjail itself + res, err = slashing.NewHandler(slashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) + require.Error(t, err) + require.Nil(t, res) + + // self-delegate to validator + msgSelfDelegate := slashingkeeper.NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount) + res, err = staking.NewHandler(stakingKeeper)(ctx, msgSelfDelegate) + require.NoError(t, err) + require.NotNil(t, res) + + // verify the validator can now unjail itself + res, err = slashing.NewHandler(slashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) + require.NoError(t, err) + require.NotNil(t, res) +} + +func TestInvalidMsg(t *testing.T) { + k := slashing.Keeper{} + h := slashing.NewHandler(k) + + res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) + require.Error(t, err) + require.Nil(t, res) + require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) +} + +// Test a validator through uptime, downtime, revocation, +// unrevocation, starting height reset, and revocation again +func TestHandleAbsentValidator(t *testing.T) { + // initial setup + ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashingkeeper.TestParams()) + power := int64(100) + amt := sdk.TokensFromConsensusPower(power) + addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0] + sh := staking.NewHandler(sk) + slh := slashing.NewHandler(keeper) + + res, err := sh(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, sk) + + require.Equal( + t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), + ) + require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) + + // will exist since the validator has been bonded + info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, int64(0), info.IndexOffset) + require.Equal(t, int64(0), info.MissedBlocksCounter) + require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) + height := int64(0) + + // 1000 first blocks OK + for ; height < keeper.SignedBlocksWindow(ctx); height++ { + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, true) + } + info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, int64(0), info.MissedBlocksCounter) + + // 500 blocks missed + for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ { + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) + + // validator should be bonded still + validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + + bondPool := sk.GetBondedPool(ctx) + require.True(sdk.IntEq(t, amt, bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount)) + + // 501st block missed + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + // counter now reset to zero + require.Equal(t, int64(0), info.MissedBlocksCounter) + + // end block + staking.EndBlocker(ctx, sk) + + // validator should have been jailed + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Unbonding, validator.GetStatus()) + + slashAmt := amt.ToDec().Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() + + // validator should have been slashed + require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) + + // 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator) + height++ + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, int64(1), info.MissedBlocksCounter) + + // end block + staking.EndBlocker(ctx, sk) + + // validator should not have been slashed any more, since it was already jailed + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) + + // unrevocation should fail prior to jail expiration + res, err = slh(ctx, types.NewMsgUnjail(addr)) + require.Error(t, err) + require.Nil(t, res) + + // unrevocation should succeed after jail expiration + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeJailDuration(ctx))}) + res, err = slh(ctx, types.NewMsgUnjail(addr)) + require.NoError(t, err) + require.NotNil(t, res) + + // end block + staking.EndBlocker(ctx, sk) + + // validator should be rebonded now + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + + // validator should have been slashed + require.Equal(t, amt.Int64()-slashAmt, bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount.Int64()) + + // Validator start height should not have been changed + info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + // we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1 + require.Equal(t, int64(1), info.MissedBlocksCounter) + + // validator should not be immediately jailed again + height++ + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + + // 500 signed blocks + nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1 + for ; height < nextHeight; height++ { + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + + // end block + staking.EndBlocker(ctx, sk) + + // validator should be jailed again after 500 unsigned blocks + nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 + for ; height <= nextHeight; height++ { + ctx = ctx.WithBlockHeight(height) + keeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + + // end block + staking.EndBlocker(ctx, sk) + + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Unbonding, validator.GetStatus()) +} From 210d78b54d342572986eebdf95526e6a254a70e3 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 11:43:18 +0100 Subject: [PATCH 10/13] refactor TestCannotUnjailUnlessMeetMinSelfDelegation to use simapp --- x/slashing/handler_test.go | 39 ++++++++++++++++++++++++++++++++++ x/slashing/old_handler_test.go | 36 ------------------------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index f4538a2e2..134a10deb 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -45,3 +45,42 @@ func TestCannotUnjailUnlessJailed(t *testing.T) { require.Nil(t, res) require.True(t, errors.Is(slashing.ErrValidatorNotJailed, err)) } + +func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { + // initial setup + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{}) + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + + slh := slashing.NewHandler(app.SlashingKeeper) + amtInt := int64(100) + addr, val, amt := sdk.ValAddress(pks[0].Address()), pks[0], sdk.TokensFromConsensusPower(amtInt) + msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt) + msg.MinSelfDelegation = amt + + res, err := staking.NewHandler(app.StakingKeeper)(ctx, msg) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, app.StakingKeeper) + + require.Equal( + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.Coins{sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, + ) + + unbondAmt := sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, sdk.OneInt()) + undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, unbondAmt) + res, err = staking.NewHandler(app.StakingKeeper)(ctx, undelegateMsg) + require.NoError(t, err) + require.NotNil(t, res) + + require.True(t, app.StakingKeeper.Validator(ctx, addr).IsJailed()) + + // assert non-jailed validator can't be unjailed + res, err = slh(ctx, slashing.NewMsgUnjail(addr)) + require.Error(t, err) + require.Nil(t, res) + require.True(t, errors.Is(slashing.ErrSelfDelegationTooLowToUnjail, err)) +} diff --git a/x/slashing/old_handler_test.go b/x/slashing/old_handler_test.go index be126e6a4..470acb7a8 100644 --- a/x/slashing/old_handler_test.go +++ b/x/slashing/old_handler_test.go @@ -1,7 +1,6 @@ package slashing_test import ( - "errors" "strings" "testing" "time" @@ -16,41 +15,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" ) -func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { - // initial setup - ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) - slh := slashing.NewHandler(keeper) - amtInt := int64(100) - addr, val, amt := slashingkeeper.Addrs[0], slashingkeeper.Pks[0], sdk.TokensFromConsensusPower(amtInt) - msg := slashingkeeper.NewTestMsgCreateValidator(addr, val, amt) - msg.MinSelfDelegation = amt - - res, err := staking.NewHandler(sk)(ctx, msg) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))}, - ) - - unbondAmt := sdk.NewCoin(sk.GetParams(ctx).BondDenom, sdk.OneInt()) - undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, unbondAmt) - res, err = staking.NewHandler(sk)(ctx, undelegateMsg) - require.NoError(t, err) - require.NotNil(t, res) - - require.True(t, sk.Validator(ctx, addr).IsJailed()) - - // assert non-jailed validator can't be unjailed - res, err = slh(ctx, slashing.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - require.True(t, errors.Is(slashing.ErrSelfDelegationTooLowToUnjail, err)) -} - func TestJailedValidatorDelegations(t *testing.T) { ctx, _, stakingKeeper, _, slashingKeeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) From c97a1b66ca8610cdcb5e37885e5cd864a4b284c3 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 17:13:49 +0100 Subject: [PATCH 11/13] migrate TestJailedValidatorDelegations to simapp --- x/slashing/handler_test.go | 81 ++++++++++++++++++++++++++++++++++ x/slashing/old_handler_test.go | 74 ------------------------------- 2 files changed, 81 insertions(+), 74 deletions(-) diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 134a10deb..8113acd29 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -2,7 +2,9 @@ package slashing_test import ( "errors" + "strings" "testing" + "time" "github.com/cosmos/cosmos-sdk/simapp" abci "github.com/tendermint/tendermint/abci/types" @@ -84,3 +86,82 @@ func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) { require.Nil(t, res) require.True(t, errors.Is(slashing.ErrSelfDelegationTooLowToUnjail, err)) } + +func TestJailedValidatorDelegations(t *testing.T) { + // initial setup + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{Time: time.Unix(0, 0)}) + + pks := simapp.CreateTestPubKeys(3) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(20)) + app.SlashingKeeper.SetParams(ctx, slashingkeeper.TestParams()) + + stakingParams := app.StakingKeeper.GetParams(ctx) + app.StakingKeeper.SetParams(ctx, stakingParams) + + // create a validator + bondAmount := sdk.TokensFromConsensusPower(10) + valPubKey := pks[1] + valAddr, consAddr := sdk.ValAddress(pks[1].Address()), sdk.ConsAddress(pks[0].Address()) + + msgCreateVal := slashingkeeper.NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount) + res, err := staking.NewHandler(app.StakingKeeper)(ctx, msgCreateVal) + require.NoError(t, err) + require.NotNil(t, res) + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + // set dummy signing info + newInfo := slashing.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) + app.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) + + // delegate tokens to the validator + delAddr := sdk.AccAddress(pks[2].Address()) + msgDelegate := slashingkeeper.NewTestMsgDelegate(delAddr, valAddr, bondAmount) + res, err = staking.NewHandler(app.StakingKeeper)(ctx, msgDelegate) + require.NoError(t, err) + require.NotNil(t, res) + + unbondAmt := sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, bondAmount) + + // unbond validator total self-delegations (which should jail the validator) + msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt) + res, err = staking.NewHandler(app.StakingKeeper)(ctx, msgUndelegate) + require.NoError(t, err) + require.NotNil(t, res) + + err = app.StakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) + require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err) + + // verify validator still exists and is jailed + validator, found := app.StakingKeeper.GetValidator(ctx, valAddr) + require.True(t, found) + require.True(t, validator.IsJailed()) + + // verify the validator cannot unjail itself + res, err = slashing.NewHandler(app.SlashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) + require.Error(t, err) + require.Nil(t, res) + + // self-delegate to validator + msgSelfDelegate := slashingkeeper.NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount) + res, err = staking.NewHandler(app.StakingKeeper)(ctx, msgSelfDelegate) + require.NoError(t, err) + require.NotNil(t, res) + + // verify the validator can now unjail itself + res, err = slashing.NewHandler(app.SlashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) + require.NoError(t, err) + require.NotNil(t, res) +} + +func TestInvalidMsg(t *testing.T) { + k := slashing.Keeper{} + h := slashing.NewHandler(k) + + res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) + require.Error(t, err) + require.Nil(t, res) + require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) +} diff --git a/x/slashing/old_handler_test.go b/x/slashing/old_handler_test.go index 470acb7a8..c8c66821c 100644 --- a/x/slashing/old_handler_test.go +++ b/x/slashing/old_handler_test.go @@ -1,7 +1,6 @@ package slashing_test import ( - "strings" "testing" "time" @@ -15,79 +14,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" ) -func TestJailedValidatorDelegations(t *testing.T) { - ctx, _, stakingKeeper, _, slashingKeeper := slashingkeeper.CreateTestInput(t, slashing.DefaultParams()) - - stakingParams := stakingKeeper.GetParams(ctx) - stakingKeeper.SetParams(ctx, stakingParams) - - // create a validator - bondAmount := sdk.TokensFromConsensusPower(10) - valPubKey := slashingkeeper.Pks[0] - valAddr, consAddr := slashingkeeper.Addrs[1], sdk.ConsAddress(slashingkeeper.Addrs[0]) - - msgCreateVal := slashingkeeper.NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount) - res, err := staking.NewHandler(stakingKeeper)(ctx, msgCreateVal) - require.NoError(t, err) - require.NotNil(t, res) - - // end block - staking.EndBlocker(ctx, stakingKeeper) - - // set dummy signing info - newInfo := slashing.NewValidatorSigningInfo(consAddr, 0, 0, time.Unix(0, 0), false, 0) - slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) - - // delegate tokens to the validator - delAddr := sdk.AccAddress(slashingkeeper.Addrs[2]) - msgDelegate := slashingkeeper.NewTestMsgDelegate(delAddr, valAddr, bondAmount) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgDelegate) - require.NoError(t, err) - require.NotNil(t, res) - - unbondAmt := sdk.NewCoin(stakingKeeper.GetParams(ctx).BondDenom, bondAmount) - - // unbond validator total self-delegations (which should jail the validator) - msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondAmt) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgUndelegate) - require.NoError(t, err) - require.NotNil(t, res) - - err = stakingKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr) - require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err) - - // verify validator still exists and is jailed - validator, found := stakingKeeper.GetValidator(ctx, valAddr) - require.True(t, found) - require.True(t, validator.IsJailed()) - - // verify the validator cannot unjail itself - res, err = slashing.NewHandler(slashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) - require.Error(t, err) - require.Nil(t, res) - - // self-delegate to validator - msgSelfDelegate := slashingkeeper.NewTestMsgDelegate(sdk.AccAddress(valAddr), valAddr, bondAmount) - res, err = staking.NewHandler(stakingKeeper)(ctx, msgSelfDelegate) - require.NoError(t, err) - require.NotNil(t, res) - - // verify the validator can now unjail itself - res, err = slashing.NewHandler(slashingKeeper)(ctx, slashing.NewMsgUnjail(valAddr)) - require.NoError(t, err) - require.NotNil(t, res) -} - -func TestInvalidMsg(t *testing.T) { - k := slashing.Keeper{} - h := slashing.NewHandler(k) - - res, err := h(sdk.NewContext(nil, abci.Header{}, false, nil), sdk.NewTestMsg()) - require.Error(t, err) - require.Nil(t, res) - require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) -} - // Test a validator through uptime, downtime, revocation, // unrevocation, starting height reset, and revocation again func TestHandleAbsentValidator(t *testing.T) { From 78791f3c4e1611892a4fc9d4d131f950a422a2b2 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 17:21:17 +0100 Subject: [PATCH 12/13] clean part of the code --- x/slashing/handler_test.go | 163 +++++++++++++++++++++++++++++ x/slashing/keeper/test_common.go | 127 +---------------------- x/slashing/old_handler_test.go | 170 ------------------------------- 3 files changed, 164 insertions(+), 296 deletions(-) delete mode 100644 x/slashing/old_handler_test.go diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 8113acd29..955eeadb8 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -6,6 +6,8 @@ import ( "testing" "time" + "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/simapp" abci "github.com/tendermint/tendermint/abci/types" @@ -165,3 +167,164 @@ func TestInvalidMsg(t *testing.T) { require.Nil(t, res) require.True(t, strings.Contains(err.Error(), "unrecognized slashing message type")) } + +// Test a validator through uptime, downtime, revocation, +// unrevocation, starting height reset, and revocation again +func TestHandleAbsentValidator(t *testing.T) { + // initial setup + app := simapp.Setup(false) + ctx := app.BaseApp.NewContext(false, abci.Header{Time: time.Unix(0, 0)}) + + pks := simapp.CreateTestPubKeys(1) + simapp.AddTestAddrsFromPubKeys(app, ctx, pks, sdk.TokensFromConsensusPower(200)) + app.SlashingKeeper.SetParams(ctx, slashingkeeper.TestParams()) + + power := int64(100) + amt := sdk.TokensFromConsensusPower(power) + addr, val := sdk.ValAddress(pks[0].Address()), pks[0] + sh := staking.NewHandler(app.StakingKeeper) + slh := slashing.NewHandler(app.SlashingKeeper) + + res, err := sh(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)) + require.NoError(t, err) + require.NotNil(t, res) + + staking.EndBlocker(ctx, app.StakingKeeper) + + require.Equal( + t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), + ) + require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) + + // will exist since the validator has been bonded + info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, int64(0), info.IndexOffset) + require.Equal(t, int64(0), info.MissedBlocksCounter) + require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) + height := int64(0) + + // 1000 first blocks OK + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) + } + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, int64(0), info.MissedBlocksCounter) + + // 500 blocks missed + for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx)); height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) + + // validator should be bonded still + validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + + bondPool := app.StakingKeeper.GetBondedPool(ctx) + require.True(sdk.IntEq(t, amt, app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) + + // 501st block missed + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + // counter now reset to zero + require.Equal(t, int64(0), info.MissedBlocksCounter) + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + // validator should have been jailed + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Unbonding, validator.GetStatus()) + + slashAmt := amt.ToDec().Mul(app.SlashingKeeper.SlashFractionDowntime(ctx)).RoundInt64() + + // validator should have been slashed + require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) + + // 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator) + height++ + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + require.Equal(t, int64(1), info.MissedBlocksCounter) + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + // validator should not have been slashed any more, since it was already jailed + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) + + // unrevocation should fail prior to jail expiration + res, err = slh(ctx, types.NewMsgUnjail(addr)) + require.Error(t, err) + require.Nil(t, res) + + // unrevocation should succeed after jail expiration + ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(app.SlashingKeeper.DowntimeJailDuration(ctx))}) + res, err = slh(ctx, types.NewMsgUnjail(addr)) + require.NoError(t, err) + require.NotNil(t, res) + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + // validator should be rebonded now + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + + // validator should have been slashed + require.Equal(t, amt.Int64()-slashAmt, app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount.Int64()) + + // Validator start height should not have been changed + info, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) + require.True(t, found) + require.Equal(t, int64(0), info.StartHeight) + // we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1 + require.Equal(t, int64(1), info.MissedBlocksCounter) + + // validator should not be immediately jailed again + height++ + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Bonded, validator.GetStatus()) + + // 500 signed blocks + nextHeight := height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 + for ; height < nextHeight; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + // validator should be jailed again after 500 unsigned blocks + nextHeight = height + app.SlashingKeeper.MinSignedPerWindow(ctx) + 1 + for ; height <= nextHeight; height++ { + ctx = ctx.WithBlockHeight(height) + app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) + } + + // end block + staking.EndBlocker(ctx, app.StakingKeeper) + + validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) + require.Equal(t, sdk.Unbonding, validator.GetStatus()) +} diff --git a/x/slashing/keeper/test_common.go b/x/slashing/keeper/test_common.go index d80c68b7e..20eb8eda6 100644 --- a/x/slashing/keeper/test_common.go +++ b/x/slashing/keeper/test_common.go @@ -5,143 +5,18 @@ package keeper // noalias import ( - "encoding/hex" - "testing" - "time" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/codec" - simappcodec "github.com/cosmos/cosmos-sdk/simapp/codec" - "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/auth" - "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/params/keeper" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" - "github.com/cosmos/cosmos-sdk/x/supply" + "github.com/tendermint/tendermint/crypto" ) // TODO remove dependencies on staking (should only refer to validator set type from sdk) var ( - Pks = []crypto.PubKey{ - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB50"), - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB51"), - newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB52"), - } - Addrs = []sdk.ValAddress{ - sdk.ValAddress(Pks[0].Address()), - sdk.ValAddress(Pks[1].Address()), - sdk.ValAddress(Pks[2].Address()), - } InitTokens = sdk.TokensFromConsensusPower(200) - initCoins = sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens)) ) -func createTestCodec() *codec.Codec { - cdc := codec.New() - sdk.RegisterCodec(cdc) - auth.RegisterCodec(cdc) - supply.RegisterCodec(cdc) - bank.RegisterCodec(cdc) - staking.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - return cdc -} - -func CreateTestInput(t *testing.T, defaults types.Params) (sdk.Context, bank.Keeper, staking.Keeper, paramtypes.Subspace, Keeper) { - keyAcc := sdk.NewKVStoreKey(auth.StoreKey) - keyBank := sdk.NewKVStoreKey(bank.StoreKey) - keyStaking := sdk.NewKVStoreKey(staking.StoreKey) - keySlashing := sdk.NewKVStoreKey(types.StoreKey) - keySupply := sdk.NewKVStoreKey(supply.StoreKey) - keyParams := sdk.NewKVStoreKey(paramtypes.StoreKey) - tkeyParams := sdk.NewTransientStoreKey(paramtypes.TStoreKey) - - db := dbm.NewMemDB() - - ms := store.NewCommitMultiStore(db) - ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyBank, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keySupply, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) - ms.MountStoreWithDB(tkeyParams, sdk.StoreTypeTransient, db) - - err := ms.LoadLatestVersion() - require.Nil(t, err) - - ctx := sdk.NewContext(ms, abci.Header{Time: time.Unix(0, 0)}, false, log.NewNopLogger()) - cdc := createTestCodec() - appCodec := simappcodec.NewAppCodec(cdc) - - feeCollectorAcc := supply.NewEmptyModuleAccount(auth.FeeCollectorName) - notBondedPool := supply.NewEmptyModuleAccount(staking.NotBondedPoolName, supply.Burner, supply.Staking) - bondPool := supply.NewEmptyModuleAccount(staking.BondedPoolName, supply.Burner, supply.Staking) - - blacklistedAddrs := make(map[string]bool) - blacklistedAddrs[feeCollectorAcc.GetAddress().String()] = true - blacklistedAddrs[notBondedPool.GetAddress().String()] = true - blacklistedAddrs[bondPool.GetAddress().String()] = true - - paramsKeeper := keeper.NewKeeper(appCodec, keyParams, tkeyParams) - accountKeeper := auth.NewAccountKeeper(appCodec, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) - - bk := bank.NewBaseKeeper(appCodec, keyBank, accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), blacklistedAddrs) - maccPerms := map[string][]string{ - auth.FeeCollectorName: nil, - staking.NotBondedPoolName: {supply.Burner, supply.Staking}, - staking.BondedPoolName: {supply.Burner, supply.Staking}, - } - supplyKeeper := supply.NewKeeper(appCodec, keySupply, accountKeeper, bk, maccPerms) - - totalSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, InitTokens.MulRaw(int64(len(Addrs))))) - supplyKeeper.SetSupply(ctx, supply.NewSupply(totalSupply)) - - sk := staking.NewKeeper(staking.ModuleCdc, keyStaking, bk, supplyKeeper, paramsKeeper.Subspace(staking.DefaultParamspace)) - genesis := staking.DefaultGenesisState() - - // set module accounts - supplyKeeper.SetModuleAccount(ctx, feeCollectorAcc) - supplyKeeper.SetModuleAccount(ctx, bondPool) - supplyKeeper.SetModuleAccount(ctx, notBondedPool) - - _ = staking.InitGenesis(ctx, sk, accountKeeper, bk, supplyKeeper, genesis) - - for i, addr := range Addrs { - addr := sdk.AccAddress(addr) - accountKeeper.SetAccount(ctx, auth.NewBaseAccount(addr, Pks[i], uint64(i), 0)) - require.NoError(t, bk.SetBalances(ctx, addr, initCoins)) - } - - paramstore := paramsKeeper.Subspace(types.DefaultParamspace) - keeper := NewKeeper(types.ModuleCdc, keySlashing, &sk, paramstore) - - keeper.SetParams(ctx, defaults) - sk.SetHooks(keeper.Hooks()) - - return ctx, bk, sk, paramstore, keeper -} - -func newPubKey(pk string) (res crypto.PubKey) { - pkBytes, err := hex.DecodeString(pk) - if err != nil { - panic(err) - } - var pkEd ed25519.PubKeyEd25519 - copy(pkEd[:], pkBytes) - return pkEd -} - // Have to change these parameters for tests // lest the tests take forever func TestParams() types.Params { diff --git a/x/slashing/old_handler_test.go b/x/slashing/old_handler_test.go deleted file mode 100644 index c8c66821c..000000000 --- a/x/slashing/old_handler_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package slashing_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/slashing" - slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" - "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/staking" -) - -// Test a validator through uptime, downtime, revocation, -// unrevocation, starting height reset, and revocation again -func TestHandleAbsentValidator(t *testing.T) { - // initial setup - ctx, bk, sk, _, keeper := slashingkeeper.CreateTestInput(t, slashingkeeper.TestParams()) - power := int64(100) - amt := sdk.TokensFromConsensusPower(power) - addr, val := slashingkeeper.Addrs[0], slashingkeeper.Pks[0] - sh := staking.NewHandler(sk) - slh := slashing.NewHandler(keeper) - - res, err := sh(ctx, slashingkeeper.NewTestMsgCreateValidator(addr, val, amt)) - require.NoError(t, err) - require.NotNil(t, res) - - staking.EndBlocker(ctx, sk) - - require.Equal( - t, bk.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, slashingkeeper.InitTokens.Sub(amt))), - ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) - - // will exist since the validator has been bonded - info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.IndexOffset) - require.Equal(t, int64(0), info.MissedBlocksCounter) - require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - height := int64(0) - - // 1000 first blocks OK - for ; height < keeper.SignedBlocksWindow(ctx); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, true) - } - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // 500 blocks missed - for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.MissedBlocksCounter) - - // validator should be bonded still - validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - - bondPool := sk.GetBondedPool(ctx) - require.True(sdk.IntEq(t, amt, bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount)) - - // 501st block missed - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // counter now reset to zero - require.Equal(t, int64(0), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, sk) - - // validator should have been jailed - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) - - slashAmt := amt.ToDec().Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() - - // validator should have been slashed - require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) - - // 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator) - height++ - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - require.Equal(t, int64(1), info.MissedBlocksCounter) - - // end block - staking.EndBlocker(ctx, sk) - - // validator should not have been slashed any more, since it was already jailed - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) - - // unrevocation should fail prior to jail expiration - res, err = slh(ctx, types.NewMsgUnjail(addr)) - require.Error(t, err) - require.Nil(t, res) - - // unrevocation should succeed after jail expiration - ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeJailDuration(ctx))}) - res, err = slh(ctx, types.NewMsgUnjail(addr)) - require.NoError(t, err) - require.NotNil(t, res) - - // end block - staking.EndBlocker(ctx, sk) - - // validator should be rebonded now - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - - // validator should have been slashed - require.Equal(t, amt.Int64()-slashAmt, bk.GetBalance(ctx, bondPool.GetAddress(), sk.BondDenom(ctx)).Amount.Int64()) - - // Validator start height should not have been changed - info, found = keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) - require.True(t, found) - require.Equal(t, int64(0), info.StartHeight) - // we've missed 2 blocks more than the maximum, so the counter was reset to 0 at 1 block more and is now 1 - require.Equal(t, int64(1), info.MissedBlocksCounter) - - // validator should not be immediately jailed again - height++ - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Bonded, validator.GetStatus()) - - // 500 signed blocks - nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1 - for ; height < nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, sk) - - // validator should be jailed again after 500 unsigned blocks - nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 - for ; height <= nextHeight; height++ { - ctx = ctx.WithBlockHeight(height) - keeper.HandleValidatorSignature(ctx, val.Address(), power, false) - } - - // end block - staking.EndBlocker(ctx, sk) - - validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) -} From bfac2a93423129fdd89eabfffb810d84cde75cdd Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 3 Mar 2020 16:57:33 +0000 Subject: [PATCH 13/13] fix types.ChainAnteDecorators() panic (#5742) * fix types.ChainAnteDecorators() panic ChainAnteDecorators() panics when no arguments are supplied. This change its behaviour and the function now returns a nil AnteHandler in case no AnteDecorator instances are supplied. Closes: #5741 * Append Terminator to non-terminated chains. * Fix test case --- CHANGELOG.md | 1 + types/handler.go | 12 ++++++------ types/handler_test.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 types/handler_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f2efc89f..9c98d0ea7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ resulted in a panic when the tx execution mode was `CheckTx`. * (client) [\#5618](https://github.com/cosmos/cosmos-sdk/pull/5618) Fix crash on the client when the verifier is not set. * (x/distribution) [\#5620](https://github.com/cosmos/cosmos-sdk/pull/5620) Fix nil pointer deref in distribution tax/rewward validation helpers. * (genesis) [\#5086](https://github.com/cosmos/cosmos-sdk/issues/5086) Ensure `gentxs` are always an empty array instead of `nil` +* (types) [\#5741](https://github.com/cosmos/cosmos-sdk/issues/5741) Prevent ChainAnteDecorators() from panicking when empty AnteDecorator slice is supplied. ### State Machine Breaking diff --git a/types/handler.go b/types/handler.go index 6815230a8..03d1f02d5 100644 --- a/types/handler.go +++ b/types/handler.go @@ -25,15 +25,15 @@ type AnteDecorator interface { // MUST set GasMeter with the FIRST AnteDecorator. Failing to do so will cause // transactions to be processed with an infinite gasmeter and open a DOS attack vector. // Use `ante.SetUpContextDecorator` or a custom Decorator with similar functionality. +// Returns nil when no AnteDecorator are supplied. func ChainAnteDecorators(chain ...AnteDecorator) AnteHandler { - if (chain[len(chain)-1] != Terminator{}) { - chain = append(chain, Terminator{}) + if len(chain) == 0 { + return nil } - if len(chain) == 1 { - return func(ctx Context, tx Tx, simulate bool) (Context, error) { - return chain[0].AnteHandle(ctx, tx, simulate, nil) - } + // handle non-terminated decorators chain + if (chain[len(chain)-1] != Terminator{}) { + chain = append(chain, Terminator{}) } return func(ctx Context, tx Tx, simulate bool) (Context, error) { diff --git a/types/handler_test.go b/types/handler_test.go new file mode 100644 index 000000000..4847a1131 --- /dev/null +++ b/types/handler_test.go @@ -0,0 +1,28 @@ +package types_test + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/tests/mocks" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func TestChainAnteDecorators(t *testing.T) { + t.Parallel() + // test panic + require.Nil(t, sdk.ChainAnteDecorators([]sdk.AnteDecorator{}...)) + + ctx, tx := sdk.Context{}, sdk.Tx(nil) + mockCtrl := gomock.NewController(t) + mockAnteDecorator1 := mocks.NewMockAnteDecorator(mockCtrl) + mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Any()).Times(1) + sdk.ChainAnteDecorators(mockAnteDecorator1)(ctx, tx, true) + + mockAnteDecorator2 := mocks.NewMockAnteDecorator(mockCtrl) + mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, mockAnteDecorator2).Times(1) + mockAnteDecorator2.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, nil).Times(1) + sdk.ChainAnteDecorators(mockAnteDecorator1, mockAnteDecorator2) +}