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

327 lines
12 KiB
Markdown
Raw Normal View History

2018-02-26 07:35:09 -08:00
# Implementation (2/2)
## Transactions
### Proposal Submission
Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal`
transaction.
```go
type TxGovSubmitProposal struct {
Title string // Title of the proposal
Description string // Description of the proposal
Type string // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal}
2018-02-26 08:28:57 -08:00
InitialDeposit int64 // Initial deposit paid by sender. Must be strictly positive.
2018-02-26 07:35:09 -08:00
}
```
**State modifications:**
* Generate new `proposalID`
* Create new `Proposal`
* Initialise `Proposals` attributes
* Decrease balance of sender by `InitialDeposit`
* If `MinDeposit` is reached:
* Push `proposalID` in `ProposalProcessingQueueEnd`
* Store each validator's voting power in `ValidatorGovInfos`
A `TxGovSubmitProposal` transaction can be handled according to the following
pseudocode.
```go
// PSEUDOCODE //
// Check if TxGovSubmitProposal is valid. If it is, create proposal //
upon receiving txGovSubmitProposal from sender do
if !correctlyFormatted(txGovSubmitProposal) then
// check if proposal is correctly formatted. Includes fee payment.
throw
else
if (txGovSubmitProposal.InitialDeposit <= 0) OR (sender.AtomBalance < InitialDeposit) then
// InitialDeposit is negative or null OR sender has insufficient funds
throw
else
sender.AtomBalance -= txGovSubmitProposal.InitialDeposit
proposalID = generate new proposalID
proposal = NewProposal()
proposal.Title = txGovSubmitProposal.Title
proposal.Description = txGovSubmitProposal.Description
proposal.Type = txGovSubmitProposal.Type
proposal.TotalDeposit = txGovSubmitProposal.InitialDeposit
2018-02-26 07:35:09 -08:00
proposal.SubmitBlock = CurrentBlock
proposal.Deposits.append({InitialDeposit, sender})
2018-03-02 03:08:18 -08:00
proposal.Submitter = sender
proposal.Votes.YesVotes = 0
proposal.Votes.NoVotes = 0
proposal.Votes.NoWithVetoVotes = 0
proposal.Votes.AbstainVotes = 0
2018-02-26 07:35:09 -08:00
activeProcedure = load(params, 'ActiveProcedure')
2018-02-26 07:35:09 -08:00
if (txGovSubmitProposal.InitialDeposit < activeProcedure.MinDeposit) then
// MinDeposit is not reached
proposal.VotingStartBlock = -1
proposal.InitTotalVotingPower = 0
else
// MinDeposit is reached
proposal.VotingStartBlock = CurrentBlock
proposal.InitTotalVotingPower = TotalVotingPower
proposal.InitProcedure = activeProcedure
2018-02-26 07:35:09 -08:00
for each validator in CurrentBondedValidators
// Store voting power of each bonded validator
validatorGovInfo = new ValidatorGovInfo
2018-02-26 07:35:09 -08:00
validatorGovInfo.InitVotingPower = validator.VotingPower
validatorGovInfo.Minus = 0
store(ValidatorGovInfos, <proposalID>:<validator.Address>, validatorGovInfo)
2018-02-26 07:35:09 -08:00
ProposalProcessingQueue.push(proposalID)
store(Proposals, proposalID, proposal) // Store proposal in Proposals mapping
return proposalID
```
### Deposit
Once a proposal is submitted, if
`Proposal.TotalDeposit < ActiveProcedure.MinDeposit`, Atom holders can send
2018-02-26 07:35:09 -08:00
`TxGovDeposit` transactions to increase the proposal's deposit.
```go
type TxGovDeposit struct {
ProposalID int64 // ID of the proposal
Deposit int64 // Number of Atoms to add to the proposal's deposit
}
```
**State modifications:**
* Decrease balance of sender by `deposit`
* Add `deposit` of sender in `proposal.Deposits`
* Increase `proposal.TotalDeposit` by sender's `deposit`
2018-02-26 07:35:09 -08:00
* If `MinDeposit` is reached:
* Push `proposalID` in `ProposalProcessingQueueEnd`
* Store each validator's voting power in `ValidatorGovInfos`
A `TxGovDeposit` transaction has to go through a number of checks to be valid.
These checks are outlined in the following pseudocode.
```go
// PSEUDOCODE //
// Check if TxGovDeposit is valid. If it is, increase deposit and check if MinDeposit is reached
upon receiving txGovDeposit from sender do
// check if proposal is correctly formatted. Includes fee payment.
if !correctlyFormatted(txGovDeposit) then
throw
else
2018-02-26 08:28:57 -08:00
proposal = load(Proposals, txGovDeposit.ProposalID)
2018-02-26 07:35:09 -08:00
if (proposal == nil) then
// There is no proposal for this proposalID
throw
else
if (txGovDeposit.Deposit <= 0) OR (sender.AtomBalance < txGovDeposit.Deposit)
// deposit is negative or null OR sender has insufficient funds
throw
else
activeProcedure = load(params, 'ActiveProcedure')
if (proposal.TotalDeposit >= activeProcedure.MinDeposit) then
2018-02-26 07:35:09 -08:00
// MinDeposit was reached
throw
else
if (CurrentBlock >= proposal.SubmitBlock + activeProcedure.MaxDepositPeriod) then
// Maximum deposit period reached
throw
else
// sender can deposit
sender.AtomBalance -= txGovDeposit.Deposit
proposal.Deposits.append({txGovVote.Deposit, sender})
proposal.TotalDeposit += txGovDeposit.Deposit
2018-02-26 07:35:09 -08:00
if (proposal.TotalDeposit >= activeProcedure.MinDeposit) then
2018-02-26 07:35:09 -08:00
// MinDeposit is reached, vote opens
proposal.VotingStartBlock = CurrentBlock
proposal.InitTotalVotingPower = TotalVotingPower
proposal.InitProcedure = activeProcedure
2018-02-26 07:35:09 -08:00
for each validator in CurrentBondedValidators
// Store voting power of each bonded validator
validatorGovInfo = NewValidatorGovInfo()
validatorGovInfo.InitVotingPower = validator.VotingPower
validatorGovInfo.Minus = 0
store(ValidatorGovInfos, <proposalID>:<validator.Address>, validatorGovInfo)
2018-02-26 07:35:09 -08:00
ProposalProcessingQueue.push(txGovDeposit.ProposalID)
2018-02-28 06:15:10 -08:00
store(Proposals, txGovVote.ProposalID, proposal)
2018-02-26 07:35:09 -08:00
```
### Vote
Once `ActiveProcedure.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
type TxGovVote struct {
ProposalID int64 // proposalID of the proposal
Option string // option from OptionSet chosen by the voter
ValidatorAddress crypto.address // Address of the validator voter wants to tie its vote to
2018-02-26 07:35:09 -08:00
}
```
**State modifications:**
* If sender is not a validator and validator has not voted, initialize or
increase minus of validator by sender's `voting power`
* If sender is not a validator and validator has voted, decrease
votes of `validatorOption` by sender's `voting power`
* If sender is not a validator, increase votes of `txGovVote.Option`
2018-02-26 07:35:09 -08:00
by sender's `voting power`
* If sender is a validator, increase votes of `txGovVote.Option` by
validator's `InitVotingPower - minus` (`minus` can be equal to 0)
2018-02-26 07:35:09 -08:00
Votes need to be tied to a validator in order to compute validator's voting
power. If a delegator is bonded to multiple validators, it will have to send
one transaction per validator (the UI should facilitate this so that multiple
transactions can be sent in one "vote flow"). If the sender is the validator
itself, then it will input its own address as `ValidatorAddress`
2018-02-26 07:35:09 -08:00
Next is a pseudocode proposal of the way `TxGovVote` transactions are
2018-02-26 07:35:09 -08:00
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) then
throw
else
2018-02-26 08:28:57 -08:00
proposal = load(Proposals, txGovDeposit.ProposalID)
2018-02-26 07:35:09 -08:00
if (proposal == nil) then
// There is no proposal for this proposalID
throw
else
validator = load(CurrentValidators, txGovVote.ValidatorAddress)
2018-02-26 07:35:09 -08:00
if !proposal.InitProcedure.OptionSet.includes(txGovVote.Option) OR
2018-02-26 07:35:09 -08:00
(validator == nil) then
// Throws if
// Option is not in Option Set of procedure that was active when vote opened OR if
// ValidatorAddress is not the address of a current validator
2018-02-26 07:35:09 -08:00
throw
else
option = load(Options, <txGovVote.ProposalID>:<sender>:<txGovVote.ValidatorAddress>)
2018-02-26 07:35:09 -08:00
if (option != nil)
// sender has already voted with the Atoms bonded to ValidatorAddress
2018-02-26 07:35:09 -08:00
throw
else
if (proposal.VotingStartBlock < 0) OR
(CurrentBlock > proposal.VotingStartBlock + proposal.InitProcedure.VotingPeriod) OR
(proposal.VotingStartBlock < lastBondingBlock(sender, txGovVote.ValidatorAddress) OR
(proposal.VotingStartBlock < lastUnbondingBlock(sender, txGovVote.Address) OR
(proposal.Votes.YesVotes/proposal.InitTotalVotingPower >= 2/3) then
2018-02-26 07:35:09 -08:00
// Throws if
// Vote has not started OR if
// Vote had ended OR if
// sender bonded Atoms to ValidatorAddress after start of vote OR if
// sender unbonded Atoms from ValidatorAddress after start of vote OR if
// special condition is met, i.e. proposal is accepted and closed
2018-02-26 07:35:09 -08:00
throw
else
validatorGovInfo = load(ValidatorGovInfos, <txGovVote.ProposalID>:<validator.ValidatorAddress>)
2018-02-26 07:35:09 -08:00
if (validatorGovInfo == nil)
// validator became validator after proposal entered voting period
throw
else
// sender can vote, check if sender == validator and store sender's option in Options
store(Options, <txGovVote.ProposalID>:<sender>:<txGovVote.ValidatorAddress>, txGovVote.Option)
2018-02-26 07:35:09 -08:00
if (sender != validator.address)
// Here, sender is not the Address of the validator whose Address is txGovVote.ValidatorAddress
2018-02-26 07:35:09 -08:00
if sender does not have bonded Atoms to txGovVote.ValidatorAddress then
2018-02-26 07:35:09 -08:00
// check in Staking module
throw
else
validatorOption = load(Options, <txGovVote.ProposalID>:<sender>:<txGovVote.ValidatorAddress>)
2018-02-26 07:35:09 -08:00
if (validatorOption == nil)
// Validator has not voted already
validatorGovInfo.Minus += sender.bondedAmounTo(txGovVote.ValidatorAddress)
store(ValidatorGovInfos, <txGovVote.ProposalID>:<validator.ValidatorAddress>, validatorGovInfo)
2018-02-26 07:35:09 -08:00
else
// Validator has already voted
// Reduce votes of option chosen by validator by sender's bonded Amount
proposal.Votes.validatorOption -= sender.bondedAmountTo(txGovVote.ValidatorAddress)
2018-02-26 07:35:09 -08:00
// increase votes of option chosen by sender by bonded Amount
senderOption = txGovVote.Option
propoal.Votes.senderOption -= sender.bondedAmountTo(txGovVote.ValidatorAddress)
store(Proposals, txGovVote.ProposalID, proposal)
2018-02-27 07:04:44 -08:00
2018-02-26 07:35:09 -08:00
else
// sender is the address of the validator whose main Address is txGovVote.ValidatorAddress
2018-02-26 07:35:09 -08:00
// i.e. sender == validator
2018-02-27 07:04:44 -08:00
proposal.Votes.validatorOption += (validatorGovInfo.InitVotingPower - validatorGovInfo.Minus)
store(Proposals, txGovVote.ProposalID, proposal)
2018-02-26 07:35:09 -08:00
```