306 lines
9.7 KiB
Go
306 lines
9.7 KiB
Go
package simulation
|
|
|
|
import (
|
|
"math/rand"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
|
simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
|
"github.com/cosmos/cosmos-sdk/x/authz"
|
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
|
|
|
banktype "github.com/cosmos/cosmos-sdk/x/bank/types"
|
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
)
|
|
|
|
// authz message types
|
|
var (
|
|
TypeMsgGrant = sdk.MsgTypeURL(&authz.MsgGrant{})
|
|
TypeMsgRevoke = sdk.MsgTypeURL(&authz.MsgRevoke{})
|
|
TypeMsgExec = sdk.MsgTypeURL(&authz.MsgExec{})
|
|
)
|
|
|
|
// Simulation operation weights constants
|
|
const (
|
|
OpWeightMsgGrant = "op_weight_msg_grant"
|
|
OpWeightRevoke = "op_weight_msg_revoke"
|
|
OpWeightExec = "op_weight_msg_execute"
|
|
)
|
|
|
|
// authz operations weights
|
|
const (
|
|
WeightGrant = 100
|
|
WeightRevoke = 90
|
|
WeightExec = 90
|
|
)
|
|
|
|
// WeightedOperations returns all the operations from the module with their respective weights
|
|
func WeightedOperations(
|
|
appParams simtypes.AppParams, cdc codec.JSONCodec, ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper, appCdc cdctypes.AnyUnpacker) simulation.WeightedOperations {
|
|
|
|
var (
|
|
weightMsgGrant int
|
|
weightExec int
|
|
weightRevoke int
|
|
)
|
|
|
|
appParams.GetOrGenerate(cdc, OpWeightMsgGrant, &weightMsgGrant, nil,
|
|
func(_ *rand.Rand) {
|
|
weightMsgGrant = WeightGrant
|
|
},
|
|
)
|
|
|
|
appParams.GetOrGenerate(cdc, OpWeightExec, &weightExec, nil,
|
|
func(_ *rand.Rand) {
|
|
weightExec = WeightExec
|
|
},
|
|
)
|
|
|
|
appParams.GetOrGenerate(cdc, OpWeightRevoke, &weightRevoke, nil,
|
|
func(_ *rand.Rand) {
|
|
weightRevoke = WeightRevoke
|
|
},
|
|
)
|
|
|
|
return simulation.WeightedOperations{
|
|
simulation.NewWeightedOperation(
|
|
weightMsgGrant,
|
|
SimulateMsgGrant(ak, bk, k),
|
|
),
|
|
simulation.NewWeightedOperation(
|
|
weightExec,
|
|
SimulateMsgExec(ak, bk, k, appCdc),
|
|
),
|
|
simulation.NewWeightedOperation(
|
|
weightRevoke,
|
|
SimulateMsgRevoke(ak, bk, k),
|
|
),
|
|
}
|
|
}
|
|
|
|
// SimulateMsgGrant generates a MsgGrant with random values.
|
|
func SimulateMsgGrant(ak authz.AccountKeeper, bk authz.BankKeeper, _ keeper.Keeper) simtypes.Operation {
|
|
return func(
|
|
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
|
|
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
|
granter, _ := simtypes.RandomAcc(r, accs)
|
|
grantee, _ := simtypes.RandomAcc(r, accs)
|
|
|
|
if granter.Address.Equals(grantee.Address) {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "granter and grantee are same"), nil, nil
|
|
}
|
|
|
|
granterAcc := ak.GetAccount(ctx, granter.Address)
|
|
spendableCoins := bk.SpendableCoins(ctx, granter.Address)
|
|
fees, err := simtypes.RandomFees(r, ctx, spendableCoins)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err
|
|
}
|
|
|
|
spendLimit := spendableCoins.Sub(fees)
|
|
if spendLimit == nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "spend limit is nil"), nil, nil
|
|
}
|
|
|
|
expiration := simtypes.RandTimestamp(r)
|
|
if expiration.Before(ctx.BlockTime()) {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "past time"), nil, nil
|
|
}
|
|
msg, err := authz.NewMsgGrant(granter.Address, grantee.Address, generateRandomAuthorization(r, spendLimit), expiration)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err
|
|
}
|
|
txCfg := simappparams.MakeTestEncodingConfig().TxConfig
|
|
tx, err := helpers.GenTx(
|
|
txCfg,
|
|
[]sdk.Msg{msg},
|
|
fees,
|
|
helpers.DefaultGenTxGas,
|
|
chainID,
|
|
[]uint64{granterAcc.GetAccountNumber()},
|
|
[]uint64{granterAcc.GetSequence()},
|
|
granter.PrivKey,
|
|
)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "unable to generate mock tx"), nil, err
|
|
}
|
|
|
|
_, _, err = app.SimDeliver(txCfg.TxEncoder(), tx)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
|
|
}
|
|
return simtypes.NewOperationMsg(msg, true, "", nil), nil, err
|
|
}
|
|
}
|
|
|
|
func generateRandomAuthorization(r *rand.Rand, spendLimit sdk.Coins) authz.Authorization {
|
|
authorizations := make([]authz.Authorization, 2)
|
|
authorizations[0] = banktype.NewSendAuthorization(spendLimit)
|
|
authorizations[1] = authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktype.MsgSend{}))
|
|
|
|
return authorizations[r.Intn(len(authorizations))]
|
|
}
|
|
|
|
// SimulateMsgRevoke generates a MsgRevoke with random values.
|
|
func SimulateMsgRevoke(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper) simtypes.Operation {
|
|
return func(
|
|
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
|
|
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
|
var granterAddr, granteeAddr sdk.AccAddress
|
|
var grant authz.Grant
|
|
hasGrant := false
|
|
|
|
k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, g authz.Grant) bool {
|
|
grant = g
|
|
granterAddr = granter
|
|
granteeAddr = grantee
|
|
hasGrant = true
|
|
return true
|
|
})
|
|
|
|
if !hasGrant {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "no grants"), nil, nil
|
|
}
|
|
|
|
granterAcc, ok := simtypes.FindAccount(accs, granterAddr)
|
|
if !ok {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "account not found")
|
|
}
|
|
|
|
spendableCoins := bk.SpendableCoins(ctx, granterAddr)
|
|
fees, err := simtypes.RandomFees(r, ctx, spendableCoins)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "fee error"), nil, err
|
|
}
|
|
|
|
a, err := grant.GetAuthorization()
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "authorization error"), nil, err
|
|
}
|
|
|
|
msg := authz.NewMsgRevoke(granterAddr, granteeAddr, a.MsgTypeURL())
|
|
txCfg := simappparams.MakeTestEncodingConfig().TxConfig
|
|
account := ak.GetAccount(ctx, granterAddr)
|
|
tx, err := helpers.GenTx(
|
|
txCfg,
|
|
[]sdk.Msg{&msg},
|
|
fees,
|
|
helpers.DefaultGenTxGas,
|
|
chainID,
|
|
[]uint64{account.GetAccountNumber()},
|
|
[]uint64{account.GetSequence()},
|
|
granterAcc.PrivKey,
|
|
)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, err.Error()), nil, err
|
|
}
|
|
|
|
_, _, err = app.SimDeliver(txCfg.TxEncoder(), tx)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "unable to deliver tx"), nil, err
|
|
}
|
|
|
|
return simtypes.NewOperationMsg(&msg, true, "", nil), nil, nil
|
|
}
|
|
}
|
|
|
|
// SimulateMsgExec generates a MsgExec with random values.
|
|
func SimulateMsgExec(ak authz.AccountKeeper, bk authz.BankKeeper, k keeper.Keeper, cdc cdctypes.AnyUnpacker) simtypes.Operation {
|
|
return func(
|
|
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
|
|
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
|
hasGrant := false
|
|
var targetGrant authz.Grant
|
|
var granterAddr sdk.AccAddress
|
|
var granteeAddr sdk.AccAddress
|
|
k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
|
|
targetGrant = grant
|
|
granterAddr = granter
|
|
granteeAddr = grantee
|
|
hasGrant = true
|
|
return true
|
|
})
|
|
|
|
if !hasGrant {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "no grant found"), nil, nil
|
|
}
|
|
|
|
grantee, ok := simtypes.FindAccount(accs, granteeAddr)
|
|
if !ok {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "grantee account not found")
|
|
}
|
|
|
|
if _, ok := simtypes.FindAccount(accs, granterAddr); !ok {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.Wrapf(sdkerrors.ErrNotFound, "granter account not found")
|
|
}
|
|
|
|
granterspendableCoins := bk.SpendableCoins(ctx, granterAddr)
|
|
coins := simtypes.RandSubsetCoins(r, granterspendableCoins)
|
|
|
|
// Check send_enabled status of each sent coin denom
|
|
if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil
|
|
}
|
|
|
|
msg := []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)}
|
|
authorization, err := targetGrant.GetAuthorization()
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
|
|
}
|
|
|
|
sendAuth, ok := authorization.(*banktype.SendAuthorization)
|
|
if !ok {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "not a send authorization"), nil, nil
|
|
}
|
|
|
|
_, err = sendAuth.Accept(ctx, msg[0])
|
|
if err != nil {
|
|
if sdkerrors.ErrInsufficientFunds.Is(err) {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil
|
|
} else {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
|
|
}
|
|
}
|
|
|
|
msgExec := authz.NewMsgExec(granteeAddr, msg)
|
|
granteeSpendableCoins := bk.SpendableCoins(ctx, granteeAddr)
|
|
fees, err := simtypes.RandomFees(r, ctx, granteeSpendableCoins)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "fee error"), nil, err
|
|
}
|
|
|
|
txCfg := simappparams.MakeTestEncodingConfig().TxConfig
|
|
granteeAcc := ak.GetAccount(ctx, granteeAddr)
|
|
tx, err := helpers.GenTx(
|
|
txCfg,
|
|
[]sdk.Msg{&msgExec},
|
|
fees,
|
|
helpers.DefaultGenTxGas,
|
|
chainID,
|
|
[]uint64{granteeAcc.GetAccountNumber()},
|
|
[]uint64{granteeAcc.GetSequence()},
|
|
grantee.PrivKey,
|
|
)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
|
|
}
|
|
|
|
_, _, err = app.SimDeliver(txCfg.TxEncoder(), tx)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
|
|
}
|
|
|
|
err = msgExec.UnpackInterfaces(cdc)
|
|
if err != nil {
|
|
return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "unmarshal error"), nil, err
|
|
}
|
|
return simtypes.NewOperationMsg(&msgExec, true, "success", nil), nil, nil
|
|
}
|
|
}
|