Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate slashing module to use new Simapp #5744

Merged
merged 14 commits into from
Mar 3, 2020
Merged
Prev Previous commit
Next Next commit
clean part of the code
  • Loading branch information
jgimeno committed Mar 3, 2020
commit 78791f3c4e1611892a4fc9d4d131f950a422a2b2
163 changes: 163 additions & 0 deletions x/slashing/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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())
}
127 changes: 1 addition & 126 deletions x/slashing/keeper/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading