cosmos-sdk/docs/spec/governance/state.md

217 lines
7.6 KiB
Markdown
Raw Normal View History

2018-02-26 07:35:09 -08:00
# Implementation (1/2)
## State
2018-06-04 08:22:38 -07:00
### Procedures and base types
2018-02-26 07:35:09 -08:00
`Procedures` define the rule according to which votes are run. There can only
be one active procedure at any given time. If governance wants to change a
procedure, either to modify a value or add/remove a parameter, a new procedure
has to be created and the previous one rendered inactive.
2018-06-04 08:22:38 -07:00
```go
2018-06-07 03:02:21 -07:00
type DepositProcedure struct {
2018-06-04 08:22:38 -07:00
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
2018-06-04 08:22:38 -07:00
}
```
2018-06-07 03:02:21 -07:00
```go
type VotingProcedure struct {
VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks
2018-06-07 03:02:21 -07:00
}
```
```go
type TallyingProcedure struct {
Threshold sdk.Dec // Minimum propotion 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
GovernancePenalty sdk.Dec // Penalty if validator does not vote
2018-06-07 03:02:21 -07:00
}
```
Procedures are stored in a global `GlobalParams` KVStore.
2018-06-04 08:22:38 -07:00
2018-06-07 03:02:21 -07:00
Additionally, we introduce some basic types:
2018-06-04 08:22:38 -07:00
2018-02-26 07:35:09 -08:00
```go
2018-05-08 12:31:34 -07:00
2018-06-04 08:20:07 -07:00
type Vote byte
2018-05-08 12:31:34 -07:00
const (
2018-06-04 08:20:07 -07:00
VoteYes = 0x1
VoteNo = 0x2
VoteNoWithVeto = 0x3
VoteAbstain = 0x4
2018-05-08 12:31:34 -07:00
)
type ProposalType byte
const (
2018-06-15 07:18:50 -07:00
ProposalTypePlainText = 0x1 // Plain text proposals
ProposalTypeSoftwareUpgrade = 0x2 // Text proposal inducing a software upgrade
2018-06-04 08:20:07 -07:00
)
type ProposalStatus byte
2018-05-08 12:31:34 -07:00
2018-02-26 07:35:09 -08:00
2018-06-04 08:20:07 -07:00
const (
ProposalStatusOpen = 0x1 // Proposal is submitted. Participants can deposit on it but not vote
ProposalStatusActive = 0x2 // MinDeposit is reachhed, participants can vote
ProposalStatusAccepted = 0x3 // Proposal has been accepted
ProposalStatusRejected = 0x4 // Proposal has been rejected
2018-06-05 07:43:56 -07:00
ProposalStatusClosed. = 0x5 // Proposal never reached MinDeposit
2018-05-08 12:31:34 -07:00
)
2018-02-26 07:35:09 -08:00
```
### Deposit
```go
type Deposit struct {
2018-05-08 12:31:34 -07:00
Amount sdk.Coins // Amount of coins deposited by depositer
Depositer crypto.address // Address of depositer
}
```
2018-06-04 08:20:07 -07:00
### ValidatorGovInfo
This type is used in a temp map when tallying
```go
2018-06-04 08:20:07 -07:00
type ValidatorGovInfo struct {
Minus sdk.Dec
2018-06-04 08:20:07 -07:00
Vote Vote
}
```
2018-02-26 07:35:09 -08:00
### Proposals
2018-05-08 12:31:34 -07:00
`Proposals` are an item to be voted on.
2018-02-26 07:35:09 -08:00
```go
type Proposal struct {
Title string // Title of the proposal
Description string // Description of the proposal
2018-05-08 12:31:34 -07:00
Type ProposalType // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
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
2018-06-04 08:20:07 -07:00
Submitter sdk.Address // Address of the submitter
2018-02-26 07:35:09 -08:00
VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached
2018-06-04 08:20:07 -07:00
CurrentStatus ProposalStatus // Current status of the proposal
YesVotes sdk.Dec
NoVotes sdk.Dec
NoWithVetoVotes sdk.Dec
AbstainVotes sdk.Dec
2018-02-26 07:35:09 -08:00
}
```
2018-06-04 08:20:07 -07:00
We also mention a method to update the tally for a given proposal:
2018-02-26 07:35:09 -08:00
```go
func (proposal Proposal) updateTally(vote byte, amount sdk.Dec)
2018-02-26 07:35:09 -08:00
```
2018-02-26 08:28:57 -08:00
### Stores
2018-06-04 08:20:07 -07:00
*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:
2018-02-26 07:35:09 -08:00
2018-06-05 07:43:56 -07:00
* A mapping from `proposalID|'proposal'` to `Proposal`
* A mapping from `proposalID|'addresses'|address` to `Vote`. This mapping allows us to query all addresses that voted on the proposal along with their vote by doing a range query on `proposalID:addresses`
2018-02-26 07:35:09 -08:00
2018-02-26 08:28:57 -08:00
For pseudocode purposes, here are the two function we will use to read or write in stores:
* `load(StoreKey, Key)`: Retrieve item stored at key `Key` in store found at key `StoreKey` in the multistore
* `store(StoreKey, Key, value)`: Write value `Value` at key `Key` in store found at key `StoreKey` in the multistore
2018-02-26 07:35:09 -08:00
### Proposal Processing Queue
**Store:**
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
`ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest
element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if
`CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is,
2018-06-04 08:20:07 -07:00
then the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted
and, if not, applies `GovernancePenalty`. If the proposal is accepted, deposits are refunded.
After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated.
2018-02-26 07:35:09 -08:00
And the pseudocode for the `ProposalProcessingQueue`:
```go
2018-06-04 08:20:07 -07:00
in EndBlock do
2018-02-26 07:35:09 -08:00
checkProposal() // First call of the recursive function
// Recursive function. First call in BeginBlock
func checkProposal()
2018-05-08 12:31:34 -07:00
proposalID = ProposalProcessingQueue.Peek()
if (proposalID == nil)
2018-02-26 07:35:09 -08:00
return
2018-06-05 07:43:56 -07:00
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
2018-06-15 07:18:50 -07:00
votingProcedure = load(GlobalParams, 'VotingProcedure')
if (CurrentTime == proposal.VotingStartTime + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive)
2018-06-04 08:20:07 -07:00
// End of voting period, tally
2018-02-26 07:35:09 -08:00
2018-05-08 12:31:34 -07:00
ProposalProcessingQueue.pop()
2018-06-15 07:18:50 -07:00
validators =
Keeper.getAllValidators()
2018-06-04 08:20:07 -07:00
tmpValMap := map(sdk.Address)ValidatorGovInfo
// Initiate mapping at 0. Validators that remain at 0 at the end of tally will be punished
for each validator in validators
tmpValMap(validator).Minus = 0
2018-06-04 08:34:31 -07:00
2018-06-04 08:20:07 -07:00
// Tally
2018-06-05 07:43:56 -07:00
voterIterator = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal
2018-06-04 08:20:07 -07:00
for each (voterAddress, vote) in voterIterator
delegations = stakeKeeper.getDelegations(voterAddress) // get all delegations for current voter
for each delegation in delegations
2018-06-07 03:02:21 -07:00
// make sure delegation.Shares does NOT include shares being unbonded
2018-06-04 08:20:07 -07:00
tmpValMap(delegation.ValidatorAddr).Minus += delegation.Shares
proposal.updateTally(vote, delegation.Shares)
_, isVal = stakeKeeper.getValidator(voterAddress)
if (isVal)
2018-06-15 07:18:50 -07:00
tmpValMap(voterAddress).Vote = vote
2018-06-04 08:34:31 -07:00
2018-06-15 07:18:50 -07:00
tallyingProcedure = load(GlobalParams, 'TallyingProcedure')
2018-06-04 08:34:31 -07:00
// Update tally if validator voted they voted
2018-06-04 08:20:07 -07:00
for each validator in validators
if tmpValMap(validator).HasVoted
proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))
2018-06-04 08:20:07 -07:00
2018-06-04 08:34:31 -07:00
2018-06-04 08:20:07 -07:00
// Check if proposal is accepted or rejected
totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
2018-06-15 07:18:50 -07:00
if (proposal.Votes.YesVotes/totalNonAbstain > tallyingProcedure.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingProcedure.Veto)
// proposal was accepted at the end of the voting period
2018-05-08 12:31:34 -07:00
// refund deposits (non-voters already punished)
2018-06-04 08:20:07 -07:00
proposal.CurrentStatus = ProposalStatusAccepted
for each (amount, depositer) in proposal.Deposits
depositer.AtomBalance += amount
2018-06-04 08:20:07 -07:00
else
// proposal was rejected
proposal.CurrentStatus = ProposalStatusRejected
2018-06-05 07:43:56 -07:00
store(Governance, <proposalID|'proposal'>, proposal)
2018-06-04 08:20:07 -07:00
checkProposal()
2018-05-08 12:31:34 -07:00
```