github.com/cosmos/cosmos-sdk@v0.50.10/x/staking/keeper/validator_test.go (about) 1 package keeper_test 2 3 import ( 4 "time" 5 6 abci "github.com/cometbft/cometbft/abci/types" 7 "github.com/golang/mock/gomock" 8 9 "cosmossdk.io/math" 10 11 sdk "github.com/cosmos/cosmos-sdk/types" 12 stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" 13 "github.com/cosmos/cosmos-sdk/x/staking/testutil" 14 stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 15 ) 16 17 func (s *KeeperTestSuite) applyValidatorSetUpdates(ctx sdk.Context, keeper *stakingkeeper.Keeper, expectedUpdatesLen int) []abci.ValidatorUpdate { 18 updates, err := keeper.ApplyAndReturnValidatorSetUpdates(ctx) 19 s.Require().NoError(err) 20 if expectedUpdatesLen >= 0 { 21 s.Require().Equal(expectedUpdatesLen, len(updates), "%v", updates) 22 } 23 return updates 24 } 25 26 func (s *KeeperTestSuite) TestValidator() { 27 ctx, keeper := s.ctx, s.stakingKeeper 28 require := s.Require() 29 30 valPubKey := PKs[0] 31 valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) 32 valTokens := keeper.TokensFromConsensusPower(ctx, 10) 33 34 // test how the validator is set from a purely unbonbed pool 35 validator := testutil.NewValidator(s.T(), valAddr, valPubKey) 36 validator, _ = validator.AddTokensFromDel(valTokens) 37 require.Equal(stakingtypes.Unbonded, validator.Status) 38 require.Equal(valTokens, validator.Tokens) 39 require.Equal(valTokens, validator.DelegatorShares.RoundInt()) 40 require.NoError(keeper.SetValidator(ctx, validator)) 41 require.NoError(keeper.SetValidatorByPowerIndex(ctx, validator)) 42 require.NoError(keeper.SetValidatorByConsAddr(ctx, validator)) 43 44 // ensure update 45 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 46 updates := s.applyValidatorSetUpdates(ctx, keeper, 1) 47 validator, err := keeper.GetValidator(ctx, valAddr) 48 require.NoError(err) 49 require.Equal(validator.ABCIValidatorUpdate(keeper.PowerReduction(ctx)), updates[0]) 50 51 // after the save the validator should be bonded 52 require.Equal(stakingtypes.Bonded, validator.Status) 53 require.Equal(valTokens, validator.Tokens) 54 require.Equal(valTokens, validator.DelegatorShares.RoundInt()) 55 56 // check each store for being saved 57 consAddr, err := validator.GetConsAddr() 58 require.NoError(err) 59 resVal, err := keeper.GetValidatorByConsAddr(ctx, consAddr) 60 require.NoError(err) 61 require.True(validator.MinEqual(&resVal)) 62 63 resVals, err := keeper.GetLastValidators(ctx) 64 require.NoError(err) 65 require.Equal(1, len(resVals)) 66 require.True(validator.MinEqual(&resVals[0])) 67 68 resVals, err = keeper.GetBondedValidatorsByPower(ctx) 69 require.NoError(err) 70 require.Equal(1, len(resVals)) 71 require.True(validator.MinEqual(&resVals[0])) 72 73 allVals, err := keeper.GetAllValidators(ctx) 74 require.NoError(err) 75 require.Equal(1, len(allVals)) 76 77 // check the last validator power 78 power := int64(100) 79 require.NoError(keeper.SetLastValidatorPower(ctx, valAddr, power)) 80 resPower, err := keeper.GetLastValidatorPower(ctx, valAddr) 81 require.NoError(err) 82 require.Equal(power, resPower) 83 require.NoError(keeper.DeleteLastValidatorPower(ctx, valAddr)) 84 resPower, err = keeper.GetLastValidatorPower(ctx, valAddr) 85 require.NoError(err) 86 require.Equal(int64(0), resPower) 87 } 88 89 // This function tests UpdateValidator, GetValidator, GetLastValidators, RemoveValidator 90 func (s *KeeperTestSuite) TestValidatorBasics() { 91 ctx, keeper := s.ctx, s.stakingKeeper 92 require := s.Require() 93 94 // construct the validators 95 var validators [3]stakingtypes.Validator 96 powers := []int64{9, 8, 7} 97 for i, power := range powers { 98 validators[i] = testutil.NewValidator(s.T(), sdk.ValAddress(PKs[i].Address().Bytes()), PKs[i]) 99 validators[i].Status = stakingtypes.Unbonded 100 validators[i].Tokens = math.ZeroInt() 101 tokens := keeper.TokensFromConsensusPower(ctx, power) 102 103 validators[i], _ = validators[i].AddTokensFromDel(tokens) 104 } 105 106 require.Equal(keeper.TokensFromConsensusPower(ctx, 9), validators[0].Tokens) 107 require.Equal(keeper.TokensFromConsensusPower(ctx, 8), validators[1].Tokens) 108 require.Equal(keeper.TokensFromConsensusPower(ctx, 7), validators[2].Tokens) 109 110 // check the empty keeper first 111 _, err := keeper.GetValidator(ctx, sdk.ValAddress(PKs[0].Address().Bytes())) 112 require.ErrorIs(err, stakingtypes.ErrNoValidatorFound) 113 resVals, err := keeper.GetLastValidators(ctx) 114 require.NoError(err) 115 require.Zero(len(resVals)) 116 117 resVals, err = keeper.GetValidators(ctx, 2) 118 require.NoError(err) 119 require.Len(resVals, 0) 120 121 // set and retrieve a record 122 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 123 validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], true) 124 require.NoError(keeper.SetValidatorByConsAddr(ctx, validators[0])) 125 resVal, err := keeper.GetValidator(ctx, sdk.ValAddress(PKs[0].Address().Bytes())) 126 require.NoError(err) 127 require.True(validators[0].MinEqual(&resVal)) 128 129 // retrieve from consensus 130 resVal, err = keeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address())) 131 require.NoError(err) 132 require.True(validators[0].MinEqual(&resVal)) 133 resVal, err = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) 134 require.NoError(err) 135 require.True(validators[0].MinEqual(&resVal)) 136 137 resVals, err = keeper.GetLastValidators(ctx) 138 require.NoError(err) 139 require.Equal(1, len(resVals)) 140 require.True(validators[0].MinEqual(&resVals[0])) 141 require.Equal(stakingtypes.Bonded, validators[0].Status) 142 require.True(keeper.TokensFromConsensusPower(ctx, 9).Equal(validators[0].BondedTokens())) 143 144 // modify a records, save, and retrieve 145 validators[0].Status = stakingtypes.Bonded 146 validators[0].Tokens = keeper.TokensFromConsensusPower(ctx, 10) 147 validators[0].DelegatorShares = math.LegacyNewDecFromInt(validators[0].Tokens) 148 validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], true) 149 resVal, err = keeper.GetValidator(ctx, sdk.ValAddress(PKs[0].Address().Bytes())) 150 require.NoError(err) 151 require.True(validators[0].MinEqual(&resVal)) 152 153 resVals, err = keeper.GetLastValidators(ctx) 154 require.NoError(err) 155 require.Equal(1, len(resVals)) 156 require.True(validators[0].MinEqual(&resVals[0])) 157 158 // add other validators 159 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 160 validators[1] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[1], true) 161 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 162 validators[2] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[2], true) 163 resVal, err = keeper.GetValidator(ctx, sdk.ValAddress(PKs[1].Address().Bytes())) 164 require.NoError(err) 165 require.True(validators[1].MinEqual(&resVal)) 166 resVal, err = keeper.GetValidator(ctx, sdk.ValAddress(PKs[2].Address().Bytes())) 167 require.NoError(err) 168 require.True(validators[2].MinEqual(&resVal)) 169 170 resVals, err = keeper.GetLastValidators(ctx) 171 require.NoError(err) 172 require.Equal(3, len(resVals)) 173 174 // remove a record 175 176 bz, err := keeper.ValidatorAddressCodec().StringToBytes(validators[1].GetOperator()) 177 require.NoError(err) 178 179 // shouldn't be able to remove if status is not unbonded 180 require.EqualError(keeper.RemoveValidator(ctx, bz), "cannot call RemoveValidator on bonded or unbonding validators: failed to remove validator") 181 182 // shouldn't be able to remove if there are still tokens left 183 validators[1].Status = stakingtypes.Unbonded 184 require.NoError(keeper.SetValidator(ctx, validators[1])) 185 require.EqualError(keeper.RemoveValidator(ctx, bz), "attempting to remove a validator which still contains tokens: failed to remove validator") 186 187 validators[1].Tokens = math.ZeroInt() // ...remove all tokens 188 require.NoError(keeper.SetValidator(ctx, validators[1])) // ...set the validator 189 require.NoError(keeper.RemoveValidator(ctx, bz)) // Now it can be removed. 190 _, err = keeper.GetValidator(ctx, sdk.ValAddress(PKs[1].Address().Bytes())) 191 require.ErrorIs(err, stakingtypes.ErrNoValidatorFound) 192 } 193 194 func (s *KeeperTestSuite) TestUpdateValidatorByPowerIndex() { 195 ctx, keeper := s.ctx, s.stakingKeeper 196 require := s.Require() 197 198 valPubKey := PKs[0] 199 valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) 200 valTokens := keeper.TokensFromConsensusPower(ctx, 100) 201 202 // add a validator 203 validator := testutil.NewValidator(s.T(), valAddr, PKs[0]) 204 validator, delSharesCreated := validator.AddTokensFromDel(valTokens) 205 require.Equal(stakingtypes.Unbonded, validator.Status) 206 require.Equal(valTokens, validator.Tokens) 207 208 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 209 stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) 210 validator, err := keeper.GetValidator(ctx, valAddr) 211 require.NoError(err) 212 require.Equal(valTokens, validator.Tokens) 213 214 power := stakingtypes.GetValidatorsByPowerIndexKey(validator, keeper.PowerReduction(ctx), keeper.ValidatorAddressCodec()) 215 require.True(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) 216 217 // burn half the delegator shares 218 require.NoError(keeper.DeleteValidatorByPowerIndex(ctx, validator)) 219 validator, burned := validator.RemoveDelShares(delSharesCreated.Quo(math.LegacyNewDec(2))) 220 require.Equal(keeper.TokensFromConsensusPower(ctx, 50), burned) 221 stakingkeeper.TestingUpdateValidator(keeper, ctx, validator, true) // update the validator, possibly kicking it out 222 require.False(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) 223 224 validator, err = keeper.GetValidator(ctx, valAddr) 225 require.NoError(err) 226 227 power = stakingtypes.GetValidatorsByPowerIndexKey(validator, keeper.PowerReduction(ctx), keeper.ValidatorAddressCodec()) 228 require.True(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) 229 230 // set new validator by power index 231 require.NoError(keeper.DeleteValidatorByPowerIndex(ctx, validator)) 232 require.False(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) 233 require.NoError(keeper.SetNewValidatorByPowerIndex(ctx, validator)) 234 require.True(stakingkeeper.ValidatorByPowerIndexExists(ctx, keeper, power)) 235 } 236 237 func (s *KeeperTestSuite) TestApplyAndReturnValidatorSetUpdatesPowerDecrease() { 238 ctx, keeper := s.ctx, s.stakingKeeper 239 require := s.Require() 240 241 powers := []int64{100, 100} 242 var validators [2]stakingtypes.Validator 243 244 for i, power := range powers { 245 validators[i] = testutil.NewValidator(s.T(), sdk.ValAddress(PKs[i].Address().Bytes()), PKs[i]) 246 tokens := keeper.TokensFromConsensusPower(ctx, power) 247 validators[i], _ = validators[i].AddTokensFromDel(tokens) 248 249 } 250 251 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 252 validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], false) 253 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 254 validators[1] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[1], false) 255 s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, gomock.Any()) 256 s.applyValidatorSetUpdates(ctx, keeper, 2) 257 258 // check initial power 259 require.Equal(int64(100), validators[0].GetConsensusPower(keeper.PowerReduction(ctx))) 260 require.Equal(int64(100), validators[1].GetConsensusPower(keeper.PowerReduction(ctx))) 261 262 // test multiple value change 263 // tendermintUpdate set: {c1, c3} -> {c1', c3'} 264 delTokens1 := keeper.TokensFromConsensusPower(ctx, 20) 265 delTokens2 := keeper.TokensFromConsensusPower(ctx, 30) 266 validators[0], _ = validators[0].RemoveDelShares(math.LegacyNewDecFromInt(delTokens1)) 267 validators[1], _ = validators[1].RemoveDelShares(math.LegacyNewDecFromInt(delTokens2)) 268 validators[0] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[0], false) 269 validators[1] = stakingkeeper.TestingUpdateValidator(keeper, ctx, validators[1], false) 270 271 // power has changed 272 require.Equal(int64(80), validators[0].GetConsensusPower(keeper.PowerReduction(ctx))) 273 require.Equal(int64(70), validators[1].GetConsensusPower(keeper.PowerReduction(ctx))) 274 275 // CometBFT updates should reflect power change 276 updates := s.applyValidatorSetUpdates(ctx, keeper, 2) 277 require.Equal(validators[0].ABCIValidatorUpdate(keeper.PowerReduction(ctx)), updates[0]) 278 require.Equal(validators[1].ABCIValidatorUpdate(keeper.PowerReduction(ctx)), updates[1]) 279 } 280 281 func (s *KeeperTestSuite) TestUpdateValidatorCommission() { 282 ctx, keeper := s.ctx, s.stakingKeeper 283 require := s.Require() 284 285 // Set MinCommissionRate to 0.05 286 params, err := keeper.GetParams(ctx) 287 require.NoError(err) 288 params.MinCommissionRate = math.LegacyNewDecWithPrec(5, 2) 289 require.NoError(keeper.SetParams(ctx, params)) 290 291 commission1 := stakingtypes.NewCommissionWithTime( 292 math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDecWithPrec(3, 1), 293 math.LegacyNewDecWithPrec(1, 1), time.Now().UTC().Add(time.Duration(-1)*time.Hour), 294 ) 295 commission2 := stakingtypes.NewCommission(math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDecWithPrec(3, 1), math.LegacyNewDecWithPrec(1, 1)) 296 297 val1 := testutil.NewValidator(s.T(), sdk.ValAddress(PKs[0].Address().Bytes()), PKs[0]) 298 val2 := testutil.NewValidator(s.T(), sdk.ValAddress(PKs[1].Address().Bytes()), PKs[1]) 299 300 val1, _ = val1.SetInitialCommission(commission1) 301 val2, _ = val2.SetInitialCommission(commission2) 302 303 require.NoError(keeper.SetValidator(ctx, val1)) 304 require.NoError(keeper.SetValidator(ctx, val2)) 305 306 testCases := []struct { 307 validator stakingtypes.Validator 308 newRate math.LegacyDec 309 expectedErr bool 310 }{ 311 {val1, math.LegacyZeroDec(), true}, 312 {val2, math.LegacyNewDecWithPrec(-1, 1), true}, 313 {val2, math.LegacyNewDecWithPrec(4, 1), true}, 314 {val2, math.LegacyNewDecWithPrec(3, 1), true}, 315 {val2, math.LegacyNewDecWithPrec(1, 2), true}, 316 {val2, math.LegacyNewDecWithPrec(2, 1), false}, 317 } 318 319 for i, tc := range testCases { 320 commission, err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate) 321 322 if tc.expectedErr { 323 require.Error(err, "expected error for test case #%d with rate: %s", i, tc.newRate) 324 } else { 325 require.NoError(err, 326 "unexpected error for test case #%d with rate: %s", i, tc.newRate, 327 ) 328 329 tc.validator.Commission = commission 330 err = keeper.SetValidator(ctx, tc.validator) 331 require.NoError(err) 332 333 bz, err := keeper.ValidatorAddressCodec().StringToBytes(tc.validator.GetOperator()) 334 require.NoError(err) 335 336 val, err := keeper.GetValidator(ctx, bz) 337 require.NoError(err, 338 "expected to find validator for test case #%d with rate: %s", i, tc.newRate, 339 ) 340 341 require.Equal(tc.newRate, val.Commission.Rate, 342 "expected new validator commission rate for test case #%d with rate: %s", i, tc.newRate, 343 ) 344 require.Equal(ctx.BlockHeader().Time, val.Commission.UpdateTime, 345 "expected new validator commission update time for test case #%d with rate: %s", i, tc.newRate, 346 ) 347 } 348 } 349 } 350 351 func (s *KeeperTestSuite) TestValidatorToken() { 352 ctx, keeper := s.ctx, s.stakingKeeper 353 require := s.Require() 354 355 valPubKey := PKs[0] 356 valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) 357 addTokens := keeper.TokensFromConsensusPower(ctx, 10) 358 delTokens := keeper.TokensFromConsensusPower(ctx, 5) 359 360 validator := testutil.NewValidator(s.T(), valAddr, valPubKey) 361 validator, _, err := keeper.AddValidatorTokensAndShares(ctx, validator, addTokens) 362 require.NoError(err) 363 require.Equal(addTokens, validator.Tokens) 364 validator, _ = keeper.GetValidator(ctx, valAddr) 365 require.Equal(math.LegacyNewDecFromInt(addTokens), validator.DelegatorShares) 366 367 _, _, err = keeper.RemoveValidatorTokensAndShares(ctx, validator, math.LegacyNewDecFromInt(delTokens)) 368 require.NoError(err) 369 validator, _ = keeper.GetValidator(ctx, valAddr) 370 require.Equal(delTokens, validator.Tokens) 371 require.True(validator.DelegatorShares.Equal(math.LegacyNewDecFromInt(delTokens))) 372 373 _, err = keeper.RemoveValidatorTokens(ctx, validator, delTokens) 374 require.NoError(err) 375 validator, _ = keeper.GetValidator(ctx, valAddr) 376 require.True(validator.Tokens.IsZero()) 377 } 378 379 func (s *KeeperTestSuite) TestUnbondingValidator() { 380 ctx, keeper := s.ctx, s.stakingKeeper 381 require := s.Require() 382 383 valPubKey := PKs[0] 384 valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) 385 validator := testutil.NewValidator(s.T(), valAddr, valPubKey) 386 addTokens := keeper.TokensFromConsensusPower(ctx, 10) 387 388 // set unbonding validator 389 endTime := time.Now() 390 endHeight := ctx.BlockHeight() + 10 391 require.NoError(keeper.SetUnbondingValidatorsQueue(ctx, endTime, endHeight, []string{valAddr.String()})) 392 393 resVals, err := keeper.GetUnbondingValidators(ctx, endTime, endHeight) 394 require.NoError(err) 395 require.Equal(1, len(resVals)) 396 require.Equal(valAddr.String(), resVals[0]) 397 398 // add another unbonding validator 399 valAddr1 := sdk.ValAddress(PKs[1].Address().Bytes()) 400 validator1 := testutil.NewValidator(s.T(), valAddr1, PKs[1]) 401 validator1.UnbondingHeight = endHeight 402 validator1.UnbondingTime = endTime 403 require.NoError(keeper.InsertUnbondingValidatorQueue(ctx, validator1)) 404 405 resVals, err = keeper.GetUnbondingValidators(ctx, endTime, endHeight) 406 require.NoError(err) 407 require.Equal(2, len(resVals)) 408 409 // delete unbonding validator from the queue 410 require.NoError(keeper.DeleteValidatorQueue(ctx, validator1)) 411 resVals, err = keeper.GetUnbondingValidators(ctx, endTime, endHeight) 412 require.NoError(err) 413 require.Equal(1, len(resVals)) 414 require.Equal(valAddr.String(), resVals[0]) 415 416 // check unbonding mature validators 417 ctx = ctx.WithBlockHeight(endHeight).WithBlockTime(endTime) 418 err = keeper.UnbondAllMatureValidators(ctx) 419 require.EqualError(err, "validator in the unbonding queue was not found: validator does not exist") 420 421 require.NoError(keeper.SetValidator(ctx, validator)) 422 ctx = ctx.WithBlockHeight(endHeight).WithBlockTime(endTime) 423 424 err = keeper.UnbondAllMatureValidators(ctx) 425 require.EqualError(err, "unexpected validator in unbonding queue; status was not unbonding") 426 427 validator.Status = stakingtypes.Unbonding 428 require.NoError(keeper.SetValidator(ctx, validator)) 429 require.NoError(keeper.UnbondAllMatureValidators(ctx)) 430 validator, err = keeper.GetValidator(ctx, valAddr) 431 require.ErrorIs(err, stakingtypes.ErrNoValidatorFound) 432 433 require.NoError(keeper.SetUnbondingValidatorsQueue(ctx, endTime, endHeight, []string{valAddr.String()})) 434 validator = testutil.NewValidator(s.T(), valAddr, valPubKey) 435 validator, _ = validator.AddTokensFromDel(addTokens) 436 validator.Status = stakingtypes.Unbonding 437 require.NoError(keeper.SetValidator(ctx, validator)) 438 require.NoError(keeper.UnbondAllMatureValidators(ctx)) 439 validator, err = keeper.GetValidator(ctx, valAddr) 440 require.NoError(err) 441 require.Equal(stakingtypes.Unbonded, validator.Status) 442 }