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

     1  package keeper_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
     7  	"github.com/golang/mock/gomock"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"cosmossdk.io/math"
    11  	storetypes "cosmossdk.io/store/types"
    12  
    13  	"github.com/cosmos/cosmos-sdk/codec/address"
    14  	"github.com/cosmos/cosmos-sdk/runtime"
    15  	"github.com/cosmos/cosmos-sdk/testutil"
    16  	sdk "github.com/cosmos/cosmos-sdk/types"
    17  	moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
    18  	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
    19  	"github.com/cosmos/cosmos-sdk/x/distribution"
    20  	"github.com/cosmos/cosmos-sdk/x/distribution/keeper"
    21  	distrtestutil "github.com/cosmos/cosmos-sdk/x/distribution/testutil"
    22  	disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
    23  	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
    24  )
    25  
    26  func TestCalculateRewardsBasic(t *testing.T) {
    27  	ctrl := gomock.NewController(t)
    28  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
    29  	storeService := runtime.NewKVStoreService(key)
    30  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
    31  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
    32  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
    33  
    34  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
    35  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
    36  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
    37  
    38  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
    39  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
    40  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
    41  
    42  	distrKeeper := keeper.NewKeeper(
    43  		encCfg.Codec,
    44  		storeService,
    45  		accountKeeper,
    46  		bankKeeper,
    47  		stakingKeeper,
    48  		"fee_collector",
    49  		authtypes.NewModuleAddress("gov").String(),
    50  	)
    51  
    52  	// reset fee pool
    53  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
    54  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
    55  
    56  	// create validator with 50% commission
    57  	valAddr := sdk.ValAddress(valConsAddr0)
    58  	addr := sdk.AccAddress(valAddr)
    59  	val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(1000))
    60  	require.NoError(t, err)
    61  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
    62  
    63  	// delegation mock
    64  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
    65  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
    66  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
    67  
    68  	// run the necessary hooks manually (given that we are not running an actual staking module)
    69  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
    70  	require.NoError(t, err)
    71  
    72  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
    73  
    74  	// historical count should be 2 (once for validator init, once for delegation init)
    75  	require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
    76  
    77  	// end period
    78  	endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
    79  
    80  	// historical count should be 2 still
    81  	require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
    82  
    83  	// calculate delegation rewards
    84  	rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
    85  	require.NoError(t, err)
    86  
    87  	// rewards should be zero
    88  	require.True(t, rewards.IsZero())
    89  
    90  	// allocate some rewards
    91  	initial := int64(10)
    92  	tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}
    93  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
    94  
    95  	// end period
    96  	endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
    97  
    98  	// calculate delegation rewards
    99  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   100  	require.NoError(t, err)
   101  
   102  	// rewards should be half the tokens
   103  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards)
   104  
   105  	// commission should be the other half
   106  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   107  	require.NoError(t, err)
   108  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, valCommission.Commission)
   109  }
   110  
   111  func TestCalculateRewardsAfterSlash(t *testing.T) {
   112  	ctrl := gomock.NewController(t)
   113  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   114  	storeService := runtime.NewKVStoreService(key)
   115  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   116  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   117  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   118  
   119  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   120  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   121  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   122  
   123  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   124  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   125  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   126  
   127  	distrKeeper := keeper.NewKeeper(
   128  		encCfg.Codec,
   129  		storeService,
   130  		accountKeeper,
   131  		bankKeeper,
   132  		stakingKeeper,
   133  		"fee_collector",
   134  		authtypes.NewModuleAddress("gov").String(),
   135  	)
   136  
   137  	// reset fee pool
   138  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   139  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   140  
   141  	// create validator with 50% commission
   142  	valAddr := sdk.ValAddress(valConsAddr0)
   143  	addr := sdk.AccAddress(valAddr)
   144  	valPower := int64(100)
   145  	stake := sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction)
   146  	val, err := distrtestutil.CreateValidator(valConsPk0, stake)
   147  	require.NoError(t, err)
   148  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   149  
   150  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
   151  
   152  	// set mock calls
   153  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(4)
   154  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
   155  
   156  	// run the necessary hooks manually (given that we are not running an actual staking module)
   157  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
   158  	require.NoError(t, err)
   159  
   160  	// next block
   161  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   162  
   163  	// end period
   164  	endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
   165  
   166  	// calculate delegation rewards
   167  	rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   168  	require.NoError(t, err)
   169  
   170  	// rewards should be zero
   171  	require.True(t, rewards.IsZero())
   172  
   173  	// start out block height
   174  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   175  
   176  	// slash the validator by 50% (simulated with manual calls; we assume the validator is bonded)
   177  	slashedTokens := distrtestutil.SlashValidator(
   178  		ctx,
   179  		valConsAddr0,
   180  		ctx.BlockHeight(),
   181  		valPower,
   182  		math.LegacyNewDecWithPrec(5, 1),
   183  		&val,
   184  		&distrKeeper,
   185  		stakingKeeper,
   186  	)
   187  	require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens)
   188  
   189  	// increase block height
   190  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   191  
   192  	// allocate some rewards
   193  	initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)
   194  	tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}}
   195  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   196  
   197  	// end period
   198  	endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
   199  
   200  	// calculate delegation rewards
   201  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   202  	require.NoError(t, err)
   203  
   204  	// rewards should be half the tokens
   205  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial.QuoRaw(2))}}, rewards)
   206  
   207  	// commission should be the other half
   208  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   209  	require.NoError(t, err)
   210  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial.QuoRaw(2))}},
   211  		valCommission.Commission)
   212  }
   213  
   214  func TestCalculateRewardsAfterManySlashes(t *testing.T) {
   215  	ctrl := gomock.NewController(t)
   216  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   217  	storeService := runtime.NewKVStoreService(key)
   218  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   219  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   220  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   221  
   222  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   223  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   224  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   225  
   226  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   227  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   228  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   229  
   230  	distrKeeper := keeper.NewKeeper(
   231  		encCfg.Codec,
   232  		storeService,
   233  		accountKeeper,
   234  		bankKeeper,
   235  		stakingKeeper,
   236  		"fee_collector",
   237  		authtypes.NewModuleAddress("gov").String(),
   238  	)
   239  
   240  	// reset fee pool
   241  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   242  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   243  
   244  	// create validator with 50% commission
   245  	valAddr := sdk.ValAddress(valConsAddr0)
   246  	addr := sdk.AccAddress(valAddr)
   247  	valPower := int64(100)
   248  	stake := sdk.TokensFromConsensusPower(valPower, sdk.DefaultPowerReduction)
   249  	val, err := distrtestutil.CreateValidator(valConsPk0, stake)
   250  	require.NoError(t, err)
   251  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   252  
   253  	// delegation mocks
   254  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
   255  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(4)
   256  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
   257  
   258  	// run the necessary hooks manually (given that we are not running an actual staking module)
   259  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
   260  	require.NoError(t, err)
   261  
   262  	// next block
   263  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   264  
   265  	// end period
   266  	endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
   267  
   268  	// calculate delegation rewards
   269  	rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   270  	require.NoError(t, err)
   271  
   272  	// rewards should be zero
   273  	require.True(t, rewards.IsZero())
   274  
   275  	// start out block height
   276  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   277  
   278  	// slash the validator by 50% (simulated with manual calls; we assume the validator is bonded)
   279  	slashedTokens := distrtestutil.SlashValidator(
   280  		ctx,
   281  		valConsAddr0,
   282  		ctx.BlockHeight(),
   283  		valPower,
   284  		math.LegacyNewDecWithPrec(5, 1),
   285  		&val,
   286  		&distrKeeper,
   287  		stakingKeeper,
   288  	)
   289  	require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens)
   290  
   291  	// expect a call for the next slash with the updated validator
   292  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
   293  
   294  	// increase block height
   295  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   296  
   297  	// allocate some rewards
   298  	initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)
   299  	tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}}
   300  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   301  
   302  	// slash the validator by 50% again
   303  	slashedTokens = distrtestutil.SlashValidator(
   304  		ctx,
   305  		valConsAddr0,
   306  		ctx.BlockHeight(),
   307  		valPower/2,
   308  		math.LegacyNewDecWithPrec(2, 1),
   309  		&val,
   310  		&distrKeeper,
   311  		stakingKeeper,
   312  	)
   313  	require.True(t, slashedTokens.IsPositive(), "expected positive slashed tokens, got: %s", slashedTokens)
   314  
   315  	// increase block height
   316  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   317  
   318  	// allocate some more rewards
   319  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   320  
   321  	// end period
   322  	endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
   323  
   324  	// calculate delegation rewards
   325  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   326  	require.NoError(t, err)
   327  
   328  	// rewards should be half the tokens
   329  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}}, rewards)
   330  
   331  	// commission should be the other half
   332  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   333  	require.NoError(t, err)
   334  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDecFromInt(initial)}},
   335  		valCommission.Commission)
   336  }
   337  
   338  func TestCalculateRewardsMultiDelegator(t *testing.T) {
   339  	ctrl := gomock.NewController(t)
   340  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   341  	storeService := runtime.NewKVStoreService(key)
   342  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   343  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   344  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   345  
   346  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   347  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   348  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   349  
   350  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   351  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   352  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   353  
   354  	distrKeeper := keeper.NewKeeper(
   355  		encCfg.Codec,
   356  		storeService,
   357  		accountKeeper,
   358  		bankKeeper,
   359  		stakingKeeper,
   360  		"fee_collector",
   361  		authtypes.NewModuleAddress("gov").String(),
   362  	)
   363  
   364  	// reset fee pool
   365  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   366  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   367  
   368  	// create validator with 50% commission
   369  	valAddr := sdk.ValAddress(valConsAddr0)
   370  	addr0 := sdk.AccAddress(valAddr)
   371  	val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
   372  	require.NoError(t, err)
   373  
   374  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   375  
   376  	del0 := stakingtypes.NewDelegation(addr0.String(), valAddr.String(), val.DelegatorShares)
   377  
   378  	// set mock calls
   379  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(4)
   380  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr0, valAddr).Return(del0, nil).Times(1)
   381  
   382  	// run the necessary hooks manually (given that we are not running an actual staking module)
   383  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr0, valAddr)
   384  	require.NoError(t, err)
   385  
   386  	// next block
   387  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   388  
   389  	// allocate some rewards
   390  	initial := int64(20)
   391  	tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}
   392  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   393  
   394  	// second delegation
   395  	addr1 := sdk.AccAddress(valConsAddr1)
   396  	_, del1, err := distrtestutil.Delegate(ctx, distrKeeper, addr1, &val, math.NewInt(100), nil, stakingKeeper)
   397  	require.NoError(t, err)
   398  
   399  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr1, valAddr).Return(del1, nil)
   400  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
   401  
   402  	// call necessary hooks to update a delegation
   403  	err = distrKeeper.Hooks().AfterDelegationModified(ctx, addr1, valAddr)
   404  	require.NoError(t, err)
   405  
   406  	// next block
   407  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   408  
   409  	// allocate some more rewards
   410  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   411  
   412  	// end period
   413  	endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
   414  
   415  	// calculate delegation rewards for del1
   416  	rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del0, endingPeriod)
   417  	require.NoError(t, err)
   418  
   419  	// rewards for del0 should be 3/4 initial
   420  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 3 / 4)}}, rewards)
   421  
   422  	// calculate delegation rewards for del2
   423  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del1, endingPeriod)
   424  	require.NoError(t, err)
   425  
   426  	// rewards for del2 should be 1/4 initial
   427  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial * 1 / 4)}}, rewards)
   428  
   429  	// commission should be equal to initial (50% twice)
   430  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   431  	require.NoError(t, err)
   432  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial)}}, valCommission.Commission)
   433  }
   434  
   435  func TestWithdrawDelegationRewardsBasic(t *testing.T) {
   436  	ctrl := gomock.NewController(t)
   437  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   438  	storeService := runtime.NewKVStoreService(key)
   439  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   440  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   441  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   442  
   443  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   444  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   445  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   446  
   447  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   448  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   449  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   450  
   451  	distrKeeper := keeper.NewKeeper(
   452  		encCfg.Codec,
   453  		storeService,
   454  		accountKeeper,
   455  		bankKeeper,
   456  		stakingKeeper,
   457  		"fee_collector",
   458  		authtypes.NewModuleAddress("gov").String(),
   459  	)
   460  
   461  	// reset fee pool
   462  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   463  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   464  
   465  	// create validator with 50% commission
   466  	valAddr := sdk.ValAddress(valConsAddr0)
   467  	addr := sdk.AccAddress(valAddr)
   468  	val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
   469  	require.NoError(t, err)
   470  
   471  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   472  
   473  	// delegation mock
   474  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
   475  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(5)
   476  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil).Times(3)
   477  
   478  	// run the necessary hooks manually (given that we are not running an actual staking module)
   479  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
   480  	require.NoError(t, err)
   481  
   482  	// next block
   483  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   484  
   485  	// allocate some rewards
   486  	initial := sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)
   487  	tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, initial)}
   488  
   489  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   490  
   491  	// historical count should be 2 (initial + latest for delegation)
   492  	require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
   493  
   494  	// withdraw rewards (the bank keeper should be called with the right amount of tokens to transfer)
   495  	expRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initial.QuoRaw(2))}
   496  	bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expRewards)
   497  	_, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valAddr), valAddr)
   498  	require.Nil(t, err)
   499  
   500  	// historical count should still be 2 (added one record, cleared one)
   501  	require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
   502  
   503  	// withdraw commission (the bank keeper should be called with the right amount of tokens to transfer)
   504  	expCommission := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, initial.QuoRaw(2))}
   505  	bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission)
   506  	_, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddr)
   507  	require.Nil(t, err)
   508  }
   509  
   510  func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) {
   511  	ctrl := gomock.NewController(t)
   512  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   513  	storeService := runtime.NewKVStoreService(key)
   514  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   515  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   516  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   517  
   518  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   519  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   520  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   521  
   522  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   523  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   524  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   525  
   526  	distrKeeper := keeper.NewKeeper(
   527  		encCfg.Codec,
   528  		storeService,
   529  		accountKeeper,
   530  		bankKeeper,
   531  		stakingKeeper,
   532  		"fee_collector",
   533  		authtypes.NewModuleAddress("gov").String(),
   534  	)
   535  
   536  	// reset fee pool
   537  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   538  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   539  
   540  	// create validator with 50% commission
   541  	valAddr := sdk.ValAddress(valConsAddr0)
   542  	addr := sdk.AccAddress(valAddr)
   543  	val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
   544  	require.NoError(t, err)
   545  
   546  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   547  
   548  	// delegation mock
   549  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
   550  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(5)
   551  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
   552  
   553  	// run the necessary hooks manually (given that we are not running an actual staking module)
   554  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
   555  	require.NoError(t, err)
   556  
   557  	// next block
   558  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   559  
   560  	// end period
   561  	endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
   562  
   563  	// calculate delegation rewards
   564  	rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   565  	require.NoError(t, err)
   566  
   567  	// rewards should be zero
   568  	require.True(t, rewards.IsZero())
   569  
   570  	// start out block height
   571  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   572  
   573  	// allocate some rewards
   574  	initial := math.LegacyNewDecFromInt(sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction))
   575  	tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}
   576  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   577  
   578  	valPower := int64(100)
   579  	// slash the validator by 50% (simulated with manual calls; we assume the validator is bonded)
   580  	distrtestutil.SlashValidator(
   581  		ctx,
   582  		valConsAddr0,
   583  		ctx.BlockHeight(),
   584  		valPower,
   585  		math.LegacyNewDecWithPrec(5, 1),
   586  		&val,
   587  		&distrKeeper,
   588  		stakingKeeper,
   589  	)
   590  
   591  	// slash the validator by 50% again
   592  	// stakingKeeper.Slash(ctx, valConsAddr0, ctx.BlockHeight(), valPower/2, math.LegacyNewDecWithPrec(5, 1))
   593  	distrtestutil.SlashValidator(
   594  		ctx,
   595  		valConsAddr0,
   596  		ctx.BlockHeight(),
   597  		valPower/2,
   598  		math.LegacyNewDecWithPrec(5, 1),
   599  		&val,
   600  		&distrKeeper,
   601  		stakingKeeper,
   602  	)
   603  
   604  	// increase block height
   605  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   606  
   607  	// allocate some more rewards
   608  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   609  
   610  	// end period
   611  	endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
   612  
   613  	// calculate delegation rewards
   614  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   615  	require.NoError(t, err)
   616  
   617  	// rewards should be half the tokens
   618  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, rewards)
   619  
   620  	// commission should be the other half
   621  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   622  	require.NoError(t, err)
   623  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, valCommission.Commission)
   624  }
   625  
   626  func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) {
   627  	ctrl := gomock.NewController(t)
   628  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   629  	storeService := runtime.NewKVStoreService(key)
   630  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   631  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   632  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   633  
   634  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   635  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   636  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   637  
   638  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   639  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   640  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   641  
   642  	distrKeeper := keeper.NewKeeper(
   643  		encCfg.Codec,
   644  		storeService,
   645  		accountKeeper,
   646  		bankKeeper,
   647  		stakingKeeper,
   648  		"fee_collector",
   649  		authtypes.NewModuleAddress("gov").String(),
   650  	)
   651  
   652  	// reset fee pool
   653  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   654  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   655  
   656  	valPower := int64(100)
   657  
   658  	// create validator with 50% commission
   659  	valAddr := sdk.ValAddress(valConsAddr0)
   660  	addr := sdk.AccAddress(valAddr)
   661  	val, err := distrtestutil.CreateValidator(valConsPk0, sdk.TokensFromConsensusPower(valPower, sdk.DefaultPowerReduction))
   662  	require.NoError(t, err)
   663  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   664  
   665  	// validator and delegation mocks
   666  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
   667  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
   668  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil)
   669  
   670  	// run the necessary hooks manually (given that we are not running an actual staking module)
   671  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
   672  	require.NoError(t, err)
   673  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(2)
   674  
   675  	// next block
   676  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   677  
   678  	// allocate some rewards
   679  	initial := math.LegacyNewDecFromInt(sdk.TokensFromConsensusPower(30, sdk.DefaultPowerReduction))
   680  	tokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}
   681  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   682  
   683  	// slash the validator
   684  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   685  	distrtestutil.SlashValidator(
   686  		ctx,
   687  		valConsAddr0,
   688  		ctx.BlockHeight(),
   689  		valPower,
   690  		math.LegacyNewDecWithPrec(5, 1),
   691  		&val,
   692  		&distrKeeper,
   693  		stakingKeeper,
   694  	)
   695  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   696  
   697  	// update validator mock
   698  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
   699  
   700  	// second delegation
   701  	_, del2, err := distrtestutil.Delegate(
   702  		ctx,
   703  		distrKeeper,
   704  		sdk.AccAddress(valConsAddr1),
   705  		&val,
   706  		sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction),
   707  		nil,
   708  		stakingKeeper,
   709  	)
   710  	require.NoError(t, err)
   711  
   712  	// new delegation mock and update validator mock
   713  	stakingKeeper.EXPECT().Delegation(gomock.Any(), sdk.AccAddress(valConsAddr1), valAddr).Return(del2, nil)
   714  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(1)
   715  
   716  	// call necessary hooks to update a delegation
   717  	err = distrKeeper.Hooks().AfterDelegationModified(ctx, sdk.AccAddress(valConsAddr1), valAddr)
   718  	require.NoError(t, err)
   719  
   720  	// next block
   721  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   722  
   723  	// allocate some more rewards
   724  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   725  
   726  	// slash the validator again
   727  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   728  	distrtestutil.SlashValidator(
   729  		ctx,
   730  		valConsAddr0,
   731  		ctx.BlockHeight(),
   732  		valPower,
   733  		math.LegacyNewDecWithPrec(5, 1),
   734  		&val,
   735  		&distrKeeper,
   736  		stakingKeeper,
   737  	)
   738  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3)
   739  
   740  	// end period
   741  	endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
   742  
   743  	// calculate delegation rewards for del1
   744  	rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   745  	require.NoError(t, err)
   746  
   747  	// rewards for del1 should be 2/3 initial (half initial first period, 1/6 initial second period)
   748  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(2).Add(initial.QuoInt64(6))}}, rewards)
   749  
   750  	// calculate delegation rewards for del2
   751  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
   752  	require.NoError(t, err)
   753  
   754  	// rewards for del2 should be initial / 3
   755  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial.QuoInt64(3)}}, rewards)
   756  
   757  	// commission should be equal to initial (twice 50% commission, unaffected by slashing)
   758  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   759  	require.NoError(t, err)
   760  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: initial}}, valCommission.Commission)
   761  }
   762  
   763  func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
   764  	ctrl := gomock.NewController(t)
   765  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   766  	storeService := runtime.NewKVStoreService(key)
   767  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   768  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   769  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   770  
   771  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   772  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   773  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   774  
   775  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   776  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   777  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   778  
   779  	distrKeeper := keeper.NewKeeper(
   780  		encCfg.Codec,
   781  		storeService,
   782  		accountKeeper,
   783  		bankKeeper,
   784  		stakingKeeper,
   785  		"fee_collector",
   786  		authtypes.NewModuleAddress("gov").String(),
   787  	)
   788  
   789  	// reset fee pool
   790  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   791  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   792  
   793  	// create validator with 50% commission
   794  	valAddr := sdk.ValAddress(valConsAddr0)
   795  	addr := sdk.AccAddress(valAddr)
   796  	val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
   797  	require.NoError(t, err)
   798  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDecWithPrec(5, 1), math.LegacyNewDec(0))
   799  
   800  	// validator and delegation mocks
   801  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
   802  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
   803  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil).Times(5)
   804  
   805  	// run the necessary hooks manually (given that we are not running an actual staking module)
   806  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
   807  	require.NoError(t, err)
   808  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(2)
   809  
   810  	// next block
   811  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   812  
   813  	// allocate some rewards
   814  	initial := int64(20)
   815  	tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))}
   816  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   817  
   818  	// historical count should be 2 (validator init, delegation init)
   819  	require.Equal(t, uint64(2), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
   820  
   821  	// second delegation
   822  	_, del2, err := distrtestutil.Delegate(
   823  		ctx,
   824  		distrKeeper,
   825  		sdk.AccAddress(valConsAddr1),
   826  		&val,
   827  		math.NewInt(100),
   828  		nil,
   829  		stakingKeeper,
   830  	)
   831  	require.NoError(t, err)
   832  
   833  	// new delegation mock and update validator mock
   834  	stakingKeeper.EXPECT().Delegation(gomock.Any(), sdk.AccAddress(valConsAddr1), valAddr).Return(del2, nil).Times(3)
   835  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(6)
   836  
   837  	// call necessary hooks to update a delegation
   838  	err = distrKeeper.Hooks().AfterDelegationModified(ctx, sdk.AccAddress(valConsAddr1), valAddr)
   839  	require.NoError(t, err)
   840  
   841  	// historical count should be 3 (second delegation init)
   842  	require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
   843  
   844  	// next block
   845  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   846  
   847  	// allocate some more rewards
   848  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   849  
   850  	// first delegator withdraws
   851  	expRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial*3/4))}
   852  	bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expRewards)
   853  	_, err = distrKeeper.WithdrawDelegationRewards(ctx, addr, valAddr)
   854  	require.NoError(t, err)
   855  
   856  	// second delegator withdraws
   857  	expRewards = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial*1/4))}
   858  	bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, sdk.AccAddress(valConsAddr1), expRewards)
   859  	_, err = distrKeeper.WithdrawDelegationRewards(ctx, sdk.AccAddress(valConsAddr1), valAddr)
   860  	require.NoError(t, err)
   861  
   862  	// historical count should be 3 (validator init + two delegations)
   863  	require.Equal(t, uint64(3), distrKeeper.GetValidatorHistoricalReferenceCount(ctx))
   864  
   865  	// validator withdraws commission
   866  	expCommission := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial))}
   867  	bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission)
   868  	_, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddr)
   869  	require.NoError(t, err)
   870  
   871  	// end period
   872  	endingPeriod, _ := distrKeeper.IncrementValidatorPeriod(ctx, val)
   873  
   874  	// calculate delegation rewards for del1
   875  	rewards, err := distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   876  	require.NoError(t, err)
   877  
   878  	// rewards for del1 should be zero
   879  	require.True(t, rewards.IsZero())
   880  
   881  	// calculate delegation rewards for del2
   882  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
   883  	require.NoError(t, err)
   884  
   885  	// rewards for del2 should be zero
   886  	require.True(t, rewards.IsZero())
   887  
   888  	// commission should be zero
   889  	valCommission, err := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   890  	require.NoError(t, err)
   891  	require.True(t, valCommission.Commission.IsZero())
   892  
   893  	// next block
   894  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   895  
   896  	// allocate some more rewards
   897  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   898  
   899  	// first delegator withdraws again
   900  	expCommission = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial*1/4))}
   901  	bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission)
   902  	_, err = distrKeeper.WithdrawDelegationRewards(ctx, addr, valAddr)
   903  	require.NoError(t, err)
   904  
   905  	// end period
   906  	endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
   907  
   908  	// calculate delegation rewards for del1
   909  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   910  	require.NoError(t, err)
   911  
   912  	// rewards for del1 should be zero
   913  	require.True(t, rewards.IsZero())
   914  
   915  	// calculate delegation rewards for del2
   916  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
   917  	require.NoError(t, err)
   918  
   919  	// rewards for del2 should be 1/4 initial
   920  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards)
   921  
   922  	// commission should be half initial
   923  	valCommission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   924  	require.NoError(t, err)
   925  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, valCommission.Commission)
   926  
   927  	// next block
   928  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
   929  
   930  	// allocate some more rewards
   931  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
   932  
   933  	// withdraw commission
   934  	expCommission = sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(initial))}
   935  	bankKeeper.EXPECT().SendCoinsFromModuleToAccount(ctx, disttypes.ModuleName, addr, expCommission)
   936  	_, err = distrKeeper.WithdrawValidatorCommission(ctx, valAddr)
   937  	require.NoError(t, err)
   938  
   939  	// end period
   940  	endingPeriod, _ = distrKeeper.IncrementValidatorPeriod(ctx, val)
   941  
   942  	// calculate delegation rewards for del1
   943  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del, endingPeriod)
   944  	require.NoError(t, err)
   945  
   946  	// rewards for del1 should be 1/4 initial
   947  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 4)}}, rewards)
   948  
   949  	// calculate delegation rewards for del2
   950  	rewards, err = distrKeeper.CalculateDelegationRewards(ctx, val, del2, endingPeriod)
   951  	require.NoError(t, err)
   952  
   953  	// rewards for del2 should be 1/2 initial
   954  	require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(initial / 2)}}, rewards)
   955  
   956  	// commission should be zero
   957  	valCommission, err = distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddr)
   958  	require.NoError(t, err)
   959  	require.True(t, valCommission.Commission.IsZero())
   960  }
   961  
   962  func Test100PercentCommissionReward(t *testing.T) {
   963  	ctrl := gomock.NewController(t)
   964  	key := storetypes.NewKVStoreKey(disttypes.StoreKey)
   965  	storeService := runtime.NewKVStoreService(key)
   966  	testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
   967  	encCfg := moduletestutil.MakeTestEncodingConfig(distribution.AppModuleBasic{})
   968  	ctx := testCtx.Ctx.WithBlockHeader(cmtproto.Header{Height: 1})
   969  
   970  	bankKeeper := distrtestutil.NewMockBankKeeper(ctrl)
   971  	stakingKeeper := distrtestutil.NewMockStakingKeeper(ctrl)
   972  	accountKeeper := distrtestutil.NewMockAccountKeeper(ctrl)
   973  
   974  	accountKeeper.EXPECT().GetModuleAddress("distribution").Return(distrAcc.GetAddress())
   975  	stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec(sdk.Bech32PrefixValAddr)).AnyTimes()
   976  	accountKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec(sdk.Bech32MainPrefix)).AnyTimes()
   977  
   978  	distrKeeper := keeper.NewKeeper(
   979  		encCfg.Codec,
   980  		storeService,
   981  		accountKeeper,
   982  		bankKeeper,
   983  		stakingKeeper,
   984  		"fee_collector",
   985  		authtypes.NewModuleAddress("gov").String(),
   986  	)
   987  
   988  	// reset fee pool
   989  	require.NoError(t, distrKeeper.FeePool.Set(ctx, disttypes.InitialFeePool()))
   990  	require.NoError(t, distrKeeper.Params.Set(ctx, disttypes.DefaultParams()))
   991  
   992  	// create validator with 50% commission
   993  	valAddr := sdk.ValAddress(valConsAddr0)
   994  	addr := sdk.AccAddress(valAddr)
   995  	val, err := distrtestutil.CreateValidator(valConsPk0, math.NewInt(100))
   996  	require.NoError(t, err)
   997  	val.Commission = stakingtypes.NewCommission(math.LegacyNewDecWithPrec(10, 1), math.LegacyNewDecWithPrec(10, 1), math.LegacyNewDec(0))
   998  
   999  	// validator and delegation mocks
  1000  	del := stakingtypes.NewDelegation(addr.String(), valAddr.String(), val.DelegatorShares)
  1001  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(3)
  1002  	stakingKeeper.EXPECT().Delegation(gomock.Any(), addr, valAddr).Return(del, nil).Times(3)
  1003  
  1004  	// run the necessary hooks manually (given that we are not running an actual staking module)
  1005  	err = distrtestutil.CallCreateValidatorHooks(ctx, distrKeeper, addr, valAddr)
  1006  	require.NoError(t, err)
  1007  	stakingKeeper.EXPECT().Validator(gomock.Any(), valAddr).Return(val, nil).Times(2)
  1008  
  1009  	// next block
  1010  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
  1011  
  1012  	// allocate some rewards
  1013  	initial := int64(20)
  1014  	tokens := sdk.DecCoins{sdk.NewDecCoin(sdk.DefaultBondDenom, math.NewInt(initial))}
  1015  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
  1016  
  1017  	// next block
  1018  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
  1019  
  1020  	// allocate some rewards
  1021  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
  1022  
  1023  	// next block
  1024  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
  1025  
  1026  	// allocate some more rewards
  1027  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
  1028  
  1029  	// next block
  1030  	ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 1)
  1031  
  1032  	// allocate some more rewards
  1033  	require.NoError(t, distrKeeper.AllocateTokensToValidator(ctx, val, tokens))
  1034  
  1035  	rewards, err := distrKeeper.WithdrawDelegationRewards(ctx, addr, valAddr)
  1036  	require.NoError(t, err)
  1037  
  1038  	zeroRewards := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, math.ZeroInt())}
  1039  	require.True(t, rewards.Equal(zeroRewards))
  1040  
  1041  	events := ctx.EventManager().Events()
  1042  	lastEvent := events[len(events)-1]
  1043  
  1044  	var hasValue bool
  1045  	for _, attr := range lastEvent.Attributes {
  1046  		if attr.Key == "amount" && attr.Value == "0stake" {
  1047  			hasValue = true
  1048  		}
  1049  	}
  1050  	require.True(t, hasValue)
  1051  }