github.com/Finschia/finschia-sdk@v0.48.1/x/authz/simulation/operations.go (about)

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