Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pending/breaking/sdk/4437-bytes-gov-keys
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#4437 replace gov store keys to use `[]byte` instead of `string`
1 change: 1 addition & 0 deletions .pending/improvements/sdk/4439-add-governance-
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#4439 add governance iterators
93 changes: 51 additions & 42 deletions x/gov/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,52 +48,61 @@ const (

var (
// functions aliases
RegisterCodec = types.RegisterCodec
RegisterProposalTypeCodec = types.RegisterProposalTypeCodec
ValidateAbstract = types.ValidateAbstract
ErrUnknownProposal = types.ErrUnknownProposal
ErrInactiveProposal = types.ErrInactiveProposal
ErrAlreadyActiveProposal = types.ErrAlreadyActiveProposal
ErrAlreadyFinishedProposal = types.ErrAlreadyFinishedProposal
ErrAddressNotStaked = types.ErrAddressNotStaked
ErrInvalidProposalContent = types.ErrInvalidProposalContent
ErrInvalidProposalType = types.ErrInvalidProposalType
ErrInvalidVote = types.ErrInvalidVote
ErrInvalidGenesis = types.ErrInvalidGenesis
ErrNoProposalHandlerExists = types.ErrNoProposalHandlerExists
KeyProposal = types.KeyProposal
KeyDeposit = types.KeyDeposit
KeyVote = types.KeyVote
KeyDepositsSubspace = types.KeyDepositsSubspace
KeyVotesSubspace = types.KeyVotesSubspace
PrefixActiveProposalQueueTime = types.PrefixActiveProposalQueueTime
KeyActiveProposalQueueProposal = types.KeyActiveProposalQueueProposal
PrefixInactiveProposalQueueTime = types.PrefixInactiveProposalQueueTime
KeyInactiveProposalQueueProposal = types.KeyInactiveProposalQueueProposal
NewMsgSubmitProposal = types.NewMsgSubmitProposal
NewMsgDeposit = types.NewMsgDeposit
NewMsgVote = types.NewMsgVote
NewProposal = types.NewProposal
ProposalStatusFromString = types.ProposalStatusFromString
ValidProposalStatus = types.ValidProposalStatus
NewTallyResult = types.NewTallyResult
NewTallyResultFromMap = types.NewTallyResultFromMap
EmptyTallyResult = types.EmptyTallyResult
NewTextProposal = types.NewTextProposal
NewSoftwareUpgradeProposal = types.NewSoftwareUpgradeProposal
RegisterProposalType = types.RegisterProposalType
ContentFromProposalType = types.ContentFromProposalType
IsValidProposalType = types.IsValidProposalType
ProposalHandler = types.ProposalHandler
VoteOptionFromString = types.VoteOptionFromString
ValidVoteOption = types.ValidVoteOption
RegisterCodec = types.RegisterCodec
RegisterProposalTypeCodec = types.RegisterProposalTypeCodec
ValidateAbstract = types.ValidateAbstract
NewDeposit = types.NewDeposit
ErrUnknownProposal = types.ErrUnknownProposal
ErrInactiveProposal = types.ErrInactiveProposal
ErrAlreadyActiveProposal = types.ErrAlreadyActiveProposal
ErrAlreadyFinishedProposal = types.ErrAlreadyFinishedProposal
ErrAddressNotStaked = types.ErrAddressNotStaked
ErrInvalidProposalContent = types.ErrInvalidProposalContent
ErrInvalidProposalType = types.ErrInvalidProposalType
ErrInvalidVote = types.ErrInvalidVote
ErrInvalidGenesis = types.ErrInvalidGenesis
ErrNoProposalHandlerExists = types.ErrNoProposalHandlerExists
KeyProposal = types.KeyProposal
KeyActiveProposalByTime = types.KeyActiveProposalByTime
KeyActiveProposalQueue = types.KeyActiveProposalQueue
KeyInactiveProposalByTime = types.KeyInactiveProposalByTime
KeyInactiveProposalQueue = types.KeyInactiveProposalQueue
KeyProposalDeposits = types.KeyProposalDeposits
KeyProposalDeposit = types.KeyProposalDeposit
KeyProposalVotes = types.KeyProposalVotes
KeyProposalVote = types.KeyProposalVote
SplitKeyProposal = types.SplitKeyProposal
SplitKeyActiveProposalQueue = types.SplitKeyActiveProposalQueue
SplitKeyInactiveProposalQueue = types.SplitKeyInactiveProposalQueue
SplitKeyDeposit = types.SplitKeyDeposit
SplitKeyVote = types.SplitKeyVote
NewMsgSubmitProposal = types.NewMsgSubmitProposal
NewMsgDeposit = types.NewMsgDeposit
NewMsgVote = types.NewMsgVote
NewProposal = types.NewProposal
ProposalStatusFromString = types.ProposalStatusFromString
ValidProposalStatus = types.ValidProposalStatus
NewTallyResult = types.NewTallyResult
NewTallyResultFromMap = types.NewTallyResultFromMap
EmptyTallyResult = types.EmptyTallyResult
NewTextProposal = types.NewTextProposal
NewSoftwareUpgradeProposal = types.NewSoftwareUpgradeProposal
RegisterProposalType = types.RegisterProposalType
ContentFromProposalType = types.ContentFromProposalType
IsValidProposalType = types.IsValidProposalType
ProposalHandler = types.ProposalHandler
NewVote = types.NewVote
VoteOptionFromString = types.VoteOptionFromString
ValidVoteOption = types.ValidVoteOption

// variable aliases
ModuleCdc = types.ModuleCdc
KeyDelimiter = types.KeyDelimiter
ProposalsKeyPrefix = types.ProposalsKeyPrefix
ActiveProposalQueuePrefix = types.ActiveProposalQueuePrefix
InactiveProposalQueuePrefix = types.InactiveProposalQueuePrefix
KeyNextProposalID = types.KeyNextProposalID
PrefixActiveProposalQueue = types.PrefixActiveProposalQueue
PrefixInactiveProposalQueue = types.PrefixInactiveProposalQueue
DepositsKeyPrefix = types.DepositsKeyPrefix
VotesKeyPrefix = types.VotesKeyPrefix
)

type (
Expand Down
129 changes: 129 additions & 0 deletions x/gov/deposit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package gov

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)

// GetDeposit gets the deposit of a specific depositor on a specific proposal
func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) (deposit Deposit, found bool) {
store := ctx.KVStore(keeper.storeKey)
bz := store.Get(types.KeyProposalDeposit(proposalID, depositorAddr))
if bz == nil {
return deposit, false
}

keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &deposit)
return deposit, true
}

