异常(Excepions)
优质
小牛编辑
132浏览
2023-12-01
有一些情况下,异常是自动抛出来的(见下),你也可以使用throw
来手动抛出一个异常。抛出异常的效果是当前的执行被终止且被撤销(值的改变和帐户余额的变化都会被回退)。异常还会通过Solidity的函数调用向上冒泡(bubbled up)传递。(send
,和底层的函数调用call
,delegatecall
,callcode
是一个例外,当发生异常时,这些函数返回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在下述场景中自动产生运行时异常。
- 如果越界,或是负的序号值访问数组。
- 如果访问一个定长的
bytesN
,序号越界,或是负的序号值。 - 如果你通过消息调用一个函数,但在调用的过程中,并没有正确结束(gas不足,没有匹配到对应的函数,或他自己出现异常)。底层操作如
call
,send
,delegatecall
或callcode
除外,它们不会抛出异常,但它们会通过返回false
来表示失败。 - 如果在使用
new
创建一个新合约时,但合约的初化化由于类似3中的原因没有正常完成。 - 被除数为0。
- 对一个二进制移动一个负的值。
- 使用枚举时,将过大值,负值转为枚举类型。
- 使用外部函数调用时,被调用的对象并不包含代码。
- 如果你的
public
的函数在没有payable
关键字时,却尝试在接收ether
(包括构造函数,和回退函数)。 - 合约通过一个
public
的getter
函数(public getter funciton)接收ether
。 - 调用一个未初始化的内部函数。
.transfer()
执行失败assert
返回false
当一个用户通过下述方式触发一个异常:
- 调用
throw
。 - 调用
require
,但参数值为false。
当上述情况发生时,在Solidity会执行一个回退操作(指令0xfd
)。与之相对的是,如果发生运行时异常,或assert
失败时,将执行无效操作(指令0xfe
)。在上述的情况下,由此促使EVM撤回所有的状态改变。这样做的原因是,没有办法继续安全执行了,因为想要发生的事件并未发生。因为我们想保持交易的原子性(一致性),所以撤销所有操作,让整个交易没有任何影响。
通过assert
判断内部条件是否达成,require
验证输入的有效性。这样的分析工具,可以假设正确的输入,减少错误。这样无效的操作码将永远不会出现。