github.com/cosmos/cosmos-sdk@v0.50.10/x/staking/keeper/msg_server_test.go (about)

     1  package keeper_test
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/golang/mock/gomock"
     8  
     9  	"cosmossdk.io/math"
    10  
    11  	"github.com/cosmos/cosmos-sdk/codec/address"
    12  	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
    13  	"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
    14  	simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
    15  	sdk "github.com/cosmos/cosmos-sdk/types"
    16  	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
    17  )
    18  
    19  var (
    20  	PKS     = simtestutil.CreateTestPubKeys(3)
    21  	Addr    = sdk.AccAddress(PKS[0].Address())
    22  	ValAddr = sdk.ValAddress(Addr)
    23  )
    24  
    25  func (s *KeeperTestSuite) execExpectCalls() {
    26  	s.accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
    27  	s.bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), Addr, stakingtypes.NotBondedPoolName, gomock.Any()).AnyTimes()
    28  }
    29  
    30  func (s *KeeperTestSuite) TestMsgCreateValidator() {
    31  	ctx, msgServer := s.ctx, s.msgServer
    32  	require := s.Require()
    33  	s.execExpectCalls()
    34  
    35  	pk1 := ed25519.GenPrivKey().PubKey()
    36  	require.NotNil(pk1)
    37  
    38  	pubkey, err := codectypes.NewAnyWithValue(pk1)
    39  	require.NoError(err)
    40  
    41  	testCases := []struct {
    42  		name      string
    43  		input     *stakingtypes.MsgCreateValidator
    44  		expErr    bool
    45  		expErrMsg string
    46  	}{
    47  		{
    48  			name: "empty description",
    49  			input: &stakingtypes.MsgCreateValidator{
    50  				Description: stakingtypes.Description{},
    51  				Commission: stakingtypes.CommissionRates{
    52  					Rate:          math.LegacyNewDecWithPrec(5, 1),
    53  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
    54  					MaxChangeRate: math.LegacyNewDec(0),
    55  				},
    56  				MinSelfDelegation: math.NewInt(1),
    57  				DelegatorAddress:  Addr.String(),
    58  				ValidatorAddress:  ValAddr.String(),
    59  				Pubkey:            pubkey,
    60  				Value:             sdk.NewInt64Coin("stake", 10000),
    61  			},
    62  			expErr:    true,
    63  			expErrMsg: "empty description",
    64  		},
    65  		{
    66  			name: "invalid validator address",
    67  			input: &stakingtypes.MsgCreateValidator{
    68  				Description: stakingtypes.Description{
    69  					Moniker: "NewValidator",
    70  				},
    71  				Commission: stakingtypes.CommissionRates{
    72  					Rate:          math.LegacyNewDecWithPrec(5, 1),
    73  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
    74  					MaxChangeRate: math.LegacyNewDec(0),
    75  				},
    76  				MinSelfDelegation: math.NewInt(1),
    77  				DelegatorAddress:  Addr.String(),
    78  				ValidatorAddress:  sdk.AccAddress([]byte("invalid")).String(),
    79  				Pubkey:            pubkey,
    80  				Value:             sdk.NewInt64Coin("stake", 10000),
    81  			},
    82  			expErr:    true,
    83  			expErrMsg: "invalid validator address",
    84  		},
    85  		{
    86  			name: "empty validator pubkey",
    87  			input: &stakingtypes.MsgCreateValidator{
    88  				Description: stakingtypes.Description{
    89  					Moniker: "NewValidator",
    90  				},
    91  				Commission: stakingtypes.CommissionRates{
    92  					Rate:          math.LegacyNewDecWithPrec(5, 1),
    93  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
    94  					MaxChangeRate: math.LegacyNewDec(0),
    95  				},
    96  				MinSelfDelegation: math.NewInt(1),
    97  				DelegatorAddress:  Addr.String(),
    98  				ValidatorAddress:  ValAddr.String(),
    99  				Pubkey:            nil,
   100  				Value:             sdk.NewInt64Coin("stake", 10000),
   101  			},
   102  			expErr:    true,
   103  			expErrMsg: "empty validator public key",
   104  		},
   105  		{
   106  			name: "empty delegation amount",
   107  			input: &stakingtypes.MsgCreateValidator{
   108  				Description: stakingtypes.Description{
   109  					Moniker: "NewValidator",
   110  				},
   111  				Commission: stakingtypes.CommissionRates{
   112  					Rate:          math.LegacyNewDecWithPrec(5, 1),
   113  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
   114  					MaxChangeRate: math.LegacyNewDec(0),
   115  				},
   116  				MinSelfDelegation: math.NewInt(1),
   117  				DelegatorAddress:  Addr.String(),
   118  				ValidatorAddress:  ValAddr.String(),
   119  				Pubkey:            pubkey,
   120  				Value:             sdk.NewInt64Coin("stake", 0),
   121  			},
   122  			expErr:    true,
   123  			expErrMsg: "invalid delegation amount",
   124  		},
   125  		{
   126  			name: "nil delegation amount",
   127  			input: &stakingtypes.MsgCreateValidator{
   128  				Description: stakingtypes.Description{
   129  					Moniker: "NewValidator",
   130  				},
   131  				Commission: stakingtypes.CommissionRates{
   132  					Rate:          math.LegacyNewDecWithPrec(5, 1),
   133  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
   134  					MaxChangeRate: math.LegacyNewDec(0),
   135  				},
   136  				MinSelfDelegation: math.NewInt(1),
   137  				DelegatorAddress:  Addr.String(),
   138  				ValidatorAddress:  ValAddr.String(),
   139  				Pubkey:            pubkey,
   140  				Value:             sdk.Coin{},
   141  			},
   142  			expErr:    true,
   143  			expErrMsg: "invalid delegation amount",
   144  		},
   145  		{
   146  			name: "zero minimum self delegation",
   147  			input: &stakingtypes.MsgCreateValidator{
   148  				Description: stakingtypes.Description{
   149  					Moniker: "NewValidator",
   150  				},
   151  				Commission: stakingtypes.CommissionRates{
   152  					Rate:          math.LegacyNewDecWithPrec(5, 1),
   153  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
   154  					MaxChangeRate: math.LegacyNewDec(0),
   155  				},
   156  				MinSelfDelegation: math.NewInt(0),
   157  				DelegatorAddress:  Addr.String(),
   158  				ValidatorAddress:  ValAddr.String(),
   159  				Pubkey:            pubkey,
   160  				Value:             sdk.NewInt64Coin("stake", 10000),
   161  			},
   162  			expErr:    true,
   163  			expErrMsg: "minimum self delegation must be a positive integer",
   164  		},
   165  		{
   166  			name: "negative minimum self delegation",
   167  			input: &stakingtypes.MsgCreateValidator{
   168  				Description: stakingtypes.Description{
   169  					Moniker: "NewValidator",
   170  				},
   171  				Commission: stakingtypes.CommissionRates{
   172  					Rate:          math.LegacyNewDecWithPrec(5, 1),
   173  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
   174  					MaxChangeRate: math.LegacyNewDec(0),
   175  				},
   176  				MinSelfDelegation: math.NewInt(-1),
   177  				DelegatorAddress:  Addr.String(),
   178  				ValidatorAddress:  ValAddr.String(),
   179  				Pubkey:            pubkey,
   180  				Value:             sdk.NewInt64Coin("stake", 10000),
   181  			},
   182  			expErr:    true,
   183  			expErrMsg: "minimum self delegation must be a positive integer",
   184  		},
   185  		{
   186  			name: "delegation less than minimum self delegation",
   187  			input: &stakingtypes.MsgCreateValidator{
   188  				Description: stakingtypes.Description{
   189  					Moniker: "NewValidator",
   190  				},
   191  				Commission: stakingtypes.CommissionRates{
   192  					Rate:          math.LegacyNewDecWithPrec(5, 1),
   193  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
   194  					MaxChangeRate: math.LegacyNewDec(0),
   195  				},
   196  				MinSelfDelegation: math.NewInt(100),
   197  				DelegatorAddress:  Addr.String(),
   198  				ValidatorAddress:  ValAddr.String(),
   199  				Pubkey:            pubkey,
   200  				Value:             sdk.NewInt64Coin("stake", 10),
   201  			},
   202  			expErr:    true,
   203  			expErrMsg: "validator's self delegation must be greater than their minimum self delegation",
   204  		},
   205  		{
   206  			name: "valid msg",
   207  			input: &stakingtypes.MsgCreateValidator{
   208  				Description: stakingtypes.Description{
   209  					Moniker:         "NewValidator",
   210  					Identity:        "xyz",
   211  					Website:         "xyz.com",
   212  					SecurityContact: "xyz@gmail.com",
   213  					Details:         "details",
   214  				},
   215  				Commission: stakingtypes.CommissionRates{
   216  					Rate:          math.LegacyNewDecWithPrec(5, 1),
   217  					MaxRate:       math.LegacyNewDecWithPrec(5, 1),
   218  					MaxChangeRate: math.LegacyNewDec(0),
   219  				},
   220  				MinSelfDelegation: math.NewInt(1),
   221  				DelegatorAddress:  Addr.String(),
   222  				ValidatorAddress:  ValAddr.String(),
   223  				Pubkey:            pubkey,
   224  				Value:             sdk.NewInt64Coin("stake", 10000),
   225  			},
   226  			expErr: false,
   227  		},
   228  	}
   229  	for _, tc := range testCases {
   230  		tc := tc
   231  		s.T().Run(tc.name, func(t *testing.T) {
   232  			_, err := msgServer.CreateValidator(ctx, tc.input)
   233  			if tc.expErr {
   234  				require.Error(err)
   235  				require.Contains(err.Error(), tc.expErrMsg)
   236  			} else {
   237  				require.NoError(err)
   238  			}
   239  		})
   240  	}
   241  }
   242  
   243  func (s *KeeperTestSuite) TestMsgEditValidator() {
   244  	ctx, msgServer := s.ctx, s.msgServer
   245  	require := s.Require()
   246  	s.execExpectCalls()
   247  
   248  	// create new context with updated block time
   249  	newCtx := ctx.WithBlockTime(ctx.BlockTime().AddDate(0, 0, 1))
   250  
   251  	pk := ed25519.GenPrivKey().PubKey()
   252  	require.NotNil(pk)
   253  
   254  	comm := stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0))
   255  	msg, err := stakingtypes.NewMsgCreateValidator(ValAddr.String(), pk, sdk.NewCoin("stake", math.NewInt(10)), stakingtypes.Description{Moniker: "NewVal"}, comm, math.OneInt())
   256  	require.NoError(err)
   257  
   258  	res, err := msgServer.CreateValidator(ctx, msg)
   259  	require.NoError(err)
   260  	require.NotNil(res)
   261  
   262  	newRate := math.LegacyZeroDec()
   263  	invalidRate := math.LegacyNewDec(2)
   264  
   265  	lowSelfDel := math.OneInt()
   266  	highSelfDel := math.NewInt(100)
   267  	negSelfDel := math.NewInt(-1)
   268  	newSelfDel := math.NewInt(3)
   269  
   270  	testCases := []struct {
   271  		name      string
   272  		ctx       sdk.Context
   273  		input     *stakingtypes.MsgEditValidator
   274  		expErr    bool
   275  		expErrMsg string
   276  	}{
   277  		{
   278  			name: "invalid validator",
   279  			ctx:  newCtx,
   280  			input: &stakingtypes.MsgEditValidator{
   281  				Description: stakingtypes.Description{
   282  					Moniker: "TestValidator",
   283  				},
   284  				ValidatorAddress:  sdk.AccAddress([]byte("invalid")).String(),
   285  				CommissionRate:    &newRate,
   286  				MinSelfDelegation: &newSelfDel,
   287  			},
   288  			expErr:    true,
   289  			expErrMsg: "invalid validator address",
   290  		},
   291  		{
   292  			name: "empty description",
   293  			ctx:  newCtx,
   294  			input: &stakingtypes.MsgEditValidator{
   295  				Description:       stakingtypes.Description{},
   296  				ValidatorAddress:  ValAddr.String(),
   297  				CommissionRate:    &newRate,
   298  				MinSelfDelegation: &newSelfDel,
   299  			},
   300  			expErr:    true,
   301  			expErrMsg: "empty description",
   302  		},
   303  		{
   304  			name: "negative self delegation",
   305  			ctx:  newCtx,
   306  			input: &stakingtypes.MsgEditValidator{
   307  				Description: stakingtypes.Description{
   308  					Moniker: "TestValidator",
   309  				},
   310  				ValidatorAddress:  ValAddr.String(),
   311  				CommissionRate:    &newRate,
   312  				MinSelfDelegation: &negSelfDel,
   313  			},
   314  			expErr:    true,
   315  			expErrMsg: "minimum self delegation must be a positive integer",
   316  		},
   317  		{
   318  			name: "invalid commission rate",
   319  			ctx:  newCtx,
   320  			input: &stakingtypes.MsgEditValidator{
   321  				Description: stakingtypes.Description{
   322  					Moniker: "TestValidator",
   323  				},
   324  				ValidatorAddress:  ValAddr.String(),
   325  				CommissionRate:    &invalidRate,
   326  				MinSelfDelegation: &newSelfDel,
   327  			},
   328  			expErr:    true,
   329  			expErrMsg: "commission rate must be between 0 and 1 (inclusive)",
   330  		},
   331  		{
   332  			name: "validator does not exist",
   333  			ctx:  newCtx,
   334  			input: &stakingtypes.MsgEditValidator{
   335  				Description: stakingtypes.Description{
   336  					Moniker: "TestValidator",
   337  				},
   338  				ValidatorAddress:  sdk.ValAddress([]byte("val")).String(),
   339  				CommissionRate:    &newRate,
   340  				MinSelfDelegation: &newSelfDel,
   341  			},
   342  			expErr:    true,
   343  			expErrMsg: "validator does not exist",
   344  		},
   345  		{
   346  			name: "change commmission rate in <24hrs",
   347  			ctx:  ctx,
   348  			input: &stakingtypes.MsgEditValidator{
   349  				Description: stakingtypes.Description{
   350  					Moniker: "TestValidator",
   351  				},
   352  				ValidatorAddress:  ValAddr.String(),
   353  				CommissionRate:    &newRate,
   354  				MinSelfDelegation: &newSelfDel,
   355  			},
   356  			expErr:    true,
   357  			expErrMsg: "commission cannot be changed more than once in 24h",
   358  		},
   359  		{
   360  			name: "minimum self delegation cannot decrease",
   361  			ctx:  newCtx,
   362  			input: &stakingtypes.MsgEditValidator{
   363  				Description: stakingtypes.Description{
   364  					Moniker: "TestValidator",
   365  				},
   366  				ValidatorAddress:  ValAddr.String(),
   367  				CommissionRate:    &newRate,
   368  				MinSelfDelegation: &lowSelfDel,
   369  			},
   370  			expErr:    true,
   371  			expErrMsg: "minimum self delegation cannot be decrease",
   372  		},
   373  		{
   374  			name: "validator self-delegation must be greater than min self delegation",
   375  			ctx:  newCtx,
   376  			input: &stakingtypes.MsgEditValidator{
   377  				Description: stakingtypes.Description{
   378  					Moniker: "TestValidator",
   379  				},
   380  				ValidatorAddress:  ValAddr.String(),
   381  				CommissionRate:    &newRate,
   382  				MinSelfDelegation: &highSelfDel,
   383  			},
   384  			expErr:    true,
   385  			expErrMsg: "validator's self delegation must be greater than their minimum self delegation",
   386  		},
   387  		{
   388  			name: "valid msg",
   389  			ctx:  newCtx,
   390  			input: &stakingtypes.MsgEditValidator{
   391  				Description: stakingtypes.Description{
   392  					Moniker:         "TestValidator",
   393  					Identity:        "abc",
   394  					Website:         "abc.com",
   395  					SecurityContact: "abc@gmail.com",
   396  					Details:         "newDetails",
   397  				},
   398  				ValidatorAddress:  ValAddr.String(),
   399  				CommissionRate:    &newRate,
   400  				MinSelfDelegation: &newSelfDel,
   401  			},
   402  			expErr: false,
   403  		},
   404  	}
   405  	for _, tc := range testCases {
   406  		tc := tc
   407  		s.T().Run(tc.name, func(t *testing.T) {
   408  			_, err := msgServer.EditValidator(tc.ctx, tc.input)
   409  			if tc.expErr {
   410  				require.Error(err)
   411  				require.Contains(err.Error(), tc.expErrMsg)
   412  			} else {
   413  				require.NoError(err)
   414  			}
   415  		})
   416  	}
   417  }
   418  
   419  func (s *KeeperTestSuite) TestMsgDelegate() {
   420  	ctx, keeper, msgServer := s.ctx, s.stakingKeeper, s.msgServer
   421  	require := s.Require()
   422  	s.execExpectCalls()
   423  
   424  	pk := ed25519.GenPrivKey().PubKey()
   425  	require.NotNil(pk)
   426  
   427  	comm := stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0))
   428  
   429  	msg, err := stakingtypes.NewMsgCreateValidator(ValAddr.String(), pk, sdk.NewCoin("stake", math.NewInt(10)), stakingtypes.Description{Moniker: "NewVal"}, comm, math.OneInt())
   430  	require.NoError(err)
   431  
   432  	res, err := msgServer.CreateValidator(ctx, msg)
   433  	require.NoError(err)
   434  	require.NotNil(res)
   435  
   436  	testCases := []struct {
   437  		name      string
   438  		input     *stakingtypes.MsgDelegate
   439  		expErr    bool
   440  		expErrMsg string
   441  	}{
   442  		{
   443  			name: "invalid validator",
   444  			input: &stakingtypes.MsgDelegate{
   445  				DelegatorAddress: Addr.String(),
   446  				ValidatorAddress: sdk.AccAddress([]byte("invalid")).String(),
   447  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   448  			},
   449  			expErr:    true,
   450  			expErrMsg: "invalid validator address",
   451  		},
   452  		{
   453  			name: "empty delegator",
   454  			input: &stakingtypes.MsgDelegate{
   455  				DelegatorAddress: "",
   456  				ValidatorAddress: ValAddr.String(),
   457  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   458  			},
   459  			expErr:    true,
   460  			expErrMsg: "invalid delegator address: empty address string is not allowed",
   461  		},
   462  		{
   463  			name: "invalid delegator",
   464  			input: &stakingtypes.MsgDelegate{
   465  				DelegatorAddress: "invalid",
   466  				ValidatorAddress: ValAddr.String(),
   467  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   468  			},
   469  			expErr:    true,
   470  			expErrMsg: "invalid delegator address: decoding bech32 failed",
   471  		},
   472  		{
   473  			name: "validator does not exist",
   474  			input: &stakingtypes.MsgDelegate{
   475  				DelegatorAddress: Addr.String(),
   476  				ValidatorAddress: sdk.ValAddress([]byte("val")).String(),
   477  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   478  			},
   479  			expErr:    true,
   480  			expErrMsg: "validator does not exist",
   481  		},
   482  		{
   483  			name: "zero amount",
   484  			input: &stakingtypes.MsgDelegate{
   485  				DelegatorAddress: Addr.String(),
   486  				ValidatorAddress: ValAddr.String(),
   487  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(0))},
   488  			},
   489  			expErr:    true,
   490  			expErrMsg: "invalid delegation amount",
   491  		},
   492  		{
   493  			name: "negative amount",
   494  			input: &stakingtypes.MsgDelegate{
   495  				DelegatorAddress: Addr.String(),
   496  				ValidatorAddress: ValAddr.String(),
   497  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(-1))},
   498  			},
   499  			expErr:    true,
   500  			expErrMsg: "invalid delegation amount",
   501  		},
   502  		{
   503  			name: "invalid BondDenom",
   504  			input: &stakingtypes.MsgDelegate{
   505  				DelegatorAddress: Addr.String(),
   506  				ValidatorAddress: ValAddr.String(),
   507  				Amount:           sdk.Coin{Denom: "test", Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   508  			},
   509  			expErr:    true,
   510  			expErrMsg: "invalid coin denomination",
   511  		},
   512  		{
   513  			name: "valid msg",
   514  			input: &stakingtypes.MsgDelegate{
   515  				DelegatorAddress: Addr.String(),
   516  				ValidatorAddress: ValAddr.String(),
   517  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   518  			},
   519  			expErr: false,
   520  		},
   521  	}
   522  
   523  	for _, tc := range testCases {
   524  		tc := tc
   525  		s.T().Run(tc.name, func(t *testing.T) {
   526  			_, err := msgServer.Delegate(ctx, tc.input)
   527  			if tc.expErr {
   528  				require.Error(err)
   529  				require.Contains(err.Error(), tc.expErrMsg)
   530  			} else {
   531  				require.NoError(err)
   532  			}
   533  		})
   534  	}
   535  }
   536  
   537  func (s *KeeperTestSuite) TestMsgBeginRedelegate() {
   538  	ctx, keeper, msgServer := s.ctx, s.stakingKeeper, s.msgServer
   539  	require := s.Require()
   540  	s.execExpectCalls()
   541  
   542  	srcValAddr := ValAddr
   543  	addr2 := sdk.AccAddress(PKS[1].Address())
   544  	dstValAddr := sdk.ValAddress(addr2)
   545  
   546  	pk := ed25519.GenPrivKey().PubKey()
   547  	require.NotNil(pk)
   548  	dstPk := ed25519.GenPrivKey().PubKey()
   549  	require.NotNil(dstPk)
   550  
   551  	comm := stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0))
   552  	amt := sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))}
   553  
   554  	msg, err := stakingtypes.NewMsgCreateValidator(srcValAddr.String(), pk, amt, stakingtypes.Description{Moniker: "NewVal"}, comm, math.OneInt())
   555  	require.NoError(err)
   556  	res, err := msgServer.CreateValidator(ctx, msg)
   557  	require.NoError(err)
   558  	require.NotNil(res)
   559  	s.bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), addr2, stakingtypes.NotBondedPoolName, gomock.Any()).AnyTimes()
   560  
   561  	msg, err = stakingtypes.NewMsgCreateValidator(dstValAddr.String(), dstPk, amt, stakingtypes.Description{Moniker: "NewVal"}, comm, math.OneInt())
   562  	require.NoError(err)
   563  
   564  	res, err = msgServer.CreateValidator(ctx, msg)
   565  	require.NoError(err)
   566  	require.NotNil(res)
   567  
   568  	shares := math.LegacyNewDec(100)
   569  	del := stakingtypes.NewDelegation(Addr.String(), srcValAddr.String(), shares)
   570  	require.NoError(keeper.SetDelegation(ctx, del))
   571  	_, err = keeper.GetDelegation(ctx, Addr, srcValAddr)
   572  	require.NoError(err)
   573  
   574  	testCases := []struct {
   575  		name      string
   576  		input     *stakingtypes.MsgBeginRedelegate
   577  		expErr    bool
   578  		expErrMsg string
   579  	}{
   580  		{
   581  			name: "invalid source validator",
   582  			input: &stakingtypes.MsgBeginRedelegate{
   583  				DelegatorAddress:    Addr.String(),
   584  				ValidatorSrcAddress: sdk.AccAddress([]byte("invalid")).String(),
   585  				ValidatorDstAddress: dstValAddr.String(),
   586  				Amount:              sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   587  			},
   588  			expErr:    true,
   589  			expErrMsg: "invalid source validator address",
   590  		},
   591  		{
   592  			name: "empty delegator",
   593  			input: &stakingtypes.MsgBeginRedelegate{
   594  				DelegatorAddress:    "",
   595  				ValidatorSrcAddress: srcValAddr.String(),
   596  				ValidatorDstAddress: dstValAddr.String(),
   597  				Amount:              sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   598  			},
   599  			expErr:    true,
   600  			expErrMsg: "invalid delegator address: empty address string is not allowed",
   601  		},
   602  		{
   603  			name: "invalid delegator",
   604  			input: &stakingtypes.MsgBeginRedelegate{
   605  				DelegatorAddress:    "invalid",
   606  				ValidatorSrcAddress: srcValAddr.String(),
   607  				ValidatorDstAddress: dstValAddr.String(),
   608  				Amount:              sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))},
   609  			},
   610  			expErr:    true,
   611  			expErrMsg: "invalid delegator address: decoding bech32 failed: invalid bech32 string length 7",
   612  		},
   613  		{
   614  			name: "invalid destination validator",
   615  			input: &stakingtypes.MsgBeginRedelegate{
   616  				DelegatorAddress:    Addr.String(),
   617  				ValidatorSrcAddress: srcValAddr.String(),
   618  				ValidatorDstAddress: sdk.AccAddress([]byte("invalid")).String(),
   619  				Amount:              sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   620  			},
   621  			expErr:    true,
   622  			expErrMsg: "invalid destination validator address",
   623  		},
   624  		{
   625  			name: "validator does not exist",
   626  			input: &stakingtypes.MsgBeginRedelegate{
   627  				DelegatorAddress:    Addr.String(),
   628  				ValidatorSrcAddress: sdk.ValAddress([]byte("invalid")).String(),
   629  				ValidatorDstAddress: dstValAddr.String(),
   630  				Amount:              sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   631  			},
   632  			expErr:    true,
   633  			expErrMsg: "validator does not exist",
   634  		},
   635  		{
   636  			name: "self redelegation",
   637  			input: &stakingtypes.MsgBeginRedelegate{
   638  				DelegatorAddress:    Addr.String(),
   639  				ValidatorSrcAddress: srcValAddr.String(),
   640  				ValidatorDstAddress: srcValAddr.String(),
   641  				Amount:              sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   642  			},
   643  			expErr:    true,
   644  			expErrMsg: "cannot redelegate to the same validator",
   645  		},
   646  		{
   647  			name: "amount greater than delegated shares amount",
   648  			input: &stakingtypes.MsgBeginRedelegate{
   649  				DelegatorAddress:    Addr.String(),
   650  				ValidatorSrcAddress: srcValAddr.String(),
   651  				ValidatorDstAddress: dstValAddr.String(),
   652  				Amount:              sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(101)),
   653  			},
   654  			expErr:    true,
   655  			expErrMsg: "invalid shares amount",
   656  		},
   657  		{
   658  			name: "zero amount",
   659  			input: &stakingtypes.MsgBeginRedelegate{
   660  				DelegatorAddress:    Addr.String(),
   661  				ValidatorSrcAddress: srcValAddr.String(),
   662  				ValidatorDstAddress: dstValAddr.String(),
   663  				Amount:              sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)),
   664  			},
   665  			expErr:    true,
   666  			expErrMsg: "invalid shares amount",
   667  		},
   668  		{
   669  			name: "invalid coin denom",
   670  			input: &stakingtypes.MsgBeginRedelegate{
   671  				DelegatorAddress:    Addr.String(),
   672  				ValidatorSrcAddress: srcValAddr.String(),
   673  				ValidatorDstAddress: dstValAddr.String(),
   674  				Amount:              sdk.NewCoin("test", shares.RoundInt()),
   675  			},
   676  			expErr:    true,
   677  			expErrMsg: "invalid coin denomination",
   678  		},
   679  		{
   680  			name: "valid msg",
   681  			input: &stakingtypes.MsgBeginRedelegate{
   682  				DelegatorAddress:    Addr.String(),
   683  				ValidatorSrcAddress: srcValAddr.String(),
   684  				ValidatorDstAddress: dstValAddr.String(),
   685  				Amount:              sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   686  			},
   687  			expErr: false,
   688  		},
   689  	}
   690  
   691  	for _, tc := range testCases {
   692  		tc := tc
   693  		s.T().Run(tc.name, func(t *testing.T) {
   694  			_, err := msgServer.BeginRedelegate(ctx, tc.input)
   695  			if tc.expErr {
   696  				require.Error(err)
   697  				require.Contains(err.Error(), tc.expErrMsg)
   698  			} else {
   699  				require.NoError(err)
   700  			}
   701  		})
   702  	}
   703  }
   704  
   705  func (s *KeeperTestSuite) TestMsgUndelegate() {
   706  	ctx, keeper, msgServer := s.ctx, s.stakingKeeper, s.msgServer
   707  	require := s.Require()
   708  	s.execExpectCalls()
   709  
   710  	pk := ed25519.GenPrivKey().PubKey()
   711  	require.NotNil(pk)
   712  
   713  	comm := stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0))
   714  	amt := sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))}
   715  
   716  	msg, err := stakingtypes.NewMsgCreateValidator(ValAddr.String(), pk, amt, stakingtypes.Description{Moniker: "NewVal"}, comm, math.OneInt())
   717  	require.NoError(err)
   718  	res, err := msgServer.CreateValidator(ctx, msg)
   719  	require.NoError(err)
   720  	require.NotNil(res)
   721  
   722  	shares := math.LegacyNewDec(100)
   723  	del := stakingtypes.NewDelegation(Addr.String(), ValAddr.String(), shares)
   724  	require.NoError(keeper.SetDelegation(ctx, del))
   725  	_, err = keeper.GetDelegation(ctx, Addr, ValAddr)
   726  	require.NoError(err)
   727  
   728  	testCases := []struct {
   729  		name      string
   730  		input     *stakingtypes.MsgUndelegate
   731  		expErr    bool
   732  		expErrMsg string
   733  	}{
   734  		{
   735  			name: "invalid validator",
   736  			input: &stakingtypes.MsgUndelegate{
   737  				DelegatorAddress: Addr.String(),
   738  				ValidatorAddress: sdk.AccAddress([]byte("invalid")).String(),
   739  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   740  			},
   741  			expErr:    true,
   742  			expErrMsg: "invalid validator address",
   743  		},
   744  		{
   745  			name: "empty delegator",
   746  			input: &stakingtypes.MsgUndelegate{
   747  				DelegatorAddress: "",
   748  				ValidatorAddress: ValAddr.String(),
   749  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: shares.RoundInt()},
   750  			},
   751  			expErr:    true,
   752  			expErrMsg: "invalid delegator address: empty address string is not allowed",
   753  		},
   754  		{
   755  			name: "invalid delegator",
   756  			input: &stakingtypes.MsgUndelegate{
   757  				DelegatorAddress: "invalid",
   758  				ValidatorAddress: ValAddr.String(),
   759  				Amount:           sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: shares.RoundInt()},
   760  			},
   761  			expErr:    true,
   762  			expErrMsg: "invalid delegator address: decoding bech32 failed",
   763  		},
   764  		{
   765  			name: "validator does not exist",
   766  			input: &stakingtypes.MsgUndelegate{
   767  				DelegatorAddress: Addr.String(),
   768  				ValidatorAddress: sdk.ValAddress([]byte("invalid")).String(),
   769  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   770  			},
   771  			expErr:    true,
   772  			expErrMsg: "validator does not exist",
   773  		},
   774  		{
   775  			name: "amount greater than delegated shares amount",
   776  			input: &stakingtypes.MsgUndelegate{
   777  				DelegatorAddress: Addr.String(),
   778  				ValidatorAddress: ValAddr.String(),
   779  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(101)),
   780  			},
   781  			expErr:    true,
   782  			expErrMsg: "invalid shares amount",
   783  		},
   784  		{
   785  			name: "zero amount",
   786  			input: &stakingtypes.MsgUndelegate{
   787  				DelegatorAddress: Addr.String(),
   788  				ValidatorAddress: ValAddr.String(),
   789  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)),
   790  			},
   791  			expErr:    true,
   792  			expErrMsg: "invalid shares amount",
   793  		},
   794  		{
   795  			name: "invalid coin denom",
   796  			input: &stakingtypes.MsgUndelegate{
   797  				DelegatorAddress: Addr.String(),
   798  				ValidatorAddress: ValAddr.String(),
   799  				Amount:           sdk.NewCoin("test", shares.RoundInt()),
   800  			},
   801  			expErr:    true,
   802  			expErrMsg: "invalid coin denomination",
   803  		},
   804  		{
   805  			name: "valid msg",
   806  			input: &stakingtypes.MsgUndelegate{
   807  				DelegatorAddress: Addr.String(),
   808  				ValidatorAddress: ValAddr.String(),
   809  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   810  			},
   811  			expErr: false,
   812  		},
   813  	}
   814  
   815  	for _, tc := range testCases {
   816  		tc := tc
   817  		s.T().Run(tc.name, func(t *testing.T) {
   818  			_, err := msgServer.Undelegate(ctx, tc.input)
   819  			if tc.expErr {
   820  				require.Error(err)
   821  				require.Contains(err.Error(), tc.expErrMsg)
   822  			} else {
   823  				require.NoError(err)
   824  			}
   825  		})
   826  	}
   827  }
   828  
   829  func (s *KeeperTestSuite) TestMsgCancelUnbondingDelegation() {
   830  	ctx, keeper, msgServer, ak := s.ctx, s.stakingKeeper, s.msgServer, s.accountKeeper
   831  	require := s.Require()
   832  
   833  	pk := ed25519.GenPrivKey().PubKey()
   834  	require.NotNil(pk)
   835  
   836  	comm := stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0))
   837  	amt := sdk.Coin{Denom: sdk.DefaultBondDenom, Amount: keeper.TokensFromConsensusPower(s.ctx, int64(100))}
   838  
   839  	s.bankKeeper.EXPECT().DelegateCoinsFromAccountToModule(gomock.Any(), Addr, stakingtypes.NotBondedPoolName, gomock.Any()).AnyTimes()
   840  
   841  	msg, err := stakingtypes.NewMsgCreateValidator(ValAddr.String(), pk, amt, stakingtypes.Description{Moniker: "NewVal"}, comm, math.OneInt())
   842  	require.NoError(err)
   843  	res, err := msgServer.CreateValidator(ctx, msg)
   844  	require.NoError(err)
   845  	require.NotNil(res)
   846  
   847  	shares := math.LegacyNewDec(100)
   848  	del := stakingtypes.NewDelegation(Addr.String(), ValAddr.String(), shares)
   849  	require.NoError(keeper.SetDelegation(ctx, del))
   850  	resDel, err := keeper.GetDelegation(ctx, Addr, ValAddr)
   851  	require.NoError(err)
   852  	require.Equal(del, resDel)
   853  
   854  	ubd := stakingtypes.NewUnbondingDelegation(Addr, ValAddr, 10, ctx.BlockTime().Add(time.Minute*10), shares.RoundInt(), 0, keeper.ValidatorAddressCodec(), ak.AddressCodec())
   855  	require.NoError(keeper.SetUnbondingDelegation(ctx, ubd))
   856  	resUnbond, err := keeper.GetUnbondingDelegation(ctx, Addr, ValAddr)
   857  	require.NoError(err)
   858  	require.Equal(ubd, resUnbond)
   859  
   860  	testCases := []struct {
   861  		name      string
   862  		input     *stakingtypes.MsgCancelUnbondingDelegation
   863  		expErr    bool
   864  		expErrMsg string
   865  	}{
   866  		{
   867  			name: "invalid validator",
   868  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   869  				DelegatorAddress: Addr.String(),
   870  				ValidatorAddress: sdk.AccAddress([]byte("invalid")).String(),
   871  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   872  				CreationHeight:   10,
   873  			},
   874  			expErr:    true,
   875  			expErrMsg: "invalid validator address",
   876  		},
   877  		{
   878  			name: "empty delegator",
   879  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   880  				DelegatorAddress: "",
   881  				ValidatorAddress: ValAddr.String(),
   882  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   883  				CreationHeight:   10,
   884  			},
   885  			expErr:    true,
   886  			expErrMsg: "invalid delegator address: empty address string is not allowed",
   887  		},
   888  		{
   889  			name: "invalid delegator",
   890  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   891  				DelegatorAddress: "invalid",
   892  				ValidatorAddress: ValAddr.String(),
   893  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   894  				CreationHeight:   10,
   895  			},
   896  			expErr:    true,
   897  			expErrMsg: "invalid delegator address: decoding bech32 failed",
   898  		},
   899  		{
   900  			name: "entry not found at height",
   901  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   902  				DelegatorAddress: Addr.String(),
   903  				ValidatorAddress: ValAddr.String(),
   904  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   905  				CreationHeight:   11,
   906  			},
   907  			expErr:    true,
   908  			expErrMsg: "unbonding delegation entry is not found at block height",
   909  		},
   910  		{
   911  			name: "invalid height",
   912  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   913  				DelegatorAddress: Addr.String(),
   914  				ValidatorAddress: ValAddr.String(),
   915  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   916  				CreationHeight:   -1,
   917  			},
   918  			expErr:    true,
   919  			expErrMsg: "invalid height",
   920  		},
   921  		{
   922  			name: "invalid coin",
   923  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   924  				DelegatorAddress: Addr.String(),
   925  				ValidatorAddress: ValAddr.String(),
   926  				Amount:           sdk.NewCoin("test", shares.RoundInt()),
   927  				CreationHeight:   10,
   928  			},
   929  			expErr:    true,
   930  			expErrMsg: "invalid coin denomination",
   931  		},
   932  		{
   933  			name: "validator does not exist",
   934  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   935  				DelegatorAddress: Addr.String(),
   936  				ValidatorAddress: sdk.ValAddress([]byte("invalid")).String(),
   937  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   938  				CreationHeight:   10,
   939  			},
   940  			expErr:    true,
   941  			expErrMsg: "validator does not exist",
   942  		},
   943  		{
   944  			name: "amount is greater than balance",
   945  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   946  				DelegatorAddress: Addr.String(),
   947  				ValidatorAddress: ValAddr.String(),
   948  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(101)),
   949  				CreationHeight:   10,
   950  			},
   951  			expErr:    true,
   952  			expErrMsg: "amount is greater than the unbonding delegation entry balance",
   953  		},
   954  		{
   955  			name: "zero amount",
   956  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   957  				DelegatorAddress: Addr.String(),
   958  				ValidatorAddress: ValAddr.String(),
   959  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)),
   960  				CreationHeight:   10,
   961  			},
   962  			expErr:    true,
   963  			expErrMsg: "invalid amount",
   964  		},
   965  		{
   966  			name: "valid msg",
   967  			input: &stakingtypes.MsgCancelUnbondingDelegation{
   968  				DelegatorAddress: Addr.String(),
   969  				ValidatorAddress: ValAddr.String(),
   970  				Amount:           sdk.NewCoin(sdk.DefaultBondDenom, shares.RoundInt()),
   971  				CreationHeight:   10,
   972  			},
   973  			expErr: false,
   974  		},
   975  	}
   976  
   977  	for _, tc := range testCases {
   978  		tc := tc
   979  		s.T().Run(tc.name, func(t *testing.T) {
   980  			_, err := msgServer.CancelUnbondingDelegation(ctx, tc.input)
   981  			if tc.expErr {
   982  				require.Error(err)
   983  				require.Contains(err.Error(), tc.expErrMsg)
   984  			} else {
   985  				require.NoError(err)
   986  			}
   987  		})
   988  	}
   989  }
   990  
   991  func (s *KeeperTestSuite) TestMsgUpdateParams() {
   992  	ctx, keeper, msgServer := s.ctx, s.stakingKeeper, s.msgServer
   993  	require := s.Require()
   994  
   995  	testCases := []struct {
   996  		name      string
   997  		input     *stakingtypes.MsgUpdateParams
   998  		expErr    bool
   999  		expErrMsg string
  1000  	}{
  1001  		{
  1002  			name: "valid params",
  1003  			input: &stakingtypes.MsgUpdateParams{
  1004  				Authority: keeper.GetAuthority(),
  1005  				Params:    stakingtypes.DefaultParams(),
  1006  			},
  1007  			expErr: false,
  1008  		},
  1009  		{
  1010  			name: "invalid authority",
  1011  			input: &stakingtypes.MsgUpdateParams{
  1012  				Authority: "invalid",
  1013  				Params:    stakingtypes.DefaultParams(),
  1014  			},
  1015  			expErr:    true,
  1016  			expErrMsg: "invalid authority",
  1017  		},
  1018  		{
  1019  			name: "negative commission rate",
  1020  			input: &stakingtypes.MsgUpdateParams{
  1021  				Authority: keeper.GetAuthority(),
  1022  				Params: stakingtypes.Params{
  1023  					MinCommissionRate: math.LegacyNewDec(-10),
  1024  					UnbondingTime:     stakingtypes.DefaultUnbondingTime,
  1025  					MaxValidators:     stakingtypes.DefaultMaxValidators,
  1026  					MaxEntries:        stakingtypes.DefaultMaxEntries,
  1027  					HistoricalEntries: stakingtypes.DefaultHistoricalEntries,
  1028  					BondDenom:         stakingtypes.BondStatusBonded,
  1029  				},
  1030  			},
  1031  			expErr:    true,
  1032  			expErrMsg: "minimum commission rate cannot be negative",
  1033  		},
  1034  		{
  1035  			name: "commission rate cannot be bigger than 100",
  1036  			input: &stakingtypes.MsgUpdateParams{
  1037  				Authority: keeper.GetAuthority(),
  1038  				Params: stakingtypes.Params{
  1039  					MinCommissionRate: math.LegacyNewDec(2),
  1040  					UnbondingTime:     stakingtypes.DefaultUnbondingTime,
  1041  					MaxValidators:     stakingtypes.DefaultMaxValidators,
  1042  					MaxEntries:        stakingtypes.DefaultMaxEntries,
  1043  					HistoricalEntries: stakingtypes.DefaultHistoricalEntries,
  1044  					BondDenom:         stakingtypes.BondStatusBonded,
  1045  				},
  1046  			},
  1047  			expErr:    true,
  1048  			expErrMsg: "minimum commission rate cannot be greater than 100%",
  1049  		},
  1050  		{
  1051  			name: "invalid bond denom",
  1052  			input: &stakingtypes.MsgUpdateParams{
  1053  				Authority: keeper.GetAuthority(),
  1054  				Params: stakingtypes.Params{
  1055  					MinCommissionRate: stakingtypes.DefaultMinCommissionRate,
  1056  					UnbondingTime:     stakingtypes.DefaultUnbondingTime,
  1057  					MaxValidators:     stakingtypes.DefaultMaxValidators,
  1058  					MaxEntries:        stakingtypes.DefaultMaxEntries,
  1059  					HistoricalEntries: stakingtypes.DefaultHistoricalEntries,
  1060  					BondDenom:         "",
  1061  				},
  1062  			},
  1063  			expErr:    true,
  1064  			expErrMsg: "bond denom cannot be blank",
  1065  		},
  1066  		{
  1067  			name: "max validators must be positive",
  1068  			input: &stakingtypes.MsgUpdateParams{
  1069  				Authority: keeper.GetAuthority(),
  1070  				Params: stakingtypes.Params{
  1071  					MinCommissionRate: stakingtypes.DefaultMinCommissionRate,
  1072  					UnbondingTime:     stakingtypes.DefaultUnbondingTime,
  1073  					MaxValidators:     0,
  1074  					MaxEntries:        stakingtypes.DefaultMaxEntries,
  1075  					HistoricalEntries: stakingtypes.DefaultHistoricalEntries,
  1076  					BondDenom:         stakingtypes.BondStatusBonded,
  1077  				},
  1078  			},
  1079  			expErr:    true,
  1080  			expErrMsg: "max validators must be positive",
  1081  		},
  1082  		{
  1083  			name: "max entries most be positive",
  1084  			input: &stakingtypes.MsgUpdateParams{
  1085  				Authority: keeper.GetAuthority(),
  1086  				Params: stakingtypes.Params{
  1087  					MinCommissionRate: stakingtypes.DefaultMinCommissionRate,
  1088  					UnbondingTime:     stakingtypes.DefaultUnbondingTime,
  1089  					MaxValidators:     stakingtypes.DefaultMaxValidators,
  1090  					MaxEntries:        0,
  1091  					HistoricalEntries: stakingtypes.DefaultHistoricalEntries,
  1092  					BondDenom:         stakingtypes.BondStatusBonded,
  1093  				},
  1094  			},
  1095  			expErr:    true,
  1096  			expErrMsg: "max entries must be positive",
  1097  		},
  1098  		{
  1099  			name: "negative unbounding time",
  1100  			input: &stakingtypes.MsgUpdateParams{
  1101  				Authority: keeper.GetAuthority(),
  1102  				Params: stakingtypes.Params{
  1103  					UnbondingTime:     time.Hour * 24 * 7 * 3 * -1,
  1104  					MaxEntries:        stakingtypes.DefaultMaxEntries,
  1105  					MaxValidators:     stakingtypes.DefaultMaxValidators,
  1106  					HistoricalEntries: stakingtypes.DefaultHistoricalEntries,
  1107  					MinCommissionRate: stakingtypes.DefaultMinCommissionRate,
  1108  					BondDenom:         "denom",
  1109  				},
  1110  			},
  1111  			expErr:    true,
  1112  			expErrMsg: "unbonding time must be positive",
  1113  		},
  1114  	}
  1115  
  1116  	for _, tc := range testCases {
  1117  		tc := tc
  1118  		s.T().Run(tc.name, func(t *testing.T) {
  1119  			_, err := msgServer.UpdateParams(ctx, tc.input)
  1120  			if tc.expErr {
  1121  				require.Error(err)
  1122  				require.Contains(err.Error(), tc.expErrMsg)
  1123  			} else {
  1124  				require.NoError(err)
  1125  			}
  1126  		})
  1127  	}
  1128  }