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

     1  package simulation
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math/rand"
     7  
     8  	"cosmossdk.io/collections"
     9  
    10  	"github.com/cosmos/cosmos-sdk/baseapp"
    11  	"github.com/cosmos/cosmos-sdk/client"
    12  	"github.com/cosmos/cosmos-sdk/codec"
    13  	"github.com/cosmos/cosmos-sdk/testutil"
    14  	sdk "github.com/cosmos/cosmos-sdk/types"
    15  	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
    16  	"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
    17  	"github.com/cosmos/cosmos-sdk/x/distribution/types"
    18  	"github.com/cosmos/cosmos-sdk/x/simulation"
    19  )
    20  
    21  // Simulation operation weights constants
    22  const (
    23  	OpWeightMsgSetWithdrawAddress          = "op_weight_msg_set_withdraw_address"
    24  	OpWeightMsgWithdrawDelegationReward    = "op_weight_msg_withdraw_delegation_reward"
    25  	OpWeightMsgWithdrawValidatorCommission = "op_weight_msg_withdraw_validator_commission"
    26  	OpWeightMsgFundCommunityPool           = "op_weight_msg_fund_community_pool"
    27  
    28  	DefaultWeightMsgSetWithdrawAddress          int = 50
    29  	DefaultWeightMsgWithdrawDelegationReward    int = 50
    30  	DefaultWeightMsgWithdrawValidatorCommission int = 50
    31  	DefaultWeightMsgFundCommunityPool           int = 50
    32  )
    33  
    34  // WeightedOperations returns all the operations from the module with their respective weights
    35  func WeightedOperations(
    36  	appParams simtypes.AppParams,
    37  	cdc codec.JSONCodec,
    38  	txConfig client.TxConfig,
    39  	ak types.AccountKeeper,
    40  	bk types.BankKeeper,
    41  	k keeper.Keeper,
    42  	sk types.StakingKeeper,
    43  ) simulation.WeightedOperations {
    44  	var weightMsgSetWithdrawAddress int
    45  	appParams.GetOrGenerate(OpWeightMsgSetWithdrawAddress, &weightMsgSetWithdrawAddress, nil, func(_ *rand.Rand) {
    46  		weightMsgSetWithdrawAddress = DefaultWeightMsgSetWithdrawAddress
    47  	})
    48  
    49  	var weightMsgWithdrawDelegationReward int
    50  	appParams.GetOrGenerate(OpWeightMsgWithdrawDelegationReward, &weightMsgWithdrawDelegationReward, nil, func(_ *rand.Rand) {
    51  		weightMsgWithdrawDelegationReward = DefaultWeightMsgWithdrawDelegationReward
    52  	})
    53  
    54  	var weightMsgWithdrawValidatorCommission int
    55  	appParams.GetOrGenerate(OpWeightMsgWithdrawValidatorCommission, &weightMsgWithdrawValidatorCommission, nil, func(_ *rand.Rand) {
    56  		weightMsgWithdrawValidatorCommission = DefaultWeightMsgWithdrawValidatorCommission
    57  	})
    58  
    59  	var weightMsgFundCommunityPool int
    60  	appParams.GetOrGenerate(OpWeightMsgFundCommunityPool, &weightMsgFundCommunityPool, nil, func(_ *rand.Rand) {
    61  		weightMsgFundCommunityPool = DefaultWeightMsgFundCommunityPool
    62  	})
    63  
    64  	return simulation.WeightedOperations{
    65  		simulation.NewWeightedOperation(
    66  			weightMsgSetWithdrawAddress,
    67  			SimulateMsgSetWithdrawAddress(txConfig, ak, bk, k),
    68  		),
    69  		simulation.NewWeightedOperation(
    70  			weightMsgWithdrawDelegationReward,
    71  			SimulateMsgWithdrawDelegatorReward(txConfig, ak, bk, k, sk),
    72  		),
    73  		simulation.NewWeightedOperation(
    74  			weightMsgWithdrawValidatorCommission,
    75  			SimulateMsgWithdrawValidatorCommission(txConfig, ak, bk, k, sk),
    76  		),
    77  		simulation.NewWeightedOperation(
    78  			weightMsgFundCommunityPool,
    79  			SimulateMsgFundCommunityPool(txConfig, ak, bk, k, sk),
    80  		),
    81  	}
    82  }
    83  
    84  // SimulateMsgSetWithdrawAddress generates a MsgSetWithdrawAddress with random values.
    85  func SimulateMsgSetWithdrawAddress(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation {
    86  	return func(
    87  		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
    88  	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
    89  		isWithdrawAddrEnabled, err := k.GetWithdrawAddrEnabled(ctx)
    90  		if err != nil {
    91  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), "error getting params"), nil, err
    92  		}
    93  
    94  		if !isWithdrawAddrEnabled {
    95  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), "withdrawal is not enabled"), nil, nil
    96  		}
    97  
    98  		simAccount, _ := simtypes.RandomAcc(r, accs)
    99  		simToAccount, _ := simtypes.RandomAcc(r, accs)
   100  
   101  		account := ak.GetAccount(ctx, simAccount.Address)
   102  		spendable := bk.SpendableCoins(ctx, account.GetAddress())
   103  
   104  		msg := types.NewMsgSetWithdrawAddress(simAccount.Address, simToAccount.Address)
   105  
   106  		txCtx := simulation.OperationInput{
   107  			R:               r,
   108  			App:             app,
   109  			TxGen:           txConfig,
   110  			Cdc:             nil,
   111  			Msg:             msg,
   112  			Context:         ctx,
   113  			SimAccount:      simAccount,
   114  			AccountKeeper:   ak,
   115  			Bankkeeper:      bk,
   116  			ModuleName:      types.ModuleName,
   117  			CoinsSpentInMsg: spendable,
   118  		}
   119  
   120  		return simulation.GenAndDeliverTxWithRandFees(txCtx)
   121  	}
   122  }
   123  
   124  // SimulateMsgWithdrawDelegatorReward generates a MsgWithdrawDelegatorReward with random values.
   125  func SimulateMsgWithdrawDelegatorReward(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation {
   126  	return func(
   127  		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
   128  	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
   129  		simAccount, _ := simtypes.RandomAcc(r, accs)
   130  		delegations, err := sk.GetAllDelegatorDelegations(ctx, simAccount.Address)
   131  		if err != nil {
   132  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "error getting delegations"), nil, err
   133  		}
   134  		if len(delegations) == 0 {
   135  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "number of delegators equal 0"), nil, nil
   136  		}
   137  
   138  		delegation := delegations[r.Intn(len(delegations))]
   139  
   140  		delAddr, err := sk.ValidatorAddressCodec().StringToBytes(delegation.GetValidatorAddr())
   141  		if err != nil {
   142  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "error converting validator address"), nil, err
   143  		}
   144  		validator, err := sk.Validator(ctx, delAddr)
   145  		if err != nil {
   146  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "error getting validator"), nil, err
   147  		}
   148  		if validator == nil {
   149  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), "validator is nil"), nil, fmt.Errorf("validator %s not found", delegation.GetValidatorAddr())
   150  		}
   151  
   152  		account := ak.GetAccount(ctx, simAccount.Address)
   153  		spendable := bk.SpendableCoins(ctx, account.GetAddress())
   154  
   155  		msg := types.NewMsgWithdrawDelegatorReward(simAccount.Address.String(), validator.GetOperator())
   156  
   157  		txCtx := simulation.OperationInput{
   158  			R:               r,
   159  			App:             app,
   160  			TxGen:           txConfig,
   161  			Cdc:             nil,
   162  			Msg:             msg,
   163  			Context:         ctx,
   164  			SimAccount:      simAccount,
   165  			AccountKeeper:   ak,
   166  			Bankkeeper:      bk,
   167  			ModuleName:      types.ModuleName,
   168  			CoinsSpentInMsg: spendable,
   169  		}
   170  
   171  		return simulation.GenAndDeliverTxWithRandFees(txCtx)
   172  	}
   173  }
   174  
   175  // SimulateMsgWithdrawValidatorCommission generates a MsgWithdrawValidatorCommission with random values.
   176  func SimulateMsgWithdrawValidatorCommission(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation {
   177  	return func(
   178  		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
   179  	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
   180  		msgType := sdk.MsgTypeURL(&types.MsgWithdrawValidatorCommission{})
   181  
   182  		allVals, err := sk.GetAllValidators(ctx)
   183  		if err != nil {
   184  			return simtypes.NoOpMsg(types.ModuleName, msgType, "error getting all validators"), nil, err
   185  		}
   186  
   187  		validator, ok := testutil.RandSliceElem(r, allVals)
   188  		if !ok {
   189  			return simtypes.NoOpMsg(types.ModuleName, msgType, "random validator is not ok"), nil, nil
   190  		}
   191  
   192  		valBz, err := sk.ValidatorAddressCodec().StringToBytes(validator.GetOperator())
   193  		if err != nil {
   194  			return simtypes.NoOpMsg(types.ModuleName, msgType, "error converting validator address"), nil, err
   195  		}
   196  
   197  		commission, err := k.GetValidatorAccumulatedCommission(ctx, valBz)
   198  
   199  		if err != nil && !errors.Is(err, collections.ErrNotFound) {
   200  			return simtypes.NoOpMsg(types.ModuleName, msgType, "error getting validator commission"), nil, err
   201  		}
   202  
   203  		if commission.Commission.IsZero() {
   204  			return simtypes.NoOpMsg(types.ModuleName, msgType, "validator commission is zero"), nil, nil
   205  		}
   206  
   207  		simAccount, found := simtypes.FindAccount(accs, sdk.AccAddress(valBz))
   208  		if !found {
   209  			return simtypes.NoOpMsg(types.ModuleName, msgType, "could not find account"), nil, fmt.Errorf("validator %s not found", validator.GetOperator())
   210  		}
   211  
   212  		account := ak.GetAccount(ctx, simAccount.Address)
   213  		spendable := bk.SpendableCoins(ctx, account.GetAddress())
   214  
   215  		msg := types.NewMsgWithdrawValidatorCommission(validator.GetOperator())
   216  
   217  		txCtx := simulation.OperationInput{
   218  			R:               r,
   219  			App:             app,
   220  			TxGen:           txConfig,
   221  			Cdc:             nil,
   222  			Msg:             msg,
   223  			Context:         ctx,
   224  			SimAccount:      simAccount,
   225  			AccountKeeper:   ak,
   226  			Bankkeeper:      bk,
   227  			ModuleName:      types.ModuleName,
   228  			CoinsSpentInMsg: spendable,
   229  		}
   230  
   231  		return simulation.GenAndDeliverTxWithRandFees(txCtx)
   232  	}
   233  }
   234  
   235  // SimulateMsgFundCommunityPool simulates MsgFundCommunityPool execution where
   236  // a random account sends a random amount of its funds to the community pool.
   237  func SimulateMsgFundCommunityPool(txConfig client.TxConfig, ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper, sk types.StakingKeeper) simtypes.Operation {
   238  	return func(
   239  		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string,
   240  	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
   241  		funder, _ := simtypes.RandomAcc(r, accs)
   242  
   243  		account := ak.GetAccount(ctx, funder.Address)
   244  		spendable := bk.SpendableCoins(ctx, account.GetAddress())
   245  
   246  		fundAmount := simtypes.RandSubsetCoins(r, spendable)
   247  		if fundAmount.Empty() {
   248  			return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgFundCommunityPool{}), "fund amount is empty"), nil, nil
   249  		}
   250  
   251  		var (
   252  			fees sdk.Coins
   253  			err  error
   254  		)
   255  
   256  		coins, hasNeg := spendable.SafeSub(fundAmount...)
   257  		if !hasNeg {
   258  			fees, err = simtypes.RandomFees(r, ctx, coins)
   259  			if err != nil {
   260  				return simtypes.NoOpMsg(types.ModuleName, sdk.MsgTypeURL(&types.MsgFundCommunityPool{}), "unable to generate fees"), nil, err
   261  			}
   262  		}
   263  
   264  		msg := types.NewMsgFundCommunityPool(fundAmount, funder.Address.String())
   265  
   266  		txCtx := simulation.OperationInput{
   267  			R:             r,
   268  			App:           app,
   269  			TxGen:         txConfig,
   270  			Cdc:           nil,
   271  			Msg:           msg,
   272  			Context:       ctx,
   273  			SimAccount:    funder,
   274  			AccountKeeper: ak,
   275  			ModuleName:    types.ModuleName,
   276  		}
   277  
   278  		return simulation.GenAndDeliverTx(txCtx, fees)
   279  	}
   280  }