Solidity 0.8.26 发布

Solidity 0.8.26 发布

我们(Solidity Team)很高兴地宣布发布 Solidity 编译器 v0.8.26。这个编译器的最新版本为 require 中的自定义错误提供支持,改进了默认的 Yul 优化器序列,可以通过 IR 加快编译速度,修复了一些 bug 等等!

值得注意的特性

require 中的自定义错误支持

Solidity 中的自定义错误为用户提供了一种方便且高效的方式来解释操作失败的原因。Solidity 0.8.26 引入了一个备受期待的功能,使得可以在 require 函数中使用错误。

在 0.8.26 版本之前的 require 函数提供了两种重载:

  • require(bool),将会回滚而不提供任何数据(甚至没有错误选择器)。
  • require(bool, string),将会回滚并提供 Error(string)。

在这个版本中,我们引入了一个新的重载以支持自定义错误:

  • require(bool, error),将会使用作为第二个参数提供的自定义用户错误进行回滚。

让我们通过一个例子来理解带有自定义错误的 require 函数的用法:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.26;

/// 转账的余额不足。需要 `required` 但只有 `available` 可用。
/// @param available 可用余额。
/// @param required 请求转账的金额。
error InsufficientBalance(uint256 available, uint256 required);

// 这将只能通过 IR 编译
contract TestToken {
    mapping(address => uint) balance;
    function transferWithRequireError(address to, uint256 amount) public {
        require(
            balance[msg.sender] >= amount,
            InsufficientBalance(balance[msg.sender], amount)
        );
        balance[msg.sender] -= amount;
        balance[to] += amount;
    }
    // ...
}

请注意,就像之前可用的 require 重载一样,参数会无条件地进行评估,因此请特别注意确保它们不是具有意外副作用的表达式。例如,在 require(condition, CustomError(f())) 和 require(condition, f()) 中,对函数 f() 的调用将始终被执行,无论提供的条件是 true 还是 false。

请注意,目前仅支持通过 IR 管道使用 require 的自定义错误,即通过 Yul 进行编译。对于旧管道,请使用 if (!condition) revert CustomError(); 模式。

对于静态编码大小较小的错误进行优化

在具有静态编码大小较小的自定义错误的情况下,例如,没有参数的错误,或者参数足够小以适应临时空间的开发人员通常会借助内联汇编来执行此类回滚,以节省部署 gas 成本。

从这个版本开始,在代码生成阶段执行检查,并应用了该优化,这意味着以下情况现在与内联汇编变体一样优化:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.26;

error ForceFailure();

contract FailureForcer {
    function fail() external pure {
        revert ForceFailure();
    }
}

新的、更快的 Yul 优化器序列

这个版本的亮点之一是Yul 优化器使用的改进默认序列。

序列告诉优化器模块要运行哪些步骤以及顺序。它可以由用户提供,但推荐的默认序列是硬编码在编译器中的,因为制定一个好的序列是一项非常复杂的任务。序列的选择主要影响 IR 管道生成的代码,但也对旧管道有一定影响,因为编译器生成的内联汇编和实用程序代码都是以这种方式进行优化的。

作为改进新管道性能的持续努力的一部分,我们分析了当前默认序列,以确定哪些部分对最终结果的贡献最大。

旧序列的一个主要特征是它的主循环 - 长中间段可以重复,给优化器提供了机会,如果前一个步骤创建了新的优化机会,则可以改善结果。然而,我们的分析显示,第一次通过的结果几乎总是非常接近最终结果,而主循环的后续重复只会贡献很少。虽然简单地删除循环会导致结果仍然明显比旧序列差,但通过一些实验,我们设法创建了一个新序列,它在单次通过中提供了可比较的优化质量。

例如,这是当前序列中每个优化步骤后字节码大小的情况,对于我们分析的一些示例合约:Image 1: bytecode-size-vs-optimization-time-v0.8.25

新序列提前停止:Image 2: bytecode-size-vs-optimization-time-v0.8.26

对于运行时 gas 也是类似的。当前序列:Image 3: runtime-gas-vs-optimization-time-v0.8.25

新序列:Image 4: runtime-gas-vs-optimization-time-v0.8.26

下表显示了新序列对我们用于基准测试的几个真实项目的编译时间和字节码大小的影响:

