assert() , require() 和 revert() 函数
Solidity 的错误处理模式
传统方法:采用 throw 和 if ... throw 模式
例:
这行代码:
if(msg.sender != owner) { throw; }
完全等价于如下三种形式:
if(msg.sender != owner) { revert(); }
assert(msg.sender == owner);
require(msg.sender == owner);
注意在 assert() 和 require() 例子中的条件声明,是 if 例子中条件块取反,也就是用 ==代替了 != 。
区别:
assert():想象为一个过于自信的实现方式,即使有错误,也会执行并扣除gas。
require():想象为一个更有礼貌些的实现方式,会发现错误,并且原谅所犯错误(译注:不扣除 gas)。
revert():碰到无效代码后,仍将回滚所有状态,但是会用两种不同于“无效代码”方式处理:允许返回一个数值,
将剩余gas返还调用者。
require()用于:
确认有效条件,例如输入,
确认合约声明变量是一致的
从调用到外部合约返回有效值
如果正确使用,分析工具会评估合约并分辨出引起assert
调用错误的条件和函数。正确函数代码将会避免引起调用错误的 assert 声明;如果发生就意味着合约中存在需要修复的bug。
为了更清楚地解释:require()
声明失败应该被认为是正常和健壮的情况(跟 revert()
一样);而当 assert()
声明失败时,则意味着有些东西失控了,需要修复代码中的问题。
判断正确使用场景:
require():
- 验证用户输入,即:
require(input<20);
- 验证外部合约响应,即:
require(external.send(amount));
- 执行合约前,验证状态条件,即:
require(block.number > SOME_BLOCK_NUMBER)
或者require(balance[msg.sender]>=amount)
- 一般地,尽量使用
require
函数 - 一般地,
require
应该在函数最开始的地方使用
revert():
- 处理与
require()
同样的类型,但是需要更复杂处理逻辑的场景
如果有复杂的 if/else
逻辑流,那么应该考虑使用 revert()
函数而不是require()
。记住,复杂逻辑意味着更多的代码。
assert():
- 检查 overflow/underflow,即:
c = a+b; assert(c > b)
- 检查非变量(invariants),即:
assert(this.balance >= totalSupply);
- 验证改变后的状态
- 预防不应该发生的条件
- 一般地,尽量少使用
assert
调用 - 一般地,
assert
应该在函数结尾处使用
基本上,require()
应该被用于函数中检查条件,assert()
用于预防不应该发生的情况,但不应该使条件错误。
另外,“除非认为之前的检查(用 if
或 require
)会导致无法验证 overflow
,否则不应该盲目使用 assert
来检查 overflow
”
网友评论