github.com/cosmos/cosmos-sdk@v0.50.10/x/staking/simulation/operations_test.go (about) 1 package simulation_test 2 3 import ( 4 "math/big" 5 "math/rand" 6 "testing" 7 "time" 8 9 abci "github.com/cometbft/cometbft/abci/types" 10 cmttypes "github.com/cometbft/cometbft/types" 11 "github.com/cosmos/gogoproto/proto" 12 "github.com/stretchr/testify/require" 13 "github.com/stretchr/testify/suite" 14 15 "cosmossdk.io/depinject" 16 sdklog "cosmossdk.io/log" 17 "cosmossdk.io/math" 18 19 "github.com/cosmos/cosmos-sdk/client" 20 "github.com/cosmos/cosmos-sdk/codec/address" 21 cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 22 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" 23 "github.com/cosmos/cosmos-sdk/runtime" 24 simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" 25 sdk "github.com/cosmos/cosmos-sdk/types" 26 moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" 27 simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 28 authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" 29 authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 30 bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 31 banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" 32 distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" 33 distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" 34 mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" 35 minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" 36 stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" 37 "github.com/cosmos/cosmos-sdk/x/staking/simulation" 38 "github.com/cosmos/cosmos-sdk/x/staking/testutil" 39 "github.com/cosmos/cosmos-sdk/x/staking/types" 40 ) 41 42 type SimTestSuite struct { 43 suite.Suite 44 45 r *rand.Rand 46 txConfig client.TxConfig 47 accounts []simtypes.Account 48 ctx sdk.Context 49 app *runtime.App 50 bankKeeper bankkeeper.Keeper 51 accountKeeper authkeeper.AccountKeeper 52 distrKeeper distrkeeper.Keeper 53 stakingKeeper *stakingkeeper.Keeper 54 55 encCfg moduletestutil.TestEncodingConfig 56 } 57 58 func (s *SimTestSuite) SetupTest() { 59 sdk.DefaultPowerReduction = math.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)) 60 61 s.r = rand.New(rand.NewSource(1)) 62 accounts := simtypes.RandomAccounts(s.r, 4) 63 64 // create genesis accounts 65 senderPrivKey := secp256k1.GenPrivKey() 66 acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) 67 accs := []simtestutil.GenesisAccount{ 68 {GenesisAccount: acc, Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100000000000000)))}, 69 } 70 71 // create validator set with single validator 72 account := accounts[0] 73 cmtPk, err := cryptocodec.ToCmtPubKeyInterface(account.PubKey) 74 require.NoError(s.T(), err) 75 validator := cmttypes.NewValidator(cmtPk, 1) 76 77 startupCfg := simtestutil.DefaultStartUpConfig() 78 startupCfg.GenesisAccounts = accs 79 startupCfg.ValidatorSet = func() (*cmttypes.ValidatorSet, error) { 80 return cmttypes.NewValidatorSet([]*cmttypes.Validator{validator}), nil 81 } 82 83 var ( 84 accountKeeper authkeeper.AccountKeeper 85 mintKeeper mintkeeper.Keeper 86 bankKeeper bankkeeper.Keeper 87 distrKeeper distrkeeper.Keeper 88 stakingKeeper *stakingkeeper.Keeper 89 ) 90 91 cfg := depinject.Configs( 92 testutil.AppConfig, 93 depinject.Supply(sdklog.NewNopLogger()), 94 ) 95 96 app, err := simtestutil.SetupWithConfiguration(cfg, startupCfg, &s.txConfig, &bankKeeper, &accountKeeper, &mintKeeper, &distrKeeper, &stakingKeeper) 97 require.NoError(s.T(), err) 98 99 ctx := app.BaseApp.NewContext(false) 100 s.Require().NoError(mintKeeper.Params.Set(ctx, minttypes.DefaultParams())) 101 s.Require().NoError(mintKeeper.Minter.Set(ctx, minttypes.DefaultInitialMinter())) 102 103 initAmt := stakingKeeper.TokensFromConsensusPower(ctx, 200) 104 initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) 105 106 s.accounts = accounts 107 // remove genesis validator account 108 // add coins to the accounts 109 for _, account := range accounts[1:] { 110 acc := accountKeeper.NewAccountWithAddress(ctx, account.Address) 111 accountKeeper.SetAccount(ctx, acc) 112 s.Require().NoError(banktestutil.FundAccount(ctx, bankKeeper, account.Address, initCoins)) 113 } 114 115 s.accountKeeper = accountKeeper 116 s.bankKeeper = bankKeeper 117 s.distrKeeper = distrKeeper 118 s.stakingKeeper = stakingKeeper 119 s.ctx = ctx 120 s.app = app 121 } 122 123 // TestWeightedOperations tests the weights of the operations. 124 func (s *SimTestSuite) TestWeightedOperations() { 125 require := s.Require() 126 127 s.ctx.WithChainID("test-chain") 128 129 cdc := s.encCfg.Codec 130 appParams := make(simtypes.AppParams) 131 132 weightedOps := simulation.WeightedOperations(appParams, cdc, s.txConfig, s.accountKeeper, 133 s.bankKeeper, s.stakingKeeper, 134 ) 135 136 expected := []struct { 137 weight int 138 opMsgRoute string 139 opMsgName string 140 }{ 141 {simulation.DefaultWeightMsgCreateValidator, types.ModuleName, sdk.MsgTypeURL(&types.MsgCreateValidator{})}, 142 {simulation.DefaultWeightMsgEditValidator, types.ModuleName, sdk.MsgTypeURL(&types.MsgEditValidator{})}, 143 {simulation.DefaultWeightMsgDelegate, types.ModuleName, sdk.MsgTypeURL(&types.MsgDelegate{})}, 144 {simulation.DefaultWeightMsgUndelegate, types.ModuleName, sdk.MsgTypeURL(&types.MsgUndelegate{})}, 145 {simulation.DefaultWeightMsgBeginRedelegate, types.ModuleName, sdk.MsgTypeURL(&types.MsgBeginRedelegate{})}, 146 {simulation.DefaultWeightMsgCancelUnbondingDelegation, types.ModuleName, sdk.MsgTypeURL(&types.MsgCancelUnbondingDelegation{})}, 147 } 148 149 for i, w := range weightedOps { 150 operationMsg, _, _ := w.Op()(s.r, s.app.BaseApp, s.ctx, s.accounts, s.ctx.ChainID()) 151 // require.NoError(t, err) // TODO check if it should be NoError 152 153 // the following checks are very much dependent from the ordering of the output given 154 // by WeightedOperations. if the ordering in WeightedOperations changes some tests 155 // will fail 156 require.Equal(expected[i].weight, w.Weight(), "weight should be the same") 157 require.Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same") 158 require.Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") 159 } 160 } 161 162 // TestSimulateMsgCreateValidator tests the normal scenario of a valid message of type TypeMsgCreateValidator. 163 // Abonormal scenarios, where the message are created by an errors are not tested here. 164 func (s *SimTestSuite) TestSimulateMsgCreateValidator() { 165 require := s.Require() 166 _, err := s.app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: s.app.LastBlockHeight() + 1, Hash: s.app.LastCommitID().Hash}) 167 require.NoError(err) 168 169 // execute operation 170 op := simulation.SimulateMsgCreateValidator(s.txConfig, s.accountKeeper, s.bankKeeper, s.stakingKeeper) 171 operationMsg, futureOperations, err := op(s.r, s.app.BaseApp, s.ctx, s.accounts[1:], "") 172 require.NoError(err) 173 174 var msg types.MsgCreateValidator 175 err = proto.Unmarshal(operationMsg.Msg, &msg) 176 require.NoError(err) 177 require.True(operationMsg.OK) 178 require.Equal(sdk.MsgTypeURL(&types.MsgCreateValidator{}), sdk.MsgTypeURL(&msg)) 179 valaddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress) 180 require.NoError(err) 181 require.Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", sdk.AccAddress(valaddr).String()) 182 require.Equal("cosmosvaloper1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7epjs3u", msg.ValidatorAddress) 183 require.Len(futureOperations, 0) 184 } 185 186 // TestSimulateMsgCancelUnbondingDelegation tests the normal scenario of a valid message of type TypeMsgCancelUnbondingDelegation. 187 // Abonormal scenarios, where the message is 188 func (s *SimTestSuite) TestSimulateMsgCancelUnbondingDelegation() { 189 require := s.Require() 190 blockTime := time.Now().UTC() 191 ctx := s.ctx.WithBlockTime(blockTime) 192 193 // setup accounts[1] as validator 194 validator0 := s.getTestingValidator0(ctx) 195 196 // setup delegation 197 delTokens := s.stakingKeeper.TokensFromConsensusPower(ctx, 2) 198 validator0, issuedShares := validator0.AddTokensFromDel(delTokens) 199 delegator := s.accounts[2] 200 delegation := types.NewDelegation(delegator.Address.String(), validator0.GetOperator(), issuedShares) 201 require.NoError(s.stakingKeeper.SetDelegation(ctx, delegation)) 202 val0bz, err := s.stakingKeeper.ValidatorAddressCodec().StringToBytes(validator0.GetOperator()) 203 s.Require().NoError(err) 204 require.NoError(s.distrKeeper.SetDelegatorStartingInfo(ctx, val0bz, delegator.Address, distrtypes.NewDelegatorStartingInfo(2, math.LegacyOneDec(), 200))) 205 206 s.setupValidatorRewards(ctx, val0bz) 207 208 // unbonding delegation 209 udb := types.NewUnbondingDelegation(delegator.Address, val0bz, s.app.LastBlockHeight()+1, blockTime.Add(2*time.Minute), delTokens, 0, address.NewBech32Codec("cosmosvaloper"), address.NewBech32Codec("cosmos")) 210 require.NoError(s.stakingKeeper.SetUnbondingDelegation(ctx, udb)) 211 s.setupValidatorRewards(ctx, val0bz) 212 213 _, err = s.app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: s.app.LastBlockHeight() + 1, Hash: s.app.LastCommitID().Hash, Time: blockTime}) 214 require.NoError(err) 215 216 // execute operation 217 op := simulation.SimulateMsgCancelUnbondingDelegate(s.txConfig, s.accountKeeper, s.bankKeeper, s.stakingKeeper) 218 accounts := []simtypes.Account{delegator} 219 operationMsg, futureOperations, err := op(s.r, s.app.BaseApp, ctx, accounts, "") 220 require.NoError(err) 221 222 var msg types.MsgCancelUnbondingDelegation 223 err = proto.Unmarshal(operationMsg.Msg, &msg) 224 require.NoError(err) 225 require.True(operationMsg.OK) 226 require.Equal(sdk.MsgTypeURL(&types.MsgCancelUnbondingDelegation{}), sdk.MsgTypeURL(&msg)) 227 require.Equal(delegator.Address.String(), msg.DelegatorAddress) 228 require.Equal(validator0.GetOperator(), msg.ValidatorAddress) 229 require.Len(futureOperations, 0) 230 } 231 232 // TestSimulateMsgEditValidator tests the normal scenario of a valid message of type TypeMsgEditValidator. 233 // Abonormal scenarios, where the message is created by an errors are not tested here. 234 func (s *SimTestSuite) TestSimulateMsgEditValidator() { 235 require := s.Require() 236 blockTime := time.Now().UTC() 237 ctx := s.ctx.WithBlockTime(blockTime) 238 239 // setup accounts[0] as validator 240 _ = s.getTestingValidator0(ctx) 241 242 _, err := s.app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: s.app.LastBlockHeight() + 1, Hash: s.app.LastCommitID().Hash, Time: blockTime}) 243 require.NoError(err) 244 245 // execute operation 246 op := simulation.SimulateMsgEditValidator(s.txConfig, s.accountKeeper, s.bankKeeper, s.stakingKeeper) 247 operationMsg, futureOperations, err := op(s.r, s.app.BaseApp, ctx, s.accounts, "") 248 require.NoError(err) 249 250 var msg types.MsgEditValidator 251 err = proto.Unmarshal(operationMsg.Msg, &msg) 252 require.NoError(err) 253 require.True(operationMsg.OK) 254 require.Equal(sdk.MsgTypeURL(&types.MsgEditValidator{}), sdk.MsgTypeURL(&msg)) 255 require.Equal("cosmosvaloper1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7epjs3u", msg.ValidatorAddress) 256 require.Len(futureOperations, 0) 257 } 258 259 // TestSimulateMsgDelegate tests the normal scenario of a valid message of type TypeMsgDelegate. 260 // Abonormal scenarios, where the message is created by an errors are not tested here. 261 func (s *SimTestSuite) TestSimulateMsgDelegate() { 262 require := s.Require() 263 blockTime := time.Now().UTC() 264 ctx := s.ctx.WithBlockTime(blockTime) 265 266 // execute operation 267 op := simulation.SimulateMsgDelegate(s.txConfig, s.accountKeeper, s.bankKeeper, s.stakingKeeper) 268 operationMsg, futureOperations, err := op(s.r, s.app.BaseApp, ctx, s.accounts[1:], "") 269 require.NoError(err) 270 271 var msg types.MsgDelegate 272 err = proto.Unmarshal(operationMsg.Msg, &msg) 273 require.NoError(err) 274 require.True(operationMsg.OK) 275 require.Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.DelegatorAddress) 276 require.Equal("stake", msg.Amount.Denom) 277 require.Equal(sdk.MsgTypeURL(&types.MsgDelegate{}), sdk.MsgTypeURL(&msg)) 278 require.Equal("cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) 279 require.Len(futureOperations, 0) 280 } 281 282 // TestSimulateMsgUndelegate tests the normal scenario of a valid message of type TypeMsgUndelegate. 283 // Abonormal scenarios, where the message is created by an errors are not tested here. 284 func (s *SimTestSuite) TestSimulateMsgUndelegate() { 285 require := s.Require() 286 blockTime := time.Now().UTC() 287 ctx := s.ctx.WithBlockTime(blockTime) 288 289 // setup accounts[1] as validator 290 validator0 := s.getTestingValidator0(ctx) 291 292 // setup delegation 293 delTokens := s.stakingKeeper.TokensFromConsensusPower(ctx, 2) 294 validator0, issuedShares := validator0.AddTokensFromDel(delTokens) 295 delegator := s.accounts[2] 296 delegation := types.NewDelegation(delegator.Address.String(), validator0.GetOperator(), issuedShares) 297 require.NoError(s.stakingKeeper.SetDelegation(ctx, delegation)) 298 val0bz, err := s.stakingKeeper.ValidatorAddressCodec().StringToBytes(validator0.GetOperator()) 299 s.Require().NoError(err) 300 require.NoError(s.distrKeeper.SetDelegatorStartingInfo(ctx, val0bz, delegator.Address, distrtypes.NewDelegatorStartingInfo(2, math.LegacyOneDec(), 200))) 301 302 s.setupValidatorRewards(ctx, val0bz) 303 304 _, err = s.app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: s.app.LastBlockHeight() + 1, Hash: s.app.LastCommitID().Hash, Time: blockTime}) 305 require.NoError(err) 306 307 // execute operation 308 op := simulation.SimulateMsgUndelegate(s.txConfig, s.accountKeeper, s.bankKeeper, s.stakingKeeper) 309 operationMsg, futureOperations, err := op(s.r, s.app.BaseApp, ctx, s.accounts, "") 310 require.NoError(err) 311 312 var msg types.MsgUndelegate 313 err = proto.Unmarshal(operationMsg.Msg, &msg) 314 require.NoError(err) 315 require.True(operationMsg.OK) 316 require.Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.DelegatorAddress) 317 require.Equal("1646627814093010272", msg.Amount.Amount.String()) 318 require.Equal("stake", msg.Amount.Denom) 319 require.Equal(sdk.MsgTypeURL(&types.MsgUndelegate{}), sdk.MsgTypeURL(&msg)) 320 require.Equal("cosmosvaloper1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7epjs3u", msg.ValidatorAddress) 321 require.Len(futureOperations, 0) 322 } 323 324 // TestSimulateMsgBeginRedelegate tests the normal scenario of a valid message of type TypeMsgBeginRedelegate. 325 // Abonormal scenarios, where the message is created by an errors, are not tested here. 326 func (s *SimTestSuite) TestSimulateMsgBeginRedelegate() { 327 require := s.Require() 328 blockTime := time.Now().UTC() 329 ctx := s.ctx.WithBlockTime(blockTime) 330 331 // setup accounts[1] as validator0 and accounts[2] as validator1 332 validator0 := s.getTestingValidator0(ctx) 333 validator1 := s.getTestingValidator1(ctx) 334 335 delTokens := s.stakingKeeper.TokensFromConsensusPower(ctx, 2) 336 validator1, issuedShares := validator1.AddTokensFromDel(delTokens) 337 338 // setup accounts[3] as delegator 339 delegator := s.accounts[3] 340 delegation := types.NewDelegation(delegator.Address.String(), validator0.GetOperator(), issuedShares) 341 require.NoError(s.stakingKeeper.SetDelegation(ctx, delegation)) 342 val0bz, err := s.stakingKeeper.ValidatorAddressCodec().StringToBytes(validator0.GetOperator()) 343 s.Require().NoError(err) 344 val1bz, err := s.stakingKeeper.ValidatorAddressCodec().StringToBytes(validator1.GetOperator()) 345 s.Require().NoError(err) 346 require.NoError(s.distrKeeper.SetDelegatorStartingInfo(ctx, val0bz, delegator.Address, distrtypes.NewDelegatorStartingInfo(2, math.LegacyOneDec(), 200))) 347 348 s.setupValidatorRewards(ctx, val0bz) 349 s.setupValidatorRewards(ctx, val1bz) 350 351 _, err = s.app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: s.app.LastBlockHeight() + 1, Hash: s.app.LastCommitID().Hash, Time: blockTime}) 352 require.NoError(err) 353 354 // execute operation 355 op := simulation.SimulateMsgBeginRedelegate(s.txConfig, s.accountKeeper, s.bankKeeper, s.stakingKeeper) 356 operationMsg, futureOperations, err := op(s.r, s.app.BaseApp, ctx, s.accounts, "") 357 s.T().Logf("operation message: %v", operationMsg) 358 require.NoError(err) 359 360 var msg types.MsgBeginRedelegate 361 err = proto.Unmarshal(operationMsg.Msg, &msg) 362 require.NoError(err) 363 require.True(operationMsg.OK) 364 require.Equal("cosmos1ua0fwyws7vzjrry3pqkklvf8mny93l9s9zg0h4", msg.DelegatorAddress) 365 require.Equal("stake", msg.Amount.Denom) 366 require.Equal(sdk.MsgTypeURL(&types.MsgBeginRedelegate{}), sdk.MsgTypeURL(&msg)) 367 require.Equal("cosmosvaloper1ghekyjucln7y67ntx7cf27m9dpuxxemnsvnaes", msg.ValidatorDstAddress) 368 require.Equal("cosmosvaloper1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7epjs3u", msg.ValidatorSrcAddress) 369 require.Len(futureOperations, 0) 370 } 371 372 func (s *SimTestSuite) getTestingValidator0(ctx sdk.Context) types.Validator { 373 commission0 := types.NewCommission(math.LegacyZeroDec(), math.LegacyOneDec(), math.LegacyOneDec()) 374 return s.getTestingValidator(ctx, commission0, 1) 375 } 376 377 func (s *SimTestSuite) getTestingValidator1(ctx sdk.Context) types.Validator { 378 commission1 := types.NewCommission(math.LegacyZeroDec(), math.LegacyZeroDec(), math.LegacyZeroDec()) 379 return s.getTestingValidator(ctx, commission1, 2) 380 } 381 382 func (s *SimTestSuite) getTestingValidator(ctx sdk.Context, commission types.Commission, n int) types.Validator { 383 account := s.accounts[n] 384 valPubKey := account.PubKey 385 valAddr := sdk.ValAddress(account.PubKey.Address().Bytes()) 386 validator := testutil.NewValidator(s.T(), valAddr, valPubKey) 387 validator, err := validator.SetInitialCommission(commission) 388 s.Require().NoError(err) 389 390 validator.DelegatorShares = math.LegacyNewDec(100) 391 validator.Tokens = s.stakingKeeper.TokensFromConsensusPower(ctx, 100) 392 393 s.Require().NoError(s.stakingKeeper.SetValidator(ctx, validator)) 394 395 return validator 396 } 397 398 func (s *SimTestSuite) setupValidatorRewards(ctx sdk.Context, valAddress sdk.ValAddress) { 399 decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyOneDec())} 400 historicalRewards := distrtypes.NewValidatorHistoricalRewards(decCoins, 2) 401 s.distrKeeper.SetValidatorHistoricalRewards(ctx, valAddress, 2, historicalRewards) 402 // setup current revards 403 currentRewards := distrtypes.NewValidatorCurrentRewards(decCoins, 3) 404 s.distrKeeper.SetValidatorCurrentRewards(ctx, valAddress, currentRewards) 405 } 406 407 func TestSimTestSuite(t *testing.T) { 408 suite.Run(t, new(SimTestSuite)) 409 }