变更日志

  • 2021 年 11 月 23 日:初始草案
  • 2023 年 5 月 16 日:提案已废弃。pre_run 和 post_run 已无必要,新增 artifacts 带来的收益也较小。

状态

已废弃

摘要

本 ADR 扩展了现有的 x/upgrade Plan proto 消息,加入了新字段,用于在升级工具中定义升级前运行和升级后运行流程。 它还定义了一种结构,用于提供升级过程中涉及的可下载产物。

背景

upgrade 模块与 Cosmovisor 配合设计,用于促进并自动化区块链从一个版本迁移到另一个版本。 用户会提交一个包含升级 Plan 的软件升级治理提案。 当前的 Plan 包含以下字段:
  • name:用于标识新版本的简短字符串。
  • height:执行升级的链高度。
  • info:包含升级信息的字符串。
info 字符串可以是任意内容。 不过,Cosmovisor 会尝试使用 info 字段自动下载区块链可执行文件的新版本。 为了让自动下载生效,Cosmovisor 期望它是以下两者之一:一个字符串化的 JSON 对象(其具体结构通过文档定义),或者一个会返回此类 JSON 的 URL。 该 JSON 对象会为不同平台(操作系统和架构,例如 "linux/amd64")标识用于下载新区块链可执行文件的 URL。 这样的 URL 可以直接返回可执行文件,也可以返回包含该可执行文件以及可能其他资源的归档文件。 如果 URL 返回的是归档文件,它会被解压到 {DAEMON_HOME}/cosmovisor/{upgrade name}。 随后,如果 {DAEMON_HOME}/cosmovisor/{upgrade name}/bin/{DAEMON_NAME} 不存在,但 {DAEMON_HOME}/cosmovisor/{upgrade name}/{DAEMON_NAME} 存在,则会将后者复制到前者。 如果 URL 返回的不是归档文件,则会将其下载到 {DAEMON_HOME}/cosmovisor/{upgrade name}/bin/{DAEMON_NAME}。 如果到达升级高度时新版本的可执行文件仍不可用,Cosmovisor 将停止运行。 DAEMON_HOME 和 DAEMON_NAME 都是用于配置 Cosmovisor 的环境变量。 当前还没有机制让 Cosmovisor 在升级后的链重启完成后再运行一条命令。 当前升级流程的时间线如下:
  1. 提交并通过一个升级治理提案。
  2. 到达升级高度。
  3. x/upgrade 模块写入 upgrade_info.json 文件。
  4. 链停止运行。
  5. Cosmovisor 备份数据目录(如果配置了该行为)。
  6. Cosmovisor 下载新的可执行文件(如果尚未就位)。
  7. Cosmovisor 执行 ${DAEMON_NAME} pre-upgrade。
  8. Cosmovisor 使用新版本以及最初提供的相同参数重启应用。

决策

Protobuf 更新

我们将更新 x/upgrade.Plan 消息,用于提供升级指令。 升级指令将包含每个平台可用产物的列表。 它允许定义 pre-run 和 post-run 命令。 这些命令不受共识保证;它们会由 Cosmovisor(或其他工具)在处理升级时执行。
message Plan {
  // ... (existing fields)

  UpgradeInstructions instructions = 6;
}
新的 UpgradeInstructions instructions 字段必须是可选的。
message UpgradeInstructions {
  string pre_run              = 1;
  string post_run             = 2;
  repeated Artifact artifacts = 3;
  string description          = 4;
}
UpgradeInstructions 中的所有字段都是可选的。
  • pre_run 是一条在已升级链重启前执行的命令。 如果定义了它,则会在停止运行并下载新产物之后、但在重启已升级链之前执行。 此命令运行时的工作目录必须是 {DAEMON_HOME}/cosmovisor/{upgrade name}。 此命令的行为必须与当前的 pre-upgrade 命令相同。 它不接收任何命令行参数,并预期以下列退出码结束:
    退出状态码在 Cosmovisor 中的处理方式
    0假定 pre-upgrade 命令已成功执行,并继续升级。
    1当 pre-upgrade 命令尚未实现时的默认退出码。
    30pre-upgrade 命令已执行但失败。这会导致整个升级失败。
    31pre-upgrade 命令已执行但失败。但会持续重试该命令,直到返回退出码 1 或 30。
    如果定义了该项,则应用监督进程(例如 Cosmovisor)绝不能运行 app pre-run。
  • post_run 是一条在已升级链启动后执行的命令。如果定义了该命令,升级节点最多只能执行它一次。 其输出和退出码应当被记录,但不应影响已升级链的运行。 此命令运行时的工作目录必须是 {DAEMON_HOME}/cosmovisor/{upgrade name}。
  • artifacts 定义要下载的项目。 每个平台应当只有一个条目。
  • description 包含关于此次升级的人类可读信息,并且可能包含对外部资源的引用。 它不应用于结构化处理信息。
