github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-042-group-module.md (about)

     1  # ADR 042: Group Module
     2  
     3  ## Changelog
     4  
     5  * 2020/04/09: Initial Draft
     6  
     7  ## Status
     8  
     9  Draft
    10  
    11  ## Abstract
    12  
    13  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.
    14  
    15  ## Context
    16  
    17  The legacy amino multi-signature mechanism of the Cosmos SDK has certain limitations:
    18  
    19  * Key rotation is not possible, although this can be solved with [account rekeying](adr-034-account-rekeying.md).
    20  * Thresholds can't be changed.
    21  * UX is cumbersome for non-technical users ([#5661](https://github.com/cosmos/cosmos-sdk/issues/5661)).
    22  * It requires `legacy_amino` sign mode ([#8141](https://github.com/cosmos/cosmos-sdk/issues/8141)).
    23  
    24  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.
    25  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.
    26  
    27  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.
    28  
    29  ## Decision
    30  
    31  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.
    32  
    33  ### Group
    34  
    35  A group is a composition of accounts with associated weights. It is not
    36  an account and doesn't have a balance. It doesn't in and of itself have any
    37  sort of voting or decision weight.
    38  Group members can create proposals and vote on them through group accounts using different decision policies.
    39  
    40  It has an `admin` account which can manage members in the group, update the group
    41  metadata and set a new admin.
    42  
    43  ```protobuf
    44  message GroupInfo {
    45  
    46      // group_id is the unique ID of this group.
    47      uint64 group_id = 1;
    48  
    49      // admin is the account address of the group's admin.
    50      string admin = 2;
    51  
    52      // metadata is any arbitrary metadata to attached to the group.
    53      bytes metadata = 3;
    54  
    55      // version is used to track changes to a group's membership structure that
    56      // would break existing proposals. Whenever a member weight has changed,
    57      // or any member is added or removed, the version is incremented and will
    58      // invalidate all proposals from older versions.
    59      uint64 version = 4;
    60  
    61      // total_weight is the sum of the group members' weights.
    62      string total_weight = 5;
    63  }
    64  ```
    65  
    66  ```protobuf
    67  message GroupMember {
    68  
    69      // group_id is the unique ID of the group.
    70      uint64 group_id = 1;
    71  
    72      // member is the member data.
    73      Member member = 2;
    74  }
    75  
    76  // Member represents a group member with an account address,
    77  // non-zero weight and metadata.
    78  message Member {
    79  
    80      // address is the member's account address.
    81      string address = 1;
    82  
    83      // weight is the member's voting weight that should be greater than 0.
    84      string weight = 2;
    85  
    86      // metadata is any arbitrary metadata to attached to the member.
    87      bytes metadata = 3;
    88  }
    89  ```
    90  
    91  ### Group Account
    92  
    93  A group account is an account associated with a group and a decision policy.
    94  A group account does have a balance.
    95  
    96  Group accounts are abstracted from groups because a single group may have
    97  multiple decision policies for different types of actions. Managing group
    98  membership separately from decision policies results in the least overhead
    99  and keeps membership consistent across different policies. The pattern that
   100  is recommended is to have a single master group account for a given group,
   101  and then to create separate group accounts with different decision policies
   102  and delegate the desired permissions from the master account to
   103  those "sub-accounts" using the [`x/authz` module](adr-030-authz-module.md).
   104  
   105  ```protobuf
   106  message GroupAccountInfo {
   107  
   108      // address is the group account address.
   109      string address = 1;
   110  
   111      // group_id is the ID of the Group the GroupAccount belongs to.
   112      uint64 group_id = 2;
   113  
   114      // admin is the account address of the group admin.
   115      string admin = 3;
   116  
   117      // metadata is any arbitrary metadata of this group account.
   118      bytes metadata = 4;
   119  
   120      // version is used to track changes to a group's GroupAccountInfo structure that
   121      // invalidates active proposal from old versions.
   122      uint64 version = 5;
   123  
   124      // decision_policy specifies the group account's decision policy.
   125      google.protobuf.Any decision_policy = 6 [(cosmos_proto.accepts_interface) = "cosmos.group.v1.DecisionPolicy"];
   126  }
   127  ```
   128  
   129  Similarly to a group admin, a group account admin can update its metadata, decision policy or set a new group account admin.
   130  
   131  A group account can also be an admin or a member of a group.
   132  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.
   133  
   134  ### Decision Policy
   135  
   136  A decision policy is the mechanism by which members of a group can vote on
   137  proposals.
   138  
   139  All decision policies should have a minimum and maximum voting window.
   140  The minimum voting window is the minimum duration that must pass in order
   141  for a proposal to potentially pass, and it may be set to 0. The maximum voting
   142  window is the maximum time that a proposal may be voted on and executed if
   143  it reached enough support before it is closed.
   144  Both of these values must be less than a chain-wide max voting window parameter.
   145  
   146  We define the `DecisionPolicy` interface that all decision policies must implement:
   147  
   148  ```go
   149  type DecisionPolicy interface {
   150  	codec.ProtoMarshaler
   151  
   152  	ValidateBasic() error
   153  	GetTimeout() types.Duration
   154  	Allow(tally Tally, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error)
   155  	Validate(g GroupInfo) error
   156  }
   157  
   158  type DecisionPolicyResult struct {
   159  	Allow bool
   160  	Final bool
   161  }
   162  ```
   163  
   164  #### Threshold decision policy
   165  
   166  A threshold decision policy defines a minimum support votes (_yes_), based on a tally
   167  of voter weights, for a proposal to pass. For
   168  this decision policy, abstain and veto are treated as no support (_no_).
   169  
   170  ```protobuf
   171  message ThresholdDecisionPolicy {
   172  
   173      // threshold is the minimum weighted sum of support votes for a proposal to succeed.
   174      string threshold = 1;
   175  
   176      // voting_period is the duration from submission of a proposal to the end of voting period
   177      // Within this period, votes and exec messages can be submitted.
   178      google.protobuf.Duration voting_period = 2 [(gogoproto.nullable) = false];
   179  }
   180  ```
   181  
   182  ### Proposal
   183  
   184  Any member of a group can submit a proposal for a group account to decide upon.
   185  A proposal consists of a set of `sdk.Msg`s that will be executed if the proposal
   186  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.
   187  
   188  Internally, a proposal also tracks:
   189  
   190  * its current `Status`: submitted, closed or aborted
   191  * its `Result`: unfinalized, accepted or rejected
   192  * its `VoteState` in the form of a `Tally`, which is calculated on new votes and when executing the proposal.
   193  
   194  ```protobuf
   195  // Tally represents the sum of weighted votes.
   196  message Tally {
   197      option (gogoproto.goproto_getters) = false;
   198  
   199      // yes_count is the weighted sum of yes votes.
   200      string yes_count = 1;
   201  
   202      // no_count is the weighted sum of no votes.
   203      string no_count = 2;
   204  
   205      // abstain_count is the weighted sum of abstainers.
   206      string abstain_count = 3;
   207  
   208      // veto_count is the weighted sum of vetoes.
   209      string veto_count = 4;
   210  }
   211  ```
   212  
   213  ### Voting
   214  
   215  Members of a group can vote on proposals. There are four choices to choose while voting - yes, no, abstain and veto. Not
   216  all decision policies will support them. Votes can contain some optional metadata.
   217  In the current implementation, the voting window begins as soon as a proposal
   218  is submitted.
   219  
   220  Voting internally updates the proposal `VoteState` as well as `Status` and `Result` if needed.
   221  
   222  ### Executing Proposals
   223  
   224  Proposals will not be automatically executed by the chain in this current design,
   225  but rather a user must submit a `Msg/Exec` transaction to attempt to execute the
   226  proposal based on the current votes and decision policy. A future upgrade could
   227  automate this and have the group account (or a fee granter) pay.
   228  
   229  #### Changing Group Membership
   230  
   231  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.
   232  
   233  ### Notes on current implementation
   234  
   235  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.
   236  
   237  #### ORM
   238  
   239  The [ORM package](https://github.com/cosmos/cosmos-sdk/discussions/9156) defines tables, sequences and secondary indexes which are used in the group module.
   240  
   241  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`.
   242  
   243  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`.
   244  
   245  Proposals are stored as part of the `proposalTable` using the `Proposal` type. The `proposal_id` is an auto-increment integer.
   246  
   247  Votes are stored in the `voteTable`. The primary key is based on the vote's `proposal_id` and `voter` account address.
   248  
   249  #### ADR-033 to route proposal messages
   250  
   251  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.
   252  
   253  ## Consequences
   254  
   255  ### Positive
   256  
   257  * Improved UX for multi-signature accounts allowing key rotation and custom decision policies.
   258  
   259  ### Negative
   260  
   261  ### Neutral
   262  
   263  * 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.
   264  * The current implementation of the group module uses the ORM package.
   265  
   266  ## Further Discussions
   267  
   268  * Convergence of `/group` and `x/gov` as both support proposals and voting: https://github.com/cosmos/cosmos-sdk/discussions/9066
   269  * `x/group` possible future improvements:
   270      * Execute proposals on submission (https://github.com/regen-network/regen-ledger/issues/288)
   271      * Withdraw a proposal (https://github.com/regen-network/cosmos-modules/issues/41)
   272      * Make `Tally` more flexible and support non-binary choices
   273  
   274  ## References
   275  
   276  * Initial specification:
   277      * https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#group-module
   278      * [#5236](https://github.com/cosmos/cosmos-sdk/pull/5236)
   279  * Proposal to add `x/group` into the Cosmos SDK: [#7633](https://github.com/cosmos/cosmos-sdk/issues/7633)