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