Skip to content

Commit

Permalink
feat: add additional voting powers hook on gov
Browse files Browse the repository at this point in the history
  • Loading branch information
dongsam committed Mar 11, 2022
1 parent 33dbf6a commit d76f922
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 6 deletions.
7 changes: 7 additions & 0 deletions x/gov/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,10 @@ func (keeper Keeper) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID
keeper.hooks.AfterProposalVotingPeriodEnded(ctx, proposalID)
}
}

// SetAdditionalVotingPowers - call hook if registered
func (keeper Keeper) SetAdditionalVotingPowers(ctx sdk.Context, votes types.Votes, votingPowers *types.AdditionalVotingPowers) {
if keeper.hooks != nil {
keeper.hooks.SetAdditionalVotingPowers(ctx, votes, votingPowers)
}
}
6 changes: 6 additions & 0 deletions x/gov/keeper/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type MockGovHooksReceiver struct {
AfterProposalVoteValid bool
AfterProposalFailedMinDepositValid bool
AfterProposalVotingPeriodEndedValid bool
GetAdditionalVotingPowersValid bool
}

func (h *MockGovHooksReceiver) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) {
Expand All @@ -42,6 +43,9 @@ func (h *MockGovHooksReceiver) AfterProposalFailedMinDeposit(ctx sdk.Context, pr
func (h *MockGovHooksReceiver) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) {
h.AfterProposalVotingPeriodEndedValid = true
}
func (h *MockGovHooksReceiver) SetAdditionalVotingPowers(ctx sdk.Context, votes types.Votes, votingPowers *types.AdditionalVotingPowers) {
h.GetAdditionalVotingPowersValid = true
}

func TestHooks(t *testing.T) {
app := simapp.Setup(false)
Expand All @@ -61,6 +65,7 @@ func TestHooks(t *testing.T) {
require.False(t, govHooksReceiver.AfterProposalVoteValid)
require.False(t, govHooksReceiver.AfterProposalFailedMinDepositValid)
require.False(t, govHooksReceiver.AfterProposalVotingPeriodEndedValid)
require.False(t, govHooksReceiver.GetAdditionalVotingPowersValid)

tp := TestProposal
_, err := app.GovKeeper.SubmitProposal(ctx, tp)
Expand Down Expand Up @@ -91,4 +96,5 @@ func TestHooks(t *testing.T) {
ctx = ctx.WithBlockHeader(newHeader)
gov.EndBlocker(ctx, app.GovKeeper)
require.True(t, govHooksReceiver.AfterProposalVotingPeriodEndedValid)
require.True(t, govHooksReceiver.GetAdditionalVotingPowersValid)
}
27 changes: 23 additions & 4 deletions x/gov/keeper/tally.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo
return false
})

keeper.IterateVotes(ctx, proposal.ProposalId, func(vote types.Vote) bool {
additionalVotingPower := types.AdditionalVotingPowers{}
votes := keeper.GetVotes(ctx, proposal.ProposalId)
keeper.hooks.SetAdditionalVotingPowers(ctx, votes, &additionalVotingPower)
for _, vote := range votes {
// if validator, just record it in the map
voter, err := sdk.AccAddressFromBech32(vote.Voter)

if err != nil {
panic(err)
}
Expand All @@ -47,6 +49,24 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo
currValidators[valAddrStr] = val
}

if ovote, ok := additionalVotingPower[vote.Voter]; ok {
for valAddrStr, votingPower := range ovote {
if val, ok := currValidators[valAddrStr]; ok && val.BondedTokens.IsPositive() {

// total shares * voting power tokens / bonded
delShares := val.DelegatorShares.MulInt(votingPower.TruncateInt()).QuoInt(val.BondedTokens)
val.DelegatorDeductions = val.DelegatorDeductions.Add(delShares)
currValidators[valAddrStr] = val

for _, option := range vote.Options {
subPower := votingPower.Mul(option.Weight)
results[option.Option] = results[option.Option].Add(subPower)
}
totalVotingPower = totalVotingPower.Add(votingPower)
}
}
}

// iterate over all delegations from voter, deduct from any delegated-to validators
keeper.sk.IterateDelegations(ctx, voter, func(index int64, delegation stakingtypes.DelegationI) (stop bool) {
valAddrStr := delegation.GetValidatorAddr().String()
Expand All @@ -71,8 +91,7 @@ func (keeper Keeper) Tally(ctx sdk.Context, proposal types.Proposal) (passes boo
})

keeper.deleteVote(ctx, vote.ProposalId, voter)
return false
})
}

// iterate over the validators again to tally their voting power
for _, val := range currValidators {
Expand Down
10 changes: 8 additions & 2 deletions x/gov/spec/02_state.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,14 @@ And the pseudocode for the `ProposalProcessingQueue`:
tmpValMap(validator.OperatorAddr).Minus = 0

// Tally
voterIterator = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal
for each (voterAddress, vote) in voterIterator
voters = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal
// set additional voting powers by hooking other modules
additionalVotingPowersMap = SetAdditionalVotingPowers(voters)
for each (voterAddress, vote) in voters
for each (validator, votingPower) in additionalVotingPowersMap[voterAddress]
tmpValMap(validator).Minus += votingPower.Shares
proposal.updateTally(vote, votingPower.Shares)

delegations = stakingKeeper.getDelegations(voterAddress) // get all delegations for current voter

for each delegation in delegations
Expand Down
2 changes: 2 additions & 0 deletions x/gov/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ type GovHooks interface {
AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) // Must be called after a vote on a proposal is cast
AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) // Must be called when proposal fails to reach min deposit
AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) // Must be called when proposal's finishes it's voting period
// SetAdditionalVotingPowers is a hook for calculating and setting additional voting power for votes in modules other than gov.
SetAdditionalVotingPowers(ctx sdk.Context, votes Votes, votingPowers *AdditionalVotingPowers) // Must be called after get votes on tally
}
5 changes: 5 additions & 0 deletions x/gov/types/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ func (h MultiGovHooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalI
h[i].AfterProposalVotingPeriodEnded(ctx, proposalID)
}
}
func (h MultiGovHooks) SetAdditionalVotingPowers(ctx sdk.Context, votes Votes, votingPowers *AdditionalVotingPowers) {
for i := range h {
h[i].SetAdditionalVotingPowers(ctx, votes, votingPowers)
}
}
3 changes: 3 additions & 0 deletions x/gov/types/vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ func (v Vote) String() string {
// Votes is a collection of Vote objects
type Votes []Vote

// AdditionalVotingPowers is additional votingPower map by validators by voters
type AdditionalVotingPowers map[string]map[string]sdk.Dec

// Equal returns true if two slices (order-dependant) of votes are equal.
func (v Votes) Equal(other Votes) bool {
if len(v) != len(other) {
Expand Down

0 comments on commit d76f922

Please sign in to comment.