异常(Excepions)

有一些情况下,异常是自动抛出来的(见下),你也可以使用throw来手动抛出一个异常。抛出异常的效果是当前的执行被终止且被撤销(值的改变和帐户余额的变化都会被回退)。异常还会通过Solidity的函数调用向上冒泡(bubbled up)传递。(send,和底层的函数调用call,delegatecallcallcode是一个例外,当发生异常时,这些函数返回false)。

捕捉异常是不可能的(或许因为异常时,需要强制回退的机制)。

在下面的例子中,我们将如展示如何使用throw来回退转帐,以及演示如何检查send的返回值。

pragma solidity ^0.4.0;

contract Sharer {
    function sendHalf(address addr) payable returns (uint balance) {
        if (!addr.send(msg.value / 2))
            throw; // also reverts the transfer to Sharer
        return this.balance;
    }
}

当前,Solidity在下述场景中自动产生运行时异常。

  1. 如果越界,或是负的序号值访问数组。
  2. 如果访问一个定长的bytesN,序号越界,或是负的序号值。
  3. 如果你通过消息调用一个函数,但在调用的过程中,并没有正确结束(gas不足,没有匹配到对应的函数,或他自己出现异常)。底层操作如call,send,delegatecallcallcode除外,它们不会抛出异常,但它们会通过返回false来表示失败。
  4. 如果在使用new创建一个新合约时,但合约的初化化由于类似3中的原因没有正常完成。
  5. 被除数为0。
  6. 对一个二进制移动一个负的值。
  7. 使用枚举时,将过大值,负值转为枚举类型。
  8. 使用外部函数调用时,被调用的对象并不包含代码。
  9. 如果你的public的函数在没有payable关键字时,却尝试在接收ether(包括构造函数,和回退函数)。
  10. 合约通过一个publicgetter函数(public getter funciton)接收ether
  11. 调用一个未初始化的内部函数。
  12. .transfer()执行失败
  13. assert返回false

当一个用户通过下述方式触发一个异常:

  1. 调用throw
  2. 调用require,但参数值为false。

当上述情况发生时,在Solidity会执行一个回退操作(指令0xfd)。与之相对的是,如果发生运行时异常,或assert失败时,将执行无效操作(指令0xfe)。在上述的情况下,由此促使EVM撤回所有的状态改变。这样做的原因是,没有办法继续安全执行了,因为想要发生的事件并未发生。因为我们想保持交易的原子性(一致性),所以撤销所有操作,让整个交易没有任何影响。

通过assert判断内部条件是否达成,require验证输入的有效性。这样的分析工具,可以假设正确的输入,减少错误。这样无效的操作码将永远不会出现。

处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。