diff --git a/x/distribution/abci_test.go b/x/distribution/abci_test.go new file mode 100644 index 000000000..ea31b564b --- /dev/null +++ b/x/distribution/abci_test.go @@ -0,0 +1,107 @@ +package distribution_test + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking/teststaking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" +) + +const ( + totalValidators = 6 + lazyValidatorIdx = 2 + power = 100 / totalValidators +) + +var ( + valTokens = sdk.TokensFromConsensusPower(50, sdk.DefaultPowerReduction) + validatorCommissionRates = stakingtypes.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()) +) + +type validator struct { + addr sdk.ValAddress + pubkey cryptotypes.PubKey + votes []abci.VoteInfo +} + +// Context in https://github.com/cosmos/cosmos-sdk/issues/9161 +func TestVerifyProposerRewardAssignement(t *testing.T) { + app := simapp.Setup(t, false) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + addrs := simapp.AddTestAddrsIncremental(app, ctx, totalValidators, valTokens) + tstaking := teststaking.NewHelper(t, ctx, app.StakingKeeper) + tstaking.Commission = validatorCommissionRates + + // create validators + validators := make([]validator, totalValidators-1) + for i := range validators { + validators[i].addr = sdk.ValAddress(addrs[i]) + validators[i].pubkey = ed25519.GenPrivKey().PubKey() + validators[i].votes = make([]abci.VoteInfo, totalValidators) + tstaking.CreateValidatorWithValPower(validators[i].addr, validators[i].pubkey, power, true) + } + app.EndBlock(abci.RequestEndBlock{}) + require.NotEmpty(t, app.Commit()) + + // verify validators lists + require.Len(t, app.StakingKeeper.GetAllValidators(ctx), totalValidators) + for i, val := range validators { + // verify all validator exists + require.NotNil(t, app.StakingKeeper.ValidatorByConsAddr(ctx, sdk.GetConsAddress(val.pubkey))) + + // populate last commit info + voteInfos := []abci.VoteInfo{} + for _, val2 := range validators { + voteInfos = append(voteInfos, abci.VoteInfo{ + Validator: abci.Validator{ + Address: sdk.GetConsAddress(val2.pubkey), + Power: power, + }, + SignedLastBlock: true, + }) + } + + // have this validator only submit the minimum amount of pre-commits + if i == lazyValidatorIdx { + for j := totalValidators * 2 / 3; j < len(voteInfos); j++ { + voteInfos[j].SignedLastBlock = false + } + } + + validators[i].votes = voteInfos + } + + // previous block submitted by validator n-1 (with 100% previous commits) and proposed by lazy validator + app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{Height: app.LastBlockHeight() + 1, ProposerAddress: sdk.GetConsAddress(validators[lazyValidatorIdx].pubkey)}, + LastCommitInfo: abci.LastCommitInfo{Votes: validators[lazyValidatorIdx-1].votes}, + }) + require.NotEmpty(t, app.Commit()) + + // previous block submitted by lazy validator (with 67% previous commits) and proposed by validator n+1 + app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{Height: app.LastBlockHeight() + 1, ProposerAddress: sdk.GetConsAddress(validators[lazyValidatorIdx+1].pubkey)}, + LastCommitInfo: abci.LastCommitInfo{Votes: validators[lazyValidatorIdx].votes}, + }) + require.NotEmpty(t, app.Commit()) + + // previous block submitted by validator n+1 (with 100% previous commits) and proposed by validator n+2 + app.BeginBlock(abci.RequestBeginBlock{ + Header: tmproto.Header{Height: app.LastBlockHeight() + 1, ProposerAddress: sdk.GetConsAddress(validators[lazyValidatorIdx+2].pubkey)}, + LastCommitInfo: abci.LastCommitInfo{Votes: validators[lazyValidatorIdx+1].votes}, + }) + require.NotEmpty(t, app.Commit()) + + rewardsValidatorBeforeLazyValidator := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, validators[lazyValidatorIdx+1].addr) + rewardsLazyValidator := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, validators[lazyValidatorIdx].addr) + rewardsValidatorAfterLazyValidator := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, validators[lazyValidatorIdx+1].addr) + require.True(t, rewardsLazyValidator[0].Amount.LT(rewardsValidatorAfterLazyValidator[0].Amount)) + require.Equal(t, rewardsValidatorBeforeLazyValidator, rewardsValidatorAfterLazyValidator) +}