feat(sims): Add simsx support to modules (#24145)

Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
This commit is contained in:
Alexander Peters 2025-04-02 15:23:36 +02:00 committed by GitHub
parent 99da3279be
commit ea908cef68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 2025 additions and 241 deletions

View File

@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Features
* (simsx) [#24062](https://github.com/cosmos/cosmos-sdk/pull/24062) [#24145](https://github.com/cosmos/cosmos-sdk/pull/24145) Add new simsx framework on top of simulations for better module dev experience.
* (baseapp) [#24069](https://github.com/cosmos/cosmos-sdk/pull/24069) Create CheckTxHandler to allow extending the logic of CheckTx.
* (types) [#24093](https://github.com/cosmos/cosmos-sdk/pull/24093) Added a new method, `IsGT`, for `types.Coin`. This method is used to check if a `types.Coin` is greater than another `types.Coin`.
* (client/keys) [#24071](https://github.com/cosmos/cosmos-sdk/pull/24071) Add support for importing hex key using standard input.

14
simsx/context.go Normal file
View File

@ -0,0 +1,14 @@
package simsx
import (
"context"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// BlockTime read header block time from sdk context
func BlockTime(ctx context.Context) time.Time {
sdkCtx := sdk.UnwrapSDKContext(ctx)
return sdkCtx.BlockTime()
}

View File

@ -107,7 +107,7 @@ type visitable interface {
// RandSubsetCoins return random amounts from the current balance. When the coins are empty, skip is called on the reporter.
// The amounts are removed from the liquid balance.
func (b *SimsAccountBalance) RandSubsetCoins(reporter SimulationReporter, filters ...CoinsFilter) sdk.Coins {
amount := b.randomAmount(5, reporter, b.Coins, filters...)
amount := b.randomAmount(1, reporter, b.Coins, filters...)
b.Coins = b.Sub(amount...)
if amount.Empty() {
reporter.Skip("got empty amounts")
@ -123,7 +123,7 @@ func (b *SimsAccountBalance) RandSubsetCoin(reporter SimulationReporter, denom s
reporter.Skipf("no such coin: %s", denom)
return sdk.NewCoin(denom, math.ZeroInt())
}
amounts := b.randomAmount(5, reporter, sdk.Coins{coin}, filters...)
amounts := b.randomAmount(1, reporter, sdk.Coins{coin}, filters...)
if amounts.Empty() {
reporter.Skip("empty coin")
return sdk.NewCoin(denom, math.ZeroInt())
@ -287,9 +287,16 @@ func NewChainDataSource(
}
// AnyAccount returns a random SimAccount matching the filter criteria. Module accounts are excluded.
// In case of an error or no matching account found, the reporter is set to skip and an empty value is returned.
// In case of an error or no matching account was found with 1 retry, the reporter is set to skip and an empty value is returned.
func (c *ChainDataSource) AnyAccount(r SimulationReporter, filters ...SimAccountFilter) SimAccount {
acc := c.randomAccount(r, 5, filters...)
acc := c.AnyAccountN(1, r, filters...)
return acc
}
// AnyAccountN returns a random SimAccount matching the filter criteria with given number of retries. Module accounts are excluded.
// In case of an error or no matching account found, the reporter is set to skip and an empty value is returned.
func (c *ChainDataSource) AnyAccountN(retries int, r SimulationReporter, filters ...SimAccountFilter) SimAccount {
acc := c.randomAccount(r, retries, filters...)
return acc
}

View File

@ -16,6 +16,7 @@ func TestChainDataSourceAnyAccount(t *testing.T) {
accs := simtypes.RandomAccounts(r, 3)
specs := map[string]struct {
filters []SimAccountFilter
retry int
assert func(t *testing.T, got SimAccount, reporter SimulationReporter)
}{
"no filters": {
@ -30,6 +31,7 @@ func TestChainDataSourceAnyAccount(t *testing.T) {
assert.Equal(t, accs[2].AddressBech32, got.AddressBech32)
assert.False(t, reporter.IsSkipped())
},
retry: 6,
},
"no match": {
filters: []SimAccountFilter{SimAccountFilterFn(func(a SimAccount) bool { return false })},
@ -43,7 +45,7 @@ func TestChainDataSourceAnyAccount(t *testing.T) {
t.Run(name, func(t *testing.T) {
reporter := NewBasicSimulationReporter()
c := NewChainDataSource(sdk.Context{}, r, nil, nil, codec, accs...)
a := c.AnyAccount(reporter, spec.filters...)
a := c.AnyAccountN(spec.retry, reporter, spec.filters...)
spec.assert(t, a, reporter)
})
}

View File

@ -16,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -171,10 +172,16 @@ func (am AppModule) GenerateGenesisState(simState *module.SimulationState) {
}
// ProposalMsgs returns msgs used for governance proposals for simulations.
// migrate to ProposalMsgsX. This method is ignored when ProposalMsgsX exists and will be removed in the future.
func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}
// ProposalMsgsX registers governance proposal messages in the simulation registry.
func (AppModule) ProposalMsgsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_update_params", 100), simulation.MsgUpdateParamsFactory())
}
// RegisterStoreDecoder registers a decoder for auth module's types
func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
sdr[types.StoreKey] = simtypes.NewStoreDecoderFuncFromCollectionsSchema(am.accountKeeper.Schema)

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -112,11 +110,5 @@ func RandomizedGenState(simState *module.SimulationState, randGenAccountsFn type
genesisAccs := randGenAccountsFn(simState)
authGenesis := types.NewGenesisState(params, genesisAccs)
bz, err := json.MarshalIndent(&authGenesis.Params, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated auth parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis)
}

View File

@ -0,0 +1,25 @@
package simulation
import (
"context"
"github.com/cosmos/cosmos-sdk/simsx"
"github.com/cosmos/cosmos-sdk/x/auth/types"
)
func MsgUpdateParamsFactory() simsx.SimMsgFactoryFn[*types.MsgUpdateParams] {
return func(_ context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgUpdateParams) {
r := testData.Rand()
params := types.DefaultParams()
params.MaxMemoCharacters = r.Uint64InRange(1, 1000)
params.TxSigLimit = r.Uint64InRange(1, 1000)
params.TxSizeCostPerByte = r.Uint64InRange(1, 1000)
params.SigVerifyCostED25519 = r.Uint64InRange(1, 1000)
params.SigVerifyCostSecp256k1 = r.Uint64InRange(1, 1000)
return nil, &types.MsgUpdateParams{
Authority: testData.ModuleAccountAddress(reporter, "gov"),
Params: params,
}
}
}

View File

@ -11,13 +11,14 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
DefaultWeightMsgUpdateParams int = 100
OpWeightMsgUpdateParams = "op_weight_msg_update_params"
OpWeightMsgUpdateParams = "op_weight_msg_update_params"
)
// ProposalMsgs defines the module weighted proposals' contents
// migrate to MsgUpdateParamsFactory instead
func ProposalMsgs() []simtypes.WeightedProposalMsg {
return []simtypes.WeightedProposalMsg{
simulation.NewWeightedProposalMsg(
@ -29,6 +30,7 @@ func ProposalMsgs() []simtypes.WeightedProposalMsg {
}
// SimulateMsgUpdateParams returns a random MsgUpdateParams
// will be removed in the future
func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
// use the default gov module account address as authority
var authority sdk.AccAddress = address.Module("gov")

View File

@ -19,6 +19,7 @@ import (
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -196,6 +197,7 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
}
// WeightedOperations returns the all the gov module operations with their respective weights.
// This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
am.registry,
@ -203,3 +205,10 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp
am.accountKeeper, am.bankKeeper, am.keeper,
)
}
// WeightedOperationsX registers weighted authz module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_grant", 100), simulation.MsgGrantFactory())
reg.Add(weights.Get("msg_revoke", 90), simulation.MsgRevokeFactory(am.keeper))
reg.Add(weights.Get("msg_exec", 90), simulation.MsgExecFactory(am.keeper))
}

View File

@ -0,0 +1,107 @@
package simulation
import (
"context"
"time"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/authz"
"github.com/cosmos/cosmos-sdk/x/authz/keeper"
banktype "github.com/cosmos/cosmos-sdk/x/bank/types"
)
func MsgGrantFactory() simsx.SimMsgFactoryFn[*authz.MsgGrant] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *authz.MsgGrant) {
granter := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
grantee := testData.AnyAccount(reporter, simsx.ExcludeAccounts(granter))
spendLimit := granter.LiquidBalance().RandSubsetCoins(reporter, simsx.WithSendEnabledCoins())
r := testData.Rand()
var expiration *time.Time
if t1 := r.Timestamp(); !t1.Before(simsx.BlockTime(ctx)) {
expiration = &t1
}
// pick random authorization
authorizations := []authz.Authorization{
banktype.NewSendAuthorization(spendLimit, nil),
authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktype.MsgSend{})),
}
randomAuthz := simsx.OneOf(r, authorizations)
msg, err := authz.NewMsgGrant(granter.Address, grantee.Address, randomAuthz, expiration)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
return []simsx.SimAccount{granter}, msg
}
}
func MsgExecFactory(k keeper.Keeper) simsx.SimMsgFactoryFn[*authz.MsgExec] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *authz.MsgExec) {
bankSendOnlyFilter := func(a authz.Authorization) bool {
_, ok := a.(*banktype.SendAuthorization)
return ok
}
granterAddr, granteeAddr, gAuthz := findGrant(ctx, k, reporter, bankSendOnlyFilter)
granter := testData.GetAccountbyAccAddr(reporter, granterAddr)
grantee := testData.GetAccountbyAccAddr(reporter, granteeAddr)
if reporter.IsSkipped() {
return nil, nil
}
amount := granter.LiquidBalance().RandSubsetCoins(reporter, simsx.WithSendEnabledCoins())
amount = amount.Min(gAuthz.(*banktype.SendAuthorization).SpendLimit)
if !amount.IsAllPositive() {
reporter.Skip("amount is not positive")
return nil, nil
}
payloadMsg := []sdk.Msg{banktype.NewMsgSend(granter.Address, grantee.Address, amount)}
msgExec := authz.NewMsgExec(grantee.Address, payloadMsg)
return []simsx.SimAccount{grantee}, &msgExec
}
}
func MsgRevokeFactory(k keeper.Keeper) simsx.SimMsgFactoryFn[*authz.MsgRevoke] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *authz.MsgRevoke) {
granterAddr, granteeAddr, auth := findGrant(ctx, k, reporter)
granter := testData.GetAccountbyAccAddr(reporter, granterAddr)
grantee := testData.GetAccountbyAccAddr(reporter, granteeAddr)
if reporter.IsSkipped() {
return nil, nil
}
msgExec := authz.NewMsgRevoke(granter.Address, grantee.Address, auth.MsgTypeURL())
return []simsx.SimAccount{granter}, &msgExec
}
}
func findGrant(
ctx context.Context,
k keeper.Keeper,
reporter simsx.SimulationReporter,
acceptFilter ...func(a authz.Authorization) bool,
) (granterAddr, granteeAddr sdk.AccAddress, auth authz.Authorization) {
var innerErr error
k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
a, err2 := grant.GetAuthorization()
if err2 != nil {
innerErr = err2
return true
}
for _, filter := range acceptFilter {
if !filter(a) {
return false
}
}
granterAddr, granteeAddr, auth = granter, grantee, a
return true
})
if innerErr != nil {
reporter.Skip(innerErr.Error())
return
}
if auth == nil {
reporter.Skip("no grant found")
}
return
}

View File

