OpenZeppelin Upgrades Core & CLI - OpenZeppelin 文档

本文档介绍了OpenZeppelin Upgrades Core库和CLI工具,用于检测智能合约的升级安全性及存储布局兼容性。重点介绍了CLI的validate命令,以及High-Level API和Low-Level API的使用方法,包括环境配置、参数说明和返回结果,方便开发者在开发流程中尽早发现和解决潜在的升级问题。

OpenZeppelin Upgrades Core & CLI

@openzeppelin/upgrades-core 包提供了一个 validate 命令,用于检查可升级合约的升级安全性和存储布局兼容性。它可以在你的整个开发过程中使用,以确保你的合约是升级安全的,并且与之前的版本兼容。

它还提供了以编程方式执行这些检查的 API,并且包含了使用 OpenZeppelin Upgrades 插件执行这些检查的核心逻辑。

CLI: Validate 命令

从包含构建信息文件的目录中检测可升级合约,并验证它们是否是升级安全的。如果你想从命令行、脚本或 CI/CD 管道中验证你项目的所有可升级合约,请使用此命令。

“构建信息文件” 由你的编译工具链(Hardhat, Foundry)生成,包含编译过程的输入和输出。

前提条件

在使用 validate 命令之前,你必须定义可升级合约,以便可以检测和验证它们,定义用于存储布局比较的参考合约,并编译你的合约。

定义可升级合约

validate 命令对看起来像可升级合约的合约执行升级安全检查。具体来说,它对满足以下任何条件的实现合约执行检查:

  • 继承自 Initializable

  • 具有 upgradeTo(address)upgradeToAndCall(address,bytes) 函数。对于继承自 UUPSUpgradeable 的合约来说是这种情况。

  • 具有 NatSpec 注释 @custom:oz-upgrades

  • 具有 NatSpec 注释 @custom:oz-upgrades-from <reference>,根据下面的定义参考合约

只需将 NatSpec 注释 @custom:oz-upgrades@custom:oz-upgrades-from <reference> 添加到每个实现合约,以便它可以被检测为用于验证的可升级合约。
定义参考合约
如果一个实现合约打算作为对现有代理的升级来部署,你 必须 定义一个参考合约用于存储布局比较。 否则,如果存在任何存储布局不兼容,你将不会收到错误。

通过将 NatSpec 注释 @custom:oz-upgrades-from <reference> 添加到你的实现合约来定义参考合约,其中 <reference> 是用于存储布局比较的参考合约的合约名称或完全限定的合约名称。该合约不需要在作用域内,如果它在项目中是不明确的,则合约名称就足够了。

例子:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:oz-upgrades-from MyContractV1
contract MyContractV2 {
  ...
}

或者:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @custom:oz-upgrades-from contracts/MyContract.sol:MyContractV1
contract MyContractV2 {
  ...
}
使用存储布局编译合约

编译你的合约,并确保你的构建配置为在构建信息目录中输出包含 Solidity 编译器输入和输出的 JSON 文件。编译器输出必须包括存储布局。如果存在任何以前的构建工件,必须首先清除它们以避免重复的合约定义。

Hardhat

配置 hardhat.config.jshardhat.config.ts 以在输出选择中包含存储布局:

module.exports = {
  solidity: {
    settings: {
      outputSelection: {
        '*': {
          '*': ['storageLayout'],
        },
      },
    },
  },
};

然后编译你的合约:

npx hardhat clean && npx hardhat compile
Foundry

配置 foundry.toml 以包含构建信息和存储布局:

[profile.default]
build_info = true
extra_output = ["storageLayout"]

然后编译你的合约:

forge clean && forge build

用法

在执行完前提条件之后,运行 npx @openzeppelin/upgrades-core validate 命令来验证你的合约:

npx @openzeppelin/upgrades-core validate [<BUILD_INFO_DIR>] [<OPTIONS>]

如果发现任何错误,该命令将以非零退出代码退出,并将错误的详细报告打印到控制台。

参数:

  • <BUILD_INFO_DIR> - 可选的构建信息目录路径,其中包含带有 Solidity 编译器输入和输出的 JSON 文件。对于 Hardhat 项目,默认为 artifacts/build-info,对于 Foundry 项目,默认为 out/build-info。如果你的项目使用自定义输出目录,你必须在此处指定其构建信息目录。

