cosmos-sdk/x/group/simulation/operations.go
2024-07-24 12:40:01 +00:00

1509 lines
51 KiB
Go

package simulation
import (
"context"
"fmt"
"math/rand"
"strings"
"sync/atomic"
"time"
gogoprotoany "github.com/cosmos/gogoproto/types/any"
"cosmossdk.io/core/address"
"cosmossdk.io/x/group"
"cosmossdk.io/x/group/keeper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/simulation"
)
const unsetGroupID = 100000000000000
// group message types
var (
TypeMsgCreateGroup = sdk.MsgTypeURL(&group.MsgCreateGroup{})
TypeMsgUpdateGroupMembers = sdk.MsgTypeURL(&group.MsgUpdateGroupMembers{})
TypeMsgUpdateGroupAdmin = sdk.MsgTypeURL(&group.MsgUpdateGroupAdmin{})
TypeMsgUpdateGroupMetadata = sdk.MsgTypeURL(&group.MsgUpdateGroupMetadata{})
TypeMsgCreateGroupWithPolicy = sdk.MsgTypeURL(&group.MsgCreateGroupWithPolicy{})
TypeMsgCreateGroupPolicy = sdk.MsgTypeURL(&group.MsgCreateGroupPolicy{})
TypeMsgUpdateGroupPolicyAdmin = sdk.MsgTypeURL(&group.MsgUpdateGroupPolicyAdmin{})
TypeMsgUpdateGroupPolicyDecisionPolicy = sdk.MsgTypeURL(&group.MsgUpdateGroupPolicyDecisionPolicy{})
TypeMsgUpdateGroupPolicyMetadata = sdk.MsgTypeURL(&group.MsgUpdateGroupPolicyMetadata{})
TypeMsgSubmitProposal = sdk.MsgTypeURL(&group.MsgSubmitProposal{})
TypeMsgWithdrawProposal = sdk.MsgTypeURL(&group.MsgWithdrawProposal{})
TypeMsgVote = sdk.MsgTypeURL(&group.MsgVote{})
TypeMsgExec = sdk.MsgTypeURL(&group.MsgExec{})
TypeMsgLeaveGroup = sdk.MsgTypeURL(&group.MsgLeaveGroup{})
)
// Simulation operation weights constants
const (
OpMsgCreateGroup = "op_weight_msg_create_group"
OpMsgUpdateGroupAdmin = "op_weight_msg_update_group_admin"
OpMsgUpdateGroupMetadata = "op_wieght_msg_update_group_metadata"
OpMsgUpdateGroupMembers = "op_weight_msg_update_group_members"
OpMsgCreateGroupPolicy = "op_weight_msg_create_group_account"
OpMsgCreateGroupWithPolicy = "op_weight_msg_create_group_with_policy"
OpMsgUpdateGroupPolicyAdmin = "op_weight_msg_update_group_account_admin"
OpMsgUpdateGroupPolicyDecisionPolicy = "op_weight_msg_update_group_account_decision_policy"
OpMsgUpdateGroupPolicyMetaData = "op_weight_msg_update_group_account_metadata"
OpMsgSubmitProposal = "op_weight_msg_submit_proposal"
OpMsgWithdrawProposal = "op_weight_msg_withdraw_proposal"
OpMsgVote = "op_weight_msg_vote"
OpMsgExec = "ops_weight_msg_exec"
OpMsgLeaveGroup = "ops_weight_msg_leave_group"
)
// 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.
const (
WeightMsgCreateGroup = 100
WeightMsgCreateGroupPolicy = 50
WeightMsgSubmitProposal = 90
WeightMsgVote = 90
WeightMsgExec = 90
WeightMsgLeaveGroup = 5
WeightMsgUpdateGroupMetadata = 5
WeightMsgUpdateGroupAdmin = 5
WeightMsgUpdateGroupMembers = 5
WeightMsgUpdateGroupPolicyAdmin = 5
WeightMsgUpdateGroupPolicyDecisionPolicy = 5
WeightMsgUpdateGroupPolicyMetadata = 5
WeightMsgWithdrawProposal = 20
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
func WeightedOperations(
registry cdctypes.InterfaceRegistry,
appParams simtypes.AppParams, cdc codec.JSONCodec, txGen client.TxConfig,
ak group.AccountKeeper, bk group.BankKeeper, k keeper.Keeper,
appCdc gogoprotoany.AnyUnpacker,
) simulation.WeightedOperations {
var (
weightMsgCreateGroup int
weightMsgUpdateGroupAdmin int
weightMsgUpdateGroupMetadata int
weightMsgUpdateGroupMembers int
weightMsgCreateGroupPolicy int
weightMsgUpdateGroupPolicyAdmin int
weightMsgUpdateGroupPolicyDecisionPolicy int
weightMsgUpdateGroupPolicyMetadata int
weightMsgSubmitProposal int
weightMsgVote int
weightMsgExec int
weightMsgLeaveGroup int
weightMsgWithdrawProposal int
weightMsgCreateGroupWithPolicy int
)
appParams.GetOrGenerate(OpMsgCreateGroup, &weightMsgCreateGroup, nil, func(_ *rand.Rand) {
weightMsgCreateGroup = WeightMsgCreateGroup
})
appParams.GetOrGenerate(OpMsgCreateGroupPolicy, &weightMsgCreateGroupPolicy, nil, func(_ *rand.Rand) {
weightMsgCreateGroupPolicy = WeightMsgCreateGroupPolicy
})
appParams.GetOrGenerate(OpMsgLeaveGroup, &weightMsgLeaveGroup, nil, func(_ *rand.Rand) {
weightMsgLeaveGroup = WeightMsgLeaveGroup
})
appParams.GetOrGenerate(OpMsgCreateGroupWithPolicy, &weightMsgCreateGroupWithPolicy, nil, func(_ *rand.Rand) {
weightMsgCreateGroupWithPolicy = WeightMsgCreateGroupWithPolicy
})
appParams.GetOrGenerate(OpMsgSubmitProposal, &weightMsgSubmitProposal, nil, func(_ *rand.Rand) {
weightMsgSubmitProposal = WeightMsgSubmitProposal
})
appParams.GetOrGenerate(OpMsgVote, &weightMsgVote, nil, func(_ *rand.Rand) {
weightMsgVote = WeightMsgVote
})
appParams.GetOrGenerate(OpMsgExec, &weightMsgExec, nil, func(_ *rand.Rand) {
weightMsgExec = WeightMsgExec
})
appParams.GetOrGenerate(OpMsgUpdateGroupMetadata, &weightMsgUpdateGroupMetadata, nil, func(_ *rand.Rand) {
weightMsgUpdateGroupMetadata = WeightMsgUpdateGroupMetadata
})
appParams.GetOrGenerate(OpMsgUpdateGroupAdmin, &weightMsgUpdateGroupAdmin, nil, func(_ *rand.Rand) {
weightMsgUpdateGroupAdmin = WeightMsgUpdateGroupAdmin
})
appParams.GetOrGenerate(OpMsgUpdateGroupMembers, &weightMsgUpdateGroupMembers, nil, func(_ *rand.Rand) {
weightMsgUpdateGroupMembers = WeightMsgUpdateGroupMembers
})
appParams.GetOrGenerate(OpMsgUpdateGroupPolicyAdmin, &weightMsgUpdateGroupPolicyAdmin, nil, func(_ *rand.Rand) {
weightMsgUpdateGroupPolicyAdmin = WeightMsgUpdateGroupPolicyAdmin
})
appParams.GetOrGenerate(OpMsgUpdateGroupPolicyDecisionPolicy, &weightMsgUpdateGroupPolicyDecisionPolicy, nil, func(_ *rand.Rand) {
weightMsgUpdateGroupPolicyDecisionPolicy = WeightMsgUpdateGroupPolicyDecisionPolicy
})
appParams.GetOrGenerate(OpMsgUpdateGroupPolicyMetaData, &weightMsgUpdateGroupPolicyMetadata, nil, func(_ *rand.Rand) {
weightMsgUpdateGroupPolicyMetadata = WeightMsgUpdateGroupPolicyMetadata
})
appParams.GetOrGenerate(OpMsgWithdrawProposal, &weightMsgWithdrawProposal, nil, func(_ *rand.Rand) {
weightMsgWithdrawProposal = WeightMsgWithdrawProposal
})
pCdc := codec.NewProtoCodec(registry)
state := NewSharedState()
// create two proposals for weightedOperations
var createProposalOps simulation.WeightedOperations
for i := 0; i < 2; i++ {
createProposalOps = append(createProposalOps, simulation.NewWeightedOperation(
weightMsgSubmitProposal,
SimulateMsgSubmitProposal(pCdc, txGen, ak, bk, k, state),
))
}
wPreCreateProposalOps := simulation.WeightedOperations{
simulation.NewWeightedOperation(
weightMsgCreateGroup,
SimulateMsgCreateGroup(pCdc, txGen, ak, bk),
),
simulation.NewWeightedOperation(
weightMsgCreateGroupPolicy,
SimulateMsgCreateGroupPolicy(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgCreateGroupWithPolicy,
SimulateMsgCreateGroupWithPolicy(pCdc, txGen, ak, bk),
),
}
wPostCreateProposalOps := simulation.WeightedOperations{
simulation.NewWeightedOperation(
WeightMsgWithdrawProposal,
SimulateMsgWithdrawProposal(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgVote,
SimulateMsgVote(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgExec,
SimulateMsgExec(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgUpdateGroupMetadata,
SimulateMsgUpdateGroupMetadata(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgUpdateGroupAdmin,
SimulateMsgUpdateGroupAdmin(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgUpdateGroupMembers,
SimulateMsgUpdateGroupMembers(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgUpdateGroupPolicyAdmin,
SimulateMsgUpdateGroupPolicyAdmin(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgUpdateGroupPolicyDecisionPolicy,
SimulateMsgUpdateGroupPolicyDecisionPolicy(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgUpdateGroupPolicyMetadata,
SimulateMsgUpdateGroupPolicyMetadata(pCdc, txGen, ak, bk, k, state),
),
simulation.NewWeightedOperation(
weightMsgLeaveGroup,
SimulateMsgLeaveGroup(pCdc, txGen, k, ak, bk, state),
),
}
return append(wPreCreateProposalOps, append(createProposalOps, wPostCreateProposalOps...)...)
}
// SimulateMsgCreateGroup generates a MsgCreateGroup with random values
func SimulateMsgCreateGroup(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, ctx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
acc, _ := simtypes.RandomAcc(r, accounts)
account := ak.GetAccount(ctx, acc.Address)
accAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "error getting account address"), nil, err
}
spendableCoins := bk.SpendableCoins(ctx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "fee error"), nil, err
}
members, err := genGroupMembers(r, accounts, ak.AddressCodec())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "error generating group members"), nil, err
}
msg := &group.MsgCreateGroup{Admin: accAddr, Members: members, Metadata: simtypes.RandStringOfLength(r, 10)}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgCreateGroupWithPolicy generates a MsgCreateGroupWithPolicy with random values
func SimulateMsgCreateGroupWithPolicy(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, ctx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
acc, _ := simtypes.RandomAcc(r, accounts)
account := ak.GetAccount(ctx, acc.Address)
accAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "error getting account address"), nil, err
}
spendableCoins := bk.SpendableCoins(ctx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "fee error"), nil, err
}
members, err := genGroupMembers(r, accounts, ak.AddressCodec())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "error generating group members"), nil, err
}
decisionPolicy := &group.ThresholdDecisionPolicy{
Threshold: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
Windows: &group.DecisionPolicyWindows{
VotingPeriod: time.Second * time.Duration(30*24*60*60),
},
}
msg := &group.MsgCreateGroupWithPolicy{
Admin: accAddr,
Members: members,
GroupMetadata: simtypes.RandStringOfLength(r, 10),
GroupPolicyMetadata: simtypes.RandStringOfLength(r, 10),
GroupPolicyAsAdmin: r.Float32() < 0.5,
}
err = msg.SetDecisionPolicy(decisionPolicy)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to set decision policy"), nil, err
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupWithPolicy, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, nil
}
}
// SimulateMsgCreateGroupPolicy generates a NewMsgCreateGroupPolicy with random values
func SimulateMsgCreateGroupPolicy(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupPolicy, ""), nil, err
}
if groupInfo == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupPolicy, ""), nil, nil
}
groupID := groupInfo.Id
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupPolicy, "fee error"), nil, err
}
accAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupPolicy, "error generating admin address"), nil, err
}
msg, err := group.NewMsgCreateGroupPolicy(
accAddr,
groupID,
simtypes.RandStringOfLength(r, 10),
&group.ThresholdDecisionPolicy{
Threshold: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
Windows: &group.DecisionPolicyWindows{
VotingPeriod: time.Second * time.Duration(30*24*60*60),
},
},
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupPolicy, err.Error()), nil, err
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroupPolicy, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
fmt.Printf("ERR DELIVER %v\n", err)
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgSubmitProposal generates a NewMsgSubmitProposal with random values
func SimulateMsgSubmitProposal(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, ""), nil, err
}
if g == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, "no group found"), nil, nil
}
if groupPolicy == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, "no group policy found"), nil, nil
}
groupID := g.Id
groupPolicyAddr := groupPolicy.Address
// Return a no-op if we know the proposal cannot be created
policy, err := groupPolicy.GetDecisionPolicy()
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, ""), nil, nil
}
err = policy.Validate(*g, group.DefaultConfig())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, ""), nil, nil
}
// Pick a random member from the group
acc, account, err := randomMember(sdkCtx, r, k, ak, accounts, groupID)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, ""), nil, err
}
if account == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, "no group member found"), nil, nil
}
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, "fee error"), nil, err
}
accAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, "error getting account address"), nil, err
}
msg := &group.MsgSubmitProposal{
GroupPolicyAddress: groupPolicyAddr,
Proposers: []string{accAddr},
Metadata: simtypes.RandStringOfLength(r, 10),
Title: "Test Proposal",
Summary: "Summary of the proposal",
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgUpdateGroupAdmin generates a MsgUpdateGroupAdmin with random values
func SimulateMsgUpdateGroupAdmin(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, ""), nil, err
}
if groupInfo == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, ""), nil, nil
}
groupID := groupInfo.Id
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, "fee error"), nil, err
}
if len(accounts) == 1 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, "can't set a new admin with only one account"), nil, nil
}
newAdmin, _ := simtypes.RandomAcc(r, accounts)
// disallow setting current admin as new admin
for acc.PubKey.Equals(newAdmin.PubKey) {
newAdmin, _ = simtypes.RandomAcc(r, accounts)
}
accAddr, err := ak.AddressCodec().BytesToString(account.GetAddress())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, "error getting admin address"), nil, err
}
newAdminAddr, err := ak.AddressCodec().BytesToString(newAdmin.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, "error getting new admin address"), nil, err
}
msg := &group.MsgUpdateGroupAdmin{
GroupId: groupID,
Admin: accAddr,
NewAdmin: newAdminAddr,
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupAdmin, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgUpdateGroupMetadata generates a MsgUpdateGroupMetadata with random values
func SimulateMsgUpdateGroupMetadata(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMetadata, ""), nil, err
}
if groupInfo == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMetadata, ""), nil, nil
}
groupID := groupInfo.Id
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMetadata, "fee error"), nil, err
}
adminAddr, err := ak.AddressCodec().BytesToString(account.GetAddress())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMetadata, "error getting admin address"), nil, err
}
msg := &group.MsgUpdateGroupMetadata{
GroupId: groupID,
Admin: adminAddr,
Metadata: simtypes.RandStringOfLength(r, 10),
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMetadata, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgUpdateGroupMembers generates a MsgUpdateGroupMembers with random values
func SimulateMsgUpdateGroupMembers(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
groupInfo, acc, account, err := randomGroup(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMembers, ""), nil, err
}
if groupInfo == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMembers, ""), nil, nil
}
groupID := groupInfo.Id
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMembers, "fee error"), nil, err
}
members, err := genGroupMembers(r, accounts, ak.AddressCodec())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgCreateGroup, "error generating group members"), nil, err
}
ctx := sdk.UnwrapSDKContext(sdkCtx)
res, err := k.GroupMembers(ctx, &group.QueryGroupMembersRequest{GroupId: groupID})
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMembers, "group members"), nil, err
}
// set existing random group member weight to zero to remove from the group
existigMembers := res.Members
if len(existigMembers) > 0 {
memberToRemove := existigMembers[r.Intn(len(existigMembers))]
var isDuplicateMember bool
for idx, m := range members {
if m.Address == memberToRemove.Member.Address {
members[idx].Weight = "0"
isDuplicateMember = true
break
}
}
if !isDuplicateMember {
m := memberToRemove.Member
m.Weight = "0"
members = append(members, group.MemberToMemberRequest(m))
}
}
adminAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMembers, "error getting admin address"), nil, err
}
msg := &group.MsgUpdateGroupMembers{
GroupId: groupID,
Admin: adminAddr,
MemberUpdates: members,
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupMembers, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgUpdateGroupPolicyAdmin generates a MsgUpdateGroupPolicyAdmin with random values
func SimulateMsgUpdateGroupPolicyAdmin(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, ""), nil, err
}
if groupPolicy == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, "no group policy found"), nil, nil
}
groupPolicyAddr := groupPolicy.Address
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, "fee error"), nil, err
}
if len(accounts) == 1 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, "can't set a new admin with only one account"), nil, nil
}
newAdmin, _ := simtypes.RandomAcc(r, accounts)
// disallow setting current admin as new admin
for acc.PubKey.Equals(newAdmin.PubKey) {
newAdmin, _ = simtypes.RandomAcc(r, accounts)
}
adminAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, "error getting admin address"), nil, err
}
newAdminAddr, err := ak.AddressCodec().BytesToString(newAdmin.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, "error getting new admin address"), nil, err
}
msg := &group.MsgUpdateGroupPolicyAdmin{
Admin: adminAddr,
GroupPolicyAddress: groupPolicyAddr,
NewAdmin: newAdminAddr,
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyAdmin, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// // SimulateMsgUpdateGroupPolicyDecisionPolicy generates a NewMsgUpdateGroupPolicyDecisionPolicy with random values
func SimulateMsgUpdateGroupPolicyDecisionPolicy(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, ""), nil, err
}
if groupPolicy == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, "no group policy found"), nil, nil
}
groupPolicyAddr := groupPolicy.Address
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, "fee error"), nil, err
}
groupPolicyBech32, err := sdk.AccAddressFromBech32(groupPolicyAddr)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, fmt.Sprintf("fail to decide bech32 address: %s", err.Error())), nil, nil
}
accAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, "error getting admin address"), nil, err
}
groupPolicyStrAddr, err := ak.AddressCodec().BytesToString(groupPolicyBech32)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, "error group policy admin address"), nil, err
}
msg, err := group.NewMsgUpdateGroupPolicyDecisionPolicy(accAddr, groupPolicyStrAddr, &group.ThresholdDecisionPolicy{
Threshold: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
Windows: &group.DecisionPolicyWindows{
VotingPeriod: time.Second * time.Duration(simtypes.RandIntBetween(r, 100, 1000)),
},
})
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, err.Error()), nil, err
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// // SimulateMsgUpdateGroupPolicyMetadata generates a MsgUpdateGroupPolicyMetadata with random values
func SimulateMsgUpdateGroupPolicyMetadata(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, ""), nil, err
}
if groupPolicy == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, "no group policy found"), nil, nil
}
groupPolicyAddr := groupPolicy.Address
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, "fee error"), nil, err
}
adminAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, "error getting admin address"), nil, err
}
msg := &group.MsgUpdateGroupPolicyMetadata{
Admin: adminAddr,
GroupPolicyAddress: groupPolicyAddr,
Metadata: simtypes.RandStringOfLength(r, 10),
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgWithdrawProposal generates a MsgWithdrawProposal with random values
func SimulateMsgWithdrawProposal(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, ""), nil, err
}
if g == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "no group found"), nil, nil
}
if groupPolicy == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "no group policy found"), nil, nil
}
groupPolicyAddr := groupPolicy.Address
policy, err := groupPolicy.GetDecisionPolicy()
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, err.Error()), nil, nil
}
err = policy.Validate(*g, group.DefaultConfig())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, err.Error()), nil, nil
}
proposalsResult, err := k.ProposalsByGroupPolicy(sdkCtx, &group.QueryProposalsByGroupPolicyRequest{Address: groupPolicyAddr})
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "fail to query group info"), nil, err
}
proposals := proposalsResult.GetProposals()
if len(proposals) == 0 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "no proposals found"), nil, nil
}
var proposal *group.Proposal
proposalID := -1
for _, p := range proposals {
if p.Status == group.PROPOSAL_STATUS_SUBMITTED {
timeout := p.VotingPeriodEnd
proposal = p
proposalID = int(p.Id)
if timeout.Before(sdkCtx.HeaderInfo().Time) || timeout.Equal(sdkCtx.HeaderInfo().Time) {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "voting period ended: skipping"), nil, nil
}
break
}
}
// return no-op if no proposal found
if proposalID == -1 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "no proposals found"), nil, nil
}
// select a random proposer
proposers := proposal.Proposers
n := randIntInRange(r, len(proposers))
proposerIdx, err := findAccount(accounts, proposers[n], ak.AddressCodec())
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "could not find account"), nil, err
}
proposer := accounts[proposerIdx]
proposerAcc := ak.GetAccount(sdkCtx, proposer.Address)
spendableCoins := bk.SpendableCoins(sdkCtx, proposer.Address)
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "fee error"), nil, err
}
proposerAddr, err := ak.AddressCodec().BytesToString(proposer.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, "error getting voter address"), nil, err
}
msg := &group.MsgWithdrawProposal{
ProposalId: uint64(proposalID),
Address: proposerAddr,
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{proposerAcc.GetAccountNumber()},
[]uint64{proposerAcc.GetSequence()},
proposer.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
if strings.Contains(err.Error(), "group was modified") || strings.Contains(err.Error(), "group policy was modified") {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "no-op:group/group-policy was modified"), nil, nil
}
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgVote generates a MsgVote with random values
func SimulateMsgVote(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
g, groupPolicy, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, ""), nil, err
}
if g == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "no group found"), nil, nil
}
if groupPolicy == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "no group policy found"), nil, nil
}
groupPolicyAddr := groupPolicy.Address
// Pick a random member from the group
acc, account, err := randomMember(sdkCtx, r, k, ak, accounts, g.Id)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, ""), nil, err
}
if account == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "no group member found"), nil, nil
}
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "fee error"), nil, err
}
proposalsResult, err := k.ProposalsByGroupPolicy(sdkCtx, &group.QueryProposalsByGroupPolicyRequest{Address: groupPolicyAddr})
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "fail to query group info"), nil, err
}
proposals := proposalsResult.GetProposals()
if len(proposals) == 0 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "no proposals found"), nil, nil
}
proposalID := -1
for _, p := range proposals {
if p.Status == group.PROPOSAL_STATUS_SUBMITTED {
timeout := p.VotingPeriodEnd
proposalID = int(p.Id)
if timeout.Before(sdkCtx.HeaderInfo().Time) || timeout.Equal(sdkCtx.HeaderInfo().Time) {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "voting period ended: skipping"), nil, nil
}
break
}
}
// return no-op if no proposal found
if proposalID == -1 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "no proposals found"), nil, nil
}
voterAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "error getting voter address"), nil, err
}
// Ensure member hasn't already voted
res, _ := k.VoteByProposalVoter(sdkCtx, &group.QueryVoteByProposalVoterRequest{
Voter: voterAddr,
ProposalId: uint64(proposalID),
})
if res != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgVote, "member has already voted"), nil, nil
}
msg := &group.MsgVote{
ProposalId: uint64(proposalID),
Voter: voterAddr,
Option: group.VOTE_OPTION_YES,
Metadata: simtypes.RandStringOfLength(r, 10),
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
if strings.Contains(err.Error(), "group was modified") || strings.Contains(err.Error(), "group policy was modified") {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "no-op:group/group-policy was modified"), nil, nil
}
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// // SimulateMsgExec generates a MsgExec with random values
func SimulateMsgExec(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
ak group.AccountKeeper,
bk group.BankKeeper,
k keeper.Keeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, ""), nil, err
}
if groupPolicy == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "no group policy found"), nil, nil
}
groupPolicyAddr := groupPolicy.Address
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "fee error"), nil, err
}
proposalsResult, err := k.ProposalsByGroupPolicy(sdkCtx, &group.QueryProposalsByGroupPolicyRequest{Address: groupPolicyAddr})
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "fail to query group info"), nil, err
}
proposals := proposalsResult.GetProposals()
if len(proposals) == 0 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "no proposals found"), nil, nil
}
proposalID := -1
for _, proposal := range proposals {
if proposal.Status == group.PROPOSAL_STATUS_ACCEPTED {
proposalID = int(proposal.Id)
break
}
}
// return no-op if no proposal found
if proposalID == -1 {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "no proposals found"), nil, nil
}
accAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "error getting executor address"), nil, err
}
msg := &group.MsgExec{
ProposalId: uint64(proposalID),
Executor: accAddr,
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyMetadata, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
if strings.Contains(err.Error(), "group was modified") || strings.Contains(err.Error(), "group policy was modified") {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "no-op:group/group-policy was modified"), nil, nil
}
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
// SimulateMsgLeaveGroup generates a MsgLeaveGroup with random values
func SimulateMsgLeaveGroup(
cdc *codec.ProtoCodec,
txGen client.TxConfig,
k keeper.Keeper,
ak group.AccountKeeper,
bk group.BankKeeper,
s *SharedState,
) simtypes.Operation {
return func(
r *rand.Rand, app simtypes.AppEntrypoint, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
groupInfo, policyInfo, _, _, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts, s)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, ""), nil, err
}
if policyInfo == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, "no policy found"), nil, nil
}
// Pick a random member from the group
acc, account, err := randomMember(sdkCtx, r, k, ak, accounts, groupInfo.Id)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, ""), nil, err
}
if account == nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, "no group member found"), nil, nil
}
spendableCoins := bk.SpendableCoins(sdkCtx, acc.Address)
fees, err := simtypes.RandomFees(r, spendableCoins)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, "fee error"), nil, err
}
accAddr, err := ak.AddressCodec().BytesToString(acc.Address)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, "error getting account address"), nil, err
}
msg := &group.MsgLeaveGroup{
Address: accAddr,
GroupId: groupInfo.Id,
}
tx, err := simtestutil.GenSignedMockTx(
r,
txGen,
[]sdk.Msg{msg},
fees,
simtestutil.DefaultGenTxGas,
chainID,
[]uint64{account.GetAccountNumber()},
[]uint64{account.GetSequence()},
acc.PrivKey,
)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, TypeMsgLeaveGroup, "unable to generate mock tx"), nil, err
}
_, _, err = app.SimDeliver(txGen.TxEncoder(), tx)
if err != nil {
return simtypes.NoOpMsg(group.ModuleName, sdk.MsgTypeURL(msg), err.Error()), nil, err
}
return simtypes.NewOperationMsg(msg, true, ""), nil, err
}
}
func randomGroup(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
ctx sdk.Context, accounts []simtypes.Account, s *SharedState,
) (groupInfo *group.GroupInfo, acc simtypes.Account, account sdk.AccountI, err error) {
groupID := k.GetGroupSequence(ctx)
if initialGroupID := s.getMinGroupID(); initialGroupID == unsetGroupID {
s.setMinGroupID(groupID)
} else if initialGroupID < groupID {
groupID = uint64(simtypes.RandIntBetween(r, int(initialGroupID+1), int(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 {
return nil, simtypes.Account{}, nil, nil
}
res, err := k.GroupInfo(ctx, &group.QueryGroupInfoRequest{GroupId: groupID})
if err != nil {
return nil, simtypes.Account{}, nil, err
}
groupInfo = res.Info
groupAdmin := groupInfo.Admin
found := -1
for i := range accounts {
addr, err := ak.AddressCodec().BytesToString(accounts[i].Address)
if err != nil {
return nil, simtypes.Account{}, nil, err
}
if addr == groupAdmin {
found = i
break
}
}
if found < 0 {
return nil, simtypes.Account{}, nil, nil
}
acc = accounts[found]
account = ak.GetAccount(ctx, acc.Address)
return groupInfo, acc, account, nil
}
func randomGroupPolicy(r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
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 {
return nil, nil, simtypes.Account{}, nil, err
}
if groupInfo == nil {
return nil, nil, simtypes.Account{}, nil, nil
}
groupID := groupInfo.Id
result, err := k.GroupPoliciesByGroup(ctx, &group.QueryGroupPoliciesByGroupRequest{GroupId: groupID})
if err != nil {
return groupInfo, nil, simtypes.Account{}, nil, err
}
n := randIntInRange(r, len(result.GroupPolicies))
if n < 0 {
return groupInfo, nil, simtypes.Account{}, nil, nil
}
groupPolicyInfo = result.GroupPolicies[n]
idx, err := findAccount(accounts, groupPolicyInfo.Admin, ak.AddressCodec())
if err != nil {
return groupInfo, nil, simtypes.Account{}, nil, nil
}
if idx < 0 {
return groupInfo, nil, simtypes.Account{}, nil, nil
}
acc = accounts[idx]
account = ak.GetAccount(ctx, acc.Address)
return groupInfo, groupPolicyInfo, acc, account, nil
}
func randomMember(ctx context.Context, r *rand.Rand, k keeper.Keeper, ak group.AccountKeeper,
accounts []simtypes.Account, groupID uint64,
) (acc simtypes.Account, account sdk.AccountI, err error) {
res, err := k.GroupMembers(ctx, &group.QueryGroupMembersRequest{
GroupId: groupID,
})
if err != nil {
return simtypes.Account{}, nil, err
}
n := randIntInRange(r, len(res.Members))
if n < 0 {
return simtypes.Account{}, nil, err
}
idx, err := findAccount(accounts, res.Members[n].Member.Address, ak.AddressCodec())
if err != nil {
return simtypes.Account{}, nil, err
}
if idx < 0 {
return simtypes.Account{}, nil, err
}
acc = accounts[idx]
account = ak.GetAccount(sdk.UnwrapSDKContext(ctx), acc.Address)
return acc, account, nil
}
func randIntInRange(r *rand.Rand, l int) int {
if l == 0 {
return -1
}
if l == 1 {
return 0
}
return simtypes.RandIntBetween(r, 0, l-1)
}
func findAccount(accounts []simtypes.Account, addr string, addressCodec address.Codec) (idx int, err error) {
idx = -1
for i := range accounts {
accAddr, err := addressCodec.BytesToString(accounts[i].Address)
if err != nil {
return idx, err
}
if accAddr == addr {
idx = i
break
}
}
return idx, err
}
func genGroupMembers(r *rand.Rand, accounts []simtypes.Account, addressCodec address.Codec) ([]group.MemberRequest, error) {
if len(accounts) == 1 {
addr, err := addressCodec.BytesToString(accounts[0].Address)
if err != nil {
return nil, err
}
return []group.MemberRequest{
{
Address: addr,
Weight: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
Metadata: simtypes.RandStringOfLength(r, 10),
},
}, nil
}
max := 5
if len(accounts) < max {
max = len(accounts)
}
membersLen := simtypes.RandIntBetween(r, 1, max)
members := make([]group.MemberRequest, membersLen)
for i := 0; i < membersLen; i++ {
addr, err := addressCodec.BytesToString(accounts[i].Address)
if err != nil {
return nil, err
}
members[i] = group.MemberRequest{
Address: addr,
Weight: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
Metadata: simtypes.RandStringOfLength(r, 10),
}
}
return members, nil
}