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 }