package keeper_test import ( "testing" "cosmossdk.io/math" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" "github.com/cosmos/cosmos-sdk/x/auth/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil" "github.com/cosmos/cosmos-sdk/x/distribution/keeper" "github.com/cosmos/cosmos-sdk/x/distribution/testutil" disttypes "github.com/cosmos/cosmos-sdk/x/distribution/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtestutil "github.com/cosmos/cosmos-sdk/x/staking/testutil" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestAllocateTokensToValidatorWithCommission(t *testing.T) { var ( bankKeeper bankkeeper.Keeper distrKeeper keeper.Keeper stakingKeeper *stakingkeeper.Keeper ) app, err := simtestutil.Setup(testutil.AppConfig, &bankKeeper, &distrKeeper, &stakingKeeper, ) require.NoError(t, err) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(1234)) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) tstaking := stakingtestutil.NewHelper(t, ctx, stakingKeeper) // create validator with 50% commission tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) tstaking.CreateValidator(sdk.ValAddress(addrs[0]), valConsPk0, sdk.NewInt(100), true) val := stakingKeeper.Validator(ctx, valAddrs[0]) // allocate tokens tokens := sdk.DecCoins{ {Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(10)}, } distrKeeper.AllocateTokensToValidator(ctx, val, tokens) // check commission expected := sdk.DecCoins{ {Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(5)}, } require.Equal(t, expected, distrKeeper.GetValidatorAccumulatedCommission(ctx, val.GetOperator()).Commission) // check current rewards require.Equal(t, expected, distrKeeper.GetValidatorCurrentRewards(ctx, val.GetOperator()).Rewards) } func TestAllocateTokensToManyValidators(t *testing.T) { var ( accountKeeper authkeeper.AccountKeeper bankKeeper bankkeeper.Keeper distrKeeper keeper.Keeper stakingKeeper *stakingkeeper.Keeper ) app, err := simtestutil.Setup(testutil.AppConfig, &accountKeeper, &bankKeeper, &distrKeeper, &stakingKeeper, ) require.NoError(t, err) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) // reset fee pool distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 2, sdk.NewInt(1234)) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) tstaking := stakingtestutil.NewHelper(t, ctx, stakingKeeper) // create validator with 50% commission tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), math.LegacyNewDec(0)) tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(100), true) // create second validator with 0% commission tstaking.Commission = stakingtypes.NewCommissionRates(math.LegacyNewDec(0), math.LegacyNewDec(0), math.LegacyNewDec(0)) tstaking.CreateValidator(valAddrs[1], valConsPk1, sdk.NewInt(100), true) abciValA := abci.Validator{ Address: valConsPk0.Address(), Power: 100, } abciValB := abci.Validator{ Address: valConsPk1.Address(), Power: 100, } // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) // allocate tokens as if both had voted and second was proposer fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))) feeCollector := accountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) require.NotNil(t, feeCollector) // fund fee collector require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, feeCollector.GetName(), fees)) accountKeeper.SetAccount(ctx, feeCollector) votes := []abci.VoteInfo{ { Validator: abciValA, SignedLastBlock: true, }, { Validator: abciValB, SignedLastBlock: true, }, } distrKeeper.AllocateTokens(ctx, 200, votes) // 98 outstanding rewards (100 less 2 to community pool) require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards) require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards) // 2 community pool coins require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: math.LegacyNewDec(2)}}, distrKeeper.GetFeePool(ctx).CommunityPool) // 50% commission for first proposer, (0.5 * 98%) * 100 / 2 = 23.25 require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission) // zero commission for second proposer require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) // just staking.proportional for first proposer less commission = (0.5 * 98%) * 100 / 2 = 24.50 require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards) // proposer reward + staking.proportional for second proposer = (0.5 * (98%)) * 100 = 49 require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards) } func TestAllocateTokensTruncation(t *testing.T) { var ( accountKeeper authkeeper.AccountKeeper bankKeeper bankkeeper.Keeper distrKeeper keeper.Keeper stakingKeeper *stakingkeeper.Keeper ) app, err := simtestutil.Setup(testutil.AppConfig, &accountKeeper, &bankKeeper, &distrKeeper, &stakingKeeper, ) require.NoError(t, err) ctx := app.BaseApp.NewContext(false, tmproto.Header{}) // reset fee pool distrKeeper.SetFeePool(ctx, disttypes.InitialFeePool()) addrs := simtestutil.AddTestAddrs(bankKeeper, stakingKeeper, ctx, 3, sdk.NewInt(1234)) valAddrs := simtestutil.ConvertAddrsToValAddrs(addrs) tstaking := stakingtestutil.NewHelper(t, ctx, stakingKeeper) // create validator with 10% commission tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) tstaking.CreateValidator(valAddrs[0], valConsPk0, sdk.NewInt(110), true) // create second validator with 10% commission tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) tstaking.CreateValidator(valAddrs[1], valConsPk1, sdk.NewInt(100), true) // create third validator with 10% commission tstaking.Commission = stakingtypes.NewCommissionRates(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(1, 1), math.LegacyNewDec(0)) tstaking.CreateValidator(valAddrs[2], valConsPk2, sdk.NewInt(100), true) abciValA := abci.Validator{ Address: valConsPk0.Address(), Power: 11, } abciValB := abci.Validator{ Address: valConsPk1.Address(), Power: 10, } abciValС := abci.Validator{ Address: valConsPk2.Address(), Power: 10, } // assert initial state: zero outstanding rewards, zero community pool, zero commission, zero current rewards require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsZero()) require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsZero()) require.True(t, distrKeeper.GetFeePool(ctx).CommunityPool.IsZero()) require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission.IsZero()) require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero()) require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards.IsZero()) require.True(t, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards.IsZero()) // allocate tokens as if both had voted and second was proposer fees := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(634195840))) feeCollector := accountKeeper.GetModuleAccount(ctx, types.FeeCollectorName) require.NotNil(t, feeCollector) require.NoError(t, banktestutil.FundModuleAccount(bankKeeper, ctx, feeCollector.GetName(), fees)) accountKeeper.SetAccount(ctx, feeCollector) votes := []abci.VoteInfo{ { Validator: abciValA, SignedLastBlock: true, }, { Validator: abciValB, SignedLastBlock: true, }, { Validator: abciValС, SignedLastBlock: true, }, } distrKeeper.AllocateTokens(ctx, 31, votes) require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards.IsValid()) require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards.IsValid()) require.True(t, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[2]).Rewards.IsValid()) }