Revamp gov spec
This commit is contained in:
parent
6bb27c1d99
commit
5735075b05
|
@ -11,32 +11,38 @@ has to be created and the previous one rendered inactive.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
||||||
type VoteType byte
|
type Vote byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VoteTypeYes = 0x1
|
VoteYes = 0x1
|
||||||
VoteTypeNo = 0x2
|
VoteNo = 0x2
|
||||||
VoteTypeNoWithVeto = 0x3
|
VoteNoWithVeto = 0x3
|
||||||
VoteTypeAbstain = 0x4
|
VoteAbstain = 0x4
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProposalType byte
|
type ProposalType byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProposalTypePlainText = 0x1
|
ProposalTypePlainText = 0x1
|
||||||
ProposalTypeSoftwareUpgrade = 0x2
|
ProposalTypeSoftwareUpgrade = 0x2
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProposalStatus byte
|
||||||
|
|
||||||
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
type Procedure struct {
|
type Procedure struct {
|
||||||
VotingPeriod int64 // Length of the voting period. Initial value: 2 weeks
|
VotingPeriod int64 // Length of the voting period. Initial value: 2 weeks
|
||||||
MinDeposit int64 // Minimum deposit for a proposal to enter voting period.
|
MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period.
|
||||||
VoteTypes []VoteType // Vote types available to voters.
|
|
||||||
ProposalTypes []ProposalType // Proposal types available to submitters.
|
|
||||||
Threshold rational.Rational // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5
|
Threshold rational.Rational // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5
|
||||||
Veto rational.Rational // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
Veto rational.Rational // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3
|
||||||
MaxDepositPeriod int64 // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
MaxDepositPeriod int64 // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
|
||||||
GovernancePenalty int64 // Penalty if validator does not vote
|
GovernancePenalty sdk.Rat // Penalty if validator does not vote
|
||||||
|
|
||||||
IsActive bool // If true, procedure is active. Only one procedure can have isActive true.
|
IsActive bool // If true, procedure is active. Only one procedure can have isActive true.
|
||||||
}
|
}
|
||||||
|
@ -53,18 +59,17 @@ The current active procedure is stored in a global `params` KVStore.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Votes
|
### ValidatorGovInfo
|
||||||
|
|
||||||
|
This type is used in a temp map when tallying
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Votes struct {
|
type ValidatorGovInfo struct {
|
||||||
Yes int64
|
Minus sdk.Rat
|
||||||
No int64
|
Vote Vote
|
||||||
NoWithVeto int64
|
|
||||||
Abstain int64
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Proposals
|
### Proposals
|
||||||
|
|
||||||
`Proposals` are an item to be voted on.
|
`Proposals` are an item to be voted on.
|
||||||
|
@ -77,37 +82,34 @@ type Proposal struct {
|
||||||
TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit
|
TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit
|
||||||
Deposits []Deposit // List of deposits on the proposal
|
Deposits []Deposit // List of deposits on the proposal
|
||||||
SubmitBlock int64 // Height of the block where TxGovSubmitProposal was included
|
SubmitBlock int64 // Height of the block where TxGovSubmitProposal was included
|
||||||
Submitter crypto.Address // Address of the submitter
|
Submitter sdk.Address // Address of the submitter
|
||||||
|
|
||||||
VotingStartBlock int64 // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached
|
VotingStartBlock int64 // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached
|
||||||
InitTotalVotingPower int64 // Total voting power when proposal enters voting period (default 0)
|
|
||||||
InitProcedure Procedure // Active Procedure when proposal enters voting period
|
InitProcedure Procedure // Active Procedure when proposal enters voting period
|
||||||
|
CurrentStatus ProposalStatus // Current status of the proposal
|
||||||
|
|
||||||
Votes Votes // Total votes for each option
|
YesVotes sdk.Rat
|
||||||
|
NoVotes sdk.Rat
|
||||||
|
NoWithVetoVotes sdk.Rat
|
||||||
|
AbstainVotes sdk.Rat
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We also introduce a type `ValidatorGovInfo`
|
We also mention a method to update the tally for a given proposal:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type ValidatorGovInfo struct {
|
func (proposal Proposal) updateTally(vote byte, amount sdk.Rat)
|
||||||
InitVotingPower int64 // Voting power of validator when proposal enters voting period
|
|
||||||
Minus int64 // Minus of validator, used to compute validator's voting power
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Stores
|
### Stores
|
||||||
|
|
||||||
*Stores are KVStores in the multistore. The key to find the store is the first parameter in the list*
|
*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` 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`
|
||||||
|
|
||||||
* `Proposals: int64 => Proposal` maps `proposalID` to the `Proposal`
|
|
||||||
`proposalID`
|
|
||||||
* `Options: <proposalID | voterAddress | validatorAddress> => VoteType`: maps to the vote of the `voterAddress` for `proposalID` re its delegation to `validatorAddress`.
|
|
||||||
Returns 0x0 If `voterAddress` has not voted under this validator.
|
|
||||||
* `ValidatorGovInfos: <proposalID | validatorAddress> => ValidatorGovInfo`: maps to the gov info for the `validatorAddress` and `proposalID`.
|
|
||||||
Returns `nil` if proposal has not entered voting period or if `address` was not the
|
|
||||||
address of a validator when proposal entered voting period.
|
|
||||||
|
|
||||||
For pseudocode purposes, here are the two function we will use to read or write in stores:
|
For pseudocode purposes, here are the two function we will use to read or write in stores:
|
||||||
|
|
||||||
|
@ -121,16 +123,14 @@ For pseudocode purposes, here are the two function we will use to read or write
|
||||||
`ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest
|
`ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest
|
||||||
element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if
|
element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if
|
||||||
`CurrentBlock == VotingStartBlock + InitProcedure.VotingPeriod`. If it is,
|
`CurrentBlock == VotingStartBlock + InitProcedure.VotingPeriod`. If it is,
|
||||||
then the application checks if validators in `InitVotingPowerList` have voted
|
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.
|
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.
|
After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated.
|
||||||
Note that if a proposal is accepted under the special condition,
|
|
||||||
its `ProposalID` must be ejected from `ProposalProcessingQueue`.
|
|
||||||
|
|
||||||
And the pseudocode for the `ProposalProcessingQueue`:
|
And the pseudocode for the `ProposalProcessingQueue`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
in BeginBlock do
|
in EndBlock do
|
||||||
|
|
||||||
checkProposal() // First call of the recursive function
|
checkProposal() // First call of the recursive function
|
||||||
|
|
||||||
|
@ -141,58 +141,55 @@ And the pseudocode for the `ProposalProcessingQueue`:
|
||||||
if (proposalID == nil)
|
if (proposalID == nil)
|
||||||
return
|
return
|
||||||
|
|
||||||
proposal = load(Proposals, proposalID)
|
proposal = load(Governance, proposalID)
|
||||||
|
|
||||||
if (proposal.Votes.YesVotes/proposal.InitTotalVotingPower > 2/3)
|
if (CurrentBlock == proposal.VotingStartBlock + proposal.Procedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive)
|
||||||
|
|
||||||
// proposal accepted early by super-majority
|
// End of voting period, tally
|
||||||
// no punishments; refund deposits
|
|
||||||
|
|
||||||
ProposalProcessingQueue.pop()
|
ProposalProcessingQueue.pop()
|
||||||
|
|
||||||
var newDeposits []Deposits
|
validators = stakeKeeper.getAllValidators()
|
||||||
|
tmpValMap := map(sdk.Address)ValidatorGovInfo
|
||||||
|
|
||||||
// XXX: why do we need to reset deposits? cant we just clear it ?
|
// Initiate mapping at 0. Validators that remain at 0 at the end of tally will be punished
|
||||||
for each (amount, depositer) in proposal.Deposits
|
for each validator in validators
|
||||||
newDeposits.append[{0, depositer}]
|
tmpValMap(validator).Minus = 0
|
||||||
depositer.AtomBalance += amount
|
|
||||||
|
voterIterator = rangeQuery(Governance, <proposalID|addresses>) //return all the addresses that voted on the proposal
|
||||||
|
|
||||||
proposal.Deposits = newDeposits
|
// Tally
|
||||||
store(Proposals, proposalID, proposal)
|
for each (voterAddress, vote) in voterIterator
|
||||||
|
delegations = stakeKeeper.getDelegations(voterAddress) // get all delegations for current voter
|
||||||
|
|
||||||
checkProposal()
|
for each delegation in delegations
|
||||||
|
tmpValMap(delegation.ValidatorAddr).Minus += delegation.Shares
|
||||||
|
proposal.updateTally(vote, delegation.Shares)
|
||||||
|
|
||||||
else if (CurrentBlock == proposal.VotingStartBlock + proposal.Procedure.VotingPeriod)
|
_, isVal = stakeKeeper.getValidator(voterAddress)
|
||||||
|
if (isVal)
|
||||||
|
tmpValMap(voterAddress).Vote = vote
|
||||||
|
|
||||||
ProposalProcessingQueue.pop()
|
// Slash validators that did not vote, or update tally if they voted
|
||||||
activeProcedure = load(params, 'ActiveProcedure')
|
for each validator in validators
|
||||||
|
if (!tmpValMap(validator).HasVoted)
|
||||||
for each validator in CurrentBondedValidators
|
slash validator by proposal.Procedure.GovernancePenalty
|
||||||
validatorGovInfo = load(ValidatorGovInfos, <proposalID | validator.Address>)
|
else
|
||||||
|
proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))
|
||||||
if (validatorGovInfo.InitVotingPower != nil)
|
|
||||||
// validator was bonded when vote started
|
|
||||||
|
|
||||||
validatorOption = load(Options, <proposalID | validator.Address>)
|
|
||||||
if (validatorOption == nil)
|
|
||||||
// validator did not vote
|
|
||||||
slash validator by activeProcedure.GovernancePenalty
|
|
||||||
|
|
||||||
|
|
||||||
totalNonAbstain = proposal.Votes.YesVotes + proposal.Votes.NoVotes + proposal.Votes.NoWithVetoVotes
|
|
||||||
if( proposal.Votes.YesVotes/totalNonAbstain > 0.5 AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < 1/3)
|
|
||||||
|
|
||||||
|
// Check if proposal is accepted or rejected
|
||||||
|
totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
|
||||||
|
if (proposal.Votes.YesVotes/totalNonAbstain > proposal.InitProcedure.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < proposal.InitProcedure.Veto)
|
||||||
// proposal was accepted at the end of the voting period
|
// proposal was accepted at the end of the voting period
|
||||||
// refund deposits (non-voters already punished)
|
// refund deposits (non-voters already punished)
|
||||||
|
proposal.CurrentStatus = ProposalStatusAccepted
|
||||||
var newDeposits []Deposits
|
|
||||||
|
|
||||||
for each (amount, depositer) in proposal.Deposits
|
for each (amount, depositer) in proposal.Deposits
|
||||||
newDeposits.append[{0, depositer}]
|
|
||||||
depositer.AtomBalance += amount
|
depositer.AtomBalance += amount
|
||||||
|
|
||||||
proposal.Deposits = newDeposits
|
else
|
||||||
store(Proposals, proposalID, proposal)
|
// proposal was rejected
|
||||||
|
proposal.CurrentStatus = ProposalStatusRejected
|
||||||
|
|
||||||
checkProposal()
|
store(Governance, proposalID, proposal)
|
||||||
|
checkProposal()
|
||||||
```
|
```
|
||||||
|
|
|
@ -12,7 +12,7 @@ type TxGovSubmitProposal struct {
|
||||||
Title string // Title of the proposal
|
Title string // Title of the proposal
|
||||||
Description string // Description of the proposal
|
Description string // Description of the proposal
|
||||||
Type ProposalType // Type of proposal
|
Type ProposalType // Type of proposal
|
||||||
InitialDeposit int64 // Initial deposit paid by sender. Must be strictly positive.
|
InitialDeposit sdk.Coins // Initial deposit paid by sender. Must be strictly positive.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -22,8 +22,7 @@ type TxGovSubmitProposal struct {
|
||||||
* Initialise `Proposals` attributes
|
* Initialise `Proposals` attributes
|
||||||
* Decrease balance of sender by `InitialDeposit`
|
* Decrease balance of sender by `InitialDeposit`
|
||||||
* If `MinDeposit` is reached:
|
* If `MinDeposit` is reached:
|
||||||
* Push `proposalID` in `ProposalProcessingQueueEnd`
|
* Push `proposalID` in `ProposalProcessingQueue`
|
||||||
* Store each validator's voting power in `ValidatorGovInfos`
|
|
||||||
|
|
||||||
A `TxGovSubmitProposal` transaction can be handled according to the following
|
A `TxGovSubmitProposal` transaction can be handled according to the following
|
||||||
pseudocode.
|
pseudocode.
|
||||||
|
@ -34,16 +33,16 @@ pseudocode.
|
||||||
|
|
||||||
upon receiving txGovSubmitProposal from sender do
|
upon receiving txGovSubmitProposal from sender do
|
||||||
|
|
||||||
if !correctlyFormatted(txGovSubmitProposal) then
|
if !correctlyFormatted(txGovSubmitProposal)
|
||||||
// check if proposal is correctly formatted. Includes fee payment.
|
// check if proposal is correctly formatted. Includes fee payment.
|
||||||
throw
|
throw
|
||||||
|
|
||||||
initialDeposit = txGovSubmitProposal.InitialDeposit
|
initialDeposit = txGovSubmitProposal.InitialDeposit
|
||||||
if (initialDeposit <= 0) OR (sender.AtomBalance < initialDeposit) then
|
if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms)
|
||||||
// InitialDeposit is negative or null OR sender has insufficient funds
|
// InitialDeposit is negative or null OR sender has insufficient funds
|
||||||
throw
|
throw
|
||||||
|
|
||||||
sender.AtomBalance -= initialDeposit
|
sender.AtomBalance -= initialDeposit.Atoms
|
||||||
|
|
||||||
proposalID = generate new proposalID
|
proposalID = generate new proposalID
|
||||||
proposal = NewProposal()
|
proposal = NewProposal()
|
||||||
|
@ -55,35 +54,25 @@ upon receiving txGovSubmitProposal from sender do
|
||||||
proposal.SubmitBlock = CurrentBlock
|
proposal.SubmitBlock = CurrentBlock
|
||||||
proposal.Deposits.append({initialDeposit, sender})
|
proposal.Deposits.append({initialDeposit, sender})
|
||||||
proposal.Submitter = sender
|
proposal.Submitter = sender
|
||||||
proposal.Votes.Yes = 0
|
proposal.YesVotes = 0
|
||||||
proposal.Votes.No = 0
|
proposal.NoVotes = 0
|
||||||
proposal.Votes.NoWithVeto = 0
|
proposal.NoWithVetoVotes = 0
|
||||||
proposal.Votes.Abstain = 0
|
proposal.AbstainVotes = 0
|
||||||
|
|
||||||
activeProcedure = load(params, 'ActiveProcedure')
|
activeProcedure = load(params, 'ActiveProcedure')
|
||||||
|
|
||||||
if (initialDeposit < activeProcedure.MinDeposit) then
|
if (initialDeposit < activeProcedure.MinDeposit)
|
||||||
// MinDeposit is not reached
|
// MinDeposit is not reached
|
||||||
|
|
||||||
|
proposal.CurrentStatus = ProposalStatusOpen
|
||||||
proposal.VotingStartBlock = -1
|
proposal.VotingStartBlock = -1
|
||||||
proposal.InitTotalVotingPower = 0
|
|
||||||
|
|
||||||
else
|
else
|
||||||
// MinDeposit is reached
|
// MinDeposit is reached
|
||||||
|
|
||||||
|
proposal.CurrentStatus = ProposalStatusActive
|
||||||
proposal.VotingStartBlock = CurrentBlock
|
proposal.VotingStartBlock = CurrentBlock
|
||||||
proposal.InitTotalVotingPower = TotalVotingPower
|
|
||||||
proposal.InitProcedure = activeProcedure
|
proposal.InitProcedure = activeProcedure
|
||||||
|
|
||||||
for each validator in CurrentBondedValidators
|
|
||||||
// Store voting power of each bonded validator
|
|
||||||
|
|
||||||
validatorGovInfo = new ValidatorGovInfo
|
|
||||||
validatorGovInfo.InitVotingPower = validator.VotingPower
|
|
||||||
validatorGovInfo.Minus = 0
|
|
||||||
|
|
||||||
store(ValidatorGovInfos, <proposalID | validator.Address>, validatorGovInfo)
|
|
||||||
|
|
||||||
ProposalProcessingQueue.push(proposalID)
|
ProposalProcessingQueue.push(proposalID)
|
||||||
|
|
||||||
store(Proposals, proposalID, proposal) // Store proposal in Proposals mapping
|
store(Proposals, proposalID, proposal) // Store proposal in Proposals mapping
|
||||||
|
@ -98,8 +87,8 @@ Once a proposal is submitted, if
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type TxGovDeposit struct {
|
type TxGovDeposit struct {
|
||||||
ProposalID int64 // ID of the proposal
|
ProposalID int64 // ID of the proposal
|
||||||
Deposit int64 // Number of Atoms to add to the proposal's deposit
|
Deposit sdk.Coins // Number of Atoms to add to the proposal's deposit
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -109,7 +98,6 @@ type TxGovDeposit struct {
|
||||||
* Increase `proposal.TotalDeposit` by sender's `deposit`
|
* Increase `proposal.TotalDeposit` by sender's `deposit`
|
||||||
* If `MinDeposit` is reached:
|
* If `MinDeposit` is reached:
|
||||||
* Push `proposalID` in `ProposalProcessingQueueEnd`
|
* 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.
|
A `TxGovDeposit` transaction has to go through a number of checks to be valid.
|
||||||
These checks are outlined in the following pseudocode.
|
These checks are outlined in the following pseudocode.
|
||||||
|
@ -121,57 +109,40 @@ These checks are outlined in the following pseudocode.
|
||||||
upon receiving txGovDeposit from sender do
|
upon receiving txGovDeposit from sender do
|
||||||
// check if proposal is correctly formatted. Includes fee payment.
|
// check if proposal is correctly formatted. Includes fee payment.
|
||||||
|
|
||||||
if !correctlyFormatted(txGovDeposit) then
|
if !correctlyFormatted(txGovDeposit)
|
||||||
throw
|
throw
|
||||||
|
|
||||||
proposal = load(Proposals, txGovDeposit.ProposalID)
|
proposal = load(Proposals, txGovDeposit.ProposalID)
|
||||||
|
|
||||||
if (proposal == nil) then
|
if (proposal == nil)
|
||||||
// There is no proposal for this proposalID
|
// There is no proposal for this proposalID
|
||||||
throw
|
throw
|
||||||
|
|
||||||
if (txGovDeposit.Deposit <= 0) OR (sender.AtomBalance < txGovDeposit.Deposit)
|
|
||||||
// deposit is negative or null OR sender has insufficient funds
|
|
||||||
throw
|
|
||||||
|
|
||||||
activeProcedure = load(params, 'ActiveProcedure')
|
activeProcedure = load(params, 'ActiveProcedure')
|
||||||
|
|
||||||
|
if (txGovDeposit.Deposit.Atoms <= 0) OR (sender.AtomBalance < txGovDeposit.Deposit.Atoms) OR (proposal.TotalDeposit >= activeProcedure.MinDeposit) OR (CurrentBlock >= proposal.SubmitBlock + activeProcedure.MaxDepositPeriod)
|
||||||
|
// deposit is negative or null
|
||||||
|
// OR sender has insufficient funds
|
||||||
|
// OR minDeposit has already been reached
|
||||||
|
// OR Maximum deposit period reached
|
||||||
|
|
||||||
if (proposal.TotalDeposit >= activeProcedure.MinDeposit) then
|
|
||||||
// MinDeposit was reached
|
|
||||||
// TODO: shouldnt we do something here ?
|
|
||||||
throw
|
throw
|
||||||
|
|
||||||
else
|
// sender can deposit
|
||||||
if (CurrentBlock >= proposal.SubmitBlock + activeProcedure.MaxDepositPeriod) then
|
sender.AtomBalance -= txGovDeposit.Deposit.Atoms
|
||||||
// Maximum deposit period reached
|
|
||||||
throw
|
proposal.Deposits.append({txGovVote.Deposit, sender})
|
||||||
|
proposal.TotalDeposit.Plus(txGovDeposit.Deposit)
|
||||||
|
|
||||||
|
if (proposal.TotalDeposit >= activeProcedure.MinDeposit)
|
||||||
|
// MinDeposit is reached, vote opens
|
||||||
|
|
||||||
// sender can deposit
|
proposal.VotingStartBlock = CurrentBlock
|
||||||
|
proposal.CurrentStatus = ProposalStatusActive
|
||||||
sender.AtomBalance -= txGovDeposit.Deposit
|
proposal.InitProcedure = activeProcedure
|
||||||
|
ProposalProcessingQueue.push(txGovDeposit.ProposalID)
|
||||||
|
|
||||||
proposal.Deposits.append({txGovVote.Deposit, sender})
|
store(Proposals, txGovVote.ProposalID, proposal)
|
||||||
proposal.TotalDeposit += txGovDeposit.Deposit
|
|
||||||
|
|
||||||
if (proposal.TotalDeposit >= activeProcedure.MinDeposit) then
|
|
||||||
// MinDeposit is reached, vote opens
|
|
||||||
|
|
||||||
proposal.VotingStartBlock = CurrentBlock
|
|
||||||
proposal.InitTotalVotingPower = TotalVotingPower
|
|
||||||
proposal.InitProcedure = activeProcedure
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
ProposalProcessingQueue.push(txGovDeposit.ProposalID)
|
|
||||||
|
|
||||||
store(Proposals, txGovVote.ProposalID, proposal)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Vote
|
### Vote
|
||||||
|
@ -183,26 +154,14 @@ vote on the proposal.
|
||||||
```go
|
```go
|
||||||
type TxGovVote struct {
|
type TxGovVote struct {
|
||||||
ProposalID int64 // proposalID of the proposal
|
ProposalID int64 // proposalID of the proposal
|
||||||
Option string // option from OptionSet chosen by the voter
|
Vote byte // option from OptionSet chosen by the voter
|
||||||
ValidatorAddress crypto.address // Address of the validator voter wants to tie its vote to
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**State modifications:**
|
**State modifications:**
|
||||||
* If sender is not a validator and validator has not voted, initialize or
|
* Record `Vote` of sender
|
||||||
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`
|
|
||||||
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)
|
|
||||||
|
|
||||||
Votes need to be tied to a validator in order to compute validator's voting
|
*Note: Gas cost for this message has to take into account the future tallying of the vote in EndBlocker*
|
||||||
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`
|
|
||||||
|
|
||||||
Next is a pseudocode proposal of the way `TxGovVote` transactions are
|
Next is a pseudocode proposal of the way `TxGovVote` transactions are
|
||||||
handled:
|
handled:
|
||||||
|
@ -214,91 +173,24 @@ handled:
|
||||||
upon receiving txGovVote from sender do
|
upon receiving txGovVote from sender do
|
||||||
// check if proposal is correctly formatted. Includes fee payment.
|
// check if proposal is correctly formatted. Includes fee payment.
|
||||||
|
|
||||||
if !correctlyFormatted(txGovDeposit) then
|
if !correctlyFormatted(txGovDeposit)
|
||||||
throw
|
throw
|
||||||
|
|
||||||
proposal = load(Proposals, txGovDeposit.ProposalID)
|
proposal = load(Proposals, txGovDeposit.ProposalID)
|
||||||
|
|
||||||
if (proposal == nil) then
|
if (proposal == nil)
|
||||||
// There is no proposal for this proposalID
|
// There is no proposal for this proposalID
|
||||||
throw
|
throw
|
||||||
|
|
||||||
validator = load(CurrentValidators, txGovVote.ValidatorAddress)
|
|
||||||
|
if (proposal.VotingStartBlock >= 0) AND
|
||||||
|
(CurrentBlock <= proposal.VotingStartBlock + proposal.InitProcedure.VotingPeriod)
|
||||||
|
|
||||||
|
// Sender can vote if
|
||||||
|
// Vote has started AND if
|
||||||
|
// Vote had notended
|
||||||
|
|
||||||
|
store(Governance, <txGovVote.ProposalID|addresses|sender>, txGovVote.Vote) // Voters can vote multiple times. Re-voting overrides previous vote. This is ok because tallying is done once at the end.
|
||||||
|
|
||||||
|
|
||||||
if !proposal.InitProcedure.OptionSet.includes(txGovVote.Option) OR
|
|
||||||
(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
|
|
||||||
|
|
||||||
throw
|
|
||||||
|
|
||||||
option = load(Options, <txGovVote.ProposalID>:<sender>:<txGovVote.ValidatorAddress>)
|
|
||||||
|
|
||||||
if (option != nil)
|
|
||||||
// sender has already voted with the Atoms bonded to ValidatorAddress
|
|
||||||
throw
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
throw
|
|
||||||
|
|
||||||
validatorGovInfo = load(ValidatorGovInfos, <txGovVote.ProposalID>:<validator.ValidatorAddress>)
|
|
||||||
|
|
||||||
if (validatorGovInfo == nil)
|
|
||||||
// validator became validator after proposal entered voting period
|
|
||||||
throw
|
|
||||||
|
|
||||||
// sender can vote, check if sender == validator and store sender's option in Options
|
|
||||||
|
|
||||||
store(Options, <txGovVote.ProposalID>:<sender>:<txGovVote.ValidatorAddress>, txGovVote.Option)
|
|
||||||
|
|
||||||
if (sender != validator.address)
|
|
||||||
// Here, sender is not the Address of the validator whose Address is txGovVote.ValidatorAddress
|
|
||||||
|
|
||||||
if sender does not have bonded Atoms to txGovVote.ValidatorAddress then
|
|
||||||
// check in Staking module
|
|
||||||
throw
|
|
||||||
|
|
||||||
validatorOption = load(Options, <txGovVote.ProposalID>:<sender>:<txGovVote.ValidatorAddress>)
|
|
||||||
|
|
||||||
if (validatorOption == nil)
|
|
||||||
// Validator has not voted already
|
|
||||||
|
|
||||||
validatorGovInfo.Minus += sender.bondedAmounTo(txGovVote.ValidatorAddress)
|
|
||||||
store(ValidatorGovInfos, <txGovVote.ProposalID>:<validator.ValidatorAddress>, validatorGovInfo)
|
|
||||||
|
|
||||||
else
|
|
||||||
// Validator has already voted
|
|
||||||
// Reduce votes of option chosen by validator by sender's bonded Amount
|
|
||||||
|
|
||||||
proposal.Votes.validatorOption -= sender.bondedAmountTo(txGovVote.ValidatorAddress)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
|
|
||||||
else
|
|
||||||
// sender is the address of the validator whose main Address is txGovVote.ValidatorAddress
|
|
||||||
// i.e. sender == validator
|
|
||||||
|
|
||||||
proposal.Votes.validatorOption += (validatorGovInfo.InitVotingPower - validatorGovInfo.Minus)
|
|
||||||
|
|
||||||
store(Proposals, txGovVote.ProposalID, proposal)
|
|
||||||
```
|
```
|
||||||
|
|
Loading…
Reference in New Issue