github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/bank/simulation/operations.go (about) 1 package simulation 2 3 import ( 4 "math/rand" 5 6 "github.com/fibonacci-chain/fbc/libs/tendermint/crypto" 7 8 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/baseapp" 9 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 10 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/simapp/helpers" 11 simappparams "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/simapp/params" 12 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 13 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank/internal/keeper" 14 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/bank/internal/types" 15 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/simulation" 16 ) 17 18 // Simulation operation weights constants 19 const ( 20 OpWeightMsgSend = "op_weight_msg_send" 21 OpWeightMsgMultiSend = "op_weight_msg_multisend" 22 ) 23 24 // WeightedOperations returns all the operations from the module with their respective weights 25 func WeightedOperations(appParams simulation.AppParams, cdc *codec.Codec, ak types.AccountKeeper, 26 bk keeper.Keeper) simulation.WeightedOperations { 27 28 var weightMsgSend, weightMsgMultiSend int 29 appParams.GetOrGenerate(cdc, OpWeightMsgSend, &weightMsgSend, nil, 30 func(_ *rand.Rand) { 31 weightMsgSend = simappparams.DefaultWeightMsgSend 32 }, 33 ) 34 35 appParams.GetOrGenerate(cdc, OpWeightMsgMultiSend, &weightMsgMultiSend, nil, 36 func(_ *rand.Rand) { 37 weightMsgMultiSend = simappparams.DefaultWeightMsgMultiSend 38 }, 39 ) 40 41 return simulation.WeightedOperations{ 42 simulation.NewWeightedOperation( 43 weightMsgSend, 44 SimulateMsgSend(ak, bk), 45 ), 46 simulation.NewWeightedOperation( 47 weightMsgMultiSend, 48 SimulateMsgMultiSend(ak, bk), 49 ), 50 } 51 } 52 53 // SimulateMsgSend tests and runs a single msg send where both 54 // accounts already exist. 55 // nolint: funlen 56 func SimulateMsgSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.Operation { 57 return func( 58 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, 59 accs []simulation.Account, chainID string, 60 ) (simulation.OperationMsg, []simulation.FutureOperation, error) { 61 62 if !bk.GetSendEnabled(ctx) { 63 return simulation.NoOpMsg(types.ModuleName), nil, nil 64 } 65 66 simAccount, toSimAcc, coins, skip, err := randomSendFields(r, ctx, accs, ak) 67 if err != nil { 68 return simulation.NoOpMsg(types.ModuleName), nil, err 69 } 70 71 if skip { 72 return simulation.NoOpMsg(types.ModuleName), nil, nil 73 } 74 75 msg := types.NewMsgSend(simAccount.Address, toSimAcc.Address, coins) 76 77 err = sendMsgSend(r, app, ak, msg, ctx, chainID, []crypto.PrivKey{simAccount.PrivKey}) 78 if err != nil { 79 return simulation.NoOpMsg(types.ModuleName), nil, err 80 } 81 82 return simulation.NewOperationMsg(msg, true, ""), nil, nil 83 } 84 } 85 86 // sendMsgSend sends a transaction with a MsgSend from a provided random account. 87 func sendMsgSend( 88 r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, // nolint:interfacer 89 msg types.MsgSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey, 90 ) error { 91 92 account := ak.GetAccount(ctx, msg.FromAddress) 93 coins := account.SpendableCoins(ctx.BlockTime()) 94 95 var ( 96 fees sdk.Coins 97 err error 98 ) 99 coins, hasNeg := coins.SafeSub(msg.Amount) 100 if !hasNeg { 101 fees, err = simulation.RandomFees(r, ctx, coins) 102 if err != nil { 103 return err 104 } 105 } 106 107 tx := helpers.GenTx( 108 []sdk.Msg{msg}, 109 fees, 110 helpers.DefaultGenTxGas, 111 chainID, 112 []uint64{account.GetAccountNumber()}, 113 []uint64{account.GetSequence()}, 114 privkeys..., 115 ) 116 117 _, _, err = app.Deliver(tx) 118 if err != nil { 119 return err 120 } 121 122 return nil 123 } 124 125 // SimulateMsgMultiSend tests and runs a single msg multisend, with randomized, capped number of inputs/outputs. 126 // all accounts in msg fields exist in state 127 // nolint: funlen 128 func SimulateMsgMultiSend(ak types.AccountKeeper, bk keeper.Keeper) simulation.Operation { 129 return func( 130 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, 131 accs []simulation.Account, chainID string, 132 ) (simulation.OperationMsg, []simulation.FutureOperation, error) { 133 134 if !bk.GetSendEnabled(ctx) { 135 return simulation.NoOpMsg(types.ModuleName), nil, nil 136 } 137 138 // random number of inputs/outputs between [1, 3] 139 inputs := make([]types.Input, r.Intn(3)+1) 140 outputs := make([]types.Output, r.Intn(3)+1) 141 142 // collect signer privKeys 143 privs := make([]crypto.PrivKey, len(inputs)) 144 145 // use map to check if address already exists as input 146 usedAddrs := make(map[string]bool) 147 148 var totalSentCoins sdk.Coins 149 for i := range inputs { 150 // generate random input fields, ignore to address 151 simAccount, _, coins, skip, err := randomSendFields(r, ctx, accs, ak) 152 153 // make sure account is fresh and not used in previous input 154 for usedAddrs[simAccount.Address.String()] { 155 simAccount, _, coins, skip, err = randomSendFields(r, ctx, accs, ak) 156 } 157 158 if err != nil { 159 return simulation.NoOpMsg(types.ModuleName), nil, err 160 } 161 if skip { 162 return simulation.NoOpMsg(types.ModuleName), nil, nil 163 } 164 165 // set input address in used address map 166 usedAddrs[simAccount.Address.String()] = true 167 168 // set signer privkey 169 privs[i] = simAccount.PrivKey 170 171 // set next input and accumulate total sent coins 172 inputs[i] = types.NewInput(simAccount.Address, coins) 173 totalSentCoins = totalSentCoins.Add(coins...) 174 } 175 176 for o := range outputs { 177 outAddr, _ := simulation.RandomAcc(r, accs) 178 179 var outCoins sdk.Coins 180 // split total sent coins into random subsets for output 181 if o == len(outputs)-1 { 182 outCoins = totalSentCoins 183 } else { 184 // take random subset of remaining coins for output 185 // and update remaining coins 186 outCoins = simulation.RandSubsetCoins(r, totalSentCoins) 187 totalSentCoins = totalSentCoins.Sub(outCoins) 188 } 189 190 outputs[o] = types.NewOutput(outAddr.Address, outCoins) 191 } 192 193 // remove any output that has no coins 194 i := 0 195 for i < len(outputs) { 196 if outputs[i].Coins.Empty() { 197 outputs[i] = outputs[len(outputs)-1] 198 outputs = outputs[:len(outputs)-1] 199 } else { 200 // continue onto next coin 201 i++ 202 } 203 } 204 205 msg := types.MsgMultiSend{ 206 Inputs: inputs, 207 Outputs: outputs, 208 } 209 210 err := sendMsgMultiSend(r, app, ak, msg, ctx, chainID, privs) 211 if err != nil { 212 return simulation.NoOpMsg(types.ModuleName), nil, err 213 } 214 215 return simulation.NewOperationMsg(msg, true, ""), nil, nil 216 } 217 } 218 219 // sendMsgMultiSend sends a transaction with a MsgMultiSend from a provided random 220 // account. 221 func sendMsgMultiSend( 222 r *rand.Rand, app *baseapp.BaseApp, ak types.AccountKeeper, // nolint:interfacer 223 msg types.MsgMultiSend, ctx sdk.Context, chainID string, privkeys []crypto.PrivKey, 224 ) error { 225 226 accountNumbers := make([]uint64, len(msg.Inputs)) 227 sequenceNumbers := make([]uint64, len(msg.Inputs)) 228 229 for i := 0; i < len(msg.Inputs); i++ { 230 acc := ak.GetAccount(ctx, msg.Inputs[i].Address) 231 accountNumbers[i] = acc.GetAccountNumber() 232 sequenceNumbers[i] = acc.GetSequence() 233 } 234 235 // feePayer is the first signer, i.e. first input address 236 feePayer := ak.GetAccount(ctx, msg.Inputs[0].Address) 237 coins := feePayer.SpendableCoins(ctx.BlockTime()) 238 239 var ( 240 fees sdk.Coins 241 err error 242 ) 243 coins, hasNeg := coins.SafeSub(msg.Inputs[0].Coins) 244 if !hasNeg { 245 fees, err = simulation.RandomFees(r, ctx, coins) 246 if err != nil { 247 return err 248 } 249 } 250 251 tx := helpers.GenTx( 252 []sdk.Msg{msg}, 253 fees, 254 helpers.DefaultGenTxGas, 255 chainID, 256 accountNumbers, 257 sequenceNumbers, 258 privkeys..., 259 ) 260 261 _, _, err = app.Deliver(tx) 262 if err != nil { 263 return err 264 } 265 266 return nil 267 } 268 269 // randomSendFields returns the sender and recipient simulation accounts as well 270 // as the transferred amount. 271 func randomSendFields( 272 r *rand.Rand, ctx sdk.Context, accs []simulation.Account, ak types.AccountKeeper, // nolint:interfacer 273 ) (simulation.Account, simulation.Account, sdk.Coins, bool, error) { 274 275 simAccount, _ := simulation.RandomAcc(r, accs) 276 toSimAcc, _ := simulation.RandomAcc(r, accs) 277 278 // disallow sending money to yourself 279 for simAccount.PubKey.Equals(toSimAcc.PubKey) { 280 toSimAcc, _ = simulation.RandomAcc(r, accs) 281 } 282 283 acc := ak.GetAccount(ctx, simAccount.Address) 284 if acc == nil { 285 return simAccount, toSimAcc, nil, true, nil // skip error 286 } 287 288 coins := acc.SpendableCoins(ctx.BlockHeader().Time) 289 290 sendCoins := simulation.RandSubsetCoins(r, coins) 291 if sendCoins.Empty() { 292 return simAccount, toSimAcc, nil, true, nil // skip error 293 } 294 295 return simAccount, toSimAcc, sendCoins, false, nil 296 }