# State ## Proposals `Proposal` objects are used to tally votes and generally track the proposal's state. They contain an array of arbitrary `sdk.Msg`'s which the governance module will attempt to resolve and then execute if the proposal passes. `Proposal`'s are identified by a unique id and contains a series of timestamps: `submit_time`, `deposit_end_time`, `voting_start_time`, `voting_end_time` which track the lifecycle of a proposal +++ https://github.com/cosmos/cosmos-sdk/blob/4a129832eb16f37a89e97652a669f0cdc9196ca9/proto/cosmos/gov/v1beta2/gov.proto#L42-L52 A proposal will generally require more than just a set of messages to explain its purpose but need some greater justification and allow a means for interested participants to discuss and debate the proposal. In most cases, it is encouraged to have an off-chain system that supports the on-chain governance process. To accommodate for this, a proposal contains a special `metadata` field, an array of bytes, which can be used to add context to the proposal. The `metadata` field allows custom use for networks, however, it is expected that the field contain a URL or some form of CID using a system such as [IPFS](https://docs.ipfs.io/concepts/content-addressing/). To support the case of interoperability across networks, the SDK recommends that the `metadata` represents the following `JSON` template: ```json { "title": "...", "description": "...", "forum": "...", // a link to the discussion platform (i.e. Discord) "other": "..." // any extra data that doesn't correspond to the other fields } ``` This makes it far easier for clients to support multiple networks. ### Writing a module that uses governance There are many aspects of a chain, or of the individual modules that you may want to use governance to perform such as changing various parameters. This is very simple to do. First, write out your message types and `MsgServer` implementation. Add an `authority` field to the keeper which will be populated in the constructor with the governance module account: `govKeeper.GetGovernanceAccount().GetAddress()`. Then for the methods in the `msg_server.go`, perform a check on the message that the signer matches `authority`. This will prevent any user from executing that message. ## 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. ### DepositParams +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L127-L145 ### VotingParams +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L147-L156 ### TallyParams +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L158-L183 Parameters are stored in a global `GlobalParams` KVStore. Additionally, we introduce some basic types: ```go type Vote byte const ( VoteYes = 0x1 VoteNo = 0x2 VoteNoWithVeto = 0x3 VoteAbstain = 0x4 ) type ProposalType string const ( ProposalTypePlainText = "Text" ProposalTypeSoftwareUpgrade = "SoftwareUpgrade" ) type ProposalStatus byte const ( StatusNil ProposalStatus = 0x00 StatusDepositPeriod ProposalStatus = 0x01 // Proposal is submitted. Participants can deposit on it but not vote StatusVotingPeriod ProposalStatus = 0x02 // MinDeposit is reached, participants can vote StatusPassed ProposalStatus = 0x03 // Proposal passed and successfully executed StatusRejected ProposalStatus = 0x04 // Proposal has been rejected StatusFailed ProposalStatus = 0x05 // Proposal passed but failed execution ) ``` ## Deposit +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/gov/v1beta1/gov.proto#L43-L53 ## ValidatorGovInfo This type is used in a temp map when tallying ```go type ValidatorGovInfo struct { Minus sdk.Dec Vote Vote } ``` ## Stores _Stores are KVStores in the multi-store. 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'` 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`. 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 ## Proposal Processing Queue **Store:** - `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the `ProposalIDs` of proposals that reached `MinDeposit`. During 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, computes the votes of each validator and checks if every validator in the validator set has voted. If the proposal is accepted, deposits are refunded. Finally, the proposal content `Handler` is executed. And the pseudocode for the `ProposalProcessingQueue`: ```go in EndBlock do for finishedProposalID in GetAllFinishedProposalIDs(block.Time) proposal = load(Governance, ) // 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, ) //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) for each (amount, depositor) in proposal.Deposits depositor.AtomBalance += amount stateWriter, err := proposal.Handler() if err != nil // proposal passed but failed during state execution proposal.CurrentStatus = ProposalStatusFailed else // proposal pass and state is persisted proposal.CurrentStatus = ProposalStatusAccepted stateWriter.save() else // proposal was rejected proposal.CurrentStatus = ProposalStatusRejected store(Governance, , proposal) ```