变更日志
- 28.06.2021:初始草案
- 02.12.2021:为新字段添加
Since:注释 - 21.07.2022:移除“同一 proto 版本中不得新增
Msg”的规则。
状态
草案摘要
本 ADR 提供了更新 Protobuf 定义时的指南和推荐实践。这些指南面向模块开发者。背景
Cosmos SDK 维护了一组 Protobuf 定义。正确设计 Protobuf 定义非常重要,以避免在同一版本内引入任何破坏性变更。原因在于不能破坏工具链(包括索引器和区块浏览器)、钱包以及其他第三方集成。 当前,在修改这些 Protobuf 定义时,Cosmos SDK 仅遵循 Buf 的建议。然而我们注意到,在某些情况下,Buf 的建议仍可能导致 SDK 中出现破坏性变更。例如:- 向
Msg添加字段。添加字段本身并不违反 Protobuf 规范。然而,当向Msg添加新字段时,如果将新的Msg发送到旧节点,未知字段拒绝机制会抛出错误。 - 将字段标记为
reserved。Protobuf 提出使用reserved关键字来删除字段,而无需提升包版本。然而这样做会破坏客户端的向后兼容性,因为 Protobuf 不会为reserved字段生成任何内容。有关此问题的更多细节,请参见 #9446。
决策
我们决定保留 Buf 的建议,但有以下例外:UNARY_RPC:Cosmos SDK 当前不支持流式 RPC。COMMENT_FIELD:Cosmos SDK 允许字段没有注释。SERVICE_SUFFIX:我们使用Query和Msg服务命名约定,不使用-Service后缀。PACKAGE_VERSION_SUFFIX:某些包(例如cosmos.crypto.ed25519)不使用版本后缀。RPC_REQUEST_STANDARD_NAME:Msg服务的请求不带-Request后缀,以保持向后兼容性。
在不提升版本的情况下更新 Protobuf 定义
1. 模块开发者可以添加新的 Protobuf 定义
模块开发者可以添加新的message、新的 Service、新的 rpc 端点,以及向现有消息中添加新字段。该建议遵循 Protobuf 规范,但为表述清晰起见,仍在本文档中明确写出,因为 SDK 还要求额外进行一项变更。
SDK 要求新增内容的 Protobuf 注释中包含一行,格式如下:
version 表示字段可用起始的某个次版本(如 0.45)或补丁版本(如 0.44.5)。这将极大帮助客户端库,它们可以选择使用反射或自定义代码生成,根据目标节点版本展示或隐藏这些字段。
例如,以下注释是有效的:
2. 字段可以标记为 deprecated,节点也可以针对这些字段实现协议层面的破坏性处理变更
Protobuf 支持 deprecated 字段选项,该选项可用于任何字段,包括 Msg 字段。如果节点处理的 Protobuf 消息中包含非空的已弃用字段,节点在处理该消息时可以改变其行为,甚至可以是协议破坏性的方式。在可能的情况下,节点必须在不破坏共识的前提下处理向后兼容性问题(除非我们提升 proto 版本)。
例如,在 Cosmos SDK 从 v0.42 升级到 v0.43 的过程中,包含了两个会破坏 Protobuf 的变更,如下所列。SDK 团队没有将包版本从 v1beta1 提升到 v1,而是决定遵循本指南:回退这些破坏性变更、将这些变更标记为已弃用,并在节点处理包含已弃用字段的消息时修改实现。更具体地说:
- Cosmos SDK 最近移除了对 基于时间的软件升级 的支持。因此,
cosmos.upgrade.v1beta1.Plan中的time字段已被标记为已弃用。此外,节点将拒绝任何包含升级 Plan 且其time字段非空的提案。 - Cosmos SDK 现已支持治理拆分投票。在查询投票时,返回的
cosmos.gov.v1beta1.Vote消息中,其option字段(用于单个投票选项)已被弃用,改用options字段(允许多个投票选项)。在可能的情况下,SDK 仍会填充已弃用的option字段,即当且仅当len(options) == 1且options[0].Weight == 1.0时。
3. 字段不得重命名
尽管官方 Protobuf 建议并未禁止重命名字段,因为这不会破坏 Protobuf 的二进制表示,但 SDK 明确禁止重命名 Protobuf 结构体中的字段。作出这一选择的主要原因是避免为客户端引入破坏性变更,因为客户端通常依赖生成类型中的硬编码字段。此外,重命名字段还会导致 Protobuf 定义的 JSON 表示发生客户端破坏性变化,而这些 JSON 表示被用于 REST 端点和 CLI 中。提升 Protobuf 包版本
TODO,需要架构评审。一些主题:- 提升版本的频率
- 提升版本时,Cosmos SDK 是否应同时支持两个版本?
- 即从 v1beta1 -> v1 时,我们是否应在 Cosmos SDK 中保留两个目录,并为两个版本都提供处理器?
- 提及 ADR-023 Protobuf 命名
后果
本节描述应用该决策后的结果性上下文。所有后果都应在此列出,而不仅仅是“正面”的后果。某项具体决策可能带来正面、负面和中性的后果,但它们都会在未来影响团队和项目。
向后兼容性
所有引入向后不兼容性的 ADR 都必须包含一个章节来描述这些不兼容性及其严重程度。ADR 必须说明作者计划如何处理这些不兼容性。若提交的 ADR 未对向后兼容性进行充分论述,可能会被直接拒绝。
正面影响
- 减少工具开发者的负担
- 提高生态系统中的兼容性
- …
负面影响
{negative consequences}
中性影响
- 在 Protobuf 审查中提高严谨性
进一步讨论
本 ADR 仍处于 DRAFT 阶段,“提升 Protobuf 包版本”部分将在我们就如何正确执行该操作作出决定后补充完善。测试用例 [可选]
对于影响共识变更的 ADR,实现对应的测试用例是强制要求。其他 ADR 可根据需要选择是否包含测试用例链接。参考资料
Changelog
- 28.06.2021: Initial Draft
- 02.12.2021: Add
Since:comment for new fields - 21.07.2022: Remove the rule of no new
Msgin the same proto version.
Status
DraftAbstract
This ADR provides guidelines and recommended practices when updating Protobuf definitions. These guidelines are targeting module developers.Context
The Cosmos SDK maintains a set of Protobuf definitions. It is important to correctly design Protobuf definitions to avoid any breaking changes within the same version. The reasons are to not break tooling (including indexers and explorers), wallets and other third-party integrations. When making changes to these Protobuf definitions, the Cosmos SDK currently only follows Buf’s recommendations. We noticed however that Buf’s recommendations might still result in breaking changes in the SDK in some cases. For example:- Adding fields to
Msgs. Adding fields is a not a Protobuf spec-breaking operation. However, when adding new fields toMsgs, the unknown field rejection will throw an error when sending the newMsgto an older node. - Marking fields as
reserved. Protobuf proposes thereservedkeyword for removing fields without the need to bump the package version. However, by doing so, client backwards compatibility is broken as Protobuf doesn’t generate anything forreservedfields. See #9446 for more details on this issue.
Decision
We decide to keep Buf’s recommendations with the following exceptions:UNARY_RPC: the Cosmos SDK currently does not support streaming RPCs.COMMENT_FIELD: the Cosmos SDK allows fields with no comments.SERVICE_SUFFIX: we use theQueryandMsgservice naming convention, which doesn’t use the-Servicesuffix.PACKAGE_VERSION_SUFFIX: some packages, such ascosmos.crypto.ed25519, don’t use a version suffix.RPC_REQUEST_STANDARD_NAME: Requests for theMsgservice don’t have the-Requestsuffix to keep backwards compatibility.
Updating Protobuf Definition Without Bumping Version
1. Module developers MAY add new Protobuf definitions
Module developers MAY add newmessages, new Services, new rpc endpoints, and new fields to existing messages. This recommendation follows the Protobuf specification, but is added in this document for clarity, as the SDK requires one additional change.
The SDK requires the Protobuf comment of the new addition to contain one line with the following format:
version denotes a minor (“0.45”) or patch (“0.44.5”) version from which the field is available. This will greatly help client libraries, who can optionally use reflection or custom code generation to show/hide these fields depending on the targetted node version.
As examples, the following comments are valid:
2. Fields MAY be marked as deprecated, and nodes MAY implement a protocol-breaking change for handling these fields
Protobuf supports the deprecated field option, and this option MAY be used on any field, including Msg fields. If a node handles a Protobuf message with a non-empty deprecated field, the node MAY change its behavior upon processing it, even in a protocol-breaking way. When possible, the node MUST handle backwards compatibility without breaking the consensus (unless we increment the proto version).
As an example, the Cosmos SDK v0.42 to v0.43 update contained two Protobuf-breaking changes, listed below. Instead of bumping the package versions from v1beta1 to v1, the SDK team decided to follow this guideline, by reverting the breaking changes, marking those changes as deprecated, and modifying the node implementation when processing messages with deprecated fields. More specifically:
- The Cosmos SDK recently removed support for time-based software upgrades. As such, the
timefield has been marked as deprecated incosmos.upgrade.v1beta1.Plan. Moreover, the node will reject any proposal containing an upgrade Plan whosetimefield is non-empty. - The Cosmos SDK now supports governance split votes. When querying for votes, the returned
cosmos.gov.v1beta1.Votemessage has itsoptionfield (used for 1 vote option) deprecated in favor of itsoptionsfield (allowing multiple vote options). Whenever possible, the SDK still populates the deprecatedoptionfield, that is, if and only if thelen(options) == 1andoptions[0].Weight == 1.0.
3. Fields MUST NOT be renamed
Whereas the official Protobuf recommendations do not prohibit renaming fields, as it does not break the Protobuf binary representation, the SDK explicitly forbids renaming fields in Protobuf structs. The main reason for this choice is to avoid introducing breaking changes for clients, which often rely on hard-coded fields from generated types. Moreover, renaming fields will lead to client-breaking JSON representations of Protobuf definitions, used in REST endpoints and in the CLI.Incrementing Protobuf Package Version
TODO, needs architecture review. Some topics:- Bumping versions frequency
- When bumping versions, should the Cosmos SDK support both versions?
- i.e. v1beta1 -> v1, should we have two folders in the Cosmos SDK, and handlers for both versions?
- mention ADR-023 Protobuf naming
Consequences
This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the “positive” ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future.
Backwards Compatibility
All ADRs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The ADR must explain how the author proposes to deal with these incompatibilities. ADR submissions without a sufficient backwards compatibility treatise may be rejected outright.
Positive
- less pain to tool developers
- more compatibility in the ecosystem
- …
Negative
{negative consequences}
Neutral
- more rigor in Protobuf review