From 6e8636d077874c7ce70ec2fe1a14674cd6f55a95 Mon Sep 17 00:00:00 2001 From: akhilkumarpilli Date: Tue, 3 Dec 2024 14:34:49 +0530 Subject: [PATCH 1/5] WIP: slashing v2 tests --- tests/integration/v2/slashing/abci_test.go | 150 +++++ tests/integration/v2/slashing/keeper_test.go | 551 ++++++++++++++++++ .../v2/slashing/slash_redelegation_test.go | 502 ++++++++++++++++ .../integration/v2/slashing/slashing_test.go | 119 ++++ 4 files changed, 1322 insertions(+) create mode 100644 tests/integration/v2/slashing/abci_test.go create mode 100644 tests/integration/v2/slashing/keeper_test.go create mode 100644 tests/integration/v2/slashing/slash_redelegation_test.go create mode 100644 tests/integration/v2/slashing/slashing_test.go diff --git a/tests/integration/v2/slashing/abci_test.go b/tests/integration/v2/slashing/abci_test.go new file mode 100644 index 000000000000..11c7338367e8 --- /dev/null +++ b/tests/integration/v2/slashing/abci_test.go @@ -0,0 +1,150 @@ +package slashing + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "cosmossdk.io/core/comet" + coreheader "cosmossdk.io/core/header" + "cosmossdk.io/depinject" + "cosmossdk.io/log" + bankkeeper "cosmossdk.io/x/bank/keeper" + "cosmossdk.io/x/slashing" + slashingkeeper "cosmossdk.io/x/slashing/keeper" + "cosmossdk.io/x/slashing/testutil" + stakingkeeper "cosmossdk.io/x/staking/keeper" + stakingtestutil "cosmossdk.io/x/staking/testutil" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/testutil/configurator" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" +) + +var AppConfig = configurator.NewAppConfig( + configurator.AccountsModule(), + configurator.AuthModule(), + configurator.BankModule(), + configurator.StakingModule(), + configurator.SlashingModule(), + configurator.TxModule(), + configurator.ValidateModule(), + configurator.ConsensusModule(), + configurator.GenutilModule(), + configurator.MintModule(), + configurator.DistributionModule(), + configurator.ProtocolPoolModule(), +) + +// TestBeginBlocker is a unit test function that tests the behavior of the BeginBlocker function. +// It sets up the necessary dependencies and context, creates a validator, and performs various operations +// to test the slashing logic. It checks if the validator is correctly jailed after a certain number of blocks. +func TestBeginBlocker(t *testing.T) { + var ( + interfaceRegistry codectypes.InterfaceRegistry + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + stakingKeeper *stakingkeeper.Keeper + slashingKeeper slashingkeeper.Keeper + ) + + app, err := simtestutil.Setup( + depinject.Configs( + AppConfig, + depinject.Supply(log.NewNopLogger()), + ), + &interfaceRegistry, + &accountKeeper, + &bankKeeper, + &stakingKeeper, + &slashingKeeper, + ) + require.NoError(t, err) + + ctx := app.BaseApp.NewContext(false) + + pks := simtestutil.CreateTestPubKeys(1) + simtestutil.AddTestAddrsFromPubKeys(bankKeeper, stakingKeeper, ctx, pks, stakingKeeper.TokensFromConsensusPower(ctx, 200)) + addr, pk := sdk.ValAddress(pks[0].Address()), pks[0] + tstaking := stakingtestutil.NewHelper(t, ctx, stakingKeeper) + + // bond the validator + power := int64(100) + acc := accountKeeper.NewAccountWithAddress(ctx, sdk.AccAddress(addr)) + accountKeeper.SetAccount(ctx, acc) + amt := tstaking.CreateValidatorWithValPower(addr, pk, power, true) + _, err = stakingKeeper.EndBlocker(ctx) + require.NoError(t, err) + bondDenom, err := stakingKeeper.BondDenom(ctx) + require.NoError(t, err) + require.Equal( + t, bankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(bondDenom, testutil.InitTokens.Sub(amt))), + ) + val, err := stakingKeeper.Validator(ctx, addr) + require.NoError(t, err) + require.Equal(t, amt, val.GetBondedTokens()) + + abciVal := comet.Validator{ + Address: pk.Address(), + Power: power, + } + + ctx = ctx.WithCometInfo(comet.Info{ + LastCommit: comet.CommitInfo{Votes: []comet.VoteInfo{{ + Validator: abciVal, + BlockIDFlag: comet.BlockIDFlagCommit, + }}}, + }) + cometInfoService := runtime.NewContextAwareCometInfoService() + + err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) + require.NoError(t, err) + + info, err := slashingKeeper.ValidatorSigningInfo.Get(ctx, sdk.ConsAddress(pk.Address())) + require.NoError(t, err) + require.Equal(t, ctx.HeaderInfo().Height, info.StartHeight) + require.Equal(t, int64(0), info.IndexOffset) + require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) + require.Equal(t, int64(0), info.MissedBlocksCounter) + + height := int64(0) + + signedBlocksWindow, err := slashingKeeper.SignedBlocksWindow(ctx) + require.NoError(t, err) + // for 100 blocks, mark the validator as having signed + for ; height < signedBlocksWindow; height++ { + ctx = ctx.WithHeaderInfo(coreheader.Info{Height: height}) + + err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) + require.NoError(t, err) + } + + minSignedPerWindow, err := slashingKeeper.MinSignedPerWindow(ctx) + require.NoError(t, err) + // for 50 blocks, mark the validator as having not signed + for ; height < ((signedBlocksWindow * 2) - minSignedPerWindow + 1); height++ { + ctx = ctx.WithHeaderInfo(coreheader.Info{Height: height}).WithCometInfo(comet.Info{ + LastCommit: comet.CommitInfo{Votes: []comet.VoteInfo{{ + Validator: abciVal, + BlockIDFlag: comet.BlockIDFlagAbsent, + }}}, + }) + + err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) + require.NoError(t, err) + } + + // end block + _, err = stakingKeeper.EndBlocker(ctx) + require.NoError(t, err) + + // validator should be jailed + validator, err := stakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) + require.NoError(t, err) + require.Equal(t, sdk.Unbonding, validator.GetStatus()) +} diff --git a/tests/integration/v2/slashing/keeper_test.go b/tests/integration/v2/slashing/keeper_test.go new file mode 100644 index 000000000000..03594413f0e7 --- /dev/null +++ b/tests/integration/v2/slashing/keeper_test.go @@ -0,0 +1,551 @@ +package slashing + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "gotest.tools/v3/assert" + + "cosmossdk.io/core/appmodule" + "cosmossdk.io/core/comet" + coreheader "cosmossdk.io/core/header" + "cosmossdk.io/log" + storetypes "cosmossdk.io/store/types" + "cosmossdk.io/x/bank" + bankkeeper "cosmossdk.io/x/bank/keeper" + banktypes "cosmossdk.io/x/bank/types" + "cosmossdk.io/x/consensus" + consensusparamkeeper "cosmossdk.io/x/consensus/keeper" + consensustypes "cosmossdk.io/x/consensus/types" + minttypes "cosmossdk.io/x/mint/types" + "cosmossdk.io/x/slashing" + slashingkeeper "cosmossdk.io/x/slashing/keeper" + "cosmossdk.io/x/slashing/testutil" + slashingtypes "cosmossdk.io/x/slashing/types" + "cosmossdk.io/x/staking" + stakingkeeper "cosmossdk.io/x/staking/keeper" + stakingtestutil "cosmossdk.io/x/staking/testutil" + stakingtypes "cosmossdk.io/x/staking/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/codec" + addresscodec "github.com/cosmos/cosmos-sdk/codec/address" + codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil" + "github.com/cosmos/cosmos-sdk/runtime" + "github.com/cosmos/cosmos-sdk/testutil/integration" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + "github.com/cosmos/cosmos-sdk/x/auth" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +type fixture struct { + app *integration.App + + ctx sdk.Context + + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + slashingKeeper slashingkeeper.Keeper + stakingKeeper *stakingkeeper.Keeper + + addrDels []sdk.AccAddress + valAddrs []sdk.ValAddress +} + +func initFixture(tb testing.TB) *fixture { + tb.Helper() + keys := storetypes.NewKVStoreKeys( + authtypes.StoreKey, banktypes.StoreKey, slashingtypes.StoreKey, stakingtypes.StoreKey, consensustypes.StoreKey, + ) + encodingCfg := moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{}, auth.AppModule{}) + cdc := encodingCfg.Codec + + authority := authtypes.NewModuleAddress("gov") + + maccPerms := map[string][]string{ + minttypes.ModuleName: {authtypes.Minter}, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + } + + msgRouter := baseapp.NewMsgServiceRouter() + queryRouter := baseapp.NewGRPCQueryRouter() + + // gomock initializations + ctrl := gomock.NewController(tb) + acctsModKeeper := authtestutil.NewMockAccountsModKeeper(ctrl) + accNum := uint64(0) + acctsModKeeper.EXPECT().NextAccountNumber(gomock.Any()).AnyTimes().DoAndReturn(func(ctx context.Context) (uint64, error) { + currentNum := accNum + accNum++ + return currentNum, nil + }) + + accountKeeper := authkeeper.NewAccountKeeper( + runtime.NewEnvironment(runtime.NewKVStoreService(keys[authtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), + cdc, + authtypes.ProtoBaseAccount, + acctsModKeeper, + maccPerms, + addresscodec.NewBech32Codec(sdk.Bech32MainPrefix), + sdk.Bech32MainPrefix, + authority.String(), + ) + + blockedAddresses := map[string]bool{ + accountKeeper.GetAuthority(): false, + } + bankKeeper := bankkeeper.NewBaseKeeper( + runtime.NewEnvironment(runtime.NewKVStoreService(keys[banktypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), + cdc, + accountKeeper, + blockedAddresses, + authority.String(), + ) + + cometInfoService := runtime.NewContextAwareCometInfoService() + + consensusParamsKeeper := consensusparamkeeper.NewKeeper(cdc, runtime.NewEnvironment(runtime.NewKVStoreService(keys[consensustypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), authtypes.NewModuleAddress("gov").String()) + + stakingKeeper := stakingkeeper.NewKeeper(cdc, runtime.NewEnvironment(runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), accountKeeper, bankKeeper, consensusParamsKeeper, authority.String(), addresscodec.NewBech32Codec(sdk.Bech32PrefixValAddr), addresscodec.NewBech32Codec(sdk.Bech32PrefixConsAddr), cometInfoService) + + slashingKeeper := slashingkeeper.NewKeeper(runtime.NewEnvironment(runtime.NewKVStoreService(keys[slashingtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), cdc, &codec.LegacyAmino{}, stakingKeeper, authority.String()) + + bankModule := bank.NewAppModule(cdc, bankKeeper, accountKeeper) + stakingModule := staking.NewAppModule(cdc, stakingKeeper) + slashingModule := slashing.NewAppModule(cdc, slashingKeeper, accountKeeper, bankKeeper, stakingKeeper, cdc.InterfaceRegistry(), cometInfoService) + consensusModule := consensus.NewAppModule(cdc, consensusParamsKeeper) + + integrationApp := integration.NewIntegrationApp(log.NewNopLogger(), keys, cdc, + encodingCfg.InterfaceRegistry.SigningContext().AddressCodec(), + encodingCfg.InterfaceRegistry.SigningContext().ValidatorAddressCodec(), + map[string]appmodule.AppModule{ + banktypes.ModuleName: bankModule, + stakingtypes.ModuleName: stakingModule, + slashingtypes.ModuleName: slashingModule, + consensustypes.ModuleName: consensusModule, + }, + msgRouter, + queryRouter, + ) + + sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context()) + + // Register MsgServer and QueryServer + slashingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), slashingkeeper.NewMsgServerImpl(slashingKeeper)) + slashingtypes.RegisterQueryServer(integrationApp.QueryHelper(), slashingkeeper.NewQuerier(slashingKeeper)) + + // set default staking params + // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 + err := slashingKeeper.Params.Set(sdkCtx, testutil.TestParams()) + assert.NilError(tb, err) + addrDels := simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, sdkCtx, 6, stakingKeeper.TokensFromConsensusPower(sdkCtx, 200)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) + + consaddr0, err := stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[0]) + assert.NilError(tb, err) + consaddr1, err := stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[1]) + assert.NilError(tb, err) + + info1 := slashingtypes.NewValidatorSigningInfo(consaddr0, int64(4), time.Unix(2, 0), false, int64(10)) + info2 := slashingtypes.NewValidatorSigningInfo(consaddr1, int64(5), time.Unix(2, 0), false, int64(10)) + + err = slashingKeeper.ValidatorSigningInfo.Set(sdkCtx, sdk.ConsAddress(addrDels[0]), info1) + assert.NilError(tb, err) + err = slashingKeeper.ValidatorSigningInfo.Set(sdkCtx, sdk.ConsAddress(addrDels[1]), info2) + assert.NilError(tb, err) + return &fixture{ + app: integrationApp, + ctx: sdkCtx, + accountKeeper: accountKeeper, + bankKeeper: bankKeeper, + slashingKeeper: slashingKeeper, + stakingKeeper: stakingKeeper, + addrDels: addrDels, + valAddrs: valAddrs, + } +} + +func TestUnJailNotBonded(t *testing.T) { + t.Parallel() + f := initFixture(t) + + p, err := f.stakingKeeper.Params.Get(f.ctx) + assert.NilError(t, err) + p.MaxValidators = 5 + assert.NilError(t, f.stakingKeeper.Params.Set(f.ctx, p)) + pks := simtestutil.CreateTestPubKeys(6) + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) + + // create max (5) validators all with the same power + for i := uint32(0); i < p.MaxValidators; i++ { + addr, val := f.valAddrs[i], pks[i] + acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) + f.accountKeeper.SetAccount(f.ctx, acc) + tstaking.CreateValidatorWithValPower(addr, val, 100, true) + } + + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + newHeight := f.ctx.BlockHeight() + 1 + f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + + // create a 6th validator with less power than the cliff validator (won't be bonded) + addr, val := f.valAddrs[5], pks[5] + acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) + f.accountKeeper.SetAccount(f.ctx, acc) + amt := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 50) + msg := tstaking.CreateValidatorMsg(addr, val, amt) + msg.MinSelfDelegation = amt + msg.Description = stakingtypes.Description{Moniker: "TestValidator"} + res, err := tstaking.CreateValidatorWithMsg(f.ctx, msg) + assert.NilError(t, err) + assert.Assert(t, res != nil) + + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + newHeight = f.ctx.BlockHeight() + 1 + f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + + tstaking.CheckValidator(addr, stakingtypes.Unbonded, false) + + // unbond below minimum self-delegation + assert.Equal(t, p.BondDenom, tstaking.Denom) + accAddr, err := f.accountKeeper.AddressCodec().BytesToString(addr) + assert.NilError(t, err) + valAddr, err := f.stakingKeeper.ValidatorAddressCodec().BytesToString(addr) + require.NoError(t, err) + tstaking.Undelegate(accAddr, valAddr, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 1), true) + + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + newHeight = f.ctx.BlockHeight() + 1 + f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + + // verify that validator is jailed + tstaking.CheckValidator(addr, -1, true) + + // verify we cannot unjail (yet) + msgUnjail := slashingtypes.MsgUnjail{ + ValidatorAddr: addr.String(), + } + _, err = f.app.RunMsg( + &msgUnjail, + integration.WithAutomaticFinalizeBlock(), + integration.WithAutomaticCommit(), + ) + assert.ErrorContains(t, err, "cannot be unjailed") + + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + newHeight = f.ctx.BlockHeight() + 1 + f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + // bond to meet minimum self-delegationa + accAddr, err = f.accountKeeper.AddressCodec().BytesToString(addr) + assert.NilError(t, err) + valAddr, err = f.stakingKeeper.ValidatorAddressCodec().BytesToString(addr) + assert.NilError(t, err) + tstaking.DelegateWithPower(accAddr, valAddr, 1) + + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + newHeight = f.ctx.BlockHeight() + 1 + f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + + // verify we can immediately unjail + _, err = f.app.RunMsg( + &msgUnjail, + integration.WithAutomaticFinalizeBlock(), + integration.WithAutomaticCommit(), + ) + assert.NilError(t, err) + + tstaking.CheckValidator(addr, -1, false) +} + +// 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) { + t.Parallel() + f := initFixture(t) + + pks := simtestutil.CreateTestPubKeys(1) + addr, valpubkey := f.valAddrs[0], pks[0] + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) + signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(f.ctx) + assert.NilError(t, err) + f.ctx = f.ctx.WithBlockHeight(signedBlocksWindow + 1).WithHeaderInfo(coreheader.Info{Height: signedBlocksWindow + 1}) + assert.NilError(t, f.slashingKeeper.AddrPubkeyRelation.Set(f.ctx, pks[0].Address(), pks[0])) + + consaddr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(valpubkey.Address()) + assert.NilError(t, err) + + info := slashingtypes.NewValidatorSigningInfo(consaddr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, sdk.ConsAddress(valpubkey.Address()), info)) + assert.Equal(t, signedBlocksWindow+1, info.StartHeight) + + // Validator created + acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) + f.accountKeeper.SetAccount(f.ctx, acc) + amt := tstaking.CreateValidatorWithValPower(addr, valpubkey, 100, true) + + _, err = f.stakingKeeper.EndBlocker(f.ctx) + require.NoError(t, err) + + bondDenom, err := f.stakingKeeper.BondDenom(f.ctx) + require.NoError(t, err) + + assert.DeepEqual( + t, f.bankKeeper.GetAllBalances(f.ctx, sdk.AccAddress(addr)), + sdk.NewCoins(sdk.NewCoin(bondDenom, testutil.InitTokens.Sub(amt))), + ) + + val, err := f.stakingKeeper.Validator(f.ctx, addr) + require.NoError(t, err) + assert.DeepEqual(t, amt, val.GetBondedTokens()) + + // Now a validator, for two blocks + assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, valpubkey.Address(), 100, comet.BlockIDFlagCommit)) + f.ctx = f.ctx.WithBlockHeight(signedBlocksWindow + 2).WithHeaderInfo(coreheader.Info{Height: signedBlocksWindow + 2}) + assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, valpubkey.Address(), 100, comet.BlockIDFlagAbsent)) + + info, found := f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, sdk.ConsAddress(valpubkey.Address())) + assert.Assert(t, found) + assert.Equal(t, signedBlocksWindow+1, info.StartHeight) + assert.Equal(t, int64(1), info.MissedBlocksCounter) + assert.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) + + // validator should be bonded still, should not have been jailed or slashed + validator, _ := f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(valpubkey)) + assert.Equal(t, sdk.Bonded, validator.GetStatus()) + bondPool := f.stakingKeeper.GetBondedPool(f.ctx) + expTokens := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 100) + assert.Assert(t, expTokens.Equal(f.bankKeeper.GetBalance(f.ctx, bondPool.GetAddress(), bondDenom).Amount)) +} + +// Test a jailed validator being "down" twice +// Ensure that they're only slashed once +func TestHandleAlreadyJailed(t *testing.T) { + t.Parallel() + f := initFixture(t) + + pks := simtestutil.CreateTestPubKeys(1) + addr, val := f.valAddrs[0], pks[0] + power := int64(100) + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) + + err := f.slashingKeeper.AddrPubkeyRelation.Set(f.ctx, pks[0].Address(), pks[0]) + assert.NilError(t, err) + + consaddr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(val.Address()) + assert.NilError(t, err) + + info := slashingtypes.NewValidatorSigningInfo(consaddr, f.ctx.HeaderInfo().Height, time.Unix(0, 0), false, int64(0)) + assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, sdk.ConsAddress(val.Address()), info)) + + acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) + f.accountKeeper.SetAccount(f.ctx, acc) + + amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) + + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + + signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(f.ctx) + assert.NilError(t, err) + + // 1000 first blocks OK + height := int64(0) + for ; height < signedBlocksWindow; height++ { + f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) + err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagCommit) + assert.NilError(t, err) + } + + minSignedPerWindow, err := f.slashingKeeper.MinSignedPerWindow(f.ctx) + assert.NilError(t, err) + + // 501 blocks missed + for ; height < signedBlocksWindow+(signedBlocksWindow-minSignedPerWindow)+1; height++ { + f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) + err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagAbsent) + assert.NilError(t, err) + } + + // end block + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + + // validator should have been jailed and slashed + validator, _ := f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(val)) + assert.Equal(t, sdk.Unbonding, validator.GetStatus()) + + // validator should have been slashed + resultingTokens := amt.Sub(f.stakingKeeper.TokensFromConsensusPower(f.ctx, 1)) + assert.DeepEqual(t, resultingTokens, validator.GetTokens()) + + // another block missed + f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) + assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagAbsent)) + + // validator should not have been slashed twice + validator, _ = f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(val)) + assert.DeepEqual(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) { + t.Parallel() + f := initFixture(t) + + params, err := f.stakingKeeper.Params.Get(f.ctx) + require.NoError(t, err) + params.MaxValidators = 1 + err = f.stakingKeeper.Params.Set(f.ctx, params) + assert.NilError(t, err) + power := int64(100) + + pks := simtestutil.CreateTestPubKeys(3) + simtestutil.AddTestAddrsFromPubKeys(f.bankKeeper, f.stakingKeeper, f.ctx, pks, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 200)) + for _, pk := range pks { + acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(pk.Address())) + f.accountKeeper.SetAccount(f.ctx, acc) + } + + addr, val := pks[0].Address(), pks[0] + consAddr := sdk.ConsAddress(addr) + tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) + valAddr := sdk.ValAddress(addr) + + assert.NilError(t, f.slashingKeeper.AddrPubkeyRelation.Set(f.ctx, pks[0].Address(), pks[0])) + + consaddrStr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(addr) + assert.NilError(t, err) + + info := slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info)) + + tstaking.CreateValidatorWithValPower(valAddr, val, power, true) + validatorUpdates, err := f.stakingKeeper.EndBlocker(f.ctx) + require.NoError(t, err) + assert.Equal(t, 1, len(validatorUpdates)) + tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + + // 100 first blocks OK + height := int64(0) + for ; height < int64(100); height++ { + f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagCommit)) + } + + // kick first validator out of validator set + tstaking.CreateValidatorWithValPower(sdk.ValAddress(pks[1].Address()), pks[1], power+1, true) + validatorUpdates, err = f.stakingKeeper.EndBlocker(f.ctx) + require.NoError(t, err) + assert.Equal(t, 2, len(validatorUpdates)) + tstaking.CheckValidator(sdk.ValAddress(pks[1].Address()), stakingtypes.Bonded, false) + tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, false) + + // 600 more blocks happened + height += 600 + f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + + // validator added back in + accAddr, err := f.accountKeeper.AddressCodec().BytesToString(sdk.AccAddress(pks[2].Address())) + assert.NilError(t, err) + vAddr, err := f.stakingKeeper.ValidatorAddressCodec().BytesToString(valAddr) + assert.NilError(t, err) + tstaking.DelegateWithPower(accAddr, vAddr, 50) + + validatorUpdates, err = f.stakingKeeper.EndBlocker(f.ctx) + require.NoError(t, err) + assert.Equal(t, 2, len(validatorUpdates)) + tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + newPower := power + 50 + + // validator misses a block + assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, comet.BlockIDFlagAbsent)) + height++ + + // shouldn't be jailed/kicked yet + tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + + signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(f.ctx) + assert.NilError(t, err) + + // validator misses an additional 500 more blocks within the SignedBlockWindow (here 1000 blocks). + latest := signedBlocksWindow + height + // misses 500 blocks + within the signing windows i.e. 700-1700 + // validators misses all 1000 blocks of a SignedBlockWindows + for ; height < latest+1; height++ { + err = f.slashingKeeper.HandleValidatorSignature(f.ctx.WithHeaderInfo(coreheader.Info{Height: height}), val.Address(), newPower, comet.BlockIDFlagAbsent) + assert.NilError(t, err) + } + + // should now be jailed & kicked + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) + + info = slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + err = f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info) + assert.NilError(t, err) + + // check all the signing information + signInfo, found := f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, consAddr) + assert.Assert(t, found) + assert.Equal(t, int64(700), signInfo.StartHeight) + assert.Equal(t, int64(0), signInfo.MissedBlocksCounter) + assert.Equal(t, int64(0), signInfo.IndexOffset) + + // some blocks pass + height = int64(5000) + f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + + info = slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + err = f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info) + assert.NilError(t, err) + + // validator rejoins and starts signing again + err = f.stakingKeeper.Unjail(f.ctx, consAddr) + assert.NilError(t, err) + err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, comet.BlockIDFlagCommit) + assert.NilError(t, err) + + // validator should not be kicked since we reset counter/array when it was jailed + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) + + // check start height is correctly set + signInfo, found = f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, consAddr) + assert.Assert(t, found) + assert.Equal(t, height, signInfo.StartHeight) + + minSignedPerWindow, err := f.slashingKeeper.MinSignedPerWindow(f.ctx) + assert.NilError(t, err) + + // validator misses 501 blocks after SignedBlockWindow period (1000 blocks) + latest = signedBlocksWindow + height + for ; height < latest+minSignedPerWindow; height++ { + f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, comet.BlockIDFlagAbsent) + assert.NilError(t, err) + } + + // validator should now be jailed & kicked + _, err = f.stakingKeeper.EndBlocker(f.ctx) + assert.NilError(t, err) + + tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) +} diff --git a/tests/integration/v2/slashing/slash_redelegation_test.go b/tests/integration/v2/slashing/slash_redelegation_test.go new file mode 100644 index 000000000000..e40b60783c3e --- /dev/null +++ b/tests/integration/v2/slashing/slash_redelegation_test.go @@ -0,0 +1,502 @@ +package slashing + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "cosmossdk.io/core/header" + "cosmossdk.io/depinject" + "cosmossdk.io/log" + "cosmossdk.io/math" + bankkeeper "cosmossdk.io/x/bank/keeper" + banktestutil "cosmossdk.io/x/bank/testutil" + distributionkeeper "cosmossdk.io/x/distribution/keeper" + slashingkeeper "cosmossdk.io/x/slashing/keeper" + stakingkeeper "cosmossdk.io/x/staking/keeper" + stakingtypes "cosmossdk.io/x/staking/types" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/tests/integration/slashing" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" +) + +func TestSlashRedelegation(t *testing.T) { + // setting up + var ( + authKeeper authkeeper.AccountKeeper + stakingKeeper *stakingkeeper.Keeper + bankKeeper bankkeeper.Keeper + slashKeeper slashingkeeper.Keeper + distrKeeper distributionkeeper.Keeper + ) + + app, err := simtestutil.Setup( + depinject.Configs( + depinject.Supply(log.NewNopLogger()), + slashing.AppConfig, + ), + &stakingKeeper, + &bankKeeper, + &slashKeeper, + &distrKeeper, + &authKeeper, + ) + require.NoError(t, err) + + // get sdk context, staking msg server and bond denom + ctx := app.BaseApp.NewContext(false) + stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) + bondDenom, err := stakingKeeper.BondDenom(ctx) + require.NoError(t, err) + + // evilVal will be slashed, goodVal won't be slashed + evilValPubKey := secp256k1.GenPrivKey().PubKey() + goodValPubKey := secp256k1.GenPrivKey().PubKey() + + // both test acc 1 and 2 delegated to evil val, both acc should be slashed when evil val is slashed + // test acc 1 use the "undelegation after redelegation" trick (redelegate to good val and then undelegate) to avoid slashing + // test acc 2 only undelegate from evil val + testAcc1 := sdk.AccAddress([]byte("addr1_______________")) + testAcc2 := sdk.AccAddress([]byte("addr2_______________")) + + // fund acc 1 and acc 2 + testCoin := sdk.NewCoin(bondDenom, stakingKeeper.TokensFromConsensusPower(ctx, 10)) + fundAccount(t, ctx, bankKeeper, authKeeper, testAcc1, testCoin) + fundAccount(t, ctx, bankKeeper, authKeeper, testAcc2, testCoin) + + balance1Before := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) + balance2Before := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) + + // assert acc 1 and acc 2 balance + require.Equal(t, balance1Before.Amount.String(), testCoin.Amount.String()) + require.Equal(t, balance2Before.Amount.String(), testCoin.Amount.String()) + + // creating evil val + evilValAddr := sdk.ValAddress(evilValPubKey.Address()) + fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(evilValAddr), testCoin) + createValMsg1, _ := stakingtypes.NewMsgCreateValidator( + evilValAddr.String(), evilValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) + _, err = stakingMsgServer.CreateValidator(ctx, createValMsg1) + require.NoError(t, err) + + // creating good val + goodValAddr := sdk.ValAddress(goodValPubKey.Address()) + fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(goodValAddr), testCoin) + createValMsg2, _ := stakingtypes.NewMsgCreateValidator( + goodValAddr.String(), goodValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) + _, err = stakingMsgServer.CreateValidator(ctx, createValMsg2) + require.NoError(t, err) + + ctx = ctx.WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) + // next block, commit height 1, move to height 2 + // acc 1 and acc 2 delegate to evil val + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // Acc 2 delegate + delMsg := stakingtypes.NewMsgDelegate(testAcc2.String(), evilValAddr.String(), testCoin) + _, err = stakingMsgServer.Delegate(ctx, delMsg) + require.NoError(t, err) + + // Acc 1 delegate + delMsg = stakingtypes.NewMsgDelegate(testAcc1.String(), evilValAddr.String(), testCoin) + _, err = stakingMsgServer.Delegate(ctx, delMsg) + require.NoError(t, err) + + // next block, commit height 2, move to height 3 + // with the new delegations, evil val increases in voting power and commit byzantine behavior at height 3 consensus + // at the same time, acc 1 and acc 2 withdraw delegation from evil val + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + evilVal, err := stakingKeeper.GetValidator(ctx, evilValAddr) + require.NoError(t, err) + + evilPower := stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) + + // Acc 1 redelegate from evil val to good val + redelMsg := stakingtypes.NewMsgBeginRedelegate(testAcc1.String(), evilValAddr.String(), goodValAddr.String(), testCoin) + _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) + require.NoError(t, err) + + // Acc 1 undelegate from good val + undelMsg := stakingtypes.NewMsgUndelegate(testAcc1.String(), goodValAddr.String(), testCoin) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + // Acc 2 undelegate from evil val + undelMsg = stakingtypes.NewMsgUndelegate(testAcc2.String(), evilValAddr.String(), testCoin) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + // next block, commit height 3, move to height 4 + // Slash evil val for byzantine behavior at height 3 consensus, + // at which acc 1 and acc 2 still contributed to evil val voting power + // even tho they undelegate at block 3, the valset update is applied after committed block 3 when height 3 consensus already passes + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // slash evil val with slash factor = 0.9, leaving only 10% of stake after slashing + evilVal, _ = stakingKeeper.GetValidator(ctx, evilValAddr) + evilValConsAddr, err := evilVal.GetConsAddr() + require.NoError(t, err) + + err = slashKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr("0.9"), evilPower, 3) + require.NoError(t, err) + + // assert invariant to make sure we conduct slashing correctly + _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) + require.False(t, stop) + + _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) + require.False(t, stop) + + _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) + require.False(t, stop) + + // one eternity later + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1000000000000000000)) + require.NoError(t, err) + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // confirm that account 1 and account 2 has been slashed, and the slash amount is correct + balance1AfterSlashing := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) + balance2AfterSlashing := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) + + require.Equal(t, balance1AfterSlashing.Amount.Mul(math.NewIntFromUint64(10)).String(), balance1Before.Amount.String()) + require.Equal(t, balance2AfterSlashing.Amount.Mul(math.NewIntFromUint64(10)).String(), balance2Before.Amount.String()) +} + +func fundAccount(t *testing.T, ctx context.Context, bankKeeper bankkeeper.Keeper, authKeeper authkeeper.AccountKeeper, addr sdk.AccAddress, amount ...sdk.Coin) { + t.Helper() + + if authKeeper.GetAccount(ctx, addr) == nil { + addrAcc := authKeeper.NewAccountWithAddress(ctx, addr) + authKeeper.SetAccount(ctx, addrAcc) + } + + require.NoError(t, banktestutil.FundAccount(ctx, bankKeeper, addr, amount)) +} + +func TestOverSlashing(t *testing.T) { + // slash penalty percentage + slashFraction := "0.45" + + // percentage of (undelegation/(undelegation + redelegation)) + undelegationPercentageStr := "0.30" + + // setting up + var ( + stakingKeeper *stakingkeeper.Keeper + bankKeeper bankkeeper.Keeper + slashKeeper slashingkeeper.Keeper + distrKeeper distributionkeeper.Keeper + authKeeper authkeeper.AccountKeeper + ) + + app, err := simtestutil.Setup(depinject.Configs( + depinject.Supply(log.NewNopLogger()), + slashing.AppConfig, + ), &stakingKeeper, &bankKeeper, &slashKeeper, &distrKeeper, &authKeeper) + require.NoError(t, err) + + // get sdk context, staking msg server and bond denom + ctx := app.BaseApp.NewContext(false) + stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) + bondDenom, err := stakingKeeper.BondDenom(ctx) + require.NoError(t, err) + + // evilVal will be slashed, goodVal won't be slashed + evilValPubKey := secp256k1.GenPrivKey().PubKey() + goodValPubKey := secp256k1.GenPrivKey().PubKey() + + /* + all test accs will delegate to evil val, which evil validator will eventually be slashed + + - test acc 1: redelegate -> undelegate full amount + - test acc 2: simple undelegation. intended scenario. + - test acc 3: redelegate -> undelegate some amount + + */ + + testAcc1 := sdk.AccAddress([]byte("addr1new____________")) + testAcc2 := sdk.AccAddress([]byte("addr2new____________")) + testAcc3 := sdk.AccAddress([]byte("addr3new____________")) + + // fund all accounts + testCoin := sdk.NewCoin(bondDenom, math.NewInt(1_000_000)) + fundAccount(t, ctx, bankKeeper, authKeeper, testAcc1, testCoin) + fundAccount(t, ctx, bankKeeper, authKeeper, testAcc2, testCoin) + fundAccount(t, ctx, bankKeeper, authKeeper, testAcc3, testCoin) + + balance1Before := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) + balance2Before := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) + balance3Before := bankKeeper.GetBalance(ctx, testAcc3, bondDenom) + + // assert acc 1, 2 and 3 balance + require.Equal(t, testCoin.Amount.String(), balance1Before.Amount.String()) + require.Equal(t, testCoin.Amount.String(), balance2Before.Amount.String()) + require.Equal(t, testCoin.Amount.String(), balance3Before.Amount.String()) + + // create evil val + evilValAddr := sdk.ValAddress(evilValPubKey.Address()) + fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(evilValAddr), testCoin) + createValMsg1, _ := stakingtypes.NewMsgCreateValidator( + evilValAddr.String(), evilValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) + _, err = stakingMsgServer.CreateValidator(ctx, createValMsg1) + require.NoError(t, err) + + // create good val 1 + goodValAddr := sdk.ValAddress(goodValPubKey.Address()) + fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(goodValAddr), testCoin) + createValMsg2, _ := stakingtypes.NewMsgCreateValidator( + goodValAddr.String(), goodValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) + _, err = stakingMsgServer.CreateValidator(ctx, createValMsg2) + require.NoError(t, err) + + // next block + ctx = ctx.WithBlockHeight(app.LastBlockHeight() + 1).WithHeaderInfo(header.Info{Height: app.LastBlockHeight() + 1}) + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // delegate all accs to evil val + delMsg := stakingtypes.NewMsgDelegate(testAcc1.String(), evilValAddr.String(), testCoin) + _, err = stakingMsgServer.Delegate(ctx, delMsg) + require.NoError(t, err) + + delMsg = stakingtypes.NewMsgDelegate(testAcc2.String(), evilValAddr.String(), testCoin) + _, err = stakingMsgServer.Delegate(ctx, delMsg) + require.NoError(t, err) + + delMsg = stakingtypes.NewMsgDelegate(testAcc3.String(), evilValAddr.String(), testCoin) + _, err = stakingMsgServer.Delegate(ctx, delMsg) + require.NoError(t, err) + + // next block + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // evilValAddr done something bad + misbehaveHeight := ctx.BlockHeader().Height + evilVal, err := stakingKeeper.GetValidator(ctx, evilValAddr) + require.NoError(t, err) + + evilValConsAddr, err := evilVal.GetConsAddr() + require.NoError(t, err) + + evilPower := stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) + + // next block + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // acc 1: redelegate to goodval1 and undelegate FULL amount + redelMsg := stakingtypes.NewMsgBeginRedelegate(testAcc1.String(), evilValAddr.String(), goodValAddr.String(), testCoin) + _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) + require.NoError(t, err) + undelMsg := stakingtypes.NewMsgUndelegate(testAcc1.String(), goodValAddr.String(), testCoin) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + // acc 2: undelegate full amount + undelMsg = stakingtypes.NewMsgUndelegate(testAcc2.String(), evilValAddr.String(), testCoin) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + // acc 3: redelegate to goodval1 and undelegate some amount + redelMsg = stakingtypes.NewMsgBeginRedelegate(testAcc3.String(), evilValAddr.String(), goodValAddr.String(), testCoin) + _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) + require.NoError(t, err) + + undelegationPercentage := math.LegacyMustNewDecFromStr(undelegationPercentageStr) + undelegationAmountDec := math.LegacyNewDecFromInt(testCoin.Amount).Mul(undelegationPercentage) + amountToUndelegate := undelegationAmountDec.TruncateInt() + + // next block + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + portionofTestCoins := sdk.NewCoin(bondDenom, amountToUndelegate) + undelMsg = stakingtypes.NewMsgUndelegate(testAcc3.String(), goodValAddr.String(), portionofTestCoins) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + // next block + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // slash the evil val + err = slashKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr(slashFraction), evilPower, misbehaveHeight) + require.NoError(t, err) + + // assert invariants + _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) + require.False(t, stop) + _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) + require.False(t, stop) + _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) + require.False(t, stop) + + // fastforward 2 blocks to complete redelegations and unbondings + for i := 0; i < 2; i++ { + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1000000000000000000)) + require.NoError(t, err) + } + + // we check all accounts should be slashed with the equal amount, and they should end up with same balance including staked amount + stakedAcc1, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc1) + require.NoError(t, err) + stakedAcc2, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc2) + require.NoError(t, err) + stakedAcc3, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc3) + require.NoError(t, err) + + balance1AfterSlashing := bankKeeper.GetBalance(ctx, testAcc1, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc1)) + balance2AfterSlashing := bankKeeper.GetBalance(ctx, testAcc2, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc2)) + balance3AfterSlashing := bankKeeper.GetBalance(ctx, testAcc3, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc3)) + + require.Equal(t, "550000stake", balance1AfterSlashing.String()) + require.Equal(t, "550000stake", balance2AfterSlashing.String()) + require.Equal(t, "550000stake", balance3AfterSlashing.String()) +} + +func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { + // setting up + var ( + authKeeper authkeeper.AccountKeeper + stakingKeeper *stakingkeeper.Keeper + bankKeeper bankkeeper.Keeper + slashKeeper slashingkeeper.Keeper + distrKeeper distributionkeeper.Keeper + ) + + app, err := simtestutil.Setup( + depinject.Configs( + depinject.Supply(log.NewNopLogger()), + slashing.AppConfig, + ), + &stakingKeeper, + &bankKeeper, + &slashKeeper, + &distrKeeper, + &authKeeper, + ) + require.NoError(t, err) + + // get sdk context, staking msg server and bond denom + ctx := app.BaseApp.NewContext(false).WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) + stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) + bondDenom, err := stakingKeeper.BondDenom(ctx) + require.NoError(t, err) + + // create validators DST and SRC + dstPubKey := secp256k1.GenPrivKey().PubKey() + srcPubKey := secp256k1.GenPrivKey().PubKey() + + dstAddr := sdk.ValAddress(dstPubKey.Address()) + srcAddr := sdk.ValAddress(srcPubKey.Address()) + + testCoin := sdk.NewCoin(bondDenom, stakingKeeper.TokensFromConsensusPower(ctx, 1000)) + fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(dstAddr), testCoin) + fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(srcAddr), testCoin) + + createValMsgDST, _ := stakingtypes.NewMsgCreateValidator( + dstAddr.String(), dstPubKey, testCoin, stakingtypes.Description{Details: "Validator DST"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) + _, err = stakingMsgServer.CreateValidator(ctx, createValMsgDST) + require.NoError(t, err) + + createValMsgSRC, _ := stakingtypes.NewMsgCreateValidator( + srcAddr.String(), srcPubKey, testCoin, stakingtypes.Description{Details: "Validator SRC"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) + _, err = stakingMsgServer.CreateValidator(ctx, createValMsgSRC) + require.NoError(t, err) + + // create a user accounts and delegate to SRC and DST + userAcc := sdk.AccAddress([]byte("user1_______________")) + fundAccount(t, ctx, bankKeeper, authKeeper, userAcc, testCoin) + + userAcc2 := sdk.AccAddress([]byte("user2_______________")) + fundAccount(t, ctx, bankKeeper, authKeeper, userAcc2, testCoin) + + delMsg := stakingtypes.NewMsgDelegate(userAcc.String(), srcAddr.String(), testCoin) + _, err = stakingMsgServer.Delegate(ctx, delMsg) + require.NoError(t, err) + + delMsg = stakingtypes.NewMsgDelegate(userAcc2.String(), dstAddr.String(), testCoin) + _, err = stakingMsgServer.Delegate(ctx, delMsg) + require.NoError(t, err) + + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // commit an infraction with DST and store the power at this height + dstVal, err := stakingKeeper.GetValidator(ctx, dstAddr) + require.NoError(t, err) + dstPower := stakingKeeper.TokensToConsensusPower(ctx, dstVal.Tokens) + dstConsAddr, err := dstVal.GetConsAddr() + require.NoError(t, err) + dstInfractionHeight := ctx.BlockHeight() + + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // undelegate all the user tokens from DST + undelMsg := stakingtypes.NewMsgUndelegate(userAcc2.String(), dstAddr.String(), testCoin) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + // commit an infraction with SRC and store the power at this height + srcVal, err := stakingKeeper.GetValidator(ctx, srcAddr) + require.NoError(t, err) + srcPower := stakingKeeper.TokensToConsensusPower(ctx, srcVal.Tokens) + srcConsAddr, err := srcVal.GetConsAddr() + require.NoError(t, err) + srcInfractionHeight := ctx.BlockHeight() + + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + // redelegate all the user tokens from SRC to DST + redelMsg := stakingtypes.NewMsgBeginRedelegate(userAcc.String(), srcAddr.String(), dstAddr.String(), testCoin) + _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) + require.NoError(t, err) + + // undelegate the self delegation from DST + undelMsg = stakingtypes.NewMsgUndelegate(sdk.AccAddress(dstAddr).String(), dstAddr.String(), testCoin) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + require.NoError(t, err) + + undelMsg = stakingtypes.NewMsgUndelegate(userAcc.String(), dstAddr.String(), testCoin) + _, err = stakingMsgServer.Undelegate(ctx, undelMsg) + require.NoError(t, err) + + // check that dst now has zero tokens + valDst, err := stakingKeeper.GetValidator(ctx, dstAddr) + require.NoError(t, err) + require.Equal(t, math.ZeroInt().String(), valDst.Tokens.String()) + + // slash the infractions + err = slashKeeper.Slash(ctx, dstConsAddr, math.LegacyMustNewDecFromStr("0.8"), dstPower, dstInfractionHeight) + require.NoError(t, err) + + err = slashKeeper.Slash(ctx, srcConsAddr, math.LegacyMustNewDecFromStr("0.5"), srcPower, srcInfractionHeight) + require.NoError(t, err) + + // assert invariants to ensure correctness + _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) + require.False(t, stop) + + _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) + require.False(t, stop) + + _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) + require.False(t, stop) +} diff --git a/tests/integration/v2/slashing/slashing_test.go b/tests/integration/v2/slashing/slashing_test.go new file mode 100644 index 000000000000..c6be2642027e --- /dev/null +++ b/tests/integration/v2/slashing/slashing_test.go @@ -0,0 +1,119 @@ +package slashing + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + + "cosmossdk.io/core/header" + "cosmossdk.io/depinject" + "cosmossdk.io/log" + "cosmossdk.io/math" + bankkeeper "cosmossdk.io/x/bank/keeper" + "cosmossdk.io/x/slashing/keeper" + "cosmossdk.io/x/slashing/types" + stakingkeeper "cosmossdk.io/x/staking/keeper" + stakingtypes "cosmossdk.io/x/staking/types" + + "github.com/cosmos/cosmos-sdk/client" + codecaddress "github.com/cosmos/cosmos-sdk/codec/address" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/testutil/configurator" + "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +var ( + priv1 = secp256k1.GenPrivKey() + addr1 = sdk.AccAddress(priv1.PubKey().Address()) + addrCodec = codecaddress.NewBech32Codec("cosmos") + valaddrCodec = codecaddress.NewBech32Codec("cosmosvaloper") + + valKey = ed25519.GenPrivKey() + valAddr = sdk.AccAddress(valKey.PubKey().Address()) +) + +func TestSlashingMsgs(t *testing.T) { + genTokens := sdk.TokensFromConsensusPower(42, sdk.DefaultPowerReduction) + bondTokens := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) + genCoin := sdk.NewCoin(sdk.DefaultBondDenom, genTokens) + bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, bondTokens) + + addrStr, err := addrCodec.BytesToString(addr1) + require.NoError(t, err) + acc1 := &authtypes.BaseAccount{ + Address: addrStr, + } + accs := []sims.GenesisAccount{{GenesisAccount: acc1, Coins: sdk.Coins{genCoin}}} + + startupCfg := sims.DefaultStartUpConfig() + startupCfg.GenesisAccounts = accs + + var ( + stakingKeeper *stakingkeeper.Keeper + bankKeeper bankkeeper.Keeper + slashingKeeper keeper.Keeper + txConfig client.TxConfig + ) + + app, err := sims.SetupWithConfiguration( + depinject.Configs( + configurator.NewAppConfig( + configurator.AccountsModule(), + configurator.AuthModule(), + configurator.StakingModule(), + configurator.SlashingModule(), + configurator.TxModule(), + configurator.ValidateModule(), + configurator.ConsensusModule(), + configurator.BankModule(), + ), + depinject.Supply(log.NewNopLogger()), + ), + startupCfg, &stakingKeeper, &bankKeeper, &slashingKeeper, &txConfig) + require.NoError(t, err) + + baseApp := app.BaseApp + + ctxCheck := baseApp.NewContext(true) + require.True(t, sdk.Coins{genCoin}.Equal(bankKeeper.GetAllBalances(ctxCheck, addr1))) + + require.NoError(t, err) + + description := stakingtypes.NewDescription("foo_moniker", "", "", "", "", &stakingtypes.Metadata{}) + commission := stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()) + + addrStrVal, err := valaddrCodec.BytesToString(addr1) + require.NoError(t, err) + createValidatorMsg, err := stakingtypes.NewMsgCreateValidator( + addrStrVal, valKey.PubKey(), bondCoin, description, commission, math.OneInt(), + ) + require.NoError(t, err) + + headerInfo := header.Info{Height: app.LastBlockHeight() + 1} + _, _, err = sims.SignCheckDeliver(t, txConfig, app.BaseApp, headerInfo, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1) + require.NoError(t, err) + require.True(t, sdk.Coins{genCoin.Sub(bondCoin)}.Equal(bankKeeper.GetAllBalances(ctxCheck, addr1))) + + ctxCheck = baseApp.NewContext(true) + validator, err := stakingKeeper.GetValidator(ctxCheck, sdk.ValAddress(addr1)) + require.NoError(t, err) + + require.Equal(t, addrStrVal, validator.OperatorAddress) + require.Equal(t, stakingtypes.Bonded, validator.Status) + require.True(math.IntEq(t, bondTokens, validator.BondedTokens())) + unjailMsg := &types.MsgUnjail{ValidatorAddr: addrStrVal} + + ctxCheck = app.BaseApp.NewContext(true) + _, err = slashingKeeper.ValidatorSigningInfo.Get(ctxCheck, sdk.ConsAddress(valAddr)) + require.NoError(t, err) + + // unjail should fail with unknown validator + headerInfo = header.Info{Height: app.LastBlockHeight() + 1} + _, _, err = sims.SignCheckDeliver(t, txConfig, app.BaseApp, headerInfo, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, false, false, priv1) + require.Error(t, err) + require.True(t, errors.Is(err, types.ErrValidatorNotJailed)) +} From 8b50778c2302bbbb4b845dce9a761d7f723ad1b6 Mon Sep 17 00:00:00 2001 From: akhilkumarpilli Date: Wed, 4 Dec 2024 16:51:53 +0530 Subject: [PATCH 2/5] fix few tests --- tests/integration/v2/slashing/keeper_test.go | 273 ++++++++---------- .../integration/v2/slashing/slashing_test.go | 98 +++---- testutil/sims/address_helpers.go | 9 +- 3 files changed, 159 insertions(+), 221 deletions(-) diff --git a/tests/integration/v2/slashing/keeper_test.go b/tests/integration/v2/slashing/keeper_test.go index 03594413f0e7..8471c4fe355b 100644 --- a/tests/integration/v2/slashing/keeper_test.go +++ b/tests/integration/v2/slashing/keeper_test.go @@ -6,171 +6,122 @@ import ( "time" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" "gotest.tools/v3/assert" - "cosmossdk.io/core/appmodule" "cosmossdk.io/core/comet" coreheader "cosmossdk.io/core/header" + "cosmossdk.io/core/transaction" + "cosmossdk.io/depinject" "cosmossdk.io/log" - storetypes "cosmossdk.io/store/types" - "cosmossdk.io/x/bank" bankkeeper "cosmossdk.io/x/bank/keeper" - banktypes "cosmossdk.io/x/bank/types" - "cosmossdk.io/x/consensus" - consensusparamkeeper "cosmossdk.io/x/consensus/keeper" - consensustypes "cosmossdk.io/x/consensus/types" - minttypes "cosmossdk.io/x/mint/types" - "cosmossdk.io/x/slashing" slashingkeeper "cosmossdk.io/x/slashing/keeper" "cosmossdk.io/x/slashing/testutil" slashingtypes "cosmossdk.io/x/slashing/types" - "cosmossdk.io/x/staking" stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtestutil "cosmossdk.io/x/staking/testutil" stakingtypes "cosmossdk.io/x/staking/types" - "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" - addresscodec "github.com/cosmos/cosmos-sdk/codec/address" - codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil" - "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/testutil/integration" + "github.com/cosmos/cosmos-sdk/tests/integration/v2" + "github.com/cosmos/cosmos-sdk/testutil/configurator" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/cosmos/cosmos-sdk/x/auth" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + _ "cosmossdk.io/x/accounts" // import as blank for app wiring + _ "cosmossdk.io/x/bank" // import as blank for app wiring + _ "cosmossdk.io/x/consensus" // import as blank for app wiring + _ "cosmossdk.io/x/distribution" // import as blank for app wiring + _ "cosmossdk.io/x/mint" // import as blank for app wiring + _ "cosmossdk.io/x/protocolpool" // import as blank for app wiring + _ "cosmossdk.io/x/slashing" // import as blank for app wiring + _ "cosmossdk.io/x/staking" // import as blank for app wiring + + _ "github.com/cosmos/cosmos-sdk/x/auth" // import as blank for app wiring + _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import as blank for app wiring + _ "github.com/cosmos/cosmos-sdk/x/genutil" // import as blank for app wiring ) type fixture struct { app *integration.App - ctx sdk.Context + ctx context.Context + cdc codec.Codec accountKeeper authkeeper.AccountKeeper bankKeeper bankkeeper.Keeper slashingKeeper slashingkeeper.Keeper stakingKeeper *stakingkeeper.Keeper + slashingMsgServer slashingtypes.MsgServer + txConfig client.TxConfig + addrDels []sdk.AccAddress valAddrs []sdk.ValAddress } -func initFixture(tb testing.TB) *fixture { - tb.Helper() - keys := storetypes.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, slashingtypes.StoreKey, stakingtypes.StoreKey, consensustypes.StoreKey, - ) - encodingCfg := moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{}, auth.AppModule{}) - cdc := encodingCfg.Codec - - authority := authtypes.NewModuleAddress("gov") - - maccPerms := map[string][]string{ - minttypes.ModuleName: {authtypes.Minter}, - stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, - stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, +func initFixture(t *testing.T) *fixture { + t.Helper() + + res := fixture{} + + moduleConfigs := []configurator.ModuleOption{ + configurator.AccountsModule(), + configurator.AuthModule(), + configurator.BankModule(), + configurator.StakingModule(), + configurator.SlashingModule(), + configurator.TxModule(), + configurator.ValidateModule(), + configurator.ConsensusModule(), + configurator.GenutilModule(), + configurator.MintModule(), + configurator.DistributionModule(), + configurator.ProtocolPoolModule(), } - msgRouter := baseapp.NewMsgServiceRouter() - queryRouter := baseapp.NewGRPCQueryRouter() - - // gomock initializations - ctrl := gomock.NewController(tb) - acctsModKeeper := authtestutil.NewMockAccountsModKeeper(ctrl) - accNum := uint64(0) - acctsModKeeper.EXPECT().NextAccountNumber(gomock.Any()).AnyTimes().DoAndReturn(func(ctx context.Context) (uint64, error) { - currentNum := accNum - accNum++ - return currentNum, nil - }) - - accountKeeper := authkeeper.NewAccountKeeper( - runtime.NewEnvironment(runtime.NewKVStoreService(keys[authtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), - cdc, - authtypes.ProtoBaseAccount, - acctsModKeeper, - maccPerms, - addresscodec.NewBech32Codec(sdk.Bech32MainPrefix), - sdk.Bech32MainPrefix, - authority.String(), - ) - - blockedAddresses := map[string]bool{ - accountKeeper.GetAuthority(): false, - } - bankKeeper := bankkeeper.NewBaseKeeper( - runtime.NewEnvironment(runtime.NewKVStoreService(keys[banktypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), - cdc, - accountKeeper, - blockedAddresses, - authority.String(), - ) - - cometInfoService := runtime.NewContextAwareCometInfoService() + var err error + startupCfg := integration.DefaultStartUpConfig(t) - consensusParamsKeeper := consensusparamkeeper.NewKeeper(cdc, runtime.NewEnvironment(runtime.NewKVStoreService(keys[consensustypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), authtypes.NewModuleAddress("gov").String()) + startupCfg.BranchService = &integration.BranchService{} + startupCfg.HeaderService = &integration.HeaderService{} - stakingKeeper := stakingkeeper.NewKeeper(cdc, runtime.NewEnvironment(runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), accountKeeper, bankKeeper, consensusParamsKeeper, authority.String(), addresscodec.NewBech32Codec(sdk.Bech32PrefixValAddr), addresscodec.NewBech32Codec(sdk.Bech32PrefixConsAddr), cometInfoService) - - slashingKeeper := slashingkeeper.NewKeeper(runtime.NewEnvironment(runtime.NewKVStoreService(keys[slashingtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), cdc, &codec.LegacyAmino{}, stakingKeeper, authority.String()) - - bankModule := bank.NewAppModule(cdc, bankKeeper, accountKeeper) - stakingModule := staking.NewAppModule(cdc, stakingKeeper) - slashingModule := slashing.NewAppModule(cdc, slashingKeeper, accountKeeper, bankKeeper, stakingKeeper, cdc.InterfaceRegistry(), cometInfoService) - consensusModule := consensus.NewAppModule(cdc, consensusParamsKeeper) - - integrationApp := integration.NewIntegrationApp(log.NewNopLogger(), keys, cdc, - encodingCfg.InterfaceRegistry.SigningContext().AddressCodec(), - encodingCfg.InterfaceRegistry.SigningContext().ValidatorAddressCodec(), - map[string]appmodule.AppModule{ - banktypes.ModuleName: bankModule, - stakingtypes.ModuleName: stakingModule, - slashingtypes.ModuleName: slashingModule, - consensustypes.ModuleName: consensusModule, - }, - msgRouter, - queryRouter, - ) - - sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context()) + res.app, err = integration.NewApp( + depinject.Configs(configurator.NewAppV2Config(moduleConfigs...), depinject.Supply(log.NewNopLogger())), + startupCfg, + &res.bankKeeper, &res.accountKeeper, &res.stakingKeeper, &res.slashingKeeper, &res.cdc, &res.txConfig) + require.NoError(t, err) - // Register MsgServer and QueryServer - slashingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), slashingkeeper.NewMsgServerImpl(slashingKeeper)) - slashingtypes.RegisterQueryServer(integrationApp.QueryHelper(), slashingkeeper.NewQuerier(slashingKeeper)) + res.ctx = res.app.StateLatestContext(t) // set default staking params // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 - err := slashingKeeper.Params.Set(sdkCtx, testutil.TestParams()) - assert.NilError(tb, err) - addrDels := simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, sdkCtx, 6, stakingKeeper.TokensFromConsensusPower(sdkCtx, 200)) + err = res.slashingKeeper.Params.Set(res.ctx, testutil.TestParams()) + assert.NilError(t, err) + + addrDels := simtestutil.AddTestAddrsIncremental(res.bankKeeper, res.stakingKeeper, res.ctx, 6, res.stakingKeeper.TokensFromConsensusPower(res.ctx, 200)) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) - consaddr0, err := stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[0]) - assert.NilError(tb, err) - consaddr1, err := stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[1]) - assert.NilError(tb, err) + consaddr0, err := res.stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[0]) + require.NoError(t, err) + consaddr1, err := res.stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[1]) + require.NoError(t, err) info1 := slashingtypes.NewValidatorSigningInfo(consaddr0, int64(4), time.Unix(2, 0), false, int64(10)) info2 := slashingtypes.NewValidatorSigningInfo(consaddr1, int64(5), time.Unix(2, 0), false, int64(10)) - err = slashingKeeper.ValidatorSigningInfo.Set(sdkCtx, sdk.ConsAddress(addrDels[0]), info1) - assert.NilError(tb, err) - err = slashingKeeper.ValidatorSigningInfo.Set(sdkCtx, sdk.ConsAddress(addrDels[1]), info2) - assert.NilError(tb, err) - return &fixture{ - app: integrationApp, - ctx: sdkCtx, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, - slashingKeeper: slashingKeeper, - stakingKeeper: stakingKeeper, - addrDels: addrDels, - valAddrs: valAddrs, - } + err = res.slashingKeeper.ValidatorSigningInfo.Set(res.ctx, sdk.ConsAddress(addrDels[0]), info1) + require.NoError(t, err) + err = res.slashingKeeper.ValidatorSigningInfo.Set(res.ctx, sdk.ConsAddress(addrDels[1]), info2) + require.NoError(t, err) + + res.addrDels = addrDels + res.valAddrs = valAddrs + + res.slashingMsgServer = slashingkeeper.NewMsgServerImpl(res.slashingKeeper) + + return &res } func TestUnJailNotBonded(t *testing.T) { @@ -194,8 +145,8 @@ func TestUnJailNotBonded(t *testing.T) { _, err = f.stakingKeeper.EndBlocker(f.ctx) assert.NilError(t, err) - newHeight := f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + newHeight := int64(f.app.LastBlockHeight()) + 1 + 1 + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: newHeight}) // create a 6th validator with less power than the cliff validator (won't be bonded) addr, val := f.valAddrs[5], pks[5] @@ -211,8 +162,8 @@ func TestUnJailNotBonded(t *testing.T) { _, err = f.stakingKeeper.EndBlocker(f.ctx) assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + newHeight = integration.HeaderInfoFromContext(f.ctx).Height + 1 + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: newHeight}) tstaking.CheckValidator(addr, stakingtypes.Unbonded, false) @@ -226,8 +177,8 @@ func TestUnJailNotBonded(t *testing.T) { _, err = f.stakingKeeper.EndBlocker(f.ctx) assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + newHeight = integration.HeaderInfoFromContext(f.ctx).Height + 1 + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: newHeight}) // verify that validator is jailed tstaking.CheckValidator(addr, -1, true) @@ -237,16 +188,20 @@ func TestUnJailNotBonded(t *testing.T) { ValidatorAddr: addr.String(), } _, err = f.app.RunMsg( - &msgUnjail, - integration.WithAutomaticFinalizeBlock(), + t, + f.ctx, + func(ctx context.Context) (transaction.Msg, error) { + res, err := f.slashingMsgServer.Unjail(ctx, &msgUnjail) + return res, err + }, integration.WithAutomaticCommit(), ) assert.ErrorContains(t, err, "cannot be unjailed") _, err = f.stakingKeeper.EndBlocker(f.ctx) assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + newHeight = integration.HeaderInfoFromContext(f.ctx).Height + 1 + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: newHeight}) // bond to meet minimum self-delegationa accAddr, err = f.accountKeeper.AddressCodec().BytesToString(addr) assert.NilError(t, err) @@ -256,13 +211,17 @@ func TestUnJailNotBonded(t *testing.T) { _, err = f.stakingKeeper.EndBlocker(f.ctx) assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) + newHeight = integration.HeaderInfoFromContext(f.ctx).Height + 1 + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: newHeight}) // verify we can immediately unjail _, err = f.app.RunMsg( - &msgUnjail, - integration.WithAutomaticFinalizeBlock(), + t, + f.ctx, + func(ctx context.Context) (transaction.Msg, error) { + res, err := f.slashingMsgServer.Unjail(ctx, &msgUnjail) + return res, err + }, integration.WithAutomaticCommit(), ) assert.NilError(t, err) @@ -277,18 +236,25 @@ func TestHandleNewValidator(t *testing.T) { t.Parallel() f := initFixture(t) + bondDenom, err := f.stakingKeeper.BondDenom(f.ctx) + require.NoError(t, err) + + bondPool := f.stakingKeeper.GetBondedPool(f.ctx) + + initialBondPoolBal := f.bankKeeper.GetBalance(f.ctx, bondPool.GetAddress(), bondDenom).Amount + pks := simtestutil.CreateTestPubKeys(1) addr, valpubkey := f.valAddrs[0], pks[0] tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(f.ctx) assert.NilError(t, err) - f.ctx = f.ctx.WithBlockHeight(signedBlocksWindow + 1).WithHeaderInfo(coreheader.Info{Height: signedBlocksWindow + 1}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: signedBlocksWindow + 1}) assert.NilError(t, f.slashingKeeper.AddrPubkeyRelation.Set(f.ctx, pks[0].Address(), pks[0])) consaddr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(valpubkey.Address()) assert.NilError(t, err) - info := slashingtypes.NewValidatorSigningInfo(consaddr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + info := slashingtypes.NewValidatorSigningInfo(consaddr, integration.HeaderInfoFromContext(f.ctx).Height, time.Unix(0, 0), false, int64(0)) assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, sdk.ConsAddress(valpubkey.Address()), info)) assert.Equal(t, signedBlocksWindow+1, info.StartHeight) @@ -300,9 +266,6 @@ func TestHandleNewValidator(t *testing.T) { _, err = f.stakingKeeper.EndBlocker(f.ctx) require.NoError(t, err) - bondDenom, err := f.stakingKeeper.BondDenom(f.ctx) - require.NoError(t, err) - assert.DeepEqual( t, f.bankKeeper.GetAllBalances(f.ctx, sdk.AccAddress(addr)), sdk.NewCoins(sdk.NewCoin(bondDenom, testutil.InitTokens.Sub(amt))), @@ -314,7 +277,7 @@ func TestHandleNewValidator(t *testing.T) { // Now a validator, for two blocks assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, valpubkey.Address(), 100, comet.BlockIDFlagCommit)) - f.ctx = f.ctx.WithBlockHeight(signedBlocksWindow + 2).WithHeaderInfo(coreheader.Info{Height: signedBlocksWindow + 2}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: signedBlocksWindow + 2}) assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, valpubkey.Address(), 100, comet.BlockIDFlagAbsent)) info, found := f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, sdk.ConsAddress(valpubkey.Address())) @@ -326,8 +289,7 @@ func TestHandleNewValidator(t *testing.T) { // validator should be bonded still, should not have been jailed or slashed validator, _ := f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(valpubkey)) assert.Equal(t, sdk.Bonded, validator.GetStatus()) - bondPool := f.stakingKeeper.GetBondedPool(f.ctx) - expTokens := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 100) + expTokens := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 100).Add(initialBondPoolBal) assert.Assert(t, expTokens.Equal(f.bankKeeper.GetBalance(f.ctx, bondPool.GetAddress(), bondDenom).Amount)) } @@ -348,7 +310,7 @@ func TestHandleAlreadyJailed(t *testing.T) { consaddr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(val.Address()) assert.NilError(t, err) - info := slashingtypes.NewValidatorSigningInfo(consaddr, f.ctx.HeaderInfo().Height, time.Unix(0, 0), false, int64(0)) + info := slashingtypes.NewValidatorSigningInfo(consaddr, integration.HeaderInfoFromContext(f.ctx).Height, time.Unix(0, 0), false, int64(0)) assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, sdk.ConsAddress(val.Address()), info)) acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) @@ -365,7 +327,7 @@ func TestHandleAlreadyJailed(t *testing.T) { // 1000 first blocks OK height := int64(0) for ; height < signedBlocksWindow; height++ { - f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}) err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagCommit) assert.NilError(t, err) } @@ -375,7 +337,7 @@ func TestHandleAlreadyJailed(t *testing.T) { // 501 blocks missed for ; height < signedBlocksWindow+(signedBlocksWindow-minSignedPerWindow)+1; height++ { - f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}) err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagAbsent) assert.NilError(t, err) } @@ -393,7 +355,7 @@ func TestHandleAlreadyJailed(t *testing.T) { assert.DeepEqual(t, resultingTokens, validator.GetTokens()) // another block missed - f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}) assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagAbsent)) // validator should not have been slashed twice @@ -432,19 +394,20 @@ func TestValidatorDippingInAndOut(t *testing.T) { consaddrStr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(addr) assert.NilError(t, err) - info := slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + info := slashingtypes.NewValidatorSigningInfo(consaddrStr, integration.HeaderInfoFromContext(f.ctx).Height, time.Unix(0, 0), false, int64(0)) assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info)) tstaking.CreateValidatorWithValPower(valAddr, val, power, true) validatorUpdates, err := f.stakingKeeper.EndBlocker(f.ctx) require.NoError(t, err) - assert.Equal(t, 1, len(validatorUpdates)) + // validator updates length should be equal to 2 as we already have one default validator + assert.Equal(t, 2, len(validatorUpdates)) tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) // 100 first blocks OK height := int64(0) for ; height < int64(100); height++ { - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}) assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagCommit)) } @@ -458,8 +421,10 @@ func TestValidatorDippingInAndOut(t *testing.T) { // 600 more blocks happened height += 600 - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}) + // store this height as we don't have block height value in context + startHeight := height // validator added back in accAddr, err := f.accountKeeper.AddressCodec().BytesToString(sdk.AccAddress(pks[2].Address())) assert.NilError(t, err) @@ -488,7 +453,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { // misses 500 blocks + within the signing windows i.e. 700-1700 // validators misses all 1000 blocks of a SignedBlockWindows for ; height < latest+1; height++ { - err = f.slashingKeeper.HandleValidatorSignature(f.ctx.WithHeaderInfo(coreheader.Info{Height: height}), val.Address(), newPower, comet.BlockIDFlagAbsent) + err = f.slashingKeeper.HandleValidatorSignature(integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}), val.Address(), newPower, comet.BlockIDFlagAbsent) assert.NilError(t, err) } @@ -497,7 +462,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { assert.NilError(t, err) tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) - info = slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + info = slashingtypes.NewValidatorSigningInfo(consaddrStr, startHeight, time.Unix(0, 0), false, int64(0)) err = f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info) assert.NilError(t, err) @@ -510,9 +475,9 @@ func TestValidatorDippingInAndOut(t *testing.T) { // some blocks pass height = int64(5000) - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}) - info = slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) + info = slashingtypes.NewValidatorSigningInfo(consaddrStr, integration.HeaderInfoFromContext(f.ctx).Height, time.Unix(0, 0), false, int64(0)) err = f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info) assert.NilError(t, err) @@ -538,7 +503,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { // validator misses 501 blocks after SignedBlockWindow period (1000 blocks) latest = signedBlocksWindow + height for ; height < latest+minSignedPerWindow; height++ { - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) + f.ctx = integration.SetHeaderInfo(f.ctx, coreheader.Info{Height: height}) err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, comet.BlockIDFlagAbsent) assert.NilError(t, err) } diff --git a/tests/integration/v2/slashing/slashing_test.go b/tests/integration/v2/slashing/slashing_test.go index c6be2642027e..dff3df80a2f9 100644 --- a/tests/integration/v2/slashing/slashing_test.go +++ b/tests/integration/v2/slashing/slashing_test.go @@ -1,29 +1,23 @@ package slashing import ( - "errors" + "context" "testing" "github.com/stretchr/testify/require" - "cosmossdk.io/core/header" - "cosmossdk.io/depinject" - "cosmossdk.io/log" + "cosmossdk.io/core/transaction" "cosmossdk.io/math" - bankkeeper "cosmossdk.io/x/bank/keeper" - "cosmossdk.io/x/slashing/keeper" "cosmossdk.io/x/slashing/types" stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtypes "cosmossdk.io/x/staking/types" - "github.com/cosmos/cosmos-sdk/client" + banktestutil "cosmossdk.io/x/bank/testutil" codecaddress "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/testutil/configurator" - "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/cosmos/cosmos-sdk/tests/integration/v2" sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) var ( @@ -37,51 +31,16 @@ var ( ) func TestSlashingMsgs(t *testing.T) { + f := initFixture(t) + genTokens := sdk.TokensFromConsensusPower(42, sdk.DefaultPowerReduction) bondTokens := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) genCoin := sdk.NewCoin(sdk.DefaultBondDenom, genTokens) bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, bondTokens) - addrStr, err := addrCodec.BytesToString(addr1) - require.NoError(t, err) - acc1 := &authtypes.BaseAccount{ - Address: addrStr, - } - accs := []sims.GenesisAccount{{GenesisAccount: acc1, Coins: sdk.Coins{genCoin}}} - - startupCfg := sims.DefaultStartUpConfig() - startupCfg.GenesisAccounts = accs - - var ( - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashingKeeper keeper.Keeper - txConfig client.TxConfig - ) - - app, err := sims.SetupWithConfiguration( - depinject.Configs( - configurator.NewAppConfig( - configurator.AccountsModule(), - configurator.AuthModule(), - configurator.StakingModule(), - configurator.SlashingModule(), - configurator.TxModule(), - configurator.ValidateModule(), - configurator.ConsensusModule(), - configurator.BankModule(), - ), - depinject.Supply(log.NewNopLogger()), - ), - startupCfg, &stakingKeeper, &bankKeeper, &slashingKeeper, &txConfig) - require.NoError(t, err) - - baseApp := app.BaseApp - - ctxCheck := baseApp.NewContext(true) - require.True(t, sdk.Coins{genCoin}.Equal(bankKeeper.GetAllBalances(ctxCheck, addr1))) - - require.NoError(t, err) + acc := f.accountKeeper.NewAccountWithAddress(f.ctx, addr1) + f.accountKeeper.SetAccount(f.ctx, acc) + require.NoError(t, banktestutil.FundAccount(f.ctx, f.bankKeeper, addr1, sdk.NewCoins(genCoin))) description := stakingtypes.NewDescription("foo_moniker", "", "", "", "", &stakingtypes.Metadata{}) commission := stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()) @@ -91,15 +50,23 @@ func TestSlashingMsgs(t *testing.T) { createValidatorMsg, err := stakingtypes.NewMsgCreateValidator( addrStrVal, valKey.PubKey(), bondCoin, description, commission, math.OneInt(), ) - require.NoError(t, err) - headerInfo := header.Info{Height: app.LastBlockHeight() + 1} - _, _, err = sims.SignCheckDeliver(t, txConfig, app.BaseApp, headerInfo, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1) + stakingMsgServer := stakingkeeper.NewMsgServerImpl(f.stakingKeeper) + _, err = f.app.RunMsg( + t, + f.ctx, + func(ctx context.Context) (transaction.Msg, error) { + res, err := stakingMsgServer.CreateValidator(ctx, createValidatorMsg) + return res, err + }, + integration.WithAutomaticCommit(), + ) + + require.True(t, sdk.Coins{genCoin.Sub(bondCoin)}.Equal(f.bankKeeper.GetAllBalances(f.ctx, addr1))) + _, err = f.stakingKeeper.EndBlocker(f.ctx) require.NoError(t, err) - require.True(t, sdk.Coins{genCoin.Sub(bondCoin)}.Equal(bankKeeper.GetAllBalances(ctxCheck, addr1))) - ctxCheck = baseApp.NewContext(true) - validator, err := stakingKeeper.GetValidator(ctxCheck, sdk.ValAddress(addr1)) + validator, err := f.stakingKeeper.GetValidator(f.ctx, sdk.ValAddress(addr1)) require.NoError(t, err) require.Equal(t, addrStrVal, validator.OperatorAddress) @@ -107,13 +74,18 @@ func TestSlashingMsgs(t *testing.T) { require.True(math.IntEq(t, bondTokens, validator.BondedTokens())) unjailMsg := &types.MsgUnjail{ValidatorAddr: addrStrVal} - ctxCheck = app.BaseApp.NewContext(true) - _, err = slashingKeeper.ValidatorSigningInfo.Get(ctxCheck, sdk.ConsAddress(valAddr)) + _, err = f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, sdk.ConsAddress(valAddr)) require.NoError(t, err) - // unjail should fail with unknown validator - headerInfo = header.Info{Height: app.LastBlockHeight() + 1} - _, _, err = sims.SignCheckDeliver(t, txConfig, app.BaseApp, headerInfo, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, false, false, priv1) - require.Error(t, err) - require.True(t, errors.Is(err, types.ErrValidatorNotJailed)) + // unjail should fail with validator not jailed + _, err = f.app.RunMsg( + t, + f.ctx, + func(ctx context.Context) (transaction.Msg, error) { + res, err := f.slashingMsgServer.Unjail(ctx, unjailMsg) + return res, err + }, + integration.WithAutomaticCommit(), + ) + require.ErrorIs(t, err, types.ErrValidatorNotJailed) } diff --git a/testutil/sims/address_helpers.go b/testutil/sims/address_helpers.go index d0b8bb8ce7fc..f90225502b80 100644 --- a/testutil/sims/address_helpers.go +++ b/testutil/sims/address_helpers.go @@ -2,6 +2,7 @@ package sims import ( "bytes" + "context" "encoding/hex" "errors" "strconv" @@ -20,7 +21,7 @@ const mintModuleName = "mint" type GenerateAccountStrategy func(int) []sdk.AccAddress // AddTestAddrsFromPubKeys adds the addresses into the SimApp providing only the public keys. -func AddTestAddrsFromPubKeys(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx sdk.Context, pubKeys []cryptotypes.PubKey, accAmt math.Int) { +func AddTestAddrsFromPubKeys(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx context.Context, pubKeys []cryptotypes.PubKey, accAmt math.Int) { bondDenom, err := stakingKeeper.BondDenom(ctx) if err != nil { panic(err) @@ -39,11 +40,11 @@ func AddTestAddrs(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx sdk.Co } // AddTestAddrsIncremental constructs and returns accNum amount of accounts with an initial balance of accAmt in random order -func AddTestAddrsIncremental(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx sdk.Context, accNum int, accAmt math.Int) []sdk.AccAddress { +func AddTestAddrsIncremental(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx context.Context, accNum int, accAmt math.Int) []sdk.AccAddress { return addTestAddrs(bankKeeper, stakingKeeper, ctx, accNum, accAmt, CreateIncrementalAccounts) } -func addTestAddrs(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx sdk.Context, accNum int, accAmt math.Int, strategy GenerateAccountStrategy) []sdk.AccAddress { +func addTestAddrs(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx context.Context, accNum int, accAmt math.Int, strategy GenerateAccountStrategy) []sdk.AccAddress { testAddrs := strategy(accNum) bondDenom, err := stakingKeeper.BondDenom(ctx) if err != nil { @@ -58,7 +59,7 @@ func addTestAddrs(bankKeeper BankKeeper, stakingKeeper StakingKeeper, ctx sdk.Co return testAddrs } -func initAccountWithCoins(bankKeeper BankKeeper, ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins) { +func initAccountWithCoins(bankKeeper BankKeeper, ctx context.Context, addr sdk.AccAddress, coins sdk.Coins) { if err := bankKeeper.MintCoins(ctx, mintModuleName, coins); err != nil { panic(err) } From 7903ac85a739937d0125acf74a718f2f2d47eb3c Mon Sep 17 00:00:00 2001 From: akhilkumarpilli Date: Thu, 5 Dec 2024 17:19:36 +0530 Subject: [PATCH 3/5] fix all tests --- tests/integration/slashing/abci_test.go | 134 ----- tests/integration/slashing/app_config.go | 32 - .../slashing/keeper/keeper_test.go | 551 ------------------ .../keeper/slash_redelegation_test.go | 502 ---------------- tests/integration/slashing/slashing_test.go | 119 ---- tests/integration/v2/slashing/abci_test.go | 92 +-- tests/integration/v2/slashing/keeper_test.go | 103 ---- .../v2/slashing/slash_redelegation_test.go | 278 ++++----- .../integration/v2/slashing/slashing_test.go | 141 ++++- 9 files changed, 245 insertions(+), 1707 deletions(-) delete mode 100644 tests/integration/slashing/abci_test.go delete mode 100644 tests/integration/slashing/app_config.go delete mode 100644 tests/integration/slashing/keeper/keeper_test.go delete mode 100644 tests/integration/slashing/keeper/slash_redelegation_test.go delete mode 100644 tests/integration/slashing/slashing_test.go diff --git a/tests/integration/slashing/abci_test.go b/tests/integration/slashing/abci_test.go deleted file mode 100644 index 96e869743e5d..000000000000 --- a/tests/integration/slashing/abci_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package slashing - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - - "cosmossdk.io/core/comet" - coreheader "cosmossdk.io/core/header" - "cosmossdk.io/depinject" - "cosmossdk.io/log" - bankkeeper "cosmossdk.io/x/bank/keeper" - "cosmossdk.io/x/slashing" - slashingkeeper "cosmossdk.io/x/slashing/keeper" - "cosmossdk.io/x/slashing/testutil" - stakingkeeper "cosmossdk.io/x/staking/keeper" - stakingtestutil "cosmossdk.io/x/staking/testutil" - - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/runtime" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" -) - -// TestBeginBlocker is a unit test function that tests the behavior of the BeginBlocker function. -// It sets up the necessary dependencies and context, creates a validator, and performs various operations -// to test the slashing logic. It checks if the validator is correctly jailed after a certain number of blocks. -func TestBeginBlocker(t *testing.T) { - var ( - interfaceRegistry codectypes.InterfaceRegistry - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - stakingKeeper *stakingkeeper.Keeper - slashingKeeper slashingkeeper.Keeper - ) - - app, err := simtestutil.Setup( - depinject.Configs( - AppConfig, - depinject.Supply(log.NewNopLogger()), - ), - &interfaceRegistry, - &accountKeeper, - &bankKeeper, - &stakingKeeper, - &slashingKeeper, - ) - require.NoError(t, err) - - ctx := app.BaseApp.NewContext(false) - - pks := simtestutil.CreateTestPubKeys(1) - simtestutil.AddTestAddrsFromPubKeys(bankKeeper, stakingKeeper, ctx, pks, stakingKeeper.TokensFromConsensusPower(ctx, 200)) - addr, pk := sdk.ValAddress(pks[0].Address()), pks[0] - tstaking := stakingtestutil.NewHelper(t, ctx, stakingKeeper) - - // bond the validator - power := int64(100) - acc := accountKeeper.NewAccountWithAddress(ctx, sdk.AccAddress(addr)) - accountKeeper.SetAccount(ctx, acc) - amt := tstaking.CreateValidatorWithValPower(addr, pk, power, true) - _, err = stakingKeeper.EndBlocker(ctx) - require.NoError(t, err) - bondDenom, err := stakingKeeper.BondDenom(ctx) - require.NoError(t, err) - require.Equal( - t, bankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(bondDenom, testutil.InitTokens.Sub(amt))), - ) - val, err := stakingKeeper.Validator(ctx, addr) - require.NoError(t, err) - require.Equal(t, amt, val.GetBondedTokens()) - - abciVal := comet.Validator{ - Address: pk.Address(), - Power: power, - } - - ctx = ctx.WithCometInfo(comet.Info{ - LastCommit: comet.CommitInfo{Votes: []comet.VoteInfo{{ - Validator: abciVal, - BlockIDFlag: comet.BlockIDFlagCommit, - }}}, - }) - cometInfoService := runtime.NewContextAwareCometInfoService() - - err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) - require.NoError(t, err) - - info, err := slashingKeeper.ValidatorSigningInfo.Get(ctx, sdk.ConsAddress(pk.Address())) - require.NoError(t, err) - require.Equal(t, ctx.HeaderInfo().Height, info.StartHeight) - require.Equal(t, int64(0), info.IndexOffset) - require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - require.Equal(t, int64(0), info.MissedBlocksCounter) - - height := int64(0) - - signedBlocksWindow, err := slashingKeeper.SignedBlocksWindow(ctx) - require.NoError(t, err) - // for 100 blocks, mark the validator as having signed - for ; height < signedBlocksWindow; height++ { - ctx = ctx.WithHeaderInfo(coreheader.Info{Height: height}) - - err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) - require.NoError(t, err) - } - - minSignedPerWindow, err := slashingKeeper.MinSignedPerWindow(ctx) - require.NoError(t, err) - // for 50 blocks, mark the validator as having not signed - for ; height < ((signedBlocksWindow * 2) - minSignedPerWindow + 1); height++ { - ctx = ctx.WithHeaderInfo(coreheader.Info{Height: height}).WithCometInfo(comet.Info{ - LastCommit: comet.CommitInfo{Votes: []comet.VoteInfo{{ - Validator: abciVal, - BlockIDFlag: comet.BlockIDFlagAbsent, - }}}, - }) - - err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) - require.NoError(t, err) - } - - // end block - _, err = stakingKeeper.EndBlocker(ctx) - require.NoError(t, err) - - // validator should be jailed - validator, err := stakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) - require.NoError(t, err) - require.Equal(t, sdk.Unbonding, validator.GetStatus()) -} diff --git a/tests/integration/slashing/app_config.go b/tests/integration/slashing/app_config.go deleted file mode 100644 index 415bea47145d..000000000000 --- a/tests/integration/slashing/app_config.go +++ /dev/null @@ -1,32 +0,0 @@ -package slashing - -import ( - _ "cosmossdk.io/x/accounts" // import as blank for app wiring - _ "cosmossdk.io/x/bank" // import as blank for app wiring - _ "cosmossdk.io/x/consensus" // import as blank for app wiring - _ "cosmossdk.io/x/distribution" // import as blank for app wiring - _ "cosmossdk.io/x/mint" // import as blank for app wiring - _ "cosmossdk.io/x/protocolpool" // import as blank for app wiring - _ "cosmossdk.io/x/slashing" // import as blank for app wiring - _ "cosmossdk.io/x/staking" // import as blank for app wiring - - "github.com/cosmos/cosmos-sdk/testutil/configurator" - _ "github.com/cosmos/cosmos-sdk/x/auth" // import as blank for app wiring - _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import as blank for app wiring - _ "github.com/cosmos/cosmos-sdk/x/genutil" // import as blank for app wiring -) - -var AppConfig = configurator.NewAppConfig( - configurator.AccountsModule(), - configurator.AuthModule(), - configurator.BankModule(), - configurator.StakingModule(), - configurator.SlashingModule(), - configurator.TxModule(), - configurator.ValidateModule(), - configurator.ConsensusModule(), - configurator.GenutilModule(), - configurator.MintModule(), - configurator.DistributionModule(), - configurator.ProtocolPoolModule(), -) diff --git a/tests/integration/slashing/keeper/keeper_test.go b/tests/integration/slashing/keeper/keeper_test.go deleted file mode 100644 index 74ccf5da61f8..000000000000 --- a/tests/integration/slashing/keeper/keeper_test.go +++ /dev/null @@ -1,551 +0,0 @@ -package keeper_test - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" - "gotest.tools/v3/assert" - - "cosmossdk.io/core/appmodule" - "cosmossdk.io/core/comet" - coreheader "cosmossdk.io/core/header" - "cosmossdk.io/log" - storetypes "cosmossdk.io/store/types" - "cosmossdk.io/x/bank" - bankkeeper "cosmossdk.io/x/bank/keeper" - banktypes "cosmossdk.io/x/bank/types" - "cosmossdk.io/x/consensus" - consensusparamkeeper "cosmossdk.io/x/consensus/keeper" - consensustypes "cosmossdk.io/x/consensus/types" - minttypes "cosmossdk.io/x/mint/types" - "cosmossdk.io/x/slashing" - slashingkeeper "cosmossdk.io/x/slashing/keeper" - "cosmossdk.io/x/slashing/testutil" - slashingtypes "cosmossdk.io/x/slashing/types" - "cosmossdk.io/x/staking" - stakingkeeper "cosmossdk.io/x/staking/keeper" - stakingtestutil "cosmossdk.io/x/staking/testutil" - stakingtypes "cosmossdk.io/x/staking/types" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/codec" - addresscodec "github.com/cosmos/cosmos-sdk/codec/address" - codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil" - "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/testutil/integration" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/cosmos/cosmos-sdk/x/auth" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authtestutil "github.com/cosmos/cosmos-sdk/x/auth/testutil" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -type fixture struct { - app *integration.App - - ctx sdk.Context - - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - slashingKeeper slashingkeeper.Keeper - stakingKeeper *stakingkeeper.Keeper - - addrDels []sdk.AccAddress - valAddrs []sdk.ValAddress -} - -func initFixture(tb testing.TB) *fixture { - tb.Helper() - keys := storetypes.NewKVStoreKeys( - authtypes.StoreKey, banktypes.StoreKey, slashingtypes.StoreKey, stakingtypes.StoreKey, consensustypes.StoreKey, - ) - encodingCfg := moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{}, auth.AppModule{}) - cdc := encodingCfg.Codec - - authority := authtypes.NewModuleAddress("gov") - - maccPerms := map[string][]string{ - minttypes.ModuleName: {authtypes.Minter}, - stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, - stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, - } - - msgRouter := baseapp.NewMsgServiceRouter() - queryRouter := baseapp.NewGRPCQueryRouter() - - // gomock initializations - ctrl := gomock.NewController(tb) - acctsModKeeper := authtestutil.NewMockAccountsModKeeper(ctrl) - accNum := uint64(0) - acctsModKeeper.EXPECT().NextAccountNumber(gomock.Any()).AnyTimes().DoAndReturn(func(ctx context.Context) (uint64, error) { - currentNum := accNum - accNum++ - return currentNum, nil - }) - - accountKeeper := authkeeper.NewAccountKeeper( - runtime.NewEnvironment(runtime.NewKVStoreService(keys[authtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), - cdc, - authtypes.ProtoBaseAccount, - acctsModKeeper, - maccPerms, - addresscodec.NewBech32Codec(sdk.Bech32MainPrefix), - sdk.Bech32MainPrefix, - authority.String(), - ) - - blockedAddresses := map[string]bool{ - accountKeeper.GetAuthority(): false, - } - bankKeeper := bankkeeper.NewBaseKeeper( - runtime.NewEnvironment(runtime.NewKVStoreService(keys[banktypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), - cdc, - accountKeeper, - blockedAddresses, - authority.String(), - ) - - cometInfoService := runtime.NewContextAwareCometInfoService() - - consensusParamsKeeper := consensusparamkeeper.NewKeeper(cdc, runtime.NewEnvironment(runtime.NewKVStoreService(keys[consensustypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), authtypes.NewModuleAddress("gov").String()) - - stakingKeeper := stakingkeeper.NewKeeper(cdc, runtime.NewEnvironment(runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), accountKeeper, bankKeeper, consensusParamsKeeper, authority.String(), addresscodec.NewBech32Codec(sdk.Bech32PrefixValAddr), addresscodec.NewBech32Codec(sdk.Bech32PrefixConsAddr), cometInfoService) - - slashingKeeper := slashingkeeper.NewKeeper(runtime.NewEnvironment(runtime.NewKVStoreService(keys[slashingtypes.StoreKey]), log.NewNopLogger(), runtime.EnvWithQueryRouterService(queryRouter), runtime.EnvWithMsgRouterService(msgRouter)), cdc, &codec.LegacyAmino{}, stakingKeeper, authority.String()) - - bankModule := bank.NewAppModule(cdc, bankKeeper, accountKeeper) - stakingModule := staking.NewAppModule(cdc, stakingKeeper) - slashingModule := slashing.NewAppModule(cdc, slashingKeeper, accountKeeper, bankKeeper, stakingKeeper, cdc.InterfaceRegistry(), cometInfoService) - consensusModule := consensus.NewAppModule(cdc, consensusParamsKeeper) - - integrationApp := integration.NewIntegrationApp(log.NewNopLogger(), keys, cdc, - encodingCfg.InterfaceRegistry.SigningContext().AddressCodec(), - encodingCfg.InterfaceRegistry.SigningContext().ValidatorAddressCodec(), - map[string]appmodule.AppModule{ - banktypes.ModuleName: bankModule, - stakingtypes.ModuleName: stakingModule, - slashingtypes.ModuleName: slashingModule, - consensustypes.ModuleName: consensusModule, - }, - msgRouter, - queryRouter, - ) - - sdkCtx := sdk.UnwrapSDKContext(integrationApp.Context()) - - // Register MsgServer and QueryServer - slashingtypes.RegisterMsgServer(integrationApp.MsgServiceRouter(), slashingkeeper.NewMsgServerImpl(slashingKeeper)) - slashingtypes.RegisterQueryServer(integrationApp.QueryHelper(), slashingkeeper.NewQuerier(slashingKeeper)) - - // set default staking params - // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 - err := slashingKeeper.Params.Set(sdkCtx, testutil.TestParams()) - assert.NilError(tb, err) - addrDels := simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, sdkCtx, 6, stakingKeeper.TokensFromConsensusPower(sdkCtx, 200)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) - - consaddr0, err := stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[0]) - assert.NilError(tb, err) - consaddr1, err := stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[1]) - assert.NilError(tb, err) - - info1 := slashingtypes.NewValidatorSigningInfo(consaddr0, int64(4), time.Unix(2, 0), false, int64(10)) - info2 := slashingtypes.NewValidatorSigningInfo(consaddr1, int64(5), time.Unix(2, 0), false, int64(10)) - - err = slashingKeeper.ValidatorSigningInfo.Set(sdkCtx, sdk.ConsAddress(addrDels[0]), info1) - assert.NilError(tb, err) - err = slashingKeeper.ValidatorSigningInfo.Set(sdkCtx, sdk.ConsAddress(addrDels[1]), info2) - assert.NilError(tb, err) - return &fixture{ - app: integrationApp, - ctx: sdkCtx, - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, - slashingKeeper: slashingKeeper, - stakingKeeper: stakingKeeper, - addrDels: addrDels, - valAddrs: valAddrs, - } -} - -func TestUnJailNotBonded(t *testing.T) { - t.Parallel() - f := initFixture(t) - - p, err := f.stakingKeeper.Params.Get(f.ctx) - assert.NilError(t, err) - p.MaxValidators = 5 - assert.NilError(t, f.stakingKeeper.Params.Set(f.ctx, p)) - pks := simtestutil.CreateTestPubKeys(6) - tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) - - // create max (5) validators all with the same power - for i := uint32(0); i < p.MaxValidators; i++ { - addr, val := f.valAddrs[i], pks[i] - acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) - f.accountKeeper.SetAccount(f.ctx, acc) - tstaking.CreateValidatorWithValPower(addr, val, 100, true) - } - - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - newHeight := f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) - - // create a 6th validator with less power than the cliff validator (won't be bonded) - addr, val := f.valAddrs[5], pks[5] - acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) - f.accountKeeper.SetAccount(f.ctx, acc) - amt := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 50) - msg := tstaking.CreateValidatorMsg(addr, val, amt) - msg.MinSelfDelegation = amt - msg.Description = stakingtypes.Description{Moniker: "TestValidator"} - res, err := tstaking.CreateValidatorWithMsg(f.ctx, msg) - assert.NilError(t, err) - assert.Assert(t, res != nil) - - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) - - tstaking.CheckValidator(addr, stakingtypes.Unbonded, false) - - // unbond below minimum self-delegation - assert.Equal(t, p.BondDenom, tstaking.Denom) - accAddr, err := f.accountKeeper.AddressCodec().BytesToString(addr) - assert.NilError(t, err) - valAddr, err := f.stakingKeeper.ValidatorAddressCodec().BytesToString(addr) - require.NoError(t, err) - tstaking.Undelegate(accAddr, valAddr, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 1), true) - - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) - - // verify that validator is jailed - tstaking.CheckValidator(addr, -1, true) - - // verify we cannot unjail (yet) - msgUnjail := slashingtypes.MsgUnjail{ - ValidatorAddr: addr.String(), - } - _, err = f.app.RunMsg( - &msgUnjail, - integration.WithAutomaticFinalizeBlock(), - integration.WithAutomaticCommit(), - ) - assert.ErrorContains(t, err, "cannot be unjailed") - - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) - // bond to meet minimum self-delegationa - accAddr, err = f.accountKeeper.AddressCodec().BytesToString(addr) - assert.NilError(t, err) - valAddr, err = f.stakingKeeper.ValidatorAddressCodec().BytesToString(addr) - assert.NilError(t, err) - tstaking.DelegateWithPower(accAddr, valAddr, 1) - - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - newHeight = f.ctx.BlockHeight() + 1 - f.ctx = f.ctx.WithBlockHeight(newHeight).WithHeaderInfo(coreheader.Info{Height: newHeight}) - - // verify we can immediately unjail - _, err = f.app.RunMsg( - &msgUnjail, - integration.WithAutomaticFinalizeBlock(), - integration.WithAutomaticCommit(), - ) - assert.NilError(t, err) - - tstaking.CheckValidator(addr, -1, false) -} - -// 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) { - t.Parallel() - f := initFixture(t) - - pks := simtestutil.CreateTestPubKeys(1) - addr, valpubkey := f.valAddrs[0], pks[0] - tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) - signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(f.ctx) - assert.NilError(t, err) - f.ctx = f.ctx.WithBlockHeight(signedBlocksWindow + 1).WithHeaderInfo(coreheader.Info{Height: signedBlocksWindow + 1}) - assert.NilError(t, f.slashingKeeper.AddrPubkeyRelation.Set(f.ctx, pks[0].Address(), pks[0])) - - consaddr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(valpubkey.Address()) - assert.NilError(t, err) - - info := slashingtypes.NewValidatorSigningInfo(consaddr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) - assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, sdk.ConsAddress(valpubkey.Address()), info)) - assert.Equal(t, signedBlocksWindow+1, info.StartHeight) - - // Validator created - acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) - f.accountKeeper.SetAccount(f.ctx, acc) - amt := tstaking.CreateValidatorWithValPower(addr, valpubkey, 100, true) - - _, err = f.stakingKeeper.EndBlocker(f.ctx) - require.NoError(t, err) - - bondDenom, err := f.stakingKeeper.BondDenom(f.ctx) - require.NoError(t, err) - - assert.DeepEqual( - t, f.bankKeeper.GetAllBalances(f.ctx, sdk.AccAddress(addr)), - sdk.NewCoins(sdk.NewCoin(bondDenom, testutil.InitTokens.Sub(amt))), - ) - - val, err := f.stakingKeeper.Validator(f.ctx, addr) - require.NoError(t, err) - assert.DeepEqual(t, amt, val.GetBondedTokens()) - - // Now a validator, for two blocks - assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, valpubkey.Address(), 100, comet.BlockIDFlagCommit)) - f.ctx = f.ctx.WithBlockHeight(signedBlocksWindow + 2).WithHeaderInfo(coreheader.Info{Height: signedBlocksWindow + 2}) - assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, valpubkey.Address(), 100, comet.BlockIDFlagAbsent)) - - info, found := f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, sdk.ConsAddress(valpubkey.Address())) - assert.Assert(t, found) - assert.Equal(t, signedBlocksWindow+1, info.StartHeight) - assert.Equal(t, int64(1), info.MissedBlocksCounter) - assert.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - - // validator should be bonded still, should not have been jailed or slashed - validator, _ := f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(valpubkey)) - assert.Equal(t, sdk.Bonded, validator.GetStatus()) - bondPool := f.stakingKeeper.GetBondedPool(f.ctx) - expTokens := f.stakingKeeper.TokensFromConsensusPower(f.ctx, 100) - assert.Assert(t, expTokens.Equal(f.bankKeeper.GetBalance(f.ctx, bondPool.GetAddress(), bondDenom).Amount)) -} - -// Test a jailed validator being "down" twice -// Ensure that they're only slashed once -func TestHandleAlreadyJailed(t *testing.T) { - t.Parallel() - f := initFixture(t) - - pks := simtestutil.CreateTestPubKeys(1) - addr, val := f.valAddrs[0], pks[0] - power := int64(100) - tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) - - err := f.slashingKeeper.AddrPubkeyRelation.Set(f.ctx, pks[0].Address(), pks[0]) - assert.NilError(t, err) - - consaddr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(val.Address()) - assert.NilError(t, err) - - info := slashingtypes.NewValidatorSigningInfo(consaddr, f.ctx.HeaderInfo().Height, time.Unix(0, 0), false, int64(0)) - assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, sdk.ConsAddress(val.Address()), info)) - - acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(addr)) - f.accountKeeper.SetAccount(f.ctx, acc) - - amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) - - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - - signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(f.ctx) - assert.NilError(t, err) - - // 1000 first blocks OK - height := int64(0) - for ; height < signedBlocksWindow; height++ { - f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) - err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagCommit) - assert.NilError(t, err) - } - - minSignedPerWindow, err := f.slashingKeeper.MinSignedPerWindow(f.ctx) - assert.NilError(t, err) - - // 501 blocks missed - for ; height < signedBlocksWindow+(signedBlocksWindow-minSignedPerWindow)+1; height++ { - f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) - err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagAbsent) - assert.NilError(t, err) - } - - // end block - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - - // validator should have been jailed and slashed - validator, _ := f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(val)) - assert.Equal(t, sdk.Unbonding, validator.GetStatus()) - - // validator should have been slashed - resultingTokens := amt.Sub(f.stakingKeeper.TokensFromConsensusPower(f.ctx, 1)) - assert.DeepEqual(t, resultingTokens, validator.GetTokens()) - - // another block missed - f.ctx = f.ctx.WithHeaderInfo(coreheader.Info{Height: height}) - assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagAbsent)) - - // validator should not have been slashed twice - validator, _ = f.stakingKeeper.GetValidatorByConsAddr(f.ctx, sdk.GetConsAddress(val)) - assert.DeepEqual(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) { - t.Parallel() - f := initFixture(t) - - params, err := f.stakingKeeper.Params.Get(f.ctx) - require.NoError(t, err) - params.MaxValidators = 1 - err = f.stakingKeeper.Params.Set(f.ctx, params) - assert.NilError(t, err) - power := int64(100) - - pks := simtestutil.CreateTestPubKeys(3) - simtestutil.AddTestAddrsFromPubKeys(f.bankKeeper, f.stakingKeeper, f.ctx, pks, f.stakingKeeper.TokensFromConsensusPower(f.ctx, 200)) - for _, pk := range pks { - acc := f.accountKeeper.NewAccountWithAddress(f.ctx, sdk.AccAddress(pk.Address())) - f.accountKeeper.SetAccount(f.ctx, acc) - } - - addr, val := pks[0].Address(), pks[0] - consAddr := sdk.ConsAddress(addr) - tstaking := stakingtestutil.NewHelper(t, f.ctx, f.stakingKeeper) - valAddr := sdk.ValAddress(addr) - - assert.NilError(t, f.slashingKeeper.AddrPubkeyRelation.Set(f.ctx, pks[0].Address(), pks[0])) - - consaddrStr, err := f.stakingKeeper.ConsensusAddressCodec().BytesToString(addr) - assert.NilError(t, err) - - info := slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) - assert.NilError(t, f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info)) - - tstaking.CreateValidatorWithValPower(valAddr, val, power, true) - validatorUpdates, err := f.stakingKeeper.EndBlocker(f.ctx) - require.NoError(t, err) - assert.Equal(t, 1, len(validatorUpdates)) - tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) - - // 100 first blocks OK - height := int64(0) - for ; height < int64(100); height++ { - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) - assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), power, comet.BlockIDFlagCommit)) - } - - // kick first validator out of validator set - tstaking.CreateValidatorWithValPower(sdk.ValAddress(pks[1].Address()), pks[1], power+1, true) - validatorUpdates, err = f.stakingKeeper.EndBlocker(f.ctx) - require.NoError(t, err) - assert.Equal(t, 2, len(validatorUpdates)) - tstaking.CheckValidator(sdk.ValAddress(pks[1].Address()), stakingtypes.Bonded, false) - tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, false) - - // 600 more blocks happened - height += 600 - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) - - // validator added back in - accAddr, err := f.accountKeeper.AddressCodec().BytesToString(sdk.AccAddress(pks[2].Address())) - assert.NilError(t, err) - vAddr, err := f.stakingKeeper.ValidatorAddressCodec().BytesToString(valAddr) - assert.NilError(t, err) - tstaking.DelegateWithPower(accAddr, vAddr, 50) - - validatorUpdates, err = f.stakingKeeper.EndBlocker(f.ctx) - require.NoError(t, err) - assert.Equal(t, 2, len(validatorUpdates)) - tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) - newPower := power + 50 - - // validator misses a block - assert.NilError(t, f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, comet.BlockIDFlagAbsent)) - height++ - - // shouldn't be jailed/kicked yet - tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) - - signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(f.ctx) - assert.NilError(t, err) - - // validator misses an additional 500 more blocks within the SignedBlockWindow (here 1000 blocks). - latest := signedBlocksWindow + height - // misses 500 blocks + within the signing windows i.e. 700-1700 - // validators misses all 1000 blocks of a SignedBlockWindows - for ; height < latest+1; height++ { - err = f.slashingKeeper.HandleValidatorSignature(f.ctx.WithHeaderInfo(coreheader.Info{Height: height}), val.Address(), newPower, comet.BlockIDFlagAbsent) - assert.NilError(t, err) - } - - // should now be jailed & kicked - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) - - info = slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) - err = f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info) - assert.NilError(t, err) - - // check all the signing information - signInfo, found := f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, consAddr) - assert.Assert(t, found) - assert.Equal(t, int64(700), signInfo.StartHeight) - assert.Equal(t, int64(0), signInfo.MissedBlocksCounter) - assert.Equal(t, int64(0), signInfo.IndexOffset) - - // some blocks pass - height = int64(5000) - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) - - info = slashingtypes.NewValidatorSigningInfo(consaddrStr, f.ctx.BlockHeight(), time.Unix(0, 0), false, int64(0)) - err = f.slashingKeeper.ValidatorSigningInfo.Set(f.ctx, consAddr, info) - assert.NilError(t, err) - - // validator rejoins and starts signing again - err = f.stakingKeeper.Unjail(f.ctx, consAddr) - assert.NilError(t, err) - err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, comet.BlockIDFlagCommit) - assert.NilError(t, err) - - // validator should not be kicked since we reset counter/array when it was jailed - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) - - // check start height is correctly set - signInfo, found = f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, consAddr) - assert.Assert(t, found) - assert.Equal(t, height, signInfo.StartHeight) - - minSignedPerWindow, err := f.slashingKeeper.MinSignedPerWindow(f.ctx) - assert.NilError(t, err) - - // validator misses 501 blocks after SignedBlockWindow period (1000 blocks) - latest = signedBlocksWindow + height - for ; height < latest+minSignedPerWindow; height++ { - f.ctx = f.ctx.WithBlockHeight(height).WithHeaderInfo(coreheader.Info{Height: height}) - err = f.slashingKeeper.HandleValidatorSignature(f.ctx, val.Address(), newPower, comet.BlockIDFlagAbsent) - assert.NilError(t, err) - } - - // validator should now be jailed & kicked - _, err = f.stakingKeeper.EndBlocker(f.ctx) - assert.NilError(t, err) - - tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) -} diff --git a/tests/integration/slashing/keeper/slash_redelegation_test.go b/tests/integration/slashing/keeper/slash_redelegation_test.go deleted file mode 100644 index b1a8ce858925..000000000000 --- a/tests/integration/slashing/keeper/slash_redelegation_test.go +++ /dev/null @@ -1,502 +0,0 @@ -package keeper_test - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "cosmossdk.io/core/header" - "cosmossdk.io/depinject" - "cosmossdk.io/log" - "cosmossdk.io/math" - bankkeeper "cosmossdk.io/x/bank/keeper" - banktestutil "cosmossdk.io/x/bank/testutil" - distributionkeeper "cosmossdk.io/x/distribution/keeper" - slashingkeeper "cosmossdk.io/x/slashing/keeper" - stakingkeeper "cosmossdk.io/x/staking/keeper" - stakingtypes "cosmossdk.io/x/staking/types" - - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/tests/integration/slashing" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" -) - -func TestSlashRedelegation(t *testing.T) { - // setting up - var ( - authKeeper authkeeper.AccountKeeper - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashKeeper slashingkeeper.Keeper - distrKeeper distributionkeeper.Keeper - ) - - app, err := simtestutil.Setup( - depinject.Configs( - depinject.Supply(log.NewNopLogger()), - slashing.AppConfig, - ), - &stakingKeeper, - &bankKeeper, - &slashKeeper, - &distrKeeper, - &authKeeper, - ) - require.NoError(t, err) - - // get sdk context, staking msg server and bond denom - ctx := app.BaseApp.NewContext(false) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) - bondDenom, err := stakingKeeper.BondDenom(ctx) - require.NoError(t, err) - - // evilVal will be slashed, goodVal won't be slashed - evilValPubKey := secp256k1.GenPrivKey().PubKey() - goodValPubKey := secp256k1.GenPrivKey().PubKey() - - // both test acc 1 and 2 delegated to evil val, both acc should be slashed when evil val is slashed - // test acc 1 use the "undelegation after redelegation" trick (redelegate to good val and then undelegate) to avoid slashing - // test acc 2 only undelegate from evil val - testAcc1 := sdk.AccAddress([]byte("addr1_______________")) - testAcc2 := sdk.AccAddress([]byte("addr2_______________")) - - // fund acc 1 and acc 2 - testCoin := sdk.NewCoin(bondDenom, stakingKeeper.TokensFromConsensusPower(ctx, 10)) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc1, testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc2, testCoin) - - balance1Before := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) - balance2Before := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) - - // assert acc 1 and acc 2 balance - require.Equal(t, balance1Before.Amount.String(), testCoin.Amount.String()) - require.Equal(t, balance2Before.Amount.String(), testCoin.Amount.String()) - - // creating evil val - evilValAddr := sdk.ValAddress(evilValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(evilValAddr), testCoin) - createValMsg1, _ := stakingtypes.NewMsgCreateValidator( - evilValAddr.String(), evilValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) - _, err = stakingMsgServer.CreateValidator(ctx, createValMsg1) - require.NoError(t, err) - - // creating good val - goodValAddr := sdk.ValAddress(goodValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(goodValAddr), testCoin) - createValMsg2, _ := stakingtypes.NewMsgCreateValidator( - goodValAddr.String(), goodValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) - _, err = stakingMsgServer.CreateValidator(ctx, createValMsg2) - require.NoError(t, err) - - ctx = ctx.WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) - // next block, commit height 1, move to height 2 - // acc 1 and acc 2 delegate to evil val - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // Acc 2 delegate - delMsg := stakingtypes.NewMsgDelegate(testAcc2.String(), evilValAddr.String(), testCoin) - _, err = stakingMsgServer.Delegate(ctx, delMsg) - require.NoError(t, err) - - // Acc 1 delegate - delMsg = stakingtypes.NewMsgDelegate(testAcc1.String(), evilValAddr.String(), testCoin) - _, err = stakingMsgServer.Delegate(ctx, delMsg) - require.NoError(t, err) - - // next block, commit height 2, move to height 3 - // with the new delegations, evil val increases in voting power and commit byzantine behavior at height 3 consensus - // at the same time, acc 1 and acc 2 withdraw delegation from evil val - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - evilVal, err := stakingKeeper.GetValidator(ctx, evilValAddr) - require.NoError(t, err) - - evilPower := stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) - - // Acc 1 redelegate from evil val to good val - redelMsg := stakingtypes.NewMsgBeginRedelegate(testAcc1.String(), evilValAddr.String(), goodValAddr.String(), testCoin) - _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) - require.NoError(t, err) - - // Acc 1 undelegate from good val - undelMsg := stakingtypes.NewMsgUndelegate(testAcc1.String(), goodValAddr.String(), testCoin) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - // Acc 2 undelegate from evil val - undelMsg = stakingtypes.NewMsgUndelegate(testAcc2.String(), evilValAddr.String(), testCoin) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - // next block, commit height 3, move to height 4 - // Slash evil val for byzantine behavior at height 3 consensus, - // at which acc 1 and acc 2 still contributed to evil val voting power - // even tho they undelegate at block 3, the valset update is applied after committed block 3 when height 3 consensus already passes - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // slash evil val with slash factor = 0.9, leaving only 10% of stake after slashing - evilVal, _ = stakingKeeper.GetValidator(ctx, evilValAddr) - evilValConsAddr, err := evilVal.GetConsAddr() - require.NoError(t, err) - - err = slashKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr("0.9"), evilPower, 3) - require.NoError(t, err) - - // assert invariant to make sure we conduct slashing correctly - _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) - require.False(t, stop) - - _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) - require.False(t, stop) - - _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) - require.False(t, stop) - - // one eternity later - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1000000000000000000)) - require.NoError(t, err) - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // confirm that account 1 and account 2 has been slashed, and the slash amount is correct - balance1AfterSlashing := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) - balance2AfterSlashing := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) - - require.Equal(t, balance1AfterSlashing.Amount.Mul(math.NewIntFromUint64(10)).String(), balance1Before.Amount.String()) - require.Equal(t, balance2AfterSlashing.Amount.Mul(math.NewIntFromUint64(10)).String(), balance2Before.Amount.String()) -} - -func fundAccount(t *testing.T, ctx context.Context, bankKeeper bankkeeper.Keeper, authKeeper authkeeper.AccountKeeper, addr sdk.AccAddress, amount ...sdk.Coin) { - t.Helper() - - if authKeeper.GetAccount(ctx, addr) == nil { - addrAcc := authKeeper.NewAccountWithAddress(ctx, addr) - authKeeper.SetAccount(ctx, addrAcc) - } - - require.NoError(t, banktestutil.FundAccount(ctx, bankKeeper, addr, amount)) -} - -func TestOverSlashing(t *testing.T) { - // slash penalty percentage - slashFraction := "0.45" - - // percentage of (undelegation/(undelegation + redelegation)) - undelegationPercentageStr := "0.30" - - // setting up - var ( - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashKeeper slashingkeeper.Keeper - distrKeeper distributionkeeper.Keeper - authKeeper authkeeper.AccountKeeper - ) - - app, err := simtestutil.Setup(depinject.Configs( - depinject.Supply(log.NewNopLogger()), - slashing.AppConfig, - ), &stakingKeeper, &bankKeeper, &slashKeeper, &distrKeeper, &authKeeper) - require.NoError(t, err) - - // get sdk context, staking msg server and bond denom - ctx := app.BaseApp.NewContext(false) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) - bondDenom, err := stakingKeeper.BondDenom(ctx) - require.NoError(t, err) - - // evilVal will be slashed, goodVal won't be slashed - evilValPubKey := secp256k1.GenPrivKey().PubKey() - goodValPubKey := secp256k1.GenPrivKey().PubKey() - - /* - all test accs will delegate to evil val, which evil validator will eventually be slashed - - - test acc 1: redelegate -> undelegate full amount - - test acc 2: simple undelegation. intended scenario. - - test acc 3: redelegate -> undelegate some amount - - */ - - testAcc1 := sdk.AccAddress([]byte("addr1new____________")) - testAcc2 := sdk.AccAddress([]byte("addr2new____________")) - testAcc3 := sdk.AccAddress([]byte("addr3new____________")) - - // fund all accounts - testCoin := sdk.NewCoin(bondDenom, math.NewInt(1_000_000)) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc1, testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc2, testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc3, testCoin) - - balance1Before := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) - balance2Before := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) - balance3Before := bankKeeper.GetBalance(ctx, testAcc3, bondDenom) - - // assert acc 1, 2 and 3 balance - require.Equal(t, testCoin.Amount.String(), balance1Before.Amount.String()) - require.Equal(t, testCoin.Amount.String(), balance2Before.Amount.String()) - require.Equal(t, testCoin.Amount.String(), balance3Before.Amount.String()) - - // create evil val - evilValAddr := sdk.ValAddress(evilValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(evilValAddr), testCoin) - createValMsg1, _ := stakingtypes.NewMsgCreateValidator( - evilValAddr.String(), evilValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) - _, err = stakingMsgServer.CreateValidator(ctx, createValMsg1) - require.NoError(t, err) - - // create good val 1 - goodValAddr := sdk.ValAddress(goodValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(goodValAddr), testCoin) - createValMsg2, _ := stakingtypes.NewMsgCreateValidator( - goodValAddr.String(), goodValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) - _, err = stakingMsgServer.CreateValidator(ctx, createValMsg2) - require.NoError(t, err) - - // next block - ctx = ctx.WithBlockHeight(app.LastBlockHeight() + 1).WithHeaderInfo(header.Info{Height: app.LastBlockHeight() + 1}) - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // delegate all accs to evil val - delMsg := stakingtypes.NewMsgDelegate(testAcc1.String(), evilValAddr.String(), testCoin) - _, err = stakingMsgServer.Delegate(ctx, delMsg) - require.NoError(t, err) - - delMsg = stakingtypes.NewMsgDelegate(testAcc2.String(), evilValAddr.String(), testCoin) - _, err = stakingMsgServer.Delegate(ctx, delMsg) - require.NoError(t, err) - - delMsg = stakingtypes.NewMsgDelegate(testAcc3.String(), evilValAddr.String(), testCoin) - _, err = stakingMsgServer.Delegate(ctx, delMsg) - require.NoError(t, err) - - // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // evilValAddr done something bad - misbehaveHeight := ctx.BlockHeader().Height - evilVal, err := stakingKeeper.GetValidator(ctx, evilValAddr) - require.NoError(t, err) - - evilValConsAddr, err := evilVal.GetConsAddr() - require.NoError(t, err) - - evilPower := stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) - - // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // acc 1: redelegate to goodval1 and undelegate FULL amount - redelMsg := stakingtypes.NewMsgBeginRedelegate(testAcc1.String(), evilValAddr.String(), goodValAddr.String(), testCoin) - _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) - require.NoError(t, err) - undelMsg := stakingtypes.NewMsgUndelegate(testAcc1.String(), goodValAddr.String(), testCoin) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - // acc 2: undelegate full amount - undelMsg = stakingtypes.NewMsgUndelegate(testAcc2.String(), evilValAddr.String(), testCoin) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - // acc 3: redelegate to goodval1 and undelegate some amount - redelMsg = stakingtypes.NewMsgBeginRedelegate(testAcc3.String(), evilValAddr.String(), goodValAddr.String(), testCoin) - _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) - require.NoError(t, err) - - undelegationPercentage := math.LegacyMustNewDecFromStr(undelegationPercentageStr) - undelegationAmountDec := math.LegacyNewDecFromInt(testCoin.Amount).Mul(undelegationPercentage) - amountToUndelegate := undelegationAmountDec.TruncateInt() - - // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - portionofTestCoins := sdk.NewCoin(bondDenom, amountToUndelegate) - undelMsg = stakingtypes.NewMsgUndelegate(testAcc3.String(), goodValAddr.String(), portionofTestCoins) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // slash the evil val - err = slashKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr(slashFraction), evilPower, misbehaveHeight) - require.NoError(t, err) - - // assert invariants - _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) - require.False(t, stop) - _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) - require.False(t, stop) - _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) - require.False(t, stop) - - // fastforward 2 blocks to complete redelegations and unbondings - for i := 0; i < 2; i++ { - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1000000000000000000)) - require.NoError(t, err) - } - - // we check all accounts should be slashed with the equal amount, and they should end up with same balance including staked amount - stakedAcc1, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc1) - require.NoError(t, err) - stakedAcc2, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc2) - require.NoError(t, err) - stakedAcc3, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc3) - require.NoError(t, err) - - balance1AfterSlashing := bankKeeper.GetBalance(ctx, testAcc1, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc1)) - balance2AfterSlashing := bankKeeper.GetBalance(ctx, testAcc2, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc2)) - balance3AfterSlashing := bankKeeper.GetBalance(ctx, testAcc3, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc3)) - - require.Equal(t, "550000stake", balance1AfterSlashing.String()) - require.Equal(t, "550000stake", balance2AfterSlashing.String()) - require.Equal(t, "550000stake", balance3AfterSlashing.String()) -} - -func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { - // setting up - var ( - authKeeper authkeeper.AccountKeeper - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashKeeper slashingkeeper.Keeper - distrKeeper distributionkeeper.Keeper - ) - - app, err := simtestutil.Setup( - depinject.Configs( - depinject.Supply(log.NewNopLogger()), - slashing.AppConfig, - ), - &stakingKeeper, - &bankKeeper, - &slashKeeper, - &distrKeeper, - &authKeeper, - ) - require.NoError(t, err) - - // get sdk context, staking msg server and bond denom - ctx := app.BaseApp.NewContext(false).WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) - bondDenom, err := stakingKeeper.BondDenom(ctx) - require.NoError(t, err) - - // create validators DST and SRC - dstPubKey := secp256k1.GenPrivKey().PubKey() - srcPubKey := secp256k1.GenPrivKey().PubKey() - - dstAddr := sdk.ValAddress(dstPubKey.Address()) - srcAddr := sdk.ValAddress(srcPubKey.Address()) - - testCoin := sdk.NewCoin(bondDenom, stakingKeeper.TokensFromConsensusPower(ctx, 1000)) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(dstAddr), testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(srcAddr), testCoin) - - createValMsgDST, _ := stakingtypes.NewMsgCreateValidator( - dstAddr.String(), dstPubKey, testCoin, stakingtypes.Description{Details: "Validator DST"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) - _, err = stakingMsgServer.CreateValidator(ctx, createValMsgDST) - require.NoError(t, err) - - createValMsgSRC, _ := stakingtypes.NewMsgCreateValidator( - srcAddr.String(), srcPubKey, testCoin, stakingtypes.Description{Details: "Validator SRC"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) - _, err = stakingMsgServer.CreateValidator(ctx, createValMsgSRC) - require.NoError(t, err) - - // create a user accounts and delegate to SRC and DST - userAcc := sdk.AccAddress([]byte("user1_______________")) - fundAccount(t, ctx, bankKeeper, authKeeper, userAcc, testCoin) - - userAcc2 := sdk.AccAddress([]byte("user2_______________")) - fundAccount(t, ctx, bankKeeper, authKeeper, userAcc2, testCoin) - - delMsg := stakingtypes.NewMsgDelegate(userAcc.String(), srcAddr.String(), testCoin) - _, err = stakingMsgServer.Delegate(ctx, delMsg) - require.NoError(t, err) - - delMsg = stakingtypes.NewMsgDelegate(userAcc2.String(), dstAddr.String(), testCoin) - _, err = stakingMsgServer.Delegate(ctx, delMsg) - require.NoError(t, err) - - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // commit an infraction with DST and store the power at this height - dstVal, err := stakingKeeper.GetValidator(ctx, dstAddr) - require.NoError(t, err) - dstPower := stakingKeeper.TokensToConsensusPower(ctx, dstVal.Tokens) - dstConsAddr, err := dstVal.GetConsAddr() - require.NoError(t, err) - dstInfractionHeight := ctx.BlockHeight() - - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // undelegate all the user tokens from DST - undelMsg := stakingtypes.NewMsgUndelegate(userAcc2.String(), dstAddr.String(), testCoin) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - // commit an infraction with SRC and store the power at this height - srcVal, err := stakingKeeper.GetValidator(ctx, srcAddr) - require.NoError(t, err) - srcPower := stakingKeeper.TokensToConsensusPower(ctx, srcVal.Tokens) - srcConsAddr, err := srcVal.GetConsAddr() - require.NoError(t, err) - srcInfractionHeight := ctx.BlockHeight() - - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - // redelegate all the user tokens from SRC to DST - redelMsg := stakingtypes.NewMsgBeginRedelegate(userAcc.String(), srcAddr.String(), dstAddr.String(), testCoin) - _, err = stakingMsgServer.BeginRedelegate(ctx, redelMsg) - require.NoError(t, err) - - // undelegate the self delegation from DST - undelMsg = stakingtypes.NewMsgUndelegate(sdk.AccAddress(dstAddr).String(), dstAddr.String(), testCoin) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) - - undelMsg = stakingtypes.NewMsgUndelegate(userAcc.String(), dstAddr.String(), testCoin) - _, err = stakingMsgServer.Undelegate(ctx, undelMsg) - require.NoError(t, err) - - // check that dst now has zero tokens - valDst, err := stakingKeeper.GetValidator(ctx, dstAddr) - require.NoError(t, err) - require.Equal(t, math.ZeroInt().String(), valDst.Tokens.String()) - - // slash the infractions - err = slashKeeper.Slash(ctx, dstConsAddr, math.LegacyMustNewDecFromStr("0.8"), dstPower, dstInfractionHeight) - require.NoError(t, err) - - err = slashKeeper.Slash(ctx, srcConsAddr, math.LegacyMustNewDecFromStr("0.5"), srcPower, srcInfractionHeight) - require.NoError(t, err) - - // assert invariants to ensure correctness - _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) - require.False(t, stop) - - _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) - require.False(t, stop) - - _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) - require.False(t, stop) -} diff --git a/tests/integration/slashing/slashing_test.go b/tests/integration/slashing/slashing_test.go deleted file mode 100644 index be116892c240..000000000000 --- a/tests/integration/slashing/slashing_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package slashing_test - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/require" - - "cosmossdk.io/core/header" - "cosmossdk.io/depinject" - "cosmossdk.io/log" - "cosmossdk.io/math" - bankkeeper "cosmossdk.io/x/bank/keeper" - "cosmossdk.io/x/slashing/keeper" - "cosmossdk.io/x/slashing/types" - stakingkeeper "cosmossdk.io/x/staking/keeper" - stakingtypes "cosmossdk.io/x/staking/types" - - "github.com/cosmos/cosmos-sdk/client" - codecaddress "github.com/cosmos/cosmos-sdk/codec/address" - "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" - "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/testutil/configurator" - "github.com/cosmos/cosmos-sdk/testutil/sims" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" -) - -var ( - priv1 = secp256k1.GenPrivKey() - addr1 = sdk.AccAddress(priv1.PubKey().Address()) - addrCodec = codecaddress.NewBech32Codec("cosmos") - valaddrCodec = codecaddress.NewBech32Codec("cosmosvaloper") - - valKey = ed25519.GenPrivKey() - valAddr = sdk.AccAddress(valKey.PubKey().Address()) -) - -func TestSlashingMsgs(t *testing.T) { - genTokens := sdk.TokensFromConsensusPower(42, sdk.DefaultPowerReduction) - bondTokens := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction) - genCoin := sdk.NewCoin(sdk.DefaultBondDenom, genTokens) - bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, bondTokens) - - addrStr, err := addrCodec.BytesToString(addr1) - require.NoError(t, err) - acc1 := &authtypes.BaseAccount{ - Address: addrStr, - } - accs := []sims.GenesisAccount{{GenesisAccount: acc1, Coins: sdk.Coins{genCoin}}} - - startupCfg := sims.DefaultStartUpConfig() - startupCfg.GenesisAccounts = accs - - var ( - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashingKeeper keeper.Keeper - txConfig client.TxConfig - ) - - app, err := sims.SetupWithConfiguration( - depinject.Configs( - configurator.NewAppConfig( - configurator.AccountsModule(), - configurator.AuthModule(), - configurator.StakingModule(), - configurator.SlashingModule(), - configurator.TxModule(), - configurator.ValidateModule(), - configurator.ConsensusModule(), - configurator.BankModule(), - ), - depinject.Supply(log.NewNopLogger()), - ), - startupCfg, &stakingKeeper, &bankKeeper, &slashingKeeper, &txConfig) - require.NoError(t, err) - - baseApp := app.BaseApp - - ctxCheck := baseApp.NewContext(true) - require.True(t, sdk.Coins{genCoin}.Equal(bankKeeper.GetAllBalances(ctxCheck, addr1))) - - require.NoError(t, err) - - description := stakingtypes.NewDescription("foo_moniker", "", "", "", "", &stakingtypes.Metadata{}) - commission := stakingtypes.NewCommissionRates(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()) - - addrStrVal, err := valaddrCodec.BytesToString(addr1) - require.NoError(t, err) - createValidatorMsg, err := stakingtypes.NewMsgCreateValidator( - addrStrVal, valKey.PubKey(), bondCoin, description, commission, math.OneInt(), - ) - require.NoError(t, err) - - headerInfo := header.Info{Height: app.LastBlockHeight() + 1} - _, _, err = sims.SignCheckDeliver(t, txConfig, app.BaseApp, headerInfo, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, true, true, priv1) - require.NoError(t, err) - require.True(t, sdk.Coins{genCoin.Sub(bondCoin)}.Equal(bankKeeper.GetAllBalances(ctxCheck, addr1))) - - ctxCheck = baseApp.NewContext(true) - validator, err := stakingKeeper.GetValidator(ctxCheck, sdk.ValAddress(addr1)) - require.NoError(t, err) - - require.Equal(t, addrStrVal, validator.OperatorAddress) - require.Equal(t, stakingtypes.Bonded, validator.Status) - require.True(math.IntEq(t, bondTokens, validator.BondedTokens())) - unjailMsg := &types.MsgUnjail{ValidatorAddr: addrStrVal} - - ctxCheck = app.BaseApp.NewContext(true) - _, err = slashingKeeper.ValidatorSigningInfo.Get(ctxCheck, sdk.ConsAddress(valAddr)) - require.NoError(t, err) - - // unjail should fail with unknown validator - headerInfo = header.Info{Height: app.LastBlockHeight() + 1} - _, _, err = sims.SignCheckDeliver(t, txConfig, app.BaseApp, headerInfo, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, false, false, priv1) - require.Error(t, err) - require.True(t, errors.Is(err, types.ErrValidatorNotJailed)) -} diff --git a/tests/integration/v2/slashing/abci_test.go b/tests/integration/v2/slashing/abci_test.go index 11c7338367e8..f771941cf096 100644 --- a/tests/integration/v2/slashing/abci_test.go +++ b/tests/integration/v2/slashing/abci_test.go @@ -8,84 +8,43 @@ import ( "cosmossdk.io/core/comet" coreheader "cosmossdk.io/core/header" - "cosmossdk.io/depinject" - "cosmossdk.io/log" - bankkeeper "cosmossdk.io/x/bank/keeper" + "cosmossdk.io/runtime/v2/services" "cosmossdk.io/x/slashing" - slashingkeeper "cosmossdk.io/x/slashing/keeper" "cosmossdk.io/x/slashing/testutil" - stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtestutil "cosmossdk.io/x/staking/testutil" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" - "github.com/cosmos/cosmos-sdk/runtime" - "github.com/cosmos/cosmos-sdk/testutil/configurator" + "github.com/cosmos/cosmos-sdk/tests/integration/v2" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" -) - -var AppConfig = configurator.NewAppConfig( - configurator.AccountsModule(), - configurator.AuthModule(), - configurator.BankModule(), - configurator.StakingModule(), - configurator.SlashingModule(), - configurator.TxModule(), - configurator.ValidateModule(), - configurator.ConsensusModule(), - configurator.GenutilModule(), - configurator.MintModule(), - configurator.DistributionModule(), - configurator.ProtocolPoolModule(), ) // TestBeginBlocker is a unit test function that tests the behavior of the BeginBlocker function. // It sets up the necessary dependencies and context, creates a validator, and performs various operations // to test the slashing logic. It checks if the validator is correctly jailed after a certain number of blocks. func TestBeginBlocker(t *testing.T) { - var ( - interfaceRegistry codectypes.InterfaceRegistry - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - stakingKeeper *stakingkeeper.Keeper - slashingKeeper slashingkeeper.Keeper - ) - - app, err := simtestutil.Setup( - depinject.Configs( - AppConfig, - depinject.Supply(log.NewNopLogger()), - ), - &interfaceRegistry, - &accountKeeper, - &bankKeeper, - &stakingKeeper, - &slashingKeeper, - ) - require.NoError(t, err) + f := initFixture(t) - ctx := app.BaseApp.NewContext(false) + ctx := f.ctx pks := simtestutil.CreateTestPubKeys(1) - simtestutil.AddTestAddrsFromPubKeys(bankKeeper, stakingKeeper, ctx, pks, stakingKeeper.TokensFromConsensusPower(ctx, 200)) + simtestutil.AddTestAddrsFromPubKeys(f.bankKeeper, f.stakingKeeper, ctx, pks, f.stakingKeeper.TokensFromConsensusPower(ctx, 200)) addr, pk := sdk.ValAddress(pks[0].Address()), pks[0] - tstaking := stakingtestutil.NewHelper(t, ctx, stakingKeeper) + tstaking := stakingtestutil.NewHelper(t, ctx, f.stakingKeeper) // bond the validator power := int64(100) - acc := accountKeeper.NewAccountWithAddress(ctx, sdk.AccAddress(addr)) - accountKeeper.SetAccount(ctx, acc) + acc := f.accountKeeper.NewAccountWithAddress(ctx, sdk.AccAddress(addr)) + f.accountKeeper.SetAccount(ctx, acc) amt := tstaking.CreateValidatorWithValPower(addr, pk, power, true) - _, err = stakingKeeper.EndBlocker(ctx) + _, err := f.stakingKeeper.EndBlocker(ctx) require.NoError(t, err) - bondDenom, err := stakingKeeper.BondDenom(ctx) + bondDenom, err := f.stakingKeeper.BondDenom(ctx) require.NoError(t, err) require.Equal( - t, bankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), + t, f.bankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), sdk.NewCoins(sdk.NewCoin(bondDenom, testutil.InitTokens.Sub(amt))), ) - val, err := stakingKeeper.Validator(ctx, addr) + val, err := f.stakingKeeper.Validator(ctx, addr) require.NoError(t, err) require.Equal(t, amt, val.GetBondedTokens()) @@ -94,57 +53,58 @@ func TestBeginBlocker(t *testing.T) { Power: power, } - ctx = ctx.WithCometInfo(comet.Info{ + ctx = integration.SetCometInfo(ctx, comet.Info{ LastCommit: comet.CommitInfo{Votes: []comet.VoteInfo{{ Validator: abciVal, BlockIDFlag: comet.BlockIDFlagCommit, }}}, }) - cometInfoService := runtime.NewContextAwareCometInfoService() + cometInfoService := &services.ContextAwareCometInfoService{} - err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) + err = slashing.BeginBlocker(ctx, f.slashingKeeper, cometInfoService) require.NoError(t, err) - info, err := slashingKeeper.ValidatorSigningInfo.Get(ctx, sdk.ConsAddress(pk.Address())) + info, err := f.slashingKeeper.ValidatorSigningInfo.Get(ctx, sdk.ConsAddress(pk.Address())) require.NoError(t, err) - require.Equal(t, ctx.HeaderInfo().Height, info.StartHeight) + require.Equal(t, integration.HeaderInfoFromContext(ctx).Height, info.StartHeight) require.Equal(t, int64(0), info.IndexOffset) require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) require.Equal(t, int64(0), info.MissedBlocksCounter) height := int64(0) - signedBlocksWindow, err := slashingKeeper.SignedBlocksWindow(ctx) + signedBlocksWindow, err := f.slashingKeeper.SignedBlocksWindow(ctx) require.NoError(t, err) // for 100 blocks, mark the validator as having signed for ; height < signedBlocksWindow; height++ { - ctx = ctx.WithHeaderInfo(coreheader.Info{Height: height}) + ctx = integration.SetHeaderInfo(ctx, coreheader.Info{Height: height}) - err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) + err = slashing.BeginBlocker(ctx, f.slashingKeeper, cometInfoService) require.NoError(t, err) } - minSignedPerWindow, err := slashingKeeper.MinSignedPerWindow(ctx) + minSignedPerWindow, err := f.slashingKeeper.MinSignedPerWindow(ctx) require.NoError(t, err) // for 50 blocks, mark the validator as having not signed for ; height < ((signedBlocksWindow * 2) - minSignedPerWindow + 1); height++ { - ctx = ctx.WithHeaderInfo(coreheader.Info{Height: height}).WithCometInfo(comet.Info{ + ctx = integration.SetHeaderInfo(ctx, coreheader.Info{Height: height}) + ctx = integration.SetCometInfo(ctx, comet.Info{ LastCommit: comet.CommitInfo{Votes: []comet.VoteInfo{{ Validator: abciVal, BlockIDFlag: comet.BlockIDFlagAbsent, }}}, }) - err = slashing.BeginBlocker(ctx, slashingKeeper, cometInfoService) + err = slashing.BeginBlocker(ctx, f.slashingKeeper, cometInfoService) require.NoError(t, err) } // end block - _, err = stakingKeeper.EndBlocker(ctx) + _, err = f.stakingKeeper.EndBlocker(ctx) require.NoError(t, err) // validator should be jailed - validator, err := stakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) + validator, err := f.stakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) require.NoError(t, err) require.Equal(t, sdk.Unbonding, validator.GetStatus()) } diff --git a/tests/integration/v2/slashing/keeper_test.go b/tests/integration/v2/slashing/keeper_test.go index 8471c4fe355b..94a84de3cf12 100644 --- a/tests/integration/v2/slashing/keeper_test.go +++ b/tests/integration/v2/slashing/keeper_test.go @@ -11,119 +11,16 @@ import ( "cosmossdk.io/core/comet" coreheader "cosmossdk.io/core/header" "cosmossdk.io/core/transaction" - "cosmossdk.io/depinject" - "cosmossdk.io/log" - bankkeeper "cosmossdk.io/x/bank/keeper" - slashingkeeper "cosmossdk.io/x/slashing/keeper" "cosmossdk.io/x/slashing/testutil" slashingtypes "cosmossdk.io/x/slashing/types" - stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtestutil "cosmossdk.io/x/staking/testutil" stakingtypes "cosmossdk.io/x/staking/types" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/tests/integration/v2" - "github.com/cosmos/cosmos-sdk/testutil/configurator" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - - _ "cosmossdk.io/x/accounts" // import as blank for app wiring - _ "cosmossdk.io/x/bank" // import as blank for app wiring - _ "cosmossdk.io/x/consensus" // import as blank for app wiring - _ "cosmossdk.io/x/distribution" // import as blank for app wiring - _ "cosmossdk.io/x/mint" // import as blank for app wiring - _ "cosmossdk.io/x/protocolpool" // import as blank for app wiring - _ "cosmossdk.io/x/slashing" // import as blank for app wiring - _ "cosmossdk.io/x/staking" // import as blank for app wiring - - _ "github.com/cosmos/cosmos-sdk/x/auth" // import as blank for app wiring - _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import as blank for app wiring - _ "github.com/cosmos/cosmos-sdk/x/genutil" // import as blank for app wiring ) -type fixture struct { - app *integration.App - - ctx context.Context - cdc codec.Codec - - accountKeeper authkeeper.AccountKeeper - bankKeeper bankkeeper.Keeper - slashingKeeper slashingkeeper.Keeper - stakingKeeper *stakingkeeper.Keeper - - slashingMsgServer slashingtypes.MsgServer - txConfig client.TxConfig - - addrDels []sdk.AccAddress - valAddrs []sdk.ValAddress -} - -func initFixture(t *testing.T) *fixture { - t.Helper() - - res := fixture{} - - moduleConfigs := []configurator.ModuleOption{ - configurator.AccountsModule(), - configurator.AuthModule(), - configurator.BankModule(), - configurator.StakingModule(), - configurator.SlashingModule(), - configurator.TxModule(), - configurator.ValidateModule(), - configurator.ConsensusModule(), - configurator.GenutilModule(), - configurator.MintModule(), - configurator.DistributionModule(), - configurator.ProtocolPoolModule(), - } - - var err error - startupCfg := integration.DefaultStartUpConfig(t) - - startupCfg.BranchService = &integration.BranchService{} - startupCfg.HeaderService = &integration.HeaderService{} - - res.app, err = integration.NewApp( - depinject.Configs(configurator.NewAppV2Config(moduleConfigs...), depinject.Supply(log.NewNopLogger())), - startupCfg, - &res.bankKeeper, &res.accountKeeper, &res.stakingKeeper, &res.slashingKeeper, &res.cdc, &res.txConfig) - require.NoError(t, err) - - res.ctx = res.app.StateLatestContext(t) - - // set default staking params - // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 - err = res.slashingKeeper.Params.Set(res.ctx, testutil.TestParams()) - assert.NilError(t, err) - - addrDels := simtestutil.AddTestAddrsIncremental(res.bankKeeper, res.stakingKeeper, res.ctx, 6, res.stakingKeeper.TokensFromConsensusPower(res.ctx, 200)) - valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) - - consaddr0, err := res.stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[0]) - require.NoError(t, err) - consaddr1, err := res.stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[1]) - require.NoError(t, err) - - info1 := slashingtypes.NewValidatorSigningInfo(consaddr0, int64(4), time.Unix(2, 0), false, int64(10)) - info2 := slashingtypes.NewValidatorSigningInfo(consaddr1, int64(5), time.Unix(2, 0), false, int64(10)) - - err = res.slashingKeeper.ValidatorSigningInfo.Set(res.ctx, sdk.ConsAddress(addrDels[0]), info1) - require.NoError(t, err) - err = res.slashingKeeper.ValidatorSigningInfo.Set(res.ctx, sdk.ConsAddress(addrDels[1]), info2) - require.NoError(t, err) - - res.addrDels = addrDels - res.valAddrs = valAddrs - - res.slashingMsgServer = slashingkeeper.NewMsgServerImpl(res.slashingKeeper) - - return &res -} - func TestUnJailNotBonded(t *testing.T) { t.Parallel() f := initFixture(t) diff --git a/tests/integration/v2/slashing/slash_redelegation_test.go b/tests/integration/v2/slashing/slash_redelegation_test.go index e40b60783c3e..0fbd76c7d711 100644 --- a/tests/integration/v2/slashing/slash_redelegation_test.go +++ b/tests/integration/v2/slashing/slash_redelegation_test.go @@ -8,50 +8,24 @@ import ( "github.com/stretchr/testify/require" "cosmossdk.io/core/header" - "cosmossdk.io/depinject" - "cosmossdk.io/log" "cosmossdk.io/math" bankkeeper "cosmossdk.io/x/bank/keeper" banktestutil "cosmossdk.io/x/bank/testutil" - distributionkeeper "cosmossdk.io/x/distribution/keeper" - slashingkeeper "cosmossdk.io/x/slashing/keeper" stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtypes "cosmossdk.io/x/staking/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" - "github.com/cosmos/cosmos-sdk/tests/integration/slashing" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + "github.com/cosmos/cosmos-sdk/tests/integration/v2" sdk "github.com/cosmos/cosmos-sdk/types" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" ) func TestSlashRedelegation(t *testing.T) { - // setting up - var ( - authKeeper authkeeper.AccountKeeper - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashKeeper slashingkeeper.Keeper - distrKeeper distributionkeeper.Keeper - ) - - app, err := simtestutil.Setup( - depinject.Configs( - depinject.Supply(log.NewNopLogger()), - slashing.AppConfig, - ), - &stakingKeeper, - &bankKeeper, - &slashKeeper, - &distrKeeper, - &authKeeper, - ) - require.NoError(t, err) - - // get sdk context, staking msg server and bond denom - ctx := app.BaseApp.NewContext(false) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) - bondDenom, err := stakingKeeper.BondDenom(ctx) + f := initFixture(t) + ctx := f.ctx + + stakingMsgServer := stakingkeeper.NewMsgServerImpl(f.stakingKeeper) + bondDenom, err := f.stakingKeeper.BondDenom(ctx) require.NoError(t, err) // evilVal will be slashed, goodVal won't be slashed @@ -65,12 +39,12 @@ func TestSlashRedelegation(t *testing.T) { testAcc2 := sdk.AccAddress([]byte("addr2_______________")) // fund acc 1 and acc 2 - testCoin := sdk.NewCoin(bondDenom, stakingKeeper.TokensFromConsensusPower(ctx, 10)) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc1, testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc2, testCoin) + testCoin := sdk.NewCoin(bondDenom, f.stakingKeeper.TokensFromConsensusPower(ctx, 10)) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, testAcc1, testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, testAcc2, testCoin) - balance1Before := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) - balance2Before := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) + balance1Before := f.bankKeeper.GetBalance(ctx, testAcc1, bondDenom) + balance2Before := f.bankKeeper.GetBalance(ctx, testAcc2, bondDenom) // assert acc 1 and acc 2 balance require.Equal(t, balance1Before.Amount.String(), testCoin.Amount.String()) @@ -78,7 +52,7 @@ func TestSlashRedelegation(t *testing.T) { // creating evil val evilValAddr := sdk.ValAddress(evilValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(evilValAddr), testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, sdk.AccAddress(evilValAddr), testCoin) createValMsg1, _ := stakingtypes.NewMsgCreateValidator( evilValAddr.String(), evilValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) _, err = stakingMsgServer.CreateValidator(ctx, createValMsg1) @@ -86,16 +60,17 @@ func TestSlashRedelegation(t *testing.T) { // creating good val goodValAddr := sdk.ValAddress(goodValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(goodValAddr), testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, sdk.AccAddress(goodValAddr), testCoin) createValMsg2, _ := stakingtypes.NewMsgCreateValidator( goodValAddr.String(), goodValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) _, err = stakingMsgServer.CreateValidator(ctx, createValMsg2) require.NoError(t, err) - ctx = ctx.WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) + ctx = integration.SetHeaderInfo(ctx, header.Info{Height: 1}) // next block, commit height 1, move to height 2 // acc 1 and acc 2 delegate to evil val - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state := f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // Acc 2 delegate @@ -111,13 +86,14 @@ func TestSlashRedelegation(t *testing.T) { // next block, commit height 2, move to height 3 // with the new delegations, evil val increases in voting power and commit byzantine behavior at height 3 consensus // at the same time, acc 1 and acc 2 withdraw delegation from evil val - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) - evilVal, err := stakingKeeper.GetValidator(ctx, evilValAddr) + evilVal, err := f.stakingKeeper.GetValidator(ctx, evilValAddr) require.NoError(t, err) - evilPower := stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) + evilPower := f.stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) // Acc 1 redelegate from evil val to good val redelMsg := stakingtypes.NewMsgBeginRedelegate(testAcc1.String(), evilValAddr.String(), goodValAddr.String(), testCoin) @@ -135,81 +111,64 @@ func TestSlashRedelegation(t *testing.T) { require.NoError(t, err) // next block, commit height 3, move to height 4 - // Slash evil val for byzantine behavior at height 3 consensus, + // Slash evil val for byzantine behavior at height 2 consensus, // at which acc 1 and acc 2 still contributed to evil val voting power // even tho they undelegate at block 3, the valset update is applied after committed block 3 when height 3 consensus already passes - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // slash evil val with slash factor = 0.9, leaving only 10% of stake after slashing - evilVal, _ = stakingKeeper.GetValidator(ctx, evilValAddr) + evilVal, _ = f.stakingKeeper.GetValidator(ctx, evilValAddr) evilValConsAddr, err := evilVal.GetConsAddr() require.NoError(t, err) - err = slashKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr("0.9"), evilPower, 3) + err = f.slashingKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr("0.9"), evilPower, 2) require.NoError(t, err) - // assert invariant to make sure we conduct slashing correctly - _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) - require.False(t, stop) - - _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) - require.False(t, stop) - - _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) - require.False(t, stop) - // one eternity later - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1000000000000000000)) + ctxHeader := integration.HeaderInfoFromContext(ctx) + ctxHeader.Time = ctxHeader.Time.Add(time.Duration(1000000000000000000)) + ctx = integration.SetHeaderInfo(ctx, ctxHeader) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // confirm that account 1 and account 2 has been slashed, and the slash amount is correct - balance1AfterSlashing := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) - balance2AfterSlashing := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) + balance1AfterSlashing := f.bankKeeper.GetBalance(ctx, testAcc1, bondDenom) + balance2AfterSlashing := f.bankKeeper.GetBalance(ctx, testAcc2, bondDenom) require.Equal(t, balance1AfterSlashing.Amount.Mul(math.NewIntFromUint64(10)).String(), balance1Before.Amount.String()) require.Equal(t, balance2AfterSlashing.Amount.Mul(math.NewIntFromUint64(10)).String(), balance2Before.Amount.String()) } -func fundAccount(t *testing.T, ctx context.Context, bankKeeper bankkeeper.Keeper, authKeeper authkeeper.AccountKeeper, addr sdk.AccAddress, amount ...sdk.Coin) { +func fundAccount(t *testing.T, ctx context.Context, bankKeeper bankkeeper.Keeper, accountKeeper authkeeper.AccountKeeper, addr sdk.AccAddress, amount ...sdk.Coin) { t.Helper() - if authKeeper.GetAccount(ctx, addr) == nil { - addrAcc := authKeeper.NewAccountWithAddress(ctx, addr) - authKeeper.SetAccount(ctx, addrAcc) + if accountKeeper.GetAccount(ctx, addr) == nil { + addrAcc := accountKeeper.NewAccountWithAddress(ctx, addr) + accountKeeper.SetAccount(ctx, addrAcc) } require.NoError(t, banktestutil.FundAccount(ctx, bankKeeper, addr, amount)) } func TestOverSlashing(t *testing.T) { + f := initFixture(t) + ctx := f.ctx + // slash penalty percentage slashFraction := "0.45" // percentage of (undelegation/(undelegation + redelegation)) undelegationPercentageStr := "0.30" - // setting up - var ( - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashKeeper slashingkeeper.Keeper - distrKeeper distributionkeeper.Keeper - authKeeper authkeeper.AccountKeeper - ) - - app, err := simtestutil.Setup(depinject.Configs( - depinject.Supply(log.NewNopLogger()), - slashing.AppConfig, - ), &stakingKeeper, &bankKeeper, &slashKeeper, &distrKeeper, &authKeeper) - require.NoError(t, err) - - // get sdk context, staking msg server and bond denom - ctx := app.BaseApp.NewContext(false) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) - bondDenom, err := stakingKeeper.BondDenom(ctx) + stakingMsgServer := stakingkeeper.NewMsgServerImpl(f.stakingKeeper) + bondDenom, err := f.stakingKeeper.BondDenom(ctx) require.NoError(t, err) // evilVal will be slashed, goodVal won't be slashed @@ -231,13 +190,13 @@ func TestOverSlashing(t *testing.T) { // fund all accounts testCoin := sdk.NewCoin(bondDenom, math.NewInt(1_000_000)) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc1, testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc2, testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, testAcc3, testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, testAcc1, testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, testAcc2, testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, testAcc3, testCoin) - balance1Before := bankKeeper.GetBalance(ctx, testAcc1, bondDenom) - balance2Before := bankKeeper.GetBalance(ctx, testAcc2, bondDenom) - balance3Before := bankKeeper.GetBalance(ctx, testAcc3, bondDenom) + balance1Before := f.bankKeeper.GetBalance(ctx, testAcc1, bondDenom) + balance2Before := f.bankKeeper.GetBalance(ctx, testAcc2, bondDenom) + balance3Before := f.bankKeeper.GetBalance(ctx, testAcc3, bondDenom) // assert acc 1, 2 and 3 balance require.Equal(t, testCoin.Amount.String(), balance1Before.Amount.String()) @@ -246,7 +205,7 @@ func TestOverSlashing(t *testing.T) { // create evil val evilValAddr := sdk.ValAddress(evilValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(evilValAddr), testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, sdk.AccAddress(evilValAddr), testCoin) createValMsg1, _ := stakingtypes.NewMsgCreateValidator( evilValAddr.String(), evilValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) _, err = stakingMsgServer.CreateValidator(ctx, createValMsg1) @@ -254,16 +213,16 @@ func TestOverSlashing(t *testing.T) { // create good val 1 goodValAddr := sdk.ValAddress(goodValPubKey.Address()) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(goodValAddr), testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, sdk.AccAddress(goodValAddr), testCoin) createValMsg2, _ := stakingtypes.NewMsgCreateValidator( goodValAddr.String(), goodValPubKey, testCoin, stakingtypes.Description{Details: "test"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) _, err = stakingMsgServer.CreateValidator(ctx, createValMsg2) require.NoError(t, err) // next block - ctx = ctx.WithBlockHeight(app.LastBlockHeight() + 1).WithHeaderInfo(header.Info{Height: app.LastBlockHeight() + 1}) - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) - require.NoError(t, err) + ctx = integration.SetHeaderInfo(ctx, header.Info{Height: int64(f.app.LastBlockHeight()) + 1}) + _, state := f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) // delegate all accs to evil val delMsg := stakingtypes.NewMsgDelegate(testAcc1.String(), evilValAddr.String(), testCoin) @@ -279,21 +238,23 @@ func TestOverSlashing(t *testing.T) { require.NoError(t, err) // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // evilValAddr done something bad - misbehaveHeight := ctx.BlockHeader().Height - evilVal, err := stakingKeeper.GetValidator(ctx, evilValAddr) + misbehaveHeight := integration.HeaderInfoFromContext(ctx).Height + evilVal, err := f.stakingKeeper.GetValidator(ctx, evilValAddr) require.NoError(t, err) evilValConsAddr, err := evilVal.GetConsAddr() require.NoError(t, err) - evilPower := stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) + evilPower := f.stakingKeeper.TokensToConsensusPower(ctx, evilVal.Tokens) // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // acc 1: redelegate to goodval1 and undelegate FULL amount @@ -319,7 +280,8 @@ func TestOverSlashing(t *testing.T) { amountToUndelegate := undelegationAmountDec.TruncateInt() // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) portionofTestCoins := sdk.NewCoin(bondDenom, amountToUndelegate) @@ -328,38 +290,35 @@ func TestOverSlashing(t *testing.T) { require.NoError(t, err) // next block - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // slash the evil val - err = slashKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr(slashFraction), evilPower, misbehaveHeight) + err = f.slashingKeeper.Slash(ctx, evilValConsAddr, math.LegacyMustNewDecFromStr(slashFraction), evilPower, misbehaveHeight) require.NoError(t, err) - // assert invariants - _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) - require.False(t, stop) - _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) - require.False(t, stop) - _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) - require.False(t, stop) - // fastforward 2 blocks to complete redelegations and unbondings for i := 0; i < 2; i++ { - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1000000000000000000)) + ctxHeader := integration.HeaderInfoFromContext(ctx) + ctxHeader.Time = ctxHeader.Time.Add(time.Duration(1000000000000000000)) + ctx = integration.SetHeaderInfo(ctx, ctxHeader) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) } // we check all accounts should be slashed with the equal amount, and they should end up with same balance including staked amount - stakedAcc1, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc1) + stakedAcc1, err := f.stakingKeeper.GetDelegatorBonded(ctx, testAcc1) require.NoError(t, err) - stakedAcc2, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc2) + stakedAcc2, err := f.stakingKeeper.GetDelegatorBonded(ctx, testAcc2) require.NoError(t, err) - stakedAcc3, err := stakingKeeper.GetDelegatorBonded(ctx, testAcc3) + stakedAcc3, err := f.stakingKeeper.GetDelegatorBonded(ctx, testAcc3) require.NoError(t, err) - balance1AfterSlashing := bankKeeper.GetBalance(ctx, testAcc1, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc1)) - balance2AfterSlashing := bankKeeper.GetBalance(ctx, testAcc2, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc2)) - balance3AfterSlashing := bankKeeper.GetBalance(ctx, testAcc3, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc3)) + balance1AfterSlashing := f.bankKeeper.GetBalance(ctx, testAcc1, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc1)) + balance2AfterSlashing := f.bankKeeper.GetBalance(ctx, testAcc2, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc2)) + balance3AfterSlashing := f.bankKeeper.GetBalance(ctx, testAcc3, bondDenom).Add(sdk.NewCoin(bondDenom, stakedAcc3)) require.Equal(t, "550000stake", balance1AfterSlashing.String()) require.Equal(t, "550000stake", balance2AfterSlashing.String()) @@ -367,32 +326,11 @@ func TestOverSlashing(t *testing.T) { } func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { - // setting up - var ( - authKeeper authkeeper.AccountKeeper - stakingKeeper *stakingkeeper.Keeper - bankKeeper bankkeeper.Keeper - slashKeeper slashingkeeper.Keeper - distrKeeper distributionkeeper.Keeper - ) - - app, err := simtestutil.Setup( - depinject.Configs( - depinject.Supply(log.NewNopLogger()), - slashing.AppConfig, - ), - &stakingKeeper, - &bankKeeper, - &slashKeeper, - &distrKeeper, - &authKeeper, - ) - require.NoError(t, err) - - // get sdk context, staking msg server and bond denom - ctx := app.BaseApp.NewContext(false).WithBlockHeight(1).WithHeaderInfo(header.Info{Height: 1}) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(stakingKeeper) - bondDenom, err := stakingKeeper.BondDenom(ctx) + f := initFixture(t) + ctx := f.ctx + + stakingMsgServer := stakingkeeper.NewMsgServerImpl(f.stakingKeeper) + bondDenom, err := f.stakingKeeper.BondDenom(ctx) require.NoError(t, err) // create validators DST and SRC @@ -402,9 +340,9 @@ func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { dstAddr := sdk.ValAddress(dstPubKey.Address()) srcAddr := sdk.ValAddress(srcPubKey.Address()) - testCoin := sdk.NewCoin(bondDenom, stakingKeeper.TokensFromConsensusPower(ctx, 1000)) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(dstAddr), testCoin) - fundAccount(t, ctx, bankKeeper, authKeeper, sdk.AccAddress(srcAddr), testCoin) + testCoin := sdk.NewCoin(bondDenom, f.stakingKeeper.TokensFromConsensusPower(ctx, 1000)) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, sdk.AccAddress(dstAddr), testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, sdk.AccAddress(srcAddr), testCoin) createValMsgDST, _ := stakingtypes.NewMsgCreateValidator( dstAddr.String(), dstPubKey, testCoin, stakingtypes.Description{Details: "Validator DST"}, stakingtypes.NewCommissionRates(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0)), math.OneInt()) @@ -418,10 +356,10 @@ func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { // create a user accounts and delegate to SRC and DST userAcc := sdk.AccAddress([]byte("user1_______________")) - fundAccount(t, ctx, bankKeeper, authKeeper, userAcc, testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, userAcc, testCoin) userAcc2 := sdk.AccAddress([]byte("user2_______________")) - fundAccount(t, ctx, bankKeeper, authKeeper, userAcc2, testCoin) + fundAccount(t, ctx, f.bankKeeper, f.accountKeeper, userAcc2, testCoin) delMsg := stakingtypes.NewMsgDelegate(userAcc.String(), srcAddr.String(), testCoin) _, err = stakingMsgServer.Delegate(ctx, delMsg) @@ -431,18 +369,20 @@ func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { _, err = stakingMsgServer.Delegate(ctx, delMsg) require.NoError(t, err) - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state := f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // commit an infraction with DST and store the power at this height - dstVal, err := stakingKeeper.GetValidator(ctx, dstAddr) + dstVal, err := f.stakingKeeper.GetValidator(ctx, dstAddr) require.NoError(t, err) - dstPower := stakingKeeper.TokensToConsensusPower(ctx, dstVal.Tokens) + dstPower := f.stakingKeeper.TokensToConsensusPower(ctx, dstVal.Tokens) dstConsAddr, err := dstVal.GetConsAddr() require.NoError(t, err) - dstInfractionHeight := ctx.BlockHeight() + dstInfractionHeight := f.app.LastBlockHeight() + 1 - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // undelegate all the user tokens from DST @@ -451,14 +391,15 @@ func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { require.NoError(t, err) // commit an infraction with SRC and store the power at this height - srcVal, err := stakingKeeper.GetValidator(ctx, srcAddr) + srcVal, err := f.stakingKeeper.GetValidator(ctx, srcAddr) require.NoError(t, err) - srcPower := stakingKeeper.TokensToConsensusPower(ctx, srcVal.Tokens) + srcPower := f.stakingKeeper.TokensToConsensusPower(ctx, srcVal.Tokens) srcConsAddr, err := srcVal.GetConsAddr() require.NoError(t, err) - srcInfractionHeight := ctx.BlockHeight() + srcInfractionHeight := f.app.LastBlockHeight() + 1 - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) // redelegate all the user tokens from SRC to DST @@ -471,7 +412,8 @@ func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { _, err = stakingMsgServer.Undelegate(ctx, undelMsg) require.NoError(t, err) - ctx, err = simtestutil.NextBlock(app, ctx, time.Duration(1)) + _, state = f.app.Deliver(t, ctx, nil) + _, err = f.app.Commit(state) require.NoError(t, err) undelMsg = stakingtypes.NewMsgUndelegate(userAcc.String(), dstAddr.String(), testCoin) @@ -479,24 +421,14 @@ func TestSlashRedelegation_ValidatorLeftWithNoTokens(t *testing.T) { require.NoError(t, err) // check that dst now has zero tokens - valDst, err := stakingKeeper.GetValidator(ctx, dstAddr) + valDst, err := f.stakingKeeper.GetValidator(ctx, dstAddr) require.NoError(t, err) require.Equal(t, math.ZeroInt().String(), valDst.Tokens.String()) // slash the infractions - err = slashKeeper.Slash(ctx, dstConsAddr, math.LegacyMustNewDecFromStr("0.8"), dstPower, dstInfractionHeight) + err = f.slashingKeeper.Slash(ctx, dstConsAddr, math.LegacyMustNewDecFromStr("0.8"), dstPower, int64(dstInfractionHeight)) require.NoError(t, err) - err = slashKeeper.Slash(ctx, srcConsAddr, math.LegacyMustNewDecFromStr("0.5"), srcPower, srcInfractionHeight) + err = f.slashingKeeper.Slash(ctx, srcConsAddr, math.LegacyMustNewDecFromStr("0.5"), srcPower, int64(srcInfractionHeight)) require.NoError(t, err) - - // assert invariants to ensure correctness - _, stop := stakingkeeper.AllInvariants(stakingKeeper)(ctx) - require.False(t, stop) - - _, stop = bankkeeper.AllInvariants(bankKeeper)(ctx) - require.False(t, stop) - - _, stop = distributionkeeper.AllInvariants(distrKeeper)(ctx) - require.False(t, stop) } diff --git a/tests/integration/v2/slashing/slashing_test.go b/tests/integration/v2/slashing/slashing_test.go index dff3df80a2f9..b701c9dc5b48 100644 --- a/tests/integration/v2/slashing/slashing_test.go +++ b/tests/integration/v2/slashing/slashing_test.go @@ -3,21 +3,48 @@ package slashing import ( "context" "testing" + "time" "github.com/stretchr/testify/require" + "gotest.tools/v3/assert" - "cosmossdk.io/core/transaction" + "cosmossdk.io/depinject" + "cosmossdk.io/log" "cosmossdk.io/math" + distrkeeper "cosmossdk.io/x/distribution/keeper" + slashingkeeper "cosmossdk.io/x/slashing/keeper" + "cosmossdk.io/x/slashing/testutil" "cosmossdk.io/x/slashing/types" + slashingtypes "cosmossdk.io/x/slashing/types" stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtypes "cosmossdk.io/x/staking/types" + bankkeeper "cosmossdk.io/x/bank/keeper" banktestutil "cosmossdk.io/x/bank/testutil" + "github.com/cosmos/cosmos-sdk/client" codecaddress "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/tests/integration/v2" + "github.com/cosmos/cosmos-sdk/testutil/configurator" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + _ "cosmossdk.io/x/accounts" // import as blank for app wiring + _ "cosmossdk.io/x/bank" // import as blank for app wiring + _ "cosmossdk.io/x/consensus" // import as blank for app wiring + _ "cosmossdk.io/x/distribution" // import as blank for app wiring + _ "cosmossdk.io/x/mint" // import as blank for app wiring + _ "cosmossdk.io/x/protocolpool" // import as blank for app wiring + _ "cosmossdk.io/x/slashing" // import as blank for app wiring + _ "cosmossdk.io/x/staking" // import as blank for app wiring + + _ "github.com/cosmos/cosmos-sdk/x/auth" // import as blank for app wiring + _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import as blank for app wiring + _ "github.com/cosmos/cosmos-sdk/x/genutil" // import as blank for app wiring ) var ( @@ -30,6 +57,89 @@ var ( valAddr = sdk.AccAddress(valKey.PubKey().Address()) ) +type fixture struct { + app *integration.App + + ctx context.Context + + accountKeeper authkeeper.AccountKeeper + bankKeeper bankkeeper.Keeper + slashingKeeper slashingkeeper.Keeper + stakingKeeper *stakingkeeper.Keeper + distrKeeper distrkeeper.Keeper + + slashingMsgServer slashingtypes.MsgServer + txConfig client.TxConfig + + valAddrs []sdk.ValAddress +} + +func initFixture(t *testing.T) *fixture { + t.Helper() + + res := fixture{} + + moduleConfigs := []configurator.ModuleOption{ + configurator.AccountsModule(), + configurator.AuthModule(), + configurator.BankModule(), + configurator.StakingModule(), + configurator.SlashingModule(), + configurator.TxModule(), + configurator.ValidateModule(), + configurator.ConsensusModule(), + configurator.GenutilModule(), + configurator.MintModule(), + configurator.DistributionModule(), + configurator.ProtocolPoolModule(), + } + + acc := &authtypes.BaseAccount{ + Address: addr1.String(), + } + + var err error + startupCfg := integration.DefaultStartUpConfig(t) + startupCfg.GenesisAccounts = []integration.GenesisAccount{{GenesisAccount: acc}} + + startupCfg.BranchService = &integration.BranchService{} + startupCfg.HeaderService = &integration.HeaderService{} + + res.app, err = integration.NewApp( + depinject.Configs(configurator.NewAppV2Config(moduleConfigs...), depinject.Supply(log.NewNopLogger())), + startupCfg, + &res.bankKeeper, &res.accountKeeper, &res.stakingKeeper, &res.slashingKeeper, &res.distrKeeper, &res.txConfig) + require.NoError(t, err) + + res.ctx = res.app.StateLatestContext(t) + + // set default staking params + // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 + err = res.slashingKeeper.Params.Set(res.ctx, testutil.TestParams()) + assert.NilError(t, err) + + addrDels := simtestutil.AddTestAddrsIncremental(res.bankKeeper, res.stakingKeeper, res.ctx, 6, res.stakingKeeper.TokensFromConsensusPower(res.ctx, 200)) + valAddrs := simtestutil.ConvertAddrsToValAddrs(addrDels) + + consaddr0, err := res.stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[0]) + require.NoError(t, err) + consaddr1, err := res.stakingKeeper.ConsensusAddressCodec().BytesToString(addrDels[1]) + require.NoError(t, err) + + info1 := slashingtypes.NewValidatorSigningInfo(consaddr0, int64(4), time.Unix(2, 0), false, int64(10)) + info2 := slashingtypes.NewValidatorSigningInfo(consaddr1, int64(5), time.Unix(2, 0), false, int64(10)) + + err = res.slashingKeeper.ValidatorSigningInfo.Set(res.ctx, sdk.ConsAddress(addrDels[0]), info1) + require.NoError(t, err) + err = res.slashingKeeper.ValidatorSigningInfo.Set(res.ctx, sdk.ConsAddress(addrDels[1]), info2) + require.NoError(t, err) + + res.valAddrs = valAddrs + res.slashingMsgServer = slashingkeeper.NewMsgServerImpl(res.slashingKeeper) + + return &res +} + func TestSlashingMsgs(t *testing.T) { f := initFixture(t) @@ -38,8 +148,6 @@ func TestSlashingMsgs(t *testing.T) { genCoin := sdk.NewCoin(sdk.DefaultBondDenom, genTokens) bondCoin := sdk.NewCoin(sdk.DefaultBondDenom, bondTokens) - acc := f.accountKeeper.NewAccountWithAddress(f.ctx, addr1) - f.accountKeeper.SetAccount(f.ctx, acc) require.NoError(t, banktestutil.FundAccount(f.ctx, f.bankKeeper, addr1, sdk.NewCoins(genCoin))) description := stakingtypes.NewDescription("foo_moniker", "", "", "", "", &stakingtypes.Metadata{}) @@ -51,20 +159,8 @@ func TestSlashingMsgs(t *testing.T) { addrStrVal, valKey.PubKey(), bondCoin, description, commission, math.OneInt(), ) - stakingMsgServer := stakingkeeper.NewMsgServerImpl(f.stakingKeeper) - _, err = f.app.RunMsg( - t, - f.ctx, - func(ctx context.Context) (transaction.Msg, error) { - res, err := stakingMsgServer.CreateValidator(ctx, createValidatorMsg) - return res, err - }, - integration.WithAutomaticCommit(), - ) - + _ = f.app.SignCheckDeliver(t, f.ctx, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, []cryptotypes.PrivKey{priv1}, "") require.True(t, sdk.Coins{genCoin.Sub(bondCoin)}.Equal(f.bankKeeper.GetAllBalances(f.ctx, addr1))) - _, err = f.stakingKeeper.EndBlocker(f.ctx) - require.NoError(t, err) validator, err := f.stakingKeeper.GetValidator(f.ctx, sdk.ValAddress(addr1)) require.NoError(t, err) @@ -77,15 +173,6 @@ func TestSlashingMsgs(t *testing.T) { _, err = f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, sdk.ConsAddress(valAddr)) require.NoError(t, err) - // unjail should fail with validator not jailed - _, err = f.app.RunMsg( - t, - f.ctx, - func(ctx context.Context) (transaction.Msg, error) { - res, err := f.slashingMsgServer.Unjail(ctx, unjailMsg) - return res, err - }, - integration.WithAutomaticCommit(), - ) - require.ErrorIs(t, err, types.ErrValidatorNotJailed) + // unjail should fail with validator not jailed error + _ = f.app.SignCheckDeliver(t, f.ctx, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, []cryptotypes.PrivKey{priv1}, types.ErrValidatorNotJailed.Error()) } From c4fd8c0dded9bd6934f298433a64658e58a06bdc Mon Sep 17 00:00:00 2001 From: akhilkumarpilli Date: Thu, 5 Dec 2024 17:22:12 +0530 Subject: [PATCH 4/5] fix lint --- .../v2/slashing/slash_redelegation_test.go | 4 --- .../integration/v2/slashing/slashing_test.go | 28 +++++++++---------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/tests/integration/v2/slashing/slash_redelegation_test.go b/tests/integration/v2/slashing/slash_redelegation_test.go index 0fbd76c7d711..0b3c76fe8f1e 100644 --- a/tests/integration/v2/slashing/slash_redelegation_test.go +++ b/tests/integration/v2/slashing/slash_redelegation_test.go @@ -134,10 +134,6 @@ func TestSlashRedelegation(t *testing.T) { _, err = f.app.Commit(state) require.NoError(t, err) - _, state = f.app.Deliver(t, ctx, nil) - _, err = f.app.Commit(state) - require.NoError(t, err) - // confirm that account 1 and account 2 has been slashed, and the slash amount is correct balance1AfterSlashing := f.bankKeeper.GetBalance(ctx, testAcc1, bondDenom) balance2AfterSlashing := f.bankKeeper.GetBalance(ctx, testAcc2, bondDenom) diff --git a/tests/integration/v2/slashing/slashing_test.go b/tests/integration/v2/slashing/slashing_test.go index b701c9dc5b48..240163db75d4 100644 --- a/tests/integration/v2/slashing/slashing_test.go +++ b/tests/integration/v2/slashing/slashing_test.go @@ -11,16 +11,24 @@ import ( "cosmossdk.io/depinject" "cosmossdk.io/log" "cosmossdk.io/math" + _ "cosmossdk.io/x/accounts" // import as blank for app wiring + _ "cosmossdk.io/x/bank" // import as blank for app wiring + bankkeeper "cosmossdk.io/x/bank/keeper" + banktestutil "cosmossdk.io/x/bank/testutil" + _ "cosmossdk.io/x/consensus" // import as blank for app wiring + _ "cosmossdk.io/x/distribution" // import as blank for app wiring distrkeeper "cosmossdk.io/x/distribution/keeper" + _ "cosmossdk.io/x/mint" // import as blank for app wiring + _ "cosmossdk.io/x/protocolpool" // import as blank for app wiring + _ "cosmossdk.io/x/slashing" // import as blank for app wiring slashingkeeper "cosmossdk.io/x/slashing/keeper" "cosmossdk.io/x/slashing/testutil" "cosmossdk.io/x/slashing/types" slashingtypes "cosmossdk.io/x/slashing/types" + _ "cosmossdk.io/x/staking" // import as blank for app wiring stakingkeeper "cosmossdk.io/x/staking/keeper" stakingtypes "cosmossdk.io/x/staking/types" - bankkeeper "cosmossdk.io/x/bank/keeper" - banktestutil "cosmossdk.io/x/bank/testutil" "github.com/cosmos/cosmos-sdk/client" codecaddress "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" @@ -30,21 +38,11 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/configurator" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/x/auth" // import as blank for app wiring authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - _ "cosmossdk.io/x/accounts" // import as blank for app wiring - _ "cosmossdk.io/x/bank" // import as blank for app wiring - _ "cosmossdk.io/x/consensus" // import as blank for app wiring - _ "cosmossdk.io/x/distribution" // import as blank for app wiring - _ "cosmossdk.io/x/mint" // import as blank for app wiring - _ "cosmossdk.io/x/protocolpool" // import as blank for app wiring - _ "cosmossdk.io/x/slashing" // import as blank for app wiring - _ "cosmossdk.io/x/staking" // import as blank for app wiring - - _ "github.com/cosmos/cosmos-sdk/x/auth" // import as blank for app wiring _ "github.com/cosmos/cosmos-sdk/x/auth/tx/config" // import as blank for app wiring - _ "github.com/cosmos/cosmos-sdk/x/genutil" // import as blank for app wiring + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + _ "github.com/cosmos/cosmos-sdk/x/genutil" // import as blank for app wiring ) var ( From c4f83f2dfbf1c6e6729a8b72965b40d523ee8f63 Mon Sep 17 00:00:00 2001 From: akhilkumarpilli Date: Thu, 5 Dec 2024 17:48:06 +0530 Subject: [PATCH 5/5] address comments --- tests/integration/v2/slashing/slash_redelegation_test.go | 1 + tests/integration/v2/slashing/slashing_test.go | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/v2/slashing/slash_redelegation_test.go b/tests/integration/v2/slashing/slash_redelegation_test.go index 0b3c76fe8f1e..4d4b56ff2d33 100644 --- a/tests/integration/v2/slashing/slash_redelegation_test.go +++ b/tests/integration/v2/slashing/slash_redelegation_test.go @@ -219,6 +219,7 @@ func TestOverSlashing(t *testing.T) { ctx = integration.SetHeaderInfo(ctx, header.Info{Height: int64(f.app.LastBlockHeight()) + 1}) _, state := f.app.Deliver(t, ctx, nil) _, err = f.app.Commit(state) + require.NoError(t, err) // delegate all accs to evil val delMsg := stakingtypes.NewMsgDelegate(testAcc1.String(), evilValAddr.String(), testCoin) diff --git a/tests/integration/v2/slashing/slashing_test.go b/tests/integration/v2/slashing/slashing_test.go index 240163db75d4..bc835e84a39a 100644 --- a/tests/integration/v2/slashing/slashing_test.go +++ b/tests/integration/v2/slashing/slashing_test.go @@ -23,7 +23,6 @@ import ( _ "cosmossdk.io/x/slashing" // import as blank for app wiring slashingkeeper "cosmossdk.io/x/slashing/keeper" "cosmossdk.io/x/slashing/testutil" - "cosmossdk.io/x/slashing/types" slashingtypes "cosmossdk.io/x/slashing/types" _ "cosmossdk.io/x/staking" // import as blank for app wiring stakingkeeper "cosmossdk.io/x/staking/keeper" @@ -48,7 +47,6 @@ import ( var ( priv1 = secp256k1.GenPrivKey() addr1 = sdk.AccAddress(priv1.PubKey().Address()) - addrCodec = codecaddress.NewBech32Codec("cosmos") valaddrCodec = codecaddress.NewBech32Codec("cosmosvaloper") valKey = ed25519.GenPrivKey() @@ -156,6 +154,7 @@ func TestSlashingMsgs(t *testing.T) { createValidatorMsg, err := stakingtypes.NewMsgCreateValidator( addrStrVal, valKey.PubKey(), bondCoin, description, commission, math.OneInt(), ) + require.NoError(t, err) _ = f.app.SignCheckDeliver(t, f.ctx, []sdk.Msg{createValidatorMsg}, "", []uint64{0}, []uint64{0}, []cryptotypes.PrivKey{priv1}, "") require.True(t, sdk.Coins{genCoin.Sub(bondCoin)}.Equal(f.bankKeeper.GetAllBalances(f.ctx, addr1))) @@ -166,11 +165,11 @@ func TestSlashingMsgs(t *testing.T) { require.Equal(t, addrStrVal, validator.OperatorAddress) require.Equal(t, stakingtypes.Bonded, validator.Status) require.True(math.IntEq(t, bondTokens, validator.BondedTokens())) - unjailMsg := &types.MsgUnjail{ValidatorAddr: addrStrVal} + unjailMsg := &slashingtypes.MsgUnjail{ValidatorAddr: addrStrVal} _, err = f.slashingKeeper.ValidatorSigningInfo.Get(f.ctx, sdk.ConsAddress(valAddr)) require.NoError(t, err) // unjail should fail with validator not jailed error - _ = f.app.SignCheckDeliver(t, f.ctx, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, []cryptotypes.PrivKey{priv1}, types.ErrValidatorNotJailed.Error()) + _ = f.app.SignCheckDeliver(t, f.ctx, []sdk.Msg{unjailMsg}, "", []uint64{0}, []uint64{1}, []cryptotypes.PrivKey{priv1}, slashingtypes.ErrValidatorNotJailed.Error()) }