github.com/cosmos/cosmos-sdk@v0.50.10/x/distribution/simulation/operations.go (about) 1 package simulation 2 3 import ( 4 "errors" 5 "fmt" 6 "math/rand" 7 8 "cosmossdk.io/collections" 9 10 "github.com/cosmos/cosmos-sdk/baseapp" 11 "github.com/cosmos/cosmos-sdk/client" 12 "github.com/cosmos/cosmos-sdk/codec" 13 "github.com/cosmos/cosmos-sdk/testutil" 14 sdk "github.com/cosmos/cosmos-sdk/types" 15 simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 16 "github.com/cosmos/cosmos-sdk/x/distribution/keeper" 17 "github.com/cosmos/cosmos-sdk/x/distribution/types" 18 "github.com/cosmos/cosmos-sdk/x/simulation" 19 ) 20 21 // Simulation operation weights constants 22 const ( 23 OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address" 24 OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward" 25 OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission" 26 OpWeightMsgFundCommunityPool = "op_weight_msg_fund_community_pool" 27 28 DefaultWeightMsgSetWithdrawAddress int = 50 29 DefaultWeightMsgWithdrawDelegationReward int = 50 30 DefaultWeightMsgWithdrawValidatorCommission int = 50 31 DefaultWeightMsgFundCommunityPool int = 50 32 ) 33 34 // WeightedOperations returns all the operations from the module with their respective weights 35 func WeightedOperations( 36 appParams simtypes.AppParams, 37 cdc codec.JSONCodec, 38 txConfig client.TxConfig, 39 ak types.AccountKeeper, 40 bk types.BankKeeper, 41 k keeper.Keeper, 42 sk types.StakingKeeper, 43 ) simulation.WeightedOperations { 44 var weightMsgSetWithdrawAddress int 45 appParams.GetOrGenerate(OpWeightMsgSetWithdrawAddress, &weightMsgSetWithdrawAddress, nil, func(_ *rand.Rand) { 46 weightMsgSetWithdrawAddress = DefaultWeightMsgSetWithdrawAddress 47 }) 48 49 var weightMsgWithdrawDelegationReward int 50 appParams.GetOrGenerate(OpWeightMsgWithdrawDelegationReward, &weightMsgWithdrawDelegationReward, nil, func(_ *rand.Rand) { 51 weightMsgWithdrawDelegationReward = DefaultWeightMsgWithdrawDelegationReward 52 }) 53 54 var weightMsgWithdrawValidatorCommission int 55 appParams.GetOrGenerate(OpWeightMsgWithdrawValidatorCommission, &weightMsgWithdrawValidatorCommission, nil, func(_ *rand.Rand) { 56 weightMsgWithdrawValidatorCommission = DefaultWeightMsgWithdrawValidatorCommission 57 }) 58 59 var weightMsgFundCommunityPool int 60 appParams.GetOrGenerate(OpWeightMsgFundCommunityPool, &weightMsgFundCommunityPool, nil, func(_ *rand.Rand) { 61 weightMsgFundCommunityPool = DefaultWeightMsgFundCommunityPool 62 }) 63 64 return simulation.WeightedOperations{ 65 simulation.NewWeightedOperation( 66 weightMsgSetWithdrawAddress, 67 SimulateMsgSetWithdrawAddress(txConfig, ak, bk, k), 68 ), 69 simulation.NewWeightedOperation( 70 weightMsgWithdrawDelegationReward, 71 SimulateMsgWithdrawDelegatorReward(txConfig, ak, bk, k, sk), 72 ), 73 simulation.NewWeightedOperation( 74 weightMsgWithdrawValidatorCommission, 75 SimulateMsgWithdrawValidatorCommission(txConfig, ak, bk, k, sk), 76 ), 77 simulation.NewWeightedOperation( 78 weightMsgFundCommunityPool, 79 SimulateMsgFundCommunityPool(txConfig, ak, bk, k, sk), 80 ), 81 } 82 } 83 84 // SimulateMsgSetWithdrawAddress generates a MsgSetWithdrawAddress with random values. 85 func SimulateMsgSetWithdrawAddress(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { 86 return func( 87 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 88 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 89 isWithdrawAddrEnabled, err := k.GetWithdrawAddrEnabled(ctx) 90 if err != nil { 91 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), "error getting params"), nil, err 92 } 93 94 if !isWithdrawAddrEnabled { 95 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), "withdrawal is not enabled"), nil, nil 96 } 97 98 simAccount, _ := simtypes.RandomAcc(r, accs) 99 simToAccount, _ := simtypes.RandomAcc(r, accs) 100 101 account := ak.GetAccount(ctx, simAccount.Address) 102 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 103 104 msg := types.NewMsgSetWithdrawAddress(simAccount.Address, simToAccount.Address) 105 106 txCtx := simulation.OperationInput{ 107 R: r, 108 App: app, 109 TxGen: txConfig, 110 Cdc: nil, 111 Msg: msg, 112 Context: ctx, 113 SimAccount: simAccount, 114 AccountKeeper: ak, 115 Bankkeeper: bk, 116 ModuleName: types.ModuleName, 117 CoinsSpentInMsg: spendable, 118 } 119 120 return simulation.GenAndDeliverTxWithRandFees(txCtx) 121 } 122 } 123 124 // SimulateMsgWithdrawDelegatorReward generates a MsgWithdrawDelegatorReward with random values. 125 func SimulateMsgWithdrawDelegatorReward(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation { 126 return func( 127 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 128 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 129 simAccount, _ := simtypes.RandomAcc(r, accs) 130 delegations, err := sk.GetAllDelegatorDelegations(ctx, simAccount.Address) 131 if err != nil { 132 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "error getting delegations"), nil, err 133 } 134 if len(delegations) == 0 { 135 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "number of delegators equal 0"), nil, nil 136 } 137 138 delegation := delegations[r.Intn(len(delegations))] 139 140 delAddr, err := sk.ValidatorAddressCodec().StringToBytes(delegation.GetValidatorAddr()) 141 if err != nil { 142 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "error converting validator address"), nil, err 143 } 144 validator, err := sk.Validator(ctx, delAddr) 145 if err != nil { 146 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "error getting validator"), nil, err 147 } 148 if validator == nil { 149 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "validator is nil"), nil, fmt.Errorf("validator %s not found", delegation.GetValidatorAddr()) 150 } 151 152 account := ak.GetAccount(ctx, simAccount.Address) 153 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 154 155 msg := types.NewMsgWithdrawDelegatorReward(simAccount.Address.String(), validator.GetOperator()) 156 157 txCtx := simulation.OperationInput{ 158 R: r, 159 App: app, 160 TxGen: txConfig, 161 Cdc: nil, 162 Msg: msg, 163 Context: ctx, 164 SimAccount: simAccount, 165 AccountKeeper: ak, 166 Bankkeeper: bk, 167 ModuleName: types.ModuleName, 168 CoinsSpentInMsg: spendable, 169 } 170 171 return simulation.GenAndDeliverTxWithRandFees(txCtx) 172 } 173 } 174 175 // SimulateMsgWithdrawValidatorCommission generates a MsgWithdrawValidatorCommission with random values. 176 func SimulateMsgWithdrawValidatorCommission(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation { 177 return func( 178 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 179 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 180 msgType := sdk.MsgTypeURL(&types.MsgWithdrawValidatorCommission{}) 181 182 allVals, err := sk.GetAllValidators(ctx) 183 if err != nil { 184 return simtypes.NoOpMsg(types.ModuleName, msgType, "error getting all validators"), nil, err 185 } 186 187 validator, ok := testutil.RandSliceElem(r, allVals) 188 if !ok { 189 return simtypes.NoOpMsg(types.ModuleName, msgType, "random validator is not ok"), nil, nil 190 } 191 192 valBz, err := sk.ValidatorAddressCodec().StringToBytes(validator.GetOperator()) 193 if err != nil { 194 return simtypes.NoOpMsg(types.ModuleName, msgType, "error converting validator address"), nil, err 195 } 196 197 commission, err := k.GetValidatorAccumulatedCommission(ctx, valBz) 198 199 if err != nil && !errors.Is(err, collections.ErrNotFound) { 200 return simtypes.NoOpMsg(types.ModuleName, msgType, "error getting validator commission"), nil, err 201 } 202 203 if commission.Commission.IsZero() { 204 return simtypes.NoOpMsg(types.ModuleName, msgType, "validator commission is zero"), nil, nil 205 } 206 207 simAccount, found := simtypes.FindAccount(accs, sdk.AccAddress(valBz)) 208 if !found { 209 return simtypes.NoOpMsg(types.ModuleName, msgType, "could not find account"), nil, fmt.Errorf("validator %s not found", validator.GetOperator()) 210 } 211 212 account := ak.GetAccount(ctx, simAccount.Address) 213 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 214 215 msg := types.NewMsgWithdrawValidatorCommission(validator.GetOperator()) 216 217 txCtx := simulation.OperationInput{ 218 R: r, 219 App: app, 220 TxGen: txConfig, 221 Cdc: nil, 222 Msg: msg, 223 Context: ctx, 224 SimAccount: simAccount, 225 AccountKeeper: ak, 226 Bankkeeper: bk, 227 ModuleName: types.ModuleName, 228 CoinsSpentInMsg: spendable, 229 } 230 231 return simulation.GenAndDeliverTxWithRandFees(txCtx) 232 } 233 } 234 235 // SimulateMsgFundCommunityPool simulates MsgFundCommunityPool execution where 236 // a random account sends a random amount of its funds to the community pool. 237 func SimulateMsgFundCommunityPool(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation { 238 return func( 239 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 240 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 241 funder, _ := simtypes.RandomAcc(r, accs) 242 243 account := ak.GetAccount(ctx, funder.Address) 244 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 245 246 fundAmount := simtypes.RandSubsetCoins(r, spendable) 247 if fundAmount.Empty() { 248 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgFundCommunityPool{}), "fund amount is empty"), nil, nil 249 } 250 251 var ( 252 fees sdk.Coins 253 err error 254 ) 255 256 coins, hasNeg := spendable.SafeSub(fundAmount...) 257 if !hasNeg { 258 fees, err = simtypes.RandomFees(r, ctx, coins) 259 if err != nil { 260 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgFundCommunityPool{}), "unable to generate fees"), nil, err 261 } 262 } 263 264 msg := types.NewMsgFundCommunityPool(fundAmount, funder.Address.String()) 265 266 txCtx := simulation.OperationInput{ 267 R: r, 268 App: app, 269 TxGen: txConfig, 270 Cdc: nil, 271 Msg: msg, 272 Context: ctx, 273 SimAccount: funder, 274 AccountKeeper: ak, 275 ModuleName: types.ModuleName, 276 } 277 278 return simulation.GenAndDeliverTx(txCtx, fees) 279 } 280 }