在编写智能合约时,一定要注意对合约参数和行为的检查,尤其是那些对外部开放的合约函数。

 

Solidity提供了require、revert、assert等关键字来进行异常的检测和处理。

一旦检测并发现错误,整个函数调用会被回滚,所有状态修改都会被回退,就像从未调用过函数一样。

 

以下分别使用了三个关键字,实现了相同的语义。

require(_data == data, "require data is valid"); if(_data != data) { revert("require data is valid"); } assert(_data == data);

 

不过,这三个关键字一般适用于不同的使用场景:

 

  • require:最常用的检测关键字,用来验证输入参数和调用函数结果是否合法。

  • revert:适用在某个分支判断的场景下。

  • assert: 检查结果是否正确、合法,一般用于函数结尾。

 

在一个合约的函数中,可以使用函数修饰器来抽象部分参数和条件的检查。

在函数体内,可以对运行状态使用if-else等判断语句进行检查,对异常的分支使用revert回退。

在函数运行结束前,可以使用assert对执行结果或中间状态进行断言检查。

 

在实践中,推荐使用require关键字,并将条件检查移到函数修饰器中去;这样可以让函数的职责更为单一,更专注到业务逻辑中。同时,函数修饰器等条件代码也更容易被复用,合约也会更加安全、层次化。

 

在本文中,我们以一个水果店库存管理系统为例,设计一个水果超市的合约。这个合约只包含了对店内所有水果品类和库存数量的管理,setFruitStock函数提供了对应水果库存设置的函数。在这个合约中,我们需要检查传入的参数,即水果名称不能为空。

pragma solidity ^0.4.25; contract FruitStore {    mapping(bytes => uint) _fruitStock;    modifier validFruitName(bytes fruitName) {        require(fruitName.length > 0, "fruite name is invalid!");        _;    }    function setFruitStock(bytes fruitName, uint stock) validFruitName(fruitName) external {        _fruitStock[fruitName] = stock;    }}

 

如上所述,我们添加了函数执行前的参数检查的函数修饰器。同理,通过使用函数执行前和函数执行后检查的函数修饰器,可以保证智能合约更加安全、清晰。智能合约的编写需要设置严格的前置和后置函数检查,来保证其安全性。