选项:

  • --contract <CONTRACT> - 要验证的合约的名称或完全限定名称。如果未指定,将验证构建信息目录中的所有可升级合约。

  • --reference <REFERENCE_CONTRACT> - 只能在同时提供 --contract 选项时使用。 用于存储布局比较的参考合约的名称或完全限定名称。如果未指定,则使用 @custom:oz-upgrades-from 注释(如果它定义在正在验证的合约中)。

  • --requireReference - 只能在同时提供 --contract 选项时使用。 与 --unsafeSkipStorageCheck 不兼容。如果指定,则需要提供 --reference 选项或合约具有 @custom:oz-upgrades-from 注释。

  • --referenceBuildInfoDirs "<BUILD_INFO_DIR>[,<BUILD_INFO_DIR>…​]" - 用于存储布局比较的项目先前版本的其他构建信息目录的可选路径。使用此选项时,在使用 --reference 选项或 @custom:oz-upgrades-from 注释中的合约名称或完全限定名称之前,使用前缀 <dirName>: 引用这些目录之一,其中 <dirName> 是目录短名称。每个目录短名称必须是唯一的,包括与主构建信息目录相比。如果传入多个目录,请用逗号分隔它们或多次调用该选项,每个目录一次。

  • --exclude "<GLOB_PATTERN>" [--exclude "<GLOB_PATTERN>"…​] - 排除对源文件路径中与任何给定 glob 模式匹配的合约的验证。例如,--exclude "contracts/mocks/**/*.sol"。不适用于参考合约。如果传入多个模式,请多次调用该选项,每个模式一次。

  • --unsafeAllow "<VALIDATION_ERROR>[,<VALIDATION_ERROR>…​]" - 有选择地禁用一个或多个验证错误或警告。 逗号分隔的列表,其中包含以下一个或多个:

  • 错误:state-variable-assignment, state-variable-immutable, external-library-linking, struct-definition, enum-definition, constructor, delegatecall, selfdestruct, missing-public-upgradeto, internal-function-storage, missing-initializer, missing-initializer-call, duplicate-initializer-call

  • 警告:incorrect-initializer-order

  • --unsafeAllowRenames - 配置存储布局检查以允许变量重命名。

  • --unsafeSkipStorageCheck - 跳过检查存储布局兼容性错误。这是一个危险的选项,旨在作为最后的手段使用。

High-Level API

high-level API 是 validate 命令 的编程等效项。如果要从 JavaScript 或 TypeScript 环境验证项目的所有可升级合约,请使用此 API。

前提条件

validate 命令 的前提条件相同。

用法

导入 validateUpgradeSafety 函数:

import { validateUpgradeSafety } from '@openzeppelin/upgrades-core';

然后调用该函数以验证你的合约,并获取包含验证结果的项目报告。

validateUpgradeSafety
validateUpgradeSafety(
  buildInfoDir?: string,
  contract?: string,
  reference?: string,
  opts: ValidateUpgradeSafetyOptions = {},
  referenceBuildInfoDirs?: string[],
  exclude?: string[],
): Promise<ProjectReport>

从构建信息目录中检测可升级合约,并验证它们是否是升级安全的。返回包含结果的项目报告

请注意,此函数不会直接抛出验证错误。 相反,你必须使用项目报告来确定是否发现任何错误。

参数:

  • buildInfoDir - 构建信息目录的路径,其中包含带有 Solidity 编译器输入和输出的 JSON 文件。对于 Hardhat 项目,默认为 artifacts/build-info,对于 Foundry 项目,默认为 out/build-info。如果你的项目使用自定义输出目录,你必须在此处指定其构建信息目录。

  • contract - 要验证的合约的名称或完全限定名称。如果未指定,将验证构建信息目录中的所有可升级合约。

  • reference - 只能在同时提供 contract 参数时使用。 用于存储布局比较的参考合约的名称或完全限定名称。如果未指定,则使用 @custom:oz-upgrades-from 注释(如果它定义在正在验证的合约中)。

  • opts - 一个对象,其中包含 通用选项 中定义的以下选项:

  • unsafeAllow

  • unsafeAllowRenames

  • unsafeSkipStorageCheck

  • requireReference - 只能在同时提供 contract 参数时使用。与 unsafeSkipStorageCheck 选项不兼容。如果指定,则需要提供 reference 参数或合约具有 @custom:oz-upgrades-from 注释。

  • referenceBuildInfoDirs - 用于存储布局比较的项目先前版本的其他构建信息目录的可选路径。使用此选项时,在使用 reference 参数或 @custom:oz-upgrades-from 注释中的合约名称或完全限定名称之前,使用前缀 <dirName>: 引用这些目录之一,其中 <dirName> 是目录短名称。每个目录短名称必须是唯一的,包括与主构建信息目录相比。

  • exclude - 排除对源文件路径中与任何给定 glob 模式匹配的合约的验证。

