github.com/cosmos/cosmos-sdk@v0.50.10/x/authz/simulation/operations.go (about) 1 package simulation 2 3 import ( 4 "math/rand" 5 "time" 6 7 "github.com/cosmos/cosmos-sdk/baseapp" 8 "github.com/cosmos/cosmos-sdk/client" 9 "github.com/cosmos/cosmos-sdk/codec" 10 cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 11 simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" 12 sdk "github.com/cosmos/cosmos-sdk/types" 13 sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 14 simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 15 "github.com/cosmos/cosmos-sdk/x/authz" 16 "github.com/cosmos/cosmos-sdk/x/authz/keeper" 17 banktype "github.com/cosmos/cosmos-sdk/x/bank/types" 18 "github.com/cosmos/cosmos-sdk/x/simulation" 19 ) 20 21 // authz message types 22 var ( 23 TypeMsgGrant = sdk.MsgTypeURL(&authz.MsgGrant{}) 24 TypeMsgRevoke = sdk.MsgTypeURL(&authz.MsgRevoke{}) 25 TypeMsgExec = sdk.MsgTypeURL(&authz.MsgExec{}) 26 ) 27 28 // Simulation operation weights constants 29 const ( 30 OpWeightMsgGrant = "op_weight_msg_grant" 31 OpWeightRevoke = "op_weight_msg_revoke" 32 OpWeightExec = "op_weight_msg_execute" 33 ) 34 35 // authz operations weights 36 const ( 37 WeightGrant = 100 38 WeightRevoke = 90 39 WeightExec = 90 40 ) 41 42 // WeightedOperations returns all the operations from the module with their respective weights 43 func WeightedOperations( 44 registry cdctypes.InterfaceRegistry, 45 appParams simtypes.AppParams, 46 cdc codec.JSONCodec, 47 txGen client.TxConfig, 48 ak authz.AccountKeeper, 49 bk authz.BankKeeper, 50 k keeper.Keeper, 51 ) simulation.WeightedOperations { 52 var ( 53 weightMsgGrant int 54 weightExec int 55 weightRevoke int 56 ) 57 58 appParams.GetOrGenerate(OpWeightMsgGrant, &weightMsgGrant, nil, func(_ *rand.Rand) { 59 weightMsgGrant = WeightGrant 60 }) 61 62 appParams.GetOrGenerate(OpWeightExec, &weightExec, nil, func(_ *rand.Rand) { 63 weightExec = WeightExec 64 }) 65 66 appParams.GetOrGenerate(OpWeightRevoke, &weightRevoke, nil, func(_ *rand.Rand) { 67 weightRevoke = WeightRevoke 68 }) 69 70 pCdc := codec.NewProtoCodec(registry) 71 72 return simulation.WeightedOperations{ 73 simulation.NewWeightedOperation( 74 weightMsgGrant, 75 SimulateMsgGrant(pCdc, txGen, ak, bk, k), 76 ), 77 simulation.NewWeightedOperation( 78 weightExec, 79 SimulateMsgExec(pCdc, txGen, ak, bk, k, registry), 80 ), 81 simulation.NewWeightedOperation( 82 weightRevoke, 83 SimulateMsgRevoke(pCdc, txGen, ak, bk, k), 84 ), 85 } 86 } 87 88 // SimulateMsgGrant generates a MsgGrant with random values. 89 func SimulateMsgGrant( 90 cdc *codec.ProtoCodec, 91 txCfg client.TxConfig, 92 ak authz.AccountKeeper, 93 bk authz.BankKeeper, 94 _ keeper.Keeper, 95 ) simtypes.Operation { 96 return func( 97 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 98 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 99 granter, _ := simtypes.RandomAcc(r, accs) 100 grantee, _ := simtypes.RandomAcc(r, accs) 101 102 if granter.Address.Equals(grantee.Address) { 103 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "granter and grantee are same"), nil, nil 104 } 105 106 granterAcc := ak.GetAccount(ctx, granter.Address) 107 spendableCoins := bk.SpendableCoins(ctx, granter.Address) 108 fees, err := simtypes.RandomFees(r, ctx, spendableCoins) 109 if err != nil { 110 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err 111 } 112 113 spendLimit := spendableCoins.Sub(fees...) 114 if len(spendLimit) == 0 { 115 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "spend limit is nil"), nil, nil 116 } 117 118 var expiration *time.Time 119 t1 := simtypes.RandTimestamp(r) 120 if !t1.Before(ctx.BlockTime()) { 121 expiration = &t1 122 } 123 randomAuthz := generateRandomAuthorization(r, spendLimit) 124 125 msg, err := authz.NewMsgGrant(granter.Address, grantee.Address, randomAuthz, expiration) 126 if err != nil { 127 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err 128 } 129 tx, err := simtestutil.GenSignedMockTx( 130 r, 131 txCfg, 132 []sdk.Msg{msg}, 133 fees, 134 simtestutil.DefaultGenTxGas, 135 chainID, 136 []uint64{granterAcc.GetAccountNumber()}, 137 []uint64{granterAcc.GetSequence()}, 138 granter.PrivKey, 139 ) 140 if err != nil { 141 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "unable to generate mock tx"), nil, err 142 } 143 144 _, _, err = app.SimTxFinalizeBlock(txCfg.TxEncoder(), tx) 145 if err != nil { 146 return simtypes.NoOpMsg(authz.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err 147 } 148 return simtypes.NewOperationMsg(msg, true, ""), nil, err 149 } 150 } 151 152 func generateRandomAuthorization(r *rand.Rand, spendLimit sdk.Coins) authz.Authorization { 153 authorizations := make([]authz.Authorization, 2) 154 sendAuthz := banktype.NewSendAuthorization(spendLimit, nil) 155 authorizations[0] = sendAuthz 156 authorizations[1] = authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktype.MsgSend{})) 157 158 return authorizations[r.Intn(len(authorizations))] 159 } 160 161 // SimulateMsgRevoke generates a MsgRevoke with random values. 162 func SimulateMsgRevoke( 163 cdc *codec.ProtoCodec, 164 txCfg client.TxConfig, 165 ak authz.AccountKeeper, 166 bk authz.BankKeeper, 167 k keeper.Keeper, 168 ) simtypes.Operation { 169 return func( 170 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 171 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 172 var granterAddr, granteeAddr sdk.AccAddress 173 var grant authz.Grant 174 hasGrant := false 175 176 k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, g authz.Grant) bool { 177 grant = g 178 granterAddr = granter 179 granteeAddr = grantee 180 hasGrant = true 181 return true 182 }) 183 184 if !hasGrant { 185 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "no grants"), nil, nil 186 } 187 188 granterAcc, ok := simtypes.FindAccount(accs, granterAddr) 189 if !ok { 190 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "account not found"), nil, sdkerrors.ErrNotFound.Wrapf("account not found") 191 } 192 193 spendableCoins := bk.SpendableCoins(ctx, granterAddr) 194 fees, err := simtypes.RandomFees(r, ctx, spendableCoins) 195 if err != nil { 196 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "fee error"), nil, err 197 } 198 199 a, err := grant.GetAuthorization() 200 if err != nil { 201 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "authorization error"), nil, err 202 } 203 204 msg := authz.NewMsgRevoke(granterAddr, granteeAddr, a.MsgTypeURL()) 205 account := ak.GetAccount(ctx, granterAddr) 206 tx, err := simtestutil.GenSignedMockTx( 207 r, 208 txCfg, 209 []sdk.Msg{&msg}, 210 fees, 211 simtestutil.DefaultGenTxGas, 212 chainID, 213 []uint64{account.GetAccountNumber()}, 214 []uint64{account.GetSequence()}, 215 granterAcc.PrivKey, 216 ) 217 if err != nil { 218 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, err.Error()), nil, err 219 } 220 221 _, _, err = app.SimTxFinalizeBlock(txCfg.TxEncoder(), tx) 222 if err != nil { 223 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "unable to execute tx: "+err.Error()), nil, err 224 } 225 226 return simtypes.NewOperationMsg(&msg, true, ""), nil, nil 227 } 228 } 229 230 // SimulateMsgExec generates a MsgExec with random values. 231 func SimulateMsgExec( 232 cdc *codec.ProtoCodec, 233 txCfg client.TxConfig, 234 ak authz.AccountKeeper, 235 bk authz.BankKeeper, 236 k keeper.Keeper, 237 unpacker cdctypes.AnyUnpacker, 238 ) simtypes.Operation { 239 return func( 240 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 241 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 242 var granterAddr sdk.AccAddress 243 var granteeAddr sdk.AccAddress 244 var sendAuth *banktype.SendAuthorization 245 var err error 246 k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool { 247 granterAddr = granter 248 granteeAddr = grantee 249 var a authz.Authorization 250 a, err = grant.GetAuthorization() 251 if err != nil { 252 return true 253 } 254 var ok bool 255 sendAuth, ok = a.(*banktype.SendAuthorization) 256 return ok 257 }) 258 259 if err != nil { 260 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err 261 } 262 if sendAuth == nil { 263 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "no grant found"), nil, nil 264 } 265 266 grantee, ok := simtypes.FindAccount(accs, granteeAddr) 267 if !ok { 268 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.ErrNotFound.Wrapf("grantee account not found") 269 } 270 271 if _, ok := simtypes.FindAccount(accs, granterAddr); !ok { 272 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.ErrNotFound.Wrapf("granter account not found") 273 } 274 275 granterspendableCoins := bk.SpendableCoins(ctx, granterAddr) 276 coins := simtypes.RandSubsetCoins(r, granterspendableCoins) 277 // if coins slice is empty, we can not create valid banktype.MsgSend 278 if len(coins) == 0 { 279 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "empty coins slice"), nil, nil 280 } 281 282 // Check send_enabled status of each sent coin denom 283 if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil { 284 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil 285 } 286 287 msg := []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)} 288 289 _, err = sendAuth.Accept(ctx, msg[0]) 290 if err != nil { 291 if sdkerrors.ErrInsufficientFunds.Is(err) { 292 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil 293 } 294 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err 295 296 } 297 298 msgExec := authz.NewMsgExec(granteeAddr, msg) 299 granteeSpendableCoins := bk.SpendableCoins(ctx, granteeAddr) 300 fees, err := simtypes.RandomFees(r, ctx, granteeSpendableCoins) 301 if err != nil { 302 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "fee error"), nil, err 303 } 304 305 granteeAcc := ak.GetAccount(ctx, granteeAddr) 306 tx, err := simtestutil.GenSignedMockTx( 307 r, 308 txCfg, 309 []sdk.Msg{&msgExec}, 310 fees, 311 simtestutil.DefaultGenTxGas, 312 chainID, 313 []uint64{granteeAcc.GetAccountNumber()}, 314 []uint64{granteeAcc.GetSequence()}, 315 grantee.PrivKey, 316 ) 317 if err != nil { 318 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err 319 } 320 321 _, _, err = app.SimTxFinalizeBlock(txCfg.TxEncoder(), tx) 322 if err != nil { 323 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err 324 } 325 326 err = msgExec.UnpackInterfaces(unpacker) 327 if err != nil { 328 return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "unmarshal error"), nil, err 329 } 330 return simtypes.NewOperationMsg(&msgExec, true, "success"), nil, nil 331 } 332 }