github.com/cosmos/cosmos-sdk@v0.50.10/x/authz/simulation/operations.go (about)

     1  package simulation
     2  
     3  import (
     4  	"math/rand"
     5  	"time"
     6  
     7  	"github.com/cosmos/cosmos-sdk/baseapp"
     8  	"github.com/cosmos/cosmos-sdk/client"
     9  	"github.com/cosmos/cosmos-sdk/codec"
    10  	cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
    11  	simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
    12  	sdk "github.com/cosmos/cosmos-sdk/types"
    13  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    14  	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
    15  	"github.com/cosmos/cosmos-sdk/x/authz"
    16  	"github.com/cosmos/cosmos-sdk/x/authz/keeper"
    17  	banktype "github.com/cosmos/cosmos-sdk/x/bank/types"
    18  	"github.com/cosmos/cosmos-sdk/x/simulation"
    19  )
    20  
    21  // authz message types
    22  var (
    23  	TypeMsgGrant  = sdk.MsgTypeURL(&authz.MsgGrant{})
    24  	TypeMsgRevoke = sdk.MsgTypeURL(&authz.MsgRevoke{})
    25  	TypeMsgExec   = sdk.MsgTypeURL(&authz.MsgExec{})
    26  )
    27  
    28  // Simulation operation weights constants
    29  const (
    30  	OpWeightMsgGrant = "op_weight_msg_grant"
    31  	OpWeightRevoke   = "op_weight_msg_revoke"
    32  	OpWeightExec     = "op_weight_msg_execute"
    33  )
    34  
    35  // authz operations weights
    36  const (
    37  	WeightGrant  = 100
    38  	WeightRevoke = 90
    39  	WeightExec   = 90
    40  )
    41  
    42  // WeightedOperations returns all the operations from the module with their respective weights
    43  func WeightedOperations(
    44  	registry cdctypes.InterfaceRegistry,
    45  	appParams simtypes.AppParams,
    46  	cdc codec.JSONCodec,
    47  	txGen client.TxConfig,
    48  	ak authz.AccountKeeper,
    49  	bk authz.BankKeeper,
    50  	k keeper.Keeper,
    51  ) simulation.WeightedOperations {
    52  	var (
    53  		weightMsgGrant int
    54  		weightExec     int
    55  		weightRevoke   int
    56  	)
    57  
    58  	appParams.GetOrGenerate(OpWeightMsgGrant, &weightMsgGrant, nil, func(_ *rand.Rand) {
    59  		weightMsgGrant = WeightGrant
    60  	})
    61  
    62  	appParams.GetOrGenerate(OpWeightExec, &weightExec, nil, func(_ *rand.Rand) {
    63  		weightExec = WeightExec
    64  	})
    65  
    66  	appParams.GetOrGenerate(OpWeightRevoke, &weightRevoke, nil, func(_ *rand.Rand) {
    67  		weightRevoke = WeightRevoke
    68  	})
    69  
    70  	pCdc := codec.NewProtoCodec(registry)
    71  
    72  	return simulation.WeightedOperations{
    73  		simulation.NewWeightedOperation(
    74  			weightMsgGrant,
    75  			SimulateMsgGrant(pCdc, txGen, ak, bk, k),
    76  		),
    77  		simulation.NewWeightedOperation(
    78  			weightExec,
    79  			SimulateMsgExec(pCdc, txGen, ak, bk, k, registry),
    80  		),
    81  		simulation.NewWeightedOperation(
    82  			weightRevoke,
    83  			SimulateMsgRevoke(pCdc, txGen, ak, bk, k),
    84  		),
    85  	}
    86  }
    87  
    88  // SimulateMsgGrant generates a MsgGrant with random values.
    89  func SimulateMsgGrant(
    90  	cdc *codec.ProtoCodec,
    91  	txCfg client.TxConfig,
    92  	ak authz.AccountKeeper,
    93  	bk authz.BankKeeper,
    94  	_ keeper.Keeper,
    95  ) simtypes.Operation {
    96  	return func(
    97  		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
    98  	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
    99  		granter, _ := simtypes.RandomAcc(r, accs)
   100  		grantee, _ := simtypes.RandomAcc(r, accs)
   101  
   102  		if granter.Address.Equals(grantee.Address) {
   103  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "granter and grantee are same"), nil, nil
   104  		}
   105  
   106  		granterAcc := ak.GetAccount(ctx, granter.Address)
   107  		spendableCoins := bk.SpendableCoins(ctx, granter.Address)
   108  		fees, err := simtypes.RandomFees(r, ctx, spendableCoins)
   109  		if err != nil {
   110  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err
   111  		}
   112  
   113  		spendLimit := spendableCoins.Sub(fees...)
   114  		if len(spendLimit) == 0 {
   115  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "spend limit is nil"), nil, nil
   116  		}
   117  
   118  		var expiration *time.Time
   119  		t1 := simtypes.RandTimestamp(r)
   120  		if !t1.Before(ctx.BlockTime()) {
   121  			expiration = &t1
   122  		}
   123  		randomAuthz := generateRandomAuthorization(r, spendLimit)
   124  
   125  		msg, err := authz.NewMsgGrant(granter.Address, grantee.Address, randomAuthz, expiration)
   126  		if err != nil {
   127  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, err.Error()), nil, err
   128  		}
   129  		tx, err := simtestutil.GenSignedMockTx(
   130  			r,
   131  			txCfg,
   132  			[]sdk.Msg{msg},
   133  			fees,
   134  			simtestutil.DefaultGenTxGas,
   135  			chainID,
   136  			[]uint64{granterAcc.GetAccountNumber()},
   137  			[]uint64{granterAcc.GetSequence()},
   138  			granter.PrivKey,
   139  		)
   140  		if err != nil {
   141  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgGrant, "unable to generate mock tx"), nil, err
   142  		}
   143  
   144  		_, _, err = app.SimTxFinalizeBlock(txCfg.TxEncoder(), tx)
   145  		if err != nil {
   146  			return simtypes.NoOpMsg(authz.ModuleName, sdk.MsgTypeURL(msg), "unable to deliver tx"), nil, err
   147  		}
   148  		return simtypes.NewOperationMsg(msg, true, ""), nil, err
   149  	}
   150  }
   151  
   152  func generateRandomAuthorization(r *rand.Rand, spendLimit sdk.Coins) authz.Authorization {
   153  	authorizations := make([]authz.Authorization, 2)
   154  	sendAuthz := banktype.NewSendAuthorization(spendLimit, nil)
   155  	authorizations[0] = sendAuthz
   156  	authorizations[1] = authz.NewGenericAuthorization(sdk.MsgTypeURL(&banktype.MsgSend{}))
   157  
   158  	return authorizations[r.Intn(len(authorizations))]
   159  }
   160  
   161  // SimulateMsgRevoke generates a MsgRevoke with random values.
   162  func SimulateMsgRevoke(
   163  	cdc *codec.ProtoCodec,
   164  	txCfg client.TxConfig,
   165  	ak authz.AccountKeeper,
   166  	bk authz.BankKeeper,
   167  	k keeper.Keeper,
   168  ) simtypes.Operation {
   169  	return func(
   170  		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
   171  	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
   172  		var granterAddr, granteeAddr sdk.AccAddress
   173  		var grant authz.Grant
   174  		hasGrant := false
   175  
   176  		k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, g authz.Grant) bool {
   177  			grant = g
   178  			granterAddr = granter
   179  			granteeAddr = grantee
   180  			hasGrant = true
   181  			return true
   182  		})
   183  
   184  		if !hasGrant {
   185  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "no grants"), nil, nil
   186  		}
   187  
   188  		granterAcc, ok := simtypes.FindAccount(accs, granterAddr)
   189  		if !ok {
   190  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "account not found"), nil, sdkerrors.ErrNotFound.Wrapf("account not found")
   191  		}
   192  
   193  		spendableCoins := bk.SpendableCoins(ctx, granterAddr)
   194  		fees, err := simtypes.RandomFees(r, ctx, spendableCoins)
   195  		if err != nil {
   196  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "fee error"), nil, err
   197  		}
   198  
   199  		a, err := grant.GetAuthorization()
   200  		if err != nil {
   201  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "authorization error"), nil, err
   202  		}
   203  
   204  		msg := authz.NewMsgRevoke(granterAddr, granteeAddr, a.MsgTypeURL())
   205  		account := ak.GetAccount(ctx, granterAddr)
   206  		tx, err := simtestutil.GenSignedMockTx(
   207  			r,
   208  			txCfg,
   209  			[]sdk.Msg{&msg},
   210  			fees,
   211  			simtestutil.DefaultGenTxGas,
   212  			chainID,
   213  			[]uint64{account.GetAccountNumber()},
   214  			[]uint64{account.GetSequence()},
   215  			granterAcc.PrivKey,
   216  		)
   217  		if err != nil {
   218  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, err.Error()), nil, err
   219  		}
   220  
   221  		_, _, err = app.SimTxFinalizeBlock(txCfg.TxEncoder(), tx)
   222  		if err != nil {
   223  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "unable to execute tx: "+err.Error()), nil, err
   224  		}
   225  
   226  		return simtypes.NewOperationMsg(&msg, true, ""), nil, nil
   227  	}
   228  }
   229  
   230  // SimulateMsgExec generates a MsgExec with random values.
   231  func SimulateMsgExec(
   232  	cdc *codec.ProtoCodec,
   233  	txCfg client.TxConfig,
   234  	ak authz.AccountKeeper,
   235  	bk authz.BankKeeper,
   236  	k keeper.Keeper,
   237  	unpacker cdctypes.AnyUnpacker,
   238  ) simtypes.Operation {
   239  	return func(
   240  		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
   241  	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
   242  		var granterAddr sdk.AccAddress
   243  		var granteeAddr sdk.AccAddress
   244  		var sendAuth *banktype.SendAuthorization
   245  		var err error
   246  		k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool {
   247  			granterAddr = granter
   248  			granteeAddr = grantee
   249  			var a authz.Authorization
   250  			a, err = grant.GetAuthorization()
   251  			if err != nil {
   252  				return true
   253  			}
   254  			var ok bool
   255  			sendAuth, ok = a.(*banktype.SendAuthorization)
   256  			return ok
   257  		})
   258  
   259  		if err != nil {
   260  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
   261  		}
   262  		if sendAuth == nil {
   263  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "no grant found"), nil, nil
   264  		}
   265  
   266  		grantee, ok := simtypes.FindAccount(accs, granteeAddr)
   267  		if !ok {
   268  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.ErrNotFound.Wrapf("grantee account not found")
   269  		}
   270  
   271  		if _, ok := simtypes.FindAccount(accs, granterAddr); !ok {
   272  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgRevoke, "Account not found"), nil, sdkerrors.ErrNotFound.Wrapf("granter account not found")
   273  		}
   274  
   275  		granterspendableCoins := bk.SpendableCoins(ctx, granterAddr)
   276  		coins := simtypes.RandSubsetCoins(r, granterspendableCoins)
   277  		// if coins slice is empty, we can not create valid banktype.MsgSend
   278  		if len(coins) == 0 {
   279  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "empty coins slice"), nil, nil
   280  		}
   281  
   282  		// Check send_enabled status of each sent coin denom
   283  		if err := bk.IsSendEnabledCoins(ctx, coins...); err != nil {
   284  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil
   285  		}
   286  
   287  		msg := []sdk.Msg{banktype.NewMsgSend(granterAddr, granteeAddr, coins)}
   288  
   289  		_, err = sendAuth.Accept(ctx, msg[0])
   290  		if err != nil {
   291  			if sdkerrors.ErrInsufficientFunds.Is(err) {
   292  				return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, nil
   293  			}
   294  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
   295  
   296  		}
   297  
   298  		msgExec := authz.NewMsgExec(granteeAddr, msg)
   299  		granteeSpendableCoins := bk.SpendableCoins(ctx, granteeAddr)
   300  		fees, err := simtypes.RandomFees(r, ctx, granteeSpendableCoins)
   301  		if err != nil {
   302  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "fee error"), nil, err
   303  		}
   304  
   305  		granteeAcc := ak.GetAccount(ctx, granteeAddr)
   306  		tx, err := simtestutil.GenSignedMockTx(
   307  			r,
   308  			txCfg,
   309  			[]sdk.Msg{&msgExec},
   310  			fees,
   311  			simtestutil.DefaultGenTxGas,
   312  			chainID,
   313  			[]uint64{granteeAcc.GetAccountNumber()},
   314  			[]uint64{granteeAcc.GetSequence()},
   315  			grantee.PrivKey,
   316  		)
   317  		if err != nil {
   318  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
   319  		}
   320  
   321  		_, _, err = app.SimTxFinalizeBlock(txCfg.TxEncoder(), tx)
   322  		if err != nil {
   323  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, err.Error()), nil, err
   324  		}
   325  
   326  		err = msgExec.UnpackInterfaces(unpacker)
   327  		if err != nil {
   328  			return simtypes.NoOpMsg(authz.ModuleName, TypeMsgExec, "unmarshal error"), nil, err
   329  		}
   330  		return simtypes.NewOperationMsg(&msgExec, true, "success"), nil, nil
   331  	}
   332  }