func (keeper Keeper) setDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress, deposit Deposit) {
store := ctx.KVStore(keeper.storeKey)
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(deposit)
store.Set(types.KeyProposalDeposit(proposalID, depositorAddr), bz)
}

// AddDeposit adds or updates a deposit of a specific depositor on a specific proposal
// Activates voting period when appropriate
func (keeper Keeper) AddDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress, depositAmount sdk.Coins) (sdk.Error, bool) {
// Checks to see if proposal exists
proposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
return ErrUnknownProposal(keeper.codespace, proposalID), false
}

// Check if proposal is still depositable
if (proposal.Status != StatusDepositPeriod) && (proposal.Status != StatusVotingPeriod) {
return ErrAlreadyFinishedProposal(keeper.codespace, proposalID), false
}

// Send coins from depositor's account to DepositedCoinsAccAddr account
// TODO: Don't use an account for this purpose; it's clumsy and prone to misuse.
err := keeper.ck.SendCoins(ctx, depositorAddr, DepositedCoinsAccAddr, depositAmount)
if err != nil {
return err, false
}

// Update proposal
proposal.TotalDeposit = proposal.TotalDeposit.Add(depositAmount)
keeper.SetProposal(ctx, proposal)

// Check if deposit has provided sufficient total funds to transition the proposal into the voting period
activatedVotingPeriod := false
if proposal.Status == StatusDepositPeriod && proposal.TotalDeposit.IsAllGTE(keeper.GetDepositParams(ctx).MinDeposit) {
keeper.activateVotingPeriod(ctx, proposal)
activatedVotingPeriod = true
}

