7.4 KiB
State
Parameters and base types
Parameters
define the rules according to which votes are run. There can only
be one active parameter set at any given time. If governance wants to change a
parameter set, either to modify a value or add/remove a parameter field, a new
parameter set has to be created and the previous one rendered inactive.
type DepositParams struct {
MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period.
MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
}
type VotingParams struct {
VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks
}
type TallyParams struct {
Quorum sdk.Dec // Minimum percentage of stake that needs to vote for a proposal to be considered valid
Threshold sdk.Dec // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5
Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
}
Parameters are stored in a global GlobalParams
KVStore.
Additionally, we introduce some basic types:
type Vote byte
const (
VoteYes = 0x1
VoteNo = 0x2
VoteNoWithVeto = 0x3
VoteAbstain = 0x4
)
type ProposalType byte
const (
ProposalTypePlainText = 0x1 // Plain text proposals
ProposalTypeSoftwareUpgrade = 0x2 // Text proposal inducing a software upgrade
)
type ProposalStatus byte
const (
ProposalStatusOpen = 0x1 // Proposal is submitted. Participants can deposit on it but not vote
ProposalStatusActive = 0x2 // MinDeposit is reached, participants can vote
ProposalStatusAccepted = 0x3 // Proposal has been accepted
ProposalStatusRejected = 0x4 // Proposal has been rejected
ProposalStatusClosed = 0x5 // Proposal never reached MinDeposit
)
Deposit
type Deposit struct {
Amount sdk.Coins // Amount of coins deposited by depositor
Depositor crypto.address // Address of depositor
}
ValidatorGovInfo
This type is used in a temp map when tallying
type ValidatorGovInfo struct {
Minus sdk.Dec
Vote Vote
}
Proposals
Proposals
are an item to be voted on. It contains the ProposalContent
which denotes what this proposal is about, and the other fields, which are the mutable state of the governance process.
type Proposal struct {
ProposalContent // Proposal content interface
TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit
Deposits []Deposit // List of deposits on the proposal
SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included
DepositEndTime time.Time // Time that the DepositPeriod of a proposal would expire
Submitter sdk.AccAddress // Address of the submitter
VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached
VotingEndTime time.Time // Time of the block that the VotingPeriod for a proposal will end.
CurrentStatus ProposalStatus // Current status of the proposal
YesVotes sdk.Dec
NoVotes sdk.Dec
NoWithVetoVotes sdk.Dec
AbstainVotes sdk.Dec
}
ProposalContent
s are an interface which contains the information about the Proposal
where it is provided from an external source, including the proposer. Governance process itself does not evaluate about the internal content.
type ProposalContent interface {
GetTitle() string
GetDescription() string
ProposalType() ProposalKind
}
We also mention a method to update the tally for a given proposal:
func (proposal Proposal) updateTally(vote byte, amount sdk.Dec)
Stores
Stores are KVStores in the multistore. The key to find the store is the first parameter in the list`
We will use one KVStore Governance
to store two mappings:
- A mapping from
proposalID|'proposal'
toProposal
- A mapping from
proposalID|'addresses'|address
toVote
. This mapping allows us to query all addresses that voted on the proposal along with their vote by doing a range query onproposalID:addresses
For pseudocode purposes, here are the two function we will use to read or write in stores:
load(StoreKey, Key)
: Retrieve item stored at keyKey
in store found at keyStoreKey
in the multistorestore(StoreKey, Key, value)
: Write valueValue
at keyKey
in store found at keyStoreKey
in the multistore
Proposal Processing Queue
Store:
ProposalProcessingQueue
: A queuequeue[proposalID]
containing all theProposalIDs
of proposals that reachedMinDeposit
. EachEndBlock
, all the proposals that have reached the end of their voting period are processed. To process a finished proposal, the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted. If the proposal is accepted, deposits are refunded.
And the pseudocode for the ProposalProcessingQueue
:
in EndBlock do
for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
validators = Keeper.getAllValidators()
tmpValMap := map(sdk.AccAddress)ValidatorGovInfo
// Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes
for each validator in validators
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
delegations = stakingKeeper.getDelegations(voterAddress) // get all delegations for current voter
for each delegation in delegations
// make sure delegation.Shares does NOT include shares being unbonded
tmpValMap(delegation.ValidatorAddr).Minus += delegation.Shares
proposal.updateTally(vote, delegation.Shares)
_, isVal = stakingKeeper.getValidator(voterAddress)
if (isVal)
tmpValMap(voterAddress).Vote = vote
tallyingParam = load(GlobalParams, 'TallyingParam')
// Update tally if validator voted they voted
for each validator in validators
if tmpValMap(validator).HasVoted
proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))
// Check if proposal is accepted or rejected
totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
if (proposal.Votes.YesVotes/totalNonAbstain > tallyingParam.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingParam.Veto)
// proposal was accepted at the end of the voting period
// refund deposits (non-voters already punished)
proposal.CurrentStatus = ProposalStatusAccepted
for each (amount, depositor) in proposal.Deposits
depositor.AtomBalance += amount
else
// proposal was rejected
proposal.CurrentStatus = ProposalStatusRejected
store(Governance, <proposalID|'proposal'>, proposal)