@ -19,6 +19,7 @@ import (
)
// authz message types
// will be removed in the future
var (
TypeMsgGrant = sdk.MsgTypeURL(&authz.MsgGrant{})
TypeMsgRevoke = sdk.MsgTypeURL(&authz.MsgRevoke{})
@ -26,6 +27,7 @@ var (
)
// Simulation operation weights constants
// will be removed in the future
const (
OpWeightMsgGrant = "op_weight_msg_grant"
OpWeightRevoke = "op_weight_msg_revoke"
@ -33,6 +35,7 @@ const (
)
// authz operations weights
// will be removed in the future
const (
WeightGrant = 100
WeightRevoke = 90
@ -40,6 +43,7 @@ const (
)
// WeightedOperations returns all the operations from the module with their respective weights
// will be removed in the future in favor of msg factory
func WeightedOperations(
registry cdctypes.InterfaceRegistry,
appParams simtypes.AppParams,
@ -86,6 +90,7 @@ func WeightedOperations(
}
// SimulateMsgGrant generates a MsgGrant with random values.
// will be removed in the future in favor of msg factory
func SimulateMsgGrant(
cdc *codec.ProtoCodec,
txCfg client.TxConfig,
@ -159,6 +164,7 @@ func generateRandomAuthorization(r *rand.Rand, spendLimit sdk.Coins) authz.Autho
}
// SimulateMsgRevoke generates a MsgRevoke with random values.
// will be removed in the future in favor of msg factory
func SimulateMsgRevoke(
cdc *codec.ProtoCodec,
txCfg client.TxConfig,
@ -228,6 +234,7 @@ func SimulateMsgRevoke(
}
// SimulateMsgExec generates a MsgExec with random values.
// will be removed in the future in favor of msg factory
func SimulateMsgExec(
cdc *codec.ProtoCodec,
txCfg client.TxConfig,

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
sdkmath "cosmossdk.io/math"
@ -86,11 +84,5 @@ func RandomizedGenState(simState *module.SimulationState) {
Supply: supply,
SendEnabled: sendEnabled,
}
paramsBytes, err := json.MarshalIndent(&bankGenesis.Params, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated bank parameters:\n%s\n", paramsBytes)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&bankGenesis)
}

View File

@ -17,6 +17,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
OpWeightMsgSend = "op_weight_msg_send"
OpWeightMsgMultiSend = "op_weight_msg_multisend"

View File

@ -11,14 +11,14 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
DefaultWeightMsgUpdateParams int = 100
OpWeightMsgUpdateParams = "op_weight_msg_update_params"
OpWeightMsgUpdateParams = "op_weight_msg_update_params"
)
// ProposalMsgs defines the module weighted proposals' contents
// Deprecated: migrate to MsgUpdateParamsFactory instead
// migrate to the msg factories instead, this method will be removed in the future
func ProposalMsgs() []simtypes.WeightedProposalMsg {
return []simtypes.WeightedProposalMsg{
simulation.NewWeightedProposalMsg(
@ -30,6 +30,7 @@ func ProposalMsgs() []simtypes.WeightedProposalMsg {
}
// SimulateMsgUpdateParams returns a random MsgUpdateParams
// will be removed in the future
func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
// use the default gov module account address as authority
var authority sdk.AccAddress = address.Module("gov")

View File

@ -17,6 +17,7 @@ import (
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -173,6 +174,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
}
// ProposalMsgs returns msgs used for governance proposals for simulations.
// migrate to ProposalMsgsX. This method is ignored when ProposalMsgsX exists and will be removed in the future.
func (AppModule) ProposalMsgs(_ module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}
@ -183,6 +185,7 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
}
// WeightedOperations returns the all the gov module operations with their respective weights.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
simState.AppParams, simState.Cdc, simState.TxConfig,
@ -190,6 +193,18 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp
)
}
// ProposalMsgsX registers governance proposal messages in the simulation registry.
func (AppModule) ProposalMsgsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_update_params", 100), simulation.MsgUpdateParamsFactory())
}
// WeightedOperationsX registers weighted distribution module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_set_withdraw_address", 50), simulation.MsgSetWithdrawAddressFactory(am.keeper))
reg.Add(weights.Get("msg_withdraw_delegation_reward", 50), simulation.MsgWithdrawDelegatorRewardFactory(am.keeper, am.stakingKeeper))
reg.Add(weights.Get("msg_withdraw_validator_commission", 50), simulation.MsgWithdrawValidatorCommissionFactory(am.keeper, am.stakingKeeper))
}
//
// App Wiring Setup
//

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
"cosmossdk.io/math"
@ -42,11 +40,5 @@ func RandomizedGenState(simState *module.SimulationState) {
WithdrawAddrEnabled: withdrawEnabled,
},
}
bz, err := json.MarshalIndent(&distrGenesis, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated distribution parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(&distrGenesis)
}

View File

@ -0,0 +1,125 @@
package simulation
import (
"context"
"errors"
"cosmossdk.io/collections"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/simsx"
"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)
func MsgSetWithdrawAddressFactory(k keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgSetWithdrawAddress] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgSetWithdrawAddress) {
switch enabled, err := k.GetWithdrawAddrEnabled(ctx); {
case err != nil:
reporter.Skip("error getting params")
return nil, nil
case !enabled:
reporter.Skip("withdrawal is not enabled")
return nil, nil
}
delegator := testData.AnyAccount(reporter)
withdrawer := testData.AnyAccount(reporter, simsx.ExcludeAccounts(delegator))
msg := types.NewMsgSetWithdrawAddress(delegator.Address, withdrawer.Address)
return []simsx.SimAccount{delegator}, msg
}
}
func MsgWithdrawDelegatorRewardFactory(k keeper.Keeper, sk types.StakingKeeper) simsx.SimMsgFactoryFn[*types.MsgWithdrawDelegatorReward] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgWithdrawDelegatorReward) {
delegator := testData.AnyAccount(reporter)
delegations, err := sk.GetAllDelegatorDelegations(ctx, delegator.Address)
switch {
case err != nil:
reporter.Skipf("error getting delegations: %v", err)
return nil, nil
case len(delegations) == 0:
reporter.Skip("no delegations found")
return nil, nil
}
delegation := delegations[testData.Rand().Intn(len(delegations))]
valAddr, err := sk.ValidatorAddressCodec().StringToBytes(delegation.GetValidatorAddr())
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
var valOper string
switch validator, err := sk.Validator(ctx, valAddr); {
case err != nil:
reporter.Skip(err.Error())
return nil, nil
case validator == nil:
reporter.Skipf("validator %s not found", delegation.GetValidatorAddr())
return nil, nil
default:
valOper = validator.GetOperator()
}
// get outstanding rewards so we can first check if the withdrawable coins are sendable
outstanding, err := k.GetValidatorOutstandingRewardsCoins(ctx, valAddr)
if err != nil {
reporter.Skipf("get outstanding rewards: %v", err)
return nil, nil
}
for _, v := range outstanding {
if !testData.IsSendEnabledDenom(v.Denom) {
reporter.Skipf("denom send not enabled: " + v.Denom)
return nil, nil
}
}
msg := types.NewMsgWithdrawDelegatorReward(delegator.AddressBech32, valOper)
return []simsx.SimAccount{delegator}, msg
}
}
func MsgWithdrawValidatorCommissionFactory(k keeper.Keeper, sk types.StakingKeeper) simsx.SimMsgFactoryFn[*types.MsgWithdrawValidatorCommission] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgWithdrawValidatorCommission) {
allVals, err := sk.GetAllValidators(ctx)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
val := simsx.OneOf(testData.Rand(), allVals)
valAddrBz, err := sk.ValidatorAddressCodec().StringToBytes(val.GetOperator())
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
commission, err := k.GetValidatorAccumulatedCommission(ctx, valAddrBz)
if err != nil && !errors.Is(err, collections.ErrNotFound) {
reporter.Skip(err.Error())
return nil, nil
}
if commission.Commission.IsZero() {
reporter.Skip("validator commission is zero")
return nil, nil
}
msg := types.NewMsgWithdrawValidatorCommission(val.GetOperator())
valAccount := testData.GetAccountbyAccAddr(reporter, valAddrBz)
return []simsx.SimAccount{valAccount}, msg
}
}
func MsgUpdateParamsFactory() simsx.SimMsgFactoryFn[*types.MsgUpdateParams] {
return func(_ context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgUpdateParams) {
r := testData.Rand()
params := types.DefaultParams()
params.CommunityTax = r.DecN(sdkmath.LegacyNewDec(1))
params.WithdrawAddrEnabled = r.Intn(2) == 0
return nil, &types.MsgUpdateParams{
Authority: testData.ModuleAccountAddress(reporter, "gov"),
Params: params,
}
}
}

View File

@ -19,6 +19,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
OpWeightMsgSetWithdrawAddress = "op_weight_msg_set_withdraw_address"
OpWeightMsgWithdrawDelegationReward = "op_weight_msg_withdraw_delegation_reward"
@ -32,6 +33,7 @@ const (
)
// WeightedOperations returns all the operations from the module with their respective weights
// migrate to the msg factories instead, this method will be removed in the future
func WeightedOperations(
appParams simtypes.AppParams,
_ codec.JSONCodec,
@ -87,6 +89,7 @@ func WeightedOperations(
}
// SimulateMsgSetWithdrawAddress generates a MsgSetWithdrawAddress with random values.
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgSetWithdrawAddress(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
@ -127,6 +130,7 @@ func SimulateMsgSetWithdrawAddress(txConfig client.TxConfig, ak types.AccountKee
}
// SimulateMsgWithdrawDelegatorReward generates a MsgWithdrawDelegatorReward with random values.
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgWithdrawDelegatorReward(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
@ -178,6 +182,7 @@ func SimulateMsgWithdrawDelegatorReward(txConfig client.TxConfig, ak types.Accou
}
// SimulateMsgWithdrawValidatorCommission generates a MsgWithdrawValidatorCommission with random values.
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgWithdrawValidatorCommission(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
@ -239,6 +244,7 @@ func SimulateMsgWithdrawValidatorCommission(txConfig client.TxConfig, ak types.A
// SimulateMsgFundCommunityPool simulates MsgFundCommunityPool execution where
// a random account sends a random amount of its funds to the community pool.
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgFundCommunityPool(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,

View File

@ -13,13 +13,14 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
DefaultWeightMsgUpdateParams int = 50
OpWeightMsgUpdateParams = "op_weight_msg_update_params"
OpWeightMsgUpdateParams = "op_weight_msg_update_params"
)
// ProposalMsgs defines the module weighted proposals' contents
// migrate to MsgUpdateParamsFactory instead
func ProposalMsgs() []simtypes.WeightedProposalMsg {
return []simtypes.WeightedProposalMsg{
simulation.NewWeightedProposalMsg(
@ -31,6 +32,7 @@ func ProposalMsgs() []simtypes.WeightedProposalMsg {
}
// SimulateMsgUpdateParams returns a random MsgUpdateParams
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
// use the default gov module account address as authority
var authority sdk.AccAddress = address.Module("gov")

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
"cosmossdk.io/x/evidence/exported"
@ -27,11 +25,5 @@ func RandomizedGenState(simState *module.SimulationState) {
simState.AppParams.GetOrGenerate(evidence, &ev, simState.Rand, func(r *rand.Rand) { ev = GenEvidences(r, simState.Accounts) })
evidenceGenesis := types.NewGenesisState(ev)
bz, err := json.MarshalIndent(&evidenceGenesis, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(evidenceGenesis)
}

View File

@ -22,6 +22,7 @@ import (
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -200,9 +201,19 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
}
// WeightedOperations returns all the feegrant module operations with their respective weights.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
am.registry, simState.AppParams, simState.Cdc, simState.TxConfig,
am.accountKeeper, am.bankKeeper, am.keeper, am.ac,
)
}
// WeightedOperationsX registers weighted feegrant module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_grant_fee_allowance", 100), simulation.MsgGrantAllowanceFactory(am.keeper))
// use old misspelled OpWeightMsgRevokeAllowance key for legacy reasons but default to the new key
// so that we can replace it at some point
w := weights.Get("msg_grant_revoke_allowance", weights.Get("msg_revoke_allowance", 100))
reg.Add(w, simulation.MsgRevokeAllowanceFactory(am.keeper))
}

View File

@ -0,0 +1,65 @@
package simulation
import (
"context"
"time"
"cosmossdk.io/x/feegrant"
"cosmossdk.io/x/feegrant/keeper"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func MsgGrantAllowanceFactory(k keeper.Keeper) simsx.SimMsgFactoryFn[*feegrant.MsgGrantAllowance] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *feegrant.MsgGrantAllowance) {
granter := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
grantee := testData.AnyAccount(reporter, simsx.ExcludeAccounts(granter))
if reporter.IsSkipped() {
return nil, nil
}
if f, _ := k.GetAllowance(ctx, granter.Address, grantee.Address); f != nil {
reporter.Skip("fee allowance exists")
return nil, nil
}
coins := granter.LiquidBalance().RandSubsetCoins(reporter, simsx.WithSendEnabledCoins())
oneYear := blockTime(ctx).AddDate(1, 0, 0)
msg, err := feegrant.NewMsgGrantAllowance(
&feegrant.BasicAllowance{SpendLimit: coins, Expiration: &oneYear},
granter.Address,
grantee.Address,
)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
return []simsx.SimAccount{granter}, msg
}
}
func MsgRevokeAllowanceFactory(k keeper.Keeper) simsx.SimMsgFactoryFn[*feegrant.MsgRevokeAllowance] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *feegrant.MsgRevokeAllowance) {
var gotGrant *feegrant.Grant
if err := k.IterateAllFeeAllowances(ctx, func(grant feegrant.Grant) bool {
gotGrant = &grant
return true
}); err != nil {
reporter.Skip(err.Error())
return nil, nil
}
if gotGrant == nil {
reporter.Skip("no grant found")
return nil, nil
}
granter := testData.GetAccount(reporter, gotGrant.Granter)
grantee := testData.GetAccount(reporter, gotGrant.Grantee)
msg := feegrant.NewMsgRevokeAllowance(granter.Address, grantee.Address)
return []simsx.SimAccount{granter}, &msg
}
}
// temporary solution. use simsx.BlockTime when available
func blockTime(ctx context.Context) time.Time {
return sdk.UnwrapSDKContext(ctx).BlockTime()
}

View File

@ -17,6 +17,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
OpWeightMsgGrantAllowance = "op_weight_msg_grant_fee_allowance"
OpWeightMsgRevokeAllowance = "op_weight_msg_grant_revoke_allowance"
@ -24,11 +25,13 @@ const (
DefaultWeightRevokeAllowance int = 100
)
// will be removed in the future
var (
TypeMsgGrantAllowance = sdk.MsgTypeURL(&feegrant.MsgGrantAllowance{})
TypeMsgRevokeAllowance = sdk.MsgTypeURL(&feegrant.MsgRevokeAllowance{})
)
// will be removed in the future in favor of msg factory
func WeightedOperations(
registry codectypes.InterfaceRegistry,
appParams simtypes.AppParams,
@ -71,6 +74,7 @@ func WeightedOperations(
}
// SimulateMsgGrantAllowance generates MsgGrantAllowance with random values.
// will be removed in the future in favor of msg factory
func SimulateMsgGrantAllowance(
cdc *codec.ProtoCodec,
txConfig client.TxConfig,
@ -126,6 +130,7 @@ func SimulateMsgGrantAllowance(
}
// SimulateMsgRevokeAllowance generates a MsgRevokeAllowance with random values.
// will be removed in the future in favor of msg factory
func SimulateMsgRevokeAllowance(
cdc *codec.ProtoCodec,
txConfig client.TxConfig,

View File

@ -22,6 +22,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -325,11 +326,13 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
// ProposalContents returns all the gov content functions used to
// simulate governance proposals.
// migrate to ProposalMsgsX. This method is ignored when ProposalMsgsX exists and will be removed in the future.
func (AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { //nolint:staticcheck // used for legacy testing
return simulation.ProposalContents()
}
// ProposalMsgs returns all the gov msgs used to simulate governance proposals.
// migrate to ProposalMsgsX. This method is ignored when ProposalMsgsX exists and will be removed in the future.
func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}
@ -340,6 +343,7 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
}
// WeightedOperations returns the all the gov module operations with their respective weights.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
simState.AppParams, simState.TxConfig,
@ -347,3 +351,29 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp
simState.ProposalMsgs, simState.LegacyProposalContents,
)
}
// ProposalMsgsX registers governance proposal messages in the simulation registry.
func (AppModule) ProposalMsgsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("submit_text_proposal", 5), simulation.TextProposalFactory())
}
// WeightedOperationsX registers weighted gov module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry, proposalMsgIter simsx.WeightedProposalMsgIter,
legacyProposals []simtypes.WeightedProposalContent, //nolint:staticcheck // used for legacy proposal types
) {
// submit proposal for each payload message
for weight, factory := range proposalMsgIter {
// use a ratio so that we don't flood with gov ops
reg.Add(weight/25, simulation.MsgSubmitProposalFactory(am.keeper, factory))
}
for _, wContent := range legacyProposals {
reg.Add(weights.Get(wContent.AppParamsKey(), uint32(wContent.DefaultWeight())), simulation.MsgSubmitLegacyProposalFactory(am.keeper, wContent.ContentSimulatorFn()))
}
state := simulation.NewSharedState()
reg.Add(weights.Get("msg_deposit", 100), simulation.MsgDepositFactory(am.keeper, state))
reg.Add(weights.Get("msg_vote", 67), simulation.MsgVoteFactory(am.keeper, state))
reg.Add(weights.Get("msg_weighted_vote", 33), simulation.MsgWeightedVoteFactory(am.keeper, state))
reg.Add(weights.Get("cancel_proposal", 5), simulation.MsgCancelProposalFactory(am.keeper, state))
reg.Add(weights.Get("legacy_text_proposal", 5), simulation.MsgSubmitLegacyProposalFactory(am.keeper, simulation.SimulateLegacyTextProposalContent))
}

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
"time"
@ -144,11 +142,5 @@ func RandomizedGenState(simState *module.SimulationState) {
startingProposalID,
v1.NewParams(minDeposit, expeditedMinDeposit, depositPeriod, votingPeriod, expeditedVotingPeriod, quorum.String(), threshold.String(), expitedVotingThreshold.String(), veto.String(), minInitialDepositRatio.String(), proposalCancelRate.String(), "", simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, minDepositRatio.String()),
)
bz, err := json.MarshalIndent(&govGenesis, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated governance parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(govGenesis)
}

