feat(sims): Add simsx support to modules (#24145)
Co-authored-by: Alex | Interchain Labs <alex@interchainlabs.io>
This commit is contained in:
parent
99da3279be
commit
ea908cef68
@ -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
14
simsx/context.go
Normal 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()
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
25
x/auth/simulation/msg_factory.go
Normal file
25
x/auth/simulation/msg_factory.go
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
107
x/authz/simulation/msg_factory.go
Normal file
107
x/authz/simulation/msg_factory.go
Normal 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
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
125
x/distribution/simulation/msg_factory.go
Normal file
125
x/distribution/simulation/msg_factory.go
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
65
x/feegrant/simulation/msg_factory.go
Normal file
65
x/feegrant/simulation/msg_factory.go
Normal 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()
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
370
x/gov/simulation/msg_factory.go
Normal file
370
x/gov/simulation/msg_factory.go
Normal 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)
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
490
x/group/simulation/msg_factory.go
Normal file
490
x/group/simulation/msg_factory.go
Normal 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)
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
29
x/mint/simulation/msg_factory.go
Normal file
29
x/mint/simulation/msg_factory.go
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
34
x/nft/simulation/msg_factory.go
Normal file
34
x/nft/simulation/msg_factory.go
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
36
x/protocolpool/simulation/msg_factory.go
Normal file
36
x/protocolpool/simulation/msg_factory.go
Normal 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
|
||||
}
|
||||
@ -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
|
||||
//
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
98
x/slashing/simulation/msg_factory.go
Normal file
98
x/slashing/simulation/msg_factory.go
Normal 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
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
334
x/staking/simulation/msg_factory.go
Normal file
334
x/staking/simulation/msg_factory.go
Normal 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
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user