github.com/Finschia/finschia-sdk@v0.48.1/x/slashing/keeper/keeper_test.go (about) 1 package keeper_test 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/stretchr/testify/require" 8 tmproto "github.com/tendermint/tendermint/proto/tendermint/types" 9 10 "github.com/Finschia/finschia-sdk/simapp" 11 sdk "github.com/Finschia/finschia-sdk/types" 12 "github.com/Finschia/finschia-sdk/x/slashing/testslashing" 13 "github.com/Finschia/finschia-sdk/x/staking" 14 "github.com/Finschia/finschia-sdk/x/staking/teststaking" 15 stakingtypes "github.com/Finschia/finschia-sdk/x/staking/types" 16 ) 17 18 func TestUnJailNotBonded(t *testing.T) { 19 app := simapp.Setup(false) 20 ctx := app.BaseApp.NewContext(false, tmproto.Header{}) 21 22 p := app.StakingKeeper.GetParams(ctx) 23 p.MaxValidators = 5 24 app.StakingKeeper.SetParams(ctx, p) 25 26 addrDels := simapp.AddTestAddrsIncremental(app, ctx, 6, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) 27 valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) 28 pks := simapp.CreateTestPubKeys(6) 29 tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) 30 31 // create max (5) validators all with the same power 32 for i := uint32(0); i < p.MaxValidators; i++ { 33 addr, val := valAddrs[i], pks[i] 34 tstaking.CreateValidatorWithValPower(addr, val, 100, true) 35 } 36 37 staking.EndBlocker(ctx, app.StakingKeeper) 38 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 39 40 // create a 6th validator with less power than the cliff validator (won't be bonded) 41 addr, val := valAddrs[5], pks[5] 42 amt := app.StakingKeeper.TokensFromConsensusPower(ctx, 50) 43 msg := tstaking.CreateValidatorMsg(addr, val, amt) 44 msg.MinSelfDelegation = amt 45 tstaking.Handle(msg, true) 46 47 staking.EndBlocker(ctx, app.StakingKeeper) 48 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 49 50 tstaking.CheckValidator(addr, stakingtypes.Unbonded, false) 51 52 // unbond below minimum self-delegation 53 require.Equal(t, p.BondDenom, tstaking.Denom) 54 tstaking.Undelegate(sdk.AccAddress(addr), addr, app.StakingKeeper.TokensFromConsensusPower(ctx, 1), true) 55 56 staking.EndBlocker(ctx, app.StakingKeeper) 57 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 58 59 // verify that validator is jailed 60 tstaking.CheckValidator(addr, -1, true) 61 62 // verify we cannot unjail (yet) 63 require.Error(t, app.SlashingKeeper.Unjail(ctx, addr)) 64 65 staking.EndBlocker(ctx, app.StakingKeeper) 66 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 67 // bond to meet minimum self-delegation 68 tstaking.DelegateWithPower(sdk.AccAddress(addr), addr, 1) 69 70 staking.EndBlocker(ctx, app.StakingKeeper) 71 ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1) 72 73 // verify we can immediately unjail 74 require.NoError(t, app.SlashingKeeper.Unjail(ctx, addr)) 75 76 tstaking.CheckValidator(addr, -1, false) 77 } 78 79 // Test a new validator entering the validator set 80 // Ensure that SigningInfo.VoterSetCounter is set correctly 81 // and that they are not immediately jailed 82 func TestHandleNewValidator(t *testing.T) { 83 app := simapp.Setup(false) 84 ctx := app.BaseApp.NewContext(false, tmproto.Header{}) 85 86 addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) 87 valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) 88 pks := simapp.CreateTestPubKeys(1) 89 addr, val := valAddrs[0], pks[0] 90 tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) 91 ctx = ctx.WithBlockHeight(app.SlashingKeeper.SignedBlocksWindow(ctx) + 1) 92 93 // Validator created 94 amt := tstaking.CreateValidatorWithValPower(addr, val, 100, true) 95 96 staking.EndBlocker(ctx, app.StakingKeeper) 97 require.Equal( 98 t, app.BankKeeper.GetAllBalances(ctx, sdk.AccAddress(addr)), 99 sdk.NewCoins(sdk.NewCoin(app.StakingKeeper.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), 100 ) 101 require.Equal(t, amt, app.StakingKeeper.Validator(ctx, addr).GetBondedTokens()) 102 103 // Now a validator, for two blocks 104 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, true) 105 ctx = ctx.WithBlockHeight(app.SlashingKeeper.SignedBlocksWindow(ctx) + 2) 106 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), 100, false) 107 108 info, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) 109 require.True(t, found) 110 require.Equal(t, app.SlashingKeeper.SignedBlocksWindow(ctx)+1, info.StartHeight) 111 require.Equal(t, int64(2), info.IndexOffset) 112 require.Equal(t, int64(1), info.MissedBlocksCounter) 113 require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) 114 115 // validator should be bonded still, should not have been jailed or slashed 116 validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) 117 require.Equal(t, stakingtypes.Bonded, validator.GetStatus()) 118 bondPool := app.StakingKeeper.GetBondedPool(ctx) 119 expTokens := app.StakingKeeper.TokensFromConsensusPower(ctx, 100) 120 require.True(t, expTokens.Equal(app.BankKeeper.GetBalance(ctx, bondPool.GetAddress(), app.StakingKeeper.BondDenom(ctx)).Amount)) 121 } 122 123 // Test a jailed validator being "down" twice 124 // Ensure that they're only slashed once 125 func TestHandleAlreadyJailed(t *testing.T) { 126 // initial setup 127 app := simapp.Setup(false) 128 ctx := app.BaseApp.NewContext(false, tmproto.Header{}) 129 130 addrDels := simapp.AddTestAddrsIncremental(app, ctx, 1, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) 131 valAddrs := simapp.ConvertAddrsToValAddrs(addrDels) 132 pks := simapp.CreateTestPubKeys(1) 133 addr, val := valAddrs[0], pks[0] 134 power := int64(100) 135 tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) 136 137 amt := tstaking.CreateValidatorWithValPower(addr, val, power, true) 138 139 staking.EndBlocker(ctx, app.StakingKeeper) 140 141 // 1000 first blocks OK 142 height := int64(0) 143 for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx); height++ { 144 ctx = ctx.WithBlockHeight(height) 145 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) 146 } 147 148 // 501 blocks missed 149 for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx))+1; height++ { 150 ctx = ctx.WithBlockHeight(height) 151 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) 152 } 153 154 // end block 155 staking.EndBlocker(ctx, app.StakingKeeper) 156 157 // validator should have been jailed and slashed 158 validator, _ := app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) 159 require.Equal(t, stakingtypes.Unbonding, validator.GetStatus()) 160 161 // validator should have been slashed 162 resultingTokens := amt.Sub(app.StakingKeeper.TokensFromConsensusPower(ctx, 1)) 163 require.Equal(t, resultingTokens, validator.GetTokens()) 164 165 // another block missed 166 ctx = ctx.WithBlockHeight(height) 167 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, false) 168 169 // validator should not have been slashed twice 170 validator, _ = app.StakingKeeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) 171 require.Equal(t, resultingTokens, validator.GetTokens()) 172 } 173 174 // Test a validator dipping in and out of the validator set 175 // Ensure that missed blocks are tracked correctly and that 176 // the voter set counter of the signing info is reset correctly 177 func TestValidatorDippingInAndOut(t *testing.T) { 178 // initial setup 179 // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 180 app := simapp.Setup(false) 181 ctx := app.BaseApp.NewContext(false, tmproto.Header{}) 182 app.SlashingKeeper.SetParams(ctx, testslashing.TestParams()) 183 184 params := app.StakingKeeper.GetParams(ctx) 185 params.MaxValidators = 1 186 app.StakingKeeper.SetParams(ctx, params) 187 power := int64(100) 188 189 pks := simapp.CreateTestPubKeys(3) 190 simapp.AddTestAddrsFromPubKeys(app, ctx, pks, app.StakingKeeper.TokensFromConsensusPower(ctx, 200)) 191 192 addr, val := pks[0].Address(), pks[0] 193 consAddr := sdk.ConsAddress(addr) 194 tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) 195 valAddr := sdk.ValAddress(addr) 196 197 tstaking.CreateValidatorWithValPower(valAddr, val, power, true) 198 staking.EndBlocker(ctx, app.StakingKeeper) 199 200 // 100 first blocks OK 201 height := int64(0) 202 for ; height < int64(100); height++ { 203 ctx = ctx.WithBlockHeight(height) 204 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), power, true) 205 } 206 207 // kick first validator out of validator set 208 tstaking.CreateValidatorWithValPower(sdk.ValAddress(pks[1].Address()), pks[1], 101, true) 209 validatorUpdates := staking.EndBlocker(ctx, app.StakingKeeper) 210 require.Equal(t, 2, len(validatorUpdates)) 211 tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, false) 212 213 // 600 more blocks happened 214 height = int64(700) 215 ctx = ctx.WithBlockHeight(height) 216 217 // validator added back in 218 tstaking.DelegateWithPower(sdk.AccAddress(pks[2].Address()), sdk.ValAddress(pks[0].Address()), 50) 219 220 validatorUpdates = staking.EndBlocker(ctx, app.StakingKeeper) 221 require.Equal(t, 2, len(validatorUpdates)) 222 tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) 223 newPower := int64(150) 224 225 // validator misses 501 blocks exceeding the liveness threshold 226 latest := height 227 for ; height < latest+501; height++ { 228 ctx = ctx.WithBlockHeight(height) 229 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) 230 } 231 232 // 398 more blocks happened 233 latest = height 234 for ; height < latest+398; height++ { 235 ctx = ctx.WithBlockHeight(height) 236 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) 237 } 238 239 // shouldn't be jailed/kicked yet because it have not joined to vote set 1000 times 240 // 100 times + (kicked) + 501 times + 398 times = 999 times 241 tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) 242 243 // check all the signing information 244 signInfo, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr) 245 require.True(t, found) 246 require.Equal(t, int64(0), signInfo.StartHeight) 247 require.Equal(t, int64(999), signInfo.IndexOffset) 248 require.Equal(t, int64(501), signInfo.MissedBlocksCounter) 249 250 // another block happened 251 ctx = ctx.WithBlockHeight(height) 252 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) 253 height++ 254 255 // should now be jailed & kicked 256 staking.EndBlocker(ctx, app.StakingKeeper) 257 tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) 258 259 // check all the signing information 260 signInfo, found = app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr) 261 require.True(t, found) 262 require.Equal(t, int64(0), signInfo.StartHeight) 263 require.Equal(t, int64(0), signInfo.IndexOffset) 264 require.Equal(t, int64(0), signInfo.MissedBlocksCounter) 265 // array should be cleared 266 for offset := int64(0); offset < app.SlashingKeeper.SignedBlocksWindow(ctx); offset++ { 267 missed := app.SlashingKeeper.GetValidatorMissedBlockBitArray(ctx, consAddr, offset) 268 require.False(t, missed) 269 } 270 271 // some blocks pass 272 height = int64(5000) 273 ctx = ctx.WithBlockHeight(height) 274 275 // validator rejoins and starts signing again 276 app.StakingKeeper.Unjail(ctx, consAddr) 277 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) 278 height++ 279 280 // validator should not be kicked since we reset counter/array when it was jailed 281 staking.EndBlocker(ctx, app.StakingKeeper) 282 tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) 283 284 // 1000 blocks happened 285 latest = height 286 for ; height < latest+1000; height++ { 287 ctx = ctx.WithBlockHeight(height) 288 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) 289 } 290 291 // validator misses 500 blocks 292 latest = height 293 for ; height < latest+500; height++ { 294 ctx = ctx.WithBlockHeight(height) 295 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) 296 } 297 tstaking.CheckValidator(valAddr, stakingtypes.Bonded, false) 298 299 // validator misses another block 300 ctx = ctx.WithBlockHeight(height) 301 app.SlashingKeeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) 302 height++ 303 304 // validator should now be jailed & kicked 305 staking.EndBlocker(ctx, app.StakingKeeper) 306 tstaking.CheckValidator(valAddr, stakingtypes.Unbonding, true) 307 }