github.com/Finschia/finschia-sdk@v0.49.1/x/staking/keeper/slash_test.go (about)

     1  package keeper_test
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    10  
    11  	"github.com/Finschia/finschia-sdk/simapp"
    12  	sdk "github.com/Finschia/finschia-sdk/types"
    13  	"github.com/Finschia/finschia-sdk/x/staking/keeper"
    14  	"github.com/Finschia/finschia-sdk/x/staking/teststaking"
    15  	"github.com/Finschia/finschia-sdk/x/staking/types"
    16  )
    17  
    18  // bootstrapSlashTest creates 3 validators and bootstrap the app.
    19  func bootstrapSlashTest(t *testing.T, power int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) {
    20  	t.Helper()
    21  	_, app, ctx := createTestInput()
    22  
    23  	addrDels, addrVals := generateAddresses(app, ctx, 100)
    24  
    25  	amt := app.StakingKeeper.TokensFromConsensusPower(ctx, power)
    26  	totalSupply := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(int64(len(addrDels)))))
    27  
    28  	notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
    29  	require.NoError(t, simapp.FundModuleAccount(app, ctx, notBondedPool.GetName(), totalSupply))
    30  
    31  	app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
    32  
    33  	numVals := int64(3)
    34  	bondedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), amt.MulRaw(numVals)))
    35  	bondedPool := app.StakingKeeper.GetBondedPool(ctx)
    36  
    37  	// set bonded pool balance
    38  	app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
    39  	require.NoError(t, simapp.FundModuleAccount(app, ctx, bondedPool.GetName(), bondedCoins))
    40  
    41  	for i := int64(0); i < numVals; i++ {
    42  		validator := teststaking.NewValidator(t, addrVals[i], PKs[i])
    43  		validator, _ = validator.AddTokensFromDel(amt)
    44  		validator = keeper.TestingUpdateValidator(app.StakingKeeper, ctx, validator, true)
    45  		err := app.StakingKeeper.SetValidatorByConsAddr(ctx, validator)
    46  		require.NoError(t, err)
    47  	}
    48  
    49  	return app, ctx, addrDels, addrVals
    50  }
    51  
    52  // tests Jail, Unjail
    53  func TestRevocation(t *testing.T) {
    54  	app, ctx, _, addrVals := bootstrapSlashTest(t, 5)
    55  
    56  	consAddr := sdk.ConsAddress(PKs[0].Address())
    57  
    58  	// initial state
    59  	val, found := app.StakingKeeper.GetValidator(ctx, addrVals[0])
    60  	require.True(t, found)
    61  	require.False(t, val.IsJailed())
    62  
    63  	// test jail
    64  	app.StakingKeeper.Jail(ctx, consAddr)
    65  	val, found = app.StakingKeeper.GetValidator(ctx, addrVals[0])
    66  	require.True(t, found)
    67  	require.True(t, val.IsJailed())
    68  
    69  	// test unjail
    70  	app.StakingKeeper.Unjail(ctx, consAddr)
    71  	val, found = app.StakingKeeper.GetValidator(ctx, addrVals[0])
    72  	require.True(t, found)
    73  	require.False(t, val.IsJailed())
    74  }
    75  
    76  // tests slashUnbondingDelegation
    77  func TestSlashUnbondingDelegation(t *testing.T) {
    78  	app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
    79  
    80  	fraction := sdk.NewDecWithPrec(5, 1)
    81  
    82  	// set an unbonding delegation with expiration timestamp (beyond which the
    83  	// unbonding delegation shouldn't be slashed)
    84  	ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0,
    85  		time.Unix(5, 0), sdk.NewInt(10))
    86  
    87  	app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
    88  
    89  	// unbonding started prior to the infraction height, stakw didn't contribute
    90  	slashAmount := app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 1, fraction)
    91  	require.True(t, slashAmount.Equal(sdk.NewInt(0)))
    92  
    93  	// after the expiration time, no longer eligible for slashing
    94  	ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)})
    95  	app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
    96  	slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction)
    97  	require.True(t, slashAmount.Equal(sdk.NewInt(0)))
    98  
    99  	// test valid slash, before expiration timestamp and to which stake contributed
   100  	notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
   101  	oldUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress())
   102  	ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)})
   103  	app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
   104  	slashAmount = app.StakingKeeper.SlashUnbondingDelegation(ctx, ubd, 0, fraction)
   105  	require.True(t, slashAmount.Equal(sdk.NewInt(5)))
   106  	ubd, found := app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
   107  	require.True(t, found)
   108  	require.Len(t, ubd.Entries, 1)
   109  
   110  	// initial balance unchanged
   111  	require.Equal(t, sdk.NewInt(10), ubd.Entries[0].InitialBalance)
   112  
   113  	// balance decreased
   114  	require.Equal(t, sdk.NewInt(5), ubd.Entries[0].Balance)
   115  	newUnbondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, notBondedPool.GetAddress())
   116  	diffTokens := oldUnbondedPoolBalances.Sub(newUnbondedPoolBalances)
   117  	require.True(t, diffTokens.AmountOf(app.StakingKeeper.BondDenom(ctx)).Equal(sdk.NewInt(5)))
   118  }
   119  
   120  // tests slashRedelegation
   121  func TestSlashRedelegation(t *testing.T) {
   122  	app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
   123  	fraction := sdk.NewDecWithPrec(5, 1)
   124  
   125  	// add bonded tokens to pool for (re)delegations
   126  	startCoins := sdk.NewCoins(sdk.NewInt64Coin(app.StakingKeeper.BondDenom(ctx), 15))
   127  	bondedPool := app.StakingKeeper.GetBondedPool(ctx)
   128  	_ = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   129  
   130  	require.NoError(t, simapp.FundModuleAccount(app, ctx, bondedPool.GetName(), startCoins))
   131  	app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
   132  
   133  	// set a redelegation with an expiration timestamp beyond which the
   134  	// redelegation shouldn't be slashed
   135  	rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0,
   136  		time.Unix(5, 0), sdk.NewInt(10), sdk.NewDec(10))
   137  
   138  	app.StakingKeeper.SetRedelegation(ctx, rd)
   139  
   140  	// set the associated delegation
   141  	del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDec(10))
   142  	app.StakingKeeper.SetDelegation(ctx, del)
   143  
   144  	// started redelegating prior to the current height, stake didn't contribute to infraction
   145  	validator, found := app.StakingKeeper.GetValidator(ctx, addrVals[1])
   146  	require.True(t, found)
   147  	slashAmount := app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 1, fraction)
   148  	require.True(t, slashAmount.Equal(sdk.NewInt(0)))
   149  
   150  	// after the expiration time, no longer eligible for slashing
   151  	ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(10, 0)})
   152  	app.StakingKeeper.SetRedelegation(ctx, rd)
   153  	validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1])
   154  	require.True(t, found)
   155  	slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction)
   156  	require.True(t, slashAmount.Equal(sdk.NewInt(0)))
   157  
   158  	balances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   159  
   160  	// test valid slash, before expiration timestamp and to which stake contributed
   161  	ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Unix(0, 0)})
   162  	app.StakingKeeper.SetRedelegation(ctx, rd)
   163  	validator, found = app.StakingKeeper.GetValidator(ctx, addrVals[1])
   164  	require.True(t, found)
   165  	slashAmount = app.StakingKeeper.SlashRedelegation(ctx, validator, rd, 0, fraction)
   166  	require.True(t, slashAmount.Equal(sdk.NewInt(5)))
   167  	rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
   168  	require.True(t, found)
   169  	require.Len(t, rd.Entries, 1)
   170  
   171  	// end block
   172  	applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
   173  
   174  	// initialbalance unchanged
   175  	require.Equal(t, sdk.NewInt(10), rd.Entries[0].InitialBalance)
   176  
   177  	// shares decreased
   178  	del, found = app.StakingKeeper.GetDelegation(ctx, addrDels[0], addrVals[1])
   179  	require.True(t, found)
   180  	require.Equal(t, int64(5), del.Shares.RoundInt64())
   181  
   182  	// pool bonded tokens should decrease
   183  	burnedCoins := sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.BondDenom(ctx), slashAmount))
   184  	require.Equal(t, balances.Sub(burnedCoins), app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress()))
   185  }
   186  
   187  // tests Slash at a future height (must panic)
   188  func TestSlashAtFutureHeight(t *testing.T) {
   189  	app, ctx, _, _ := bootstrapSlashTest(t, 10)
   190  
   191  	consAddr := sdk.ConsAddress(PKs[0].Address())
   192  	fraction := sdk.NewDecWithPrec(5, 1)
   193  	require.Panics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 1, 10, fraction) })
   194  }
   195  
   196  // test slash at a negative height
   197  // this just represents pre-genesis and should have the same effect as slashing at height 0
   198  func TestSlashAtNegativeHeight(t *testing.T) {
   199  	app, ctx, _, _ := bootstrapSlashTest(t, 10)
   200  	consAddr := sdk.ConsAddress(PKs[0].Address())
   201  	fraction := sdk.NewDecWithPrec(5, 1)
   202  
   203  	bondedPool := app.StakingKeeper.GetBondedPool(ctx)
   204  	oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   205  
   206  	_, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   207  	require.True(t, found)
   208  	app.StakingKeeper.Slash(ctx, consAddr, -2, 10, fraction)
   209  
   210  	// read updated state
   211  	validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   212  	require.True(t, found)
   213  
   214  	// end block
   215  	applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
   216  
   217  	validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator())
   218  	require.True(t, found)
   219  	// power decreased
   220  	require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   221  
   222  	// pool bonded shares decreased
   223  	newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   224  	diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx))
   225  	require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String())
   226  }
   227  
   228  // tests Slash at the current height
   229  func TestSlashValidatorAtCurrentHeight(t *testing.T) {
   230  	app, ctx, _, _ := bootstrapSlashTest(t, 10)
   231  	consAddr := sdk.ConsAddress(PKs[0].Address())
   232  	fraction := sdk.NewDecWithPrec(5, 1)
   233  
   234  	bondedPool := app.StakingKeeper.GetBondedPool(ctx)
   235  	oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   236  
   237  	_, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   238  	require.True(t, found)
   239  	app.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction)
   240  
   241  	// read updated state
   242  	validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   243  	require.True(t, found)
   244  
   245  	// end block
   246  	applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
   247  
   248  	validator, found = app.StakingKeeper.GetValidator(ctx, validator.GetOperator())
   249  	assert.True(t, found)
   250  	// power decreased
   251  	require.Equal(t, int64(5), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   252  
   253  	// pool bonded shares decreased
   254  	newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   255  	diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx))
   256  	require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 5).String(), diffTokens.String())
   257  }
   258  
   259  // tests Slash at a previous height with an unbonding delegation
   260  func TestSlashWithUnbondingDelegation(t *testing.T) {
   261  	app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
   262  
   263  	consAddr := sdk.ConsAddress(PKs[0].Address())
   264  	fraction := sdk.NewDecWithPrec(5, 1)
   265  
   266  	// set an unbonding delegation with expiration timestamp beyond which the
   267  	// unbonding delegation shouldn't be slashed
   268  	ubdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4)
   269  	ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, time.Unix(0, 0), ubdTokens)
   270  	app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
   271  
   272  	// slash validator for the first time
   273  	ctx = ctx.WithBlockHeight(12)
   274  	bondedPool := app.StakingKeeper.GetBondedPool(ctx)
   275  	oldBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   276  
   277  	_, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   278  	require.True(t, found)
   279  	app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction)
   280  
   281  	// end block
   282  	applyValidatorSetUpdates(t, ctx, app.StakingKeeper, 1)
   283  
   284  	// read updating unbonding delegation
   285  	ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
   286  	require.True(t, found)
   287  	require.Len(t, ubd.Entries, 1)
   288  
   289  	// balance decreased
   290  	require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 2), ubd.Entries[0].Balance)
   291  
   292  	// bonded tokens burned
   293  	newBondedPoolBalances := app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   294  	diffTokens := oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx))
   295  	require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 3), diffTokens)
   296  
   297  	// read updated validator
   298  	validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   299  	require.True(t, found)
   300  
   301  	// power decreased by 3 - 6 stake originally bonded at the time of infraction
   302  	// was still bonded at the time of discovery and was slashed by half, 4 stake
   303  	// bonded at the time of discovery hadn't been bonded at the time of infraction
   304  	// and wasn't slashed
   305  	require.Equal(t, int64(7), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   306  
   307  	// slash validator again
   308  	ctx = ctx.WithBlockHeight(13)
   309  	app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction)
   310  
   311  	ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
   312  	require.True(t, found)
   313  	require.Len(t, ubd.Entries, 1)
   314  
   315  	// balance decreased again
   316  	require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance)
   317  
   318  	// bonded tokens burned again
   319  	newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   320  	diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx))
   321  	require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 6), diffTokens)
   322  
   323  	// read updated validator
   324  	validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   325  	require.True(t, found)
   326  
   327  	// power decreased by 3 again
   328  	require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   329  
   330  	// slash validator again
   331  	// all originally bonded stake has been slashed, so this will have no effect
   332  	// on the unbonding delegation, but it will slash stake bonded since the infraction
   333  	// this may not be the desirable behavior, ref https://github.com/cosmos/cosmos-sdk/issues/1440
   334  	ctx = ctx.WithBlockHeight(13)
   335  	app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction)
   336  
   337  	ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
   338  	require.True(t, found)
   339  	require.Len(t, ubd.Entries, 1)
   340  
   341  	// balance unchanged
   342  	require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance)
   343  
   344  	// bonded tokens burned again
   345  	newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   346  	diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx))
   347  	require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 9), diffTokens)
   348  
   349  	// read updated validator
   350  	validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   351  	require.True(t, found)
   352  
   353  	// power decreased by 3 again
   354  	require.Equal(t, int64(1), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   355  
   356  	// slash validator again
   357  	// all originally bonded stake has been slashed, so this will have no effect
   358  	// on the unbonding delegation, but it will slash stake bonded since the infraction
   359  	// this may not be the desirable behavior, ref https://github.com/cosmos/cosmos-sdk/issues/1440
   360  	ctx = ctx.WithBlockHeight(13)
   361  	app.StakingKeeper.Slash(ctx, consAddr, 9, 10, fraction)
   362  
   363  	ubd, found = app.StakingKeeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
   364  	require.True(t, found)
   365  	require.Len(t, ubd.Entries, 1)
   366  
   367  	// balance unchanged
   368  	require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance)
   369  
   370  	// just 1 bonded token burned again since that's all the validator now has
   371  	newBondedPoolBalances = app.BankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
   372  	diffTokens = oldBondedPoolBalances.Sub(newBondedPoolBalances).AmountOf(app.StakingKeeper.BondDenom(ctx))
   373  	require.Equal(t, app.StakingKeeper.TokensFromConsensusPower(ctx, 10), diffTokens)
   374  
   375  	// apply TM updates
   376  	applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1)
   377  
   378  	// read updated validator
   379  	// power decreased by 1 again, validator is out of stake
   380  	// validator should be in unbonding period
   381  	validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   382  	require.Equal(t, validator.GetStatus(), types.Unbonding)
   383  }
   384  
   385  // tests Slash at a previous height with a redelegation
   386  func TestSlashWithRedelegation(t *testing.T) {
   387  	app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
   388  	consAddr := sdk.ConsAddress(PKs[0].Address())
   389  	fraction := sdk.NewDecWithPrec(5, 1)
   390  	bondDenom := app.StakingKeeper.BondDenom(ctx)
   391  
   392  	// set a redelegation
   393  	rdTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6)
   394  	rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11,
   395  		time.Unix(0, 0), rdTokens, rdTokens.ToDec())
   396  	app.StakingKeeper.SetRedelegation(ctx, rd)
   397  
   398  	// set the associated delegation
   399  	del := types.NewDelegation(addrDels[0], addrVals[1], rdTokens.ToDec())
   400  	app.StakingKeeper.SetDelegation(ctx, del)
   401  
   402  	// update bonded tokens
   403  	bondedPool := app.StakingKeeper.GetBondedPool(ctx)
   404  	notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
   405  	rdCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdTokens.MulRaw(2)))
   406  
   407  	require.NoError(t, simapp.FundModuleAccount(app, ctx, bondedPool.GetName(), rdCoins))
   408  
   409  	app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
   410  
   411  	oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   412  	oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
   413  
   414  	// slash validator
   415  	ctx = ctx.WithBlockHeight(12)
   416  	_, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   417  	require.True(t, found)
   418  
   419  	require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, fraction) })
   420  	burnAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 10).ToDec().Mul(fraction).TruncateInt()
   421  
   422  	bondedPool = app.StakingKeeper.GetBondedPool(ctx)
   423  	notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
   424  
   425  	// burn bonded tokens from only from delegations
   426  	bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   427  	require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
   428  
   429  	notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
   430  	require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance))
   431  	oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   432  
   433  	// read updating redelegation
   434  	rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
   435  	require.True(t, found)
   436  	require.Len(t, rd.Entries, 1)
   437  	// read updated validator
   438  	validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   439  	require.True(t, found)
   440  	// power decreased by 2 - 4 stake originally bonded at the time of infraction
   441  	// was still bonded at the time of discovery and was slashed by half, 4 stake
   442  	// bonded at the time of discovery hadn't been bonded at the time of infraction
   443  	// and wasn't slashed
   444  	require.Equal(t, int64(8), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   445  
   446  	// slash the validator again
   447  	_, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   448  	require.True(t, found)
   449  
   450  	require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) })
   451  	burnAmount = app.StakingKeeper.TokensFromConsensusPower(ctx, 7)
   452  
   453  	// read updated pool
   454  	bondedPool = app.StakingKeeper.GetBondedPool(ctx)
   455  	notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
   456  
   457  	// seven bonded tokens burned
   458  	bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   459  	require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
   460  	require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance))
   461  
   462  	bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   463  	require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
   464  
   465  	notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
   466  	require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance))
   467  	oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   468  
   469  	// read updating redelegation
   470  	rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
   471  	require.True(t, found)
   472  	require.Len(t, rd.Entries, 1)
   473  	// read updated validator
   474  	validator, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   475  	require.True(t, found)
   476  	// power decreased by 4
   477  	require.Equal(t, int64(4), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   478  
   479  	// slash the validator again, by 100%
   480  	ctx = ctx.WithBlockHeight(12)
   481  	_, found = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   482  	require.True(t, found)
   483  
   484  	require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) })
   485  
   486  	burnAmount = app.StakingKeeper.TokensFromConsensusPower(ctx, 10).ToDec().Mul(sdk.OneDec()).TruncateInt()
   487  	burnAmount = burnAmount.Sub(sdk.OneDec().MulInt(rdTokens).TruncateInt())
   488  
   489  	// read updated pool
   490  	bondedPool = app.StakingKeeper.GetBondedPool(ctx)
   491  	notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
   492  
   493  	bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   494  	require.True(sdk.IntEq(t, oldBonded.Sub(burnAmount), bondedPoolBalance))
   495  	notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
   496  	require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance))
   497  	oldBonded = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   498  
   499  	// read updating redelegation
   500  	rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
   501  	require.True(t, found)
   502  	require.Len(t, rd.Entries, 1)
   503  	// apply TM updates
   504  	applyValidatorSetUpdates(t, ctx, app.StakingKeeper, -1)
   505  	// read updated validator
   506  	// validator decreased to zero power, should be in unbonding period
   507  	validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   508  	require.Equal(t, validator.GetStatus(), types.Unbonding)
   509  
   510  	// slash the validator again, by 100%
   511  	// no stake remains to be slashed
   512  	ctx = ctx.WithBlockHeight(12)
   513  	// validator still in unbonding period
   514  	validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   515  	require.Equal(t, validator.GetStatus(), types.Unbonding)
   516  
   517  	require.NotPanics(t, func() { app.StakingKeeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) })
   518  
   519  	// read updated pool
   520  	bondedPool = app.StakingKeeper.GetBondedPool(ctx)
   521  	notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
   522  
   523  	bondedPoolBalance = app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   524  	require.True(sdk.IntEq(t, oldBonded, bondedPoolBalance))
   525  	notBondedPoolBalance = app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
   526  	require.True(sdk.IntEq(t, oldNotBonded, notBondedPoolBalance))
   527  
   528  	// read updating redelegation
   529  	rd, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
   530  	require.True(t, found)
   531  	require.Len(t, rd.Entries, 1)
   532  	// read updated validator
   533  	// power still zero, still in unbonding period
   534  	validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, consAddr)
   535  	require.Equal(t, validator.GetStatus(), types.Unbonding)
   536  }
   537  
   538  // tests Slash at a previous height with both an unbonding delegation and a redelegation
   539  func TestSlashBoth(t *testing.T) {
   540  	app, ctx, addrDels, addrVals := bootstrapSlashTest(t, 10)
   541  	fraction := sdk.NewDecWithPrec(5, 1)
   542  	bondDenom := app.StakingKeeper.BondDenom(ctx)
   543  
   544  	// set a redelegation with expiration timestamp beyond which the
   545  	// redelegation shouldn't be slashed
   546  	rdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 6)
   547  	rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11,
   548  		time.Unix(0, 0), rdATokens,
   549  		rdATokens.ToDec())
   550  	app.StakingKeeper.SetRedelegation(ctx, rdA)
   551  
   552  	// set the associated delegation
   553  	delA := types.NewDelegation(addrDels[0], addrVals[1], rdATokens.ToDec())
   554  	app.StakingKeeper.SetDelegation(ctx, delA)
   555  
   556  	// set an unbonding delegation with expiration timestamp (beyond which the
   557  	// unbonding delegation shouldn't be slashed)
   558  	ubdATokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 4)
   559  	ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11,
   560  		time.Unix(0, 0), ubdATokens)
   561  	app.StakingKeeper.SetUnbondingDelegation(ctx, ubdA)
   562  
   563  	bondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, rdATokens.MulRaw(2)))
   564  	notBondedCoins := sdk.NewCoins(sdk.NewCoin(bondDenom, ubdATokens))
   565  
   566  	// update bonded tokens
   567  	bondedPool := app.StakingKeeper.GetBondedPool(ctx)
   568  	notBondedPool := app.StakingKeeper.GetNotBondedPool(ctx)
   569  
   570  	require.NoError(t, simapp.FundModuleAccount(app, ctx, bondedPool.GetName(), bondedCoins))
   571  	require.NoError(t, simapp.FundModuleAccount(app, ctx, notBondedPool.GetName(), notBondedCoins))
   572  
   573  	app.AccountKeeper.SetModuleAccount(ctx, bondedPool)
   574  	app.AccountKeeper.SetModuleAccount(ctx, notBondedPool)
   575  
   576  	oldBonded := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   577  	oldNotBonded := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
   578  	// slash validator
   579  	ctx = ctx.WithBlockHeight(12)
   580  	_, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
   581  	require.True(t, found)
   582  	consAddr0 := sdk.ConsAddress(PKs[0].Address())
   583  	app.StakingKeeper.Slash(ctx, consAddr0, 10, 10, fraction)
   584  
   585  	burnedNotBondedAmount := fraction.MulInt(ubdATokens).TruncateInt()
   586  	burnedBondAmount := app.StakingKeeper.TokensFromConsensusPower(ctx, 10).ToDec().Mul(fraction).TruncateInt()
   587  	burnedBondAmount = burnedBondAmount.Sub(burnedNotBondedAmount)
   588  
   589  	// read updated pool
   590  	bondedPool = app.StakingKeeper.GetBondedPool(ctx)
   591  	notBondedPool = app.StakingKeeper.GetNotBondedPool(ctx)
   592  
   593  	bondedPoolBalance := app.BankKeeper.GetBalance(ctx, bondedPool.GetAddress(), bondDenom).Amount
   594  	require.True(sdk.IntEq(t, oldBonded.Sub(burnedBondAmount), bondedPoolBalance))
   595  
   596  	notBondedPoolBalance := app.BankKeeper.GetBalance(ctx, notBondedPool.GetAddress(), bondDenom).Amount
   597  	require.True(sdk.IntEq(t, oldNotBonded.Sub(burnedNotBondedAmount), notBondedPoolBalance))
   598  
   599  	// read updating redelegation
   600  	rdA, found = app.StakingKeeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
   601  	require.True(t, found)
   602  	require.Len(t, rdA.Entries, 1)
   603  	// read updated validator
   604  	validator, found := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
   605  	require.True(t, found)
   606  	// power not decreased, all stake was bonded since
   607  	require.Equal(t, int64(10), validator.GetConsensusPower(app.StakingKeeper.PowerReduction(ctx)))
   608  }