From 78791f3c4e1611892a4fc9d4d131f950a422a2b2 Mon Sep 17 00:00:00 2001 From: Jonathan Gimeno Date: Tue, 3 Mar 2020 17:21:17 +0100 Subject: [PATCH] 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()) -}