Solidity 0.8.5 发布

首页 » 名家观点 » Solidity 0.8.5 发布
  • 译文出自:登链翻译计划 [1]

  • 译者:翻译小组 [2]

  • 校对:Tiny 熊 [3]

Solidity 团队于 2021 年 6 月 10 日发布 0.8.5 版本。

Solidity v0.8.5[4] 允许从 bytes 转换为 bytesNN 值,增加了 verbatim 内置函数以在 Yul 中注入任意字节码,并修复了几个较小的错误。

值得注意的新功能

字节转换

完整的功能文档 可以在这里 [5] 找到。

这个版本引入了将 bytesbytes 切片转换为固定字节长度类型 bytes1/…/bytes32 的能力。虽然固定长度的字节类型之间的转换一直是可能的,而现在也可以将动态大小的字节类型转换成固定长度的字节类型。

如果一个字节数组长于目标固定字节类型,将进行末端截断:

    function f(bytes calldata c) public view returns (bytes8) {  
     // If c is longer than 8 bytes, truncation happens  
     return bytes8(c);  
    }  

调用 f("12345678") 将返回 12345678,如同调用 f("1234567890")。如果数组比目标固定类型短,它将在末尾填充零,所以调用 f("1234") 将返回 1234

使用 bytes 转换功能的一个好例子是在代理中使用:

    // SPDX-License-Identifier: GPL-3.0  
    pragma solidity ^0.8.5;  
    contract Proxy {  
     /// @dev Address of the client contract managed by this proxy  
     address client;  
     constructor(address_client) {  
      client =_client;  
     }  
     /// Forwards all calls to the client but performs additional checks for calls to "setOwner(address)".  
     function forward(bytes calldata_payload) external {  
      require(_payload.length >= 4);  
      bytes4 sig = bytes4(_payload[:4]);  

      if (sig == bytes4(keccak256("setOwner(address)"))) {  
       address owner = abi.decode(_payload[4:], (address));  
       require(owner != address(0), "Address of owner cannot be zero.");  
      }  
      (bool status,) = client.delegatecall(_payload);  
      require(status, "Forwarded call failed.");  
     }  
    }  

在 0.8.5 以前,不可能做到 bytes4 sig = bytes4(_payload[:4]);,相反,你必须使用以下方法进行转换:

    bytes4 sig =  
    _payload[0] |  
     (bytes4(_payload[1]) >> 8) |  
     (bytes4(_payload[2]) >> 16) |  
     (bytes4(_payload[3]) >> 24);  

Yul 中的 Verbatim

完整的功能文档可以在 这里 [6] 找到。

这个版本为 Yul 引入了一组 verbatim 内置函数,允许你在二进制中注入任意字节码。目前只能通过纯 Yul 来实现,也就是说,不能通过内联汇编来实现。

主要有两个用途(下文将详细介绍):

  1. 使用 Yul 不知道的操作码(因为它们只是被提议的,或者因为你的目标是一个与 EVM 不兼容的链)。

  2. 产生未被优化器修改的特定字节码序列。

这些函数是 verbatimi_o("", ...),其中 :

  • n 是一个介于 0 和 99 之间的小数,用于指定输入栈槽 / 变量的数量。

  • m 是一个介于 0 和 99 之间的十进制数,指定输出栈槽 / 变量的数量。

  • data 是一个字符串常量,包含字节的序列。

注意,在使用 verbatim 时有一些注意事项,关于它的细节可以在文档 [7] 中找到。

用于新的操作码

作为一个实际的例子,我们可以用它来方便地将一个新提出的 EVM 操作码注入二进制。以提议的 BASEFEE(在 0x48)操作码为例(见 EIP-3198[8] 和 EIP-1559[9]),由于 Solidity 编译器目前不支持这个操作码,人们可以使用 verbatim 在 Yul 中实现它。

    {  
     function basefee() -> out {  
      out := verbatim_0i_1o(hex"48")  
     }  

     sstore(0, basefee())  
    }  

下面是另一个例子,它有一个输入参数为 verbatim

    let x := calldataload(0)  
    // The hex"600202" corresponds to EVM instructions:  
    // PUSH 02 MUL  
    // That is, it multiplies x by 2.  
    let double := verbatim_1i_1o(hex"600202", x)  

上面的代码将产生一个 dup1 操作码来检索 x(不过优化器可能直接使用 calldataload 操作码的结果),后面直接是 600202。该代码被假定为消耗 x 的(复制的)值,并在堆栈的顶部产生结果。然后编译器生成代码,为 double 分配一个堆栈槽,并将结果存储在那里。

用于 Optimism 使用场景

