深入理解Solidity——Assert, Require, Revert 和 Exceptions

Assert, Require, Revert 和 Exceptions

Solidity使用state-reverting异常来处理错误。 这种异常将回滚当前调用(及其所有子调用)状态的所有变化,并将错误标志给调用者。
函数assertrequire可以用于检查条件,如果条件不满足则抛出异常。
assert函数只能用于测试内部错误,并检查不变量。
应该使用require函数来确认input或合约状态变量满足条件,或者验证调用外部合约的返回值。
如果正确使用,分析工具可以评估合约和函数调用是否assert失败。 正常运行的代码不应该assert失败;如果发生这种情况,则需要修复合约中的bug。

还有另外两种方法可以触发异常:

  • revert函数可用于标记错误并回滚当前的调用。
  • throw关键字也可以用作revert()的替代方法。
注解
从0.4.13版本,throw关键字已被弃用,将来会被淘汰。

当异常发生在子调用中时,它们会自动“冒泡”(即异常被重新抛出)。
但也有例外:send和底层函数call, delegatecallcallcode——在异常情况下返回false,而不是“冒泡”。

警告
如果调用的帐户不存在,call, delegatecallcallcode的返回值会是true,EVM就是这么设计的。如果需要,必须在调用之前检查账户是否存在。

捕捉异常目前还做不到。

在下面的示例中,您可以看到如何使用require来轻松检查输入条件,以及assert如何用于内部错误检查:

pragma solidity ^0.4.0;

contract Sharer {
    function sendHalf(address addr) public payable returns (uint balance) {
        require(msg.value % 2 == 0); // 只允许偶数
        uint balanceBeforeTransfer = this.balance;
        addr.transfer(msg.value / 2);
        //因为转账失败而引发异常
        //不能在这里回调,我们不可能
        //仍然有一半的钱。
        assert(this.balance == balanceBeforeTransfer - msg.value / 2);
        return this.balance;
    }
}

在以下情况下会生成assert风格异常:

  • 以过大或负数索引访问数组(比如x[i]i >= x.lengthi < 0)
  • 以过大或负数索引访问固定长度的bytesN
  • 除数或模数为零(例如5/023%0)。
  • 对负数进行位移
  • 将过大或负数转化为枚举类型。
  • 调用内部函数类型的0初始化(zero-initialized)变量。
  • assert的条件为false

在以下情况下会生成require风格异常:

  • 调用throw
  • 调用require并且条件为false
  • 通过消息调用来调用函数,但是它没有正确完成(即,用尽了gas,没有匹配的函数,或者抛出了异常),除非使用底层别的操作callsenddelegatecallcallcode。 底层别的操作不会抛出异常,而是通过返回false来表示失败。
  • 使用new关键字创建合约但是失败。
  • 执行一个外部函数调用,其指向不包含代码的合约。
  • 合约通过public函数接收Ether,但没有payable修饰符。包括构造函数和回退函数。
  • 合约通过一个publicgetter函数接收Ether。
  • 如果.transfer()失败。

在内部,Solidity对require-style异常执行一个revert操作(0xfd指令),并执行一个无效操作(指令0xfe)来抛出一个assert-style异常。 在这两种情况下,这将导致EVM revert对状态所做的所有更改。 revert的原因是没有安全的方式来继续执行,因为没有发生预期的效果。 因为我们要保留交易的原子性,所以最安全的做法是恢复所有的变化,并使整个事务(或至少调用)无效。 请注意,assert风格的异常消耗调用中可用的所有gas,而require风格的异常将不会消耗从Metropolis版本开始的任何gas。

上一篇:深入理解Solidity——作用域和声明

下一篇:深入理解Solidity——创建合约

你可能感兴趣的:(Solidity文档翻译系列,以太坊去中心化应用开发)