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 }