返回:

ProjectReport
interface ProjectReport {
  ok: boolean;
  explain(color?: boolean): string;
  numPassed: number;
  numTotal: number;
}

一个对象,表示升级安全检查和存储布局比较的结果,并包含所有发现的错误的报告。

成员:

  • ok - 如果发现任何错误,则为 false,否则为 true

  • explain() - 返回一条详细解释错误的详细信息的消息(如果有)。

  • numPassed - 通过升级安全检查的合约数量。

  • numTotal - 检测到的可升级合约的总数。

Low-Level API

此 low-level API 已弃用。 请改用 High-Level API

low-level API 使用 Solidity 输入和输出 JSON 对象,并允许你对单个合约执行升级安全检查和存储布局比较。 如果要验证特定合约而不是整个项目,请使用此 API。

前提条件

编译你的合约以生成 Solidity 输入和输出 JSON 对象。 编译器输出必须包括存储布局。

请注意,validate 命令 中的其他前提条件不是必需的,因为 low-level API 不会自动检测可升级合约。 相反,你必须为你想要验证的每个实现合约创建一个 UpgradeableContract 的实例,并调用它上面的函数以获取升级安全和存储布局报告。

用法

导入 UpgradeableContract 类:

import { UpgradeableContract } from '@openzeppelin/upgrades-core';

然后为你想要验证的每个实现合约创建一个 UpgradeableContract 的实例,并调用它上面的 .getErrorReport() 和/或 .getStorageLayoutReport() 以分别获取升级安全和存储布局报告。

UpgradeableContract

此类表示可升级合约的实现,并提供对错误报告的访问。

constructor UpgradeableContract
constructor UpgradeableContract(
  name: string,
  solcInput: SolcInput,
  solcOutput: SolcOutput,
  opts?: {
    unsafeAllow?: ValidationError[],
    unsafeAllowRenames?: boolean,
    unsafeSkipStorageCheck?: boolean,
    kind?: 'uups' | 'transparent' | 'beacon',
  },
  solcVersion?: string,
): UpgradeableContract

创建一个新的 UpgradeableContract 实例。

参数:

  • name - 实现合约的名称,可以是完全限定的名称或合约名称。 如果多个合约具有相同的名称,则必须使用完全限定的名称,例如 contracts/Bar.sol:Bar

  • solcInput - 实现合约的 Solidity 输入 JSON 对象。

  • solcOutput - 实现合约的 Solidity 输出 JSON 对象。

  • opts - 一个对象,其中包含 通用选项 中定义的以下选项:

  • kind

  • unsafeAllow

  • unsafeAllowRenames

  • unsafeSkipStorageCheck

  • solcVersion - 用于编译实现合约的 Solidity 版本。

在 Hardhat 中,solcInputsolcOutput 可以从 Build Info 文件中获取,该文件本身可以使用 hre.artifacts.getBuildInfo 检索。
.getErrorReport
getErrorReport(): Report

返回:

  • 关于与代理合约有关的错误的报告,例如使用 selfdestruct
.getStorageUpgradeReport
getStorageUpgradeReport(
  upgradedContract: UpgradeableContract,
  opts?: {
    unsafeAllow?: ValidationError[],
    unsafeAllowRenames?: boolean,
    unsafeSkipStorageCheck?: boolean,
    kind?: 'uups' | 'transparent' | 'beacon',
  },
): Report

将可升级合约的存储布局与建议的升级的存储布局进行比较。

参数:

  • upgradedContract - 另一个 UpgradeableContract 实例,表示建议的升级。

  • opts - 一个对象,其中包含 通用选项 中定义的以下选项:

  • kind

  • unsafeAllow

  • unsafeAllowRenames

  • unsafeSkipStorageCheck

返回:

  • 关于与代理合约有关的错误的报告,例如使用 selfdestruct 和存储布局冲突。
Report
interface Report {
  ok: boolean;
  explain(color?: boolean): string;
}

一个对象,表示分析的结果。

成员:

  • ok - 如果发现任何错误,则为 false,否则为 true

  • explain() - 返回一条详细解释错误的详细信息的消息(如果有)。

← Foundry Upgrades API

  • 原文链接: docs.openzeppelin.com/up...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。