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

     1  package keeper_test
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	abci "github.com/cometbft/cometbft/abci/types"
     8  	cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
     9  	"github.com/golang/mock/gomock"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"cosmossdk.io/math"
    13  	storetypes "cosmossdk.io/store/types"
    14  
    15  	"github.com/cosmos/cosmos-sdk/codec/address"
    16  	"github.com/cosmos/cosmos-sdk/runtime"
    17  	"github.com/cosmos/cosmos-sdk/testutil"
    18  	sdk "github.com/cosmos/cosmos-sdk/types"
    19  	moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
    20  	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
    21  	"github.com/cosmos/cosmos-sdk/x/distribution"
    22  	"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
    23  	distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil"
    24  	disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
    25  	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
    26  )
    27  
    28  func TestAllocateTokensToValidatorWithCommission(t *testing.T) {
    29  	ctrl := gomock.NewController(t)
    30  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
    31  	storeService := runtime.NewKVStoreService(key)
    32  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
    33  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
    34  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
    35  
    36  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
    37  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
    38  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
    39  
    40  	valCodec := address.NewBech32Codec("cosmosvaloper")
    41  
    42  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
    43  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(valCodec).AnyTimes()
    44  
    45  	distrKeeper := keeper.NewKeeper(
    46  		encCfg.Codec,
    47  		storeService,
    48  		accountKeeper,
    49  		bankKeeper,
    50  		stakingKeeper,
    51  		"fee_collector",
    52  		authtypes.NewModuleAddress("gov").String(),
    53  	)
    54  
    55  	// create validator with 50% commission
    56  	val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
    57  	require.NoError(t, err)
    58  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
    59  	stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk0)).Return(val, nil).AnyTimes()
    60  
    61  	// allocate tokens
    62  	tokens := sdk.DecCoins{
    63  		{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(10)},
    64  	}
    65  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
    66  
    67  	// check commission
    68  	expected := sdk.DecCoins{
    69  		{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(5)},
    70  	}
    71  
    72  	valBz, err := valCodec.StringToBytes(val.GetOperator())
    73  	require.NoError(t, err)
    74  
    75  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valBz)
    76  	require.NoError(t, err)
    77  	require.Equal(t, expected, valCommission.Commission)
    78  
    79  	// check current rewards
    80  	currentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valBz)
    81  	require.NoError(t, err)
    82  	require.Equal(t, expected, currentRewards.Rewards)
    83  }
    84  
    85  func TestAllocateTokensToManyValidators(t *testing.T) {
    86  	ctrl := gomock.NewController(t)
    87  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
    88  	storeService := runtime.NewKVStoreService(key)
    89  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
    90  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
    91  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
    92  
    93  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
    94  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
    95  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
    96  
    97  	feeCollectorAcc := authtypes.NewEmptyModuleAccount("fee_collector")
    98  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
    99  	accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), "fee_collector").Return(feeCollectorAcc)
   100  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec("cosmosvaloper")).AnyTimes()
   101  
   102  	distrKeeper := keeper.NewKeeper(
   103  		encCfg.Codec,
   104  		storeService,
   105  		accountKeeper,
   106  		bankKeeper,
   107  		stakingKeeper,
   108  		"fee_collector",
   109  		authtypes.NewModuleAddress("gov").String(),
   110  	)
   111  
   112  	// reset fee pool & set params
   113  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   114  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   115  
   116  	// create validator with 50% commission
   117  	valAddr0 := sdk.ValAddress(valConsAddr0)
   118  	val0, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
   119  	require.NoError(t, err)
   120  	val0.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   121  	stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk0)).Return(val0, nil).AnyTimes()
   122  
   123  	// create second validator with 0% commission
   124  	valAddr1 := sdk.ValAddress(valConsAddr1)
   125  	val1, err := distrtestutil.CreateValidator(valConsPk1, math.NewInt(100))
   126  	require.NoError(t, err)
   127  	val1.Commission = stakingtypes.NewCommission(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0))
   128  	stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk1)).Return(val1, nil).AnyTimes()
   129  
   130  	abciValA := abci.Validator{
   131  		Address: valConsPk0.Address(),
   132  		Power:   100,
   133  	}
   134  	abciValB := abci.Validator{
   135  		Address: valConsPk1.Address(),
   136  		Power:   100,
   137  	}
   138  
   139  	// assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards
   140  	val0OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
   141  	require.NoError(t, err)
   142  	require.True(t, val0OutstandingRewards.Rewards.IsZero())
   143  
   144  	val1OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
   145  	require.NoError(t, err)
   146  	require.True(t, val1OutstandingRewards.Rewards.IsZero())
   147  
   148  	feePool, err := distrKeeper.FeePool.Get(ctx)
   149  	require.NoError(t, err)
   150  	require.True(t, feePool.CommunityPool.IsZero())
   151  
   152  	val0Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0)
   153  	require.NoError(t, err)
   154  	require.True(t, val0Commission.Commission.IsZero())
   155  
   156  	val1Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1)
   157  	require.NoError(t, err)
   158  	require.True(t, val1Commission.Commission.IsZero())
   159  
   160  	val0CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0)
   161  	require.NoError(t, err)
   162  	require.True(t, val0CurrentRewards.Rewards.IsZero())
   163  
   164  	val1CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1)
   165  	require.NoError(t, err)
   166  	require.True(t, val1CurrentRewards.Rewards.IsZero())
   167  
   168  	// allocate tokens as if both had voted and second was proposer
   169  	fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(100)))
   170  	bankKeeper.EXPECT().GetAllBalances(gomock.Any(), feeCollectorAcc.GetAddress()).Return(fees)
   171  	bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), "fee_collector", disttypes.ModuleName, fees)
   172  
   173  	votes := []abci.VoteInfo{
   174  		{
   175  			Validator: abciValA,
   176  		},
   177  		{
   178  			Validator: abciValB,
   179  		},
   180  	}
   181  	require.NoError(t, distrKeeper.AllocateTokens(ctx, 200, votes))
   182  
   183  	// 98 outstanding rewards (100 less 2 to community pool)
   184  	val0OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
   185  	require.NoError(t, err)
   186  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, val0OutstandingRewards.Rewards)
   187  
   188  	val1OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
   189  	require.NoError(t, err)
   190  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, val1OutstandingRewards.Rewards)
   191  
   192  	// 2 community pool coins
   193  	feePool, err = distrKeeper.FeePool.Get(ctx)
   194  	require.NoError(t, err)
   195  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(2)}}, feePool.CommunityPool)
   196  
   197  	// 50% commission for first proposer, (0.5 * 98%) * 100 / 2 = 23.25
   198  	val0Commission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0)
   199  	require.NoError(t, err)
   200  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(2450, 2)}}, val0Commission.Commission)
   201  
   202  	// zero commission for second proposer
   203  	val1Commission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1)
   204  	require.NoError(t, err)
   205  	require.True(t, val1Commission.Commission.IsZero())
   206  
   207  	// just staking.proportional for first proposer less commission = (0.5 * 98%) * 100 / 2 = 24.50
   208  	val0CurrentRewards, err = distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0)
   209  	require.NoError(t, err)
   210  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(2450, 2)}}, val0CurrentRewards.Rewards)
   211  
   212  	// proposer reward + staking.proportional for second proposer = (0.5 * (98%)) * 100 = 49
   213  	val1CurrentRewards, err = distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1)
   214  	require.NoError(t, err)
   215  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecWithPrec(490, 1)}}, val1CurrentRewards.Rewards)
   216  }
   217  
   218  func TestAllocateTokensTruncation(t *testing.T) {
   219  	ctrl := gomock.NewController(t)
   220  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   221  	storeService := runtime.NewKVStoreService(key)
   222  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   223  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   224  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
   225  
   226  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   227  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   228  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   229  
   230  	feeCollectorAcc := authtypes.NewEmptyModuleAccount("fee_collector")
   231  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   232  	accountKeeper.EXPECT().GetModuleAccount(gomock.Any(), "fee_collector").Return(feeCollectorAcc)
   233  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec("cosmosvaloper")).AnyTimes()
   234  
   235  	distrKeeper := keeper.NewKeeper(
   236  		encCfg.Codec,
   237  		storeService,
   238  		accountKeeper,
   239  		bankKeeper,
   240  		stakingKeeper,
   241  		"fee_collector",
   242  		authtypes.NewModuleAddress("gov").String(),
   243  	)
   244  
   245  	// reset fee pool
   246  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   247  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   248  
   249  	// create validator with 10% commission
   250  	valAddr0 := sdk.ValAddress(valConsAddr0)
   251  	val0, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
   252  	require.NoError(t, err)
   253  	val0.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDec(0))
   254  	stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk0)).Return(val0, nil).AnyTimes()
   255  
   256  	// create second validator with 10% commission
   257  	valAddr1 := sdk.ValAddress(valConsAddr1)
   258  	val1, err := distrtestutil.CreateValidator(valConsPk1, math.NewInt(100))
   259  	require.NoError(t, err)
   260  	val1.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDec(0))
   261  	stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk1)).Return(val1, nil).AnyTimes()
   262  
   263  	// create third validator with 10% commission
   264  	valAddr2 := sdk.ValAddress(valConsAddr2)
   265  	val2, err := stakingtypes.NewValidator(sdk.ValAddress(valConsAddr2).String(), valConsPk1, stakingtypes.Description{})
   266  	require.NoError(t, err)
   267  	val2.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDecWithPrec(1, 1), math.LegacyNewDec(0))
   268  	stakingKeeper.EXPECT().ValidatorByConsAddr(gomock.Any(), sdk.GetConsAddress(valConsPk2)).Return(val2, nil).AnyTimes()
   269  
   270  	abciValA := abci.Validator{
   271  		Address: valConsPk0.Address(),
   272  		Power:   11,
   273  	}
   274  	abciValB := abci.Validator{
   275  		Address: valConsPk1.Address(),
   276  		Power:   10,
   277  	}
   278  	abciValC := abci.Validator{
   279  		Address: valConsPk2.Address(),
   280  		Power:   10,
   281  	}
   282  
   283  	// assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards
   284  	val0OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
   285  	require.NoError(t, err)
   286  	require.True(t, val0OutstandingRewards.Rewards.IsZero())
   287  
   288  	val1OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
   289  	require.NoError(t, err)
   290  	require.True(t, val1OutstandingRewards.Rewards.IsZero())
   291  
   292  	feePool, err := distrKeeper.FeePool.Get(ctx)
   293  	require.NoError(t, err)
   294  	require.True(t, feePool.CommunityPool.IsZero())
   295  
   296  	val0Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr0)
   297  	require.NoError(t, err)
   298  	require.True(t, val0Commission.Commission.IsZero())
   299  
   300  	val1Commission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr1)
   301  	require.NoError(t, err)
   302  	require.True(t, val1Commission.Commission.IsZero())
   303  
   304  	val0CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr0)
   305  	require.NoError(t, err)
   306  	require.True(t, val0CurrentRewards.Rewards.IsZero())
   307  
   308  	val1CurrentRewards, err := distrKeeper.GetValidatorCurrentRewards(ctx, valAddr1)
   309  	require.NoError(t, err)
   310  	require.True(t, val1CurrentRewards.Rewards.IsZero())
   311  
   312  	// allocate tokens as if both had voted and second was proposer
   313  	fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(634195840)))
   314  	bankKeeper.EXPECT().GetAllBalances(gomock.Any(), feeCollectorAcc.GetAddress()).Return(fees)
   315  	bankKeeper.EXPECT().SendCoinsFromModuleToModule(gomock.Any(), "fee_collector", disttypes.ModuleName, fees)
   316  
   317  	votes := []abci.VoteInfo{
   318  		{
   319  			Validator: abciValA,
   320  		},
   321  		{
   322  			Validator: abciValB,
   323  		},
   324  		{
   325  			Validator: abciValC,
   326  		},
   327  	}
   328  	require.NoError(t, distrKeeper.AllocateTokens(ctx, 31, votes))
   329  
   330  	val0OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr0)
   331  	require.NoError(t, err)
   332  	require.True(t, val0OutstandingRewards.Rewards.IsValid())
   333  
   334  	val1OutstandingRewards, err = distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr1)
   335  	require.NoError(t, err)
   336  	require.True(t, val1OutstandingRewards.Rewards.IsValid())
   337  
   338  	val2OutstandingRewards, err := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddr2)
   339  	require.NoError(t, err)
   340  	require.True(t, val2OutstandingRewards.Rewards.IsValid())
   341  }