github.com/Finschia/finschia-sdk@v0.48.1/docs/core/encoding.md (about) 1 <!-- 2 order: 6 3 --> 4 5 # Encoding 6 7 While encoding in the Cosmos SDK used to be mainly handled by `go-amino` codec, the Cosmos SDK is moving towards using `gogoprotobuf` for both state and client-side encoding. {synopsis} 8 9 ## Pre-requisite Readings 10 11 * [Anatomy of a Cosmos SDK application](../basics/app-anatomy.md) {prereq} 12 13 ## Encoding 14 15 The Cosmos SDK utilizes two binary wire encoding protocols, [Amino](https://github.com/tendermint/go-amino/) which is an object encoding specification and [Protocol Buffers](https://developers.google.com/protocol-buffers), a subset of Proto3 with an extension for 16 interface support. See the [Proto3 spec](https://developers.google.com/protocol-buffers/docs/proto3) 17 for more information on Proto3, which Amino is largely compatible with (but not with Proto2). 18 19 Due to Amino having significant performance drawbacks, being reflection-based, and 20 not having any meaningful cross-language/client support, Protocol Buffers, specifically 21 [gogoprotobuf](https://github.com/gogo/protobuf/), is being used in place of Amino. 22 Note, this process of using Protocol Buffers over Amino is still an ongoing process. 23 24 Binary wire encoding of types in the Cosmos SDK can be broken down into two main 25 categories, client encoding and store encoding. Client encoding mainly revolves 26 around transaction processing and signing, whereas store encoding revolves around 27 types used in state-machine transitions and what is ultimately stored in the Merkle 28 tree. 29 30 For store encoding, protobuf definitions can exist for any type and will typically 31 have an Amino-based "intermediary" type. Specifically, the protobuf-based type 32 definition is used for serialization and persistence, whereas the Amino-based type 33 is used for business logic in the state-machine where they may converted back-n-forth. 34 Note, the Amino-based types may slowly be phased-out in the future so developers 35 should take note to use the protobuf message definitions where possible. 36 37 In the `codec` package, there exists two core interfaces, `Marshaler` and `ProtoMarshaler`, 38 where the former encapsulates the current Amino interface except it operates on 39 types implementing the latter instead of generic `interface{}` types. 40 41 In addition, there exists two implementations of `Marshaler`. The first being 42 `AminoCodec`, where both binary and JSON serialization is handled via Amino. The 43 second being `ProtoCodec`, where both binary and JSON serialization is handled 44 via Protobuf. 45 46 This means that modules may use Amino or Protobuf encoding but the types must 47 implement `ProtoMarshaler`. If modules wish to avoid implementing this interface 48 for their types, they may use an Amino codec directly. 49 50 ### Amino 51 52 Every module uses an Amino codec to serialize types and interfaces. This codec typically 53 has types and interfaces registered in that module's domain only (e.g. messages), 54 but there are exceptions like `x/gov`. Each module exposes a `RegisterLegacyAminoCodec` function 55 that allows a user to provide a codec and have all the types registered. An application 56 will call this method for each necessary module. 57 58 Where there is no protobuf-based type definition for a module (see below), Amino 59 is used to encode and decode raw wire bytes to the concrete type or interface: 60 61 ```go 62 bz := keeper.cdc.MustMarshal(typeOrInterface) 63 keeper.cdc.MustUnmarshal(bz, &typeOrInterface) 64 ``` 65 66 Note, there are length-prefixed variants of the above functionality and this is 67 typically used for when the data needs to be streamed or grouped together 68 (e.g. `ResponseDeliverTx.Data`) 69 70 #### Authz authorizations and Gov proposals 71 72 Since authz's `MsgExec` and `MsgGrant` message types, as well as gov's `MsgSubmitProposal`, can contain different messages instances, it is important that developers 73 add the following code inside the `init` method of their module's `codec.go` file: 74 75 ```go 76 import ( 77 authzcodec "github.com/Finschia/finschia-sdk/x/authz/codec" 78 govcodec "github.com/Finschia/finschia-sdk/x/gov/codec" 79 ) 80 81 init() { 82 // Register all Amino interfaces and concrete types on the authz and gov Amino codec so that this can later be 83 // used to properly serialize MsgGrant, MsgExec and MsgSubmitProposal instances 84 RegisterLegacyAminoCodec(authzcodec.Amino) 85 RegisterLegacyAminoCodec(govcodec.Amino) 86 } 87 ``` 88 89 This will allow the `x/authz` module to properly serialize and de-serializes `MsgExec` instances using Amino, 90 which is required when signing this kind of messages using a Ledger. 91 92 ### Gogoproto 93 94 Modules are encouraged to utilize Protobuf encoding for their respective types. In the Cosmos 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). 95 96 ### Guidelines for protobuf message definitions 97 98 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: 99 100 * use `cosmos_proto.accepts_interface` to annote fields that accept interfaces 101 * pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` 102 * annotate interface implementations with `cosmos_proto.implements_interface` 103 * pass the same fully qualified name as `protoName` to `InterfaceRegistry.RegisterInterface` 104 105 ### Transaction Encoding 106 107 Another important use of Protobuf is the encoding and decoding of 108 [transactions](./transactions.md). Transactions are defined by the application or 109 the Cosmos SDK but are then passed to the underlying consensus engine to be relayed to 110 other peers. Since the underlying consensus engine is agnostic to the application, 111 the consensus engine accepts only transactions in the form of raw bytes. 112 113 * The `TxEncoder` object performs the encoding. 114 * The `TxDecoder` object performs the decoding. 115 116 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/types/tx_msg.go#L83-L87 117 118 A standard implementation of both these objects can be found in the [`auth` module](../../x/auth/spec/README.md): 119 120 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/decoder.go 121 122 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/x/auth/tx/encoder.go 123 124 See [ADR-020](../architecture/adr-020-protobuf-transaction-encoding.md) for details of how a transaction is encoded. 125 126 ### Interface Encoding and Usage of `Any` 127 128 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): 129 130 ```proto 131 message Profile { 132 // account is the account associated to a profile. 133 cosmos.auth.v1beta1.BaseAccount account = 1; 134 // bio is a short description of the account. 135 string bio = 4; 136 } 137 ``` 138 139 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? 140 141 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/auth/types/account.go#L307-L330 142 143 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: 144 145 ```protobuf 146 message Profile { 147 // account is the account associated to a profile. 148 google.protobuf.Any account = 1 [ 149 (cosmos_proto.accepts_interface) = "AccountI"; // Asserts that this field only accepts Go types implementing `AccountI`. It is purely informational for now. 150 ]; 151 // bio is a short description of the account. 152 string bio = 4; 153 } 154 ``` 155 156 To add an account inside a profile, we need to "pack" it inside an `Any` first, using `codectypes.NewAnyWithValue`: 157 158 ```go 159 var myAccount AccountI 160 myAccount = ... // Can be a BaseAccount, a ContinuousVestingAccount or any struct implementing `AccountI` 161 162 // Pack the account into an Any 163 accAny, err := codectypes.NewAnyWithValue(myAccount) 164 if err != nil { 165 return nil, err 166 } 167 168 // Create a new Profile with the any. 169 profile := Profile { 170 Account: accAny, 171 Bio: "some bio", 172 } 173 174 // We can then marshal the profile as usual. 175 bz, err := cdc.Marshal(profile) 176 jsonBz, err := cdc.MarshalJSON(profile) 177 ``` 178 179 To summarize, to encode an interface, you must 1/ pack the interface into an `Any` and 2/ marshal the `Any`. For convenience, the Cosmos 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). 180 181 The reverse operation of retrieving the concrete Go type from inside an `Any`, called "unpacking", is done with the `GetCachedValue()` on `Any`. 182 183 ```go 184 profileBz := ... // The proto-encoded bytes of a Profile, e.g. retrieved through gRPC. 185 var myProfile Profile 186 // Unmarshal the bytes into the myProfile struct. 187 err := cdc.Unmarshal(profilebz, &myProfile) 188 189 // Let's see the types of the Account field. 190 fmt.Printf("%T\n", myProfile.Account) // Prints "Any" 191 fmt.Printf("%T\n", myProfile.Account.GetCachedValue()) // Prints "BaseAccount", "ContinuousVestingAccount" or whatever was initially packed in the Any. 192 193 // Get the address of the accountt. 194 accAddr := myProfile.Account.GetCachedValue().(AccountI).GetAddress() 195 ``` 196 197 It is important to note that for `GetCachedValue()` to work, `Profile` (and any other structs embedding `Profile`) must implement the `UnpackInterfaces` method: 198 199 ```go 200 func (p *Profile) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { 201 if p.Account != nil { 202 var account AccountI 203 return unpacker.UnpackAny(p.Account, &account) 204 } 205 206 return nil 207 } 208 ``` 209 210 The `UnpackInterfaces` gets called recursively on all structs implementing this method, to allow all `Any`s to have their `GetCachedValue()` correctly populated. 211 212 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). 213 214 #### `Any` Encoding in the Cosmos SDK 215 216 The above `Profile` example is a fictive example used for educational purposes. In the Cosmos SDK, we use `Any` encoding in several places (non-exhaustive list): 217 218 * the `cryptotypes.PubKey` interface for encoding different types of public keys, 219 * the `sdk.Msg` interface for encoding different `Msg`s in a transaction, 220 * the `AccountI` interface for encodinig different types of accounts (similar to the above example) in the x/auth query responses, 221 * the `Evidencei` interface for encoding different types of evidences in the x/evidence module, 222 * the `AuthorizationI` interface for encoding different types of x/authz authorizations, 223 * the [`Validator`](https://github.com/cosmos/cosmos-sdk/blob/v0.42.5/x/staking/types/staking.pb.go#L306-L337) struct that contains information about a validator. 224 225 A real-life example of encoding the pubkey as `Any` inside the Validator struct in x/staking is shown in the following example: 226 227 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.42.1/x/staking/types/validator.go#L40-L61 228 229 ## FAQ 230 231 ### How to create modules using protobuf encoding 232 233 #### Defining module types 234 235 Protobuf types can be defined to encode: 236 237 * state 238 * [`Msg`s](../building-modules/messages-and-queries.md#messages) 239 * [Query services](../building-modules/query-services.md) 240 * [genesis](../building-modules/genesis.md) 241 242 #### Naming and conventions 243 244 We encourage developers to follow industry guidelines: [Protocol Buffers style guide](https://developers.google.com/protocol-buffers/docs/style) 245 and [Buf](https://buf.build/docs/style-guide), see more details in [ADR 023](../architecture/adr-023-protobuf-naming.md) 246 247 ### How to update modules to protobuf encoding 248 249 If modules do not contain any interfaces (e.g. `Account` or `Content`), then they 250 may simply migrate any existing types that 251 are encoded and persisted via their concrete Amino codec to Protobuf (see 1. for further guidelines) and accept a `Marshaler` as the codec which is implemented via the `ProtoCodec` 252 without any further customization. 253 254 However, if a module type composes an interface, it must wrap it in the `skd.Any` (from `/types` package) type. To do that, a module-level .proto file must use [`google.protobuf.Any`](https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/any.proto) for respective message type interface types. 255 256 For example, in the `x/evidence` module defines an `Evidence` interface, which is used by the `MsgSubmitEvidence`. The structure definition must use `sdk.Any` to wrap the evidence file. In the proto file we define it as follows: 257 258 ```protobuf 259 // proto/cosmos/evidence/v1beta1/tx.proto 260 261 message MsgSubmitEvidence { 262 string submitter = 1; 263 google.protobuf.Any evidence = 2 [(cosmos_proto.accepts_interface) = "Evidence"]; 264 } 265 ``` 266 267 The Cosmos SDK `codec.Codec` interface provides support methods `MarshalInterface` and `UnmarshalInterface` to easy encoding of state to `Any`. 268 269 Module should register interfaces using `InterfaceRegistry` which provides a mechanism for registering interfaces: `RegisterInterface(protoName string, iface interface{})` and implementations: `RegisterImplementations(iface interface{}, impls ...proto.Message)` that can be safely unpacked from Any, similarly to type registration with Amino: 270 271 +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc4/codec/types/interface_registry.go#L25-L66 272 273 In addition, an `UnpackInterfaces` phase should be introduced to deserialization to unpack interfaces before they're needed. Protobuf types that contain a protobuf `Any` either directly or via one of their members should implement the `UnpackInterfacesMessage` interface: 274 275 ```go 276 type UnpackInterfacesMessage interface { 277 UnpackInterfaces(InterfaceUnpacker) error 278 } 279 ``` 280 281 ## Next {hide} 282 283 Learn about [gRPC, REST and other endpoints](./grpc_rest.md) {hide}