第二个使用场景对于像 Optimism 这样的第 2 层解决方案来说是很有用的,以及其他类似的情况,比如字节码分析或调试。Optimism 目前使用一个自定义的 Solidity 编译器,因为他们模拟了智能合约的执行,其中对状态的改变(存储、外部调用等)都不会直接执行,而是由对管理人合约的调用来代替,该合约存储了这些改变以备验证。这方面的问题是检查合约是否符合这些限制(即为每一个变化正确调用管理人合约),特别是由于这必须由链上欺诈检测机制来完成。他们所做的是,检查合约是否使用了任何一个改变状态的操作码,除了调用管理人合约的 call 操作码之外。为了正确检测这个异常,导致这个 call 操作码的操作序列必须有一个特定的形式,通常,Solidity 优化器会进行一些重新排列,并破坏这个形式。幸运的是,verbatim 可以解决这个问题,这样 Optimism 就不需要再依赖自定义的 Solidity 编译器,可以使用所有后来的 Solidity 编译器版本而不需要修改。

Optimism 编译器可以采用由 Solidity 编译器生成的 Yul 代码,附加以下 Yul 辅助函数,并在语法上将所有改变状态的内置函数调用替换为其 ovm_ 对应的函数。例如,所有的 sstore(x, y) 调用被 ovm_sstore(x, y) 调用所取代。在这种替换之后,Yul 优化器甚至可以再次运行。(这段代码只说明了 sstore。)

    /// Generic call to the manager contract.  
    function ovm_callManager(arguments, arguments_size, output_area, output_area_size) {  
     verbatim_4i_0o(  
      hex"336000905af158600e01573d6000803e3d6000fd5b3d6001141558600a015760016000f35b",  
      arguments,  
      arguments_size,  
      output_area,  
      output_area_size  
     )  
    }  

    // Call a manager function with two arguments  
    function ovm_kall_2i(signature, x, y) {  
     // Store touched memory in locals and restore it at the end.  
     let tmp_a := mload(0x00)  
     let tmp_b := mload(0x20)  
     let tmp_c := mload(0x40)  
     mstore(0, signature)  
     mstore(4, x)  
     mstore(0x24, y)  
     ovm_callManager(0, 0x44, 0, 0)  
     mstore(0x00, tmp_a)  
     mstore(0x20, tmp_b)  
     mstore(0x40, tmp_c)  
    }  

    // Replace all calls to ``sstore(x, y)`` by ``ovm_sstore(x, y)``  
    function ovm_sstore(x, y) {  
     // The hex code is the selector of  
     // the sstore function on the manager contract.  
     ovm_kall_2i(hex"22bd64c0", x, y)  
    }  

完整的更新日志

语言特性方面

  • 允许从 bytesbytes 片转换到 bytes1/…/bytes32

  • Yul: 增加 verbatim 内置函数,以注入任意字节码。

编译器功能方面

  • 代码生成器:为 panic 异常代码插入辅助函数,而不是无条件地内联。

  • EVM:将默认的 EVM 版本设置为 Berlin

  • SMTChecker:函数定义可以用自定义的 Natspec 标签 custom:smtchecker abstract-function-nondet 来注解,以便在调用时用非确定性的值抽象化。

  • 标准 JSON/ 组合 JSON:新的工件 functionDebugData,包含函数入口点的字节码偏移,未来可能会有更多信息。

  • Yul 优化器:评估 keccak256(a, c),当内存位置 a 的值在编译时是已知的,c 是常数 <=32

AST 的变化

  • 增加成员 hexValue,用于 Yul 字符串和十六进制字符

还修复一些 bug ,衷心感谢所有帮助实现该版本的贡献者。

可以在这里 [10] 下载新版本的 Solidity 。


本翻译由 Cell Network[11] 赞助支持。

来源:https://blog.soliditylang.org/2021/06/10/solidity-0.8.5-release-announcement/

参考资料

[1]

登链翻译计划 :https://github.com/lbc-team/Pioneer

[2]

翻译小组 :https://learnblockchain.cn/people/412

[3]

Tiny 熊 :https://learnblockchain.cn/people/15

[4]

Solidity v0.8.5:https://github.com/ethereum/solidity/releases/tag/v0.8.5

[5]

可以在这里 :https://docs.soliditylang.org/en/v0.8.5/types.html#explicit-conversions

[6]

这里 :https://docs.soliditylang.org/en/v0.8.5/yul.html#verbatim

[7]

文档 :https://docs.soliditylang.org/en/v0.8.5/yul.html#verbatim

[8]

EIP-3198:https://eips.ethereum.org/EIPS/eip-3198

[9]

EIP-1559:https://eips.ethereum.org/EIPS/eip-1559

[10]

这里 :https://github.com/ethereum/solidity/releases/tag/v0.8.5

[11]

Cell Network:https://www.cellnetwork.io/?utm_souce=learnblockchain

Solidity 0.8.5 发布

0 0 投票数
文章评分
订阅评论
提醒
guest
0 Comments
内联反馈
查看所有评论
0
希望看到您的想法,请发表评论。x
()
x