message Artifact {
  string platform      = 1;
  string url           = 2;
  string checksum      = 3;
  string checksum_algo = 4;
}
  • platform 是必填字符串,格式应为 {OS}/{CPU},例如 "linux/amd64"。 也应允许使用字符串 "any"。 当找不到特定 {OS}/{CPU} 条目时,platform 为 "any" 的 Artifact 应作为回退项使用。 也就是说,如果存在一个 Artifact,其 platform 与系统的 OS 和 CPU 匹配,则应使用它; 否则,如果存在一个 Artifact,其 platform 为 any,则应使用它; 否则不应下载任何产物。
  • url 是必填的 URL 字符串,必须符合 RFC 1738: Uniform Resource Locators。 对该 url 发起的请求必须返回可执行文件,或者返回包含 bin/{DAEMON_NAME} 或 {DAEMON_NAME} 之一的归档文件。 URL 不应包含校验和,校验和应由 checksum 属性指定。
  • checksum 是对 url 发起请求后预期结果的校验和。 它不是必需的,但推荐提供。 如果提供,则必须是十六进制编码的校验和字符串。 如果提供了 checksum,但其值与 url 返回结果的校验和不同,则使用这些 UpgradeInstructions 的工具必须失败。
  • checksum_algo 是一个字符串,用于标识生成 checksum 所使用的算法。 推荐算法:sha256、sha512。 也支持但不推荐的算法:sha1、md5。 如果提供了 checksum,则也必须提供 checksum_algo。
url 不要求包含 checksum 查询参数。 如果 url 确实包含 checksum 查询参数,则 checksum 和 checksum_algo 字段也必须被填充,且它们的值必须与该查询参数的值一致。 例如,如果 url 是 "https://example.com?checksum=md5:d41d8cd98f00b204e9800998ecf8427e",那么 checksum 字段必须是 "d41d8cd98f00b204e9800998ecf8427e",而 checksum_algo 字段必须是 "md5"。

升级模块更新

如果升级 Plan 未使用新的 UpgradeInstructions 字段,则会保留现有功能。 将 info 字段解析为 URL 或 binaries JSON 的方式将被弃用。 在校验期间,如果 info 字段以这种方式使用,将发出警告,但不会报错。 我们将更新 upgrade-info.json 文件的创建逻辑,使其包含 UpgradeInstructions。 我们将更新 CLI 提供的可选校验逻辑,以适配新的 Plan 结构。 我们将新增以下校验:
  1. 如果提供了 UpgradeInstructions:
    1. artifacts 中必须至少有一个条目。
    2. 所有 artifacts 的 platform 都必须唯一。
    3. 对于每个 Artifact,如果 url 包含 checksum 查询参数:
      1. checksum 查询参数的值必须符合 {checksum_algo}:{checksum} 格式。
      2. 查询参数中的 {checksum} 必须等于 Artifact 中提供的 checksum。
      3. 查询参数中的 {checksum_algo} 必须等于 Artifact 中提供的 checksum_algo。
  2. 当前已通过 info 字段执行以下校验。我们将对 UpgradeInstructions 应用类似校验。 对于每个 Artifact:
    1. platform 必须符合 {OS}/{CPU} 格式,或者为 "any"。
    2. url 字段不能为空。
    3. url 字段必须是合法 URL。
    4. 必须在 checksum 字段中提供校验和,或者在 url 中以查询参数形式提供。
    5. 如果 checksum 字段有值,且 url 也包含 checksum 查询参数,则两者的值必须相等。
    6. url 必须返回一个文件,或者返回包含 bin/{DAEMON_NAME} 或 {DAEMON_NAME} 之一的归档文件。
    7. 如果提供了 checksum(无论是在字段中还是作为查询参数),则 url 返回结果的校验和必须等于提供的校验和。
