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