Skip to main content
Version: Next

ADR 069: x/gov modularity, multiple choice and optimistic proposals

Changelog

  • 2023-11-17: Initial draft (@julienrbrt, @tac0turtle)

Status

ACCEPTED - Implemented

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. This second iteration of 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. 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:

// 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 to support optimistic proposals:

// optimistic_authorized_addreses is an optional governance parameter that limits the authorized accounts that can submit optimistic proposals
repeated string optimistic_authorized_addreses = 17 [(cosmos_proto.scalar) = "cosmos.AddressString"];

// 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.

The number of voting options will be limited to a maximum of 4. A new vote option SPAM will be added and distinguished from 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. This is done in order to simplify the proposal submission and allow defining the voting options directly.

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. A multiple choice proposal will be stored as a v1.Proposal. A query will be available for multiple choice proposal types to get the voting options.

Votes

As mentioned above 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). 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.

This leads to the addition of the following governance parameter in v1.Params:

// 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:

// 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 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 config:

type CalculateVoteResultsAndVotingPowerFn func(
ctx context.Context,
keeper Keeper,
proposalID uint64,
validators map[string]v1.ValidatorGovInfo,
) (totalVoterPower math.LegacyDec, results map[v1.VoteOption]math.LegacyDec, err error)

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 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.

References