下载 Artifact 的方式将与当前从 info 中下载 URL 的方式相同。

Cosmovisor 更新

如果 upgrade-info.json 文件不包含任何 UpgradeInstructions,将保持现有功能不变。 我们将更新 Cosmovisor,使其在 upgrade-info.json 中查找并处理新的 UpgradeInstructions。 如果提供了 UpgradeInstructions,我们将执行以下操作:
  1. info 字段将被忽略。
  2. artifacts 字段将用于根据 Cosmovisor 运行所在的 platform 来识别要下载的制品。
  3. 如果提供了 checksum(无论是在字段中还是作为 url 中的查询参数),并且下载的制品校验和不同,则升级流程将被中断,Cosmovisor 会带错误退出。
  4. 如果定义了 pre_run 命令,它将在原本会执行 app pre-upgrade 命令的同一流程节点执行。 它将使用与 Cosmovisor 运行其他命令时相同的环境来执行。
  5. 如果定义了 post_run 命令,它将在执行重启链的命令之后执行。 它将在后台进程中使用与其他命令相同的环境执行。 该命令产生的任何输出都会被记录到日志中。 完成后,其退出码也会被记录到日志中。
我们将弃用 info 字段除人类可读信息之外的其他用途。 如果 info 字段被用于定义资产(无论是通过 URL 还是 JSON),将记录一条警告日志。 新的升级时间线与当前版本非常相似。变更部分已加粗:
  1. 提交并通过一项升级治理提案。
  2. 到达升级高度。
  3. x/upgrade 模块写入 upgrade_info.json 文件 (现在可能包含 UpgradeInstructions)。
  4. 链停止运行。
  5. Cosmovisor 备份数据目录(如果已配置这样做)。
  6. Cosmovisor 下载新的可执行文件(如果尚未就位)。
  7. Cosmovisor 执行 pre_run 命令(如果已提供),否则执行 ${DAEMON_NAME} pre-upgrade 命令。
  8. Cosmovisor 使用新版本和最初提供的相同参数重启应用。
  9. Cosmovisor 会立即在分离进程中运行 post_run 命令。

后果

向后兼容性

由于对现有定义的唯一变更是在 Plan 消息中新增了 instructions 字段,并且该字段是可选的,因此就 proto 消息而言不存在向后不兼容问题。 此外,在未提供 UpgradeInstructions 时将保持当前行为,因此无论是对 upgrade 模块还是对 Cosmovisor,都不存在向后不兼容问题。

向前兼容性

为了在软件升级中使用 UpgradeInstructions,以下两个条件必须同时满足:
  1. 链必须已经使用足够新版本的 Cosmos SDK。
  2. 链的节点必须使用足够新版本的 Cosmovisor。

正面影响

  1. 用于定义制品的结构更清晰,因为它现在定义在 proto 中,而不是文档中。
  2. pre-run 命令的可用性变得更加明确。
  3. post-run 命令成为可能。

负面影响

  1. Plan 消息变得更大了。不过这可以忽略,因为 A)x/upgrades 模块最多只会存储一个升级计划,B)升级发生得足够少,增加的 gas 成本无需担心。
  2. 没有提供一个可返回 UpgradeInstructions 的 URL 选项。
  3. 对于某个平台,提供多个资产(可执行文件和其他文件)的唯一方式是将归档文件用作该平台的制品。

中性影响

  1. 当未提供 UpgradeInstructions 时,info 字段的现有功能会保持不变。