// Add or update deposit object
currDeposit, found := keeper.GetDeposit(ctx, proposalID, depositorAddr)
if !found {
newDeposit := NewDeposit(proposalID, depositorAddr, depositAmount)
keeper.setDeposit(ctx, proposalID, depositorAddr, newDeposit)
} else {
currDeposit.Amount = currDeposit.Amount.Add(depositAmount)
keeper.setDeposit(ctx, proposalID, depositorAddr, currDeposit)
}

return nil, activatedVotingPeriod
}

// GetDeposits returns all the deposits from the store
func (keeper Keeper) GetDeposits(ctx sdk.Context) (deposits Deposits) {
store := ctx.KVStore(keeper.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DepositsKeyPrefix)

keeper.IterateDeposits(ctx, iterator, func(deposit Deposit) bool {
deposits = append(deposits, deposit)
return false
})
return
}

// GetProposalDeposits returns all the deposits from a proposal
func (keeper Keeper) GetProposalDeposits(ctx sdk.Context, proposalID uint64) (deposits Deposits) {
iterator := keeper.GetProposalDepositsIterator(ctx, proposalID)

keeper.IterateDeposits(ctx, iterator, func(deposit Deposit) bool {
deposits = append(deposits, deposit)
return false
})
return
}

// GetProposalDepositsIterator gets all the deposits on a specific proposal as an sdk.Iterator
func (keeper Keeper) GetProposalDepositsIterator(ctx sdk.Context, proposalID uint64) sdk.Iterator {
store := ctx.KVStore(keeper.storeKey)
return sdk.KVStorePrefixIterator(store, types.KeyProposalDeposits(proposalID))
}

// RefundProposalDeposits refunds and deletes all the deposits on a specific proposal
func (keeper Keeper) RefundProposalDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
iterator := keeper.GetProposalDepositsIterator(ctx, proposalID)

keeper.IterateDeposits(ctx, iterator, func(deposit Deposit) bool {
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositor, deposit.Amount)
if err != nil {
panic("should not happen")
}
store.Delete(iterator.Key())
return false
})
}

// DeleteProposalDeposits deletes all the deposits on a specific proposal without refunding them
func (keeper Keeper) DeleteProposalDeposits(ctx sdk.Context, proposalID uint64) {
store := ctx.KVStore(keeper.storeKey)
iterator := keeper.GetProposalDepositsIterator(ctx, proposalID)

keeper.IterateDeposits(ctx, iterator, func(deposit Deposit) bool {
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount)
if err != nil {
panic("should not happen")
}

store.Delete(iterator.Key())
return false
})
}
71 changes: 29 additions & 42 deletions x/gov/endblocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,100 +7,87 @@ import (
"github.com/cosmos/cosmos-sdk/x/gov/tags"
)

// Called every block, process inflation, update validator set
// EndBlocker called every block, process inflation, update validator set
func EndBlocker(ctx sdk.Context, keeper Keeper) sdk.Tags {
logger := keeper.Logger(ctx)
resTags := sdk.NewTags()

// delete inactive proposal from store and its deposits
inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
defer inactiveIterator.Close()
for ; inactiveIterator.Valid(); inactiveIterator.Next() {
var proposalID uint64

keeper.cdc.MustUnmarshalBinaryLengthPrefixed(inactiveIterator.Value(), &proposalID)
inactiveProposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
panic(fmt.Sprintf("proposal %d does not exist", proposalID))
}

keeper.DeleteProposal(ctx, proposalID)
keeper.DeleteDeposits(ctx, proposalID) // delete any associated deposits (burned)
keeper.IterateInactiveProposalsQueue(ctx, inactiveIterator, func(proposal Proposal) bool {
keeper.DeleteProposal(ctx, proposal.ProposalID)
keeper.DeleteProposalDeposits(ctx, proposal.ProposalID)

resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID))
resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposal.ProposalID))
resTags = resTags.AppendTag(tags.ProposalResult, tags.ActionProposalDropped)

