Merge PR #4460: Gov Keys and Iterators
* refactor gov keys * iterators and renamings * invert queue key order * changelog * fix tests * update alias.go * Apply suggestions from code review Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * rename keys * rename functions * Apply suggestions from code review Co-Authored-By: Alexander Bezobchuk <alexanderbez@users.noreply.github.com> * address Aleks' comments * fix test * address Karoly's comments
This commit is contained in:
parent
3962b3ca23
commit
fe695b8f7b
1
.pending/breaking/sdk/4437-bytes-gov-keys
Normal file
1
.pending/breaking/sdk/4437-bytes-gov-keys
Normal file
@ -0,0 +1 @@
|
||||
#4437 Replace governance module store keys to use `[]byte` instead of `string`.
|
||||
1
.pending/improvements/sdk/4439-add-governance-
Normal file
1
.pending/improvements/sdk/4439-add-governance-
Normal file
@ -0,0 +1 @@
|
||||
#4439 Implement governance module iterators.
|
||||
@ -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
|
||||
ProposalKey = types.ProposalKey
|
||||
ActiveProposalByTimeKey = types.ActiveProposalByTimeKey
|
||||
ActiveProposalQueueKey = types.ActiveProposalQueueKey
|
||||
InactiveProposalByTimeKey = types.InactiveProposalByTimeKey
|
||||
InactiveProposalQueueKey = types.InactiveProposalQueueKey
|
||||
DepositsKey = types.DepositsKey
|
||||
DepositKey = types.DepositKey
|
||||
VotesKey = types.VotesKey
|
||||
VoteKey = types.VoteKey
|
||||
SplitProposalKey = types.SplitProposalKey
|
||||
SplitActiveProposalQueueKey = types.SplitActiveProposalQueueKey
|
||||
SplitInactiveProposalQueueKey = types.SplitInactiveProposalQueueKey
|
||||
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
|
||||
KeyNextProposalID = types.KeyNextProposalID
|
||||
PrefixActiveProposalQueue = types.PrefixActiveProposalQueue
|
||||
PrefixInactiveProposalQueue = types.PrefixInactiveProposalQueue
|
||||
ProposalsKeyPrefix = types.ProposalsKeyPrefix
|
||||
ActiveProposalQueuePrefix = types.ActiveProposalQueuePrefix
|
||||
InactiveProposalQueuePrefix = types.InactiveProposalQueuePrefix
|
||||
ProposalIDKey = types.ProposalIDKey
|
||||
DepositsKeyPrefix = types.DepositsKeyPrefix
|
||||
VotesKeyPrefix = types.VotesKeyPrefix
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
122
x/gov/deposit.go
Normal file
122
x/gov/deposit.go
Normal file
@ -0,0 +1,122 @@
|
||||
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.DepositKey(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.DepositKey(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
|
||||
deposit, found := keeper.GetDeposit(ctx, proposalID, depositorAddr)
|
||||
if found {
|
||||
deposit.Amount = deposit.Amount.Add(depositAmount)
|
||||
} else {
|
||||
deposit = NewDeposit(proposalID, depositorAddr, depositAmount)
|
||||
}
|
||||
|
||||
keeper.setDeposit(ctx, proposalID, depositorAddr, deposit)
|
||||
|
||||
return nil, activatedVotingPeriod
|
||||
}
|
||||
|
||||
// GetAllDeposits returns all the deposits from the store
|
||||
func (keeper Keeper) GetAllDeposits(ctx sdk.Context) (deposits Deposits) {
|
||||
keeper.IterateAllDeposits(ctx, func(deposit Deposit) bool {
|
||||
deposits = append(deposits, deposit)
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetDeposits returns all the deposits from a proposal
|
||||
func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) (deposits Deposits) {
|
||||
keeper.IterateDeposits(ctx, proposalID, func(deposit Deposit) bool {
|
||||
deposits = append(deposits, deposit)
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetDepositsIterator gets all the deposits on a specific proposal as an sdk.Iterator
|
||||
func (keeper Keeper) GetDepositsIterator(ctx sdk.Context, proposalID uint64) sdk.Iterator {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return sdk.KVStorePrefixIterator(store, types.DepositsKey(proposalID))
|
||||
}
|
||||
|
||||
// RefundDeposits refunds and deletes all the deposits on a specific proposal
|
||||
func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
|
||||
keeper.IterateDeposits(ctx, proposalID, func(deposit Deposit) bool {
|
||||
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositor, deposit.Amount)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
store.Delete(DepositKey(proposalID, deposit.Depositor))
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteDeposits deletes all the deposits on a specific proposal without refunding them
|
||||
func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
|
||||
keeper.IterateDeposits(ctx, proposalID, func(deposit Deposit) bool {
|
||||
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
store.Delete(DepositKey(proposalID, deposit.Depositor))
|
||||
return false
|
||||
})
|
||||
}
|
||||
@ -7,100 +7,85 @@ 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()
|
||||
|
||||
inactiveIterator := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time)
|
||||
defer inactiveIterator.Close()
|
||||
for ; inactiveIterator.Valid(); inactiveIterator.Next() {
|
||||
var proposalID uint64
|
||||
// delete inactive proposal from store and its deposits
|
||||
keeper.IterateInactiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal Proposal) bool {
|
||||
keeper.DeleteProposal(ctx, proposal.ProposalID)
|
||||
keeper.DeleteDeposits(ctx, proposal.ProposalID)
|
||||
|
||||
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)
|
||||
|
||||
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, ctx.BlockHeader().Time, func(proposal Proposal) bool {
|
||||
var tagValue, logMsg string
|
||||
|
||||
passes, burnDeposits, tallyResults := tally(ctx, keeper, proposal)
|
||||
|
||||
if burnDeposits {
|
||||
keeper.DeleteDeposits(ctx, activeProposal.ProposalID)
|
||||
keeper.DeleteDeposits(ctx, proposal.ProposalID)
|
||||
} else {
|
||||
keeper.RefundDeposits(ctx, activeProposal.ProposalID)
|
||||
keeper.RefundDeposits(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
|
||||
}
|
||||
|
||||
@ -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.GetDepositsIterator(ctx, proposalID)
|
||||
require.True(t, depositsIterator.Valid())
|
||||
depositsIterator.Close()
|
||||
activeQueue.Close()
|
||||
|
||||
@ -16,27 +16,16 @@ const (
|
||||
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
StartingProposalID uint64 `json:"starting_proposal_id"`
|
||||
Deposits []DepositWithMetadata `json:"deposits"`
|
||||
Votes []VoteWithMetadata `json:"votes"`
|
||||
Proposals []Proposal `json:"proposals"`
|
||||
DepositParams DepositParams `json:"deposit_params"`
|
||||
VotingParams VotingParams `json:"voting_params"`
|
||||
TallyParams TallyParams `json:"tally_params"`
|
||||
}
|
||||
|
||||
// DepositWithMetadata (just for genesis)
|
||||
type DepositWithMetadata struct {
|
||||
ProposalID uint64 `json:"proposal_id"`
|
||||
Deposit Deposit `json:"deposit"`
|
||||
}
|
||||
|
||||
// VoteWithMetadata (just for genesis)
|
||||
type VoteWithMetadata struct {
|
||||
ProposalID uint64 `json:"proposal_id"`
|
||||
Vote Vote `json:"vote"`
|
||||
StartingProposalID uint64 `json:"starting_proposal_id"`
|
||||
Deposits Deposits `json:"deposits"`
|
||||
Votes Votes `json:"votes"`
|
||||
Proposals []Proposal `json:"proposals"`
|
||||
DepositParams DepositParams `json:"deposit_params"`
|
||||
VotingParams VotingParams `json:"voting_params"`
|
||||
TallyParams TallyParams `json:"tally_params"`
|
||||
}
|
||||
|
||||
// NewGenesisState creates a new genesis state for the governance module
|
||||
func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParams, tp TallyParams) GenesisState {
|
||||
return GenesisState{
|
||||
StartingProposalID: startingProposalID,
|
||||
@ -79,7 +68,7 @@ func (data GenesisState) IsEmpty() bool {
|
||||
return data.Equal(emptyGenState)
|
||||
}
|
||||
|
||||
// ValidateGenesis
|
||||
// ValidateGenesis checks if parameters are within valid ranges
|
||||
func ValidateGenesis(data GenesisState) error {
|
||||
threshold := data.TallyParams.Threshold
|
||||
if threshold.IsNegative() || threshold.GT(sdk.OneDec()) {
|
||||
@ -103,26 +92,23 @@ func ValidateGenesis(data GenesisState) error {
|
||||
|
||||
// InitGenesis - store genesis parameters
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
|
||||
err := k.setInitialProposalID(ctx, data.StartingProposalID)
|
||||
if err != nil {
|
||||
// TODO: Handle this with #870
|
||||
panic(err)
|
||||
}
|
||||
|
||||
k.setProposalID(ctx, data.StartingProposalID)
|
||||
k.setDepositParams(ctx, data.DepositParams)
|
||||
k.setVotingParams(ctx, data.VotingParams)
|
||||
k.setTallyParams(ctx, data.TallyParams)
|
||||
for _, deposit := range data.Deposits {
|
||||
k.setDeposit(ctx, deposit.ProposalID, deposit.Deposit.Depositor, deposit.Deposit)
|
||||
k.setDeposit(ctx, deposit.ProposalID, deposit.Depositor, deposit)
|
||||
}
|
||||
for _, vote := range data.Votes {
|
||||
k.setVote(ctx, vote.ProposalID, vote.Vote.Voter, vote.Vote)
|
||||
k.setVote(ctx, vote.ProposalID, vote.Voter, vote)
|
||||
}
|
||||
for _, proposal := range data.Proposals {
|
||||
switch proposal.Status {
|
||||
case StatusDepositPeriod:
|
||||
k.InsertInactiveProposalQueue(ctx, proposal.DepositEndTime, proposal.ProposalID)
|
||||
k.InsertInactiveProposalQueue(ctx, proposal.ProposalID, proposal.DepositEndTime)
|
||||
case StatusVotingPeriod:
|
||||
k.InsertActiveProposalQueue(ctx, proposal.VotingEndTime, proposal.ProposalID)
|
||||
k.InsertActiveProposalQueue(ctx, proposal.ProposalID, proposal.VotingEndTime)
|
||||
}
|
||||
k.SetProposal(ctx, proposal)
|
||||
}
|
||||
@ -130,35 +116,27 @@ func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
|
||||
|
||||
// ExportGenesis - output genesis parameters
|
||||
func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
startingProposalID, _ := k.peekCurrentProposalID(ctx)
|
||||
startingProposalID, _ := k.GetProposalID(ctx)
|
||||
depositParams := k.GetDepositParams(ctx)
|
||||
votingParams := k.GetVotingParams(ctx)
|
||||
tallyParams := k.GetTallyParams(ctx)
|
||||
var deposits []DepositWithMetadata
|
||||
var votes []VoteWithMetadata
|
||||
|
||||
proposals := k.GetProposalsFiltered(ctx, nil, nil, StatusNil, 0)
|
||||
|
||||
var proposalsDeposits Deposits
|
||||
var proposalsVotes Votes
|
||||
for _, proposal := range proposals {
|
||||
proposalID := proposal.ProposalID
|
||||
depositsIterator := k.GetDeposits(ctx, proposalID)
|
||||
defer depositsIterator.Close()
|
||||
for ; depositsIterator.Valid(); depositsIterator.Next() {
|
||||
var deposit Deposit
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit)
|
||||
deposits = append(deposits, DepositWithMetadata{proposalID, deposit})
|
||||
}
|
||||
votesIterator := k.GetVotes(ctx, proposalID)
|
||||
defer votesIterator.Close()
|
||||
for ; votesIterator.Valid(); votesIterator.Next() {
|
||||
var vote Vote
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote)
|
||||
votes = append(votes, VoteWithMetadata{proposalID, vote})
|
||||
}
|
||||
deposits := k.GetDeposits(ctx, proposal.ProposalID)
|
||||
proposalsDeposits = append(proposalsDeposits, deposits...)
|
||||
|
||||
votes := k.GetVotes(ctx, proposal.ProposalID)
|
||||
proposalsVotes = append(proposalsVotes, votes...)
|
||||
}
|
||||
|
||||
return GenesisState{
|
||||
StartingProposalID: startingProposalID,
|
||||
Deposits: deposits,
|
||||
Votes: votes,
|
||||
Deposits: proposalsDeposits,
|
||||
Votes: proposalsVotes,
|
||||
Proposals: proposals,
|
||||
DepositParams: depositParams,
|
||||
VotingParams: votingParams,
|
||||
|
||||
510
x/gov/keeper.go
510
x/gov/keeper.go
@ -1,10 +1,12 @@
|
||||
package gov
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
@ -78,173 +80,6 @@ func NewKeeper(
|
||||
// Logger returns a module-specific logger.
|
||||
func (keeper Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/gov") }
|
||||
|
||||
// Proposals
|
||||
func (keeper Keeper) SubmitProposal(ctx sdk.Context, content Content) (Proposal, sdk.Error) {
|
||||
if !keeper.router.HasRoute(content.ProposalRoute()) {
|
||||
return Proposal{}, ErrNoProposalHandlerExists(keeper.codespace, content)
|
||||
}
|
||||
|
||||
// Execute the proposal content in a cache-wrapped context to validate the
|
||||
// actual parameter changes before the proposal proceeds through the
|
||||
// governance process. State is not persisted.
|
||||
cacheCtx, _ := ctx.CacheContext()
|
||||
handler := keeper.router.GetRoute(content.ProposalRoute())
|
||||
if err := handler(cacheCtx, content); err != nil {
|
||||
return Proposal{}, ErrInvalidProposalContent(keeper.codespace, err.Result().Log)
|
||||
}
|
||||
|
||||
proposalID, err := keeper.getNewProposalID(ctx)
|
||||
if err != nil {
|
||||
return Proposal{}, err
|
||||
}
|
||||
|
||||
submitTime := ctx.BlockHeader().Time
|
||||
depositPeriod := keeper.GetDepositParams(ctx).MaxDepositPeriod
|
||||
|
||||
proposal := NewProposal(content, proposalID, submitTime, submitTime.Add(depositPeriod))
|
||||
|
||||
keeper.SetProposal(ctx, proposal)
|
||||
keeper.InsertInactiveProposalQueue(ctx, proposal.DepositEndTime, proposalID)
|
||||
|
||||
return proposal, nil
|
||||
}
|
||||
|
||||
// Get Proposal from store by ProposalID
|
||||
func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (proposal Proposal, ok bool) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(KeyProposal(proposalID))
|
||||
if bz == nil {
|
||||
return
|
||||
}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposal)
|
||||
return proposal, true
|
||||
}
|
||||
|
||||
// Implements sdk.AccountKeeper.
|
||||
func (keeper Keeper) SetProposal(ctx sdk.Context, proposal Proposal) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal)
|
||||
store.Set(KeyProposal(proposal.ProposalID), bz)
|
||||
}
|
||||
|
||||
// Implements sdk.AccountKeeper.
|
||||
func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
proposal, ok := keeper.GetProposal(ctx, proposalID)
|
||||
if !ok {
|
||||
panic("DeleteProposal cannot fail to GetProposal")
|
||||
}
|
||||
keeper.RemoveFromInactiveProposalQueue(ctx, proposal.DepositEndTime, proposalID)
|
||||
keeper.RemoveFromActiveProposalQueue(ctx, proposal.VotingEndTime, proposalID)
|
||||
store.Delete(KeyProposal(proposalID))
|
||||
}
|
||||
|
||||
// Get Proposal from store by ProposalID
|
||||
// voterAddr will filter proposals by whether or not that address has voted on them
|
||||
// depositorAddr will filter proposals by whether or not that address has deposited to them
|
||||
// status will filter proposals by status
|
||||
// numLatest will fetch a specified number of the most recent proposals, or 0 for all proposals
|
||||
func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositorAddr sdk.AccAddress, status ProposalStatus, numLatest uint64) []Proposal {
|
||||
|
||||
maxProposalID, err := keeper.peekCurrentProposalID(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
matchingProposals := []Proposal{}
|
||||
|
||||
if numLatest == 0 {
|
||||
numLatest = maxProposalID
|
||||
}
|
||||
|
||||
for proposalID := maxProposalID - numLatest; proposalID < maxProposalID; proposalID++ {
|
||||
if voterAddr != nil && len(voterAddr) != 0 {
|
||||
_, found := keeper.GetVote(ctx, proposalID, voterAddr)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if depositorAddr != nil && len(depositorAddr) != 0 {
|
||||
_, found := keeper.GetDeposit(ctx, proposalID, depositorAddr)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
proposal, ok := keeper.GetProposal(ctx, proposalID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if ValidProposalStatus(status) {
|
||||
if proposal.Status != status {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
matchingProposals = append(matchingProposals, proposal)
|
||||
}
|
||||
return matchingProposals
|
||||
}
|
||||
|
||||
// Set the initial proposal ID
|
||||
func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID uint64) sdk.Error {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(KeyNextProposalID)
|
||||
if bz != nil {
|
||||
return ErrInvalidGenesis(keeper.codespace, "initial proposal ID already set")
|
||||
}
|
||||
bz = keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
|
||||
store.Set(KeyNextProposalID, bz)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the last used proposal ID
|
||||
func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID uint64) {
|
||||
proposalID, err := keeper.peekCurrentProposalID(ctx)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
proposalID--
|
||||
return
|
||||
}
|
||||
|
||||
// Gets the next available ProposalID and increments it
|
||||
func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID uint64, err sdk.Error) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(KeyNextProposalID)
|
||||
if bz == nil {
|
||||
return 0, ErrInvalidGenesis(keeper.codespace, "initial proposal ID never set")
|
||||
}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID)
|
||||
bz = keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID + 1)
|
||||
store.Set(KeyNextProposalID, bz)
|
||||
return proposalID, nil
|
||||
}
|
||||
|
||||
// Peeks the next available ProposalID without incrementing it
|
||||
func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID uint64, err sdk.Error) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(KeyNextProposalID)
|
||||
if bz == nil {
|
||||
return 0, ErrInvalidGenesis(keeper.codespace, "initial proposal ID never set")
|
||||
}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID)
|
||||
return proposalID, nil
|
||||
}
|
||||
|
||||
func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) {
|
||||
proposal.VotingStartTime = ctx.BlockHeader().Time
|
||||
votingPeriod := keeper.GetVotingParams(ctx).VotingPeriod
|
||||
proposal.VotingEndTime = proposal.VotingStartTime.Add(votingPeriod)
|
||||
proposal.Status = StatusVotingPeriod
|
||||
keeper.SetProposal(ctx, proposal)
|
||||
|
||||
keeper.RemoveFromInactiveProposalQueue(ctx, proposal.DepositEndTime, proposal.ProposalID)
|
||||
keeper.InsertActiveProposalQueue(ctx, proposal.VotingEndTime, proposal.ProposalID)
|
||||
}
|
||||
|
||||
// Params
|
||||
|
||||
// Returns the current DepositParams from the global param store
|
||||
@ -280,205 +115,160 @@ func (keeper Keeper) setTallyParams(ctx sdk.Context, tallyParams TallyParams) {
|
||||
keeper.paramSpace.Set(ctx, ParamStoreKeyTallyParams, &tallyParams)
|
||||
}
|
||||
|
||||
// Votes
|
||||
|
||||
// Adds a vote on a specific proposal
|
||||
func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, option VoteOption) sdk.Error {
|
||||
proposal, ok := keeper.GetProposal(ctx, proposalID)
|
||||
if !ok {
|
||||
return ErrUnknownProposal(keeper.codespace, proposalID)
|
||||
}
|
||||
if proposal.Status != StatusVotingPeriod {
|
||||
return ErrInactiveProposal(keeper.codespace, proposalID)
|
||||
}
|
||||
|
||||
if !ValidVoteOption(option) {
|
||||
return ErrInvalidVote(keeper.codespace, option)
|
||||
}
|
||||
|
||||
vote := Vote{
|
||||
ProposalID: proposalID,
|
||||
Voter: voterAddr,
|
||||
Option: option,
|
||||
}
|
||||
keeper.setVote(ctx, proposalID, voterAddr, vote)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gets the vote of a specific voter on a specific proposal
|
||||
func (keeper Keeper) GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) (Vote, bool) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(KeyVote(proposalID, voterAddr))
|
||||
if bz == nil {
|
||||
return Vote{}, false
|
||||
}
|
||||
var vote Vote
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &vote)
|
||||
return vote, true
|
||||
}
|
||||
|
||||
func (keeper Keeper) setVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, vote Vote) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote)
|
||||
store.Set(KeyVote(proposalID, voterAddr), bz)
|
||||
}
|
||||
|
||||
// Gets all the votes on a specific proposal
|
||||
func (keeper Keeper) GetVotes(ctx sdk.Context, proposalID uint64) sdk.Iterator {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return sdk.KVStorePrefixIterator(store, KeyVotesSubspace(proposalID))
|
||||
}
|
||||
|
||||
func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
store.Delete(KeyVote(proposalID, voterAddr))
|
||||
}
|
||||
|
||||
// Deposits
|
||||
|
||||
// Gets the deposit of a specific depositor on a specific proposal
|
||||
func (keeper Keeper) GetDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) (Deposit, bool) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(KeyDeposit(proposalID, depositorAddr))
|
||||
if bz == nil {
|
||||
return Deposit{}, false
|
||||
}
|
||||
var deposit Deposit
|
||||
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(KeyDeposit(proposalID, depositorAddr), bz)
|
||||
}
|
||||
|
||||
// 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 := Deposit{depositorAddr, proposalID, depositAmount}
|
||||
keeper.setDeposit(ctx, proposalID, depositorAddr, newDeposit)
|
||||
} else {
|
||||
currDeposit.Amount = currDeposit.Amount.Add(depositAmount)
|
||||
keeper.setDeposit(ctx, proposalID, depositorAddr, currDeposit)
|
||||
}
|
||||
|
||||
return nil, activatedVotingPeriod
|
||||
}
|
||||
|
||||
// Gets all the deposits on a specific proposal as an sdk.Iterator
|
||||
func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) sdk.Iterator {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return sdk.KVStorePrefixIterator(store, KeyDepositsSubspace(proposalID))
|
||||
}
|
||||
|
||||
// Refunds and deletes all the deposits on a specific proposal
|
||||
func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
depositsIterator := keeper.GetDeposits(ctx, proposalID)
|
||||
defer depositsIterator.Close()
|
||||
for ; depositsIterator.Valid(); depositsIterator.Next() {
|
||||
deposit := &Deposit{}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit)
|
||||
|
||||
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, deposit.Depositor, deposit.Amount)
|
||||
if err != nil {
|
||||
panic("should not happen")
|
||||
}
|
||||
|
||||
store.Delete(depositsIterator.Key())
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes all the deposits on a specific proposal without refunding them
|
||||
func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
depositsIterator := keeper.GetDeposits(ctx, proposalID)
|
||||
defer depositsIterator.Close()
|
||||
for ; depositsIterator.Valid(); depositsIterator.Next() {
|
||||
deposit := &Deposit{}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit)
|
||||
|
||||
// TODO: Find a way to do this without using accounts.
|
||||
err := keeper.ck.SendCoins(ctx, DepositedCoinsAccAddr, BurnedDepositCoinsAccAddr, deposit.Amount)
|
||||
if err != nil {
|
||||
panic("should not happen")
|
||||
}
|
||||
|
||||
store.Delete(depositsIterator.Key())
|
||||
}
|
||||
}
|
||||
|
||||
// ProposalQueues
|
||||
|
||||
// Returns an iterator for all the proposals in the Active Queue that expire by endTime
|
||||
// InsertActiveProposalQueue inserts a ProposalID into the active proposal queue at endTime
|
||||
func (keeper Keeper) InsertActiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
|
||||
store.Set(types.ActiveProposalQueueKey(proposalID, endTime), bz)
|
||||
}
|
||||
|
||||
// RemoveFromActiveProposalQueue removes a proposalID from the Active Proposal Queue
|
||||
func (keeper Keeper) RemoveFromActiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
store.Delete(types.ActiveProposalQueueKey(proposalID, endTime))
|
||||
}
|
||||
|
||||
// InsertInactiveProposalQueue Inserts a ProposalID into the inactive proposal queue at endTime
|
||||
func (keeper Keeper) InsertInactiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
|
||||
store.Set(types.InactiveProposalQueueKey(proposalID, endTime), bz)
|
||||
}
|
||||
|
||||
// RemoveFromInactiveProposalQueue removes a proposalID from the Inactive Proposal Queue
|
||||
func (keeper Keeper) RemoveFromInactiveProposalQueue(ctx sdk.Context, proposalID uint64, endTime time.Time) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
store.Delete(types.InactiveProposalQueueKey(proposalID, endTime))
|
||||
}
|
||||
|
||||
// Iterators
|
||||
|
||||
// IterateProposals iterates over the all the proposals and performs a callback function
|
||||
func (keeper Keeper) IterateProposals(ctx sdk.Context, cb func(proposal types.Proposal) (stop bool)) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, types.ProposalsKeyPrefix)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var proposal types.Proposal
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &proposal)
|
||||
|
||||
if cb(proposal) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterateActiveProposalsQueue iterates over the proposals in the active proposal queue
|
||||
// and performs a callback function
|
||||
func (keeper Keeper) IterateActiveProposalsQueue(ctx sdk.Context, endTime time.Time, cb func(proposal types.Proposal) (stop bool)) {
|
||||
iterator := keeper.ActiveProposalQueueIterator(ctx, endTime)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
proposalID, _ := types.SplitActiveProposalQueueKey(iterator.Key())
|
||||
proposal, found := keeper.GetProposal(ctx, proposalID)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("proposal %d does not exist", proposalID))
|
||||
}
|
||||
|
||||
if cb(proposal) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterateInactiveProposalsQueue iterates over the proposals in the inactive proposal queue
|
||||
// and performs a callback function
|
||||
func (keeper Keeper) IterateInactiveProposalsQueue(ctx sdk.Context, endTime time.Time, cb func(proposal types.Proposal) (stop bool)) {
|
||||
iterator := keeper.InactiveProposalQueueIterator(ctx, endTime)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
proposalID, _ := types.SplitInactiveProposalQueueKey(iterator.Key())
|
||||
proposal, found := keeper.GetProposal(ctx, proposalID)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("proposal %d does not exist", proposalID))
|
||||
}
|
||||
|
||||
if cb(proposal) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterateAllDeposits iterates over the all the stored deposits and performs a callback function
|
||||
func (keeper Keeper) IterateAllDeposits(ctx sdk.Context, cb func(deposit types.Deposit) (stop bool)) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, types.DepositsKeyPrefix)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var deposit types.Deposit
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit)
|
||||
|
||||
if cb(deposit) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterateDeposits iterates over the all the proposals deposits and performs a callback function
|
||||
func (keeper Keeper) IterateDeposits(ctx sdk.Context, proposalID uint64, cb func(deposit types.Deposit) (stop bool)) {
|
||||
iterator := keeper.GetDepositsIterator(ctx, proposalID)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var deposit types.Deposit
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &deposit)
|
||||
|
||||
if cb(deposit) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterateAllVotes iterates over the all the stored votes and performs a callback function
|
||||
func (keeper Keeper) IterateAllVotes(ctx sdk.Context, cb func(vote types.Vote) (stop bool)) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
iterator := sdk.KVStorePrefixIterator(store, types.VotesKeyPrefix)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var vote types.Vote
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote)
|
||||
|
||||
if cb(vote) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterateVotes iterates over the all the proposals votes and performs a callback function
|
||||
func (keeper Keeper) IterateVotes(ctx sdk.Context, proposalID uint64, cb func(vote types.Vote) (stop bool)) {
|
||||
iterator := keeper.GetVotesIterator(ctx, proposalID)
|
||||
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var vote types.Vote
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(iterator.Value(), &vote)
|
||||
|
||||
if cb(vote) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ActiveProposalQueueIterator returns an sdk.Iterator for all the proposals in the Active Queue that expire by endTime
|
||||
func (keeper Keeper) ActiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return store.Iterator(PrefixActiveProposalQueue, sdk.PrefixEndBytes(PrefixActiveProposalQueueTime(endTime)))
|
||||
return store.Iterator(ActiveProposalQueuePrefix, sdk.PrefixEndBytes(types.ActiveProposalByTimeKey(endTime)))
|
||||
}
|
||||
|
||||
// Inserts a ProposalID into the active proposal queue at endTime
|
||||
func (keeper Keeper) InsertActiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
|
||||
store.Set(KeyActiveProposalQueueProposal(endTime, proposalID), bz)
|
||||
}
|
||||
|
||||
// removes a proposalID from the Active Proposal Queue
|
||||
func (keeper Keeper) RemoveFromActiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
store.Delete(KeyActiveProposalQueueProposal(endTime, proposalID))
|
||||
}
|
||||
|
||||
// Returns an iterator for all the proposals in the Inactive Queue that expire by endTime
|
||||
// InactiveProposalQueueIterator returns an sdk.Iterator for all the proposals in the Inactive Queue that expire by endTime
|
||||
func (keeper Keeper) InactiveProposalQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return store.Iterator(PrefixInactiveProposalQueue, sdk.PrefixEndBytes(PrefixInactiveProposalQueueTime(endTime)))
|
||||
}
|
||||
|
||||
// Inserts a ProposalID into the inactive proposal queue at endTime
|
||||
func (keeper Keeper) InsertInactiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
|
||||
store.Set(KeyInactiveProposalQueueProposal(endTime, proposalID), bz)
|
||||
}
|
||||
|
||||
// removes a proposalID from the Inactive Proposal Queue
|
||||
func (keeper Keeper) RemoveFromInactiveProposalQueue(ctx sdk.Context, endTime time.Time, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
store.Delete(KeyInactiveProposalQueueProposal(endTime, proposalID))
|
||||
return store.Iterator(InactiveProposalQueuePrefix, sdk.PrefixEndBytes(types.InactiveProposalByTimeKey(endTime)))
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ func TestDeposits(t *testing.T) {
|
||||
require.True(t, proposal.VotingStartTime.Equal(ctx.BlockHeader().Time))
|
||||
|
||||
// Test deposit iterator
|
||||
depositsIterator := input.keeper.GetDeposits(ctx, proposalID)
|
||||
depositsIterator := input.keeper.GetDepositsIterator(ctx, proposalID)
|
||||
require.True(t, depositsIterator.Valid())
|
||||
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit)
|
||||
require.Equal(t, input.addrs[0], deposit.Depositor)
|
||||
@ -224,7 +224,7 @@ func TestVotes(t *testing.T) {
|
||||
require.Equal(t, OptionNoWithVeto, vote.Option)
|
||||
|
||||
// Test vote iterator
|
||||
votesIterator := input.keeper.GetVotes(ctx, proposalID)
|
||||
votesIterator := input.keeper.GetVotesIterator(ctx, proposalID)
|
||||
require.True(t, votesIterator.Valid())
|
||||
input.keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote)
|
||||
require.True(t, votesIterator.Valid())
|
||||
|
||||
155
x/gov/proposal.go
Normal file
155
x/gov/proposal.go
Normal file
@ -0,0 +1,155 @@
|
||||
package gov
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
// SubmitProposal create new proposal given a content
|
||||
func (keeper Keeper) SubmitProposal(ctx sdk.Context, content Content) (Proposal, sdk.Error) {
|
||||
if !keeper.router.HasRoute(content.ProposalRoute()) {
|
||||
return Proposal{}, ErrNoProposalHandlerExists(keeper.codespace, content)
|
||||
}
|
||||
|
||||
// Execute the proposal content in a cache-wrapped context to validate the
|
||||
// actual parameter changes before the proposal proceeds through the
|
||||
// governance process. State is not persisted.
|
||||
cacheCtx, _ := ctx.CacheContext()
|
||||
handler := keeper.router.GetRoute(content.ProposalRoute())
|
||||
if err := handler(cacheCtx, content); err != nil {
|
||||
return Proposal{}, ErrInvalidProposalContent(keeper.codespace, err.Result().Log)
|
||||
}
|
||||
|
||||
proposalID, err := keeper.GetProposalID(ctx)
|
||||
if err != nil {
|
||||
return Proposal{}, err
|
||||
}
|
||||
|
||||
submitTime := ctx.BlockHeader().Time
|
||||
depositPeriod := keeper.GetDepositParams(ctx).MaxDepositPeriod
|
||||
|
||||
proposal := NewProposal(content, proposalID, submitTime, submitTime.Add(depositPeriod))
|
||||
|
||||
keeper.SetProposal(ctx, proposal)
|
||||
keeper.InsertInactiveProposalQueue(ctx, proposalID, proposal.DepositEndTime)
|
||||
keeper.setProposalID(ctx, proposalID+1)
|
||||
|
||||
return proposal, nil
|
||||
}
|
||||
|
||||
// GetProposal get Proposal from store by ProposalID
|
||||
func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (proposal Proposal, ok bool) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(ProposalKey(proposalID))
|
||||
if bz == nil {
|
||||
return
|
||||
}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposal)
|
||||
return proposal, true
|
||||
}
|
||||
|
||||
// SetProposal set a proposal to store
|
||||
func (keeper Keeper) SetProposal(ctx sdk.Context, proposal Proposal) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposal)
|
||||
store.Set(ProposalKey(proposal.ProposalID), bz)
|
||||
}
|
||||
|
||||
// DeleteProposal deletes a proposal from store
|
||||
func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
proposal, ok := keeper.GetProposal(ctx, proposalID)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("couldn't find proposal with id#%d", proposalID))
|
||||
}
|
||||
keeper.RemoveFromInactiveProposalQueue(ctx, proposalID, proposal.DepositEndTime)
|
||||
keeper.RemoveFromActiveProposalQueue(ctx, proposalID, proposal.VotingEndTime)
|
||||
store.Delete(ProposalKey(proposalID))
|
||||
}
|
||||
|
||||
// GetProposals returns all the proposals from store
|
||||
func (keeper Keeper) GetProposals(ctx sdk.Context) (proposals Proposals) {
|
||||
keeper.IterateProposals(ctx, func(proposal types.Proposal) bool {
|
||||
proposals = append(proposals, proposal)
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetProposalsFiltered get Proposals from store by ProposalID
|
||||
// voterAddr will filter proposals by whether or not that address has voted on them
|
||||
// depositorAddr will filter proposals by whether or not that address has deposited to them
|
||||
// status will filter proposals by status
|
||||
// numLatest will fetch a specified number of the most recent proposals, or 0 for all proposals
|
||||
func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositorAddr sdk.AccAddress, status ProposalStatus, numLatest uint64) []Proposal {
|
||||
|
||||
maxProposalID, err := keeper.GetProposalID(ctx)
|
||||
if err != nil {
|
||||
return []Proposal{}
|
||||
}
|
||||
|
||||
matchingProposals := []Proposal{}
|
||||
|
||||
if numLatest == 0 {
|
||||
numLatest = maxProposalID
|
||||
}
|
||||
|
||||
for proposalID := maxProposalID - numLatest; proposalID < maxProposalID; proposalID++ {
|
||||
if voterAddr != nil && len(voterAddr) != 0 {
|
||||
_, found := keeper.GetVote(ctx, proposalID, voterAddr)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if depositorAddr != nil && len(depositorAddr) != 0 {
|
||||
_, found := keeper.GetDeposit(ctx, proposalID, depositorAddr)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
proposal, ok := keeper.GetProposal(ctx, proposalID)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if ValidProposalStatus(status) && proposal.Status != status {
|
||||
continue
|
||||
}
|
||||
|
||||
matchingProposals = append(matchingProposals, proposal)
|
||||
}
|
||||
return matchingProposals
|
||||
}
|
||||
|
||||
// GetProposalID gets the highest proposal ID
|
||||
func (keeper Keeper) GetProposalID(ctx sdk.Context) (proposalID uint64, err sdk.Error) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(ProposalIDKey)
|
||||
if bz == nil {
|
||||
return 0, ErrInvalidGenesis(keeper.codespace, "initial proposal ID hasn't been set")
|
||||
}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &proposalID)
|
||||
return proposalID, nil
|
||||
}
|
||||
|
||||
// Set the proposal ID
|
||||
func (keeper Keeper) setProposalID(ctx sdk.Context, proposalID uint64) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(proposalID)
|
||||
store.Set(ProposalIDKey, bz)
|
||||
}
|
||||
|
||||
func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) {
|
||||
proposal.VotingStartTime = ctx.BlockHeader().Time
|
||||
votingPeriod := keeper.GetVotingParams(ctx).VotingPeriod
|
||||
proposal.VotingEndTime = proposal.VotingStartTime.Add(votingPeriod)
|
||||
proposal.Status = StatusVotingPeriod
|
||||
keeper.SetProposal(ctx, proposal)
|
||||
|
||||
keeper.RemoveFromInactiveProposalQueue(ctx, proposal.ProposalID, proposal.DepositEndTime)
|
||||
keeper.InsertActiveProposalQueue(ctx, proposal.ProposalID, proposal.VotingEndTime)
|
||||
}
|
||||
@ -179,14 +179,7 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper
|
||||
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
|
||||
}
|
||||
|
||||
var deposits []Deposit
|
||||
depositsIterator := keeper.GetDeposits(ctx, params.ProposalID)
|
||||
defer depositsIterator.Close()
|
||||
for ; depositsIterator.Valid(); depositsIterator.Next() {
|
||||
deposit := Deposit{}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit)
|
||||
deposits = append(deposits, deposit)
|
||||
}
|
||||
deposits := keeper.GetDeposits(ctx, params.ProposalID)
|
||||
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, deposits)
|
||||
if err != nil {
|
||||
@ -237,14 +230,7 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
|
||||
return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error()))
|
||||
}
|
||||
|
||||
var votes []Vote
|
||||
votesIterator := keeper.GetVotes(ctx, params.ProposalID)
|
||||
defer votesIterator.Close()
|
||||
for ; votesIterator.Valid(); votesIterator.Next() {
|
||||
vote := Vote{}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote)
|
||||
votes = append(votes, vote)
|
||||
}
|
||||
votes := keeper.GetVotes(ctx, params.ProposalID)
|
||||
|
||||
bz, err := codec.MarshalJSONIndent(keeper.cdc, votes)
|
||||
if err != nil {
|
||||
|
||||
@ -64,7 +64,12 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, contentSim Con
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
|
||||
proposalID := k.GetLastProposalID(ctx)
|
||||
proposalID, err := k.GetProposalID(ctx)
|
||||
if err != nil {
|
||||
return simulation.NoOpMsg(), nil, err
|
||||
}
|
||||
|
||||
proposalID = uint64(math.Max(float64(proposalID)-1, 0))
|
||||
|
||||
// 2) Schedule operations for votes
|
||||
// 2.1) first pick a number of people to vote.
|
||||
@ -192,7 +197,9 @@ func randomDeposit(r *rand.Rand) sdk.Coins {
|
||||
|
||||
// Pick a random proposal ID
|
||||
func randomProposalID(r *rand.Rand, k gov.Keeper, ctx sdk.Context) (proposalID uint64, ok bool) {
|
||||
lastProposalID := k.GetLastProposalID(ctx)
|
||||
lastProposalID, _ := k.GetProposalID(ctx)
|
||||
lastProposalID = uint64(math.Max(float64(lastProposalID)-1, 0))
|
||||
|
||||
if lastProposalID < 1 || lastProposalID == (2<<63-1) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package gov
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
// validatorGovInfo used for tallying
|
||||
@ -49,13 +50,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, burn
|
||||
return false
|
||||
})
|
||||
|
||||
// iterate over all the votes
|
||||
votesIterator := keeper.GetVotes(ctx, proposal.ProposalID)
|
||||
defer votesIterator.Close()
|
||||
for ; votesIterator.Valid(); votesIterator.Next() {
|
||||
vote := &Vote{}
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), vote)
|
||||
|
||||
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()
|
||||
@ -83,7 +78,8 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, burn
|
||||
}
|
||||
|
||||
keeper.deleteVote(ctx, vote.ProposalID, vote.Voter)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// iterate over the validators again to tally their voting power
|
||||
for _, val := range currValidators {
|
||||
|
||||
@ -8,11 +8,16 @@ import (
|
||||
|
||||
// Deposit
|
||||
type Deposit struct {
|
||||
Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor
|
||||
ProposalID uint64 `json:"proposal_id"` // proposalID of the proposal
|
||||
Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor
|
||||
Amount sdk.Coins `json:"amount"` // Deposit amount
|
||||
}
|
||||
|
||||
// NewDeposit creates a new Deposit instance
|
||||
func NewDeposit(proposalID uint64, depositor sdk.AccAddress, amount sdk.Coins) Deposit {
|
||||
return Deposit{proposalID, depositor, amount}
|
||||
}
|
||||
|
||||
func (d Deposit) String() string {
|
||||
return fmt.Sprintf("deposit by %s on Proposal %d is for the amount %s",
|
||||
d.Depositor, d.ProposalID, d.Amount)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// ModuleKey is the name of the module
|
||||
// ModuleName is the name of the module
|
||||
ModuleName = "gov"
|
||||
|
||||
// StoreKey is the store key string for gov
|
||||
@ -21,74 +21,146 @@ const (
|
||||
// QuerierRoute is the querier route for gov
|
||||
QuerierRoute = ModuleName
|
||||
|
||||
// Parameter store default namestore
|
||||
// DefaultParamspace default name for parameter store
|
||||
DefaultParamspace = ModuleName
|
||||
)
|
||||
|
||||
// Key for getting a the next available proposalID from the store
|
||||
// Keys for governance store
|
||||
// Items are stored with the following key: values
|
||||
//
|
||||
// - 0x00<proposalID_Bytes>: Proposal
|
||||
//
|
||||
// - 0x01<endTime_Bytes><proposalID_Bytes>: activeProposalID
|
||||
//
|
||||
// - 0x02<endTime_Bytes><proposalID_Bytes>: inactiveProposalID
|
||||
//
|
||||
// - 0x03: nextProposalID
|
||||
//
|
||||
// - 0x10<proposalID_Bytes><depositorAddr_Bytes>: Deposit
|
||||
//
|
||||
// - 0x20<proposalID_Bytes><voterAddr_Bytes>: Voter
|
||||
var (
|
||||
KeyDelimiter = []byte(":")
|
||||
ProposalsKeyPrefix = []byte{0x00}
|
||||
ActiveProposalQueuePrefix = []byte{0x01}
|
||||
InactiveProposalQueuePrefix = []byte{0x02}
|
||||
ProposalIDKey = []byte{0x03}
|
||||
|
||||
KeyNextProposalID = []byte("newProposalID")
|
||||
PrefixActiveProposalQueue = []byte("activeProposalQueue")
|
||||
PrefixInactiveProposalQueue = []byte("inactiveProposalQueue")
|
||||
DepositsKeyPrefix = []byte{0x10}
|
||||
|
||||
VotesKeyPrefix = []byte{0x20}
|
||||
)
|
||||
|
||||
// Key for getting a specific proposal from the store
|
||||
func KeyProposal(proposalID uint64) []byte {
|
||||
return []byte(fmt.Sprintf("proposals:%d", proposalID))
|
||||
var lenTime = len(sdk.FormatTimeBytes(time.Now()))
|
||||
|
||||
// ProposalKey gets a specific proposal from the store
|
||||
func ProposalKey(proposalID uint64) []byte {
|
||||
bz := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bz, proposalID)
|
||||
return append(ProposalsKeyPrefix, bz...)
|
||||
}
|
||||
|
||||
// Key for getting a specific deposit from the store
|
||||
func KeyDeposit(proposalID uint64, depositorAddr sdk.AccAddress) []byte {
|
||||
return []byte(fmt.Sprintf("deposits:%d:%d", proposalID, depositorAddr))
|
||||
// ActiveProposalByTimeKey gets the active proposal queue key by endTime
|
||||
func ActiveProposalByTimeKey(endTime time.Time) []byte {
|
||||
return append(ActiveProposalQueuePrefix, sdk.FormatTimeBytes(endTime)...)
|
||||
}
|
||||
|
||||
// Key for getting a specific vote from the store
|
||||
func KeyVote(proposalID uint64, voterAddr sdk.AccAddress) []byte {
|
||||
return []byte(fmt.Sprintf("votes:%d:%d", proposalID, voterAddr))
|
||||
// ActiveProposalQueueKey returns the key for a proposalID in the activeProposalQueue
|
||||
func ActiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte {
|
||||
bz := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bz, proposalID)
|
||||
|
||||
return append(ActiveProposalByTimeKey(endTime), bz...)
|
||||
}
|
||||
|
||||
// Key for getting all deposits on a proposal from the store
|
||||
func KeyDepositsSubspace(proposalID uint64) []byte {
|
||||
return []byte(fmt.Sprintf("deposits:%d:", proposalID))
|
||||
// InactiveProposalByTimeKey gets the inactive proposal queue key by endTime
|
||||
func InactiveProposalByTimeKey(endTime time.Time) []byte {
|
||||
return append(InactiveProposalQueuePrefix, sdk.FormatTimeBytes(endTime)...)
|
||||
}
|
||||
|
||||
// Key for getting all votes on a proposal from the store
|
||||
func KeyVotesSubspace(proposalID uint64) []byte {
|
||||
return []byte(fmt.Sprintf("votes:%d:", proposalID))
|
||||
// InactiveProposalQueueKey returns the key for a proposalID in the inactiveProposalQueue
|
||||
func InactiveProposalQueueKey(proposalID uint64, endTime time.Time) []byte {
|
||||
bz := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bz, proposalID)
|
||||
|
||||
return append(InactiveProposalByTimeKey(endTime), bz...)
|
||||
}
|
||||
|
||||
// Returns the key for a proposalID in the activeProposalQueue
|
||||
func PrefixActiveProposalQueueTime(endTime time.Time) []byte {
|
||||
return bytes.Join([][]byte{
|
||||
PrefixActiveProposalQueue,
|
||||
sdk.FormatTimeBytes(endTime),
|
||||
}, KeyDelimiter)
|
||||
// DepositsKey gets the first part of the deposits key based on the proposalID
|
||||
func DepositsKey(proposalID uint64) []byte {
|
||||
bz := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bz, proposalID)
|
||||
return append(DepositsKeyPrefix, bz...)
|
||||
}
|
||||
|
||||
// Returns the key for a proposalID in the activeProposalQueue
|
||||
func KeyActiveProposalQueueProposal(endTime time.Time, proposalID uint64) []byte {
|
||||
return bytes.Join([][]byte{
|
||||
PrefixActiveProposalQueue,
|
||||
sdk.FormatTimeBytes(endTime),
|
||||
sdk.Uint64ToBigEndian(proposalID),
|
||||
}, KeyDelimiter)
|
||||
// DepositKey key of a specific deposit from the store
|
||||
func DepositKey(proposalID uint64, depositorAddr sdk.AccAddress) []byte {
|
||||
return append(DepositsKey(proposalID), depositorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// Returns the key for a proposalID in the activeProposalQueue
|
||||
func PrefixInactiveProposalQueueTime(endTime time.Time) []byte {
|
||||
return bytes.Join([][]byte{
|
||||
PrefixInactiveProposalQueue,
|
||||
sdk.FormatTimeBytes(endTime),
|
||||
}, KeyDelimiter)
|
||||
// VotesKey gets the first part of the votes key based on the proposalID
|
||||
func VotesKey(proposalID uint64) []byte {
|
||||
bz := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bz, proposalID)
|
||||
return append(VotesKeyPrefix, bz...)
|
||||
}
|
||||
|
||||
// Returns the key for a proposalID in the activeProposalQueue
|
||||
func KeyInactiveProposalQueueProposal(endTime time.Time, proposalID uint64) []byte {
|
||||
return bytes.Join([][]byte{
|
||||
PrefixInactiveProposalQueue,
|
||||
sdk.FormatTimeBytes(endTime),
|
||||
sdk.Uint64ToBigEndian(proposalID),
|
||||
}, KeyDelimiter)
|
||||
// VoteKey key of a specific vote from the store
|
||||
func VoteKey(proposalID uint64, voterAddr sdk.AccAddress) []byte {
|
||||
return append(VotesKey(proposalID), voterAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// Split keys function; used for iterators
|
||||
|
||||
// SplitProposalKey split the proposal key and returns the proposal id
|
||||
func SplitProposalKey(key []byte) (proposalID uint64) {
|
||||
if len(key[1:]) != 8 {
|
||||
panic(fmt.Sprintf("unexpected key length (%d ≠ 8)", len(key[1:])))
|
||||
}
|
||||
|
||||
return binary.LittleEndian.Uint64(key[1:])
|
||||
}
|
||||
|
||||
// SplitActiveProposalQueueKey split the active proposal key and returns the proposal id and endTime
|
||||
func SplitActiveProposalQueueKey(key []byte) (proposalID uint64, endTime time.Time) {
|
||||
return splitKeyWithTime(key)
|
||||
}
|
||||
|
||||
// SplitInactiveProposalQueueKey split the inactive proposal key and returns the proposal id and endTime
|
||||
func SplitInactiveProposalQueueKey(key []byte) (proposalID uint64, endTime time.Time) {
|
||||
return splitKeyWithTime(key)
|
||||
}
|
||||
|
||||
// SplitKeyDeposit split the deposits key and returns the proposal id and depositor address
|
||||
func SplitKeyDeposit(key []byte) (proposalID uint64, depositorAddr sdk.AccAddress) {
|
||||
return splitKeyWithAddress(key)
|
||||
}
|
||||
|
||||
// SplitKeyVote split the votes key and returns the proposal id and voter address
|
||||
func SplitKeyVote(key []byte) (proposalID uint64, voterAddr sdk.AccAddress) {
|
||||
return splitKeyWithAddress(key)
|
||||
}
|
||||
|
||||
// private functions
|
||||
|
||||
func splitKeyWithTime(key []byte) (proposalID uint64, endTime time.Time) {
|
||||
if len(key[1:]) != 8+lenTime {
|
||||
panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key[1:]), lenTime+8))
|
||||
}
|
||||
|
||||
endTime, err := sdk.ParseTimeBytes(key[1 : 1+lenTime])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
proposalID = binary.LittleEndian.Uint64(key[1+lenTime:])
|
||||
return
|
||||
}
|
||||
|
||||
func splitKeyWithAddress(key []byte) (proposalID uint64, addr sdk.AccAddress) {
|
||||
if len(key[1:]) != 8+sdk.AddrLen {
|
||||
panic(fmt.Sprintf("unexpected key length (%d ≠ %d)", len(key), 8+sdk.AddrLen))
|
||||
}
|
||||
|
||||
proposalID = binary.LittleEndian.Uint64(key[1:9])
|
||||
addr = sdk.AccAddress(key[9:])
|
||||
return
|
||||
}
|
||||
|
||||
70
x/gov/types/keys_test.go
Normal file
70
x/gov/types/keys_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
)
|
||||
|
||||
var addr = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address())
|
||||
|
||||
func TestProposalKeys(t *testing.T) {
|
||||
// key proposal
|
||||
key := ProposalKey(1)
|
||||
proposalID := SplitProposalKey(key)
|
||||
require.Equal(t, int(proposalID), 1)
|
||||
|
||||
// key active proposal queue
|
||||
now := time.Now()
|
||||
key = ActiveProposalQueueKey(3, now)
|
||||
proposalID, expTime := SplitActiveProposalQueueKey(key)
|
||||
require.Equal(t, int(proposalID), 3)
|
||||
require.True(t, now.Equal(expTime))
|
||||
|
||||
// key inactive proposal queue
|
||||
key = InactiveProposalQueueKey(3, now)
|
||||
proposalID, expTime = SplitInactiveProposalQueueKey(key)
|
||||
require.Equal(t, int(proposalID), 3)
|
||||
require.True(t, now.Equal(expTime))
|
||||
|
||||
// invalid key
|
||||
require.Panics(t, func() { SplitProposalKey([]byte("test")) })
|
||||
require.Panics(t, func() { SplitInactiveProposalQueueKey([]byte("test")) })
|
||||
}
|
||||
|
||||
func TestDepositKeys(t *testing.T) {
|
||||
|
||||
key := DepositsKey(2)
|
||||
proposalID := SplitProposalKey(key)
|
||||
require.Equal(t, int(proposalID), 2)
|
||||
|
||||
key = DepositKey(2, addr)
|
||||
proposalID, depositorAddr := SplitKeyDeposit(key)
|
||||
require.Equal(t, int(proposalID), 2)
|
||||
require.Equal(t, addr, depositorAddr)
|
||||
|
||||
// invalid key
|
||||
addr2 := sdk.AccAddress("test1")
|
||||
key = DepositKey(5, addr2)
|
||||
require.Panics(t, func() { SplitKeyDeposit(key) })
|
||||
}
|
||||
|
||||
func TestVoteKeys(t *testing.T) {
|
||||
|
||||
key := VotesKey(2)
|
||||
proposalID := SplitProposalKey(key)
|
||||
require.Equal(t, int(proposalID), 2)
|
||||
|
||||
key = VoteKey(2, addr)
|
||||
proposalID, voterAddr := SplitKeyDeposit(key)
|
||||
require.Equal(t, int(proposalID), 2)
|
||||
require.Equal(t, addr, voterAddr)
|
||||
|
||||
// invalid key
|
||||
addr2 := sdk.AccAddress("test1")
|
||||
key = VoteKey(5, addr2)
|
||||
require.Panics(t, func() { SplitKeyVote(key) })
|
||||
}
|
||||
@ -9,11 +9,16 @@ import (
|
||||
|
||||
// Vote
|
||||
type Vote struct {
|
||||
Voter sdk.AccAddress `json:"voter"` // address of the voter
|
||||
ProposalID uint64 `json:"proposal_id"` // proposalID of the proposal
|
||||
Voter sdk.AccAddress `json:"voter"` // address of the voter
|
||||
Option VoteOption `json:"option"` // option from OptionSet chosen by the voter
|
||||
}
|
||||
|
||||
// NewVote creates a new Vote instance
|
||||
func NewVote(proposalID uint64, voter sdk.AccAddress, option VoteOption) Vote {
|
||||
return Vote{proposalID, voter, option}
|
||||
}
|
||||
|
||||
func (v Vote) String() string {
|
||||
return fmt.Sprintf("voter %s voted with option %s on proposal %d", v.Voter, v.Option, v.ProposalID)
|
||||
}
|
||||
|
||||
73
x/gov/vote.go
Normal file
73
x/gov/vote.go
Normal file
@ -0,0 +1,73 @@
|
||||
package gov
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov/types"
|
||||
)
|
||||
|
||||
// AddVote Adds a vote on a specific proposal
|
||||
func (keeper Keeper) AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, option VoteOption) sdk.Error {
|
||||
proposal, ok := keeper.GetProposal(ctx, proposalID)
|
||||
if !ok {
|
||||
return ErrUnknownProposal(keeper.codespace, proposalID)
|
||||
}
|
||||
if proposal.Status != StatusVotingPeriod {
|
||||
return ErrInactiveProposal(keeper.codespace, proposalID)
|
||||
}
|
||||
|
||||
if !ValidVoteOption(option) {
|
||||
return ErrInvalidVote(keeper.codespace, option)
|
||||
}
|
||||
|
||||
vote := NewVote(proposalID, voterAddr, option)
|
||||
keeper.setVote(ctx, proposalID, voterAddr, vote)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAllVotes returns all the votes from the store
|
||||
func (keeper Keeper) GetAllVotes(ctx sdk.Context) (votes Votes) {
|
||||
keeper.IterateAllVotes(ctx, func(vote Vote) bool {
|
||||
votes = append(votes, vote)
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetVotes returns all the votes from a proposal
|
||||
func (keeper Keeper) GetVotes(ctx sdk.Context, proposalID uint64) (votes Votes) {
|
||||
keeper.IterateVotes(ctx, proposalID, func(vote Vote) bool {
|
||||
votes = append(votes, vote)
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// GetVote gets the vote from an address on a specific proposal
|
||||
func (keeper Keeper) GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) (vote Vote, found bool) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := store.Get(types.VoteKey(proposalID, voterAddr))
|
||||
if bz == nil {
|
||||
return vote, false
|
||||
}
|
||||
|
||||
keeper.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &vote)
|
||||
return vote, true
|
||||
}
|
||||
|
||||
func (keeper Keeper) setVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, vote Vote) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
bz := keeper.cdc.MustMarshalBinaryLengthPrefixed(vote)
|
||||
store.Set(types.VoteKey(proposalID, voterAddr), bz)
|
||||
}
|
||||
|
||||
// GetVotesIterator gets all the votes on a specific proposal as an sdk.Iterator
|
||||
func (keeper Keeper) GetVotesIterator(ctx sdk.Context, proposalID uint64) sdk.Iterator {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
return sdk.KVStorePrefixIterator(store, types.VotesKey(proposalID))
|
||||
}
|
||||
|
||||
func (keeper Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) {
|
||||
store := ctx.KVStore(keeper.storeKey)
|
||||
store.Delete(types.VoteKey(proposalID, voterAddr))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user