github.com/cosmos/cosmos-sdk@v0.50.10/x/distribution/simulation/operations_test.go (about) 1 package simulation_test 2 3 import ( 4 "math/rand" 5 "testing" 6 7 abci "github.com/cometbft/cometbft/abci/types" 8 "github.com/cosmos/gogoproto/proto" 9 "github.com/stretchr/testify/suite" 10 11 "cosmossdk.io/depinject" 12 "cosmossdk.io/log" 13 "cosmossdk.io/math" 14 15 "github.com/cosmos/cosmos-sdk/client" 16 "github.com/cosmos/cosmos-sdk/codec" 17 "github.com/cosmos/cosmos-sdk/codec/address" 18 "github.com/cosmos/cosmos-sdk/runtime" 19 simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" 20 sdk "github.com/cosmos/cosmos-sdk/types" 21 simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 22 authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" 23 bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 24 banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" 25 "github.com/cosmos/cosmos-sdk/x/distribution/keeper" 26 "github.com/cosmos/cosmos-sdk/x/distribution/simulation" 27 distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil" 28 "github.com/cosmos/cosmos-sdk/x/distribution/types" 29 stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" 30 stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" 31 ) 32 33 // TestWeightedOperations tests the weights of the operations. 34 func (suite *SimTestSuite) TestWeightedOperations() { 35 appParams := make(simtypes.AppParams) 36 37 weightedOps := simulation.WeightedOperations(appParams, suite.cdc, suite.txConfig, suite.accountKeeper, 38 suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper) 39 40 // setup 3 accounts 41 s := rand.NewSource(1) 42 r := rand.New(s) 43 accs := suite.getTestingAccounts(r, 3) 44 45 expected := []struct { 46 weight int 47 opMsgRoute string 48 opMsgName string 49 }{ 50 {simulation.DefaultWeightMsgSetWithdrawAddress, types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{})}, 51 {simulation.DefaultWeightMsgWithdrawDelegationReward, types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{})}, 52 {simulation.DefaultWeightMsgWithdrawValidatorCommission, types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawValidatorCommission{})}, 53 {simulation.DefaultWeightMsgFundCommunityPool, types.ModuleName, sdk.MsgTypeURL(&types.MsgFundCommunityPool{})}, 54 } 55 56 for i, w := range weightedOps { 57 operationMsg, _, err := w.Op()(r, suite.app.BaseApp, suite.ctx, accs, "") 58 suite.Require().NoError(err) 59 60 // the following checks are very much dependent from the ordering of the output given 61 // by WeightedOperations. if the ordering in WeightedOperations changes some tests 62 // will fail 63 suite.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same") 64 suite.Require().Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same") 65 suite.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") 66 } 67 } 68 69 // TestSimulateMsgSetWithdrawAddress tests the normal scenario of a valid message of type TypeMsgSetWithdrawAddress. 70 // Abonormal scenarios, where the message is created by an errors, are not tested here. 71 func (suite *SimTestSuite) TestSimulateMsgSetWithdrawAddress() { 72 // setup 3 accounts 73 s := rand.NewSource(1) 74 r := rand.New(s) 75 accounts := suite.getTestingAccounts(r, 3) 76 77 suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{ 78 Height: suite.app.LastBlockHeight() + 1, 79 Hash: suite.app.LastCommitID().Hash, 80 }) 81 82 // execute operation 83 op := simulation.SimulateMsgSetWithdrawAddress(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper) 84 operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") 85 suite.Require().NoError(err) 86 87 var msg types.MsgSetWithdrawAddress 88 err = proto.Unmarshal(operationMsg.Msg, &msg) 89 suite.Require().NoError(err) 90 suite.Require().True(operationMsg.OK) 91 suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.DelegatorAddress) 92 suite.Require().Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.WithdrawAddress) 93 suite.Require().Equal(sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), sdk.MsgTypeURL(&msg)) 94 suite.Require().Len(futureOperations, 0) 95 } 96 97 // TestSimulateMsgWithdrawDelegatorReward tests the normal scenario of a valid message 98 // of type TypeMsgWithdrawDelegatorReward. 99 // Abonormal scenarios, where the message is created by an errors, are not tested here. 100 func (suite *SimTestSuite) TestSimulateMsgWithdrawDelegatorReward() { 101 // setup 3 accounts 102 s := rand.NewSource(4) 103 r := rand.New(s) 104 accounts := suite.getTestingAccounts(r, 3) 105 106 // setup accounts[0] as validator 107 validator0 := suite.getTestingValidator0(accounts) 108 109 // setup delegation 110 delTokens := sdk.TokensFromConsensusPower(2, sdk.DefaultPowerReduction) 111 validator0, issuedShares := validator0.AddTokensFromDel(delTokens) 112 delegator := accounts[1] 113 114 delegation := stakingtypes.NewDelegation(delegator.Address.String(), validator0.GetOperator(), issuedShares) 115 suite.Require().NoError(suite.stakingKeeper.SetDelegation(suite.ctx, delegation)) 116 valBz, err := address.NewBech32Codec("cosmosvaloper").StringToBytes(validator0.GetOperator()) 117 suite.Require().NoError(err) 118 suite.distrKeeper.SetDelegatorStartingInfo(suite.ctx, valBz, delegator.Address, types.NewDelegatorStartingInfo(2, math.LegacyOneDec(), 200)) 119 120 suite.setupValidatorRewards(valBz) 121 122 _, err = suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{ 123 Height: suite.app.LastBlockHeight() + 1, 124 Hash: suite.app.LastCommitID().Hash, 125 }) 126 suite.Require().NoError(err) 127 128 // execute operation 129 op := simulation.SimulateMsgWithdrawDelegatorReward(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper) 130 operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") 131 suite.Require().NoError(err) 132 133 var msg types.MsgWithdrawDelegatorReward 134 err = proto.Unmarshal(operationMsg.Msg, &msg) 135 suite.Require().NoError(err) 136 suite.Require().True(operationMsg.OK) 137 suite.Require().Equal("cosmosvaloper1l4s054098kk9hmr5753c6k3m2kw65h686d3mhr", msg.ValidatorAddress) 138 suite.Require().Equal("cosmos1d6u7zhjwmsucs678d7qn95uqajd4ucl9jcjt26", msg.DelegatorAddress) 139 suite.Require().Equal(sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), sdk.MsgTypeURL(&msg)) 140 suite.Require().Len(futureOperations, 0) 141 } 142 143 // TestSimulateMsgWithdrawValidatorCommission tests the normal scenario of a valid message 144 // of type TypeMsgWithdrawValidatorCommission. 145 // Abonormal scenarios, where the message is created by an errors, are not tested here. 146 func (suite *SimTestSuite) TestSimulateMsgWithdrawValidatorCommission() { 147 suite.testSimulateMsgWithdrawValidatorCommission("atoken") 148 suite.testSimulateMsgWithdrawValidatorCommission("tokenxxx") 149 } 150 151 // all the checks in this function should not fail if we change the tokenName 152 func (suite *SimTestSuite) testSimulateMsgWithdrawValidatorCommission(tokenName string) { 153 // setup 3 accounts 154 s := rand.NewSource(1) 155 r := rand.New(s) 156 accounts := suite.getTestingAccounts(r, 3) 157 158 // setup accounts[0] as validator 159 validator0 := suite.getTestingValidator0(accounts) 160 161 // set module account coins 162 distrAcc := suite.distrKeeper.GetDistributionAccount(suite.ctx) 163 suite.Require().NoError(banktestutil.FundModuleAccount(suite.ctx, suite.bankKeeper, distrAcc.GetName(), sdk.NewCoins( 164 sdk.NewCoin(tokenName, math.NewInt(10)), 165 sdk.NewCoin("stake", math.NewInt(5)), 166 ))) 167 suite.accountKeeper.SetModuleAccount(suite.ctx, distrAcc) 168 169 // set outstanding rewards 170 valCommission := sdk.NewDecCoins( 171 sdk.NewDecCoinFromDec(tokenName, math.LegacyNewDec(5).Quo(math.LegacyNewDec(2))), 172 sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(1).Quo(math.LegacyNewDec(1))), 173 ) 174 valCodec := address.NewBech32Codec("cosmosvaloper") 175 176 val0, err := valCodec.StringToBytes(validator0.GetOperator()) 177 suite.Require().NoError(err) 178 179 genVal0, err := valCodec.StringToBytes(suite.genesisVals[0].GetOperator()) 180 suite.Require().NoError(err) 181 182 suite.distrKeeper.SetValidatorOutstandingRewards(suite.ctx, val0, types.ValidatorOutstandingRewards{Rewards: valCommission}) 183 suite.distrKeeper.SetValidatorOutstandingRewards(suite.ctx, genVal0, types.ValidatorOutstandingRewards{Rewards: valCommission}) 184 185 // setup validator accumulated commission 186 suite.distrKeeper.SetValidatorAccumulatedCommission(suite.ctx, val0, types.ValidatorAccumulatedCommission{Commission: valCommission}) 187 suite.distrKeeper.SetValidatorAccumulatedCommission(suite.ctx, genVal0, types.ValidatorAccumulatedCommission{Commission: valCommission}) 188 189 _, err = suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{ 190 Height: suite.app.LastBlockHeight() + 1, 191 Hash: suite.app.LastCommitID().Hash, 192 }) 193 suite.Require().NoError(err) 194 195 // execute operation 196 op := simulation.SimulateMsgWithdrawValidatorCommission(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper) 197 operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") 198 if !operationMsg.OK { 199 suite.Require().Equal("could not find account", operationMsg.Comment) 200 } else { 201 suite.Require().NoError(err) 202 203 var msg types.MsgWithdrawValidatorCommission 204 err = proto.Unmarshal(operationMsg.Msg, &msg) 205 suite.Require().NoError(err) 206 suite.Require().True(operationMsg.OK) 207 suite.Require().Equal("cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress) 208 suite.Require().Equal(sdk.MsgTypeURL(&types.MsgWithdrawValidatorCommission{}), sdk.MsgTypeURL(&msg)) 209 suite.Require().Len(futureOperations, 0) 210 } 211 } 212 213 // TestSimulateMsgFundCommunityPool tests the normal scenario of a valid message of type TypeMsgFundCommunityPool. 214 // Abonormal scenarios, where the message is created by an errors, are not tested here. 215 func (suite *SimTestSuite) TestSimulateMsgFundCommunityPool() { 216 // setup 3 accounts 217 s := rand.NewSource(1) 218 r := rand.New(s) 219 accounts := suite.getTestingAccounts(r, 3) 220 221 suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{ 222 Height: suite.app.LastBlockHeight() + 1, 223 Hash: suite.app.LastCommitID().Hash, 224 }) 225 226 // execute operation 227 op := simulation.SimulateMsgFundCommunityPool(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper) 228 operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "") 229 suite.Require().NoError(err) 230 231 var msg types.MsgFundCommunityPool 232 err = proto.Unmarshal(operationMsg.Msg, &msg) 233 suite.Require().NoError(err) 234 suite.Require().True(operationMsg.OK) 235 suite.Require().Equal("4896096stake", msg.Amount.String()) 236 suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Depositor) 237 suite.Require().Equal(sdk.MsgTypeURL(&types.MsgFundCommunityPool{}), sdk.MsgTypeURL(&msg)) 238 suite.Require().Len(futureOperations, 0) 239 } 240 241 type SimTestSuite struct { 242 suite.Suite 243 244 ctx sdk.Context 245 app *runtime.App 246 genesisVals []stakingtypes.Validator 247 248 txConfig client.TxConfig 249 cdc codec.Codec 250 stakingKeeper *stakingkeeper.Keeper 251 accountKeeper authkeeper.AccountKeeper 252 bankKeeper bankkeeper.Keeper 253 distrKeeper keeper.Keeper 254 } 255 256 func (suite *SimTestSuite) SetupTest() { 257 var ( 258 appBuilder *runtime.AppBuilder 259 err error 260 ) 261 suite.app, err = simtestutil.Setup( 262 depinject.Configs( 263 distrtestutil.AppConfig, 264 depinject.Supply(log.NewNopLogger()), 265 ), 266 &suite.accountKeeper, 267 &suite.bankKeeper, 268 &suite.cdc, 269 &appBuilder, 270 &suite.stakingKeeper, 271 &suite.distrKeeper, 272 &suite.txConfig, 273 ) 274 275 suite.NoError(err) 276 277 suite.ctx = suite.app.BaseApp.NewContext(false) 278 279 genesisVals, err := suite.stakingKeeper.GetAllValidators(suite.ctx) 280 suite.Require().NoError(err) 281 suite.Require().Len(genesisVals, 1) 282 suite.genesisVals = genesisVals 283 } 284 285 func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account { 286 accounts := simtypes.RandomAccounts(r, n) 287 288 initAmt := suite.stakingKeeper.TokensFromConsensusPower(suite.ctx, 200) 289 initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) 290 291 // add coins to the accounts 292 for _, account := range accounts { 293 acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, account.Address) 294 suite.accountKeeper.SetAccount(suite.ctx, acc) 295 suite.Require().NoError(banktestutil.FundAccount(suite.ctx, suite.bankKeeper, account.Address, initCoins)) 296 } 297 298 return accounts 299 } 300 301 func (suite *SimTestSuite) getTestingValidator0(accounts []simtypes.Account) stakingtypes.Validator { 302 commission0 := stakingtypes.NewCommission(math.LegacyZeroDec(), math.LegacyOneDec(), math.LegacyOneDec()) 303 return suite.getTestingValidator(accounts, commission0, 0) 304 } 305 306 func (suite *SimTestSuite) getTestingValidator(accounts []simtypes.Account, commission stakingtypes.Commission, n int) stakingtypes.Validator { 307 require := suite.Require() 308 account := accounts[n] 309 valPubKey := account.PubKey 310 valAddr := sdk.ValAddress(account.PubKey.Address().Bytes()) 311 validator, err := stakingtypes.NewValidator(valAddr.String(), valPubKey, stakingtypes. 312 Description{}) 313 require.NoError(err) 314 validator, err = validator.SetInitialCommission(commission) 315 require.NoError(err) 316 validator.DelegatorShares = math.LegacyNewDec(100) 317 validator.Tokens = math.NewInt(1000000) 318 319 suite.stakingKeeper.SetValidator(suite.ctx, validator) 320 321 return validator 322 } 323 324 func (suite *SimTestSuite) setupValidatorRewards(valAddress sdk.ValAddress) { 325 decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyOneDec())} 326 historicalRewards := types.NewValidatorHistoricalRewards(decCoins, 2) 327 suite.distrKeeper.SetValidatorHistoricalRewards(suite.ctx, valAddress, 2, historicalRewards) 328 // setup current revards 329 currentRewards := types.NewValidatorCurrentRewards(decCoins, 3) 330 suite.distrKeeper.SetValidatorCurrentRewards(suite.ctx, valAddress, currentRewards) 331 } 332 333 func TestSimTestSuite(t *testing.T) { 334 suite.Run(t, new(SimTestSuite)) 335 }