-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: ADR 069 - x/gov
modularity, multiple choice and optimistic proposal
#18498
Changes from all commits
2cacb0c
1d9a404
997408a
f5e699f
2b06aea
bb4596a
5274e3f
e86a8d2
f53cfe2
0a42433
e3452b9
6ba3d65
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
# ADR 069: `x/gov` modularity, multiple choice and optimistic proposals | ||
|
||
## Changelog | ||
|
||
* 2023-11-17: Initial draft (@julienrbrt, @tac0turtle) | ||
|
||
## Status | ||
|
||
PROPOSED | ||
|
||
## Abstract | ||
|
||
Governance is an important aspect of Cosmos SDK chains. | ||
|
||
This ADR aimed to extend the `x/gov` module functionalities by adding two different kinds of proposals, as well as making `x/gov` more composable and extendable. | ||
|
||
Those two types are, namely: multiple choice proposals and optimistic proposals. | ||
|
||
## Context | ||
|
||
`x/gov` is the center of Cosmos governance, and has already been improved from its first version `v1beta1`, with a second version [`v1`][5]. | ||
This second iteration on gov unlocked many possibilities by letting governance proposals contain any number of proposals. | ||
The last addition of gov has been expedited proposals (proposals that have a shorter voting period and a higher quorum, approval threshold). | ||
|
||
The community requested ([1], [4]) two additional proposals for improving governance choices. Those proposals would be useful when having protocol decisions made on specific choices or simplifying regular proposals that do not require high community involvement. | ||
|
||
Additionally, the SDK should allow chains to customize the tallying method of proposals (if they want to count the votes in another way). Currently, the Cosmos SDK counts votes proportionally to the voting power/stake. However, custom tallying could allow counting votes with a quadratic function instead. | ||
|
||
## Decision | ||
|
||
`x/gov` will integrate these functions and extract helpers and interfaces for extending the `x/gov` module capabilities. | ||
|
||
### Proposals | ||
|
||
Currently, all proposals are [`v1.Proposal`][5]. Optimistic and multiple choice proposals require a different tally logic, but the rest of the proposal stays the same to not create other proposal types, `v1.Proposal` will have an extra field: | ||
|
||
```protobuf | ||
// ProposalType enumerates the valid proposal types. | ||
// All proposal types are v1.Proposal which have different voting periods or tallying logic. | ||
enum ProposalType { | ||
// PROPOSAL_TYPE_UNSPECIFIED defines no proposal type, which fallback to PROPOSAL_TYPE_STANDARD. | ||
PROPOSAL_TYPE_UNSPECIFIED = 0; | ||
// PROPOSAL_TYPE_STANDARD defines the type for a standard proposal. | ||
PROPOSAL_TYPE_STANDARD = 1; | ||
// PROPOSAL_TYPE_MULTIPLE_CHOICE defines the type for a multiple choice proposal. | ||
PROPOSAL_TYPE_MULTIPLE_CHOICE = 2; | ||
// PROPOSAL_TYPE_OPTIMISTIC defines the type for an optimistic proposal. | ||
PROPOSAL_TYPE_OPTIMISTIC = 3; | ||
// PROPOSAL_TYPE_EXPEDITED defines the type for an expedited proposal. | ||
PROPOSAL_TYPE_EXPEDITED = 4; | ||
} | ||
``` | ||
|
||
Note, that expedited becomes a proposal type itself instead of a boolean on the `v1.Proposal` struct. | ||
|
||
> An expedited proposal is by design a standard proposal with a quicker voting period and higher threshold. When an expedited proposal fails, it gets converted to a standard proposal. | ||
|
||
An expedited optimistic proposal and an expedited multiple choice proposal do not make sense based on the definition above and is a proposal type instead of a proposal characteristic. | ||
|
||
#### Optimistic Proposal | ||
|
||
An optimistic proposal is a proposal that passes unless a threshold a NO votes is reached. | ||
|
||
Voter can only vote NO on the proposal. If the NO threshold is reached, the optimistic proposal is converted to a standard proposal. | ||
|
||
Two governance parameters will be in added [`v1.Params`][5] to support optimistic proposals: | ||
|
||
```protobuf | ||
// optimistic_authorized_addreses is an optional governance parameter that limits the authorized accounts than can submit optimisitc proposals | ||
repeated string optimistic_authorized_addreses = 17 [(cosmos_proto.scalar) = "cosmos.AddressString"]; | ||
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Optimistic rejected threshold defines at which percentage of NO votes, the optimistic proposal should fail and be converted to a standard proposal. | ||
string optimistic_rejected_threshold = 18 [(cosmos_proto.scalar) = "cosmos.Dec"]; | ||
``` | ||
|
||
#### Multiple Choice Proposal | ||
|
||
A multiple choice proposal is a proposal where the voting options can be defined by the proposer. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not really a fan of the fact that the options can only be set by the proposer. It relies on the proposer being fair in setting them. But definitely doing anything different would add a lot of complexity and it's not guaranteed to have any benefit. A voter that is not agreeing with any option - and want none of them to pass - but does not want to abuse the SPAM vote can only choose to not vote, and try to keep the quorum lower than the threshold. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we first thought about it I thought the same. We thought of adding a blank vote option, but eventually decided to let that as well for the proposer to specify. Usually, chains have an off chain process for before submitting a proposal. The voting option discovery should be done there and if someone decides to skip that process, it makes sense to consider the proposal as spam. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the voting option discovery should be done on the forum, completely agree with that, and ideally it's what makes the most sense. |
||
|
||
The number of voting option will be limited to a maximum of 4. | ||
tac0turtle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
A new vote option `SPAM` will be added and distinguished of those voting options. `SPAM` will be used to mark a proposal as spam and is explained further below. | ||
|
||
Multiple choice proposals, contrary to any other proposal type, cannot have messages to execute. They are only text proposals. | ||
|
||
Submitting a new multiple choice proposal will use a different message than the [`v1.MsgSubmitProposal`][5]. This is done in order to simplify the proposal submittion and allow defining the voting options directly. | ||
|
||
|
||
```protobuf | ||
message MsgSubmitMultipleChoiceProposal { | ||
repeated cosmos.base.v1beta1.Coin initial_deposit = 1 | ||
string proposer = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; | ||
string metadata = 3; | ||
string title = 4; | ||
string summary = 5; | ||
string option_one = 6; | ||
string option_two = 7; | ||
string option_three = 8; | ||
string option_four = 9; | ||
} | ||
``` | ||
|
||
Voters can only vote on the defined options in the proposal. | ||
|
||
To maintain compatibility with the existing endpoints, the voting options will not be stored in the proposal itself and each option will be mapped to [`v1.VoteOption`][5]. A multiple choice proposal will be stored as a [`v1.Proposal`][5]. A query will be available for multiple choice proposal types to get the voting options. | ||
|
||
### Votes | ||
|
||
As mentioned above [multiple choice proposal](#multiple-choice-proposal) will introduce an additional vote option: `SPAM`. | ||
|
||
This vote option will be supported by all proposal types. | ||
At the end of the voting period, if a proposal is voted as `SPAM`, it fails and its deposit is burned. | ||
|
||
`SPAM` differs from the `No with Veto` vote as its threshold is dynamic. | ||
A proposal is marked as `SPAM` when the total of weighted votes for all options is lower than the amount of weighted vote on `SPAM` | ||
(`spam` > `option_one + option_two + option_three + option_four` = proposal marked as spam). | ||
julienrbrt marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+114
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is tallying done at the end of the VP (EndBlock)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Tallying stays in end blocker in this ADR. Future improvements will attempt to make it lazy. However we do need methods in the tally interface for that. |
||
This allows clear spam proposals to be marked as spam easily, even with low participation from validators. | ||
|
||
To avoid voters wrongfully voting down a proposal as `SPAM`, voters will be slashed `x`% (default 0%) of their voting stake if they voted `SPAM` on a proposal that wasn't a spam proposal. The parameter allows to incentivise voters to only vote `SPAM` on actual spam proposals and not use `SPAM` as a way to vote `No with Veto` with a different threshold. | ||
julienrbrt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
This leads to the addition of the following governance parameter in [`v1.Params`][5]: | ||
|
||
```protobuf | ||
// burn_spam_amount defines the percentage of the voting stake that will be burned if a voter votes SPAM on a proposal that is not marked as SPAM. | ||
string burn_spam_amount = 8 [(cosmos_proto.scalar) = "cosmos.Dec"]; | ||
``` | ||
|
||
Additionally, the current vote options will be aliased to better accommodate the multiple choice proposal: | ||
|
||
```protobuf | ||
// VoteOption enumerates the valid vote options for a given governance proposal. | ||
enum VoteOption { | ||
option allow_alias = true; | ||
|
||
// VOTE_OPTION_UNSPECIFIED defines a no-op vote option. | ||
VOTE_OPTION_UNSPECIFIED = 0; | ||
// VOTE_OPTION_ONE defines the first proposal vote option. | ||
VOTE_OPTION_ONE = 1; | ||
// VOTE_OPTION_YES defines the yes proposal vote option. | ||
VOTE_OPTION_YES = 1; | ||
// VOTE_OPTION_TWO defines the second proposal vote option. | ||
VOTE_OPTION_TWO = 2; | ||
// VOTE_OPTION_ABSTAIN defines the abstain proposal vote option. | ||
VOTE_OPTION_ABSTAIN = 2; | ||
// VOTE_OPTION_THREE defines the third proposal vote option. | ||
VOTE_OPTION_THREE = 3; | ||
// VOTE_OPTION_NO defines the no proposal vote option. | ||
VOTE_OPTION_NO = 3; | ||
// VOTE_OPTION_FOUR defines the fourth proposal vote option. | ||
VOTE_OPTION_FOUR = 4; | ||
// VOTE_OPTION_NO_WITH_VETO defines the no with veto proposal vote option. | ||
VOTE_OPTION_NO_WITH_VETO = 4; | ||
// VOTE_OPTION_SPAM defines the spam proposal vote option. | ||
VOTE_OPTION_SPAM = 5; | ||
} | ||
``` | ||
|
||
The order does not change for a standard proposal (1 = yes, 2 = abstain, 3 = no, 4 = no with veto as it was) and the aliased enum can be used interchangeably. | ||
|
||
Updating vote options means updating [`v1.TallyResult`][5] as well. | ||
|
||
#### Tally | ||
|
||
Due to the vote option change, each proposal can have the same tallying method. | ||
|
||
However, chains may want to change the tallying function (weighted vote per voting power) of `x/gov` for a different algorithm (using a quadratic function on the voter stake, for instance). | ||
|
||
The custom tallying function can be passed to the `x/gov` keeper with the following interface: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is extremely useful |
||
|
||
```go | ||
type Tally interface{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need to think a bit more of what exactly we need but at a high level you should be able to process a custom result with the voting power that has voted so far There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
could you elaborate here? I dont understand what you mean |
||
// to be decided | ||
|
||
// Calculate calculates the tally result | ||
Calculate(proposal v1.Proposal, govKeeper GovKeeper, stakingKeeper StakingKeeper) govv1.TallyResult | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Tally could be simplified to I assume Tally could be a module's keeper implementing this interface and already holding within it other keepers besides gov. Passing GovKeeper in instead of having it in the tally implementer avoids circular dependencies between the tally interface and gov itself. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The GovKeeper does not have the other keepers it uses public. |
||
// IsAccepted returns true if the proposal passes/is accepted | ||
IsAccepted() bool | ||
// BurnDeposit returns true if the proposal deposit should be burned | ||
BurnDeposit() bool | ||
} | ||
``` | ||
|
||
## Consequences | ||
|
||
Changing voting possibilities has a direct consequence for the clients. Clients, like Keplr or Mintscan, need to implement logic for multiple choice proposals. | ||
|
||
That logic consists of querying multiple choice proposals vote mapping their vote options. | ||
|
||
### Backwards Compatibility | ||
|
||
Legacy proposals (`v1beta1`) endpoints will not be supporting the new proposal types. | ||
|
||
Voting on a gov v1 proposal having a different type than [`standard` or `expedited`](#proposals) via the `v1beta1` will not be supported. | ||
This is already the case for the expedited proposals. | ||
|
||
### Positive | ||
|
||
* Extended governance features | ||
* Extended governance customization | ||
|
||
### Negative | ||
|
||
* Increase gov wiring complexity | ||
|
||
### Neutral | ||
|
||
* Increases the number of parameters available | ||
|
||
## Further Discussions | ||
|
||
This ADR starts the `x/gov` overhaul for the `cosmossdk.io/x/gov` v1.0.0 release. | ||
Further internal improvements of `x/gov` will happen soon after, in order to simplify its state management and making gov calculation in a more "lazy"-fashion. | ||
|
||
Those improvements may change the tallying api. | ||
|
||
* https://github.com/cosmos/cosmos-sdk/issues/16270 | ||
|
||
## References | ||
|
||
* [https://github.com/cosmos/cosmos-sdk/issues/16270][1] | ||
* [https://github.com/cosmos/cosmos-sdk/issues/17781][2] | ||
* [https://github.com/cosmos/cosmos-sdk/issues/14403][3] | ||
* [https://github.com/decentralists/DAO/issues/28][4] | ||
|
||
[1]: https://grants.osmosis.zone/blog/rfp-cosmos-sdk-governance-module-improvements | ||
[2]: https://github.com/cosmos/cosmos-sdk/issues/17781 | ||
[3]: https://github.com/cosmos/cosmos-sdk/issues/14403 | ||
[4]: https://github.com/decentralists/DAO/issues/28 | ||
[5]: https://buf.build/cosmos/cosmos-sdk/docs/main:cosmos.gov.v1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the intended use case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A chain can decide how they want to use it (given the possibility to restrict proposers).
However, we can think of it as a way to alleviate governance burden for recurring proposals or any boring proposals where no governance discussion needs to be involved. In the case of Osmosis, that would be periodic incentive updates for example. I have no example for the Hub however 😅