github.com/cosmos/cosmos-sdk@v0.50.10/x/gov/simulation/operations.go (about) 1 package simulation 2 3 import ( 4 "math" 5 "math/rand" 6 "time" 7 8 sdkmath "cosmossdk.io/math" 9 10 "github.com/cosmos/cosmos-sdk/baseapp" 11 "github.com/cosmos/cosmos-sdk/client" 12 simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" 13 sdk "github.com/cosmos/cosmos-sdk/types" 14 simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 15 "github.com/cosmos/cosmos-sdk/x/gov/keeper" 16 "github.com/cosmos/cosmos-sdk/x/gov/types" 17 v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" 18 "github.com/cosmos/cosmos-sdk/x/simulation" 19 ) 20 21 var initialProposalID = uint64(100000000000000) 22 23 // Governance message types and routes 24 var ( 25 TypeMsgDeposit = sdk.MsgTypeURL(&v1.MsgDeposit{}) 26 TypeMsgVote = sdk.MsgTypeURL(&v1.MsgVote{}) 27 TypeMsgVoteWeighted = sdk.MsgTypeURL(&v1.MsgVoteWeighted{}) 28 TypeMsgSubmitProposal = sdk.MsgTypeURL(&v1.MsgSubmitProposal{}) 29 TypeMsgCancelProposal = sdk.MsgTypeURL(&v1.MsgCancelProposal{}) 30 ) 31 32 // Simulation operation weights constants 33 const ( 34 OpWeightMsgDeposit = "op_weight_msg_deposit" 35 OpWeightMsgVote = "op_weight_msg_vote" 36 OpWeightMsgVoteWeighted = "op_weight_msg_weighted_vote" 37 OpWeightMsgCancelProposal = "op_weight_msg_cancel_proposal" 38 39 DefaultWeightMsgDeposit = 100 40 DefaultWeightMsgVote = 67 41 DefaultWeightMsgVoteWeighted = 33 42 DefaultWeightTextProposal = 5 43 DefaultWeightMsgCancelProposal = 5 44 ) 45 46 // WeightedOperations returns all the operations from the module with their respective weights 47 func WeightedOperations( 48 appParams simtypes.AppParams, 49 txGen client.TxConfig, 50 ak types.AccountKeeper, 51 bk types.BankKeeper, 52 k *keeper.Keeper, 53 wMsgs []simtypes.WeightedProposalMsg, 54 wContents []simtypes.WeightedProposalContent, //nolint:staticcheck // used for legacy testing 55 ) simulation.WeightedOperations { 56 var ( 57 weightMsgDeposit int 58 weightMsgVote int 59 weightMsgVoteWeighted int 60 weightMsgCancelProposal int 61 ) 62 63 appParams.GetOrGenerate(OpWeightMsgDeposit, &weightMsgDeposit, nil, 64 func(_ *rand.Rand) { 65 weightMsgDeposit = DefaultWeightMsgDeposit 66 }, 67 ) 68 69 appParams.GetOrGenerate(OpWeightMsgVote, &weightMsgVote, nil, 70 func(_ *rand.Rand) { 71 weightMsgVote = DefaultWeightMsgVote 72 }, 73 ) 74 75 appParams.GetOrGenerate(OpWeightMsgVoteWeighted, &weightMsgVoteWeighted, nil, 76 func(_ *rand.Rand) { 77 weightMsgVoteWeighted = DefaultWeightMsgVoteWeighted 78 }, 79 ) 80 81 appParams.GetOrGenerate(OpWeightMsgCancelProposal, &weightMsgCancelProposal, nil, 82 func(_ *rand.Rand) { 83 weightMsgCancelProposal = DefaultWeightMsgCancelProposal 84 }, 85 ) 86 87 // generate the weighted operations for the proposal msgs 88 var wProposalOps simulation.WeightedOperations 89 for _, wMsg := range wMsgs { 90 wMsg := wMsg // pin variable 91 var weight int 92 appParams.GetOrGenerate(wMsg.AppParamsKey(), &weight, nil, 93 func(_ *rand.Rand) { weight = wMsg.DefaultWeight() }, 94 ) 95 96 wProposalOps = append( 97 wProposalOps, 98 simulation.NewWeightedOperation( 99 weight, 100 SimulateMsgSubmitProposal(txGen, ak, bk, k, wMsg.MsgSimulatorFn()), 101 ), 102 ) 103 } 104 105 // generate the weighted operations for the proposal contents 106 var wLegacyProposalOps simulation.WeightedOperations 107 for _, wContent := range wContents { 108 wContent := wContent // pin variable 109 var weight int 110 appParams.GetOrGenerate(wContent.AppParamsKey(), &weight, nil, 111 func(_ *rand.Rand) { weight = wContent.DefaultWeight() }, 112 ) 113 114 wLegacyProposalOps = append( 115 wLegacyProposalOps, 116 simulation.NewWeightedOperation( 117 weight, 118 SimulateMsgSubmitLegacyProposal(txGen, ak, bk, k, wContent.ContentSimulatorFn()), 119 ), 120 ) 121 } 122 123 wGovOps := simulation.WeightedOperations{ 124 simulation.NewWeightedOperation( 125 weightMsgDeposit, 126 SimulateMsgDeposit(txGen, ak, bk, k), 127 ), 128 simulation.NewWeightedOperation( 129 weightMsgVote, 130 SimulateMsgVote(txGen, ak, bk, k), 131 ), 132 simulation.NewWeightedOperation( 133 weightMsgVoteWeighted, 134 SimulateMsgVoteWeighted(txGen, ak, bk, k), 135 ), 136 simulation.NewWeightedOperation( 137 weightMsgCancelProposal, 138 SimulateMsgCancelProposal(txGen, ak, bk, k), 139 ), 140 } 141 142 return append(wGovOps, append(wProposalOps, wLegacyProposalOps...)...) 143 } 144 145 // SimulateMsgSubmitProposal simulates creating a msg Submit Proposal 146 // voting on the proposal, and subsequently slashing the proposal. It is implemented using 147 // future operations. 148 func SimulateMsgSubmitProposal( 149 txGen client.TxConfig, 150 ak types.AccountKeeper, 151 bk types.BankKeeper, 152 k *keeper.Keeper, 153 msgSim simtypes.MsgSimulatorFn, 154 ) simtypes.Operation { 155 return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 156 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 157 msgs := []sdk.Msg{} 158 proposalMsg := msgSim(r, ctx, accs) 159 if proposalMsg != nil { 160 msgs = append(msgs, proposalMsg) 161 } 162 163 return simulateMsgSubmitProposal(txGen, ak, bk, k, msgs)(r, app, ctx, accs, chainID) 164 } 165 } 166 167 // SimulateMsgSubmitLegacyProposal simulates creating a msg Submit Proposal 168 // voting on the proposal, and subsequently slashing the proposal. It is implemented using 169 // future operations. 170 func SimulateMsgSubmitLegacyProposal( 171 txGen client.TxConfig, 172 ak types.AccountKeeper, 173 bk types.BankKeeper, 174 k *keeper.Keeper, 175 contentSim simtypes.ContentSimulatorFn, //nolint:staticcheck // used for legacy testing 176 ) simtypes.Operation { 177 return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 178 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 179 // 1) submit proposal now 180 content := contentSim(r, ctx, accs) 181 if content == nil { 182 return simtypes.NoOpMsg(types.ModuleName, TypeMsgSubmitProposal, "content is nil"), nil, nil 183 } 184 185 govacc := k.GetGovernanceAccount(ctx) 186 contentMsg, err := v1.NewLegacyContent(content, govacc.GetAddress().String()) 187 if err != nil { 188 return simtypes.NoOpMsg(types.ModuleName, TypeMsgSubmitProposal, "error converting legacy content into proposal message"), nil, err 189 } 190 191 return simulateMsgSubmitProposal(txGen, ak, bk, k, []sdk.Msg{contentMsg})(r, app, ctx, accs, chainID) 192 } 193 } 194 195 func simulateMsgSubmitProposal( 196 txGen client.TxConfig, 197 ak types.AccountKeeper, 198 bk types.BankKeeper, 199 k *keeper.Keeper, 200 proposalMsgs []sdk.Msg, 201 ) simtypes.Operation { 202 // The states are: 203 // column 1: All validators vote 204 // column 2: 90% vote 205 // column 3: 75% vote 206 // column 4: 40% vote 207 // column 5: 15% vote 208 // column 6: noone votes 209 // All columns sum to 100 for simplicity, values chosen by @valardragon semi-arbitrarily, 210 // feel free to change. 211 numVotesTransitionMatrix, _ := simulation.CreateTransitionMatrix([][]int{ 212 {20, 10, 0, 0, 0, 0}, 213 {55, 50, 20, 10, 0, 0}, 214 {25, 25, 30, 25, 30, 15}, 215 {0, 15, 30, 25, 30, 30}, 216 {0, 0, 20, 30, 30, 30}, 217 {0, 0, 0, 10, 10, 25}, 218 }) 219 220 statePercentageArray := []float64{1, .9, .75, .4, .15, 0} 221 curNumVotesState := 1 222 223 return func( 224 r *rand.Rand, 225 app *baseapp.BaseApp, 226 ctx sdk.Context, 227 accs []simtypes.Account, 228 chainID string, 229 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 230 simAccount, _ := simtypes.RandomAcc(r, accs) 231 expedited := r.Intn(2) == 0 232 deposit, skip, err := randomDeposit(r, ctx, ak, bk, k, simAccount.Address, true, expedited) 233 switch { 234 case skip: 235 return simtypes.NoOpMsg(types.ModuleName, TypeMsgSubmitProposal, "skip deposit"), nil, nil 236 case err != nil: 237 return simtypes.NoOpMsg(types.ModuleName, TypeMsgSubmitProposal, "unable to generate deposit"), nil, err 238 } 239 240 msg, err := v1.NewMsgSubmitProposal( 241 proposalMsgs, 242 deposit, 243 simAccount.Address.String(), 244 simtypes.RandStringOfLength(r, 100), 245 simtypes.RandStringOfLength(r, 100), 246 simtypes.RandStringOfLength(r, 100), 247 expedited, 248 ) 249 if err != nil { 250 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(msg), "unable to generate a submit proposal msg"), nil, err 251 } 252 253 account := ak.GetAccount(ctx, simAccount.Address) 254 tx, err := simtestutil.GenSignedMockTx( 255 r, 256 txGen, 257 []sdk.Msg{msg}, 258 sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, 259 simtestutil.DefaultGenTxGas, 260 chainID, 261 []uint64{account.GetAccountNumber()}, 262 []uint64{account.GetSequence()}, 263 simAccount.PrivKey, 264 ) 265 if err != nil { 266 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(msg), "unable to generate mock tx"), nil, err 267 } 268 269 _, _, err = app.SimDeliver(txGen.TxEncoder(), tx) 270 if err != nil { 271 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err 272 } 273 274 opMsg := simtypes.NewOperationMsg(msg, true, "") 275 276 // get the submitted proposal ID 277 proposalID, err := k.ProposalID.Peek(ctx) 278 if err != nil { 279 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(msg), "unable to generate proposalID"), nil, err 280 } 281 282 // 2) Schedule operations for votes 283 // 2.1) first pick a number of people to vote. 284 curNumVotesState = numVotesTransitionMatrix.NextState(r, curNumVotesState) 285 numVotes := int(math.Ceil(float64(len(accs)) * statePercentageArray[curNumVotesState])) 286 287 // 2.2) select who votes and when 288 whoVotes := r.Perm(len(accs)) 289 290 // didntVote := whoVotes[numVotes:] 291 whoVotes = whoVotes[:numVotes] 292 params, _ := k.Params.Get(ctx) 293 votingPeriod := params.VotingPeriod 294 295 fops := make([]simtypes.FutureOperation, numVotes+1) 296 for i := 0; i < numVotes; i++ { 297 whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second) 298 fops[i] = simtypes.FutureOperation{ 299 BlockTime: whenVote, 300 Op: operationSimulateMsgVote(txGen, ak, bk, k, accs[whoVotes[i]], int64(proposalID)), 301 } 302 } 303 304 return opMsg, fops, nil 305 } 306 } 307 308 // SimulateMsgDeposit generates a MsgDeposit with random values. 309 func SimulateMsgDeposit( 310 txGen client.TxConfig, 311 ak types.AccountKeeper, 312 bk types.BankKeeper, 313 k *keeper.Keeper, 314 ) simtypes.Operation { 315 return func( 316 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, 317 accs []simtypes.Account, chainID string, 318 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 319 simAccount, _ := simtypes.RandomAcc(r, accs) 320 proposalID, ok := randomProposalID(r, k, ctx, v1.StatusDepositPeriod) 321 if !ok { 322 return simtypes.NoOpMsg(types.ModuleName, TypeMsgDeposit, "unable to generate proposalID"), nil, nil 323 } 324 325 p, err := k.Proposals.Get(ctx, proposalID) 326 if err != nil { 327 return simtypes.NoOpMsg(types.ModuleName, TypeMsgDeposit, "unable to get proposal"), nil, err 328 } 329 330 deposit, skip, err := randomDeposit(r, ctx, ak, bk, k, simAccount.Address, false, p.Expedited) 331 switch { 332 case skip: 333 return simtypes.NoOpMsg(types.ModuleName, TypeMsgDeposit, "skip deposit"), nil, nil 334 case err != nil: 335 return simtypes.NoOpMsg(types.ModuleName, TypeMsgDeposit, "unable to generate deposit"), nil, err 336 } 337 338 msg := v1.NewMsgDeposit(simAccount.Address, proposalID, deposit) 339 340 account := ak.GetAccount(ctx, simAccount.Address) 341 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 342 343 var fees sdk.Coins 344 coins, hasNeg := spendable.SafeSub(deposit...) 345 if !hasNeg { 346 fees, err = simtypes.RandomFees(r, ctx, coins) 347 if err != nil { 348 return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(msg), "unable to generate fees"), nil, err 349 } 350 } 351 352 txCtx := simulation.OperationInput{ 353 R: r, 354 App: app, 355 TxGen: txGen, 356 Cdc: nil, 357 Msg: msg, 358 Context: ctx, 359 SimAccount: simAccount, 360 AccountKeeper: ak, 361 ModuleName: types.ModuleName, 362 } 363 364 return simulation.GenAndDeliverTx(txCtx, fees) 365 } 366 } 367 368 // SimulateMsgVote generates a MsgVote with random values. 369 func SimulateMsgVote( 370 txGen client.TxConfig, 371 ak types.AccountKeeper, 372 bk types.BankKeeper, 373 k *keeper.Keeper, 374 ) simtypes.Operation { 375 return operationSimulateMsgVote(txGen, ak, bk, k, simtypes.Account{}, -1) 376 } 377 378 func operationSimulateMsgVote( 379 txGen client.TxConfig, 380 ak types.AccountKeeper, 381 bk types.BankKeeper, 382 k *keeper.Keeper, 383 simAccount simtypes.Account, 384 proposalIDInt int64, 385 ) simtypes.Operation { 386 return func( 387 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, 388 accs []simtypes.Account, chainID string, 389 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 390 if simAccount.Equals(simtypes.Account{}) { 391 simAccount, _ = simtypes.RandomAcc(r, accs) 392 } 393 394 var proposalID uint64 395 396 switch { 397 case proposalIDInt < 0: 398 var ok bool 399 proposalID, ok = randomProposalID(r, k, ctx, v1.StatusVotingPeriod) 400 if !ok { 401 return simtypes.NoOpMsg(types.ModuleName, TypeMsgVote, "unable to generate proposalID"), nil, nil 402 } 403 default: 404 proposalID = uint64(proposalIDInt) 405 } 406 407 option := randomVotingOption(r) 408 msg := v1.NewMsgVote(simAccount.Address, proposalID, option, "") 409 410 account := ak.GetAccount(ctx, simAccount.Address) 411 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 412 413 txCtx := simulation.OperationInput{ 414 R: r, 415 App: app, 416 TxGen: txGen, 417 Cdc: nil, 418 Msg: msg, 419 Context: ctx, 420 SimAccount: simAccount, 421 AccountKeeper: ak, 422 Bankkeeper: bk, 423 ModuleName: types.ModuleName, 424 CoinsSpentInMsg: spendable, 425 } 426 427 return simulation.GenAndDeliverTxWithRandFees(txCtx) 428 } 429 } 430 431 // SimulateMsgVoteWeighted generates a MsgVoteWeighted with random values. 432 func SimulateMsgVoteWeighted( 433 txGen client.TxConfig, 434 ak types.AccountKeeper, 435 bk types.BankKeeper, 436 k *keeper.Keeper, 437 ) simtypes.Operation { 438 return operationSimulateMsgVoteWeighted(txGen, ak, bk, k, simtypes.Account{}, -1) 439 } 440 441 func operationSimulateMsgVoteWeighted( 442 txGen client.TxConfig, 443 ak types.AccountKeeper, 444 bk types.BankKeeper, 445 k *keeper.Keeper, 446 simAccount simtypes.Account, 447 proposalIDInt int64, 448 ) simtypes.Operation { 449 return func( 450 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, 451 accs []simtypes.Account, chainID string, 452 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 453 if simAccount.Equals(simtypes.Account{}) { 454 simAccount, _ = simtypes.RandomAcc(r, accs) 455 } 456 457 var proposalID uint64 458 459 switch { 460 case proposalIDInt < 0: 461 var ok bool 462 proposalID, ok = randomProposalID(r, k, ctx, v1.StatusVotingPeriod) 463 if !ok { 464 return simtypes.NoOpMsg(types.ModuleName, TypeMsgVoteWeighted, "unable to generate proposalID"), nil, nil 465 } 466 default: 467 proposalID = uint64(proposalIDInt) 468 } 469 470 options := randomWeightedVotingOptions(r) 471 msg := v1.NewMsgVoteWeighted(simAccount.Address, proposalID, options, "") 472 473 account := ak.GetAccount(ctx, simAccount.Address) 474 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 475 476 txCtx := simulation.OperationInput{ 477 R: r, 478 App: app, 479 TxGen: txGen, 480 Cdc: nil, 481 Msg: msg, 482 Context: ctx, 483 SimAccount: simAccount, 484 AccountKeeper: ak, 485 Bankkeeper: bk, 486 ModuleName: types.ModuleName, 487 CoinsSpentInMsg: spendable, 488 } 489 490 return simulation.GenAndDeliverTxWithRandFees(txCtx) 491 } 492 } 493 494 // SimulateMsgCancelProposal generates a MsgCancelProposal. 495 func SimulateMsgCancelProposal( 496 txGen client.TxConfig, 497 ak types.AccountKeeper, 498 bk types.BankKeeper, 499 k *keeper.Keeper, 500 ) simtypes.Operation { 501 return func( 502 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, 503 accs []simtypes.Account, chainID string, 504 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 505 simAccount := accs[0] 506 proposal := randomProposal(r, k, ctx) 507 if proposal == nil { 508 return simtypes.NoOpMsg(types.ModuleName, TypeMsgCancelProposal, "no proposals found"), nil, nil 509 } 510 511 if proposal.Proposer != simAccount.Address.String() { 512 return simtypes.NoOpMsg(types.ModuleName, TypeMsgCancelProposal, "invalid proposer"), nil, nil 513 } 514 515 if (proposal.Status != v1.StatusDepositPeriod) && (proposal.Status != v1.StatusVotingPeriod) { 516 return simtypes.NoOpMsg(types.ModuleName, TypeMsgCancelProposal, "invalid proposal status"), nil, nil 517 } 518 519 account := ak.GetAccount(ctx, simAccount.Address) 520 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 521 522 msg := v1.NewMsgCancelProposal(proposal.Id, account.GetAddress().String()) 523 524 txCtx := simulation.OperationInput{ 525 R: r, 526 App: app, 527 TxGen: txGen, 528 Cdc: nil, 529 Msg: msg, 530 Context: ctx, 531 SimAccount: simAccount, 532 AccountKeeper: ak, 533 Bankkeeper: bk, 534 ModuleName: types.ModuleName, 535 CoinsSpentInMsg: spendable, 536 } 537 538 return simulation.GenAndDeliverTxWithRandFees(txCtx) 539 } 540 } 541 542 // Pick a random deposit with a random denomination with a 543 // deposit amount between (0, min(balance, minDepositAmount)) 544 // This is to simulate multiple users depositing to get the 545 // proposal above the minimum deposit amount 546 func randomDeposit( 547 r *rand.Rand, 548 ctx sdk.Context, 549 ak types.AccountKeeper, 550 bk types.BankKeeper, 551 k *keeper.Keeper, 552 addr sdk.AccAddress, 553 useMinAmount bool, 554 expedited bool, 555 ) (deposit sdk.Coins, skip bool, err error) { 556 account := ak.GetAccount(ctx, addr) 557 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 558 559 if spendable.Empty() { 560 return nil, true, nil // skip 561 } 562 563 params, _ := k.Params.Get(ctx) 564 minDeposit := params.MinDeposit 565 if expedited { 566 minDeposit = params.ExpeditedMinDeposit 567 } 568 denomIndex := r.Intn(len(minDeposit)) 569 denom := minDeposit[denomIndex].Denom 570 571 spendableBalance := spendable.AmountOf(denom) 572 if spendableBalance.IsZero() { 573 return nil, true, nil 574 } 575 576 minDepositAmount := minDeposit[denomIndex].Amount 577 578 minDepositRatio, err := sdkmath.LegacyNewDecFromStr(params.GetMinDepositRatio()) 579 if err != nil { 580 return nil, false, err 581 } 582 583 threshold := minDepositAmount.ToLegacyDec().Mul(minDepositRatio).TruncateInt() 584 585 minAmount := sdkmath.ZeroInt() 586 if useMinAmount { 587 minDepositPercent, err := sdkmath.LegacyNewDecFromStr(params.MinInitialDepositRatio) 588 if err != nil { 589 return nil, false, err 590 } 591 592 minAmount = sdkmath.LegacyNewDecFromInt(minDepositAmount).Mul(minDepositPercent).TruncateInt() 593 } 594 595 amount, err := simtypes.RandPositiveInt(r, minDepositAmount.Sub(minAmount)) 596 if err != nil { 597 return nil, false, err 598 } 599 amount = amount.Add(minAmount) 600 601 if amount.GT(spendableBalance) || amount.LT(threshold) { 602 return nil, true, nil 603 } 604 605 return sdk.Coins{sdk.NewCoin(denom, amount)}, false, nil 606 } 607 608 // randomProposal returns a random proposal stored in state 609 func randomProposal(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context) *v1.Proposal { 610 var proposals []*v1.Proposal 611 err := k.Proposals.Walk(ctx, nil, func(key uint64, value v1.Proposal) (stop bool, err error) { 612 proposals = append(proposals, &value) 613 return false, nil 614 }) 615 if err != nil { 616 panic(err) 617 } 618 if len(proposals) == 0 { 619 return nil 620 } 621 randomIndex := r.Intn(len(proposals)) 622 return proposals[randomIndex] 623 } 624 625 // Pick a random proposal ID between the initial proposal ID 626 // (defined in gov GenesisState) and the latest proposal ID 627 // that matches a given Status. 628 // It does not provide a default ID. 629 func randomProposalID(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context, status v1.ProposalStatus) (proposalID uint64, found bool) { 630 proposalID, _ = k.ProposalID.Peek(ctx) 631 632 switch { 633 case proposalID > initialProposalID: 634 // select a random ID between [initialProposalID, proposalID] 635 proposalID = uint64(simtypes.RandIntBetween(r, int(initialProposalID), int(proposalID))) 636 637 default: 638 // This is called on the first call to this funcion 639 // in order to update the global variable 640 initialProposalID = proposalID 641 } 642 643 proposal, err := k.Proposals.Get(ctx, proposalID) 644 if err != nil || proposal.Status != status { 645 return proposalID, false 646 } 647 648 return proposalID, true 649 } 650 651 // Pick a random voting option 652 func randomVotingOption(r *rand.Rand) v1.VoteOption { 653 switch r.Intn(4) { 654 case 0: 655 return v1.OptionYes 656 case 1: 657 return v1.OptionAbstain 658 case 2: 659 return v1.OptionNo 660 case 3: 661 return v1.OptionNoWithVeto 662 default: 663 panic("invalid vote option") 664 } 665 } 666 667 // Pick a random weighted voting options 668 func randomWeightedVotingOptions(r *rand.Rand) v1.WeightedVoteOptions { 669 w1 := r.Intn(100 + 1) 670 w2 := r.Intn(100 - w1 + 1) 671 w3 := r.Intn(100 - w1 - w2 + 1) 672 w4 := 100 - w1 - w2 - w3 673 weightedVoteOptions := v1.WeightedVoteOptions{} 674 if w1 > 0 { 675 weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{ 676 Option: v1.OptionYes, 677 Weight: sdkmath.LegacyNewDecWithPrec(int64(w1), 2).String(), 678 }) 679 } 680 if w2 > 0 { 681 weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{ 682 Option: v1.OptionAbstain, 683 Weight: sdkmath.LegacyNewDecWithPrec(int64(w2), 2).String(), 684 }) 685 } 686 if w3 > 0 { 687 weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{ 688 Option: v1.OptionNo, 689 Weight: sdkmath.LegacyNewDecWithPrec(int64(w3), 2).String(), 690 }) 691 } 692 if w4 > 0 { 693 weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{ 694 Option: v1.OptionNoWithVeto, 695 Weight: sdkmath.LegacyNewDecWithPrec(int64(w4), 2).String(), 696 }) 697 } 698 return weightedVoteOptions 699 }