View File

@ -0,0 +1,370 @@
package simulation
import (
"context"
"math"
"math/rand"
"sync/atomic"
"time"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/gov/keeper"
v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
func MsgDepositFactory(k *keeper.Keeper, sharedState *SharedState) simsx.SimMsgFactoryFn[*v1.MsgDeposit] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *v1.MsgDeposit) {
r := testData.Rand()
proposalID, ok := randomProposalID(r.Rand, k, sdk.UnwrapSDKContext(ctx), v1.StatusDepositPeriod, sharedState)
if !ok {
reporter.Skip("no proposal in deposit state")
return nil, nil
}
proposal, err := k.Proposals.Get(ctx, proposalID)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
// calculate deposit amount
deposit := randDeposit(ctx, proposal, k, r, reporter)
if reporter.IsSkipped() {
return nil, nil
}
from := testData.AnyAccount(reporter, simsx.WithLiquidBalanceGTE(deposit))
return []simsx.SimAccount{from}, v1.NewMsgDeposit(from.Address, proposalID, sdk.NewCoins(deposit))
}
}
func MsgVoteFactory(k *keeper.Keeper, sharedState *SharedState) simsx.SimMsgFactoryFn[*v1.MsgVote] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *v1.MsgVote) {
r := testData.Rand()
proposalID, ok := randomProposalID(r.Rand, k, sdk.UnwrapSDKContext(ctx), v1.StatusVotingPeriod, sharedState)
if !ok {
reporter.Skip("no proposal in voting state")
return nil, nil
}
from := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
msg := v1.NewMsgVote(from.Address, proposalID, randomVotingOption(r.Rand), "")
return []simsx.SimAccount{from}, msg
}
}
func MsgWeightedVoteFactory(k *keeper.Keeper, sharedState *SharedState) simsx.SimMsgFactoryFn[*v1.MsgVoteWeighted] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *v1.MsgVoteWeighted) {
r := testData.Rand()
proposalID, ok := randomProposalID(r.Rand, k, sdk.UnwrapSDKContext(ctx), v1.StatusVotingPeriod, sharedState)
if !ok {
reporter.Skip("no proposal in deposit state")
return nil, nil
}
from := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
msg := v1.NewMsgVoteWeighted(from.Address, proposalID, randomWeightedVotingOptions(r.Rand), "")
return []simsx.SimAccount{from}, msg
}
}
func MsgCancelProposalFactory(k *keeper.Keeper, sharedState *SharedState) simsx.SimMsgFactoryFn[*v1.MsgCancelProposal] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *v1.MsgCancelProposal) {
r := testData.Rand()
status := simsx.OneOf(r, []v1.ProposalStatus{v1.StatusDepositPeriod, v1.StatusVotingPeriod})
proposalID, ok := randomProposalID(r.Rand, k, sdk.UnwrapSDKContext(ctx), status, sharedState)
if !ok {
reporter.Skip("no proposal in deposit state")
return nil, nil
}
proposal, err := k.Proposals.Get(ctx, proposalID)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
// is cancellable? copied from keeper
if proposal.VotingEndTime != nil && proposal.VotingEndTime.Before(sdk.UnwrapSDKContext(ctx).BlockTime()) {
reporter.Skip("not cancellable anymore")
return nil, nil
}
from := testData.GetAccount(reporter, proposal.Proposer)
if from.LiquidBalance().Empty() {
reporter.Skip("proposer is broke")
return nil, nil
}
msg := v1.NewMsgCancelProposal(proposalID, from.AddressBech32)
return []simsx.SimAccount{from}, msg
}
}
func MsgSubmitLegacyProposalFactory(k *keeper.Keeper, contentSimFn simtypes.ContentSimulatorFn) simsx.SimMsgFactoryX { //nolint:staticcheck // used for legacy testing
return simsx.NewSimMsgFactoryWithFutureOps[*v1.MsgSubmitProposal](func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter, fOpsReg simsx.FutureOpsRegistry) ([]simsx.SimAccount, *v1.MsgSubmitProposal) {
// 1) submit proposal now
accs := testData.AllAccounts()
content := contentSimFn(testData.Rand().Rand, sdk.UnwrapSDKContext(ctx), accs)
if content == nil {
reporter.Skip("content is nil")
return nil, nil
}
govacc := must(testData.AddressCodec().BytesToString(k.GetGovernanceAccount(ctx).GetAddress()))
contentMsg := must(v1.NewLegacyContent(content, govacc))
return submitProposalWithVotesScheduled(ctx, k, testData, reporter, fOpsReg, contentMsg)
})
}
func MsgSubmitProposalFactory(k *keeper.Keeper, payloadFactory simsx.FactoryMethod) simsx.SimMsgFactoryX {
return simsx.NewSimMsgFactoryWithFutureOps[*v1.MsgSubmitProposal](func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter, fOpsReg simsx.FutureOpsRegistry) ([]simsx.SimAccount, *v1.MsgSubmitProposal) {
_, proposalMsg := payloadFactory(ctx, testData, reporter)
return submitProposalWithVotesScheduled(ctx, k, testData, reporter, fOpsReg, proposalMsg)
})
}
func submitProposalWithVotesScheduled(
ctx context.Context,
k *keeper.Keeper,
testData *simsx.ChainDataSource,
reporter simsx.SimulationReporter,
fOpsReg simsx.FutureOpsRegistry,
proposalMsgs ...sdk.Msg,
) ([]simsx.SimAccount, *v1.MsgSubmitProposal) {
r := testData.Rand()
expedited := r.Bool()
params := must(k.Params.Get(ctx))
minDeposits := params.MinDeposit
if expedited {
minDeposits = params.ExpeditedMinDeposit
}
minDeposit := r.Coin(minDeposits)
minDepositRatio := must(sdkmath.LegacyNewDecFromStr(params.GetMinDepositRatio()))
threshold := minDeposit.Amount.ToLegacyDec().Mul(minDepositRatio).TruncateInt()
minDepositPercent := must(sdkmath.LegacyNewDecFromStr(params.MinInitialDepositRatio))
minAmount := sdkmath.LegacyNewDecFromInt(minDeposit.Amount).Mul(minDepositPercent).TruncateInt()
amount, err := r.PositiveSDKIntn(minDeposit.Amount.Sub(minAmount))
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
if amount.LT(threshold) {
reporter.Skip("below threshold amount for proposal")
return nil, nil
}
deposit := minDeposit
// deposit := sdk.Coin{Amount: amount.Add(minAmount), Denom: minDeposit.Denom}
proposer := testData.AnyAccount(reporter, simsx.WithLiquidBalanceGTE(deposit))
if reporter.IsSkipped() || !proposer.LiquidBalance().BlockAmount(deposit) {
return nil, nil
}
msg, err := v1.NewMsgSubmitProposal(
proposalMsgs,
sdk.Coins{deposit},
proposer.AddressBech32,
r.StringN(100),
r.StringN(100),
r.StringN(100),
expedited,
)
if err != nil {
reporter.Skip("unable to generate a submit proposal msg")
return nil, nil
}
// futureOps
var (
// The states are:
// column 1: All validators vote
// column 2: 90% vote
// column 3: 75% vote
// column 4: 40% vote
// column 5: 15% vote
// column 6: no one votes
// All columns sum to 100 for simplicity, values chosen by @valardragon semi-arbitrarily,
// feel free to change.
numVotesTransitionMatrix = must(simulation.CreateTransitionMatrix([][]int{
{20, 10, 0, 0, 0, 0},
{55, 50, 20, 10, 0, 0},
{25, 25, 30, 25, 30, 15},
{0, 15, 30, 25, 30, 30},
{0, 0, 20, 30, 30, 30},
{0, 0, 0, 10, 10, 25},
}))
statePercentageArray = []float64{1, .9, .75, .4, .15, 0}
curNumVotesState = 1
)
// get the submitted proposal ID
proposalID := must(k.ProposalID.Peek(ctx))
// 2) Schedule operations for votes
// 2.1) first pick a number of people to vote.
curNumVotesState = numVotesTransitionMatrix.NextState(r.Rand, curNumVotesState)
numVotes := int(math.Ceil(float64(testData.AccountsCount()) * statePercentageArray[curNumVotesState]))
// 2.2) select who votes and when
whoVotes := r.Perm(testData.AccountsCount())
// didntVote := whoVotes[numVotes:]
whoVotes = whoVotes[:numVotes]
votingPeriod := params.VotingPeriod
// future ops so that votes do not flood the sims.
if r.Intn(100) == 1 { // 1% chance
now := simsx.BlockTime(ctx)
for i := 0; i < numVotes; i++ {
var vF simsx.SimMsgFactoryFn[*v1.MsgVote] = func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *v1.MsgVote) {
switch p, err := k.Proposals.Get(ctx, proposalID); {
case err != nil:
reporter.Skip(err.Error())
return nil, nil
case p.Status != v1.ProposalStatus_PROPOSAL_STATUS_VOTING_PERIOD:
reporter.Skip("proposal not in voting period")
return nil, nil
}
voter := testData.AccountAt(reporter, whoVotes[i])
msg := v1.NewMsgVote(voter.Address, proposalID, randomVotingOption(r.Rand), "")
return []simsx.SimAccount{voter}, msg
}
whenVote := now.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second)
fOpsReg.Add(whenVote, vF)
}
}
return []simsx.SimAccount{proposer}, msg
}
// TextProposalFactory returns a random text proposal content.
// A text proposal is a proposal that contains no msgs.
func TextProposalFactory() simsx.SimMsgFactoryFn[sdk.Msg] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, sdk.Msg) {
return nil, nil
}
}
func randDeposit(
ctx context.Context,
proposal v1.Proposal,
k *keeper.Keeper,
r *simsx.XRand,
reporter simsx.SimulationReporter,
) sdk.Coin {
params, err := k.Params.Get(ctx)
if err != nil {
reporter.Skipf("gov params: %s", err)
return sdk.Coin{}
}
minDeposits := params.MinDeposit
if proposal.Expedited {
minDeposits = params.ExpeditedMinDeposit
}
minDeposit := simsx.OneOf(r, minDeposits)
minDepositRatio, err := sdkmath.LegacyNewDecFromStr(params.GetMinDepositRatio())
if err != nil {
reporter.Skip(err.Error())
return sdk.Coin{}
}
threshold := minDeposit.Amount.ToLegacyDec().Mul(minDepositRatio).TruncateInt()
depositAmount, err := r.PositiveSDKIntInRange(threshold, minDeposit.Amount)
if err != nil {
reporter.Skipf("deposit amount: %s", err)
return sdk.Coin{}
}
return sdk.Coin{Denom: minDeposit.Denom, Amount: depositAmount}
}
// Pick a random proposal ID between the initial proposal ID
// (defined in gov GenesisState) and the latest proposal ID
// that matches a given Status.
// It does not provide a default ID.
func randomProposalID(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context, status v1.ProposalStatus, s *SharedState) (proposalID uint64, found bool) {
proposalID, _ = k.ProposalID.Peek(ctx)
if initialProposalID := s.getMinProposalID(); initialProposalID == unsetProposalID {
s.setMinProposalID(proposalID)
} else if initialProposalID < proposalID {
proposalID = uint64(simtypes.RandIntBetween(r, int(initialProposalID), int(proposalID)))
}
proposal, err := k.Proposals.Get(ctx, proposalID)
if err != nil || proposal.Status != status {
return proposalID, false
}
return proposalID, true
}
// Pick a random weighted voting options
func randomWeightedVotingOptions(r *rand.Rand) v1.WeightedVoteOptions {
w1 := r.Intn(100 + 1)
w2 := r.Intn(100 - w1 + 1)
w3 := r.Intn(100 - w1 - w2 + 1)
w4 := 100 - w1 - w2 - w3
weightedVoteOptions := v1.WeightedVoteOptions{}
if w1 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionYes,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w1), 2).String(),
})
}
if w2 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionAbstain,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w2), 2).String(),
})
}
if w3 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionNo,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w3), 2).String(),
})
}
if w4 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionNoWithVeto,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w4), 2).String(),
})
}
return weightedVoteOptions
}
func randomVotingOption(r *rand.Rand) v1.VoteOption {
switch r.Intn(4) {
case 0:
return v1.OptionYes
case 1:
return v1.OptionAbstain
case 2:
return v1.OptionNo
case 3:
return v1.OptionNoWithVeto
default:
panic("invalid vote option")
}
}
func must[T any](r T, err error) T {
if err != nil {
panic(err)
}
return r
}
const unsetProposalID = 100000000000000
// SharedState shared state between message invocations
type SharedState struct {
minProposalID atomic.Uint64
}
// NewSharedState constructor
func NewSharedState() *SharedState {
r := &SharedState{}
r.setMinProposalID(unsetProposalID)
return r
}
func (s *SharedState) getMinProposalID() uint64 {
return s.minProposalID.Load()
}
func (s *SharedState) setMinProposalID(id uint64) {
s.minProposalID.Store(id)
}