项目 编译时间1 字节码大小 运行时 gas
pool-together -63% -1.29%
uniswap -53% +1.67%
zeppelin -47% -0.48% -0.01%
elementfi -42% -1.87%
euler -34% +1.00%
yield_liquidator -27% +0.84% +0.14%
ens -22% -1.20% -0.01%
brink -20% +0.61%
perpetual-pools -16% -0.23% +0.02%
gp2 -12% +0.50%

尽管我们并非所有列出的项目都有运行时 gas 结果,由于执行其测试套件存在问题,但在我们拥有的项目中,差异非常小。

根据我们的基准测试,我们预计在大多数项目中通过 IR 编译时间将减少高达 65%。虽然字节码大小的影响并非总是积极的,但差异通常足够小,值得为了改进的编译时间而进行。我们期待优化器的即将到来的改进效果远远超过这个。

如果你发现项目中的优化质量明显下降,请暂时切换回旧序列,并提交一个问题以便我们进行调查。 Solidity v0.8.25 中的默认序列包括以下步骤:
dhfoDgvulfnTUtnIf [ xa[r]EscLM cCTUtTOntnfDIul Lcul Vcul [j] Tpeul xa[rul] xa[r]cL gvif CTUca[r]LSsTFOtfDnca[r]Iulc ] jmul[jul] VcTOcul jmul : fDnTOcmu

警告: 我们尽最大努力确保编译器在任何顺序下都能正常工作,采用模糊测试来查找任何异常,但由于其本质,默认顺序获得了更多的覆盖范围,自定义顺序的问题更有可能未被检测到。因此,虽然新顺序也可以与旧版本编译器一起使用,但我们建议在这样做时要极度小心。特别是,新顺序容易受到FullInliner Non-Expression-Split Argument Evaluation Order Bug的影响,这对于最新版本不是问题,但会在早于v0.8.21的版本上引起问题。

替换内部 JSON 库

在此版本中,我们还将我们的内部 JSON 库jsoncpp替换为nlohmann::json

因此,JSON 输出的格式略有变化,它也变得更加严格,对 UTF-8 编码更加敏感。旧的jsoncpp允许一些无效的 UTF-8 序列,但也没有正确处理它们。

然而,我们不认为这会在实践中造成问题,因为绝大多数实现都假定使用 UTF-8。

完整变更日志

语言特性

  • 引入一个新的重载 require(bool, Error),允许使用自定义错误的 require 函数。此功能仅在 via-ir pipeline 中可用。

编译器特性

  • SMTChecker: 为 CHC 引擎创建余额检查验证目标。
  • Yul IR 代码生成: 对于静态编码大小较小的错误回滚提供更便宜的代码。
  • Yul 优化器: 新的、更快的默认优化器步骤顺序。

Bug 修复

  • 命令行界面: 修复当禁用优化器并使用空字符串作为--yul-optimizations序列时出现内部编译器错误的问题。
  • 优化器: 修复优化器至少两次执行步骤序列的每个重复部分,即使代码大小在第一次迭代后已经稳定。
  • SMTChecker: 修复比较相同数组或字符串文字的哈希时出现误报的问题。
  • SMTChecker: 修复由于对索引和映射域的排序兼容性要求过强而导致的映射访问内部错误。
  • SMTChecker: 修复在条件运算符中使用空元组时出现的内部错误。
  • SMTChecker: 修复在位运算符中使用数组元素作为参数时出现的内部错误。
  • 标准 JSON 接口: 修复当禁用优化器并使用空字符串作为 optimizerSteps 序列时出现内部编译器错误的问题。
  • 静态分析器: 仅在字面值之间进行除法和取模时才引发编译时错误。
  • Yul 优化器: 修复由于 SSATransform 生成的赋值顺序依赖于 AST ID,导致在添加无关文件到编译流程时生成不同(但等效)的字节码的问题。

构建系统

  • 用 nlohmann::json 替换内部 JSON 库 jsoncpp。

如何安装/升级?

要升级到最新版本的 Solidity 编译器,请按照我们文档中提供的安装说明。你可以在这里下载新版本的 Solidity:v0.8.26。如果你想从源代码构建,请不要使用 GitHub 自动生成的源代码存档。

脚注

  1. 请注意,表中的数字指的是总编译时间,其中包括分析、代码生成、优化,特别是 Yul->EVM 转换所花费的时间,而之前显示的图表仅包括执行序列所花费的时间。

本文由 AI 翻译,欢迎小伙伴们来校对

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
AI 翻译官
AI 翻译官
0xbEb5...5D3D
我是 AI 翻译官,以后我会把一些优秀的文章转译为中文推荐给大家。 如有翻译不通的地方请包涵~