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

     1  package simulation_test
     2  
     3  import (
     4  	"math/rand"
     5  	"testing"
     6  
     7  	abci "github.com/cometbft/cometbft/abci/types"
     8  	"github.com/cosmos/gogoproto/proto"
     9  	"github.com/stretchr/testify/suite"
    10  
    11  	"cosmossdk.io/depinject"
    12  	"cosmossdk.io/log"
    13  	"cosmossdk.io/math"
    14  
    15  	"github.com/cosmos/cosmos-sdk/client"
    16  	"github.com/cosmos/cosmos-sdk/codec"
    17  	"github.com/cosmos/cosmos-sdk/codec/address"
    18  	"github.com/cosmos/cosmos-sdk/runtime"
    19  	simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
    20  	sdk "github.com/cosmos/cosmos-sdk/types"
    21  	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
    22  	authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
    23  	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
    24  	banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
    25  	"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
    26  	"github.com/cosmos/cosmos-sdk/x/distribution/simulation"
    27  	distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil"
    28  	"github.com/cosmos/cosmos-sdk/x/distribution/types"
    29  	stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
    30  	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
    31  )
    32  
    33  // TestWeightedOperations tests the weights of the operations.
    34  func (suite *SimTestSuite) TestWeightedOperations() {
    35  	appParams := make(simtypes.AppParams)
    36  
    37  	weightedOps := simulation.WeightedOperations(appParams, suite.cdc, suite.txConfig, suite.accountKeeper,
    38  		suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper)
    39  
    40  	// setup 3 accounts
    41  	s := rand.NewSource(1)
    42  	r := rand.New(s)
    43  	accs := suite.getTestingAccounts(r, 3)
    44  
    45  	expected := []struct {
    46  		weight     int
    47  		opMsgRoute string
    48  		opMsgName  string
    49  	}{
    50  		{simulation.DefaultWeightMsgSetWithdrawAddress, types.ModuleName, sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{})},
    51  		{simulation.DefaultWeightMsgWithdrawDelegationReward, types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{})},
    52  		{simulation.DefaultWeightMsgWithdrawValidatorCommission, types.ModuleName, sdk.MsgTypeURL(&types.MsgWithdrawValidatorCommission{})},
    53  		{simulation.DefaultWeightMsgFundCommunityPool, types.ModuleName, sdk.MsgTypeURL(&types.MsgFundCommunityPool{})},
    54  	}
    55  
    56  	for i, w := range weightedOps {
    57  		operationMsg, _, err := w.Op()(r, suite.app.BaseApp, suite.ctx, accs, "")
    58  		suite.Require().NoError(err)
    59  
    60  		// the following checks are very much dependent from the ordering of the output given
    61  		// by WeightedOperations. if the ordering in WeightedOperations changes some tests
    62  		// will fail
    63  		suite.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same")
    64  		suite.Require().Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same")
    65  		suite.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same")
    66  	}
    67  }
    68  
    69  // TestSimulateMsgSetWithdrawAddress tests the normal scenario of a valid message of type TypeMsgSetWithdrawAddress.
    70  // Abonormal scenarios, where the message is created by an errors, are not tested here.
    71  func (suite *SimTestSuite) TestSimulateMsgSetWithdrawAddress() {
    72  	// setup 3 accounts
    73  	s := rand.NewSource(1)
    74  	r := rand.New(s)
    75  	accounts := suite.getTestingAccounts(r, 3)
    76  
    77  	suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{
    78  		Height: suite.app.LastBlockHeight() + 1,
    79  		Hash:   suite.app.LastCommitID().Hash,
    80  	})
    81  
    82  	// execute operation
    83  	op := simulation.SimulateMsgSetWithdrawAddress(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper)
    84  	operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
    85  	suite.Require().NoError(err)
    86  
    87  	var msg types.MsgSetWithdrawAddress
    88  	err = proto.Unmarshal(operationMsg.Msg, &msg)
    89  	suite.Require().NoError(err)
    90  	suite.Require().True(operationMsg.OK)
    91  	suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.DelegatorAddress)
    92  	suite.Require().Equal("cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.WithdrawAddress)
    93  	suite.Require().Equal(sdk.MsgTypeURL(&types.MsgSetWithdrawAddress{}), sdk.MsgTypeURL(&msg))
    94  	suite.Require().Len(futureOperations, 0)
    95  }
    96  
    97  // TestSimulateMsgWithdrawDelegatorReward tests the normal scenario of a valid message
    98  // of type TypeMsgWithdrawDelegatorReward.
    99  // Abonormal scenarios, where the message is created by an errors, are not tested here.
   100  func (suite *SimTestSuite) TestSimulateMsgWithdrawDelegatorReward() {
   101  	// setup 3 accounts
   102  	s := rand.NewSource(4)
   103  	r := rand.New(s)
   104  	accounts := suite.getTestingAccounts(r, 3)
   105  
   106  	// setup accounts[0] as validator
   107  	validator0 := suite.getTestingValidator0(accounts)
   108  
   109  	// setup delegation
   110  	delTokens := sdk.TokensFromConsensusPower(2, sdk.DefaultPowerReduction)
   111  	validator0, issuedShares := validator0.AddTokensFromDel(delTokens)
   112  	delegator := accounts[1]
   113  
   114  	delegation := stakingtypes.NewDelegation(delegator.Address.String(), validator0.GetOperator(), issuedShares)
   115  	suite.Require().NoError(suite.stakingKeeper.SetDelegation(suite.ctx, delegation))
   116  	valBz, err := address.NewBech32Codec("cosmosvaloper").StringToBytes(validator0.GetOperator())
   117  	suite.Require().NoError(err)
   118  	suite.distrKeeper.SetDelegatorStartingInfo(suite.ctx, valBz, delegator.Address, types.NewDelegatorStartingInfo(2, math.LegacyOneDec(), 200))
   119  
   120  	suite.setupValidatorRewards(valBz)
   121  
   122  	_, err = suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{
   123  		Height: suite.app.LastBlockHeight() + 1,
   124  		Hash:   suite.app.LastCommitID().Hash,
   125  	})
   126  	suite.Require().NoError(err)
   127  
   128  	// execute operation
   129  	op := simulation.SimulateMsgWithdrawDelegatorReward(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper)
   130  	operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
   131  	suite.Require().NoError(err)
   132  
   133  	var msg types.MsgWithdrawDelegatorReward
   134  	err = proto.Unmarshal(operationMsg.Msg, &msg)
   135  	suite.Require().NoError(err)
   136  	suite.Require().True(operationMsg.OK)
   137  	suite.Require().Equal("cosmosvaloper1l4s054098kk9hmr5753c6k3m2kw65h686d3mhr", msg.ValidatorAddress)
   138  	suite.Require().Equal("cosmos1d6u7zhjwmsucs678d7qn95uqajd4ucl9jcjt26", msg.DelegatorAddress)
   139  	suite.Require().Equal(sdk.MsgTypeURL(&types.MsgWithdrawDelegatorReward{}), sdk.MsgTypeURL(&msg))
   140  	suite.Require().Len(futureOperations, 0)
   141  }
   142  
   143  // TestSimulateMsgWithdrawValidatorCommission tests the normal scenario of a valid message
   144  // of type TypeMsgWithdrawValidatorCommission.
   145  // Abonormal scenarios, where the message is created by an errors, are not tested here.
   146  func (suite *SimTestSuite) TestSimulateMsgWithdrawValidatorCommission() {
   147  	suite.testSimulateMsgWithdrawValidatorCommission("atoken")
   148  	suite.testSimulateMsgWithdrawValidatorCommission("tokenxxx")
   149  }
   150  
   151  // all the checks in this function should not fail if we change the tokenName
   152  func (suite *SimTestSuite) testSimulateMsgWithdrawValidatorCommission(tokenName string) {
   153  	// setup 3 accounts
   154  	s := rand.NewSource(1)
   155  	r := rand.New(s)
   156  	accounts := suite.getTestingAccounts(r, 3)
   157  
   158  	// setup accounts[0] as validator
   159  	validator0 := suite.getTestingValidator0(accounts)
   160  
   161  	// set module account coins
   162  	distrAcc := suite.distrKeeper.GetDistributionAccount(suite.ctx)
   163  	suite.Require().NoError(banktestutil.FundModuleAccount(suite.ctx, suite.bankKeeper, distrAcc.GetName(), sdk.NewCoins(
   164  		sdk.NewCoin(tokenName, math.NewInt(10)),
   165  		sdk.NewCoin("stake", math.NewInt(5)),
   166  	)))
   167  	suite.accountKeeper.SetModuleAccount(suite.ctx, distrAcc)
   168  
   169  	// set outstanding rewards
   170  	valCommission := sdk.NewDecCoins(
   171  		sdk.NewDecCoinFromDec(tokenName, math.LegacyNewDec(5).Quo(math.LegacyNewDec(2))),
   172  		sdk.NewDecCoinFromDec("stake", math.LegacyNewDec(1).Quo(math.LegacyNewDec(1))),
   173  	)
   174  	valCodec := address.NewBech32Codec("cosmosvaloper")
   175  
   176  	val0, err := valCodec.StringToBytes(validator0.GetOperator())
   177  	suite.Require().NoError(err)
   178  
   179  	genVal0, err := valCodec.StringToBytes(suite.genesisVals[0].GetOperator())
   180  	suite.Require().NoError(err)
   181  
   182  	suite.distrKeeper.SetValidatorOutstandingRewards(suite.ctx, val0, types.ValidatorOutstandingRewards{Rewards: valCommission})
   183  	suite.distrKeeper.SetValidatorOutstandingRewards(suite.ctx, genVal0, types.ValidatorOutstandingRewards{Rewards: valCommission})
   184  
   185  	// setup validator accumulated commission
   186  	suite.distrKeeper.SetValidatorAccumulatedCommission(suite.ctx, val0, types.ValidatorAccumulatedCommission{Commission: valCommission})
   187  	suite.distrKeeper.SetValidatorAccumulatedCommission(suite.ctx, genVal0, types.ValidatorAccumulatedCommission{Commission: valCommission})
   188  
   189  	_, err = suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{
   190  		Height: suite.app.LastBlockHeight() + 1,
   191  		Hash:   suite.app.LastCommitID().Hash,
   192  	})
   193  	suite.Require().NoError(err)
   194  
   195  	// execute operation
   196  	op := simulation.SimulateMsgWithdrawValidatorCommission(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper)
   197  	operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
   198  	if !operationMsg.OK {
   199  		suite.Require().Equal("could not find account", operationMsg.Comment)
   200  	} else {
   201  		suite.Require().NoError(err)
   202  
   203  		var msg types.MsgWithdrawValidatorCommission
   204  		err = proto.Unmarshal(operationMsg.Msg, &msg)
   205  		suite.Require().NoError(err)
   206  		suite.Require().True(operationMsg.OK)
   207  		suite.Require().Equal("cosmosvaloper1tnh2q55v8wyygtt9srz5safamzdengsn9dsd7z", msg.ValidatorAddress)
   208  		suite.Require().Equal(sdk.MsgTypeURL(&types.MsgWithdrawValidatorCommission{}), sdk.MsgTypeURL(&msg))
   209  		suite.Require().Len(futureOperations, 0)
   210  	}
   211  }
   212  
   213  // TestSimulateMsgFundCommunityPool tests the normal scenario of a valid message of type TypeMsgFundCommunityPool.
   214  // Abonormal scenarios, where the message is created by an errors, are not tested here.
   215  func (suite *SimTestSuite) TestSimulateMsgFundCommunityPool() {
   216  	// setup 3 accounts
   217  	s := rand.NewSource(1)
   218  	r := rand.New(s)
   219  	accounts := suite.getTestingAccounts(r, 3)
   220  
   221  	suite.app.FinalizeBlock(&abci.RequestFinalizeBlock{
   222  		Height: suite.app.LastBlockHeight() + 1,
   223  		Hash:   suite.app.LastCommitID().Hash,
   224  	})
   225  
   226  	// execute operation
   227  	op := simulation.SimulateMsgFundCommunityPool(suite.txConfig, suite.accountKeeper, suite.bankKeeper, suite.distrKeeper, suite.stakingKeeper)
   228  	operationMsg, futureOperations, err := op(r, suite.app.BaseApp, suite.ctx, accounts, "")
   229  	suite.Require().NoError(err)
   230  
   231  	var msg types.MsgFundCommunityPool
   232  	err = proto.Unmarshal(operationMsg.Msg, &msg)
   233  	suite.Require().NoError(err)
   234  	suite.Require().True(operationMsg.OK)
   235  	suite.Require().Equal("4896096stake", msg.Amount.String())
   236  	suite.Require().Equal("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", msg.Depositor)
   237  	suite.Require().Equal(sdk.MsgTypeURL(&types.MsgFundCommunityPool{}), sdk.MsgTypeURL(&msg))
   238  	suite.Require().Len(futureOperations, 0)
   239  }
   240  
   241  type SimTestSuite struct {
   242  	suite.Suite
   243  
   244  	ctx         sdk.Context
   245  	app         *runtime.App
   246  	genesisVals []stakingtypes.Validator
   247  
   248  	txConfig      client.TxConfig
   249  	cdc           codec.Codec
   250  	stakingKeeper *stakingkeeper.Keeper
   251  	accountKeeper authkeeper.AccountKeeper
   252  	bankKeeper    bankkeeper.Keeper
   253  	distrKeeper   keeper.Keeper
   254  }
   255  
   256  func (suite *SimTestSuite) SetupTest() {
   257  	var (
   258  		appBuilder *runtime.AppBuilder
   259  		err        error
   260  	)
   261  	suite.app, err = simtestutil.Setup(
   262  		depinject.Configs(
   263  			distrtestutil.AppConfig,
   264  			depinject.Supply(log.NewNopLogger()),
   265  		),
   266  		&suite.accountKeeper,
   267  		&suite.bankKeeper,
   268  		&suite.cdc,
   269  		&appBuilder,
   270  		&suite.stakingKeeper,
   271  		&suite.distrKeeper,
   272  		&suite.txConfig,
   273  	)
   274  
   275  	suite.NoError(err)
   276  
   277  	suite.ctx = suite.app.BaseApp.NewContext(false)
   278  
   279  	genesisVals, err := suite.stakingKeeper.GetAllValidators(suite.ctx)
   280  	suite.Require().NoError(err)
   281  	suite.Require().Len(genesisVals, 1)
   282  	suite.genesisVals = genesisVals
   283  }
   284  
   285  func (suite *SimTestSuite) getTestingAccounts(r *rand.Rand, n int) []simtypes.Account {
   286  	accounts := simtypes.RandomAccounts(r, n)
   287  
   288  	initAmt := suite.stakingKeeper.TokensFromConsensusPower(suite.ctx, 200)
   289  	initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt))
   290  
   291  	// add coins to the accounts
   292  	for _, account := range accounts {
   293  		acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, account.Address)
   294  		suite.accountKeeper.SetAccount(suite.ctx, acc)
   295  		suite.Require().NoError(banktestutil.FundAccount(suite.ctx, suite.bankKeeper, account.Address, initCoins))
   296  	}
   297  
   298  	return accounts
   299  }
   300  
   301  func (suite *SimTestSuite) getTestingValidator0(accounts []simtypes.Account) stakingtypes.Validator {
   302  	commission0 := stakingtypes.NewCommission(math.LegacyZeroDec(), math.LegacyOneDec(), math.LegacyOneDec())
   303  	return suite.getTestingValidator(accounts, commission0, 0)
   304  }
   305  
   306  func (suite *SimTestSuite) getTestingValidator(accounts []simtypes.Account, commission stakingtypes.Commission, n int) stakingtypes.Validator {
   307  	require := suite.Require()
   308  	account := accounts[n]
   309  	valPubKey := account.PubKey
   310  	valAddr := sdk.ValAddress(account.PubKey.Address().Bytes())
   311  	validator, err := stakingtypes.NewValidator(valAddr.String(), valPubKey, stakingtypes.
   312  		Description{})
   313  	require.NoError(err)
   314  	validator, err = validator.SetInitialCommission(commission)
   315  	require.NoError(err)
   316  	validator.DelegatorShares = math.LegacyNewDec(100)
   317  	validator.Tokens = math.NewInt(1000000)
   318  
   319  	suite.stakingKeeper.SetValidator(suite.ctx, validator)
   320  
   321  	return validator
   322  }
   323  
   324  func (suite *SimTestSuite) setupValidatorRewards(valAddress sdk.ValAddress) {
   325  	decCoins := sdk.DecCoins{sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, math.LegacyOneDec())}
   326  	historicalRewards := types.NewValidatorHistoricalRewards(decCoins, 2)
   327  	suite.distrKeeper.SetValidatorHistoricalRewards(suite.ctx, valAddress, 2, historicalRewards)
   328  	// setup current revards
   329  	currentRewards := types.NewValidatorCurrentRewards(decCoins, 3)
   330  	suite.distrKeeper.SetValidatorCurrentRewards(suite.ctx, valAddress, currentRewards)
   331  }
   332  
   333  func TestSimTestSuite(t *testing.T) {
   334  	suite.Run(t, new(SimTestSuite))
   335  }