-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add ADR-042 * Fix link * Small improvements * Update link * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Update docs/architecture/adr-042-group-module.md Co-authored-by: Robert Zaremba <robert@zaremba.ch> * Move orm to specific section * Update docs/architecture/adr-042-group-module.md * Update naming * Add concrete use cases * Rework ### Proposal * Rework Vote, Exec and implementation sections * Update to account for removal of ServiceMsg Co-authored-by: Alessio Treglia <alessio@tendermint.com> Co-authored-by: Robert Zaremba <robert@zaremba.ch> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
- Loading branch information
1 parent
e96839d
commit 6425825
Showing
1 changed file
with
277 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
# ADR 042: Group Module | ||
|
||
## Changelog | ||
|
||
- 2020/04/09: Initial Draft | ||
|
||
## Status | ||
|
||
Draft | ||
|
||
## Abstract | ||
|
||
This ADR defines the `x/group` module which allows the creation and management of on-chain multi-signature accounts and enables voting for message execution based on configurable decision policies. | ||
|
||
## Context | ||
|
||
The legacy amino multi-signature mechanism of the Cosmos SDK has certain limitations: | ||
- Key rotation is not possible, although this can be solved with [account rekeying](adr-034-account-rekeying.md). | ||
- Thresholds can't be changed. | ||
- UX is cumbersome for non-technical users ([#5661](https://github.com/cosmos/cosmos-sdk/issues/5661)). | ||
- It requires `legacy_amino` sign mode ([#8141](https://github.com/cosmos/cosmos-sdk/issues/8141)). | ||
|
||
While the group module is not meant to be a total replacement for the current multi-signature accounts, it provides a solution to the limitations described above, with a more flexible key management system where keys can be added, updated or removed, as well as configurable thresholds. | ||
It's meant to be used with other access control modules such as [`x/feegrant`](./adr-029-fee-grant-module.md) ans [`x/authz`](adr-030-authz-module.md) to simplify key management for individuals and organizations. | ||
|
||
The proof of concept of the group module can be found in https://github.com/regen-network/regen-ledger/tree/master/proto/regen/group/v1alpha1 and https://github.com/regen-network/regen-ledger/tree/master/x/group. | ||
|
||
## Decision | ||
|
||
We propose merging the `x/group` module with its supporting [ORM/Table Store package](https://github.com/regen-network/regen-ledger/tree/master/orm) ([#7098](https://github.com/cosmos/cosmos-sdk/issues/7098)) into the Cosmos SDK and continuing development here. There will be a dedicated ADR for the ORM package. | ||
|
||
### Group | ||
|
||
A group is a composition of accounts with associated weights. It is not | ||
an account and doesn't have a balance. It doesn't in and of itself have any | ||
sort of voting or decision weight. | ||
Group members can create proposals and vote on them through group accounts using different decision policies. | ||
|
||
It has an `admin` account which can manage members in the group, update the group | ||
metadata and set a new admin. | ||
|
||
```proto | ||
message GroupInfo { | ||
// group_id is the unique ID of this group. | ||
uint64 group_id = 1; | ||
// admin is the account address of the group's admin. | ||
string admin = 2; | ||
// metadata is any arbitrary metadata to attached to the group. | ||
bytes metadata = 3; | ||
// version is used to track changes to a group's membership structure that | ||
// would break existing proposals. Whenever a member weight has changed, | ||
// or any member is added or removed, the version is incremented and will | ||
// invalidate all proposals from older versions. | ||
uint64 version = 4; | ||
// total_weight is the sum of the group members' weights. | ||
string total_weight = 5; | ||
} | ||
``` | ||
|
||
```proto | ||
message GroupMember { | ||
// group_id is the unique ID of the group. | ||
uint64 group_id = 1; | ||
// member is the member data. | ||
Member member = 2; | ||
} | ||
// Member represents a group member with an account address, | ||
// non-zero weight and metadata. | ||
message Member { | ||
// address is the member's account address. | ||
string address = 1; | ||
// weight is the member's voting weight that should be greater than 0. | ||
string weight = 2; | ||
// metadata is any arbitrary metadata to attached to the member. | ||
bytes metadata = 3; | ||
} | ||
``` | ||
|
||
### Group Account | ||
|
||
A group account is an account associated with a group and a decision policy. | ||
A group account does have a balance. | ||
|
||
Group accounts are abstracted from groups because a single group may have | ||
multiple decision policies for different types of actions. Managing group | ||
membership separately from decision policies results in the least overhead | ||
and keeps membership consistent across different policies. The pattern that | ||
is recommended is to have a single master group account for a given group, | ||
and then to create separate group accounts with different decision policies | ||
and delegate the desired permissions from the master account to | ||
those "sub-accounts" using the [`x/authz` module](adr-030-authz-module.md). | ||
|
||
```proto | ||
message GroupAccountInfo { | ||
// address is the group account address. | ||
string address = 1; | ||
// group_id is the ID of the Group the GroupAccount belongs to. | ||
uint64 group_id = 2; | ||
// admin is the account address of the group admin. | ||
string admin = 3; | ||
// metadata is any arbitrary metadata of this group account. | ||
bytes metadata = 4; | ||
// version is used to track changes to a group's GroupAccountInfo structure that | ||
// invalidates active proposal from old versions. | ||
uint64 version = 5; | ||
// decision_policy specifies the group account's decision policy. | ||
google.protobuf.Any decision_policy = 6 [(cosmos_proto.accepts_interface) = "DecisionPolicy"]; | ||
} | ||
``` | ||
|
||
Similarly to a group admin, a group account admin can update its metadata, decision policy or set a new group account admin. | ||
|
||
A group account can also be an admin or a member of a group. | ||
For instance, a group admin could be another group account which could "elects" the members or it could be the same group that elects itself. | ||
|
||
### Decision Policy | ||
|
||
A decision policy is the mechanism by which members of a group can vote on | ||
proposals. | ||
|
||
All decision policies should have a minimum and maximum voting window. | ||
The minimum voting window is the minimum duration that must pass in order | ||
for a proposal to potentially pass, and it may be set to 0. The maximum voting | ||
window is the maximum time that a proposal may be voted on and executed if | ||
it reached enough support before it is closed. | ||
Both of these values must be less than a chain-wide max voting window parameter. | ||
|
||
We define the `DecisionPolicy` interface that all decision policies must implement: | ||
|
||
```go | ||
type DecisionPolicy interface { | ||
codec.ProtoMarshaler | ||
|
||
ValidateBasic() error | ||
GetTimeout() types.Duration | ||
Allow(tally Tally, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error) | ||
Validate(g GroupInfo) error | ||
} | ||
|
||
type DecisionPolicyResult struct { | ||
Allow bool | ||
Final bool | ||
} | ||
``` | ||
|
||
#### Threshold decision policy | ||
|
||
A threshold decision policy defines a minimum support votes (_yes_), based on a tally | ||
of voter weights, for a proposal to pass. For | ||
this decision policy, abstain and veto are treated as no support (_no_). | ||
|
||
```proto | ||
message ThresholdDecisionPolicy { | ||
// threshold is the minimum weighted sum of support votes for a proposal to succeed. | ||
string threshold = 1; | ||
// voting_period is the duration from submission of a proposal to the end of voting period | ||
// Within this period, votes and exec messages can be submitted. | ||
google.protobuf.Duration voting_period = 2 [(gogoproto.nullable) = false]; | ||
} | ||
``` | ||
|
||
### Proposal | ||
|
||
Any member of a group can submit a proposal for a group account to decide upon. | ||
A proposal consists of a set of `sdk.Msg`s that will be executed if the proposal | ||
passes as well as any metadata associated with the proposal. These `sdk.Msg`s get validated as part of the `Msg/CreateProposal` request validation. They should also have their signer set as the group account. | ||
|
||
Internally, a proposal also tracks: | ||
- its current `Status`: submitted, closed or aborted | ||
- its `Result`: unfinalized, accepted or rejected | ||
- its `VoteState` in the form of a `Tally`, which is calculated on new votes and when executing the proposal. | ||
|
||
```proto | ||
// Tally represents the sum of weighted votes. | ||
message Tally { | ||
option (gogoproto.goproto_getters) = false; | ||
// yes_count is the weighted sum of yes votes. | ||
string yes_count = 1; | ||
// no_count is the weighted sum of no votes. | ||
string no_count = 2; | ||
// abstain_count is the weighted sum of abstainers. | ||
string abstain_count = 3; | ||
// veto_count is the weighted sum of vetoes. | ||
string veto_count = 4; | ||
} | ||
``` | ||
|
||
### Voting | ||
|
||
Members of a group can vote on proposals. There are four choices to choose while voting - yes, no, abstain and veto. Not | ||
all decision policies will support them. Votes can contain some optional metadata. | ||
In the current implementation, the voting window begins as soon as a proposal | ||
is submitted. | ||
|
||
Voting internally updates the proposal `VoteState` as well as `Status` and `Result` if needed. | ||
|
||
### Executing Proposals | ||
|
||
Proposals will not be automatically executed by the chain in this current design, | ||
but rather a user must submit a `Msg/Exec` transaction to attempt to execute the | ||
proposal based on the current votes and decision policy. A future upgrade could | ||
automate this and have the group account (or a fee granter) pay. | ||
|
||
#### Changing Group Membership | ||
|
||
In the current implementation, updating a group or a group account after submitting a proposal will make it invalid. It will simply fail if someone calls `Msg/Exec` and will eventually be garbage collected. | ||
|
||
### Notes on current implementation | ||
|
||
This section outlines the current implementation used in the proof of concept of the group module but this could be subject to changes and iterated on. | ||
|
||
#### ORM | ||
|
||
The [ORM package](https://github.com/cosmos/cosmos-sdk/discussions/9156) defines tables, sequences and secondary indexes which are used in the group module. | ||
|
||
Groups are stored in state as part of a `groupTable`, the `group_id` being an auto-increment integer. Group members are stored in a `groupMemberTable`. | ||
|
||
Group accounts are stored in a `groupAccountTable`. The group account address is generated based on an auto-increment integer which is used to derive the group module `RootModuleKey` into a `DerivedModuleKey`, as stated in [ADR-033](adr-033-protobuf-inter-module-comm.md#modulekeys-and-moduleids). The group account is added as a new `ModuleAccount` through `x/auth`. | ||
|
||
Proposals are stored as part of the `proposalTable` using the `Proposal` type. The `proposal_id` is an auto-increment integer. | ||
|
||
Votes are stored in the `voteTable`. The primary key is based on the vote's `proposal_id` and `voter` account address. | ||
|
||
#### ADR-033 to route proposal messages | ||
|
||
Inter-module communication introduced by [ADR-033](adr-033-protobuf-inter-module-comm.md) can be used to route a proposal's messages using the `DerivedModuleKey` corresponding to the proposal's group account. | ||
|
||
## Consequences | ||
|
||
### Positive | ||
|
||
- Improved UX for multi-signature accounts allowing key rotation and custom decision policies. | ||
|
||
### Negative | ||
|
||
### Neutral | ||
|
||
- It uses ADR 033 so it will need to be implemented within the Cosmos SDK, but this doesn't imply necessarily any large refactoring of existing Cosmos SDK modules. | ||
- The current implementation of the group module uses the ORM package. | ||
|
||
## Further Discussions | ||
|
||
- Convergence of `/group` and `x/gov` as both support proposals and voting: https://github.com/cosmos/cosmos-sdk/discussions/9066 | ||
- `x/group` possible future improvements: | ||
- Execute proposals on submission (https://github.com/regen-network/regen-ledger/issues/288) | ||
- Withdraw a proposal (https://github.com/regen-network/cosmos-modules/issues/41) | ||
- Make `Tally` more flexible and support non-binary choices | ||
|
||
## References | ||
|
||
- Initial specification: | ||
- https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#group-module | ||
- [#5236](https://github.com/cosmos/cosmos-sdk/pull/5236) | ||
- Proposal to add `x/group` into the SDK: [#7633](https://github.com/cosmos/cosmos-sdk/issues/7633) |