github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-050-sign-mode-textual-annex1.md (about)

     1  # ADR 050: SIGN_MODE_TEXTUAL: Annex 1 Value Renderers
     2  
     3  ## Changelog
     4  
     5  * Dec 06, 2021: Initial Draft
     6  * Feb 07, 2022: Draft read and concept-ACKed by the Ledger team.
     7  * Dec 01, 2022: Remove `Object: ` prefix on Any header screen.
     8  * Dec 13, 2022: Sign over bytes hash when bytes length > 32.
     9  * Mar 27, 2023: Update `Any` value renderer to omit message header screen.
    10  
    11  ## Status
    12  
    13  Accepted. Implementation started. Small value renderers details still need to be polished.
    14  
    15  ## Abstract
    16  
    17  This Annex describes value renderers, which are used for displaying Protobuf values in a human-friendly way using a string array.
    18  
    19  ## Value Renderers
    20  
    21  Value Renderers describe how values of different Protobuf types should be encoded as a string array. Value renderers can be formalized as a set of bijective functions `func renderT(value T) []string`, where `T` is one of the below Protobuf types for which this spec is defined.
    22  
    23  ### Protobuf `number`
    24  
    25  * Applies to:
    26      * protobuf numeric integer types (`int{32,64}`, `uint{32,64}`, `sint{32,64}`, `fixed{32,64}`, `sfixed{32,64}`)
    27      * strings whose `customtype` is `github.com/cosmos/cosmos-sdk/types.Int` or `github.com/cosmos/cosmos-sdk/types.Dec`
    28      * bytes whose `customtype` is `github.com/cosmos/cosmos-sdk/types.Int` or `github.com/cosmos/cosmos-sdk/types.Dec`
    29  * Trailing decimal zeroes are always removed
    30  * Formatting with `'`s for every three integral digits.
    31  * Usage of `.` to denote the decimal delimiter.
    32  
    33  #### Examples
    34  
    35  * `1000` (uint64) -> `1'000`
    36  * `"1000000.00"` (string representing a Dec) -> `1'000'000`
    37  * `"1000000.10"` (string representing a Dec) -> `1'000'000.1`
    38  
    39  ### `coin`
    40  
    41  * Applies to `cosmos.base.v1beta1.Coin`.
    42  * Denoms are converted to `display` denoms using `Metadata` (if available). **This requires a state query**. The definition of `Metadata` can be found in the [bank protobuf definition](https://buf.build/cosmos/cosmos-sdk/docs/main:cosmos.bank.v1beta1#cosmos.bank.v1beta1.Metadata). If the `display` field is empty or nil, then we do not perform any denom conversion.
    43  * Amounts are converted to `display` denom amounts and rendered as `number`s above
    44      * We do not change the capitalization of the denom. In practice, `display` denoms are stored in lowercase in state (e.g. `10 atom`), however they are often showed in UPPERCASE in everyday life (e.g. `10 ATOM`). Value renderers keep the case used in state, but we may recommend chains changing the denom metadata to be uppercase for better user display.
    45  * One space between the denom and amount (e.g. `10 atom`).
    46  * In the future, IBC denoms could maybe be converted to DID/IIDs, if we can find a robust way for doing this (ex. `cosmos:cosmos:hub:bank:denom:atom`)
    47  
    48  #### Examples
    49  
    50  * `1000000000uatom` -> `["1'000 atom"]`, because atom is the metadata's display denom.
    51  
    52  ### `coins`
    53  
    54  * an array of `coin` is display as the concatenation of each `coin` encoded as the specification above, the joined together with the delimiter `", "` (a comma and a space, no quotes around).
    55  * the list of coins is ordered by unicode code point of the display denom: `A-Z` < `a-z`. For example, the string `aAbBcC` would be sorted `ABCabc`.
    56      * if the coins list had 0 items in it then it'll be rendered as `zero`
    57  
    58  ### Example
    59  
    60  * `["3cosm", "2000000uatom"]` -> `2 atom, 3 COSM` (assuming the display denoms are `atom` and `COSM`)
    61  * `["10atom", "20Acoin"]` -> `20 Acoin, 10 atom` (assuming the display denoms are `atom` and `Acoin`)
    62  * `[]` -> `zero` 
    63  
    64  ### `repeated`
    65  
    66  * Applies to all `repeated` fields, except `cosmos.tx.v1beta1.TxBody#Messages`, which has a particular encoding (see [ADR-050](./adr-050-sign-mode-textual.md)).
    67  * A repeated type has the following template:
    68  
    69  ```
    70  <field_name>: <int> <field_kind>
    71  <field_name> (<index>/<int>): <value rendered 1st line>
    72  <optional value rendered in the next lines>
    73  <field_name> (<index>/<int>): <value rendered 1st line>
    74  <optional value rendered in the next lines>
    75  End of <field_name>.
    76  ```
    77  
    78  where:
    79  
    80  * `field_name` is the Protobuf field name of the repeated field
    81  * `field_kind`:
    82      * if the type of the repeated field is a message, `field_kind` is the message name
    83      * if the type of the repeated field is an enum, `field_kind` is the enum name
    84      * in any other case, `field_kind` is the protobuf primitive type (e.g. "string" or "bytes")
    85  * `int` is the length of the array
    86  * `index` is one based index of the repeated field
    87  
    88  #### Examples
    89  
    90  Given the proto definition:
    91  
    92  ```protobuf
    93  message AllowedMsgAllowance {
    94    repeated string allowed_messages = 1;
    95  }
    96  ```
    97  
    98  and initializing with:
    99  
   100  ```go
   101  x := []AllowedMsgAllowance{"cosmos.bank.v1beta1.MsgSend", "cosmos.gov.v1.MsgVote"}
   102  ```
   103  
   104  we have the following value-rendered encoding:
   105  
   106  ```
   107  Allowed messages: 2 strings
   108  Allowed messages (1/2): cosmos.bank.v1beta1.MsgSend
   109  Allowed messages (2/2): cosmos.gov.v1.MsgVote
   110  End of Allowed messages
   111  ```
   112  
   113  ### `message`
   114  
   115  * Applies to all Protobuf messages that do not have a custom encoding.
   116  * Field names follow [sentence case](https://en.wiktionary.org/wiki/sentence_case)
   117      * replace each `_` with a space
   118      * capitalize first letter of the sentence
   119  * Field names are ordered by their Protobuf field number
   120  * Screen title is the field name, and screen content is the value.
   121  * Nesting:
   122      * if a field contains a nested message, we value-render the underlying message using the template:
   123  
   124    ```
   125    <field_name>: <1st line of value-rendered message>
   126    > <lines 2-n of value-rendered message>             // Notice the `>` prefix.
   127    ```
   128  
   129      * `>` character is used to denote nesting. For each additional level of nesting, add `>`.
   130  
   131  #### Examples
   132  
   133  Given the following Protobuf messages:
   134  
   135  ```protobuf
   136  enum VoteOption {
   137    VOTE_OPTION_UNSPECIFIED = 0;
   138    VOTE_OPTION_YES = 1;
   139    VOTE_OPTION_ABSTAIN = 2;
   140    VOTE_OPTION_NO = 3;
   141    VOTE_OPTION_NO_WITH_VETO = 4;
   142  }
   143  
   144  message WeightedVoteOption {
   145    VoteOption option = 1;
   146    string     weight = 2 [(cosmos_proto.scalar) = "cosmos.Dec"];
   147  }
   148  
   149  message Vote {
   150    uint64 proposal_id = 1;
   151    string voter       = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
   152    reserved 3;
   153    repeated WeightedVoteOption options = 4;
   154  }
   155  ```
   156  
   157  we get the following encoding for the `Vote` message:
   158  
   159  ```
   160  Vote object
   161  > Proposal id: 4
   162  > Voter: cosmos1abc...def
   163  > Options: 2 WeightedVoteOptions
   164  > Options (1/2): WeightedVoteOption object
   165  >> Option: VOTE_OPTION_YES
   166  >> Weight: 0.7
   167  > Options (2/2): WeightedVoteOption object
   168  >> Option: VOTE_OPTION_NO
   169  >> Weight: 0.3
   170  > End of Options
   171  ```
   172  
   173  ### Enums
   174  
   175  * Show the enum variant name as string.
   176  
   177  #### Examples
   178  
   179  See example above with `message Vote{}`.
   180  
   181  ### `google.protobuf.Any`
   182  
   183  * Applies to `google.protobuf.Any`
   184  * Rendered as:
   185  
   186  ```
   187  <type_url>
   188  > <value rendered underlying message>
   189  ```
   190  
   191  There is however one exception: when the underlying message is a Protobuf message that does not have a custom encoding, then the message header screen is omitted, and one level of indentation is removed.
   192  
   193  Messages that have a custom encoding, including `google.protobuf.Timestamp`, `google.protobuf.Duration`, `google.protobuf.Any`, `cosmos.base.v1beta1.Coin`, and messages that have an app-defined custom encoding, will preserve their header and indentation level.
   194  
   195  #### Examples
   196  
   197  Message header screen is stripped, one-level of indentation removed:
   198  ```
   199  /cosmos.gov.v1.Vote
   200  > Proposal id: 4
   201  > Vote: cosmos1abc...def
   202  > Options: 2 WeightedVoteOptions
   203  > Options (1/2): WeightedVoteOption object
   204  >> Option: Yes
   205  >> Weight: 0.7
   206  > Options (2/2): WeightedVoteOption object
   207  >> Option: No
   208  >> Weight: 0.3
   209  > End of Options
   210  ```
   211  
   212  Message with custom encoding:
   213  ```
   214  /cosmos.base.v1beta1.Coin
   215  > 10uatom
   216  ```
   217  
   218  ### `google.protobuf.Timestamp`
   219  
   220  Rendered using [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) (a
   221  simplification of ISO 8601), which is the current recommendation for portable
   222  time values. The rendering always uses "Z" (UTC) as the timezone. It uses only
   223  the necessary fractional digits of a second, omitting the fractional part
   224  entirely if the timestamp has no fractional seconds. (The resulting timestamps
   225  are not automatically sortable by standard lexicographic order, but we favor
   226  the legibility of the shorter string.)
   227  
   228  #### Examples
   229  
   230  The timestamp with 1136214245 seconds and 700000000 nanoseconds is rendered
   231  as `2006-01-02T15:04:05.7Z`.
   232  The timestamp with 1136214245 seconds and zero nanoseconds is rendered
   233  as `2006-01-02T15:04:05Z`.
   234  
   235  ### `google.protobuf.Duration`
   236  
   237  The duration proto expresses a raw number of seconds and nanoseconds.
   238  This will be rendered as longer time units of days, hours, and minutes,
   239  plus any remaining seconds, in that order.
   240  Leading and trailing zero-quantity units will be omitted, but all
   241  units in between nonzero units will be shown, e.g. ` 3 days, 0 hours, 0 minutes, 5 seconds`.
   242  
   243  Even longer time units such as months or years are imprecise.
   244  Weeks are precise, but not commonly used - `91 days` is more immediately
   245  legible than `13 weeks`.  Although `days` can be problematic,
   246  e.g. noon to noon on subsequent days can be 23 or 25 hours depending on
   247  daylight savings transitions, there is significant advantage in using
   248  strict 24-hour days over using only hours (e.g. `91 days` vs `2184 hours`).
   249  
   250  When nanoseconds are nonzero, they will be shown as fractional seconds,
   251  with only the minimum number of digits, e.g `0.5 seconds`.
   252  
   253  A duration of exactly zero is shown as `0 seconds`.
   254  
   255  Units will be given as singular (no trailing `s`) when the quantity is exactly one,
   256  and will be shown in plural otherwise.
   257  
   258  Negative durations will be indicated with a leading minus sign (`-`).
   259  
   260  Examples:
   261  
   262  * `1 day`
   263  * `30 days`
   264  * `-1 day, 12 hours`
   265  * `3 hours, 0 minutes, 53.025 seconds`
   266  
   267  ### bytes
   268  
   269  * Bytes of length shorter or equal to 35 are rendered in hexadecimal, all capital letters, without the `0x` prefix.
   270  * Bytes of length greater than 35 are hashed using SHA256. The rendered text is `SHA-256=`, followed by the 32-byte hash, in hexadecimal, all capital letters, without the `0x` prefix.
   271  * The hexadecimal string is finally separated into groups of 4 digits, with a space `' '` as separator. If the bytes length is odd, the 2 remaining hexadecimal characters are at the end.
   272  
   273  The number 35 was chosen because it is the longest length where the hashed-and-prefixed representation is longer than the original data directly formatted, using the 3 rules above. More specifically:
   274  - a 35-byte array will have 70 hex characters, plus 17 space characters, resulting in 87 characters.
   275  - byte arrays starting from length 36 will be be hashed to 32 bytes, which is 64 hex characters plus 15 spaces, and with the `SHA-256=` prefix, it takes 87 characters.
   276  Also, secp256k1 public keys have length 33, so their Textual representation is not their hashed value, which we would like to avoid.
   277  
   278  Note: Data longer than 35 bytes are not rendered in a way that can be inverted. See ADR-050's [section about invertability](./adr-050-sign-mode-textual.md#invertible-rendering) for a discussion.
   279  
   280  #### Examples
   281  
   282  Inputs are displayed as byte arrays.
   283  
   284  * `[0]`: `00`
   285  * `[0,1,2]`: `0001 02`
   286  * `[0,1,2,..,34]`: `0001 0203 0405 0607 0809 0A0B 0C0D 0E0F 1011 1213 1415 1617 1819 1A1B 1C1D 1E1F 2021 22`
   287  * `[0,1,2,..,35]`: `SHA-256=5D7E 2D9B 1DCB C85E 7C89 0036 A2CF 2F9F E7B6 6554 F2DF 08CE C6AA 9C0A 25C9 9C21`
   288  
   289  ### address bytes
   290  
   291  We currently use `string` types in protobuf for addresses so this may not be needed, but if any address bytes are used in sign mode textual they should be rendered with bech32 formatting
   292  
   293  ### strings
   294  
   295  Strings are rendered as-is.
   296  
   297  ### Default Values
   298  
   299  * Default Protobuf values for each field are skipped.
   300  
   301  #### Example
   302  
   303  ```protobuf
   304  message TestData {
   305    string signer = 1;
   306    string metadata = 2;
   307  }
   308  ```
   309  
   310  ```go
   311  myTestData := TestData{
   312    Signer: "cosmos1abc"
   313  }
   314  ```
   315  
   316  We get the following encoding for the `TestData` message:
   317  
   318  ```
   319  TestData object
   320  > Signer: cosmos1abc
   321  ```
   322  
   323  ### bool
   324  
   325  Boolean values are rendered as `True` or `False`.
   326  
   327  ### [ABANDONED] Custom `msg_title` instead of Msg `type_url`
   328  
   329  _This paragraph is in the Annex for informational purposes only, and will be removed in a next update of the ADR._
   330  
   331  <details>
   332    <summary>Click to see abandoned idea.</summary>
   333  
   334  * all protobuf messages to be used with `SIGN_MODE_TEXTUAL` CAN have a short title associated with them that can be used in format strings whenever the type URL is explicitly referenced via the `cosmos.msg.v1.textual.msg_title` Protobuf message option.
   335  * if this option is not specified for a Msg, then the Protobuf fully qualified name will be used.
   336  
   337  ```protobuf
   338  message MsgSend {
   339    option (cosmos.msg.v1.textual.msg_title) = "bank send coins";
   340  }
   341  ```
   342  
   343  * they MUST be unique per message, per chain
   344  
   345  #### Examples
   346  
   347  * `cosmos.gov.v1.MsgVote` -> `governance v1 vote`
   348  
   349  #### Best Pratices
   350  
   351  We recommend to use this option only for `Msg`s whose Protobuf fully qualified name can be hard to understand. As such, the two examples above (`MsgSend` and `MsgVote`) are not good examples to be used with `msg_title`. We still allow `msg_title` for chains who might have `Msg`s with complex or non-obvious names.
   352  
   353  In those cases, we recommend to drop the version (e.g. `v1`) in the string if there's only one version of the module on chain. This way, the bijective mapping can figure out which message each string corresponds to. If multiple Protobuf versions of the same module exist on the same chain, we recommend keeping the first `msg_title` with version, and the second `msg_title` with version (e.g. `v2`):
   354  
   355  * `mychain.mymodule.v1.MsgDo` -> `mymodule do something`
   356  * `mychain.mymodule.v2.MsgDo` -> `mymodule v2 do something`
   357  
   358  </details>