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 }