后续讨论

  1. Draft PR #10032 Comment: 考虑为 UpgradeInstructions instructions 使用不同名称(无论是消息类型名还是字段名)。
  2. Draft PR #10032 Comment:
    1. 考虑将 string platform 字段放入 UpgradeInstructions 中,并使 UpgradeInstructions 成为 Plan 中的重复字段。
    2. 考虑在 Plan 中使用一个 oneof 字段,该字段可以是 UpgradeInstructions,也可以是一个应返回 UpgradeInstructions 的 URL。
    3. 考虑允许 info 是 UpgradeInstructions 的 JSON 序列化版本,或者是一个返回该内容的 URL。
  3. Draft PR #10032 Comment: 考虑不包含 UpgradeInstructions.description 字段,而改为使用 info 字段实现该用途。
  4. Draft PR #10032 Comment: 考虑通过向 Artifact 消息添加 name 字段,允许为任意给定的 platform 下载多个制品。
  5. PR #10502 Comment 允许通过 URL 提供新的 UpgradeInstructions。
  6. PR #10502 Comment 允许为资产定义 signer(作为使用 checksum 的替代方案)。

参考资料


Changelog

  • Nov, 23, 2021: Initial Draft
  • May, 16, 2023: Proposal ABANDONED. pre_run and post_run are not necessary anymore and adding the artifacts brings minor benefits.

Status

ABANDONED

Abstract

This ADR expands the existing x/upgrade Plan proto message to include new fields for defining pre-run and post-run processes within upgrade tooling. It also defines a structure for providing downloadable artifacts involved in an upgrade.

Context

The upgrade module in conjunction with Cosmovisor are designed to facilitate and automate a blockchain’s transition from one version to another. Users submit a software upgrade governance proposal containing an upgrade Plan. The Plan currently contains the following fields:
  • name: A short string identifying the new version.
  • height: The chain height at which the upgrade is to be performed.
  • info: A string containing information about the upgrade.
The info string can be anything. However, Cosmovisor will try to use the info field to automatically download a new version of the blockchain executable. For the auto-download to work, Cosmovisor expects it to be either a stringified JSON object (with a specific structure defined through documentation), or a URL that will return such JSON. The JSON object identifies URLs used to download the new blockchain executable for different platforms (OS and Architecture, e.g. “linux/amd64”). Such a URL can either return the executable file directly or can return an archive containing the executable and possibly other assets. If the URL returns an archive, it is decompressed into {DAEMON_HOME}/cosmovisor/{upgrade name}. Then, if {DAEMON_HOME}/cosmovisor/{upgrade name}/bin/{DAEMON_NAME} does not exist, but {DAEMON_HOME}/cosmovisor/{upgrade name}/{DAEMON_NAME} does, the latter is copied to the former. If the URL returns something other than an archive, it is downloaded to {DAEMON_HOME}/cosmovisor/{upgrade name}/bin/{DAEMON_NAME}. If an upgrade height is reached and the new version of the executable version isn’t available, Cosmovisor will stop running. Both DAEMON_HOME and DAEMON_NAME are environment variables used to configure Cosmovisor. Currently, there is no mechanism that makes Cosmovisor run a command after the upgraded chain has been restarted. The current upgrade process has this timeline:
  1. An upgrade governance proposal is submitted and approved.
  2. The upgrade height is reached.
  3. The x/upgrade module writes the upgrade_info.json file.
  4. The chain halts.
  5. Cosmovisor backs up the data directory (if set up to do so).
  6. Cosmovisor downloads the new executable (if not already in place).
  7. Cosmovisor executes the ${DAEMON_NAME} pre-upgrade.
  8. Cosmovisor restarts the app using the new version and same args originally provided.

Decision

Protobuf Updates

