OpenZeppelin 对 ink! 和 cargo-contract 进行了安全评估,未发现严重问题,但发现了两个高危问题,Parity 团队正在解决。
该安全评估由 OpenZeppelin 编写。
对 ink! 和 cargo-contract 的安全审查被认为是成功的,没有检测到严重问题。 然而,发现了两个高危问题,Parity 团队已经在着手解决。 此外,OpenZeppelin 提出了一些修改和改进建议,以遵循最佳实践,最大限度地减少潜在的攻击面,并增强语言和工具。
Parity 团队要求我们对他们用于编写智能合约的 Rust eDSL, ink!的第 4 版进行安全审查。 这次合作是由Polkadot treasury资助的。 为了确保全面和结构化的评估,我们将合作分为三个不同的阶段。 第一阶段是了解 ink! 及其生态系统,第二阶段是进行安全审查,第三阶段是与 Parity 团队合作解决在前一阶段发现的问题。 我们利用从 ink! 和 cargo contract 中获得的知识进行安全分析。 我们的目标是验证 ink! 在缓解已知安全风险方面的有效性。 本安全概述报告的主要目标是分享我们发现的安全漏洞,提供修复建议,并分享我们在维护 OpenZeppelin 合约和构建开发者工具方面获得的经验和见解。
OpenZeppelin 认为此次合作是一个机会,可以帮助 Parity 团队改善开发者和审计人员未来使用 ink! 的整体体验。 即将交付的成果将包括审查下面“范围”部分中提到的所有项目。
OpenZeppelin 对 ink! 仓库、cargo-contract
CLI 进行了安全分析,并对 Solidity 进行了功能对比。 主要的重点是识别潜在的漏洞并提供改进建议。 具体而言,分析包括以下领域:
cargo-contract
进行安全分析。 我们将专注于识别功能的不安全实现、错误配置以及可能影响工具安全性的其他问题。审查范围内的提交和仓库是:
免责声明:鉴于本次服务的持续时间有限以及代码库的规模庞大,必须注意这次审查并非详尽无遗。 虽然 OpenZeppelin 尽了最大努力分享可能构成问题或改进领域的任何情况,但我们不能保证所有此类问题都已被检测到。
ink! 是一种基于 Rust 的特定领域语言(DSL),用于编写在基于 Substrate 框架构建的区块链上执行的智能合约。 设计这种语言的主要目的是使其尽可能类似于编写常规 Rust 代码,同时仍然安全高效。 选择 Rust 作为 ink! 的基础语言提供了多种好处,例如类型安全、内存安全和小二进制大小,使其成为开发人员的理想选择。
要部署使用 ink! 编写的智能合约,目标区块链必须具有 pallet-contracts
,这是一个支持合约执行的模块。 ink! 由 9 个 crate 组成:allocator、e2e、engine、env、ink、metadata、prelude、primitives 和 storage。
用于创建、编译、测试、上传、实例化和解码 ink! 合约的命令行界面 (CLI),从而使开发过程更加高效。 该应用程序由 4 个 crate 组成:
本节定义了此威胁模型的参与者、资产、外部依赖和入口点。
使用 ink! 和 cargo-contract 的用户或智能合约。 这使我们能够定义每个入口点所需的访问权限或特权,以及与每个资产交互所需的访问权限或特权。 考虑到哪些参与者与 ink! 交互有助于确定语言和使用它制作的智能合约如何受到损害。
ID | 名称 | 描述 |
---|---|---|
1 | 普通用户 | 通过非限制性消息和访问事件与智能合约交互的用户。 |
2 | 智能合约开发者 | 创建和维护 ink! 智能合约并使用 cargo-contract CLI 的开发者。 |
3 | 智能合约 | 一个 ink! 智能合约。 |
4 | 特权用户 | 可以通过其受限制的消息与智能合约交互的经过身份验证的用户。 |
5 | 核心开发者 | 维护 ink! 和 cargo-contract 的代码库仓库的开发者。 |
6 | 贡献者 | 对 ink! 或 cargo-contract 的开发做出贡献的非官方团队成员。 |
指定恶意用户如何访问 ink!、其命令行界面工具以及使用它构建的智能合约并与之交互。
ID | 名称 | 描述 | 参与者 |
---|---|---|---|
1 | 构造函数 | 这些入口点在实例化智能合约时调用。 它初始化合约的状态变量并在需要时执行一些逻辑。 | (2) 智能合约开发者, (3) 智能合约, (4) 特权用户 |
2 | 非限制性消息 | 具有 message 属性的公共函数是特权用户与智能合约交互的唯一方式。 此方法定义了合约响应特定参数的行为。 | (1) 普通用户, (2) 智能合约开发者, (3) 智能合约, (4) 特权用户 |
3 | 限制性消息 | 修改合约存储的敏感部分的函数,只能由合约存储中设置的特权 AccountID 调用。 |
(4) 特权用户 |
4 | 合约元数据 | 它提供有关合约名称、版本、方法、存储和通用数据的信息。 | (2) 智能合约开发者 |
5 | Cargo-contract CLI | 用于管理 ink! 智能合约的命令行界面 (CLI),包括编译、测试、部署和与已部署的智能合约交互。 | (2) 智能合约开发者, (3) 智能合约 |
6 | Dapp 的 UI | 用于与 ink! Dapps 交互的用户界面 (UI)。 | (1) 普通用户, (2) 智能合约开发者, (4) 特权用户 |
7 | GitHub 仓库 | 用于存储和管理 ink! 智能合约源代码的代码仓库。 此入口点是恶意开发者的常见入口点。 | (2) 智能合约开发者, (5) 核心开发者 |
指 ink! 及其使用它开发的合约中可能对攻击者有价值的元素,例如代币、余额、凭据和抽象资产。 这些资产需要防止潜在的攻击者。
ID | 名称 | 描述 | 参与者 |
---|---|---|---|
1 | ink 语言 | 语言实现及其规范,定义了 ink! 智能合约的行为。 ink! 语言的正确性和安全性对于防止潜在的攻击至关重要。 | (5) 核心开发者, (2) 智能合约开发者, (6) 贡献者 |
2 | ink 依赖 | 定义 ink! 语言及其依赖的 crate。 这些 crate 的所有权和控制对于 ink! 的稳定性和安全性至关重要。 | (5) 核心开发者, (6) 贡献者 |
3 | 开放合约存储 | 智能合约的存储包括各种用户数据,例如余额和津贴。 用户可以授予其他用户或智能合约对其资产的特权。 | (4) 特权用户, (3) 智能合约, (1) 普通用户 |
4 | 限制性合约存储 | 智能合约的存储可能包括用户权限、所有者和其他合约数据。 | (2) 智能合约开发者, (3) 智能合约, (4) 特权用户 |
5 | 用户密钥 | 用于签署交易并与合约交互的用户的私钥。 | (1) 普通用户, (4) 特权用户 |
6 | 修改代码库仓库 | 托管 ink! 和 cargo-contract 的仓库的完整性应仅由其核心开发者修改。 这包括版本控制和代码审查。 | (5) 核心开发者 |
7 | 链原生代币 | 用于支付 gas 的底层代币。 | (1) 普通用户, (3) 智能合约 |
8 | 项目声誉 | 声誉是 ink! 作为构建智能合约的首选语言取得成功的关键。 | (1) 核心开发者 |
这些是可能对语言和使用它制作的合约构成威胁的主要外部元素。
ID | 描述 |
---|---|
1 | Rust 语言及其包管理器 (Cargo),用于编写和管理 ink! 和 cargo-contract 的依赖。 |
2 | Substrate 运行时模块,为使用 ink! 编写的智能合约提供特定于区块链的功能和接口。 在这种情况下,最相关的是 contract-pallet,用于部署和执行 WebAssembly 智能合约的运行时。 |
3 | Substrate 客户端库,用于与 Substrate 区块链节点交互。 |
4 | Blake2 哈希实现。 |
5 | GitHub,ink! 和 cargo-contract 的官方代码库托管在此处。 |
6 | Rust WebAssembly 目标,用于将 ink! 智能合约编译为 WebAssembly。 |
7 | 第三方 Rust crate,为 ink! 和 cargo-contract 提供额外的功能。 |
8 | Substrate 区块链,ink! 智能合约将部署并在其上执行。 |
作为标准化的一种手段,OpenZeppelin 实施了一个用于对问题进行分类的矩阵,该矩阵符合 OWASP 风险评级方法。 该方法广泛应用于网络安全行业,提供了一种系统的方法来评估和确定与给定系统或应用程序相关的潜在风险的优先级。 然而,必须承认的是,OWASP 方法并非专门为区块链技术的独特特征而设计。 我们认为有必要在我们的矩阵中更加强调影响轴。 生态系统中的金融资产的存在及其去中心化的性质放大了损害的可能性。 此外,由于这些技术的不可变性,减轻损害的可能性通常很低。
术语:
潜在风险的可能性和影响分为三个等级:高、中和低。 风险的严重程度由其可能性和影响的组合决定,可以分为四个类别:严重、高、中和低,如表所示。 此外,如果我们发现其他不值得作为漏洞报告的建议,它们将在“注释 & 附加信息”部分下报告。
该问题将大量用户的敏感信息置于风险之中,并且/或者很可能对客户的声誉产生灾难性影响,或对客户和用户造成严重的财务影响。 它通常涉及但不限于某种形式的资金损失或锁定,或其他核心系统功能故障。
该问题将大量用户的敏感信息置于风险之中,并且/或者很可能对客户的声誉产生重大影响,或对客户和用户造成显着的财务影响。 它通常涉及但不限于某种形式的临时资金损失或锁定,或其他核心系统功能故障,并且可能存在合理的缓解措施。
该问题将一部分用户的敏感信息置于风险之中,如果被利用,将对客户的声誉产生不利影响,或者很可能对客户和用户造成适度的财务影响。
该问题相对较小,无法定期利用,或者客户根据其业务模型已将其归类为低影响的风险。
该问题与安全无关,但仍然值得注意以提高代码库的质量。
为了彻底评估 ink! 中的潜在漏洞,我们使用了 DASP 框架(一个广泛认可的开放项目,用于识别智能合约漏洞的前 10 个类别)和 SWC 注册表(EIP-1470
中概述的分类系统)。 这种方法使我们能够根据常见的 Solidity 漏洞识别最容易受到攻击的 ink! 组件。 我们可以使用 Dasp 10 的适用部分和 OpenZeppelin 的内部知识来识别以下威胁和策略:
由于 DASP 和 SWC 注册表可能不是最新的,因此我们还利用 OpenZeppelin 的内部知识来增强我们对可能在 ink! 上下文中应用的潜在漏洞的范围。
我们的重点是潜在的攻击者,他们可能会利用他们对 EVM 区块链中常见漏洞的了解,并试图在 ink! 的上下文中利用它们。
为了确保全面和结构化的审查,我们遵循了 SCSVS 清单(智能合约安全验证标准)。 这种方法可以帮助防止在评估 ink! 期间忽略潜在的漏洞。
本节列出了可以防止威胁实现的保护措施。 如果某个威胁没有对策,则它是一个漏洞。
重入:
set_allow_reentry
标志以允许被调用者重新进入当前合约。Checks-Effects-Interactions
模式,以确保在进行任何外部调用之前进行所有状态更改。访问控制:
算术问题:
不良随机性:
拒绝服务:
外部数据源依赖:
依赖项劫持:
以下是一些 Parity 团队可以实施以改善使用 ink! 和 cargo-contract
的整体用户和开发者体验的通用建议,同时促进 Solidity 开发者的顺利入门。
随着智能合约变得越来越普遍,拥有更好的工具和流程来监控和响应事件变得越来越重要。 创建与事件响应更一致的智能合约监控新计划,例如 Forta 或 OpenZeppelin Defender,可以帮助开发者和组织更好地保护他们的合约并快速响应潜在问题。
更新: 已确认,未解决。 Parity 团队表示:
与 Sirato Substrate Explorer 正在讨论中。
set_code_hash
的使用实现合约可升级性的一种方法是使用 set_code_hash 函数,该函数允许在不修改合约地址的情况下更新合约代码。 为了检测合约是否具有此功能,区块浏览器或第三方应用程序可以检查合约的 Wat 并查找 set_code_hash 导入。 如果存在,则表明合约可以升级其代码。
更新: 已确认,未解决。 Parity 团队表示:
与 Sirato Substrate Explorer 正在讨论中。
Solidity 中的 Immutable 变量允许开发者创建在运行时无法修改的只读变量,这对于安全性和效率目的非常有用。 在 ink! 中实现类似的功能将使开发者在设计和构建安全高效的智能合约方面具有更大的灵活性。
更新: 该建议已在 “ink” 仓库的 issue 1714 中得到确认。
添加在 trait 方法中具有默认实现的能力(类似于 Ethereum 生态系统中的抽象合约)将使开发者更容易编写可重用和可组合的合约。
更新: 该建议已在 “ink” 仓库的 issue 1689 中得到确认。
在 ink! 中添加对类似 Solidity 库的支持将允许开发者跨多个合约创建和重用公共函数,从而提高代码的可重用性并减少重复。 此添加还将有助于简化开发过程,并使开发者更容易构建更复杂的智能合约。
更新: 该建议已在 “ink” 仓库的 issue 1684 中得到确认。
cargo-contract
功能建议以下是 OpenZeppelin 团队在参与过程中详尽使用 cargo-contract
工具后提出的一些想法。 开发其中一些建议的可行性取决于用于构建 cargo-contract
的 rust 库的灵活性。
cargo contract generate-interface $language
在 cargo-contract
上创建一个为目标编程语言生成接口的命令,将使开发者更容易将其智能合约与其他用不同编程语言编写的合约集成。 此命令将促进跨合约通信,并帮助创建更复杂和高级的合约应用程序。
最初,该功能应支持 ink! 和 Solidity,因为这些是 pallet-contracts
最常见的选择。 其设计应该足够灵活,可以添加可能变得流行的其他语言。
更新: 该建议已在 “cargo-contract” 仓库的 issue 807 中得到确认。
build
命令当前多合约项目的构建过程需要单独构建每个合约,这可能是一个耗时且乏味的过程。 为了改进此过程并使其对开发者来说更方便,建议包括直接在 cargo contract build 命令中构建单个项目中的多个合约的功能。
此功能将允许开发者使用单个命令构建项目中的所有合约,从而减少构建过程所需的时间和精力。 这也将使开发者更容易管理和维护他们的项目,因为他们不必担心为每个合约管理多个构建命令。
更新: 该建议已在 “cargo-contract” 仓库的 issue 961 中得到确认。
encode
命令cargo contract encode [options] --message <MESSAGE> --args <ARGS...>
当前 cargo contract 中没有编码命令。 encode
函数将允许开发者编码函数调用的参数,从而更容易测试不同的场景和用例并调试代码。
更新: 已在 “cargo-contract” 仓库的 pull request #998 中解决。
cargo contract rpc [options] METHOD [PARAMS...]
用户无法使用当前可用的命令对节点进行原始 RPC 调用。
添加此功能将通过让开发者直接访问节点的 RPC 接口来改进调试过程,从而更容易诊断、修复和试验不同的场景。
更新: 该建议已在 “cargo-contract” 仓库的 issue 987 中得到确认。
cargo-contract
中实现 fork 功能为了促进测试和开发,在 cargo-contract
中实现 fork 功能会很方便。 此功能将允许开发者使用目标区块链的当前状态测试他们的智能合约,这可以帮助在将合约部署到实时网络之前识别和修复潜在问题。
更新: 该建议已在 “cargo-contract” 仓库的 issue 988 中得到确认。
cargo-contract accounts
命令cargo contract accounts
此命令应显示所有可用帐户的地址、别名和余额的列表。 它还可以指示哪个地址是默认帐户,并提供有关如何更改它的提示(请参阅下面的 set-default-account
命令)。
更新: 已确认,但尚未解决。
cargo contract set-default-account $account
命令cargo contract set-default-account $account
示例:
$ cargo contract set-default-account //Alice
此操作应将作为参数发送的 $account 设置为要在所有后续命令中使用的默认帐户。 在大多数情况下,当通过 CLI 与一个或多个合约交互时,通常总是使用相同的帐户,而不是多个帐户。 添加设置默认帐户的功能允许开发者避免每次实例化或与合约交互时都使用 --suri
标志。 如果在使用命令时定义了 --suri
标志,则该特定用途的默认帐户应被指定的帐户覆盖。
更新: 已确认,但尚未解决。
cargo contract deployed-contracts
命令$ cargo contract deployed-contracts
此命令应让开发者看到到目前为止已部署的所有合约的列表。 它应显示合约的名称(可以从元数据中获取)、部署的地址(或地址)以及所有可访问的方法及其参数(即它们的接口)的列表。 大多数这些功能已在合约 UI 工具中可用,但根据我们的经验,开发者在开发智能合约时会看到使用 CLI 工具的更多价值,并且能够看到已部署的所有合约及其接口。
更新: 已确认,但尚未解决。
cargo contract deployed-contract $contract
命令这与上面提到的命令类似,但用于特定合约。 此命令将接收合约 $contract
作为参数,并显示部署的地址(或地址)以及所有可访问的方法及其参数的列表。
更新: 该建议已在 “cargo-contract” 仓库的 issue 783 中解决。
cargo contract call
命令拆分为 cargo contract call
和 cargo contract send
$ cargo contract call --contract $contract --message $message --suri $account [metadata.json]
区块链生态系统中,可升级性是一个众所周知的特性:它允许修改、修复和改进合约实现,而无需在新地址中部署新合约。正如 Parity 团队所知,使用代理模式来实现可升级性既有优点也有缺点(下面提到了一些)。
此命令应允许开发人员将给定的代理 $proxy-address
升级到新的实现 $implementation-address
。为了缓解问题,此合约应检查:
更新: 在“cargo-contract”存储库的 issue 981 中确认了该建议。
开发人员可能会忘记向命令发送一个或多个参数,并且命令的默认行为是执行失败。 相反,提示输入缺少的参数可能是改善使用命令的整体体验的一种方法。
例如,当用户在控制台中编写 $ cargo contract
而不指定参数时,该工具可以通过以下方式提供帮助:
$ cargo contract call
$ 你想调用哪个合约?
[x] 合约 A
[ ] 合约 B
[ ] 合约 C
....
$ 你想调用哪个消息?
[ ] foo(u32)
[x] bar(u32, u32)
[ ] buz()
$ 指定参数,以逗号分隔:
$ 42,50
$ 使用默认元数据?
[x] 是
[ ] 否
在此示例中,如果用户手动(使用标志)指定他们想要调用的合约,则只会询问消息名称、参数和元数据,依此类推。 此示例可以应用于 cargo-contract
工具中接收参数的任何其他命令。
更新: 已确认,但未解决。 Parity 团队声明:
在你所有的建议中,我们为此建议分配了最低优先级。 我们普遍认为这可能有用,但它引发了关于 “cargo-contract” 更大愿景的讨论。 我们中的一些人认为 “cargo-contract” 应该是一个精简的工具,更多用户友好的功能应该构建在其之上,例如 swanky-cli 等工具,这些工具在底层使用 “cargo-contract”。
Parity 团队可以通过瞄准两个不同的受众:审计员和开发人员,从而提高 ink! 作为 parachains 的主要智能合约语言的采用率。 两个受众可能都有学习新语言的不同原因。 以下是我们在其他区块链生态系统中看到的一些已被验证为有价值的建议。
对于审计员:
对于开发人员:
Type智能合约语言和框架Timeline从 2023-01-16 到 2023-02-10Languagesink! – RustTotal Issues11 (3 已解决)Critical Severity Issues0 (0 已解决)High Severity Issues2 (0 已解决)Medium Severity Issues2 (0 已解决)Low Severity Issues5 (3 已解决)Notes & Additional Information2 (0 已解决)
ink! 具有一项功能,允许开发人员为给定的函数硬编码选择器。 此功能支持在保持相同选择器的同时更改函数名称,并且还有助于创建与语言无关的合约标准。
但是,在合约中允许自定义选择器可能会导致代理选择器冲突。 当使用者在实现上调用特定函数时,代理中匹配的选择器可能会导致代理中代码的意外执行。 此问题使诈骗项目更容易创建难以检测的恶意后门。 与 ink! 相比,Solidity 需要找到具有匹配选择器的函数签名,然后才能利用此漏洞,但这并非易事。 如果找到并添加了此类函数签名,则它们很可能会发出危险信号,因为该名称通常对代码库没有意义。
自定义选择器还会使使用函数选择器来识别特定函数的第三方监控或索引服务感到困惑。 这些服务可能依赖于标准选择器,这些选择器是标准的一部分或属于社区数据库,例如 4byte 目录。 如果合约使用自定义选择器,则这些服务可能无法识别和监控交易,从而导致错误。
鉴于概述的潜在危险,值得重新考虑此功能,并寻找替代方案来处理与语言无关的合约标准。或者,要求实现合约的元数据来构建代理,并在与实现发生选择器冲突时阻止代码被编译可能是一个可行的解决方案。如果使用自定义选择器的优势不大于潜在风险,请考虑删除它们。
更新: 已确认,将解决。可以在 “ink” 存储库的 issue 1643 上跟踪进度。
默认情况下,ink! 尝试将所有存储结构字段存储在单个存储单元下。此行为会导致可升级合约出现问题,因为代理和实现都会将其 Packed 字段写入相同的存储键(0x00000000
),除非开发人员为实现合约中的变量显式设置手动键。因此,可能会发生存储覆盖。
如果修改了实现中的第一个变量,它将更改代理存储布局中的第一个变量或其中的某些字节,具体取决于变量大小。反过来也是如此。
如果没有充分的信息,开发人员可能无法正确修改实现合约的存储,这可能会导致意外行为和合约的潜在故障。此外,这可能会导致不可预测的存储布局,从而进一步使问题复杂化。因此,开发人员务必确保他们拥有所有必要的信息,并采取适当的步骤来修改存储,以防止出现此类问题。
此外,升级之间没有用于检查存储布局是否已更改的验证。文档指定开发人员不应更改合约状态变量的声明顺序,也不应更改其类型。
即使违反了该限制,编译过程仍然会成功,但可能会导致值混淆或无法正确读取存储。这些问题可能会导致使用合约的应用程序中出现严重错误。
解决这些问题的一些缓解措施可能是:
cargo contract upgrade
命令。 这将保留实现的存储布局,并检查在升级之间是否未损坏,并检查在代理中定义的变量是否使用手动键而不是自动键定义,或者在代理中定义的每个变量的第一个位置是否与实现中定义的任何变量不冲突。更新: 已确认,将解决。可以在 “ink” 存储库的 issue 1679 和 1680 上跟踪进度。
重放攻击对区块链技术构成了严重的安全威胁。 为了维护区块链网络中签名的完整性,必须使用 nonce,该值跟踪给定帐户发出的交易数量。
在基于 Substrate 的区块链上,如果帐户的余额低于存在性存款,则 nonce 将被重置。 此操作可能会损害重放保护机制,并增加成功攻击的风险。 此外,长时间的过期或截止日期也可能会增加重放攻击的可能性。
除了仅依赖截止日期之外,请考虑添加替代的保护机制,例如在哈希消息时强制执行强大的域分隔符,或者建议开发人员将用于给定地址的签名存储在相应的合约中。 另一个解决方案是即使帐户的余额低于所需的最低限额,也保留 nonce。
更新: 已确认,将添加更多文档以使使用者了解此行为。 可以在 ink-docs 存储库的 issue 178 中关注进度。 Parity 团队声明:
此行为在 Substrate 世界中是正常的,我们唯一能做的就是向新手更好地强调它。 我们将添加有关此行为的文档。
ink!
智能合约中无法使用无界数组默认情况下,ink!
尝试将所有向量元素存储在单个存储单元下。 因此,查询一个项目会返回向量中的所有元素,但缓冲区具有有限的容量(在默认配置中约为 16KB)。 因此,任何尝试解码超出此限制的合约都会引发错误,从而无法实现某些智能合约,例如 ERC20votes
扩展 和 EnumerableSet
。 如果未超过限制,则操作将在执行中消耗大量 gas,导致与这些合约的交互因其成本而不太有吸引力。
此缺陷的影响可能非常重大,因为它限制了合约开发人员的功能。 无界数组对于许多用例至关重要,并且无法使用 ink!
实现它们会大大降低 Dapp 开发的可能性范围。
如果可能,请考虑创建另一个存储集合以在不同的槽中存储数组元素。
更新: 已确认,将解决。可以在 “ink” 存储库的 issue 1682 上跟踪进度。
upgradeable-contracts
示例 展示了实现可升级性的两种方法:
set-code-hash
目录中实现,展示了如何通过 ink::env::set_code_hash
函数更新实现逻辑来升级用 ink! 编写的智能合约。forward-calls
,展示了如何使用代理执行升级。 与 Solidity 中众所周知的 Proxy 模式实现类似,此方法中的想法是使用前者的上下文但后者的逻辑将调用从 Proxy 合约转发到实现合约。问题在于,在后者中,forward
函数 不使用 delegatecall
的实现,而是执行常规的调用操作。 因此,此示例中使用的上下文和存储将不是 Proxy 的上下文和存储,而是实现的上下文和存储,从而破坏了可升级性模式。
建议通过包含可升级和不可升级代理的示例来改进 ink! 中 delegatecall
的工作方式的文档。 此外,请考虑使用委托而不是常规调用来更新提到的示例。
更新: 在 pull request #1697 和 pull request #1704 中解决。
decode
命令中缺少输入验证Cargo 合约有一个解码命令来解析编码的输入或输出数据并提取底层值。 该功能有两个标志。 一个用于指示要解码的数据类型,另一个用于数据本身,该数据必须是十六进制值。 但是,该函数的当前实现接受的字节数多于目标类型所需的字节数,这可能会导致数据的错误解释。
考虑更新实现以正确接受目标函数所需的预期字节数。 此措施将降低混淆和意外结果的风险。
更新: 在提交 769c112 的 pull request #982 中解决。
ink! 允许开发人员为合约上定义的函数设置自定义选择器,如 自定义选择器可能会促成代理选择器冲突攻击 中所述。 如果未使用此功能,则改用来自 ink
crate 的 compute
函数来计算函数选择器。 此函数通过哈希函数名称并获取其前 4 个字节来计算选择器(类似于 Solidity 中的做法)。
问题在于,函数选择器仅使用函数名称来计算,而不考虑任何其他值。 这可能会导致函数选择器冲突,因为很可能在代理和实现中使用相同的函数名称。
以下是解决此问题的一些潜在的缓解策略:
upgrade
命令,如建议部分所述,以检查系统要升级到的代理和实现之间是否没有重复的函数选择器。proxy
,它可以覆盖 compute
函数 的实现,以便它不仅使用函数的名称,而且还将其附加到代理合约的名称,代理合约名称的哈希,或任何其他使选择器不同的内容,并正确记录它。 此外,proxy
宏将提高合约本身的可读性,因为开发人员和审计员会知道该合约的行为类似于代理。更新: 已确认,将解决。可以在 “cargo-contract” 存储库的 issue 981 上跟踪进度。
ManualKey
功能的误导性行为ink! 智能合约包括 ManualKey
功能,该功能允许开发人员指定 Mapping 或 Lazy 集合的键的值。 但是,此功能的潜在问题是,可以在代码中将键设置为零,而在元数据中显示为不同的值,从而导致混淆和可能的错误。
为避免此问题,可能值得禁止使用者在使用 ManualKey 时将变量的键设置为 0。 由于开发人员可能会依赖代码中指定的值,因此此更改可以防止混淆并提高代码的可靠性。
更新: 在提交 63c846d 的 pull request #1670 中解决。
使用 cargo contract CLI 构建 ink! 合约的过程会受到各种因素的影响,这些因素会改变最终产品。 这些因素包括 Rust 的版本、启用的功能、cargo-contract 版本、优化遍数和构建模式。 构建过程在不同的操作系统和架构中是不确定的。
构建过程的不确定性给合约验证带来了困难,这使得难以建立对合约及其可靠性的信任,这两者对于使用者来说都是至关重要的。
为解决此问题,请考虑标准化构建过程,并在最早阶段(而不是在合约部署之后)向开发人员提供明确的指导方针和通知。 这种方法将确保合约验证简单明了,并且使用者可以信任合约。
更新: 已确认,将解决。可以在 “ink-docs” 存储库的 issue 99 和 “cargo-contract” 存储库的 issue 525 上查看进度。
该文档的西班牙语版本有许多页面是用英语编写的,这会导致混淆,并使说西班牙语的人难以理解信息。
考虑完成西班牙语翻译,并在它们准备好投入生产之前禁用它们。
更新: 已确认,尚未解决。
README.md
引用内部 crateink! 存储库的内部 crate 引用了存储库的主要 README.md,而不是它们自己的 README.md,导致缺少有关模块的信息以及损坏的图像链接。
缺少这些信息可能会妨碍内部 crate 的可用性,使开发人员难以理解每个 crate 的目的和用法。 此外,损坏的图像链接会给使用者造成负面印象。
为每个 crate 创建单独的 README.md 文件将在很大程度上解决此问题。
更新: 已确认,将解决。可以在 “ink” 存储库的 issue 1690 上跟踪进度。
安全审查报告重点介绍了潜在的漏洞,并为改进 ink! 生态系统提供了建议。 我们很高兴地报告说,在此过程中与 Parity 团队的合作非常棒。 他们对我们的建议持开放态度,并且每周的会议都非常富有成效。
我们看到了 ink! 及其工具 cargo-contract
的巨大潜力,该工具展示了强大的安全措施,并坚定不移地致力于确保其使用者的安全。
总的来说,我们有信心,通过持续的合作和不断努力提高安全性,ink! 及其相关工具将在未来得到广泛采用。 我们期待看到 ink! 的持续发展和壮大。
有关 ink! 的更多信息,你可以访问 ink! 文档 或关注 ink! Twitter。 ink! 文档还对 智能合约如何在 Polkadot 中工作 进行了总体解释。
- 原文链接: blog.openzeppelin.com/se...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!