Skip to content
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

ADR 030: Authorization Module #7105

Merged
merged 23 commits into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/architecture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@ Please add a entry below in your Pull Request for an ADR.
- [ADR 025: IBC Passive Channels](./adr-025-ibc-passive-channels.md)
- [ADR 026: IBC Client Recovery Mechanisms](./adr-026-ibc-client-recovery-mechanisms.md)
- [ADR 027: Deterministic Protobuf Serialization](./adr-027-deterministic-protobuf-serialization.md)
- [ADR 030: Message Authorization Module](./adr-030-msg-auth-module.md)
218 changes: 218 additions & 0 deletions docs/architecture/adr-030-msg-auth-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
# ADR 030: Msg Authorization Module
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ADR is about authorization and delegation.
How about renaming to Delegation Module, Auth Delegation Module? ... and respectively other types below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original name was msg delegation but people complained that it was confusing because of validator delegation. So we chose authorization as an alternative

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so how about the other proposals, Auth Delegation Module?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People basically didn't want us to use the word "delegation" at all as I recall.


## Changelog

- 2019-11-06: Initial Draft
- 2020-08-18: Updated Draft

## Status

Accepted

## Context


## Decision

We will create a module named `msg_authorization`. The `msg_authorization` module provides support for
granting arbitrary capabilities from one account (the granter) to another account (the grantee). Capabilities
must be granted for a particular type of `sdk.Msg` one by one using an implementation
of `Capability`.

### Types

Capabilities determine exactly what action is delegated. They are extensible
and can be defined for any sdk.Msg type even outside of the module where the Msg is defined.

#### Capability

```go
type Capability interface {
// MsgType returns the type of Msg's that this capability can accept
MsgType() sdk.Msg
aaronc marked this conversation as resolved.
Show resolved Hide resolved
// Accept determines whether this grant allows the provided action, and if
// so provides an upgraded capability grant
Accept(msg sdk.Msg, block abci.Header) (allow bool, updated Capability, delete bool)
}
```

For example a `SendCapability` like this is defined for `MsgSend` that takes
a `SpendLimit` and updates it down to zero:

```go
type SendCapability struct {
// SpendLimit specifies the maximum amount of tokens that can be spent
// by this capability and will be updated as tokens are spent. If it is
// empty, there is no spend limit and any amount of coins can be spent.
SpendLimit sdk.Coins
}

func (cap SendCapability) MsgType() sdk.Msg {
return &bank.MsgSend{}
}

func (cap SendCapability) Accept(msg sdk.Msg, block abci.Header) (allow bool, updated Capability, delete bool) {
switch msg := msg.(type) {
case bank.MsgSend:
left, invalid := cap.SpendLimit.SafeSub(msg.Amount)
if invalid {
return false, nil, false
}
if left.IsZero() {
return true, nil, true
}
return true, SendCapability{SpendLimit: left}, false
}
return false, nil, false
}
```

A different type of capability for `MsgSend` could be implemented
using the `Capability` interface with no need to change the underlying
`bank` module.

### Messages

#### `MsgGrant`

```go
// MsgGrant grants the provided capability to the grantee on the granter's
// account with the provided expiration time.
type MsgGrant struct {
Granter sdk.AccAddress `json:"granter"`
Grantee sdk.AccAddress `json:"grantee"`
Capability Capability `json:"capability"`
// Expiration specifies the expiration time of the grant
Expiration time.Time `json:"expiration"`
}
```

#### MsgRevoke

```go
// MsgRevoke revokes any capability with the provided sdk.Msg type on the
// granter's account with that has been granted to the grantee.
type MsgRevoke struct {
Granter sdk.AccAddress `json:"granter"`
Grantee sdk.AccAddress `json:"grantee"`
// CapabilityMsgType is the full protobuf message name for the Msg type
// whose capability is being revoked.
CapabilityMsgType string `json:"capability_msg_type"`
}
aaronc marked this conversation as resolved.
Show resolved Hide resolved
```

#### MsgExecDelegated

```go
// MsgExecDelegated attempts to execute the provided messages using
// capabilities granted to the grantee. Each message should have only
// one signer corresponding to the granter of the capability.
type MsgExecDelegated struct {
Grantee sdk.AccAddress `json:"grantee"`
Msgs []sdk.Msg `json:"msg"`
}
```

### Keeper

#### Constructor: `NewKeeper(storeKey sdk.StoreKey, cdc *codec.Codec, router sdk.Router) Keeper`

The message delegation keeper receives a reference to the baseapp's `Router` in order
to dispatch delegated messages.

#### `DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.Msg) sdk.Result`

`DispatchActions` attempts to execute the provided messages via capability
grants from the message signer to the grantee.

#### `Grant(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, capability Capability, expiration time.Time)`

Grants the provided capability to the grantee on the granter's account with the provided expiration
time. If there is an existing capability grant for the same `sdk.Msg` type, this grant
overwrites that.

#### `Revoke(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType sdk.Msg)`

Revokes any capability for the provided message type granted to the grantee by the granter.

#### `GetCapability(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType sdk.Msg) (cap Capability, expiration time.Time)`

Returns any `Capability` (or `nil`), with the expiration time, granted to the grantee by the granter for the provided msg type.

### CLI

#### `--send-as` Flag

When a CLI user wants to run a transaction as another user using `MsgExecDelegated`, they
can use the `--send-as` flag. For instance `gaiacli tx gov vote 1 yes --from mykey --send-as cosmos3thsdgh983egh823`
would send a transaction like this:

```go
MsgExecDelegated {
Grantee: mykey,
Msgs: []sdk.Msg{
MsgVote {
ProposalID: 1,
Voter: cosmos3thsdgh983egh823
Option: Yes
}
}
}
```
#### `tx grant <grantee> <capability> --from <granter>`

This CLI command will send a `MsgGrant` tx. `capability` should be encoded as
JSON on the CLI.

#### `tx revoke <grantee> <capability-msg-type> --from <granter>`

This CLI command will send a `MsgRevoke` tx. `capability-msg-type` should be encoded as
JSON on the CLI.

### Built-in Capabilities

#### `SendCapability`

```go
type SendCapability struct {
// SpendLimit specifies the maximum amount of tokens that can be spent
// by this capability and will be updated as tokens are spent. If it is
// empty, there is no spend limit and any amount of coins can be spent.
SpendLimit sdk.Coins
}
```

#### `GenericCapability`

```go
// GenericCapability grants the permission to execute any transaction of the provided
// sdk.Msg type without restrictions
type GenericCapability struct {
// MsgType is the type of Msg this capability grant allows
MsgType sdk.Msg
}
```

## Status

Accepted

## Consequences

### Positive

- Users will be able to authorize arbitrary permissions on their accounts to other
users, simplifying key management for some use cases
- The solution is more generic than previously considered approaches and the
`Capability` interface approach can be extended to cover other use cases by
SDK users

### Negative

### Neutral

## References

- Initial Hackatom implementation: https://github.com/cosmos-gaians/cosmos-sdk/tree/hackatom/x/delegation
- Post-Hackatom spec: https://gist.github.com/aaronc/b60628017352df5983791cad30babe56#delegation-module
- B-Harvest subkeys spec: https://github.com/cosmos/cosmos-sdk/issues/4480