View File

@ -3,7 +3,6 @@ package simulation
import (
"math"
"math/rand"
"sync/atomic"
"time"
sdkmath "cosmossdk.io/math"
@ -19,9 +18,8 @@ import (
"github.com/cosmos/cosmos-sdk/x/simulation"
)
const unsetProposalID = 100000000000000
// Governance message types and routes
// will be removed in the future
var (
TypeMsgDeposit = sdk.MsgTypeURL(&v1.MsgDeposit{})
TypeMsgVote = sdk.MsgTypeURL(&v1.MsgVote{})
@ -31,6 +29,7 @@ var (
)
// Simulation operation weights constants
// will be removed in the future
const (
OpWeightMsgDeposit = "op_weight_msg_deposit"
OpWeightMsgVote = "op_weight_msg_vote"
@ -44,27 +43,8 @@ const (
DefaultWeightMsgCancelProposal = 5
)
// sharedState shared state between message invocations
type sharedState struct {
minProposalID atomic.Uint64
}
// newSharedState constructor
func newSharedState() *sharedState {
r := &sharedState{}
r.setMinProposalID(unsetProposalID)
return r
}
func (s *sharedState) getMinProposalID() uint64 {
return s.minProposalID.Load()
}
func (s *sharedState) setMinProposalID(id uint64) {
s.minProposalID.Store(id)
}
// WeightedOperations returns all the operations from the module with their respective weights
// will be removed in the future in favor of msg factory
func WeightedOperations(
appParams simtypes.AppParams,
txGen client.TxConfig,
@ -138,7 +118,7 @@ func WeightedOperations(
),
)
}
state := newSharedState()
state := NewSharedState()
wGovOps := simulation.WeightedOperations{
simulation.NewWeightedOperation(
weightMsgDeposit,
@ -164,6 +144,7 @@ func WeightedOperations(
// SimulateMsgSubmitProposal simulates creating a msg Submit Proposal
// voting on the proposal, and subsequently slashing the proposal. It is implemented using
// future operations.
// will be removed in the future in favor of msg factory
func SimulateMsgSubmitProposal(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -186,6 +167,7 @@ func SimulateMsgSubmitProposal(
// SimulateMsgSubmitLegacyProposal simulates creating a msg Submit Proposal
// voting on the proposal, and subsequently slashing the proposal. It is implemented using
// future operations.
// will be removed in the future in favor of msg factory
func SimulateMsgSubmitLegacyProposal(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -310,7 +292,7 @@ func simulateMsgSubmitProposal(
whoVotes = whoVotes[:numVotes]
params, _ := k.Params.Get(ctx)
votingPeriod := params.VotingPeriod
s := newSharedState()
s := NewSharedState()
fops := make([]simtypes.FutureOperation, numVotes+1)
for i := 0; i < numVotes; i++ {
whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second)
@ -325,14 +307,14 @@ func simulateMsgSubmitProposal(
}
// SimulateMsgDeposit generates a MsgDeposit with random values.
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgDeposit(
txGen client.TxConfig,
ak types.AccountKeeper,
bk types.BankKeeper,
k *keeper.Keeper,
) simtypes.Operation {
return simulateMsgDeposit(txGen, ak, bk, k, newSharedState())
return simulateMsgDeposit(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgDeposit(
@ -340,7 +322,7 @@ func simulateMsgDeposit(
ak types.AccountKeeper,
bk types.BankKeeper,
k *keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
@ -403,7 +385,7 @@ func SimulateMsgVote(
bk types.BankKeeper,
k *keeper.Keeper,
) simtypes.Operation {
return simulateMsgVote(txGen, ak, bk, k, newSharedState())
return simulateMsgVote(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgVote(
@ -411,7 +393,7 @@ func simulateMsgVote(
ak types.AccountKeeper,
bk types.BankKeeper,
k *keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return operationSimulateMsgVote(txGen, ak, bk, k, simtypes.Account{}, -1, s)
}
@ -423,7 +405,7 @@ func operationSimulateMsgVote(
k *keeper.Keeper,
simAccount simtypes.Account,
proposalIDInt int64,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
@ -471,13 +453,14 @@ func operationSimulateMsgVote(
}
// SimulateMsgVoteWeighted generates a MsgVoteWeighted with random values.
// will be removed in the future in favor of msg factory
func SimulateMsgVoteWeighted(
txGen client.TxConfig,
ak types.AccountKeeper,
bk types.BankKeeper,
k *keeper.Keeper,
) simtypes.Operation {
return simulateMsgVoteWeighted(txGen, ak, bk, k, newSharedState())
return simulateMsgVoteWeighted(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgVoteWeighted(
@ -485,7 +468,7 @@ func simulateMsgVoteWeighted(
ak types.AccountKeeper,
bk types.BankKeeper,
k *keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return operationSimulateMsgVoteWeighted(txGen, ak, bk, k, simtypes.Account{}, -1, s)
}
@ -497,7 +480,7 @@ func operationSimulateMsgVoteWeighted(
k *keeper.Keeper,
simAccount simtypes.Account,
proposalIDInt int64,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
@ -545,6 +528,7 @@ func operationSimulateMsgVoteWeighted(
}
// SimulateMsgCancelProposal generates a MsgCancelProposal.
// will be removed in the future in favor of msg factory
func SimulateMsgCancelProposal(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -674,72 +658,3 @@ func randomProposal(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context) *v1.Proposa
randomIndex := r.Intn(len(proposals))
return proposals[randomIndex]
}
// Pick a random proposal ID between the initial proposal ID
// (defined in gov GenesisState) and the latest proposal ID
// that matches a given Status.
// It does not provide a default ID.
func randomProposalID(r *rand.Rand, k *keeper.Keeper, ctx sdk.Context, status v1.ProposalStatus, s *sharedState) (proposalID uint64, found bool) {
proposalID, _ = k.ProposalID.Peek(ctx)
if initialProposalID := s.getMinProposalID(); initialProposalID == unsetProposalID {
s.setMinProposalID(proposalID)
} else if initialProposalID < proposalID {
proposalID = uint64(simtypes.RandIntBetween(r, int(initialProposalID), int(proposalID)))
}
proposal, err := k.Proposals.Get(ctx, proposalID)
if err != nil || proposal.Status != status {
return proposalID, false
}
return proposalID, true
}
// Pick a random voting option
func randomVotingOption(r *rand.Rand) v1.VoteOption {
switch r.Intn(4) {
case 0:
return v1.OptionYes
case 1:
return v1.OptionAbstain
case 2:
return v1.OptionNo
case 3:
return v1.OptionNoWithVeto
default:
panic("invalid vote option")
}
}
// Pick a random weighted voting options
func randomWeightedVotingOptions(r *rand.Rand) v1.WeightedVoteOptions {
w1 := r.Intn(100 + 1)
w2 := r.Intn(100 - w1 + 1)
w3 := r.Intn(100 - w1 - w2 + 1)
w4 := 100 - w1 - w2 - w3
weightedVoteOptions := v1.WeightedVoteOptions{}
if w1 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionYes,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w1), 2).String(),
})
}
if w2 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionAbstain,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w2), 2).String(),
})
}
if w3 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionNo,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w3), 2).String(),
})
}
if w4 > 0 {
weightedVoteOptions = append(weightedVoteOptions, &v1.WeightedVoteOption{
Option: v1.OptionNoWithVeto,
Weight: sdkmath.LegacyNewDecWithPrec(int64(w4), 2).String(),
})
}
return weightedVoteOptions
}

View File

@ -10,9 +10,11 @@ import (
)
// OpWeightSubmitTextProposal app params key for text proposal
// will be removed in the future
const OpWeightSubmitTextProposal = "op_weight_submit_text_proposal"
// ProposalMsgs defines the module weighted proposals' contents
// will be removed in the future
func ProposalMsgs() []simtypes.WeightedProposalMsg {
return []simtypes.WeightedProposalMsg{
simulation.NewWeightedProposalMsg(
@ -25,6 +27,7 @@ func ProposalMsgs() []simtypes.WeightedProposalMsg {
// SimulateTextProposal returns a random text proposal content.
// A text proposal is a proposal that contains no msgs.
// will be removed in the future
func SimulateTextProposal(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
return nil
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"time"
"cosmossdk.io/core/address"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
storetypes "cosmossdk.io/store/types"
@ -221,6 +222,10 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s", group.ModuleName))
}
func (k Keeper) AddressCodec() address.Codec {
return k.accKeeper.AddressCodec()
}
// GetGroupSequence returns the current value of the group table sequence
func (k Keeper) GetGroupSequence(ctx sdk.Context) uint64 {
return k.groupTable.Sequence().CurVal(ctx.KVStore(k.key))

View File

@ -18,6 +18,7 @@ import (
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -161,6 +162,7 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
}
// WeightedOperations returns the all the gov module operations with their respective weights.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
am.registry,
@ -169,6 +171,26 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp
)
}
// WeightedOperationsX registers weighted group module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry) {
s := simulation.NewSharedState()
// note: using old keys for backwards compatibility
reg.Add(weights.Get("msg_create_group", 100), simulation.MsgCreateGroupFactory())
reg.Add(weights.Get("msg_update_group_admin", 5), simulation.MsgUpdateGroupAdminFactory(am.keeper, s))
reg.Add(weights.Get("msg_update_group_metadata", 5), simulation.MsgUpdateGroupMetadataFactory(am.keeper, s))
reg.Add(weights.Get("msg_update_group_members", 5), simulation.MsgUpdateGroupMembersFactory(am.keeper, s))
reg.Add(weights.Get("msg_create_group_account", 50), simulation.MsgCreateGroupPolicyFactory(am.keeper, s))
reg.Add(weights.Get("msg_create_group_with_policy", 50), simulation.MsgCreateGroupWithPolicyFactory())
reg.Add(weights.Get("msg_update_group_account_admin", 5), simulation.MsgUpdateGroupPolicyAdminFactory(am.keeper, s))
reg.Add(weights.Get("msg_update_group_account_decision_policy", 5), simulation.MsgUpdateGroupPolicyDecisionPolicyFactory(am.keeper, s))
reg.Add(weights.Get("msg_update_group_account_metadata", 5), simulation.MsgUpdateGroupPolicyMetadataFactory(am.keeper, s))
reg.Add(weights.Get("msg_submit_proposal", 2*90), simulation.MsgSubmitProposalFactory(am.keeper, s))
reg.Add(weights.Get("msg_withdraw_proposal", 20), simulation.MsgWithdrawProposalFactory(am.keeper, s))
reg.Add(weights.Get("msg_vote", 90), simulation.MsgVoteFactory(am.keeper, s))
reg.Add(weights.Get("msg_exec", 90), simulation.MsgExecFactory(am.keeper, s))
reg.Add(weights.Get("msg_leave_group", 5), simulation.MsgLeaveGroupFactory(am.keeper, s))
}
//
// App Wiring Setup
//

View File

@ -0,0 +1,490 @@
package simulation
import (
"context"
"slices"
"strconv"
"sync/atomic"
"time"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/group"
"github.com/cosmos/cosmos-sdk/x/group/keeper"
)
func MsgCreateGroupFactory() simsx.SimMsgFactoryFn[*group.MsgCreateGroup] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgCreateGroup) {
admin := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
members := genGroupMembersX(testData, reporter)
msg := &group.MsgCreateGroup{Admin: admin.AddressBech32, Members: members, Metadata: testData.Rand().StringN(10)}
return []simsx.SimAccount{admin}, msg
}
}
func MsgCreateGroupPolicyFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgCreateGroupPolicy] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgCreateGroupPolicy) {
groupInfo := randomGroupX(ctx, k, testData, reporter, s)
groupAdmin := testData.GetAccount(reporter, groupInfo.Admin)
if reporter.IsSkipped() {
return nil, nil
}
groupID := groupInfo.Id
r := testData.Rand()
msg, err := group.NewMsgCreateGroupPolicy(
groupAdmin.Address,
groupID,
r.StringN(10),
&group.ThresholdDecisionPolicy{
Threshold: strconv.Itoa(r.IntInRange(1, 10)),
Windows: &group.DecisionPolicyWindows{
VotingPeriod: time.Second * time.Duration(30*24*60*60),
},
},
)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
return []simsx.SimAccount{groupAdmin}, msg
}
}
func MsgCreateGroupWithPolicyFactory() simsx.SimMsgFactoryFn[*group.MsgCreateGroupWithPolicy] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgCreateGroupWithPolicy) {
admin := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
members := genGroupMembersX(testData, reporter)
r := testData.Rand()
msg := &group.MsgCreateGroupWithPolicy{
Admin: admin.AddressBech32,
Members: members,
GroupMetadata: r.StringN(10),
GroupPolicyMetadata: r.StringN(10),
GroupPolicyAsAdmin: r.Float32() < 0.5,
}
decisionPolicy := &group.ThresholdDecisionPolicy{
Threshold: strconv.Itoa(r.IntInRange(1, 10)),
Windows: &group.DecisionPolicyWindows{
VotingPeriod: time.Second * time.Duration(30*24*60*60),
},
}
if err := msg.SetDecisionPolicy(decisionPolicy); err != nil {
reporter.Skip(err.Error())
return nil, nil
}
return []simsx.SimAccount{admin}, msg
}
}
func MsgWithdrawProposalFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgWithdrawProposal] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgWithdrawProposal) {
groupInfo, groupPolicy := randomGroupPolicyX(ctx, testData, reporter, k, s)
if reporter.IsSkipped() {
return nil, nil
}
policy, err := groupPolicy.GetDecisionPolicy()
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
err = policy.Validate(*groupInfo, group.DefaultConfig())
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
proposalsResult, err := k.ProposalsByGroupPolicy(ctx,
&group.QueryProposalsByGroupPolicyRequest{Address: groupPolicy.Address},
)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
now := simsx.BlockTime(ctx)
proposal := simsx.First(proposalsResult.GetProposals(), func(p *group.Proposal) bool {
return p.Status == group.PROPOSAL_STATUS_SUBMITTED && p.VotingPeriodEnd.After(now)
})
if proposal == nil {
reporter.Skip("no proposal found")
return nil, nil
}
// select a random proposer
r := testData.Rand()
proposer := testData.GetAccount(reporter, simsx.OneOf(r, (*proposal).Proposers))
msg := &group.MsgWithdrawProposal{
ProposalId: (*proposal).Id,
Address: proposer.AddressBech32,
}
return []simsx.SimAccount{proposer}, msg
}
}
func MsgVoteFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgVote] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgVote) {
groupInfo, groupPolicy := randomGroupPolicyX(ctx, testData, reporter, k, s)
if reporter.IsSkipped() {
return nil, nil
}
proposalsResult, err := k.ProposalsByGroupPolicy(ctx,
&group.QueryProposalsByGroupPolicyRequest{Address: groupPolicy.Address},
)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
now := simsx.BlockTime(ctx)
proposal := simsx.First(proposalsResult.GetProposals(), func(p *group.Proposal) bool {
return p.Status == group.PROPOSAL_STATUS_SUBMITTED && p.VotingPeriodEnd.After(now)
})
if proposal == nil {
reporter.Skip("no proposal found")
return nil, nil
}
// select a random member
r := testData.Rand()
res, err := k.GroupMembers(ctx, &group.QueryGroupMembersRequest{GroupId: groupInfo.Id})
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
if len(res.Members) == 0 {
reporter.Skip("group has no members")
return nil, nil
}
voter := testData.GetAccount(reporter, simsx.OneOf(r, res.Members).Member.Address)
vRes, err := k.VotesByProposal(ctx, &group.QueryVotesByProposalRequest{
ProposalId: (*proposal).Id,
})
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
if slices.ContainsFunc(vRes.Votes, func(v *group.Vote) bool { return v.Voter == voter.AddressBech32 }) {
reporter.Skip("voted already on proposal")
return nil, nil
}
msg := &group.MsgVote{
ProposalId: (*proposal).Id,
Voter: voter.AddressBech32,
Option: group.VOTE_OPTION_YES,
Metadata: r.StringN(10),
}
return []simsx.SimAccount{voter}, msg
}
}
func MsgSubmitProposalFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgSubmitProposal] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgSubmitProposal) {
groupInfo, groupPolicy := randomGroupPolicyX(ctx, testData, reporter, k, s)
if reporter.IsSkipped() {
return nil, nil
}
// Return a no-op if we know the proposal cannot be created
policy, err := groupPolicy.GetDecisionPolicy()
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
if err = policy.Validate(*groupInfo, group.DefaultConfig()); err != nil {
reporter.Skip(err.Error())
return nil, nil
}
// Pick a random member from the group
r := testData.Rand()
res, err := k.GroupMembers(ctx, &group.QueryGroupMembersRequest{GroupId: groupInfo.Id})
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
if len(res.Members) == 0 {
reporter.Skip("group has no members")
return nil, nil
}
proposer := testData.GetAccount(reporter, simsx.OneOf(r, res.Members).Member.Address)
msg := &group.MsgSubmitProposal{
GroupPolicyAddress: groupPolicy.Address,
Proposers: []string{proposer.AddressBech32},
Metadata: r.StringN(10),
Title: "Test Proposal",
Summary: "Summary of the proposal",
}
return []simsx.SimAccount{proposer}, msg
}
}
func MsgExecFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgExec] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgExec) {
groupPolicy, policyAdmin := randomGroupPolicyWithAdmin(ctx, testData, reporter, k, s)
if reporter.IsSkipped() {
return nil, nil
}
proposalsResult, err := k.ProposalsByGroupPolicy(ctx,
&group.QueryProposalsByGroupPolicyRequest{Address: groupPolicy.Address},
)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
proposal := simsx.First(proposalsResult.GetProposals(), func(p *group.Proposal) bool {
return p.Status == group.PROPOSAL_STATUS_ACCEPTED
})
if proposal == nil {
reporter.Skip("no proposal found")
return nil, nil
}
msg := &group.MsgExec{
ProposalId: (*proposal).Id,
Executor: policyAdmin.AddressBech32,
}
return []simsx.SimAccount{policyAdmin}, msg
}
}
func randomGroupPolicyWithAdmin(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter, k keeper.Keeper, s *SharedState) (*group.GroupPolicyInfo, simsx.SimAccount) {
for i := 0; i < 5; i++ {
_, groupPolicy := randomGroupPolicyX(ctx, testData, reporter, k, s)
if groupPolicy != nil && testData.HasAccount(groupPolicy.Admin) {
return groupPolicy, testData.GetAccount(reporter, groupPolicy.Admin)
}
}
reporter.Skip("no group policy found with a sims account")
return nil, simsx.SimAccount{}
}
func MsgUpdateGroupMetadataFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgUpdateGroupMetadata] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgUpdateGroupMetadata) {
groupInfo := randomGroupX(ctx, k, testData, reporter, s)
groupAdmin := testData.GetAccount(reporter, groupInfo.Admin)
if reporter.IsSkipped() {
return nil, nil
}
msg := &group.MsgUpdateGroupMetadata{
GroupId: groupInfo.Id,
Admin: groupAdmin.AddressBech32,
Metadata: testData.Rand().StringN(10),
}
return []simsx.SimAccount{groupAdmin}, msg
}
}
func MsgUpdateGroupAdminFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgUpdateGroupAdmin] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgUpdateGroupAdmin) {
groupInfo := randomGroupX(ctx, k, testData, reporter, s)
groupAdmin := testData.GetAccount(reporter, groupInfo.Admin)
if reporter.IsSkipped() {
return nil, nil
}
newAdmin := testData.AnyAccount(reporter, simsx.ExcludeAccounts(groupAdmin))
msg := &group.MsgUpdateGroupAdmin{
GroupId: groupInfo.Id,
Admin: groupAdmin.AddressBech32,
NewAdmin: newAdmin.AddressBech32,
}
return []simsx.SimAccount{groupAdmin}, msg
}
}
func MsgUpdateGroupMembersFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgUpdateGroupMembers] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgUpdateGroupMembers) {
groupInfo := randomGroupX(ctx, k, testData, reporter, s)
groupAdmin := testData.GetAccount(reporter, groupInfo.Admin)
if reporter.IsSkipped() {
return nil, nil
}
res, err := k.GroupMembers(ctx, &group.QueryGroupMembersRequest{GroupId: groupInfo.Id})
if err != nil {
reporter.Skip("group members not found")
return nil, nil
}
oldMemberAddrs := simsx.Collect(res.Members, func(a *group.GroupMember) string { return a.Member.Address })
members := genGroupMembersX(testData, reporter, simsx.ExcludeAddresses(oldMemberAddrs...))
if len(res.Members) > 1 {
// set existing random group member weight to zero to remove from the group
obsoleteMember := simsx.OneOf(testData.Rand(), res.Members)
obsoleteMember.Member.Weight = "0"
members = append(members, group.MemberToMemberRequest(obsoleteMember.Member))
}
msg := &group.MsgUpdateGroupMembers{
GroupId: groupInfo.Id,
Admin: groupAdmin.AddressBech32,
MemberUpdates: members,
}
return []simsx.SimAccount{groupAdmin}, msg
}
}
func MsgUpdateGroupPolicyAdminFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgUpdateGroupPolicyAdmin] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgUpdateGroupPolicyAdmin) {
groupPolicy, policyAdmin := randomGroupPolicyWithAdmin(ctx, testData, reporter, k, s)
if reporter.IsSkipped() {
return nil, nil
}
newAdmin := testData.AnyAccount(reporter, simsx.ExcludeAccounts(policyAdmin))
msg := &group.MsgUpdateGroupPolicyAdmin{
Admin: policyAdmin.AddressBech32,
GroupPolicyAddress: groupPolicy.Address,
NewAdmin: newAdmin.AddressBech32,
}
return []simsx.SimAccount{policyAdmin}, msg
}
}
func MsgUpdateGroupPolicyDecisionPolicyFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgUpdateGroupPolicyDecisionPolicy] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgUpdateGroupPolicyDecisionPolicy) {
groupPolicy, policyAdmin := randomGroupPolicyWithAdmin(ctx, testData, reporter, k, s)
if reporter.IsSkipped() {
return nil, nil
}
r := testData.Rand()
policyAddr, err := k.AddressCodec().StringToBytes(groupPolicy.Address)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
msg, err := group.NewMsgUpdateGroupPolicyDecisionPolicy(policyAdmin.Address, policyAddr, &group.ThresholdDecisionPolicy{
Threshold: strconv.Itoa(r.IntInRange(1, 10)),
Windows: &group.DecisionPolicyWindows{
VotingPeriod: time.Second * time.Duration(r.IntInRange(100, 1000)),
},
})
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
return []simsx.SimAccount{policyAdmin}, msg
}
}
func MsgUpdateGroupPolicyMetadataFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgUpdateGroupPolicyMetadata] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgUpdateGroupPolicyMetadata) {
groupPolicy, policyAdmin := randomGroupPolicyWithAdmin(ctx, testData, reporter, k, s)
if reporter.IsSkipped() {
return nil, nil
}
msg := &group.MsgUpdateGroupPolicyMetadata{
Admin: policyAdmin.AddressBech32,
GroupPolicyAddress: groupPolicy.Address,
Metadata: testData.Rand().StringN(10),
}
return []simsx.SimAccount{policyAdmin}, msg
}
}
func MsgLeaveGroupFactory(k keeper.Keeper, s *SharedState) simsx.SimMsgFactoryFn[*group.MsgLeaveGroup] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *group.MsgLeaveGroup) {
groupInfo := randomGroupX(ctx, k, testData, reporter, s)
if reporter.IsSkipped() {
return nil, nil
}
res, err := k.GroupMembers(ctx, &group.QueryGroupMembersRequest{GroupId: groupInfo.Id})
if err != nil {
reporter.Skip("group members not found")
return nil, nil
}
if len(res.Members) == 0 {
reporter.Skip("group has no members")
return nil, nil
}
anyMember := simsx.OneOf(testData.Rand(), res.Members)
leaver := testData.GetAccount(reporter, anyMember.Member.Address)
msg := &group.MsgLeaveGroup{
GroupId: groupInfo.Id,
Address: leaver.AddressBech32,
}
return []simsx.SimAccount{leaver}, msg
}
}
func genGroupMembersX(testData *simsx.ChainDataSource, reporter simsx.SimulationReporter, filters ...simsx.SimAccountFilter) []group.MemberRequest {
r := testData.Rand()
membersCount := r.Intn(5) + 1
members := make([]group.MemberRequest, membersCount)
uniqueAccountsFilter := simsx.UniqueAccounts()
for i := 0; i < membersCount && !reporter.IsSkipped(); i++ {
m := testData.AnyAccount(reporter, append(filters, uniqueAccountsFilter)...)
members[i] = group.MemberRequest{
Address: m.AddressBech32,
Weight: strconv.Itoa(r.IntInRange(1, 10)),
Metadata: r.StringN(10),
}
}
return members
}
func randomGroupX(ctx context.Context, k keeper.Keeper, testdata *simsx.ChainDataSource, reporter simsx.SimulationReporter, s *SharedState) *group.GroupInfo {
r := testdata.Rand()
groupID := k.GetGroupSequence(sdk.UnwrapSDKContext(ctx))
if initialGroupID := s.getMinGroupID(); initialGroupID == unsetGroupID {
s.setMinGroupID(groupID)
} else if initialGroupID < groupID {
groupID = r.Uint64InRange(initialGroupID+1, groupID+1)
}
// when groupID is 0, it proves that SimulateMsgCreateGroup has never been called. that is, no group exists in the chain
if groupID == 0 {
reporter.Skip("no group exists")
return nil
}
res, err := k.GroupInfo(ctx, &group.QueryGroupInfoRequest{GroupId: groupID})
if err != nil {
reporter.Skip(err.Error())
return nil
}
return res.Info
}
func randomGroupPolicyX(
ctx context.Context,
testdata *simsx.ChainDataSource,
reporter simsx.SimulationReporter,
k keeper.Keeper,
s *SharedState,
) (*group.GroupInfo, *group.GroupPolicyInfo) {
for i := 0; i < 5; i++ {
groupInfo := randomGroupX(ctx, k, testdata, reporter, s)
if reporter.IsSkipped() {
return nil, nil
}
groupID := groupInfo.Id
result, err := k.GroupPoliciesByGroup(ctx, &group.QueryGroupPoliciesByGroupRequest{GroupId: groupID})
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
if len(result.GroupPolicies) != 0 {
return groupInfo, simsx.OneOf(testdata.Rand(), result.GroupPolicies)
}
}
reporter.Skip("no group policies")
return nil, nil
}
// SharedState shared state between message invocations
type SharedState struct {
minGroupID atomic.Uint64
}
// NewSharedState constructor
func NewSharedState() *SharedState {
r := &SharedState{}
r.setMinGroupID(unsetGroupID)
return r
}
func (s *SharedState) getMinGroupID() uint64 {
return s.minGroupID.Load()
}
func (s *SharedState) setMinGroupID(id uint64) {
s.minGroupID.Store(id)
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
"math/rand"
"strings"
"sync/atomic"
"time"
"github.com/cosmos/cosmos-sdk/baseapp"
@ -23,6 +22,7 @@ import (
const unsetGroupID = 100000000000000
// group message types
// will be removed in the future
var (
TypeMsgCreateGroup = sdk.MsgTypeURL(&group.MsgCreateGroup{})
TypeMsgUpdateGroupMembers = sdk.MsgTypeURL(&group.MsgUpdateGroupMembers{})
@ -41,6 +41,7 @@ var (
)
// Simulation operation weights constants
// will be removed in the future
const (
OpMsgCreateGroup = "op_weight_msg_create_group"
OpMsgUpdateGroupAdmin = "op_weight_msg_update_group_admin"
@ -60,6 +61,7 @@ const (
// If update group or group policy txn's executed, `SimulateMsgVote` & `SimulateMsgExec` txn's returns `noOp`.
// That's why we have less weight for update group & group-policy txn's.
// will be removed in the future
const (
WeightMsgCreateGroup = 100
WeightMsgCreateGroupPolicy = 50
@ -77,27 +79,8 @@ const (
WeightMsgCreateGroupWithPolicy = 50
)
// sharedState shared state between message invocations
type sharedState struct {
minGroupID atomic.Uint64
}
// newSharedState constructor
func newSharedState() *sharedState {
r := &sharedState{}
r.setMinGroupID(unsetGroupID)
return r
}
func (s *sharedState) getMinGroupID() uint64 {
return s.minGroupID.Load()
}
func (s *sharedState) setMinGroupID(id uint64) {
s.minGroupID.Store(id)
}
// WeightedOperations returns all the operations from the module with their respective weights
// migrate to the msg factories instead, this method will be removed in the future
func WeightedOperations(
registry cdctypes.InterfaceRegistry,
appParams simtypes.AppParams, _ codec.JSONCodec, txGen client.TxConfig,
@ -166,7 +149,7 @@ func WeightedOperations(
pCdc := codec.NewProtoCodec(registry)
state := newSharedState()
state := NewSharedState()
// create two proposals for weightedOperations
var createProposalOps simulation.WeightedOperations
@ -239,7 +222,7 @@ func WeightedOperations(
}
// SimulateMsgCreateGroup generates a MsgCreateGroup with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgCreateGroup(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -287,7 +270,7 @@ func SimulateMsgCreateGroup(
}
// SimulateMsgCreateGroupWithPolicy generates a MsgCreateGroupWithPolicy with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgCreateGroupWithPolicy(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -352,7 +335,7 @@ func SimulateMsgCreateGroupWithPolicy(
}
// SimulateMsgCreateGroupPolicy generates a NewMsgCreateGroupPolicy with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgCreateGroupPolicy(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
@ -360,7 +343,7 @@ func SimulateMsgCreateGroupPolicy(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgCreateGroupPolicy(cdc, txGen, ak, bk, k, newSharedState())
return simulateMsgCreateGroupPolicy(cdc, txGen, ak, bk, k, NewSharedState())
}
func simulateMsgCreateGroupPolicy(
@ -369,7 +352,7 @@ func simulateMsgCreateGroupPolicy(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -430,7 +413,7 @@ func simulateMsgCreateGroupPolicy(
}
// SimulateMsgSubmitProposal generates a NewMsgSubmitProposal with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgSubmitProposal(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
@ -438,7 +421,7 @@ func SimulateMsgSubmitProposal(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgSubmitProposal(cdc, txGen, ak, bk, k, newSharedState())
return simulateMsgSubmitProposal(cdc, txGen, ak, bk, k, NewSharedState())
}
func simulateMsgSubmitProposal(
@ -447,7 +430,7 @@ func simulateMsgSubmitProposal(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -523,7 +506,7 @@ func simulateMsgSubmitProposal(
}
// SimulateMsgUpdateGroupAdmin generates a MsgUpdateGroupAdmin with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateGroupAdmin(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -531,7 +514,7 @@ func SimulateMsgUpdateGroupAdmin(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgUpdateGroupAdmin(txGen, ak, bk, k, newSharedState())
return simulateMsgUpdateGroupAdmin(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgUpdateGroupAdmin(
@ -539,7 +522,7 @@ func simulateMsgUpdateGroupAdmin(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -599,7 +582,7 @@ func simulateMsgUpdateGroupAdmin(
}
// SimulateMsgUpdateGroupMetadata generates a MsgUpdateGroupMetadata with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateGroupMetadata(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -607,7 +590,7 @@ func SimulateMsgUpdateGroupMetadata(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgUpdateGroupMetadata(txGen, ak, bk, k, newSharedState())
return simulateMsgUpdateGroupMetadata(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgUpdateGroupMetadata(
@ -615,7 +598,7 @@ func simulateMsgUpdateGroupMetadata(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -666,7 +649,7 @@ func simulateMsgUpdateGroupMetadata(
}
// SimulateMsgUpdateGroupMembers generates a MsgUpdateGroupMembers with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateGroupMembers(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -674,7 +657,7 @@ func SimulateMsgUpdateGroupMembers(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgUpdateGroupMembers(txGen, ak, bk, k, newSharedState())
return simulateMsgUpdateGroupMembers(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgUpdateGroupMembers(
@ -682,7 +665,7 @@ func simulateMsgUpdateGroupMembers(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -760,7 +743,7 @@ func simulateMsgUpdateGroupMembers(
}
// SimulateMsgUpdateGroupPolicyAdmin generates a MsgUpdateGroupPolicyAdmin with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateGroupPolicyAdmin(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -768,7 +751,7 @@ func SimulateMsgUpdateGroupPolicyAdmin(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgUpdateGroupPolicyAdmin(txGen, ak, bk, k, newSharedState())
return simulateMsgUpdateGroupPolicyAdmin(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgUpdateGroupPolicyAdmin(
@ -776,7 +759,7 @@ func simulateMsgUpdateGroupPolicyAdmin(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -836,7 +819,7 @@ func simulateMsgUpdateGroupPolicyAdmin(
}
// SimulateMsgUpdateGroupPolicyDecisionPolicy generates a NewMsgUpdateGroupPolicyDecisionPolicy with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateGroupPolicyDecisionPolicy(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -844,7 +827,7 @@ func SimulateMsgUpdateGroupPolicyDecisionPolicy(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgUpdateGroupPolicyDecisionPolicy(txGen, ak, bk, k, newSharedState())
return simulateMsgUpdateGroupPolicyDecisionPolicy(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgUpdateGroupPolicyDecisionPolicy(
@ -852,7 +835,7 @@ func simulateMsgUpdateGroupPolicyDecisionPolicy(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -911,7 +894,7 @@ func simulateMsgUpdateGroupPolicyDecisionPolicy(
}
// SimulateMsgUpdateGroupPolicyMetadata generates a MsgUpdateGroupPolicyMetadata with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateGroupPolicyMetadata(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -919,7 +902,7 @@ func SimulateMsgUpdateGroupPolicyMetadata(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgUpdateGroupPolicyMetadata(txGen, ak, bk, k, newSharedState())
return simulateMsgUpdateGroupPolicyMetadata(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgUpdateGroupPolicyMetadata(
@ -927,7 +910,7 @@ func simulateMsgUpdateGroupPolicyMetadata(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -978,7 +961,7 @@ func simulateMsgUpdateGroupPolicyMetadata(
}
// SimulateMsgWithdrawProposal generates a MsgWithdrawProposal with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgWithdrawProposal(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -986,7 +969,7 @@ func SimulateMsgWithdrawProposal(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgWithdrawProposal(txGen, ak, bk, k, newSharedState())
return simulateMsgWithdrawProposal(txGen, ak, bk, k, NewSharedState())
}
// simulateMsgWithdrawProposal generates a MsgWithdrawProposal with random values
@ -995,7 +978,7 @@ func simulateMsgWithdrawProposal(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -1097,7 +1080,7 @@ func simulateMsgWithdrawProposal(
}
// SimulateMsgVote generates a MsgVote with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgVote(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -1105,7 +1088,7 @@ func SimulateMsgVote(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgVote(txGen, ak, bk, k, newSharedState())
return simulateMsgVote(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgVote(
@ -1113,7 +1096,7 @@ func simulateMsgVote(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -1215,7 +1198,7 @@ func simulateMsgVote(
}
// SimulateMsgExec generates a MsgExec with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgExec(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -1223,7 +1206,7 @@ func SimulateMsgExec(
bk group.BankKeeper,
k keeper.Keeper,
) simtypes.Operation {
return simulateMsgExec(txGen, ak, bk, k, newSharedState())
return simulateMsgExec(txGen, ak, bk, k, NewSharedState())
}
func simulateMsgExec(
@ -1231,7 +1214,7 @@ func simulateMsgExec(
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -1306,7 +1289,7 @@ func simulateMsgExec(
}
// SimulateMsgLeaveGroup generates a MsgLeaveGroup with random values
// Deprecated: this is an internal method and will be removed
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgLeaveGroup(
_ *codec.ProtoCodec,
txGen client.TxConfig,
@ -1314,7 +1297,7 @@ func SimulateMsgLeaveGroup(
ak group.AccountKeeper,
bk group.BankKeeper,
) simtypes.Operation {
return simulateMsgLeaveGroup(txGen, k, ak, bk, newSharedState())
return simulateMsgLeaveGroup(txGen, k, ak, bk, NewSharedState())
}
func simulateMsgLeaveGroup(
@ -1322,7 +1305,7 @@ func simulateMsgLeaveGroup(
k keeper.Keeper,
ak group.AccountKeeper,
bk group.BankKeeper,
s *sharedState,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
@ -1381,7 +1364,7 @@ func simulateMsgLeaveGroup(
}
func randomGroup(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
ctx sdk.Context, accounts []simtypes.Account, s *sharedState,
ctx sdk.Context, accounts []simtypes.Account, s *SharedState,
) (groupInfo *group.GroupInfo, acc simtypes.Account, account sdk.AccountI, err error) {
groupID := k.GetGroupSequence(ctx)
@ -1419,7 +1402,7 @@ func randomGroup(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
}
func randomGroupPolicy(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
ctx sdk.Context, accounts []simtypes.Account, s *sharedState,
ctx sdk.Context, accounts []simtypes.Account, s *SharedState,
) (groupInfo *group.GroupInfo, groupPolicyInfo *group.GroupPolicyInfo, acc simtypes.Account, account sdk.AccountI, err error) {
groupInfo, _, _, err = randomGroup(r, k, ak, ctx, accounts, s)
if err != nil {

View File

@ -15,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -170,10 +171,16 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
}
// ProposalMsgs returns msgs used for governance proposals for simulations.
// migrate to ProposalMsgsX. This method is ignored when ProposalMsgsX exists and will be removed in the future.
func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}
// ProposalMsgsX returns msgs used for governance proposals for simulations.
func (AppModule) ProposalMsgsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_update_params", 100), simulation.MsgUpdateParamsFactory())
}
// RegisterStoreDecoder registers a decoder for mint module's types.
func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
sdr[types.StoreKey] = simtypes.NewStoreDecoderFuncFromCollectionsSchema(am.keeper.Schema)

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
"cosmossdk.io/math"
@ -69,11 +67,5 @@ func RandomizedGenState(simState *module.SimulationState) {
params := types.NewParams(mintDenom, inflationRateChange, inflationMax, inflationMin, goalBonded, blocksPerYear)
mintGenesis := types.NewGenesisState(types.InitialMinter(inflation), params)
bz, err := json.MarshalIndent(&mintGenesis, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated minting parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(mintGenesis)
}

View File

@ -0,0 +1,29 @@
package simulation
import (
"context"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/simsx"
"github.com/cosmos/cosmos-sdk/x/mint/types"
)
// MsgUpdateParamsFactory creates a gov proposal for param updates
func MsgUpdateParamsFactory() simsx.SimMsgFactoryFn[*types.MsgUpdateParams] {
return func(_ context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgUpdateParams) {
r := testData.Rand()
params := types.DefaultParams()
params.BlocksPerYear = r.Uint64InRange(1, 1_000_000)
params.GoalBonded = sdkmath.LegacyNewDecWithPrec(int64(r.IntInRange(1, 100)), 2)
params.InflationMin = sdkmath.LegacyNewDecWithPrec(int64(r.IntInRange(1, 50)), 2)
params.InflationMax = sdkmath.LegacyNewDecWithPrec(int64(r.IntInRange(50, 100)), 2)
params.InflationRateChange = sdkmath.LegacyNewDecWithPrec(int64(r.IntInRange(1, 100)), 2)
params.MintDenom = r.StringN(10)
return nil, &types.MsgUpdateParams{
Authority: testData.ModuleAccountAddress(reporter, "gov"),
Params: params,
}
}
}

View File

@ -13,6 +13,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
DefaultWeightMsgUpdateParams int = 100
@ -20,6 +21,7 @@ const (
)
// ProposalMsgs defines the module weighted proposals' contents
// migrate to the msg factories instead, this method will be removed in the future
func ProposalMsgs() []simtypes.WeightedProposalMsg {
return []simtypes.WeightedProposalMsg{
simulation.NewWeightedProposalMsg(
@ -31,6 +33,7 @@ func ProposalMsgs() []simtypes.WeightedProposalMsg {
}
// SimulateMsgUpdateParams returns a random MsgUpdateParams
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
// use the default gov module account address as authority
var authority sdk.AccAddress = address.Module("gov")

View File

@ -20,6 +20,7 @@ import (
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -144,6 +145,7 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
}
// WeightedOperations returns the all the nft module operations with their respective weights.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
am.registry,
@ -152,6 +154,11 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp
)
}
// WeightedOperationsX registers weighted nft module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_send", 100), simulation.MsgSendFactory(am.keeper))
}
//
// App Wiring Setup
//

View File

@ -0,0 +1,34 @@
package simulation
import (
"context"
"cosmossdk.io/x/nft"
"cosmossdk.io/x/nft/keeper"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func MsgSendFactory(k keeper.Keeper) simsx.SimMsgFactoryFn[*nft.MsgSend] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *nft.MsgSend) {
from := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
to := testData.AnyAccount(reporter, simsx.ExcludeAccounts(from))
if reporter.IsSkipped() {
return nil, nil
}
n, err := randNFT(sdk.UnwrapSDKContext(ctx), testData.Rand().Rand, k, from.Address)
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
msg := &nft.MsgSend{
ClassId: n.ClassId,
Id: n.Id,
Sender: from.AddressBech32,
Receiver: to.AddressBech32,
}
return []simsx.SimAccount{from}, msg
}
}

View File

@ -16,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/simulation"
)
// will be removed in the future
const (
// OpWeightMsgSend Simulation operation weights constants
OpWeightMsgSend = "op_weight_msg_send"
@ -24,9 +25,11 @@ const (
WeightSend = 100
)
// will be removed in the future
var TypeMsgSend = sdk.MsgTypeURL(&nft.MsgSend{})
// WeightedOperations returns all the operations from the module with their respective weights
// migrate to the msg factories instead, this method will be removed in the future
func WeightedOperations(
registry cdctypes.InterfaceRegistry,
appParams simtypes.AppParams,
@ -53,6 +56,7 @@ func WeightedOperations(
}
// SimulateMsgSend generates a MsgSend with random values.
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgSend(
_ *codec.ProtoCodec,
txCfg client.TxConfig,

View File

@ -19,6 +19,7 @@ func min(a, b int) int {
// SimulateParamChangeProposalContent returns random parameter change content.
// It will generate a ParameterChangeProposal object with anywhere between 1 and
// the total amount of defined parameters changes, all of which have random valid values.
// Deprecated: This method will be removed in the future
func SimulateParamChangeProposalContent(paramChangePool []simulation.LegacyParamChange) simulation.ContentSimulatorFn { //nolint:staticcheck // used for legacy testing
numProposals := 0
// Bound the maximum number of simultaneous parameter changes

View File

@ -5,6 +5,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/simulation"
)
// will be removed in the future
const (
// OpWeightSubmitParamChangeProposal app params key for param change proposal
OpWeightSubmitParamChangeProposal = "op_weight_submit_param_change_proposal"
@ -13,6 +14,8 @@ const (
// ProposalContents defines the module weighted proposals' contents
//
// will be removed in the future
//
//nolint:staticcheck // used for legacy testing
func ProposalContents(paramChanges []simtypes.LegacyParamChange) []simtypes.WeightedProposalContent {
return []simtypes.WeightedProposalContent{

View File

@ -12,6 +12,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -141,6 +142,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
}
// ProposalMsgs returns msgs used for governance proposals for simulations.
// migrate to ProposalMsgsX. This method is ignored when ProposalMsgsX exists and will be removed in the future.
func (AppModule) ProposalMsgs(_ module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}
@ -149,6 +151,8 @@ func (AppModule) ProposalMsgs(_ module.SimulationState) []simtypes.WeightedPropo
func (am AppModule) RegisterStoreDecoder(_ simtypes.StoreDecoderRegistry) {
}
// WeightedOperations returns the all the protocolpool module operations with their respective weights.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
simState.AppParams,
@ -158,3 +162,13 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp
am.keeper,
)
}
// ProposalMsgsX registers governance proposal messages in the simulation registry.
func (am AppModule) ProposalMsgsX(weight simsx.WeightSource, reg simsx.Registry) {
reg.Add(weight.Get("msg_community_pool_spend", 50), simulation.MsgCommunityPoolSpendFactory())
}
// WeightedOperationsX registers weighted protocolpool module operations for simulation.
func (am AppModule) WeightedOperationsX(weight simsx.WeightSource, reg simsx.Registry) {
reg.Add(weight.Get("msg_fund_community_pool", 50), simulation.MsgFundCommunityPoolFactory())
}

View File

@ -0,0 +1,36 @@
package simulation
import (
"context"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/protocolpool/types"
)
func MsgFundCommunityPoolFactory() simsx.SimMsgFactoryFn[*types.MsgFundCommunityPool] {
return func(_ context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgFundCommunityPool) {
funder := testData.AnyAccount(reporter, simsx.WithSpendableBalance())
fundAmount := funder.LiquidBalance().RandSubsetCoins(reporter)
msg := types.NewMsgFundCommunityPool(fundAmount, funder.AddressBech32)
return []simsx.SimAccount{funder}, msg
}
}
// MsgCommunityPoolSpendFactory creates a gov proposal to send tokens from the community pool to a random account
func MsgCommunityPoolSpendFactory() simsx.SimMsgFactoryFn[*types.MsgCommunityPoolSpend] {
return func(_ context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgCommunityPoolSpend) {
return nil, &types.MsgCommunityPoolSpend{
Authority: testData.ModuleAccountAddress(reporter, "gov"),
Recipient: testData.AnyAccount(reporter).AddressBech32,
Amount: must(sdk.ParseCoinsNormalized("100stake,2testtoken")),
}
}
}
func must[T any](r T, err error) T {
if err != nil {
panic(err)
}
return r
}

View File

@ -15,6 +15,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -174,6 +175,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
}
// ProposalMsgs returns msgs used for governance proposals for simulations.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}
@ -183,6 +185,11 @@ func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc)
}
// ProposalMsgsX returns msgs used for governance proposals for simulations.
func (AppModule) ProposalMsgsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_update_params", 100), simulation.MsgUpdateParamsFactory())
}
// WeightedOperations returns the all the slashing module operations with their respective weights.
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
@ -191,6 +198,12 @@ func (am AppModule) WeightedOperations(simState module.SimulationState) []simtyp
)
}
// WeightedOperationsX registers weighted slashing module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry) {
// note: using old keys for backwards compatibility
reg.Add(weights.Get("msg_unjail", 20), simulation.MsgUnjailFactory(am.keeper, am.stakingKeeper))
}
//
// App Wiring Setup
//

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
"time"
@ -70,11 +68,5 @@ func RandomizedGenState(simState *module.SimulationState) {
)
slashingGenesis := types.NewGenesisState(params, []types.SigningInfo{}, []types.ValidatorMissedBlocks{})
bz, err := json.MarshalIndent(&slashingGenesis, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated slashing parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(slashingGenesis)
}

View File

@ -0,0 +1,98 @@
package simulation
import (
"context"
"errors"
"time"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/simsx"
"github.com/cosmos/cosmos-sdk/x/slashing/keeper"
"github.com/cosmos/cosmos-sdk/x/slashing/types"
)
func MsgUnjailFactory(k keeper.Keeper, sk types.StakingKeeper) simsx.SimMsgFactoryX {
return simsx.NewSimMsgFactoryWithDeliveryResultHandler[*types.MsgUnjail](func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgUnjail, simsx.SimDeliveryResultHandler) {
allVals, err := sk.GetAllValidators(ctx)
if err != nil {
reporter.Skip(err.Error())
return nil, nil, nil
}
validator := simsx.OneOf(testData.Rand(), allVals)
if !validator.IsJailed() {
reporter.Skip("validator not jailed")
return nil, nil, nil
}
if validator.InvalidExRate() {
reporter.Skip("validator with invalid exchange rate")
return nil, nil, nil
}
info, err := k.GetValidatorSigningInfo(ctx, must(validator.GetConsAddr()))
if err != nil {
reporter.Skip(err.Error())
return nil, nil, nil
}
valOperBz := must(sk.ValidatorAddressCodec().StringToBytes(validator.GetOperator()))
valOper := testData.GetAccountbyAccAddr(reporter, valOperBz)
if reporter.IsSkipped() {
return nil, nil, nil
}
selfDel, err := sk.Delegation(ctx, valOper.Address, valOperBz)
if selfDel == nil || err != nil {
reporter.Skip("no self delegation")
return nil, nil, nil
}
var handler simsx.SimDeliveryResultHandler
// result should fail if:
// - validator cannot be unjailed due to tombstone
// - validator is still in jailed period
// - self delegation too low
if info.Tombstoned ||
simsx.BlockTime(ctx).Before(info.JailedUntil) ||
selfDel.GetShares().IsNil() ||
validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
handler = func(err error) error {
if err == nil {
switch {
case info.Tombstoned:
return errors.New("validator should not have been unjailed if validator tombstoned")
case simsx.BlockTime(ctx).Before(info.JailedUntil):
return errors.New("validator unjailed while validator still in jail period")
case selfDel.GetShares().IsNil() || validator.TokensFromShares(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()):
return errors.New("validator unjailed even though self-delegation too low")
}
}
return nil
}
}
return []simsx.SimAccount{valOper}, types.NewMsgUnjail(validator.GetOperator()), handler
})
}
// MsgUpdateParamsFactory creates a gov proposal for param updates
func MsgUpdateParamsFactory() simsx.SimMsgFactoryFn[*types.MsgUpdateParams] {
return func(_ context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgUpdateParams) {
r := testData.Rand()
params := types.DefaultParams()
params.DowntimeJailDuration = time.Duration(r.Timestamp().UnixNano())
params.SignedBlocksWindow = int64(r.IntInRange(1, 1000))
params.MinSignedPerWindow = sdkmath.LegacyNewDecWithPrec(int64(r.IntInRange(1, 100)), 2)
params.SlashFractionDoubleSign = sdkmath.LegacyNewDecWithPrec(int64(r.IntInRange(1, 100)), 2)
params.SlashFractionDowntime = sdkmath.LegacyNewDecWithPrec(int64(r.IntInRange(1, 100)), 2)
return nil, &types.MsgUpdateParams{
Authority: testData.ModuleAccountAddress(reporter, "gov"),
Params: params,
}
}
}
func must[T any](r T, err error) T {
if err != nil {
panic(err)
}
return r
}

View File

@ -18,6 +18,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
OpWeightMsgUnjail = "op_weight_msg_unjail"
@ -25,6 +26,7 @@ const (
)
// WeightedOperations returns all the operations from the module with their respective weights
// migrate to the msg factories instead, this method will be removed in the future
func WeightedOperations(
registry codectypes.InterfaceRegistry,
appParams simtypes.AppParams,
@ -49,6 +51,7 @@ func WeightedOperations(
}
// SimulateMsgUnjail generates a MsgUnjail with random values
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUnjail(
cdc *codec.ProtoCodec,
txGen client.TxConfig,

View File

@ -14,6 +14,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
DefaultWeightMsgUpdateParams int = 100
@ -21,6 +22,7 @@ const (
)
// ProposalMsgs defines the module weighted proposals' contents
// migrate to the msg factories instead, this method will be removed in the future
func ProposalMsgs() []simtypes.WeightedProposalMsg {
return []simtypes.WeightedProposalMsg{
simulation.NewWeightedProposalMsg(
@ -32,6 +34,7 @@ func ProposalMsgs() []simtypes.WeightedProposalMsg {
}
// SimulateMsgUpdateParams returns a random MsgUpdateParams
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
// use the default gov module account address as authority
var authority sdk.AccAddress = address.Module("gov")

View File

@ -21,6 +21,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/runtime"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
@ -280,19 +281,36 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
}
// ProposalMsgs returns msgs used for governance proposals for simulations.
// migrate to ProposalMsgsX. This method is ignored when ProposalMsgsX exists and will be removed in the future.
func (AppModule) ProposalMsgs(_ module.SimulationState) []simtypes.WeightedProposalMsg {
return simulation.ProposalMsgs()
}
// ProposalMsgsX registers governance proposal messages in the simulation registry.
func (AppModule) ProposalMsgsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_update_params", 100), simulation.MsgUpdateParamsFactory())
}
// RegisterStoreDecoder registers a decoder for staking module's types
func (am AppModule) RegisterStoreDecoder(sdr simtypes.StoreDecoderRegistry) {
sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc)
}
// WeightedOperations returns the all the staking module operations with their respective weights.
// migrate to WeightedOperationsX. This method is ignored when WeightedOperationsX exists and will be removed in the future
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
simState.AppParams, simState.Cdc, simState.TxConfig,
am.accountKeeper, am.bankKeeper, am.keeper,
)
}
// WeightedOperationsX registers weighted staking module operations for simulation.
func (am AppModule) WeightedOperationsX(weights simsx.WeightSource, reg simsx.Registry) {
reg.Add(weights.Get("msg_create_validator", 100), simulation.MsgCreateValidatorFactory(am.keeper))
reg.Add(weights.Get("msg_delegate", 100), simulation.MsgDelegateFactory(am.keeper))
reg.Add(weights.Get("msg_undelegate", 100), simulation.MsgUndelegateFactory(am.keeper))
reg.Add(weights.Get("msg_edit_validator", 5), simulation.MsgEditValidatorFactory(am.keeper))
reg.Add(weights.Get("msg_begin_redelegate", 100), simulation.MsgBeginRedelegateFactory(am.keeper))
reg.Add(weights.Get("msg_cancel_unbonding_delegation", 100), simulation.MsgCancelUnbondingDelegationFactory(am.keeper))
}

View File

@ -1,8 +1,6 @@
package simulation
import (
"encoding/json"
"fmt"
"math/rand"
"time"
@ -91,11 +89,5 @@ func RandomizedGenState(simState *module.SimulationState) {
}
stakingGenesis := types.NewGenesisState(params, validators, delegations)
bz, err := json.MarshalIndent(&stakingGenesis.Params, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("Selected randomly generated staking parameters:\n%s\n", bz)
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(stakingGenesis)
}

View File

@ -0,0 +1,334 @@
package simulation
import (
"context"
"slices"
"time"
"cosmossdk.io/math"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/simsx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
func MsgCreateValidatorFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgCreateValidator] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgCreateValidator) {
r := testData.Rand()
withoutValidators := simsx.SimAccountFilterFn(func(a simsx.SimAccount) bool {
_, err := k.GetValidator(ctx, sdk.ValAddress(a.Address))
return err != nil
})
withoutConsAddrUsed := simsx.SimAccountFilterFn(func(a simsx.SimAccount) bool {
consPubKey := sdk.GetConsAddress(a.ConsKey.PubKey())
_, err := k.GetValidatorByConsAddr(ctx, consPubKey)
return err != nil
})
bondDenom := must(k.BondDenom(ctx))
valOper := testData.AnyAccount(reporter, withoutValidators, withoutConsAddrUsed, simsx.WithDenomBalance(bondDenom))
if reporter.IsSkipped() {
return nil, nil
}
newPubKey := valOper.ConsKey.PubKey()
assertKeyUnused(ctx, reporter, k, newPubKey)
if reporter.IsSkipped() {
return nil, nil
}
selfDelegation := valOper.LiquidBalance().RandSubsetCoin(reporter, bondDenom)
description := types.NewDescription(
r.StringN(10),
r.StringN(10),
r.StringN(10),
r.StringN(10),
r.StringN(10),
)
maxCommission := math.LegacyNewDecWithPrec(int64(r.IntInRange(0, 100)), 2)
commission := types.NewCommissionRates(
r.DecN(maxCommission),
maxCommission,
r.DecN(maxCommission),
)
addr := must(k.ValidatorAddressCodec().BytesToString(valOper.Address))
msg, err := types.NewMsgCreateValidator(addr, newPubKey, selfDelegation, description, commission, math.OneInt())
if err != nil {
reporter.Skip(err.Error())
return nil, nil
}
return []simsx.SimAccount{valOper}, msg
}
}
func MsgDelegateFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgDelegate] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgDelegate) {
r := testData.Rand()
bondDenom := must(k.BondDenom(ctx))
val := randomValidator(ctx, reporter, k, r)
if reporter.IsSkipped() {
return nil, nil
}
if val.InvalidExRate() {
reporter.Skip("validator's invalid exchange rate")
return nil, nil
}
sender := testData.AnyAccount(reporter)
delegation := sender.LiquidBalance().RandSubsetCoin(reporter, bondDenom)
return []simsx.SimAccount{sender}, types.NewMsgDelegate(sender.AddressBech32, val.GetOperator(), delegation)
}
}
func MsgUndelegateFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgUndelegate] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgUndelegate) {
r := testData.Rand()
bondDenom := must(k.BondDenom(ctx))
val := randomValidator(ctx, reporter, k, r)
if reporter.IsSkipped() {
return nil, nil
}
// select delegator and amount for undelegate
valAddr := must(k.ValidatorAddressCodec().StringToBytes(val.GetOperator()))
delegations := must(k.GetValidatorDelegations(ctx, valAddr))
if delegations == nil {
reporter.Skip("no delegation entries")
return nil, nil
}
// get random delegator from validator
delegation := delegations[r.Intn(len(delegations))]
delAddr := delegation.GetDelegatorAddr()
delegator := testData.GetAccount(reporter, delAddr)
if hasMaxUD := must(k.HasMaxUnbondingDelegationEntries(ctx, delegator.Address, valAddr)); hasMaxUD {
reporter.Skipf("max unbodings")
return nil, nil
}
totalBond := val.TokensFromShares(delegation.GetShares()).TruncateInt()
if !totalBond.IsPositive() {
reporter.Skip("total bond is negative")
return nil, nil
}
unbondAmt := must(r.PositiveSDKIntn(totalBond))
msg := types.NewMsgUndelegate(delAddr, val.GetOperator(), sdk.NewCoin(bondDenom, unbondAmt))
return []simsx.SimAccount{delegator}, msg
}
}
func MsgEditValidatorFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgEditValidator] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgEditValidator) {
r := testData.Rand()
val := randomValidator(ctx, reporter, k, r)
if reporter.IsSkipped() {
return nil, nil
}
newCommissionRate := r.DecN(val.Commission.MaxRate)
if err := val.Commission.ValidateNewRate(newCommissionRate, simsx.BlockTime(ctx)); err != nil {
// skip as the commission is invalid
reporter.Skip("invalid commission rate")
return nil, nil
}
valOpAddrBz := must(k.ValidatorAddressCodec().StringToBytes(val.GetOperator()))
valOper := testData.GetAccountbyAccAddr(reporter, valOpAddrBz)
d := types.NewDescription(r.StringN(10), r.StringN(10), r.StringN(10), r.StringN(10), r.StringN(10))
msg := types.NewMsgEditValidator(val.GetOperator(), d, &newCommissionRate, nil)
return []simsx.SimAccount{valOper}, msg
}
}
func MsgBeginRedelegateFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgBeginRedelegate] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgBeginRedelegate) {
bondDenom := must(k.BondDenom(ctx))
if !testData.IsSendEnabledDenom(bondDenom) {
reporter.Skip("bond denom send not enabled")
return nil, nil
}
r := testData.Rand()
// select random validator as src
vals := must(k.GetAllValidators(ctx))
if len(vals) < 2 {
reporter.Skip("insufficient number of validators")
return nil, nil
}
srcVal := simsx.OneOf(r, vals)
srcValOpAddrBz := must(k.ValidatorAddressCodec().StringToBytes(srcVal.GetOperator()))
delegations := must(k.GetValidatorDelegations(ctx, srcValOpAddrBz))
if delegations == nil {
reporter.Skip("no delegations")
return nil, nil
}
// get random delegator from src validator
delegation := simsx.OneOf(r, delegations)
totalBond := srcVal.TokensFromShares(delegation.GetShares()).TruncateInt()
if !totalBond.IsPositive() {
reporter.Skip("total bond is negative")
return nil, nil
}
redAmount, err := r.PositiveSDKIntn(totalBond)
if err != nil || redAmount.IsZero() {
reporter.Skip("unable to generate positive amount")
return nil, nil
}
if totalBond.Sub(redAmount).IsZero() {
reporter.Skip("can not redelegate all")
return nil, nil
}
// check if the shares truncate to zero
shares := must(srcVal.SharesFromTokens(redAmount))
if srcVal.TokensFromShares(shares).TruncateInt().IsZero() {
reporter.Skip("shares truncate to zero")
return nil, nil
}
// pick a random delegator
delAddr := delegation.GetDelegatorAddr()
delAddrBz := must(testData.AddressCodec().StringToBytes(delAddr))
if hasRecRedel := must(k.HasReceivingRedelegation(ctx, delAddrBz, srcValOpAddrBz)); hasRecRedel {
reporter.Skip("receiving redelegation is not allowed")
return nil, nil
}
delegator := testData.GetAccountbyAccAddr(reporter, delAddrBz)
if reporter.IsSkipped() {
return nil, nil
}
// get random destination validator
destVal := simsx.OneOf(r, vals)
if srcVal.Equal(&destVal) {
destVal = simsx.OneOf(r, slices.DeleteFunc(vals, func(v types.Validator) bool { return srcVal.Equal(&v) }))
}
if destVal.InvalidExRate() {
reporter.Skip("invalid delegation rate")
return nil, nil
}
destAddrBz := must(k.ValidatorAddressCodec().StringToBytes(destVal.GetOperator()))
if hasMaxRedel := must(k.HasMaxRedelegationEntries(ctx, delAddrBz, srcValOpAddrBz, destAddrBz)); hasMaxRedel {
reporter.Skip("maximum redelegation entries reached")
return nil, nil
}
msg := types.NewMsgBeginRedelegate(
delAddr, srcVal.GetOperator(), destVal.GetOperator(),
sdk.NewCoin(bondDenom, redAmount),
)
return []simsx.SimAccount{delegator}, msg
}
}
func MsgCancelUnbondingDelegationFactory(k *keeper.Keeper) simsx.SimMsgFactoryFn[*types.MsgCancelUnbondingDelegation] {
return func(ctx context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgCancelUnbondingDelegation) {
r := testData.Rand()
val := randomValidator(ctx, reporter, k, r)
if reporter.IsSkipped() {
return nil, nil
}
if val.IsJailed() || val.InvalidExRate() {
reporter.Skip("validator is jailed")
return nil, nil
}
valOpAddrBz := must(k.ValidatorAddressCodec().StringToBytes(val.GetOperator()))
valOper := testData.GetAccountbyAccAddr(reporter, valOpAddrBz)
unbondingDelegation, err := k.GetUnbondingDelegation(ctx, valOper.Address, valOpAddrBz)
if err != nil {
reporter.Skip("no unbonding delegation")
return nil, nil
}
// This is a temporary fix to make staking simulation pass. We should fetch
// the first unbondingDelegationEntry that matches the creationHeight, because
// currently the staking msgServer chooses the first unbondingDelegationEntry
// with the matching creationHeight.
//
// ref: https://github.com/cosmos/cosmos-sdk/issues/12932
creationHeight := unbondingDelegation.Entries[r.Intn(len(unbondingDelegation.Entries))].CreationHeight
var unbondingDelegationEntry types.UnbondingDelegationEntry
for _, entry := range unbondingDelegation.Entries {
if entry.CreationHeight == creationHeight {
unbondingDelegationEntry = entry
break
}
}
if unbondingDelegationEntry.CompletionTime.Before(simsx.BlockTime(ctx)) {
reporter.Skip("unbonding delegation is already processed")
return nil, nil
}
if !unbondingDelegationEntry.Balance.IsPositive() {
reporter.Skip("delegator receiving balance is negative")
return nil, nil
}
cancelBondAmt := r.Amount(unbondingDelegationEntry.Balance)
if cancelBondAmt.IsZero() {
reporter.Skip("cancelBondAmt amount is zero")
return nil, nil
}
msg := types.NewMsgCancelUnbondingDelegation(
valOper.AddressBech32,
val.GetOperator(),
unbondingDelegationEntry.CreationHeight,
sdk.NewCoin(must(k.BondDenom(ctx)), cancelBondAmt),
)
return []simsx.SimAccount{valOper}, msg
}
}
// MsgUpdateParamsFactory creates a gov proposal for param updates
func MsgUpdateParamsFactory() simsx.SimMsgFactoryFn[*types.MsgUpdateParams] {
return func(_ context.Context, testData *simsx.ChainDataSource, reporter simsx.SimulationReporter) ([]simsx.SimAccount, *types.MsgUpdateParams) {
r := testData.Rand()
params := types.DefaultParams()
// do not modify denom or staking will break
params.HistoricalEntries = r.Uint32InRange(0, 1000)
params.MaxEntries = r.Uint32InRange(1, 1000)
params.MaxValidators = r.Uint32InRange(1, 1000)
params.UnbondingTime = time.Duration(r.Timestamp().UnixNano())
// modifying commission rate can cause issues for proposals within the same block
// params.MinCommissionRate = r.DecN(sdkmath.LegacyNewDec(1))
return nil, &types.MsgUpdateParams{
Authority: testData.ModuleAccountAddress(reporter, "gov"),
Params: params,
}
}
}
func randomValidator(ctx context.Context, reporter simsx.SimulationReporter, k *keeper.Keeper, r *simsx.XRand) types.Validator {
vals, err := k.GetAllValidators(ctx)
if err != nil || len(vals) == 0 {
reporter.Skipf("unable to get validators or empty list: %s", err)
return types.Validator{}
}
return simsx.OneOf(r, vals)
}
// skips execution if there's another key rotation for the same key in the same block
func assertKeyUnused(ctx context.Context, reporter simsx.SimulationReporter, k *keeper.Keeper, newPubKey cryptotypes.PubKey) {
newConsAddr := sdk.ConsAddress(newPubKey.Address())
if _, err := k.GetValidatorByConsAddr(ctx, newConsAddr); err == nil {
reporter.Skip("cons key already used")
return
}
}
func must[T any](r T, err error) T {
if err != nil {
panic(err)
}
return r
}

View File

@ -19,6 +19,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
DefaultWeightMsgCreateValidator int = 100
DefaultWeightMsgEditValidator int = 5
@ -36,6 +37,7 @@ const (
)
// WeightedOperations returns all the operations from the module with their respective weights
// migrate to the msg factories instead, this method will be removed in the future
func WeightedOperations(
appParams simtypes.AppParams,
cdc codec.JSONCodec,
@ -106,6 +108,7 @@ func WeightedOperations(
}
// SimulateMsgCreateValidator generates a MsgCreateValidator with random values
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgCreateValidator(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -193,6 +196,7 @@ func SimulateMsgCreateValidator(
}
// SimulateMsgEditValidator generates a MsgEditValidator with random values
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgEditValidator(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -268,6 +272,7 @@ func SimulateMsgEditValidator(
}
// SimulateMsgDelegate generates a MsgDelegate with random values
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgDelegate(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -346,6 +351,7 @@ func SimulateMsgDelegate(
}
// SimulateMsgUndelegate generates a MsgUndelegate with random values
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUndelegate(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -461,6 +467,7 @@ func SimulateMsgUndelegate(
}
// SimulateMsgCancelUnbondingDelegate generates a MsgCancelUnbondingDelegate with random values
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgCancelUnbondingDelegate(
txGen client.TxConfig,
ak types.AccountKeeper,
@ -561,6 +568,7 @@ func SimulateMsgCancelUnbondingDelegate(
}
// SimulateMsgBeginRedelegate generates a MsgBeginRedelegate with random values
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgBeginRedelegate(
txGen client.TxConfig,
ak types.AccountKeeper,

View File

@ -14,6 +14,7 @@ import (
)
// Simulation operation weights constants
// will be removed in the future
const (
DefaultWeightMsgUpdateParams int = 100
@ -21,6 +22,7 @@ const (
)
// ProposalMsgs defines the module weighted proposals' contents
// migrate to the msg factories instead, this method will be removed in the future
func ProposalMsgs() []simtypes.WeightedProposalMsg {
return []simtypes.WeightedProposalMsg{
simulation.NewWeightedProposalMsg(
@ -32,6 +34,7 @@ func ProposalMsgs() []simtypes.WeightedProposalMsg {
}
// SimulateMsgUpdateParams returns a random MsgUpdateParams
// migrate to the msg factories instead, this method will be removed in the future
func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg {
// use the default gov module account address as authority
var authority sdk.AccAddress = address.Module("gov")