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  }