本文档介绍了为简化 protobuf 使用、专为 Cosmos SDK 应用开发者添加的多种 protobuf 标量类型。

Gogoproto

我们鼓励各模块为各自类型使用 Protobuf 编码。在 Cosmos SDK 中,我们使用 Gogoproto 对 Protobuf 规范的特定实现,相比官方的 Google protobuf 实现,它在性能和开发体验上都有所提升。

protobuf 消息定义指南

除了遵循官方 Protocol Buffer 指南之外,我们还建议在处理接口时,在 .proto 文件中使用以下注解:
  • 对接受接口的 Any 字段,使用 cosmos_proto.accepts_interface 进行注解:
    • 向 InterfaceRegistry.RegisterInterface 传入与 protoName 相同的完整限定名。
    • 示例:(cosmos_proto.accepts_interface) = "cosmos.gov.v1beta1.Content"(而不只是 Content)。
  • 使用 cosmos_proto.implements_interface 标注接口实现:
    • 向 InterfaceRegistry.RegisterInterface 传入与 protoName 相同的完整限定名。
    • 示例:(cosmos_proto.implements_interface) = "cosmos.authz.v1beta1.Authorization"(而不只是 Authorization)。
随后,代码生成器可以匹配 accepts_interface 与 implements_interface 注解,以判断某些 Protobuf 消息是否允许被打包进指定的 Any 字段中。

签名者

Signer 用于指定 Cosmos SDK 应当使用哪个字段来确定消息的签名者。客户端也可以使用该字段来推断应通过哪个字段确定消息签名者。 在这里了解更多关于 signer 字段的信息。
// Reference: https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L40
option (cosmos.msg.v1.signer) = "from_address";

标量

Scalar 类型为客户端提供了一种方式,使其能够理解应如何按照模块和 SDK 的预期构造 protobuf 消息。
(cosmos_proto.scalar) = "cosmos.AddressString"
账户地址字符串标量示例:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L46
string from_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
验证者地址字符串标量示例:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/distribution/v1beta1/query.proto#L108
string validator_address = 1 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
Dec 标量示例:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/distribution/v1beta1/distribution.proto#L17
string community_tax = 1 [(cosmos_proto.scalar) = "cosmos.Dec"];
Int 标量示例:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/gov/v1/gov.proto#L127
string yes_count = 1 [(cosmos_proto.scalar) = "cosmos.Int"];
可提供的标量值包括:cosmos.AddressString、cosmos.ValidatorAddressString、cosmos.ConsensusAddressString、cosmos.Int、cosmos.Dec。

Implements_Interface

Implement interface 用于向客户端工具(例如 telescope)提供如何对 protobuf 消息进行编码和解码的信息。
option (cosmos_proto.implements_interface) = "cosmos.auth.v1beta1.AccountI";

Method、Field、Message Added In

method_added_in、field_added_in 和 message_added_in 是用于告知客户端某个方法、字段或消息从较晚版本开始受支持的注解。当新方法或字段在后续版本中加入时,这对客户端了解自己可以调用哪些能力非常有用。 这些注解的使用方式如下:
option (cosmos_proto.method_added_in) = "cosmos-sdk 0.50.1";
option (cosmos_proto.field_added_in) = "cosmos-sdk 0.50.1";
option (cosmos_proto.message_added_in) = "cosmos-sdk 0.50.1";

Amino

Amino 编解码器已在 v0.50+ 中移除,这意味着不再需要注册 legacyAminoCodec。为了替代 amino 编解码器,现在使用 Amino protobuf 注解向 amino 编解码器提供如何对 protobuf 消息进行编码和解码的信息。
Amino 注解仅用于保持与 amino 的向后兼容。新模块不要求使用 amino 注解。
下面这些注解用于以向后兼容的方式,向 amino 编解码器提供如何对 protobuf 消息进行编码和解码的信息。

名称

Name 指定向用户显示的 amino 名称,以便用户了解自己正在签署哪条消息。
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L41
option (amino.name) = "cosmos-sdk/MsgSend";

字段名称

Field name 指定向用户显示的 amino 字段名称,以便用户了解自己正在签署哪个字段。
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/distribution/v1beta1/distribution.proto#L165
uint64 height = 3 [(amino.field_name) = "creation_height"];

不省略空值

Dont omitempty 指定在编码为 amino 时不应省略该字段。
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L48
repeated cosmos.base.v1beta1.Coin amount = 3 [(amino.dont_omitempty) = true];

编码

Encoding 用于指示 amino JSON marshaler 如何对某些可能不同于标准编码行为的字段进行编码。最常见的例子是,在使用 amino JSON 编码格式时,repeated cosmos.base.v1beta1.Coin 的编码方式。legacy_coins 选项会告诉 JSON marshaler 如何对 cosmos.base.v1beta1.Coin 的空切片进行编码。
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/genesis.proto#L23
(amino.encoding) = "legacy_coins",

模块查询安全

cosmos.query.v1.module_query_safe 注解(源码)用于将某个查询方法标记为可在状态机内部安全调用,例如从其他模块的 keeper 中、通过 ADR-033 模块间调用,或从 CosmWasm 合约中调用。
rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) {
  option (cosmos.query.v1.module_query_safe) = true;
}
当该注解设置为 true 时,表示该查询满足以下条件:
  1. 确定性:在给定区块高度的情况下,每次调用都会返回完全相同的响应,并且不会在 SDK 补丁版本之间引入破坏状态机的一致性变化。
  2. Gas 可追踪:Gas 消耗会被正确计量,从而避免高计算量查询不消耗 Gas 的攻击向量。
