变更日志
- 2021 年 12 月 06 日:初始草案
- 2022 年 02 月 07 日:Ledger 团队已阅读草案并在概念层面确认。
- 2022 年 12 月 01 日:移除 Any 标题屏上的
Object:前缀。 - 2022 年 12 月 13 日:当字节长度 > 32 时,对字节哈希进行签名。
- 2023 年 03 月 27 日:更新
Any值渲染器,省略消息头屏。
状态
已接受。实现已开始。小型值渲染器的细节仍需打磨。摘要
本附录描述了值渲染器,它们用于借助字符串数组以便于人类理解的方式显示 Protobuf 值。值渲染器
值渲染器描述了不同 Protobuf 类型的值应如何编码为字符串数组。值渲染器可以形式化为一组双射函数func renderT(value T) []string,其中 T 是下文本规范定义的某一种 Protobuf 类型。
Protobuf number
- 适用于:
- protobuf 数值整数类型(
int{32,64}、uint{32,64}、sint{32,64}、fixed{32,64}、sfixed{32,64}) customtype为github.com/cosmos/cosmos-sdk/types.Int或github.com/cosmos/cosmos-sdk/types.Dec的字符串customtype为github.com/cosmos/cosmos-sdk/types.Int或github.com/cosmos/cosmos-sdk/types.Dec的字节串
- protobuf 数值整数类型(
- 末尾的小数零始终会被移除
- 每三位整数数字之间使用
'进行格式化。 - 使用
.作为小数分隔符。
示例
1000(uint64) ->1'000"1000000.00"(表示 Dec 的字符串)->1'000'000"1000000.10"(表示 Dec 的字符串)->1'000'000.1
coin
- 适用于
cosmos.base.v1beta1.Coin。 - 面额会使用
Metadata转换为display面额(如果可用)。这需要进行状态查询。Metadata的定义可见于 bank protobuf definition。如果display字段为空或为 nil,则不执行任何面额转换。 - 数量会转换为
display面额对应的数量,并按上文的number方式渲染- 我们不会更改面额的大小写。实际中,状态里的
display面额通常以小写存储(例如10 atom),但在日常使用中常以大写展示(例如10 ATOM)。值渲染器会保留状态中使用的大小写,但我们可能会建议链将面额元数据改为大写,以获得更好的用户显示效果。
- 我们不会更改面额的大小写。实际中,状态里的
- 面额与数量之间使用一个空格(例如
10 atom)。 - 将来,IBC 面额或许可以转换为 DID/IID,前提是我们能找到一种稳健的实现方式(例如
cosmos:cosmos:hub:bank:denom:atom)
示例
1000000000uatom->[”1’000 atom”],因为 atom 是 metadata 的 display 面额。
coins
coin数组会显示为将每个coin按上述规范编码后的结果,再使用分隔符", "(逗号加空格,不包含引号)连接起来。- coin 列表按 display 面额的 Unicode 码点排序:
A-Z<a-z。例如,字符串aAbBcC排序后为ABCabc。- 如果 coins 列表包含 0 个元素,则会渲染为
zero
- 如果 coins 列表包含 0 个元素,则会渲染为
示例
[”3cosm”, “2000000uatom”]->2 atom, 3 COSM(假设 display 面额分别为atom和COSM)[”10atom”, “20Acoin”]->20 Acoin, 10 atom(假设 display 面额分别为atom和Acoin)[]->zero
repeated
- 适用于所有
repeated字段,但cosmos.tx.v1beta1.TxBody#Messages除外,它有专门的编码方式(见 ADR-050)。 - repeated 类型使用以下模板:
field_name是 repeated 字段的 Protobuf 字段名field_kind:- 如果 repeated 字段的类型是消息,
field_kind为消息名 - 如果 repeated 字段的类型是枚举,
field_kind为枚举名 - 其他情况中,
field_kind为 protobuf 原始类型(例如"string"或"bytes")
- 如果 repeated 字段的类型是消息,
int是数组长度index是 repeated 字段从 1 开始的索引
示例
给定如下 proto 定义:message
- 适用于所有没有自定义编码的 Protobuf 消息。
-
字段名遵循 句式大小写
- 将每个
_替换为空格 - 将句子的首字母大写
- 将每个
- 字段名按其 Protobuf 字段编号排序
- 屏幕标题是字段名,屏幕内容是值。
-
嵌套:
- 如果某个字段包含嵌套消息,我们会使用以下模板对底层消息进行值渲染:
>字符用于表示嵌套。每增加一层嵌套,就再增加一个>。
示例
给定以下 Protobuf 消息:Vote 消息的如下编码:
枚举
- 将枚举变体名显示为字符串。
示例
参见上文message Vote{} 的示例。
google.protobuf.Any
- 适用于
google.protobuf.Any - 渲染为:
google.protobuf.Timestamp、google.protobuf.Duration、google.protobuf.Any、cosmos.base.v1beta1.Coin,以及具有应用自定义编码的消息,将保留其头部和缩进级别。
示例
消息头屏被去除,并减少一级缩进:google.protobuf.Timestamp
使用 RFC 3339 进行渲染(它是 ISO 8601 的一种简化形式),这是当前针对可移植时间值的推荐格式。渲染结果始终使用 "Z"(UTC)作为时区。它只使用必要的秒小数位;如果时间戳没有小数秒,则完全省略小数部分。(得到的时间戳不会自动按标准字典序排序,但我们更倾向于更短字符串带来的可读性。)
示例
具有 1136214245 秒和 700000000 纳秒的时间戳会被渲染为2006-01-02T15:04:05.7Z。
具有 1136214245 秒和零纳秒的时间戳会被渲染为 2006-01-02T15:04:05Z。
google.protobuf.Duration
duration proto 表示原始的秒数和纳秒数。
它会依次渲染为更长的时间单位:天、小时和分钟,外加剩余的秒数。
前导和尾随的零数量单位会被省略,但位于非零单位之间的所有单位都会显示,例如 3 days, 0 hours, 0 minutes, 5 seconds。
更长的时间单位,例如月或年,并不精确。
周虽然是精确的,但并不常用,91 days 比 13 weeks 更容易立即理解。尽管 days 也可能带来问题,例如在夏令时切换时,相邻两天的中午到中午可能是 23 或 25 小时,但相比只使用小时(例如 91 days 对比 2184 hours),使用严格的 24 小时天数仍有明显优势。
当纳秒非零时,会显示为小数秒,并且只保留最少位数,例如 0.5 seconds。
恰好为零的 duration 显示为 0 seconds。
当数量恰好为一时,单位使用单数形式(不带结尾 s);其他情况使用复数形式。
负的 duration 会以前导负号(-)表示。
示例:
1 day30 days-1 day, 12 hours3 hours, 0 minutes, 53.025 seconds
bytes
- 长度小于或等于 35 的字节串会渲染为十六进制,全部使用大写字母,不带
0x前缀。 - 长度大于 35 的字节串会使用 SHA256 哈希。渲染文本为
SHA-256=,后接 32 字节哈希值,使用十六进制、全部大写字母、不带0x前缀。 - 最终还会将十六进制字符串按每 4 位分组,使用空格
' '作为分隔符。如果字节长度为奇数,则剩余的 2 个十六进制字符位于末尾。
- 35 字节数组会有 70 个十六进制字符,加上 17 个空格字符,总计 87 个字符。
- 从长度 36 开始的字节数组会被哈希为 32 字节,即 64 个十六进制字符加 15 个空格,再加上
SHA-256=前缀,总长度为 87 个字符。 此外,secp256k1 公钥长度为 33,因此它们的 Textual 表示不会是其哈希值,这正是我们希望避免的。
示例
输入以字节数组形式展示。[0]:00[0,1,2]:0001 02[0,1,2,..,34]:0001 0203 0405 0607 0809 0A0B 0C0D 0E0F 1011 1213 1415 1617 1819 1A1B 1C1D 1E1F 2021 22[0,1,2,..,35]:SHA-256=5D7E 2D9B 1DCB C85E 7C89 0036 A2CF 2F9F E7B6 6554 F2DF 08CE C6AA 9C0A 25C9 9C21
address bytes
我们目前在 protobuf 中使用string 类型表示地址,因此这可能并不需要;但如果在 sign mode textual 中使用任何地址字节,它们应当使用 bech32 格式进行渲染
strings
字符串按原样渲染。默认值
- 每个字段的 Protobuf 默认值都会被跳过。
示例
TestData 消息的如下编码:
bool
布尔值会渲染为True 或 False。
[已废弃] 使用自定义 msg_title 替代 Msg type_url
本段位于附录中,仅用于提供信息,将在 ADR 的下一次更新中移除。
Changelog
- Dec 06, 2021: Initial Draft
- Feb 07, 2022: Draft read and concept-ACKed by the Ledger team.
- Dec 01, 2022: Remove
Object:prefix on Any header screen. - Dec 13, 2022: Sign over bytes hash when bytes length > 32.
- Mar 27, 2023: Update
Anyvalue renderer to omit message header screen.
Status
Accepted. Implementation started. Small value renderers details still need to be polished.Abstract
This Annex describes value renderers, which are used for displaying Protobuf values in a human-friendly way using a string array.Value Renderers
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 functionsfunc renderT(value T) []string, where T is one of the below Protobuf types for which this spec is defined.
Protobuf number
- Applies to:
- protobuf numeric integer types (
int{32,64},uint{32,64},sint{32,64},fixed{32,64},sfixed{32,64}) - strings whose
customtypeisgithub.com/cosmos/cosmos-sdk/types.Intorgithub.com/cosmos/cosmos-sdk/types.Dec - bytes whose
customtypeisgithub.com/cosmos/cosmos-sdk/types.Intorgithub.com/cosmos/cosmos-sdk/types.Dec
- protobuf numeric integer types (
- Trailing decimal zeroes are always removed
- Formatting with
's for every three integral digits. - Usage of
.to denote the decimal delimiter.
Examples
1000(uint64) ->1'000"1000000.00"(string representing a Dec) ->1'000'000"1000000.10"(string representing a Dec) ->1'000'000.1
coin
- Applies to
cosmos.base.v1beta1.Coin. - Denoms are converted to
displaydenoms usingMetadata(if available). This requires a state query. The definition ofMetadatacan be found in the bank protobuf definition. If thedisplayfield is empty or nil, then we do not perform any denom conversion. - Amounts are converted to
displaydenom amounts and rendered asnumbers above- We do not change the capitalization of the denom. In practice,
displaydenoms 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.
- We do not change the capitalization of the denom. In practice,
- One space between the denom and amount (e.g.
10 atom). - 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)
Examples
1000000000uatom->["1'000 atom"], because atom is the metadata’s display denom.
coins
- an array of
coinis display as the concatenation of eachcoinencoded as the specification above, the joined together with the delimiter", "(a comma and a space, no quotes around). - the list of coins is ordered by unicode code point of the display denom:
A-Z<a-z. For example, the stringaAbBcCwould be sortedABCabc.- if the coins list had 0 items in it then it’ll be rendered as
zero
- if the coins list had 0 items in it then it’ll be rendered as
Example
["3cosm", "2000000uatom"]->2 atom, 3 COSM(assuming the display denoms areatomandCOSM)["10atom", "20Acoin"]->20 Acoin, 10 atom(assuming the display denoms areatomandAcoin)[]->zero
repeated
- Applies to all
repeatedfields, exceptcosmos.tx.v1beta1.TxBody#Messages, which has a particular encoding (see ADR-050). - A repeated type has the following template:
field_nameis the Protobuf field name of the repeated fieldfield_kind:- if the type of the repeated field is a message,
field_kindis the message name - if the type of the repeated field is an enum,
field_kindis the enum name - in any other case,
field_kindis the protobuf primitive type (e.g. “string” or “bytes”)
- if the type of the repeated field is a message,
intis the length of the arrayindexis one based index of the repeated field
Examples
Given the proto definition:message
- Applies to all Protobuf messages that do not have a custom encoding.
-
Field names follow sentence case
- replace each
_with a space - capitalize first letter of the sentence
- replace each
- Field names are ordered by their Protobuf field number
- Screen title is the field name, and screen content is the value.
-
Nesting:
- if a field contains a nested message, we value-render the underlying message using the template:
>character is used to denote nesting. For each additional level of nesting, add>.
Examples
Given the following Protobuf messages:Vote message:
Enums
- Show the enum variant name as string.
Examples
See example above withmessage Vote{}.
google.protobuf.Any
- Applies to
google.protobuf.Any - Rendered as:
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.
Examples
Message header screen is stripped, one-level of indentation removed:google.protobuf.Timestamp
Rendered using RFC 3339 (a
simplification of ISO 8601), which is the current recommendation for portable
time values. The rendering always uses “Z” (UTC) as the timezone. It uses only
the necessary fractional digits of a second, omitting the fractional part
entirely if the timestamp has no fractional seconds. (The resulting timestamps
are not automatically sortable by standard lexicographic order, but we favor
the legibility of the shorter string.)
Examples
The timestamp with 1136214245 seconds and 700000000 nanoseconds is rendered as2006-01-02T15:04:05.7Z.
The timestamp with 1136214245 seconds and zero nanoseconds is rendered
as 2006-01-02T15:04:05Z.
google.protobuf.Duration
The duration proto expresses a raw number of seconds and nanoseconds.
This will be rendered as longer time units of days, hours, and minutes,
plus any remaining seconds, in that order.
Leading and trailing zero-quantity units will be omitted, but all
units in between nonzero units will be shown, e.g. 3 days, 0 hours, 0 minutes, 5 seconds.
Even longer time units such as months or years are imprecise.
Weeks are precise, but not commonly used - 91 days is more immediately
legible than 13 weeks. Although days can be problematic,
e.g. noon to noon on subsequent days can be 23 or 25 hours depending on
daylight savings transitions, there is significant advantage in using
strict 24-hour days over using only hours (e.g. 91 days vs 2184 hours).
When nanoseconds are nonzero, they will be shown as fractional seconds,
with only the minimum number of digits, e.g 0.5 seconds.
A duration of exactly zero is shown as 0 seconds.
Units will be given as singular (no trailing s) when the quantity is exactly one,
and will be shown in plural otherwise.
Negative durations will be indicated with a leading minus sign (-).
Examples:
1 day30 days-1 day, 12 hours3 hours, 0 minutes, 53.025 seconds
bytes
- Bytes of length shorter or equal to 35 are rendered in hexadecimal, all capital letters, without the
0xprefix. - 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 the0xprefix. - 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.
- a 35-byte array will have 70 hex characters, plus 17 space characters, resulting in 87 characters.
- 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. Also, secp256k1 public keys have length 33, so their Textual representation is not their hashed value, which we would like to avoid.
Examples
Inputs are displayed as byte arrays.[0]:00[0,1,2]:0001 02[0,1,2,..,34]:0001 0203 0405 0607 0809 0A0B 0C0D 0E0F 1011 1213 1415 1617 1819 1A1B 1C1D 1E1F 2021 22[0,1,2,..,35]:SHA-256=5D7E 2D9B 1DCB C85E 7C89 0036 A2CF 2F9F E7B6 6554 F2DF 08CE C6AA 9C0A 25C9 9C21
address bytes
We currently usestring 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
strings
Strings are rendered as-is.Default Values
- Default Protobuf values for each field are skipped.
Example
TestData message:
bool
Boolean values are rendered asTrue or False.