常见异常

异常类型

执行合约时,会出现四种类型的异常:

  • Assert-style 异常(断言异常)
  • Require-style 异常
  • Validation-style 异常
  • VM illegal-style 异常

Assert-style 异常

下列情况将会产生一个 assert-style 异常,会抛出 invalid opcode 错误,消耗所有的 Entropy(包括当前为止已经消耗的 Entropy 和未消耗的 Entropy, 即 fee_limit)

  • 访问数组的索引太大或为负数(例如 x[i] 其中 i >= x.length 或 i < 0)
  • 访问固定长度 bytesN 的索引太大或为负数
  • 用零当除数做除法或模运算(例如 5 / 0 或 23 % 0 )
  • 移位负数位
  • 将一个太大或负数值转换为一个枚举类型
  • 调用未被初始化的内部函数类型变量
  • 调用assert的参数(表达式)最终结果是false
  • 合约执行过程中超时
  • 发生JVMStackOverFlowException
  • 发生OutOfMem异常,即内存超过了3M
  • 合约运行过程中,发生了加法等溢出

Require-style 异常

下列情况将会产生一个 require-style 异常,会抛出 revert 错误,仅仅消耗当前为止已经消耗的Entropy,不包括未消耗的 Entropy(fee_limit). 典型错误信息 "REVERT opcode executed".

  • 调用throw
  • 如果你调用require的参数(表达式)最终结果为false
  • 如果你通过消息调用某个函数,但该函数没有正确结束(比如该函数耗尽了 Entropy,或者本身抛出一个异常)。如果调用函数时没有指定Entropy,会把所有Entropy都传进去,表面看来会消耗所有Entropy,只有设置了Entropy值,才能看出差别。该函数不包括低级别的操作call、send、 delegatecall或者callcode 。低级操作不会抛出异常,而通过返回 false 来指示失败。
  • 如果你使用new关键字创建合约,但合约没有正确创建(因为创建合约时无法指定Entropy,会把所有Entropy都传进去,表面看来会消耗所有Entropy)
  • 如果你的合约通过一个没有payable修饰符的公有函数(包括构造函数、fallback函数和一般的公有函数)接收VS
  • transfer() 失败
  • 调用revert()
  • 到达最大函数栈深64
    注:assert-style 和 require-style 这两种情况下,都会导致VVM 回退。回退的原因是不能继续安全地执行,因为没有实现预期的效果。 因为我们想保留交易的原子性,所以最安全的做法是回退所有更改。但是会进行 Entropy扣费。

Validation-style 异常

下列情况将会产生 validation-style 异常,由于这些异常是虚拟机执行前的检测,该类异常交易不会上链,也不会消耗任何 Entropy或 Photon.

  • 当前版本不支持虚拟机
  • 创建合约的时候,合约名字超出32字节
  • 创建合约的时候,消耗调用者资源的比例不在[0, 100]之间
  • 创建合约的时候,新生成的合约地址发生了hash冲突,即合约地址已经生成过
  • callvalue不为0,如果发生余额不足
  • feeLimit不在合法范围之内
  • 向不支持constant的节点发送了constant的请求
  • trigger的合约在数据库中不存在

VM illegal-style 异常

下列情况会发生一个 VMillegal-style 异常,该类异常交易不会上链,但是会在网络层惩罚发送该交易的节点,断开连接一段时间。

  • 创建合约的时候,OwnerAddress 和 OriginAddress 不相等
  • 广播了 constant 请求