github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/slashing/internal/keeper/keeper_test.go (about) 1 package keeper 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/stretchr/testify/require" 8 9 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 10 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/slashing/internal/types" 11 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking" 12 ) 13 14 // Test a new validator entering the validator set 15 // Ensure that SigningInfo.StartHeight is set correctly 16 // and that they are not immediately jailed 17 func TestHandleNewValidator(t *testing.T) { 18 // initial setup 19 ctx, ck, sk, _, keeper := CreateTestInput(t, TestParams()) 20 addr, val := Addrs[0], Pks[0] 21 amt := sdk.TokensFromConsensusPower(100) 22 sh := staking.NewHandler(sk) 23 24 // 1000 first blocks not a validator 25 ctx.SetBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) 26 27 // Validator created 28 res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) 29 require.NoError(t, err) 30 require.NotNil(t, res) 31 32 staking.EndBlocker(ctx, sk) 33 34 require.Equal( 35 t, ck.GetCoins(ctx, sdk.AccAddress(addr)), 36 sdk.NewCoins(sdk.NewCoin(sk.GetParams(ctx).BondDenom, InitTokens.Sub(amt))), 37 ) 38 require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) 39 40 // Now a validator, for two blocks 41 keeper.HandleValidatorSignature(ctx, val.Address(), 100, true) 42 ctx.SetBlockHeight(keeper.SignedBlocksWindow(ctx) + 2) 43 keeper.HandleValidatorSignature(ctx, val.Address(), 100, false) 44 45 info, found := keeper.GetValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) 46 require.True(t, found) 47 require.Equal(t, keeper.SignedBlocksWindow(ctx)+1, info.StartHeight) 48 require.Equal(t, int64(2), info.IndexOffset) 49 require.Equal(t, int64(1), info.MissedBlocksCounter) 50 require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) 51 52 // validator should be bonded still, should not have been jailed or slashed 53 validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) 54 require.Equal(t, sdk.Bonded, validator.GetStatus()) 55 bondPool := sk.GetBondedPool(ctx) 56 expTokens := sdk.NewDecFromInt(sdk.TokensFromConsensusPower(100)) 57 require.Equal(t, expTokens, bondPool.GetCoins().AmountOf(sk.BondDenom(ctx))) 58 } 59 60 // Test a jailed validator being "down" twice 61 // Ensure that they're only slashed once 62 func TestHandleAlreadyJailed(t *testing.T) { 63 64 // initial setup 65 ctx, _, sk, _, keeper := CreateTestInput(t, types.DefaultParams()) 66 power := int64(100) 67 amt := sdk.TokensFromConsensusPower(power) 68 addr, val := Addrs[0], Pks[0] 69 sh := staking.NewHandler(sk) 70 res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) 71 require.NoError(t, err) 72 require.NotNil(t, res) 73 74 staking.EndBlocker(ctx, sk) 75 76 // 1000 first blocks OK 77 height := int64(0) 78 for ; height < keeper.SignedBlocksWindow(ctx); height++ { 79 ctx.SetBlockHeight(height) 80 keeper.HandleValidatorSignature(ctx, val.Address(), power, true) 81 } 82 83 // 501 blocks missed 84 for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ { 85 ctx.SetBlockHeight(height) 86 keeper.HandleValidatorSignature(ctx, val.Address(), power, false) 87 } 88 89 // end block 90 staking.EndBlocker(ctx, sk) 91 92 // validator should have been jailed and slashed 93 validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) 94 require.Equal(t, sdk.Unbonding, validator.GetStatus()) 95 96 // validator should have been slashed 97 resultingTokens := amt.Sub(sdk.TokensFromConsensusPower(1)) 98 require.Equal(t, resultingTokens, validator.GetTokens()) 99 100 // another block missed 101 ctx.SetBlockHeight(height) 102 keeper.HandleValidatorSignature(ctx, val.Address(), power, false) 103 104 // validator should not have been slashed twice 105 validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) 106 require.Equal(t, resultingTokens, validator.GetTokens()) 107 108 } 109 110 // Test a validator dipping in and out of the validator set 111 // Ensure that missed blocks are tracked correctly and that 112 // the start height of the signing info is reset correctly 113 func TestValidatorDippingInAndOut(t *testing.T) { 114 115 // initial setup 116 // TestParams set the SignedBlocksWindow to 1000 and MaxMissedBlocksPerWindow to 500 117 ctx, _, sk, _, keeper := CreateTestInput(t, TestParams()) 118 params := sk.GetParams(ctx) 119 params.MaxValidators = 1 120 sk.SetParams(ctx, params) 121 power := int64(100) 122 amt := sdk.TokensFromConsensusPower(power) 123 addr, val := Addrs[0], Pks[0] 124 consAddr := sdk.ConsAddress(addr) 125 sh := staking.NewHandler(sk) 126 res, err := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) 127 require.NoError(t, err) 128 require.NotNil(t, res) 129 130 staking.EndBlocker(ctx, sk) 131 132 // 100 first blocks OK 133 height := int64(0) 134 for ; height < int64(100); height++ { 135 ctx.SetBlockHeight(height) 136 keeper.HandleValidatorSignature(ctx, val.Address(), power, true) 137 } 138 139 // kick first validator out of validator set 140 newAmt := sdk.TokensFromConsensusPower(101) 141 res, err = sh(ctx, NewTestMsgCreateValidator(Addrs[1], Pks[1], newAmt)) 142 require.NoError(t, err) 143 require.NotNil(t, res) 144 145 validatorUpdates := staking.EndBlocker(ctx, sk) 146 require.Equal(t, 2, len(validatorUpdates)) 147 validator, _ := sk.GetValidator(ctx, addr) 148 require.Equal(t, sdk.Unbonding, validator.Status) 149 150 // 600 more blocks happened 151 height = int64(700) 152 ctx.SetBlockHeight(height) 153 154 // validator added back in 155 delTokens := sdk.TokensFromConsensusPower(50) 156 res, err = sh(ctx, NewTestMsgDelegate(sdk.AccAddress(Addrs[2]), Addrs[0], delTokens)) 157 require.NoError(t, err) 158 require.NotNil(t, res) 159 160 validatorUpdates = staking.EndBlocker(ctx, sk) 161 require.Equal(t, 2, len(validatorUpdates)) 162 validator, _ = sk.GetValidator(ctx, addr) 163 require.Equal(t, sdk.Bonded, validator.Status) 164 newPower := int64(150) 165 166 // validator misses a block 167 keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) 168 height++ 169 170 // shouldn't be jailed/kicked yet 171 validator, _ = sk.GetValidator(ctx, addr) 172 require.Equal(t, sdk.Bonded, validator.Status) 173 174 // validator misses 500 more blocks, 501 total 175 latest := height 176 for ; height < latest+500; height++ { 177 ctx.SetBlockHeight(height) 178 keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) 179 } 180 181 // should now be jailed & kicked 182 staking.EndBlocker(ctx, sk) 183 validator, _ = sk.GetValidator(ctx, addr) 184 require.Equal(t, sdk.Unbonding, validator.Status) 185 186 // check all the signing information 187 signInfo, found := keeper.GetValidatorSigningInfo(ctx, consAddr) 188 require.True(t, found) 189 require.Equal(t, int64(0), signInfo.MissedBlocksCounter) 190 require.Equal(t, int64(0), signInfo.IndexOffset) 191 // array should be cleared 192 for offset := int64(0); offset < keeper.SignedBlocksWindow(ctx); offset++ { 193 missed := keeper.GetValidatorMissedBlockBitArray(ctx, consAddr, offset) 194 require.False(t, missed) 195 } 196 197 // some blocks pass 198 height = int64(5000) 199 ctx.SetBlockHeight(height) 200 201 // validator rejoins and starts signing again 202 sk.Unjail(ctx, consAddr) 203 keeper.HandleValidatorSignature(ctx, val.Address(), newPower, true) 204 height++ 205 206 // validator should not be kicked since we reset counter/array when it was jailed 207 staking.EndBlocker(ctx, sk) 208 validator, _ = sk.GetValidator(ctx, addr) 209 require.Equal(t, sdk.Bonded, validator.Status) 210 211 // validator misses 501 blocks 212 latest = height 213 for ; height < latest+501; height++ { 214 ctx.SetBlockHeight(height) 215 keeper.HandleValidatorSignature(ctx, val.Address(), newPower, false) 216 } 217 218 // validator should now be jailed & kicked 219 staking.EndBlocker(ctx, sk) 220 validator, _ = sk.GetValidator(ctx, addr) 221 require.Equal(t, sdk.Unbonding, validator.Status) 222 223 }