github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-020-protobuf-transaction-encoding.md (about) 1 # ADR 020: Protocol Buffer Transaction Encoding 2 3 ## Changelog 4 5 * 2020 March 06: Initial Draft 6 * 2020 March 12: API Updates 7 * 2020 April 13: Added details on interface `oneof` handling 8 * 2020 April 30: Switch to `Any` 9 * 2020 May 14: Describe public key encoding 10 * 2020 June 08: Store `TxBody` and `AuthInfo` as bytes in `SignDoc`; Document `TxRaw` as broadcast and storage type. 11 * 2020 August 07: Use ADR 027 for serializing `SignDoc`. 12 * 2020 August 19: Move sequence field from `SignDoc` to `SignerInfo`, as discussed in [#6966](https://github.com/cosmos/cosmos-sdk/issues/6966). 13 * 2020 September 25: Remove `PublicKey` type in favor of `secp256k1.PubKey`, `ed25519.PubKey` and `multisig.LegacyAminoPubKey`. 14 * 2020 October 15: Add `GetAccount` and `GetAccountWithHeight` methods to the `AccountRetriever` interface. 15 * 2021 Feb 24: The Cosmos SDK does not use Tendermint's `PubKey` interface anymore, but its own `cryptotypes.PubKey`. Updates to reflect this. 16 * 2021 May 3: Rename `clientCtx.JSONMarshaler` to `clientCtx.JSONCodec`. 17 * 2021 June 10: Add `clientCtx.Codec: codec.Codec`. 18 19 ## Status 20 21 Accepted 22 23 ## Context 24 25 This ADR is a continuation of the motivation, design, and context established in 26 [ADR 019](./adr-019-protobuf-state-encoding.md), namely, we aim to design the 27 Protocol Buffer migration path for the client-side of the Cosmos SDK. 28 29 Specifically, the client-side migration path primarily includes tx generation and 30 signing, message construction and routing, in addition to CLI & REST handlers and 31 business logic (i.e. queriers). 32 33 With this in mind, we will tackle the migration path via two main areas, txs and 34 querying. However, this ADR solely focuses on transactions. Querying should be 35 addressed in a future ADR, but it should build off of these proposals. 36 37 Based on detailed discussions ([\#6030](https://github.com/cosmos/cosmos-sdk/issues/6030) 38 and [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078)), the original 39 design for transactions was changed substantially from an `oneof` /JSON-signing 40 approach to the approach described below. 41 42 ## Decision 43 44 ### Transactions 45 46 Since interface values are encoded with `google.protobuf.Any` in state (see [ADR 019](adr-019-protobuf-state-encoding.md)), 47 `sdk.Msg`s are encoding with `Any` in transactions. 48 49 One of the main goals of using `Any` to encode interface values is to have a 50 core set of types which is reused by apps so that 51 clients can safely be compatible with as many chains as possible. 52 53 It is one of the goals of this specification to provide a flexible cross-chain transaction 54 format that can serve a wide variety of use cases without breaking client 55 compatibility. 56 57 In order to facilitate signing, transactions are separated into `TxBody`, 58 which will be re-used by `SignDoc` below, and `signatures`: 59 60 ```protobuf 61 // types/types.proto 62 package cosmos_sdk.v1; 63 64 message Tx { 65 TxBody body = 1; 66 AuthInfo auth_info = 2; 67 // A list of signatures that matches the length and order of AuthInfo's signer_infos to 68 // allow connecting signature meta information like public key and signing mode by position. 69 repeated bytes signatures = 3; 70 } 71 72 // A variant of Tx that pins the signer's exact binary represenation of body and 73 // auth_info. This is used for signing, broadcasting and verification. The binary 74 // `serialize(tx: TxRaw)` is stored in Tendermint and the hash `sha256(serialize(tx: TxRaw))` 75 // becomes the "txhash", commonly used as the transaction ID. 76 message TxRaw { 77 // A protobuf serialization of a TxBody that matches the representation in SignDoc. 78 bytes body = 1; 79 // A protobuf serialization of an AuthInfo that matches the representation in SignDoc. 80 bytes auth_info = 2; 81 // A list of signatures that matches the length and order of AuthInfo's signer_infos to 82 // allow connecting signature meta information like public key and signing mode by position. 83 repeated bytes signatures = 3; 84 } 85 86 message TxBody { 87 // A list of messages to be executed. The required signers of those messages define 88 // the number and order of elements in AuthInfo's signer_infos and Tx's signatures. 89 // Each required signer address is added to the list only the first time it occurs. 90 // 91 // By convention, the first required signer (usually from the first message) is referred 92 // to as the primary signer and pays the fee for the whole transaction. 93 repeated google.protobuf.Any messages = 1; 94 string memo = 2; 95 int64 timeout_height = 3; 96 repeated google.protobuf.Any extension_options = 1023; 97 } 98 99 message AuthInfo { 100 // This list defines the signing modes for the required signers. The number 101 // and order of elements must match the required signers from TxBody's messages. 102 // The first element is the primary signer and the one which pays the fee. 103 repeated SignerInfo signer_infos = 1; 104 // The fee can be calculated based on the cost of evaluating the body and doing signature verification of the signers. This can be estimated via simulation. 105 Fee fee = 2; 106 } 107 108 message SignerInfo { 109 // The public key is optional for accounts that already exist in state. If unset, the 110 // verifier can use the required signer address for this position and lookup the public key. 111 google.protobuf.Any public_key = 1; 112 // ModeInfo describes the signing mode of the signer and is a nested 113 // structure to support nested multisig pubkey's 114 ModeInfo mode_info = 2; 115 // sequence is the sequence of the account, which describes the 116 // number of committed transactions signed by a given address. It is used to prevent 117 // replay attacks. 118 uint64 sequence = 3; 119 } 120 121 message ModeInfo { 122 oneof sum { 123 Single single = 1; 124 Multi multi = 2; 125 } 126 127 // Single is the mode info for a single signer. It is structured as a message 128 // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the future 129 message Single { 130 SignMode mode = 1; 131 } 132 133 // Multi is the mode info for a multisig public key 134 message Multi { 135 // bitarray specifies which keys within the multisig are signing 136 CompactBitArray bitarray = 1; 137 // mode_infos is the corresponding modes of the signers of the multisig 138 // which could include nested multisig public keys 139 repeated ModeInfo mode_infos = 2; 140 } 141 } 142 143 enum SignMode { 144 SIGN_MODE_UNSPECIFIED = 0; 145 146 SIGN_MODE_DIRECT = 1; 147 148 SIGN_MODE_TEXTUAL = 2; 149 150 SIGN_MODE_LEGACY_AMINO_JSON = 127; 151 } 152 ``` 153 154 As will be discussed below, in order to include as much of the `Tx` as possible 155 in the `SignDoc`, `SignerInfo` is separated from signatures so that only the 156 raw signatures themselves live outside of what is signed over. 157 158 Because we are aiming for a flexible, extensible cross-chain transaction 159 format, new transaction processing options should be added to `TxBody` as soon 160 those use cases are discovered, even if they can't be implemented yet. 161 162 Because there is coordination overhead in this, `TxBody` includes an 163 `extension_options` field which can be used for any transaction processing 164 options that are not already covered. App developers should, nevertheless, 165 attempt to upstream important improvements to `Tx`. 166 167 ### Signing 168 169 All of the signing modes below aim to provide the following guarantees: 170 171 * **No Malleability**: `TxBody` and `AuthInfo` cannot change once the transaction 172 is signed 173 * **Predictable Gas**: if I am signing a transaction where I am paying a fee, 174 the final gas is fully dependent on what I am signing 175 176 These guarantees give the maximum amount confidence to message signers that 177 manipulation of `Tx`s by intermediaries can't result in any meaningful changes. 178 179 #### `SIGN_MODE_DIRECT` 180 181 The "direct" signing behavior is to sign the raw `TxBody` bytes as broadcast over 182 the wire. This has the advantages of: 183 184 * requiring the minimum additional client capabilities beyond a standard protocol 185 buffers implementation 186 * leaving effectively zero holes for transaction malleability (i.e. there are no 187 subtle differences between the signing and encoding formats which could 188 potentially be exploited by an attacker) 189 190 Signatures are structured using the `SignDoc` below which reuses the serialization of 191 `TxBody` and `AuthInfo` and only adds the fields which are needed for signatures: 192 193 ```protobuf 194 // types/types.proto 195 message SignDoc { 196 // A protobuf serialization of a TxBody that matches the representation in TxRaw. 197 bytes body = 1; 198 // A protobuf serialization of an AuthInfo that matches the representation in TxRaw. 199 bytes auth_info = 2; 200 string chain_id = 3; 201 uint64 account_number = 4; 202 } 203 ``` 204 205 In order to sign in the default mode, clients take the following steps: 206 207 1. Serialize `TxBody` and `AuthInfo` using any valid protobuf implementation. 208 2. Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). 209 3. Sign the encoded `SignDoc` bytes. 210 4. Build a `TxRaw` and serialize it for broadcasting. 211 212 Signature verification is based on comparing the raw `TxBody` and `AuthInfo` 213 bytes encoded in `TxRaw` not based on any ["canonicalization"](https://github.com/regen-network/canonical-proto3) 214 algorithm which creates added complexity for clients in addition to preventing 215 some forms of upgradeability (to be addressed later in this document). 216 217 Signature verifiers do: 218 219 1. Deserialize a `TxRaw` and pull out `body` and `auth_info`. 220 2. Create a list of required signer addresses from the messages. 221 3. For each required signer: 222 * Pull account number and sequence from the state. 223 * Obtain the public key either from state or `AuthInfo`'s `signer_infos`. 224 * Create a `SignDoc` and serialize it using [ADR 027](./adr-027-deterministic-protobuf-serialization.md). 225 * Verify the signature at the same list position against the serialized `SignDoc`. 226 227 #### `SIGN_MODE_LEGACY_AMINO` 228 229 In order to support legacy wallets and exchanges, Amino JSON will be temporarily 230 supported transaction signing. Once wallets and exchanges have had a 231 chance to upgrade to protobuf based signing, this option will be disabled. In 232 the meantime, it is foreseen that disabling the current Amino signing would cause 233 too much breakage to be feasible. Note that this is mainly a requirement of the 234 Cosmos Hub and other chains may choose to disable Amino signing immediately. 235 236 Legacy clients will be able to sign a transaction using the current Amino 237 JSON format and have it encoded to protobuf using the REST `/tx/encode` 238 endpoint before broadcasting. 239 240 #### `SIGN_MODE_TEXTUAL` 241 242 As was discussed extensively in [\#6078](https://github.com/cosmos/cosmos-sdk/issues/6078), 243 there is a desire for a human-readable signing encoding, especially for hardware 244 wallets like the [Ledger](https://www.ledger.com) which display 245 transaction contents to users before signing. JSON was an attempt at this but 246 falls short of the ideal. 247 248 `SIGN_MODE_TEXTUAL` is intended as a placeholder for a human-readable 249 encoding which will replace Amino JSON. This new encoding should be even more 250 focused on readability than JSON, possibly based on formatting strings like 251 [MessageFormat](http://userguide.icu-project.org/formatparse/messages). 252 253 In order to ensure that the new human-readable format does not suffer from 254 transaction malleability issues, `SIGN_MODE_TEXTUAL` 255 requires that the _human-readable bytes are concatenated with the raw `SignDoc`_ 256 to generate sign bytes. 257 258 Multiple human-readable formats (maybe even localized messages) may be supported 259 by `SIGN_MODE_TEXTUAL` when it is implemented. 260 261 ### Unknown Field Filtering 262 263 Unknown fields in protobuf messages should generally be rejected by transaction 264 processors because: 265 266 * important data may be present in the unknown fields, that if ignored, will 267 cause unexpected behavior for clients 268 * they present a malleability vulnerability where attackers can bloat tx size 269 by adding random uninterpreted data to unsigned content (i.e. the master `Tx`, 270 not `TxBody`) 271 272 There are also scenarios where we may choose to safely ignore unknown fields 273 (https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-624400188) to 274 provide graceful forwards compatibility with newer clients. 275 276 We propose that field numbers with bit 11 set (for most use cases this is 277 the range of 1024-2047) be considered non-critical fields that can safely be 278 ignored if unknown. 279 280 To handle this we will need an unknown field filter that: 281 282 * always rejects unknown fields in unsigned content (i.e. top-level `Tx` and 283 unsigned parts of `AuthInfo` if present based on the signing mode) 284 * rejects unknown fields in all messages (including nested `Any`s) other than 285 fields with bit 11 set 286 287 This will likely need to be a custom protobuf parser pass that takes message bytes 288 and `FileDescriptor`s and returns a boolean result. 289 290 ### Public Key Encoding 291 292 Public keys in the Cosmos SDK implement the `cryptotypes.PubKey` interface. 293 We propose to use `Any` for protobuf encoding as we are doing with other interfaces (for example, in `BaseAccount.PubKey` and `SignerInfo.PublicKey`). 294 The following public keys are implemented: secp256k1, secp256r1, ed25519 and legacy-multisignature. 295 296 Ex: 297 298 ```protobuf 299 message PubKey { 300 bytes key = 1; 301 } 302 ``` 303 304 `multisig.LegacyAminoPubKey` has an array of `Any`'s member to support any 305 protobuf public key type. 306 307 Apps should only attempt to handle a registered set of public keys that they 308 have tested. The provided signature verification ante handler decorators will 309 enforce this. 310 311 ### CLI & REST 312 313 Currently, the REST and CLI handlers encode and decode types and txs via Amino 314 JSON encoding using a concrete Amino codec. Being that some of the types dealt with 315 in the client can be interfaces, similar to how we described in [ADR 019](./adr-019-protobuf-state-encoding.md), 316 the client logic will now need to take a codec interface that knows not only how 317 to handle all the types, but also knows how to generate transactions, signatures, 318 and messages. 319 320 ```go 321 type AccountRetriever interface { 322 GetAccount(clientCtx Context, addr sdk.AccAddress) (client.Account, error) 323 GetAccountWithHeight(clientCtx Context, addr sdk.AccAddress) (client.Account, int64, error) 324 EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error 325 GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) 326 } 327 328 type Generator interface { 329 NewTx() TxBuilder 330 NewFee() ClientFee 331 NewSignature() ClientSignature 332 MarshalTx(tx types.Tx) ([]byte, error) 333 } 334 335 type TxBuilder interface { 336 GetTx() sdk.Tx 337 338 SetMsgs(...sdk.Msg) error 339 GetSignatures() []sdk.Signature 340 SetSignatures(...sdk.Signature) 341 GetFee() sdk.Fee 342 SetFee(sdk.Fee) 343 GetMemo() string 344 SetMemo(string) 345 } 346 ``` 347 348 We then update `Context` to have new fields: `Codec`, `TxGenerator`, 349 and `AccountRetriever`, and we update `AppModuleBasic.GetTxCmd` to take 350 a `Context` which should have all of these fields pre-populated. 351 352 Each client method should then use one of the `Init` methods to re-initialize 353 the pre-populated `Context`. `tx.GenerateOrBroadcastTx` can be used to 354 generate or broadcast a transaction. For example: 355 356 ```go 357 import "github.com/spf13/cobra" 358 import "github.com/cosmos/cosmos-sdk/client" 359 import "github.com/cosmos/cosmos-sdk/client/tx" 360 361 func NewCmdDoSomething(clientCtx client.Context) *cobra.Command { 362 return &cobra.Command{ 363 RunE: func(cmd *cobra.Command, args []string) error { 364 clientCtx := ctx.InitWithInput(cmd.InOrStdin()) 365 msg := NewSomeMsg{...} 366 tx.GenerateOrBroadcastTx(clientCtx, msg) 367 }, 368 } 369 } 370 ``` 371 372 ## Future Improvements 373 374 ### `SIGN_MODE_TEXTUAL` specification 375 376 A concrete specification and implementation of `SIGN_MODE_TEXTUAL` is intended 377 as a near-term future improvement so that the ledger app and other wallets 378 can gracefully transition away from Amino JSON. 379 380 ### `SIGN_MODE_DIRECT_AUX` 381 382 (\*Documented as option (3) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933) 383 384 We could add a mode `SIGN_MODE_DIRECT_AUX` 385 to support scenarios where multiple signatures 386 are being gathered into a single transaction but the message composer does not 387 yet know which signatures will be included in the final transaction. For instance, 388 I may have a 3/5 multisig wallet and want to send a `TxBody` to all 5 389 signers to see who signs first. As soon as I have 3 signatures then I will go 390 ahead and build the full transaction. 391 392 With `SIGN_MODE_DIRECT`, each signer needs 393 to sign the full `AuthInfo` which includes the full list of all signers and 394 their signing modes, making the above scenario very hard. 395 396 `SIGN_MODE_DIRECT_AUX` would allow "auxiliary" signers to create their signature 397 using only `TxBody` and their own `PublicKey`. This allows the full list of 398 signers in `AuthInfo` to be delayed until signatures have been collected. 399 400 An "auxiliary" signer is any signer besides the primary signer who is paying 401 the fee. For the primary signer, the full `AuthInfo` is actually needed to calculate gas and fees 402 because that is dependent on how many signers and which key types and signing 403 modes they are using. Auxiliary signers, however, do not need to worry about 404 fees or gas and thus can just sign `TxBody`. 405 406 To generate a signature in `SIGN_MODE_DIRECT_AUX` these steps would be followed: 407 408 1. Encode `SignDocAux` (with the same requirement that fields must be serialized 409 in order): 410 411 ```protobuf 412 // types/types.proto 413 message SignDocAux { 414 bytes body_bytes = 1; 415 // PublicKey is included in SignDocAux : 416 // 1. as a special case for multisig public keys. For multisig public keys, 417 // the signer should use the top-level multisig public key they are signing 418 // against, not their own public key. This is to prevent against a form 419 // of malleability where a signature could be taken out of context of the 420 // multisig key that was intended to be signed for 421 // 2. to guard against scenario where configuration information is encoded 422 // in public keys (it has been proposed) such that two keys can generate 423 // the same signature but have different security properties 424 // 425 // By including it here, the composer of AuthInfo cannot reference the 426 // a public key variant the signer did not intend to use 427 PublicKey public_key = 2; 428 string chain_id = 3; 429 uint64 account_number = 4; 430 } 431 ``` 432 433 2. Sign the encoded `SignDocAux` bytes 434 3. Send their signature and `SignerInfo` to primary signer who will then 435 sign and broadcast the final transaction (with `SIGN_MODE_DIRECT` and `AuthInfo` 436 added) once enough signatures have been collected 437 438 ### `SIGN_MODE_DIRECT_RELAXED` 439 440 (_Documented as option (1)(a) in https://github.com/cosmos/cosmos-sdk/issues/6078#issuecomment-628026933_) 441 442 This is a variation of `SIGN_MODE_DIRECT` where multiple signers wouldn't need to 443 coordinate public keys and signing modes in advance. It would involve an alternate 444 `SignDoc` similar to `SignDocAux` above with fee. This could be added in the future 445 if client developers found the burden of collecting public keys and modes in advance 446 too burdensome. 447 448 ## Consequences 449 450 ### Positive 451 452 * Significant performance gains. 453 * Supports backward and forward type compatibility. 454 * Better support for cross-language clients. 455 * Multiple signing modes allow for greater protocol evolution 456 457 ### Negative 458 459 * `google.protobuf.Any` type URLs increase transaction size although the effect 460 may be negligible or compression may be able to mitigate it. 461 462 ### Neutral 463 464 ## References