committed by
Jack Zampolin
parent
d184121ea0
commit
d1614ebb7e
@@ -2,7 +2,7 @@
|
||||
|
||||
## Abstract
|
||||
|
||||
This paper specifies the Governance module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016.
|
||||
This paper specifies the Governance module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016.
|
||||
|
||||
The module enables Cosmos-SDK based blockchain to support an on-chain governance system. In this system, holders of the native staking token of the chain can vote on proposals on a 1 token 1 vote basis. Next is a list of features the module currently supports:
|
||||
|
||||
@@ -24,7 +24,7 @@ The following specification uses *Atom* as the native staking token. The module
|
||||
1. **[Design overview](overview.md)**
|
||||
2. **Implementation**
|
||||
1. **[State](state.md)**
|
||||
1. Procedures
|
||||
1. Parameters
|
||||
2. Proposals
|
||||
3. Proposal Processing Queue
|
||||
2. **[Transactions](transactions.md)**
|
||||
|
||||
@@ -2,36 +2,35 @@
|
||||
|
||||
## State
|
||||
|
||||
### Procedures and base types
|
||||
|
||||
`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.
|
||||
### 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.
|
||||
|
||||
```go
|
||||
type DepositProcedure 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 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
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type VotingProcedure struct {
|
||||
VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks
|
||||
type VotingParams struct {
|
||||
VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks
|
||||
}
|
||||
```
|
||||
|
||||
```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
|
||||
type TallyParams struct {
|
||||
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
|
||||
GovernancePenalty sdk.Dec // Penalty if validator does not vote
|
||||
}
|
||||
```
|
||||
|
||||
Procedures are stored in a global `GlobalParams` KVStore.
|
||||
Parameters are stored in a global `GlobalParams` KVStore.
|
||||
|
||||
Additionally, we introduce some basic types:
|
||||
|
||||
@@ -61,7 +60,7 @@ const (
|
||||
ProposalStatusActive = 0x2 // MinDeposit is reachhed, participants can vote
|
||||
ProposalStatusAccepted = 0x3 // Proposal has been accepted
|
||||
ProposalStatusRejected = 0x4 // Proposal has been rejected
|
||||
ProposalStatusClosed. = 0x5 // Proposal never reached MinDeposit
|
||||
ProposalStatusClosed = 0x5 // Proposal never reached MinDeposit
|
||||
)
|
||||
```
|
||||
|
||||
@@ -76,7 +75,7 @@ const (
|
||||
|
||||
### ValidatorGovInfo
|
||||
|
||||
This type is used in a temp map when tallying
|
||||
This type is used in a temp map when tallying
|
||||
|
||||
```go
|
||||
type ValidatorGovInfo struct {
|
||||
@@ -87,7 +86,7 @@ This type is used in a temp map when tallying
|
||||
|
||||
### Proposals
|
||||
|
||||
`Proposals` are an item to be voted on.
|
||||
`Proposals` are an item to be voted on.
|
||||
|
||||
```go
|
||||
type Proposal struct {
|
||||
@@ -99,7 +98,7 @@ type Proposal struct {
|
||||
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
|
||||
@@ -135,7 +134,7 @@ For pseudocode purposes, here are the two function we will use to read or write
|
||||
### Proposal Processing Queue
|
||||
|
||||
**Store:**
|
||||
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
|
||||
* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the
|
||||
`ProposalIDs` of proposals that reached `MinDeposit`. Each `EndBlock`, 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
|
||||
@@ -145,8 +144,8 @@ For pseudocode purposes, here are the two function we will use to read or write
|
||||
And the pseudocode for the `ProposalProcessingQueue`:
|
||||
|
||||
```go
|
||||
in EndBlock do
|
||||
|
||||
in EndBlock do
|
||||
|
||||
for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
|
||||
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
|
||||
|
||||
@@ -171,7 +170,7 @@ And the pseudocode for the `ProposalProcessingQueue`:
|
||||
if (isVal)
|
||||
tmpValMap(voterAddress).Vote = vote
|
||||
|
||||
tallyingProcedure = load(GlobalParams, 'TallyingProcedure')
|
||||
tallyingParam = load(GlobalParams, 'TallyingParam')
|
||||
|
||||
// Update tally if validator voted they voted
|
||||
for each validator in validators
|
||||
@@ -182,14 +181,14 @@ And the pseudocode for the `ProposalProcessingQueue`:
|
||||
|
||||
// Check if proposal is accepted or rejected
|
||||
totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
|
||||
if (proposal.Votes.YesVotes/totalNonAbstain > tallyingProcedure.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingProcedure.Veto)
|
||||
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, depositer) in proposal.Deposits
|
||||
depositer.AtomBalance += amount
|
||||
|
||||
else
|
||||
else
|
||||
// proposal was rejected
|
||||
proposal.CurrentStatus = ProposalStatusRejected
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
### Proposal Submission
|
||||
|
||||
Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal`
|
||||
Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal`
|
||||
transaction.
|
||||
|
||||
```go
|
||||
@@ -24,7 +24,7 @@ type TxGovSubmitProposal struct {
|
||||
* If `MinDeposit` is reached:
|
||||
* Push `proposalID` in `ProposalProcessingQueue`
|
||||
|
||||
A `TxGovSubmitProposal` transaction can be handled according to the following
|
||||
A `TxGovSubmitProposal` transaction can be handled according to the following
|
||||
pseudocode.
|
||||
|
||||
```go
|
||||
@@ -32,31 +32,31 @@ pseudocode.
|
||||
// Check if TxGovSubmitProposal is valid. If it is, create proposal //
|
||||
|
||||
upon receiving txGovSubmitProposal from sender do
|
||||
|
||||
if !correctlyFormatted(txGovSubmitProposal)
|
||||
|
||||
if !correctlyFormatted(txGovSubmitProposal)
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
throw
|
||||
|
||||
initialDeposit = txGovSubmitProposal.InitialDeposit
|
||||
if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms)
|
||||
|
||||
initialDeposit = txGovSubmitProposal.InitialDeposit
|
||||
if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms)
|
||||
// InitialDeposit is negative or null OR sender has insufficient funds
|
||||
throw
|
||||
|
||||
if (txGovSubmitProposal.Type != ProposalTypePlainText) OR (txGovSubmitProposal.Type != ProposalTypeSoftwareUpgrade)
|
||||
|
||||
|
||||
sender.AtomBalance -= initialDeposit.Atoms
|
||||
|
||||
depositProcedure = load(GlobalParams, 'DepositProcedure')
|
||||
|
||||
depositParam = load(GlobalParams, 'DepositParam')
|
||||
|
||||
proposalID = generate new proposalID
|
||||
proposal = NewProposal()
|
||||
|
||||
|
||||
proposal.Title = txGovSubmitProposal.Title
|
||||
proposal.Description = txGovSubmitProposal.Description
|
||||
proposal.Type = txGovSubmitProposal.Type
|
||||
proposal.TotalDeposit = initialDeposit
|
||||
proposal.SubmitTime = <CurrentTime>
|
||||
proposal.DepositEndTime = <CurrentTime>.Add(depositProcedure.MaxDepositPeriod)
|
||||
proposal.DepositEndTime = <CurrentTime>.Add(depositParam.MaxDepositPeriod)
|
||||
proposal.Deposits.append({initialDeposit, sender})
|
||||
proposal.Submitter = sender
|
||||
proposal.YesVotes = 0
|
||||
@@ -64,15 +64,15 @@ upon receiving txGovSubmitProposal from sender do
|
||||
proposal.NoWithVetoVotes = 0
|
||||
proposal.AbstainVotes = 0
|
||||
proposal.CurrentStatus = ProposalStatusOpen
|
||||
|
||||
|
||||
store(Proposals, <proposalID|'proposal'>, proposal) // Store proposal in Proposals mapping
|
||||
return proposalID
|
||||
```
|
||||
|
||||
### Deposit
|
||||
|
||||
Once a proposal is submitted, if
|
||||
`Proposal.TotalDeposit < ActiveProcedure.MinDeposit`, Atom holders can send
|
||||
Once a proposal is submitted, if
|
||||
`Proposal.TotalDeposit < ActiveParam.MinDeposit`, Atom holders can send
|
||||
`TxGovDeposit` transactions to increase the proposal's deposit.
|
||||
|
||||
```go
|
||||
@@ -89,7 +89,7 @@ type TxGovDeposit struct {
|
||||
* If `MinDeposit` is reached:
|
||||
* Push `proposalID` in `ProposalProcessingQueueEnd`
|
||||
|
||||
A `TxGovDeposit` transaction has to go through a number of checks to be valid.
|
||||
A `TxGovDeposit` transaction has to go through a number of checks to be valid.
|
||||
These checks are outlined in the following pseudocode.
|
||||
|
||||
```go
|
||||
@@ -98,27 +98,27 @@ These checks are outlined in the following pseudocode.
|
||||
|
||||
upon receiving txGovDeposit from sender do
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
throw
|
||||
|
||||
|
||||
proposal = load(Proposals, <txGovDeposit.ProposalID|'proposal'>) // proposal is a const key, proposalID is variable
|
||||
|
||||
if (proposal == nil)
|
||||
if (proposal == nil)
|
||||
// There is no proposal for this proposalID
|
||||
throw
|
||||
|
||||
|
||||
if (txGovDeposit.Deposit.Atoms <= 0) OR (sender.AtomBalance < txGovDeposit.Deposit.Atoms) OR (proposal.CurrentStatus != ProposalStatusOpen)
|
||||
|
||||
// deposit is negative or null
|
||||
// deposit is negative or null
|
||||
// OR sender has insufficient funds
|
||||
// OR proposal is not open for deposit anymore
|
||||
|
||||
throw
|
||||
|
||||
depositProcedure = load(GlobalParams, 'DepositProcedure')
|
||||
depositParam = load(GlobalParams, 'DepositParam')
|
||||
|
||||
if (CurrentBlock >= proposal.SubmitBlock + depositProcedure.MaxDepositPeriod)
|
||||
if (CurrentBlock >= proposal.SubmitBlock + depositParam.MaxDepositPeriod)
|
||||
proposal.CurrentStatus = ProposalStatusClosed
|
||||
|
||||
else
|
||||
@@ -127,21 +127,21 @@ upon receiving txGovDeposit from sender do
|
||||
|
||||
proposal.Deposits.append({txGovVote.Deposit, sender})
|
||||
proposal.TotalDeposit.Plus(txGovDeposit.Deposit)
|
||||
|
||||
if (proposal.TotalDeposit >= depositProcedure.MinDeposit)
|
||||
|
||||
if (proposal.TotalDeposit >= depositParam.MinDeposit)
|
||||
// MinDeposit is reached, vote opens
|
||||
|
||||
|
||||
proposal.VotingStartBlock = CurrentBlock
|
||||
proposal.CurrentStatus = ProposalStatusActive
|
||||
ProposalProcessingQueue.push(txGovDeposit.ProposalID)
|
||||
ProposalProcessingQueue.push(txGovDeposit.ProposalID)
|
||||
|
||||
store(Proposals, <txGovVote.ProposalID|'proposal'>, proposal)
|
||||
```
|
||||
|
||||
### Vote
|
||||
|
||||
Once `ActiveProcedure.MinDeposit` is reached, voting period starts. From there,
|
||||
bonded Atom holders are able to send `TxGovVote` transactions to cast their
|
||||
Once `ActiveParam.MinDeposit` is reached, voting period starts. From there,
|
||||
bonded Atom holders are able to send `TxGovVote` transactions to cast their
|
||||
vote on the proposal.
|
||||
|
||||
```go
|
||||
@@ -157,25 +157,25 @@ vote on the proposal.
|
||||
*Note: Gas cost for this message has to take into account the future tallying of the vote in EndBlocker*
|
||||
|
||||
|
||||
Next is a pseudocode proposal of the way `TxGovVote` transactions are
|
||||
Next is a pseudocode proposal of the way `TxGovVote` transactions are
|
||||
handled:
|
||||
|
||||
```go
|
||||
// PSEUDOCODE //
|
||||
// Check if TxGovVote is valid. If it is, count vote//
|
||||
|
||||
|
||||
upon receiving txGovVote from sender do
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
// check if proposal is correctly formatted. Includes fee payment.
|
||||
|
||||
if !correctlyFormatted(txGovDeposit)
|
||||
throw
|
||||
|
||||
|
||||
proposal = load(Proposals, <txGovDeposit.ProposalID|'proposal'>)
|
||||
|
||||
if (proposal == nil)
|
||||
if (proposal == nil)
|
||||
// There is no proposal for this proposalID
|
||||
throw
|
||||
|
||||
|
||||
|
||||
if (proposal.CurrentStatus == ProposalStatusActive)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user