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