Skip to content

Commit

Permalink
Docs: Anys Usage, Events & small cleanups (bp #8895) (#8911)
Browse files Browse the repository at this point in the history
Co-authored-by: Barrie Byron <barrie.byron@tendermint.com>
Co-authored-by: Alessio Treglia <alessio@tendermint.com>
(cherry picked from commit 1a4418b)
Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
  • Loading branch information
4 people authored Mar 17, 2021
1 parent a619b1d commit 16187a8
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 79 deletions.
2 changes: 1 addition & 1 deletion docs/basics/accounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ Also see the [`Addresses`](#addresses) section for more information.

`PubKey`s used in the Cosmos SDK are Protobuf messages and have the following methods:

+++ https://github.com/cosmos/cosmos-sdk/blob/master/crypto/types/types.go#L8-L17
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/crypto/types/types.go#L8-L17

- For `secp256k1` keys, the actual implementation can be found [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/keys/secp256k1/secp256k1.go).
- For `ed25519` keys, it can be found [here](https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/crypto/keys/ed25519/ed25519.go).
Expand Down
3 changes: 2 additions & 1 deletion docs/basics/app-anatomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ The `EncodingConfig` structure is the last important part of the `app.go` file.

Here are descriptions of what each of the four fields means:

- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces, which are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct which contains a `type_url` (the concrete type of the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method, which can be used to register the module's own interfaces and implementations.
- `InterfaceRegistry`: The `InterfaceRegistry` is used by the Protobuf codec to handle interfaces that are encoded and decoded (we also say "unpacked") using [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto). `Any` could be thought as a struct that contains a `type_url` (name of a concrete type implementing the interface) and a `value` (its encoded bytes). `InterfaceRegistry` provides a mechanism for registering interfaces and implementations that can be safely unpacked from `Any`. Each of the application's modules implements the `RegisterInterfaces` method that can be used to register the module's own interfaces and implementations.
- You can read more about Any in [ADR-19](../architecture/adr-019-protobuf-state-encoding.md#usage-of-any-to-encode-interfaces).
- To go more into details, the SDK uses an implementation of the Protobuf specification called [`gogoprotobuf`](https://github.com/gogo/protobuf). By default, the [gogo protobuf implementation of `Any`](https://godoc.org/github.com/gogo/protobuf/types) uses [global type registration](https://github.com/gogo/protobuf/blob/master/proto/properties.go#L540) to decode values packed in `Any` into concrete Go types. This introduces a vulnerability where any malicious module in the dependency tree could registry a type with the global protobuf registry and cause it to be loaded and unmarshaled by a transaction that referenced it in the `type_url` field. For more information, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md).
- `Marshaler`: The `Marshaler` is the default codec used throughout the SDK. It is composed of a `BinaryMarshaler` used to encode and decode state, and a `JSONMarshaler` used to output data to the users (for example in the [CLI](#cli)). By default, the SDK uses Protobuf as `Marshaler`.
- `TxConfig`: `TxConfig` defines an interface a client can utilize to generate an application-defined concrete transaction type. Currently, the SDK handles two transaction types: `SIGN_MODE_DIRECT` (which uses Protobuf binary as over-the-wire encoding) and `SIGN_MODE_LEGACY_AMINO_JSON` (which depends on Amino). Read more about transactions [here](../core/transactions.md).
Expand Down
2 changes: 1 addition & 1 deletion docs/building-modules/invariants.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ At its core, the `InvariantRegistry` is defined in the SDK as an interface:

Typically, this interface is implemented in the `keeper` of a specific module. The most used implementation of an `InvariantRegistry` can be found in the `crisis` module:

+++ https://github.com/cosmos/cosmos-sdk/blob/master/x/crisis/keeper/keeper.go#L50-L54
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/crisis/keeper/keeper.go#L50-L54

The `InvariantRegistry` is therefore typically instantiated by instantiating the `keeper` of the `crisis` module in the [application's constructor function](../basics/app-anatomy.md#constructor-function).

Expand Down
4 changes: 2 additions & 2 deletions docs/building-modules/simulator.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ Operations on the simulation are simulated using the full [transaction cycle](..

Shown below is how weights are set:

+++ https://github.com/cosmos/cosmos-sdk/blob/master/x/staking/simulation/operations.go#L18-L68
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/staking/simulation/operations.go#L18-L68

As you can see the weights are predefined in this case but there are options on how to override this behavior with different weights. One is allowing `*rand.Rand` to define a random weight for the operation, or you can inject your own predefined weights.
As you can see, the weights are predefined in this case. Options exist to override this behavior with different weights. One option is to use `*rand.Rand` to define a random weight for the operation, or you can inject your own predefined weights.

Here is how one can override the above package `simappparams`.

Expand Down
4 changes: 2 additions & 2 deletions docs/core/baseapp.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ is the canonical state of the application and the volatile states, `checkState`
are used to handle state transitions in-between the main state made during [`Commit`](#commit).

Internally, there is only a single `CommitMultiStore` which we refer to as the main or root state.
From this root state, we derive two volatile state through a mechanism called _store branching_ (performed by `CacheWrap` function).
From this root state, we derive two volatile states by using a mechanism called _store branching_ (performed by `CacheWrap` function).
The types can be illustrated as follows:

![Types](./baseapp_state_types.png)
Expand Down Expand Up @@ -333,7 +333,7 @@ The `AnteHandler` is theoretically optional, but still a very important componen
- Perform preliminary _stateful_ validity checks like ensuring signatures are valid or that the sender has enough funds to pay for fees.
- Play a role in the incentivisation of stakeholders via the collection of transaction fees.

`BaseApp` holds an `anteHandler` as paraemter, which is initialized in the [application's constructor](../basics/app-anatomy.md#application-constructor). The most widely used `anteHandler` today is that of the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth/ante/ante.go).
`BaseApp` holds an `anteHandler` as parameter that is initialized in the [application's constructor](../basics/app-anatomy.md#application-constructor). The most widely used `anteHandler` is the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/ante/ante.go).

Click [here](../basics/gas-fees.md#antehandler) for more on the `anteHandler`.

Expand Down
162 changes: 134 additions & 28 deletions docs/core/encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,141 @@ typically used for when the data needs to be streamed or grouped together

### Gogoproto

Modules are encouraged to utilize Protobuf encoding for their respective types.
Modules are encouraged to utilize Protobuf encoding for their respective types. In the SDK, we use the [Gogoproto](https://github.com/gogo/protobuf) specific implementation of the Protobuf spec that offers speed and DX improvements compared to the official [Google protobuf implementation](https://github.com/protocolbuffers/protobuf).

#### FAQ
### Guidelines for protobuf message definitions

In addition to [following official Protocol Buffer guidelines](https://developers.google.com/protocol-buffers/docs/proto3#simple), we recommend using these annotations in .proto files when dealing with interfaces:

- use `cosmos_proto.accepts_interface` to annote fields that accept interfaces
- pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface`
- annotate interface implementations with `cosmos_proto.implements_interface`
- pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface`

### Transaction Encoding

Another important use of Protobuf is the encoding and decoding of
[transactions](./transactions.md). Transactions are defined by the application or
the SDK but are then passed to the underlying consensus engine to be relayed to
other peers. Since the underlying consensus engine is agnostic to the application,
the consensus engine accepts only transactions in the form of raw bytes.

- The `TxEncoder` object performs the encoding.
- The `TxDecoder` object performs the decoding.

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/types/tx_msg.go#L83-L87

A standard implementation of both these objects can be found in the [`auth` module](../../x/auth/spec/README.md):

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/decoder.go

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/encoder.go

See [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) for details of how a transaction is encoded.

### Interface Encoding and Usage of `Any`

The Protobuf DSL is strongly typed, which can make inserting variable-typed fields difficult. Imagine we want to create a `Profile` protobuf message that serves as a wrapper over [an account](../basics/accounts.md):

```proto
message Profile {
// account is the account associated to a profile.
cosmos.auth.v1beta1.BaseAccount account = 1;
// bio is a short description of the account.
string bio = 4;
}
```

In this `Profile` example, we hardcoded `account` as a `BaseAccount`. However, there are several other types of [user accounts related to vesting](../../x/auth/spec/05_vesting.md), such as `BaseVestingAccount` or `ContinuousVestingAccount`. All of these accounts are different, but they all implement the `AccountI` interface. How would you create a `Profile` that allows all these types of accounts with an `account` field that accepts an `AccountI` interface?

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/types/account.go#L307-L330

In [ADR-019](../architecture/adr-019-protobuf-state-encoding.md), it has been decided to use [`Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto)s to encode interfaces in protobuf. An `Any` contains an arbitrary serialized message as bytes, along with a URL that acts as a globally unique identifier for and resolves to that message's type. This strategy allows us to pack arbitrary Go types inside protobuf messages. Our new `Profile` then looks like:

```protobuf
message Profile {
// account is the account associated to a profile.
google.protobuf.Any account = 1 [
(cosmos_proto.accepts_interface) = "AccountI"; // Asserts that this field only accepts Go types implementing `AccountI`. It is purely informational for now.
];
// bio is a short description of the account.
string bio = 4;
}
```

To add an account inside a profile, we need to "pack" it inside an `Any` first, using `codectypes.NewAnyWithValue`:

```go
var myAccount AccountI
myAccount = ... // Can be a BaseAccount, a ContinuousVestingAccount or any struct implementing `AccountI`

// Pack the account into an Any
accAny, err := codectypes.NewAnyWithValue(myAccount)
if err != nil {
return nil, err
}

// Create a new Profile with the any.
profile := Profile {
Account: accAny,
Bio: "some bio",
}

// We can then marshal the profile as usual.
bz, err := cdc.MarshalBinaryBare(profile)
jsonBz, err := cdc.MarshalJSON(profile)
```

To summarize, to encode an interface, you must 1/ pack the interface into an `Any` and 2/ marshal the `Any`. For convenience, the SDK provides a `MarshalInterface` method to bundle these two steps. Have a look at [a real-life example in the x/auth module](https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/keeper/keeper.go#L218-L221).

The reverse operation of retrieving the concrete Go type from inside an `Any`, called "unpacking", is done with the `GetCachedValue()` on `Any`.

```go
profileBz := ... // The proto-encoded bytes of a Profile, e.g. retrieved through gRPC.
var myProfile Profile
// Unmarshal the bytes into the myProfile struct.
err := cdc.UnmarshalBinaryBare(profilebz, &myProfile)

// Let's see the types of the Account field.
fmt.Printf("%T\n", myProfile.Account) // Prints "Any"
fmt.Printf("%T\n", myProfile.Account.GetCachedValue()) // Prints "BaseAccount", "ContinuousVestingAccount" or whatever was initially packed in the Any.

// Get the address of the accountt.
accAddr := myProfile.Account.GetCachedValue().(AccountI).GetAddress()
```

It is important to note that for `GetCachedValue()` to work, `Profile` (and any other structs embedding `Profile`) must implement the `UnpackInterfaces` method:

```go
func (p *Profile) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
if p.Account != nil {
var account AccountI
return unpacker.UnpackAny(p.Account, &account)
}

return nil
}
```

The `UnpackInterfaces` gets called recursively on all structs implementing this method, to allow all `Any`s to have their `GetCachedValue()` correctly populated.

For more information about interface encoding, and especially on `UnpackInterfaces` and how the `Any`'s `type_url` gets resolved using the `InterfaceRegistry`, please refer to [ADR-019](../architecture/adr-019-protobuf-state-encoding.md).

#### `Any` Encoding in the SDK

The above `Profile` example is a fictive example used for educational purposes. In the SDK, we use `Any` encoding in several places (non-exhaustive list):

- the `cryptotypes.PubKey` interface for encoding different types of public keys,
- the `sdk.Msg` interface for encoding different `Msg`s in a transaction,
- the `AccountI` interface for encodinig different types of accounts (similar to the above example) in the x/auth query responses,
- the `Evidencei` interface for encoding different types of evidences in the x/evidence module,
- the `AuthorizationI` interface for encoding different types of x/authz authorizations.

A real-life example of encoding the pubkey as `Any` inside the Validator struct in x/staking is shown in the following example:

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/staking/types/validator.go#L40-L61

## FAQ

1. How to create modules using protobuf encoding?

Expand Down Expand Up @@ -123,32 +255,6 @@ type UnpackInterfacesMessage interface {
}
```

#### Guidelines for protobuf message definitions

In addition to [following official guidelines](https://developers.google.com/protocol-buffers/docs/proto3#simple), we recommend to use these annotations in .proto files when dealing with interfaces:

- fields which accept interfaces should be annotated with `cosmos_proto.accepts_interface`
using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface`
- interface implementations should be annotated with `cosmos_proto.implements_interface`
using the same full-qualified name passed as `protoName` to `InterfaceRegistry.RegisterInterface`

#### Transaction Encoding

Another important use of Protobuf is the encoding and decoding of
[transactions](./transactions.md). Transactions are defined by the application or
the SDK, but passed to the underlying consensus engine in order to be relayed to
other peers. Since the underlying consensus engine is agnostic to the application,
it only accepts transactions in the form of raw bytes. The encoding is done by an
object called `TxEncoder` and the decoding by an object called `TxDecoder`.

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/types/tx_msg.go#L83-L87

A standard implementation of both these objects can be found in the [`auth` module](https://github.com/cosmos/cosmos-sdk/blob/master/x/auth):

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/decoder.go

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/encoder.go

## Next {hide}

Learn about [gRPC, REST and other endpoints](./grpc_rest.md) {hide}
Loading

0 comments on commit 16187a8

Please sign in to comment.