diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f9a8aa8980a..4058ea0b6270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -178,6 +178,7 @@ to detail this new feature and how state transitions occur. * (cli) [\#4763](https://github.com/cosmos/cosmos-sdk/issues/4763) Fix flag `--min-self-delegation` for staking `EditValidator` * (keys) Fix ledger custom coin type support bug +* (x/gov) [\#5107](https://github.com/cosmos/cosmos-sdk/pull/5107) Sum validator operator's all voting power when tally votes * (baseapp) [\#5200](https://github.com/cosmos/cosmos-sdk/issues/5200) Remove duplicate events from previous messages. ## [v0.37.2] - 2019-10-10 diff --git a/x/gov/keeper/tally.go b/x/gov/keeper/tally.go index 1bd9082031a6..f6b0008d4052 100644 --- a/x/gov/keeper/tally.go +++ b/x/gov/keeper/tally.go @@ -35,30 +35,31 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo keeper.IterateVotes(ctx, proposal.ProposalID, func(vote types.Vote) bool { // if validator, just record it in the map - // if delegator tally voting power valAddrStr := sdk.ValAddress(vote.Voter).String() if val, ok := currValidators[valAddrStr]; ok { val.Vote = vote.Option currValidators[valAddrStr] = val - } else { - // iterate over all delegations from voter, deduct from any delegated-to validators - keeper.sk.IterateDelegations(ctx, vote.Voter, func(index int64, delegation exported.DelegationI) (stop bool) { - valAddrStr := delegation.GetValidatorAddr().String() + } - if val, ok := currValidators[valAddrStr]; ok { - val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares()) - currValidators[valAddrStr] = val + // iterate over all delegations from voter, deduct from any delegated-to validators + keeper.sk.IterateDelegations(ctx, vote.Voter, func(index int64, delegation exported.DelegationI) (stop bool) { + valAddrStr := delegation.GetValidatorAddr().String() - delegatorShare := delegation.GetShares().Quo(val.DelegatorShares) - votingPower := delegatorShare.MulInt(val.BondedTokens) + if val, ok := currValidators[valAddrStr]; ok { + // There is no need to handle the special case that validator address equal to voter address. + // Because voter's voting power will tally again even if there will deduct voter's voting power from validator. + val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares()) + currValidators[valAddrStr] = val - results[vote.Option] = results[vote.Option].Add(votingPower) - totalVotingPower = totalVotingPower.Add(votingPower) - } + delegatorShare := delegation.GetShares().Quo(val.DelegatorShares) + votingPower := delegatorShare.MulInt(val.BondedTokens) - return false - }) - } + results[vote.Option] = results[vote.Option].Add(votingPower) + totalVotingPower = totalVotingPower.Add(votingPower) + } + + return false + }) keeper.deleteVote(ctx, vote.ProposalID, vote.Voter) return false diff --git a/x/gov/keeper/tally_test.go b/x/gov/keeper/tally_test.go index dcf370289565..ceaed4eb4437 100644 --- a/x/gov/keeper/tally_test.go +++ b/x/gov/keeper/tally_test.go @@ -396,3 +396,41 @@ func TestTallyJailedValidator(t *testing.T) { require.False(t, burnDeposits) require.False(t, tallyResults.Equals(types.EmptyTallyResult())) } + +func TestTallyValidatorMultipleDelegations(t *testing.T) { + ctx, _, keeper, sk, _ := createTestInput(t, false, 100) + createValidators(ctx, sk, []int64{10, 10, 10}) + + delTokens := sdk.TokensFromConsensusPower(10) + val2, found := sk.GetValidator(ctx, valOpAddr2) + require.True(t, found) + + _, err := sk.Delegate(ctx, valAccAddr1, delTokens, sdk.Unbonded, val2, true) + require.NoError(t, err) + + tp := TestProposal + proposal, err := keeper.SubmitProposal(ctx, tp) + require.NoError(t, err) + proposalID := proposal.ProposalID + proposal.Status = types.StatusVotingPeriod + keeper.SetProposal(ctx, proposal) + + require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr1, types.OptionYes)) + require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr2, types.OptionNo)) + require.NoError(t, keeper.AddVote(ctx, proposalID, valAccAddr3, types.OptionYes)) + + proposal, ok := keeper.GetProposal(ctx, proposalID) + require.True(t, ok) + passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) + + require.True(t, passes) + require.False(t, burnDeposits) + + expectedYes := sdk.TokensFromConsensusPower(30) + expectedAbstain := sdk.TokensFromConsensusPower(0) + expectedNo := sdk.TokensFromConsensusPower(10) + expectedNoWithVeto := sdk.TokensFromConsensusPower(0) + expectedTallyResult := types.NewTallyResult(expectedYes, expectedAbstain, expectedNo, expectedNoWithVeto) + + require.True(t, tallyResults.Equals(expectedTallyResult)) +}