Solidity是一门为实现智能合约而创建的高级编程语言,在区块链领域非常流行。FISCO BCOS从开始就支持使用Solidity进行合约开发,在FISCO BCOS v2.6.0中,社区使用evmone替换了原有的EVM解释器以获得更好的性能表现,同时也实现了对Solidity 0.6版本支持。本文将简要解读Solidity 0.6版本中的新特性和重要变更,以便社区开发者了解。
新特性解析
本节重点介绍Solidity 0.6版本中新加入的特性。
1. 引入新的try/catch特性
Solidity 0.6版本引入了新的try/catch特性,能够捕获外部函数调用或者合约创建时候的错误。
下面是一个例子。
创建CharitySplitter合约对象时,需要传入owner的地址,如果传入全0的地址,就会出现错误。如果错误出现在try后边的表达式中或者是表达式涉及的内部函数中,则不能够被捕获。
另一个需要注意的点是catch语句后的参数区别:
· catch Error(string memory reason)用于捕获revert(“reasonString”) 和 require(false, “reasonString”)
· catch (bytes memory lowLevelData)用于捕获除上一种情况之外的异常,例如assert()或者除以0或者异常没有错误信息的情况
· catch { … }用于不关心异常信息的情况
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.1;
contract CharitySplitter {
address public owner;
constructor(address _owner) public {
require(_owner != address(0), “no-owner-provided”);
owner = _owner;
}
}
contract CharitySplitterFactory {
mapping(address => CharitySplitter) public charitySplitters;
uint256 public errorCount;
event Log(string reason);
event LogBytes(bytes reason);
function createCharitySplitter(address charityOwner) public {
try new CharitySplitter(charityOwner) returns (
CharitySplitter newCharitySplitter
) {
charitySplitters[msg.sender] = newCharitySplitter;
} catch Error(string memory reason) {
// catch failing revert() and require()
errorCount++;
emit Log(reason);
} catch (bytes memory reason) {
// catch failing assert()
errorCount++;
emit LogBytes(reason);
}
}
}
2. struct和enum现在可以在合约文件中声明,不再限制在contract或library中
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.1;
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill
3. 数组切片可以作为calldata数组,例如
abi.decode(msg.data[4:], (uint, uint))
4.注释描述规范了Natspec在开发文档中支持多返回值,并强制执行与@param同名的检查
5. Yul和内联汇编有一个名为 leave 的新语句,该语句退出当前函数
6. 现在可以通过 payable(x) 把 address 转换为 address payable , x 需要为地址类型
需要明确指定的更改
本节列出了该版本中需要更明确显示代码的更改。
· 仅当函数被标记为 virtual 或在接口中定义,才可以被override,在接口外部没有实现的函数必须标记为 virtual,覆盖函数或修饰符时,必须使用新关键字 override 标注,当覆盖的函数或修饰符在多个基类定义时,需要在override关键字后的括号中列出,例如override(Base1, Base2) 。
· 数组的长度现在是只读的,即便是storage 类型的数组,不再能通过修改数组length来调整数组的大小,请使用 push(), push(value) 或 pop() 替代,或者赋值一个新的数组给原数组。
· 新关键字 abstract 用来标记一个抽象合约,如果一个合约有未实现的函数, 需要使用abstract标记,抽象合约不能通过 new 创建,并且编译时不会生成字节码。
· 库需要实现所有的函数,不再仅仅是内部函数。
· 内联汇编中声明的变量名可以不再以 _slot 或 _offset 结尾。
· 内联汇编中的变量声明可能不会屏蔽在内联汇编代码块之外声明的变量,如果变量名包含一个点,则该点之前的前缀可能与内联汇编块外部的任何声明都没有冲突。
· 子类合约不能屏蔽父类的状态变量,如果父类声明了非private的变量x,那么子类不能再声明同名状态变量。
语义和句法变化
本节描述了该版本中必须要修改代码的变更。
· 禁止从外部函数类型到 address 的转换,外部函数类型从这个版本起具有address 的成员,类似于现有的 selector 成员。
· 对于动态storage类型的数组, 函数 push(value) 不再返回新的长度。
· 未命名的函数通常称为 fallback 函数 ,被拆分为一个使用 fallback 关键字定义的回调函数和使用关键字 receive 定义的接受以太函数。
1. 如果有receive函数,每当调用数据为空时,该函数会被调用,无论是否有以太接收, receive 函数隐含带有 payable。
2. 当没有其他函数匹配时,新的回调函数会被调用 (如果不存在 receive 函数,则当调用数据为空时,也会调用回退函数)。回调函数可以选择是否标记为 payable,如果没有 payable 那么附加以太且匹配不到其他函数的调用会回滚。只有在实现升级或代理模式的合约时,才需要实现新的回调函数。
小结
为了实现对国密的支持,FISCO BCOS对FISCO-BCOS/solidity项目做了部分更改,主要是在国密模式将哈希算法替换为SM3。当前FISCO-BCOS/solidity项目有0.4.25、0.5.2、0.6.10三个版本,支持win、mac、Linux-generic64和Linux-aarch64四种平台,分别提供国密和非国密的solc编译器,相应的console项目也提供0.4、0.5和0.6三个版本的发布包。
FISCO BCOS开源社区会持续关注Solidity的进展,及时添加对新特性的支持,目前FISCO BCOS开发工作仍在持续进行,新版本需求也在持续收集规划中,欢迎大家共同探讨交流。