logger.Info(
fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %s (had only %s); deleted",
inactiveProposal.ProposalID,
inactiveProposal.GetTitle(),
proposal.ProposalID,
proposal.GetTitle(),
keeper.GetDepositParams(ctx).MinDeposit,
inactiveProposal.TotalDeposit,
proposal.TotalDeposit,
),
)
}
return false
})

// fetch active proposals whose voting periods have ended (are passed the block time)
activeIterator := keeper.ActiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
defer activeIterator.Close()
for ; activeIterator.Valid(); activeIterator.Next() {
var proposalID uint64

keeper.cdc.MustUnmarshalBinaryLengthPrefixed(activeIterator.Value(), &proposalID)
activeProposal, ok := keeper.GetProposal(ctx, proposalID)
if !ok {
panic(fmt.Sprintf("proposal %d does not exist", proposalID))
}
passes, burnDeposits, tallyResults := tally(ctx, keeper, activeProposal)

keeper.IterateActiveProposalsQueue(ctx, activeIterator, func(proposal Proposal) bool {
var tagValue, logMsg string

passes, burnDeposits, tallyResults := tally(ctx, keeper, proposal)

if burnDeposits {
keeper.DeleteDeposits(ctx, activeProposal.ProposalID)
keeper.DeleteProposalDeposits(ctx, proposal.ProposalID)
} else {
keeper.RefundDeposits(ctx, activeProposal.ProposalID)
keeper.RefundProposalDeposits(ctx, proposal.ProposalID)
}

if passes {
handler := keeper.router.GetRoute(activeProposal.ProposalRoute())
handler := keeper.router.GetRoute(proposal.ProposalRoute())
cacheCtx, writeCache := ctx.CacheContext()

// The proposal handler may execute state mutating logic depending
// on the proposal content. If the handler fails, no state mutation
// is written and the error message is logged.
err := handler(cacheCtx, activeProposal.Content)
err := handler(cacheCtx, proposal.Content)
if err == nil {
activeProposal.Status = StatusPassed
proposal.Status = StatusPassed
tagValue = tags.ActionProposalPassed
logMsg = "passed"

// write state to the underlying multi-store
writeCache()
} else {
activeProposal.Status = StatusFailed
proposal.Status = StatusFailed
tagValue = tags.ActionProposalFailed
logMsg = fmt.Sprintf("passed, but failed on execution: %s", err.ABCILog())
}
} else {
activeProposal.Status = StatusRejected
proposal.Status = StatusRejected
tagValue = tags.ActionProposalRejected
logMsg = "rejected"
}

activeProposal.FinalTallyResult = tallyResults
proposal.FinalTallyResult = tallyResults

keeper.SetProposal(ctx, activeProposal)
keeper.RemoveFromActiveProposalQueue(ctx, activeProposal.VotingEndTime, activeProposal.ProposalID)
keeper.SetProposal(ctx, proposal)
keeper.RemoveFromActiveProposalQueue(ctx, proposal.ProposalID, proposal.VotingEndTime)

logger.Info(
fmt.Sprintf(
"proposal %d (%s) tallied; result: %s",
activeProposal.ProposalID, activeProposal.GetTitle(), logMsg,
proposal.ProposalID, proposal.GetTitle(), logMsg,
),
)

resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID))
resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposal.ProposalID))
resTags = resTags.AppendTag(tags.ProposalResult, tagValue)
}

return false
})

return resTags
}
2 changes: 1 addition & 1 deletion x/gov/endblocker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func TestTickPassedVotingPeriod(t *testing.T) {
proposal, ok := input.keeper.GetProposal(ctx, activeProposalID)
require.True(t, ok)
require.Equal(t, StatusVotingPeriod, proposal.Status)
depositsIterator := input.keeper.GetDeposits(ctx, proposalID)
depositsIterator := input.keeper.GetProposalDepositsIterator(ctx, proposalID)
require.True(t, depositsIterator.Valid())
depositsIterator.Close()
activeQueue.Close()
Expand Down
Loading