Solidity 0.8.5 新变化
- 原文:Solidity 0.8.5 发布公告
- 译文出自:登链翻译计划
- 译者:翻译小组
- 校对:Tiny 熊
- 本文永久链接:learnblockchain.cn/article…
Solidity团队于2021年6月10日发布0.8.5版本。
Solidity v0.8.5允许从bytes
转换为bytesNN
值,增加了verbatim
内置函数以在Yul中注入任意字节码,并修复了几个较小的错误。
完整的功能文档可以在这里找到。
这个版本引入了将bytes
和bytes
切片转换为固定字节长度类型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);
Verbatim
完整的功能文档可以在这里找到。
这个版本为Yul引入了一组verbatim
内置函数,允许你在二进制中注入任意字节码。目前只能通过纯Yul来实现,也就是说,不能通过内联汇编来实现。
主要有两个用途(下文将详细介绍):
这些函数是verbatim<n>i_<m>o("<data>", ...)
,其中:
n
是一个介于0和99之间的小数,用于指定输入栈槽/变量的数量。m
是一个介于0和99之间的十进制数,指定输出栈槽/变量的数量。data
是一个字符串常量,包含字节的序列。注意,在使用verbatim
时有一些注意事项,关于它的细节可以在文档中找到。
作为一个实际的例子,我们可以用它来方便地将一个新提出的EVM操作码注入二进制。以提议的BASEFEE
(在0x48
)操作码为例(见EIP-3198和EIP-1559),由于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这样的第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)
}
bytes
和 bytes
片转换到 bytes1
/.../bytes32
。verbatim
内置函数,以注入任意字节码。Berlin
。custom:smtchecker abstract-function-nondet
来注解,以便在调用时用非确定性的值抽象化。functionDebugData
,包含函数入口点的字节码偏移,未来可能会有更多信息。keccak256(a, c)
,当内存位置a
的值在编译时是已知的,c
是常数<=32
。hexValue
,用于Yul字符串和十六进制字符还修复一些 bug ,衷心感谢所有帮助实现该版本的贡献者。
可以在这里下载新版本的 Solidity 。
本翻译由 Cell Network 赞助支持。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!