github.com/Finschia/finschia-sdk@v0.48.1/x/staking/simulation/operations.go (about) 1 package simulation 2 3 import ( 4 "fmt" 5 "math/rand" 6 7 "github.com/Finschia/finschia-sdk/baseapp" 8 "github.com/Finschia/finschia-sdk/codec" 9 simappparams "github.com/Finschia/finschia-sdk/simapp/params" 10 sdk "github.com/Finschia/finschia-sdk/types" 11 simtypes "github.com/Finschia/finschia-sdk/types/simulation" 12 "github.com/Finschia/finschia-sdk/x/simulation" 13 "github.com/Finschia/finschia-sdk/x/staking/keeper" 14 "github.com/Finschia/finschia-sdk/x/staking/types" 15 ) 16 17 // Simulation operation weights constants 18 // nolint:gosec 19 const ( 20 OpWeightMsgCreateValidator = "op_weight_msg_create_validator" //nolint:gosec 21 OpWeightMsgEditValidator = "op_weight_msg_edit_validator" //nolint:gosec 22 OpWeightMsgDelegate = "op_weight_msg_delegate" //nolint:gosec 23 OpWeightMsgUndelegate = "op_weight_msg_undelegate" //nolint:gosec 24 OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate" //nolint:gosec 25 ) 26 27 // WeightedOperations returns all the operations from the module with their respective weights 28 func WeightedOperations( 29 appParams simtypes.AppParams, cdc codec.JSONCodec, ak types.AccountKeeper, 30 bk types.BankKeeper, k keeper.Keeper, 31 ) simulation.WeightedOperations { 32 var ( 33 weightMsgCreateValidator int 34 weightMsgEditValidator int 35 weightMsgDelegate int 36 weightMsgUndelegate int 37 weightMsgBeginRedelegate int 38 ) 39 40 appParams.GetOrGenerate(cdc, OpWeightMsgCreateValidator, &weightMsgCreateValidator, nil, 41 func(_ *rand.Rand) { 42 weightMsgCreateValidator = simappparams.DefaultWeightMsgCreateValidator 43 }, 44 ) 45 46 appParams.GetOrGenerate(cdc, OpWeightMsgEditValidator, &weightMsgEditValidator, nil, 47 func(_ *rand.Rand) { 48 weightMsgEditValidator = simappparams.DefaultWeightMsgEditValidator 49 }, 50 ) 51 52 appParams.GetOrGenerate(cdc, OpWeightMsgDelegate, &weightMsgDelegate, nil, 53 func(_ *rand.Rand) { 54 weightMsgDelegate = simappparams.DefaultWeightMsgDelegate 55 }, 56 ) 57 58 appParams.GetOrGenerate(cdc, OpWeightMsgUndelegate, &weightMsgUndelegate, nil, 59 func(_ *rand.Rand) { 60 weightMsgUndelegate = simappparams.DefaultWeightMsgUndelegate 61 }, 62 ) 63 64 appParams.GetOrGenerate(cdc, OpWeightMsgBeginRedelegate, &weightMsgBeginRedelegate, nil, 65 func(_ *rand.Rand) { 66 weightMsgBeginRedelegate = simappparams.DefaultWeightMsgBeginRedelegate 67 }, 68 ) 69 70 return simulation.WeightedOperations{ 71 simulation.NewWeightedOperation( 72 weightMsgCreateValidator, 73 SimulateMsgCreateValidator(ak, bk, k), 74 ), 75 simulation.NewWeightedOperation( 76 weightMsgEditValidator, 77 SimulateMsgEditValidator(ak, bk, k), 78 ), 79 simulation.NewWeightedOperation( 80 weightMsgDelegate, 81 SimulateMsgDelegate(ak, bk, k), 82 ), 83 simulation.NewWeightedOperation( 84 weightMsgUndelegate, 85 SimulateMsgUndelegate(ak, bk, k), 86 ), 87 simulation.NewWeightedOperation( 88 weightMsgBeginRedelegate, 89 SimulateMsgBeginRedelegate(ak, bk, k), 90 ), 91 } 92 } 93 94 // SimulateMsgCreateValidator generates a MsgCreateValidator with random values 95 func SimulateMsgCreateValidator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) 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 simAccount, _ := simtypes.RandomAcc(r, accs) 100 address := sdk.ValAddress(simAccount.Address) 101 102 // ensure the validator doesn't exist already 103 _, found := k.GetValidator(ctx, address) 104 if found { 105 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "unable to find validator"), nil, nil 106 } 107 108 denom := k.GetParams(ctx).BondDenom 109 110 balance := bk.GetBalance(ctx, simAccount.Address, denom).Amount 111 if !balance.IsPositive() { 112 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "balance is negative"), nil, nil 113 } 114 115 amount, err := simtypes.RandPositiveInt(r, balance) 116 if err != nil { 117 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "unable to generate positive amount"), nil, err 118 } 119 120 selfDelegation := sdk.NewCoin(denom, amount) 121 122 account := ak.GetAccount(ctx, simAccount.Address) 123 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 124 125 var fees sdk.Coins 126 127 coins, hasNeg := spendable.SafeSub(sdk.Coins{selfDelegation}) 128 if !hasNeg { 129 fees, err = simtypes.RandomFees(r, ctx, coins) 130 if err != nil { 131 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateValidator, "unable to generate fees"), nil, err 132 } 133 } 134 135 description := types.NewDescription( 136 simtypes.RandStringOfLength(r, 10), 137 simtypes.RandStringOfLength(r, 10), 138 simtypes.RandStringOfLength(r, 10), 139 simtypes.RandStringOfLength(r, 10), 140 simtypes.RandStringOfLength(r, 10), 141 ) 142 143 maxCommission := sdk.NewDecWithPrec(int64(simtypes.RandIntBetween(r, 0, 100)), 2) 144 commission := types.NewCommissionRates( 145 simtypes.RandomDecAmount(r, maxCommission), 146 maxCommission, 147 simtypes.RandomDecAmount(r, maxCommission), 148 ) 149 150 msg, err := types.NewMsgCreateValidator(address, simAccount.ConsKey.PubKey(), selfDelegation, description, commission, sdk.OneInt()) 151 if err != nil { 152 return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "unable to create CreateValidator message"), nil, err 153 } 154 155 txCtx := simulation.OperationInput{ 156 R: r, 157 App: app, 158 TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 159 Cdc: nil, 160 Msg: msg, 161 MsgType: msg.Type(), 162 Context: ctx, 163 SimAccount: simAccount, 164 AccountKeeper: ak, 165 ModuleName: types.ModuleName, 166 } 167 168 return simulation.GenAndDeliverTx(txCtx, fees) 169 } 170 } 171 172 // SimulateMsgEditValidator generates a MsgEditValidator with random values 173 func SimulateMsgEditValidator(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { 174 return func( 175 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 176 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 177 if len(k.GetAllValidators(ctx)) == 0 { 178 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "number of validators equal zero"), nil, nil 179 } 180 181 val, ok := keeper.RandomValidator(r, k, ctx) 182 if !ok { 183 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "unable to pick a validator"), nil, nil 184 } 185 186 address := val.GetOperator() 187 188 newCommissionRate := simtypes.RandomDecAmount(r, val.Commission.MaxRate) 189 190 if err := val.Commission.ValidateNewRate(newCommissionRate, ctx.BlockHeader().Time); err != nil { 191 // skip as the commission is invalid 192 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "invalid commission rate"), nil, nil 193 } 194 195 simAccount, found := simtypes.FindAccount(accs, sdk.AccAddress(val.GetOperator())) 196 if !found { 197 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgEditValidator, "unable to find account"), nil, fmt.Errorf("validator %s not found", val.GetOperator()) 198 } 199 200 account := ak.GetAccount(ctx, simAccount.Address) 201 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 202 203 description := types.NewDescription( 204 simtypes.RandStringOfLength(r, 10), 205 simtypes.RandStringOfLength(r, 10), 206 simtypes.RandStringOfLength(r, 10), 207 simtypes.RandStringOfLength(r, 10), 208 simtypes.RandStringOfLength(r, 10), 209 ) 210 211 msg := types.NewMsgEditValidator(address, description, &newCommissionRate, nil) 212 213 txCtx := simulation.OperationInput{ 214 R: r, 215 App: app, 216 TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 217 Cdc: nil, 218 Msg: msg, 219 MsgType: msg.Type(), 220 Context: ctx, 221 SimAccount: simAccount, 222 AccountKeeper: ak, 223 Bankkeeper: bk, 224 ModuleName: types.ModuleName, 225 CoinsSpentInMsg: spendable, 226 } 227 228 return simulation.GenAndDeliverTxWithRandFees(txCtx) 229 } 230 } 231 232 // SimulateMsgDelegate generates a MsgDelegate with random values 233 func SimulateMsgDelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { 234 return func( 235 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 236 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 237 denom := k.GetParams(ctx).BondDenom 238 239 if len(k.GetAllValidators(ctx)) == 0 { 240 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "number of validators equal zero"), nil, nil 241 } 242 243 simAccount, _ := simtypes.RandomAcc(r, accs) 244 val, ok := keeper.RandomValidator(r, k, ctx) 245 if !ok { 246 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "unable to pick a validator"), nil, nil 247 } 248 249 if val.InvalidExRate() { 250 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "validator's invalid echange rate"), nil, nil 251 } 252 253 amount := bk.GetBalance(ctx, simAccount.Address, denom).Amount 254 if !amount.IsPositive() { 255 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "balance is negative"), nil, nil 256 } 257 258 amount, err := simtypes.RandPositiveInt(r, amount) 259 if err != nil { 260 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "unable to generate positive amount"), nil, err 261 } 262 263 bondAmt := sdk.NewCoin(denom, amount) 264 265 account := ak.GetAccount(ctx, simAccount.Address) 266 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 267 268 var fees sdk.Coins 269 270 coins, hasNeg := spendable.SafeSub(sdk.Coins{bondAmt}) 271 if !hasNeg { 272 fees, err = simtypes.RandomFees(r, ctx, coins) 273 if err != nil { 274 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgDelegate, "unable to generate fees"), nil, err 275 } 276 } 277 278 msg := types.NewMsgDelegate(simAccount.Address, val.GetOperator(), bondAmt) 279 280 txCtx := simulation.OperationInput{ 281 R: r, 282 App: app, 283 TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 284 Cdc: nil, 285 Msg: msg, 286 MsgType: msg.Type(), 287 Context: ctx, 288 SimAccount: simAccount, 289 AccountKeeper: ak, 290 ModuleName: types.ModuleName, 291 } 292 293 return simulation.GenAndDeliverTx(txCtx, fees) 294 } 295 } 296 297 // SimulateMsgUndelegate generates a MsgUndelegate with random values 298 func SimulateMsgUndelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { 299 return func( 300 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 301 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 302 // get random validator 303 validator, ok := keeper.RandomValidator(r, k, ctx) 304 if !ok { 305 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "validator is not ok"), nil, nil 306 } 307 308 valAddr := validator.GetOperator() 309 delegations := k.GetValidatorDelegations(ctx, validator.GetOperator()) 310 if delegations == nil { 311 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "keeper does have any delegation entries"), nil, nil 312 } 313 314 // get random delegator from validator 315 delegation := delegations[r.Intn(len(delegations))] 316 delAddr := delegation.GetDelegatorAddr() 317 318 if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) { 319 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "keeper does have a max unbonding delegation entries"), nil, nil 320 } 321 322 totalBond := validator.TokensFromShares(delegation.GetShares()).TruncateInt() 323 if !totalBond.IsPositive() { 324 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "total bond is negative"), nil, nil 325 } 326 327 unbondAmt, err := simtypes.RandPositiveInt(r, totalBond) 328 if err != nil { 329 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "invalid unbond amount"), nil, err 330 } 331 332 if unbondAmt.IsZero() { 333 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgUndelegate, "unbond amount is zero"), nil, nil 334 } 335 336 msg := types.NewMsgUndelegate( 337 delAddr, valAddr, sdk.NewCoin(k.BondDenom(ctx), unbondAmt), 338 ) 339 340 // need to retrieve the simulation account associated with delegation to retrieve PrivKey 341 var simAccount simtypes.Account 342 343 for _, simAcc := range accs { 344 if simAcc.Address.Equals(delAddr) { 345 simAccount = simAcc 346 break 347 } 348 } 349 // if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error 350 if simAccount.PrivKey == nil { 351 return simtypes.NoOpMsg(types.ModuleName, msg.Type(), "account private key is nil"), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) 352 } 353 354 account := ak.GetAccount(ctx, delAddr) 355 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 356 357 txCtx := simulation.OperationInput{ 358 R: r, 359 App: app, 360 TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 361 Cdc: nil, 362 Msg: msg, 363 MsgType: msg.Type(), 364 Context: ctx, 365 SimAccount: simAccount, 366 AccountKeeper: ak, 367 Bankkeeper: bk, 368 ModuleName: types.ModuleName, 369 CoinsSpentInMsg: spendable, 370 } 371 372 return simulation.GenAndDeliverTxWithRandFees(txCtx) 373 } 374 } 375 376 // SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values 377 func SimulateMsgBeginRedelegate(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation { 378 return func( 379 r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, 380 ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { 381 // get random source validator 382 srcVal, ok := keeper.RandomValidator(r, k, ctx) 383 if !ok { 384 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "unable to pick validator"), nil, nil 385 } 386 387 srcAddr := srcVal.GetOperator() 388 delegations := k.GetValidatorDelegations(ctx, srcAddr) 389 if delegations == nil { 390 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "keeper does have any delegation entries"), nil, nil 391 } 392 393 // get random delegator from src validator 394 delegation := delegations[r.Intn(len(delegations))] 395 delAddr := delegation.GetDelegatorAddr() 396 397 if k.HasReceivingRedelegation(ctx, delAddr, srcAddr) { 398 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "receveing redelegation is not allowed"), nil, nil // skip 399 } 400 401 // get random destination validator 402 destVal, ok := keeper.RandomValidator(r, k, ctx) 403 if !ok { 404 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "unable to pick validator"), nil, nil 405 } 406 407 destAddr := destVal.GetOperator() 408 if srcAddr.Equals(destAddr) || destVal.InvalidExRate() || k.HasMaxRedelegationEntries(ctx, delAddr, srcAddr, destAddr) { 409 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "checks failed"), nil, nil 410 } 411 412 totalBond := srcVal.TokensFromShares(delegation.GetShares()).TruncateInt() 413 if !totalBond.IsPositive() { 414 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "total bond is negative"), nil, nil 415 } 416 417 redAmt, err := simtypes.RandPositiveInt(r, totalBond) 418 if err != nil { 419 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "unable to generate positive amount"), nil, err 420 } 421 422 if redAmt.IsZero() { 423 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "amount is zero"), nil, nil 424 } 425 426 // check if the shares truncate to zero 427 shares, err := srcVal.SharesFromTokens(redAmt) 428 if err != nil { 429 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "invalid shares"), nil, err 430 } 431 432 if srcVal.TokensFromShares(shares).TruncateInt().IsZero() { 433 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "shares truncate to zero"), nil, nil // skip 434 } 435 436 // need to retrieve the simulation account associated with delegation to retrieve PrivKey 437 var simAccount simtypes.Account 438 439 for _, simAcc := range accs { 440 if simAcc.Address.Equals(delAddr) { 441 simAccount = simAcc 442 break 443 } 444 } 445 446 // if simaccount.PrivKey == nil, delegation address does not exist in accs. Return error 447 if simAccount.PrivKey == nil { 448 return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgBeginRedelegate, "account private key is nil"), nil, fmt.Errorf("delegation addr: %s does not exist in simulation accounts", delAddr) 449 } 450 451 account := ak.GetAccount(ctx, delAddr) 452 spendable := bk.SpendableCoins(ctx, account.GetAddress()) 453 454 msg := types.NewMsgBeginRedelegate( 455 delAddr, srcAddr, destAddr, 456 sdk.NewCoin(k.BondDenom(ctx), redAmt), 457 ) 458 459 txCtx := simulation.OperationInput{ 460 R: r, 461 App: app, 462 TxGen: simappparams.MakeTestEncodingConfig().TxConfig, 463 Cdc: nil, 464 Msg: msg, 465 MsgType: msg.Type(), 466 Context: ctx, 467 SimAccount: simAccount, 468 AccountKeeper: ak, 469 Bankkeeper: bk, 470 ModuleName: types.ModuleName, 471 CoinsSpentInMsg: spendable, 472 } 473 474 return simulation.GenAndDeliverTxWithRandFees(txCtx) 475 } 476 }