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