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 }