We will update the x/upgrade.Plan message for providing upgrade instructions. The upgrade instructions will contain a list of artifacts available for each platform. It allows for the definition of a pre-run and post-run commands. These commands are not consensus guaranteed; they will be executed by Cosmosvisor (or other) during its upgrade handling.
message Plan {
  // ... (existing fields)

  UpgradeInstructions instructions = 6;
}
The new UpgradeInstructions instructions field MUST be optional.
message UpgradeInstructions {
  string pre_run              = 1;
  string post_run             = 2;
  repeated Artifact artifacts = 3;
  string description          = 4;
}
All fields in the UpgradeInstructions are optional.
  • pre_run is a command to run prior to the upgraded chain restarting. If defined, it will be executed after halting and downloading the new artifact but before restarting the upgraded chain. The working directory this command runs from MUST be {DAEMON_HOME}/cosmovisor/{upgrade name}. This command MUST behave the same as the current pre-upgrade command. It does not take in any command-line arguments and is expected to terminate with the following exit codes:
    Exit status codeHow it is handled in Cosmosvisor
    0Assumes pre-upgrade command executed successfully and continues the upgrade.
    1Default exit code when pre-upgrade command has not been implemented.
    30pre-upgrade command was executed but failed. This fails the entire upgrade.
    31pre-upgrade command was executed but failed. But the command is retried until exit code 1 or 30 are returned.
    If defined, then the app supervisors (e.g. Cosmovisor) MUST NOT run app pre-run.
  • post_run is a command to run after the upgraded chain has been started. If defined, this command MUST be only executed at most once by an upgrading node. The output and exit code SHOULD be logged but SHOULD NOT affect the running of the upgraded chain. The working directory this command runs from MUST be {DAEMON_HOME}/cosmovisor/{upgrade name}.
  • artifacts define items to be downloaded. It SHOULD have only one entry per platform.
  • description contains human-readable information about the upgrade and might contain references to external resources. It SHOULD NOT be used for structured processing information.
message Artifact {
  string platform      = 1;
  string url           = 2;
  string checksum      = 3;
  string checksum_algo = 4;
}
  • platform is a required string that SHOULD be in the format {OS}/{CPU}, e.g. "linux/amd64". The string "any" SHOULD also be allowed. An Artifact with a platform of "any" SHOULD be used as a fallback when a specific {OS}/{CPU} entry is not found. That is, if an Artifact exists with a platform that matches the system’s OS and CPU, that should be used; otherwise, if an Artifact exists with a platform of any, that should be used; otherwise no artifact should be downloaded.
  • url is a required URL string that MUST conform to RFC 1738: Uniform Resource Locators. A request to this url MUST return either an executable file or an archive containing either bin/{DAEMON_NAME} or {DAEMON_NAME}. The URL should not contain checksum - it should be specified by the checksum attribute.
  • checksum is a checksum of the expected result of a request to the url. It is not required, but is recommended. If provided, it MUST be a hex encoded checksum string. Tools utilizing these UpgradeInstructions MUST fail if a checksum is provided but is different from the checksum of the result returned by the url.
  • checksum_algo is a string identify the algorithm used to generate the checksum. Recommended algorithms: sha256, sha512. Algorithms also supported (but not recommended): sha1, md5. If a checksum is provided, a checksum_algo MUST also be provided.
A url is not required to contain a checksum query parameter. If the url does contain a checksum query parameter, the checksum and checksum_algo fields MUST also be populated, and their values MUST match the value of the query parameter. For example, if the url is "https://example.com?checksum=md5:d41d8cd98f00b204e9800998ecf8427e", then the checksum field must be "d41d8cd98f00b204e9800998ecf8427e" and the checksum_algo field must be "md5".

Upgrade Module Updates

If an upgrade Plan does not use the new UpgradeInstructions field, existing functionality will be maintained. The parsing of the info field as either a URL or binaries JSON will be deprecated. During validation, if the info field is used as such, a warning will be issued, but not an error. We will update the creation of the upgrade-info.json file to include the UpgradeInstructions. We will update the optional validation available via CLI to account for the new Plan structure. We will add the following validation:
  1. If UpgradeInstructions are provided:
    1. There MUST be at least one entry in artifacts.
    2. All of the artifacts MUST have a unique platform.
    3. For each Artifact, if the url contains a checksum query parameter:
      1. The checksum query parameter value MUST be in the format of {checksum_algo}:{checksum}.
      2. The {checksum} from the query parameter MUST equal the checksum provided in the Artifact.
      3. The {checksum_algo} from the query parameter MUST equal the checksum_algo provided in the Artifact.
  2. The following validation is currently done using the info field. We will apply similar validation to the UpgradeInstructions. For each Artifact:
    1. The platform MUST have the format {OS}/{CPU} or be "any".
    2. The url field MUST NOT be empty.
    3. The url field MUST be a proper URL.
    4. A checksum MUST be provided either in the checksum field or as a query parameter in the url.
    5. If the checksum field has a value and the url also has a checksum query parameter, the two values MUST be equal.
    6. The url MUST return either a file or an archive containing either bin/{DAEMON_NAME} or {DAEMON_NAME}.
    7. If a checksum is provided (in the field or as a query param), the checksum of the result of the url MUST equal the provided checksum.