如果你要为自己的查询添加该注解,必须确保同时满足以上两个条件。对于可能消耗大量 Gas 的查询(例如带分页且可能被错误配置的查询),应添加 Protobuf 注释以提醒下游模块开发者。 该注解在 v0.47 中引入。
This document explains the various protobuf scalars that have been added to make working with protobuf easier for Cosmos SDK application developers

Gogoproto

Modules are encouraged to utilize Protobuf encoding for their respective types. In the Cosmos SDK, we use the Gogoproto specific implementation of the Protobuf spec that offers speed and developer experience improvements compared to the official Google protobuf implementation.

Guidelines for protobuf message definitions

In addition to following official Protocol Buffer guidelines, we recommend using these annotations in .proto files when dealing with interfaces:
  • Use cosmos_proto.accepts_interface to annotate Any fields that accept interfaces:
    • Pass the same fully qualified name as protoName to InterfaceRegistry.RegisterInterface.
    • Example: (cosmos_proto.accepts_interface) = "cosmos.gov.v1beta1.Content" (not just Content).
  • Annotate interface implementations with cosmos_proto.implements_interface:
    • Pass the same fully qualified name as protoName to InterfaceRegistry.RegisterInterface.
    • Example: (cosmos_proto.implements_interface) = "cosmos.authz.v1beta1.Authorization" (not just Authorization).
Code generators can then match the accepts_interface and implements_interface annotations to determine whether some Protobuf messages are allowed to be packed in a given Any field.

Signer

Signer specifies which field should be used to determine the signer of a message for the Cosmos SDK. This field can be used for clients as well to infer which field should be used to determine the signer of a message. Read more about the signer field here.
// Reference: https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L40
option (cosmos.msg.v1.signer) = "from_address";

Scalar

The scalar type defines a way for clients to understand how to construct protobuf messages according to what is expected by the module and sdk.
(cosmos_proto.scalar) = "cosmos.AddressString"
Example of account address string scalar:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L46
string from_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
Example of validator address string scalar:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/distribution/v1beta1/query.proto#L108
string validator_address = 1 [(cosmos_proto.scalar) = "cosmos.ValidatorAddressString"];
Example of Dec scalar:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/distribution/v1beta1/distribution.proto#L17
string community_tax = 1 [(cosmos_proto.scalar) = "cosmos.Dec"];
Example of Int scalar:
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/gov/v1/gov.proto#L127
string yes_count = 1 [(cosmos_proto.scalar) = "cosmos.Int"];
There are a few options for what can be provided as a scalar: cosmos.AddressString, cosmos.ValidatorAddressString, cosmos.ConsensusAddressString, cosmos.Int, cosmos.Dec.

Implements_Interface

Implement interface is used to provide information to client tooling like telescope on how to encode and decode protobuf messages.
option (cosmos_proto.implements_interface) = "cosmos.auth.v1beta1.AccountI";

Method,Field,Message Added In

method_added_in, field_added_in and message_added_in are annotations to indicate to clients that a method, field, or message has been supported since a later version. This is useful when new methods or fields are added in later versions and the client needs to be aware of what it can call. The annotations are used as follows:
option (cosmos_proto.method_added_in) = "cosmos-sdk 0.50.1";
option (cosmos_proto.field_added_in) = "cosmos-sdk 0.50.1";
option (cosmos_proto.message_added_in) = "cosmos-sdk 0.50.1";

Amino

The amino codec was removed in v0.50+, this means there is not a need register legacyAminoCodec. To replace the amino codec, Amino protobuf annotations are used to provide information to the amino codec on how to encode and decode protobuf messages.
Amino annotations are only used for backwards compatibility with amino. New modules are not required use amino annotations.
The below annotations are used to provide information to the amino codec on how to encode and decode protobuf messages in a backwards compatible manner.

Name

Name specifies the amino name that would show up for the user in order for them see which message they are signing.
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L41
option (amino.name) = "cosmos-sdk/MsgSend";

Field_Name

Field name specifies the amino name that would show up for the user in order for them see which field they are signing.
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/distribution/v1beta1/distribution.proto#L165
uint64 height = 3 [(amino.field_name) = "creation_height"];

Dont_OmitEmpty

Dont omitempty specifies that the field should not be omitted when encoding to amino.
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/tx.proto#L48
repeated cosmos.base.v1beta1.Coin amount = 3 [(amino.dont_omitempty) = true];

Encoding

Encoding instructs the amino json marshaler how to encode certain fields that may differ from the standard encoding behavior. The most common example of this is how repeated cosmos.base.v1beta1.Coin is encoded when using the amino json encoding format. The legacy_coins option tells the json marshaler how to encode a null slice of cosmos.base.v1beta1.Coin.
// https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/proto/cosmos/bank/v1beta1/genesis.proto#L23
(amino.encoding) = "legacy_coins",

Module Query Safe

The cosmos.query.v1.module_query_safe annotation (source) marks a query method as safe to call from within the state machine — for example from another module’s keeper, via ADR-033 intermodule calls, or from CosmWasm contracts.
rpc Balance(QueryBalanceRequest) returns (QueryBalanceResponse) {
  option (cosmos.query.v1.module_query_safe) = true;
}
When set to true, the annotation asserts that the query is:
  1. Deterministic: given a block height, it returns the exact same response on every call and does not introduce state-machine-breaking changes across SDK patch versions.
  2. Gas-tracked: gas consumption is correctly accounted for, preventing attack vectors where high-computation queries consume no gas.
If you add this annotation to your own query, you must ensure both conditions hold. For queries that may consume significant gas (for example those with pagination that could be misconfigured), add a Protobuf comment warning downstream module developers. This annotation was introduced in v0.47.