Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: distribute every 50 blocks #527

Merged
merged 12 commits into from
Feb 16, 2024
5 changes: 5 additions & 0 deletions tests/e2e/distribution/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
sdk "github.com/cosmos/cosmos-sdk/types"
distr "github.com/cosmos/cosmos-sdk/x/distribution"
"github.com/cosmos/cosmos-sdk/x/distribution/client/cli"
distrclitestutil "github.com/cosmos/cosmos-sdk/x/distribution/client/testutil"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
Expand All @@ -39,6 +40,10 @@ func NewE2ETestSuite(cfg network.Config) *E2ETestSuite {
func (s *E2ETestSuite) SetupSuite() {
s.T().Log("setting up e2e test suite")

// We distribute rewards every block here since we test the delayed distribution
// in another test.
distr.BlockMultipleToDistributeRewards = 1

cfg := network.DefaultConfig(simapp.NewTestNetworkFixture)
cfg.NumValidators = 1
s.cfg = cfg
Expand Down
74 changes: 68 additions & 6 deletions tests/integration/distribution/keeper/allocation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"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"
dist "github.com/cosmos/cosmos-sdk/x/distribution"
"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"
Expand Down Expand Up @@ -71,6 +72,10 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
stakingKeeper *stakingkeeper.Keeper
)

// set distribute to every block for first test.
// we test the delayed distribution in the next test.
dist.BlockMultipleToDistributeRewards = 1

app, err := simtestutil.Setup(testutil.AppConfig,
&accountKeeper,
&bankKeeper,
Expand Down Expand Up @@ -137,23 +142,80 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
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)
firstValidator0OutstandingRewards := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards
firstValidator1OutstandingRewards := distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, firstValidator0OutstandingRewards)
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, firstValidator1OutstandingRewards)

// 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)
firstValidator0Commission := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, firstValidator0Commission)

// zero commission for second proposer
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero())
firstValidator1Commission := distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission
require.True(t, firstValidator1Commission.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)
firstValidator0CurrentRewards := distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(2450, 2)}}, firstValidator0CurrentRewards)

// 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)
firstValidator1CurrentRewards := distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards
require.Equal(t, sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDecWithPrec(490, 1)}}, firstValidator1CurrentRewards)

// test that the block height triggers the distribution
dist.BlockMultipleToDistributeRewards = 50

// block height is not a multiple, should not trigger allocation (no change in rewards)
ctx = ctx.WithBlockHeight(dist.BlockMultipleToDistributeRewards - 1)
app.BeginBlocker(ctx, abci.RequestBeginBlock{Header: tmproto.Header{ProposerAddress: valAddrs[0].Bytes()},
LastCommitInfo: abci.CommitInfo{
Votes: votes,
},
})
require.Equal(t, firstValidator0OutstandingRewards, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards)
require.Equal(t, firstValidator1OutstandingRewards, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards)
require.Equal(t, firstValidator0Commission, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission)
require.Equal(t, firstValidator1Commission, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission)
require.Equal(t, firstValidator0CurrentRewards, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards)
require.Equal(t, firstValidator1CurrentRewards, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards)

// block height is a multiple, should trigger allocation
ctx = ctx.WithBlockHeight(dist.BlockMultipleToDistributeRewards)

feesCollectedInt := bankKeeper.GetAllBalances(ctx, feeCollector.GetAddress())

// feesCollected was increased from last BeginBlocker call, then will occur again from the new BeginBlocker call,
// so we need to double the feesCollected to simulate the new BeginBlocker call
feesCollectedInt[0].Amount = feesCollectedInt[0].Amount.MulRaw(2)
feesCollected := sdk.NewDecCoinsFromCoins(feesCollectedInt...)

communityTax := distrKeeper.GetCommunityTax(ctx)
voteMultiplier := math.LegacyOneDec().Sub(communityTax)
feeMultiplier := feesCollected.MulDecTruncate(voteMultiplier)
powerFraction := math.LegacyNewDec(100).QuoTruncate(math.LegacyNewDec(200))

newRewards := feeMultiplier.MulDecTruncate(powerFraction)
pendingRewards := firstValidator0OutstandingRewards.Add(newRewards...)

pendingCommission := firstValidator0OutstandingRewards.Add(newRewards...)
pendingCommission[0].Amount = pendingCommission[0].Amount.Quo(sdk.NewDec(2))

app.BeginBlocker(ctx, abci.RequestBeginBlock{Header: tmproto.Header{ProposerAddress: valAddrs[0].Bytes()},
LastCommitInfo: abci.CommitInfo{
Votes: votes,
},
})

require.Equal(t, pendingRewards, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[0]).Rewards)
require.Equal(t, pendingRewards, distrKeeper.GetValidatorOutstandingRewards(ctx, valAddrs[1]).Rewards)
require.Equal(t, pendingCommission, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[0]).Commission)
require.True(t, distrKeeper.GetValidatorAccumulatedCommission(ctx, valAddrs[1]).Commission.IsZero())
require.Equal(t, pendingCommission, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[0]).Rewards)
require.Equal(t, pendingRewards, distrKeeper.GetValidatorCurrentRewards(ctx, valAddrs[1]).Rewards)
}

func TestAllocateTokensTruncation(t *testing.T) {
Expand Down
9 changes: 8 additions & 1 deletion tests/integration/distribution/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper_test

import (
sdk "github.com/cosmos/cosmos-sdk/types"
banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)

Expand Down Expand Up @@ -159,7 +160,13 @@ func (s *KeeperTestSuite) TestCommunityPoolSpend() {
for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
_, err := s.msgServer.CommunityPoolSpend(s.ctx, tc.input)
amount := sdk.NewCoins(sdk.NewInt64Coin("stake", 100))
s.Require().NoError(banktestutil.FundAccount(s.bankKeeper, s.ctx, s.addrs[0], amount))

err := s.distrKeeper.FundCommunityPool(s.ctx, amount, s.addrs[0])
s.Require().Nil(err)

_, err = s.msgServer.CommunityPoolSpend(s.ctx, tc.input)

if tc.expErr {
s.Require().Error(err)
Expand Down
19 changes: 12 additions & 7 deletions x/distribution/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,25 @@ import (
"github.com/cosmos/cosmos-sdk/x/distribution/types"
)

var BlockMultipleToDistributeRewards = int64(50)

// BeginBlocker sets the proposer for determining distribution during endblock
// and distribute rewards for the previous block.
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)

// determine the total power signing the block
var previousTotalPower int64
for _, voteInfo := range req.LastCommitInfo.GetVotes() {
previousTotalPower += voteInfo.Validator.Power
}

// TODO this is Tendermint-dependent
// ref https://github.com/cosmos/cosmos-sdk/issues/3095
if ctx.BlockHeight() > 1 {
blockHeight := ctx.BlockHeight()
// only allocate rewards if the block height is greater than 1
// and for every multiple of 50 blocks for performance reasons.
if blockHeight > 1 && blockHeight%BlockMultipleToDistributeRewards == 0 {
// determine the total power signing the block
var previousTotalPower int64
for _, voteInfo := range req.LastCommitInfo.GetVotes() {
previousTotalPower += voteInfo.Validator.Power
}

k.AllocateTokens(ctx, previousTotalPower, req.LastCommitInfo.GetVotes())
}

Expand Down
Loading