Downloading of an Artifact will happen the same way that URLs from info are currently downloaded.

Cosmovisor Updates

If the upgrade-info.json file does not contain any UpgradeInstructions, existing functionality will be maintained. We will update Cosmovisor to look for and handle the new UpgradeInstructions in upgrade-info.json. If the UpgradeInstructions are provided, we will do the following:
  1. The info field will be ignored.
  2. The artifacts field will be used to identify the artifact to download based on the platform that Cosmovisor is running in.
  3. If a checksum is provided (either in the field or as a query param in the url), and the downloaded artifact has a different checksum, the upgrade process will be interrupted and Cosmovisor will exit with an error.
  4. If a pre_run command is defined, it will be executed at the same point in the process where the app pre-upgrade command would have been executed. It will be executed using the same environment as other commands run by Cosmovisor.
  5. If a post_run command is defined, it will be executed after executing the command that restarts the chain. It will be executed in a background process using the same environment as the other commands. Any output generated by the command will be logged. Once complete, the exit code will be logged.
We will deprecate the use of the info field for anything other than human readable information. A warning will be logged if the info field is used to define the assets (either by URL or JSON). The new upgrade timeline is very similar to the current one. Changes are in bold:
  1. An upgrade governance proposal is submitted and approved.
  2. The upgrade height is reached.
  3. The x/upgrade module writes the upgrade_info.json file (now possibly with UpgradeInstructions).
  4. The chain halts.
  5. Cosmovisor backs up the data directory (if set up to do so).
  6. Cosmovisor downloads the new executable (if not already in place).
  7. Cosmovisor executes the pre_run command if provided, or else the ${DAEMON_NAME} pre-upgrade command.
  8. Cosmovisor restarts the app using the new version and same args originally provided.
  9. Cosmovisor immediately runs the post_run command in a detached process.

Consequences

Backwards Compatibility

Since the only change to existing definitions is the addition of the instructions field to the Plan message, and that field is optional, there are no backwards incompatibilities with respects to the proto messages. Additionally, current behavior will be maintained when no UpgradeInstructions are provided, so there are no backwards incompatibilities with respects to either the upgrade module or Cosmovisor.

Forwards Compatibility

In order to utilize the UpgradeInstructions as part of a software upgrade, both of the following must be true:
  1. The chain must already be using a sufficiently advanced version of the Cosmos SDK.
  2. The chain’s nodes must be using a sufficiently advanced version of Cosmovisor.

Positive

  1. The structure for defining artifacts is clearer since it is now defined in the proto instead of in documentation.
  2. Availability of a pre-run command becomes more obvious.
  3. A post-run command becomes possible.

Negative

  1. The Plan message becomes larger. This is negligible because A) the x/upgrades module only stores at most one upgrade plan, and B) upgrades are rare enough that the increased gas cost isn’t a concern.
  2. There is no option for providing a URL that will return the UpgradeInstructions.
  3. The only way to provide multiple assets (executables and other files) for a platform is to use an archive as the platform’s artifact.

Neutral

  1. Existing functionality of the info field is maintained when the UpgradeInstructions aren’t provided.

Further Discussions

  1. Draft PR #10032 Comment: Consider different names for UpgradeInstructions instructions (either the message type or field name).
  2. Draft PR #10032 Comment:
    1. Consider putting the string platform field inside UpgradeInstructions and make UpgradeInstructions a repeated field in Plan.
    2. Consider using a oneof field in the Plan which could either be UpgradeInstructions or else a URL that should return the UpgradeInstructions.
    3. Consider allowing info to either be a JSON serialized version of UpgradeInstructions or else a URL that returns that.
  3. Draft PR #10032 Comment: Consider not including the UpgradeInstructions.description field, using the info field for that purpose instead.
  4. Draft PR #10032 Comment: Consider allowing multiple artifacts to be downloaded for any given platform by adding a name field to the Artifact message.
  5. PR #10502 Comment Allow the new UpgradeInstructions to be provided via URL.
  6. PR #10502 Comment Allow definition of a signer for assets (as an alternative to using a checksum).

References