【Solidity智能合约系列】09--Solidity错误处理

前言

我想错误处理这个词,对于有过编程经验的人来说都不陌生,它是指程序运行过程中发生错误(Error)或者异常(Exception)的处理方式。在类似Java这样的语言中,我们是通过try...catch...捕捉异常来处理错误的,然而Solidity处理错误和我们常见的语言不一样,下面我们就一起来了解一下在Solidity中的错误处理是怎么样的。

Solidity是通过回退状态的方式,发生异常时会撤消当前调用(及其所有子调用)所改变的状态,同时给调用者返回一个错误标识。

我们可以把区块链理解为是全球共享的分布式事务性数据库。全球共享意味着参与这个网络的每一个人都可以读写其中的记录。如果想修改这个数据库中的内容,就必须创建一个事务,事务意味着要做的修改(假如我们想同时修改两个值)只能被完全的应用或者一点都没有进行。Solidity错误处理就是要保证每次调用都是事务性的。

错误处理

Solidity提供了两个函数assertrequire,用于条件检查,如果条件不满足则抛出异常。assert函数只能用于检查内部错误和不变量,require函数用于检查输入变量或合同状态变量是否满足条件以及验证调用外部合约返回值。如果使用assert合理的话,有一个Solidity分析工具就可以帮我们分析出智能合约中的错误,帮助我们发现合约中有逻辑错误的bug。

正确运行的代码应该永远不会达到失败的assert状态,如果发生了这种情况,你应该修复你合约中的bug。

还有2种方式可以触发异常,revert函数可以用来标识一个错误,并且回退当前调用。将来可能会在revert中包含有关错误的详细信息,throw关键字可以用作revert()的替代。

注意:
从0.4.13版本,throw关键字已被弃用,将来会被淘汰。

当子调用中发生异常时,异常会自动向上“冒泡”。 不过也有一些例外:send,以及底层的函数调用call, delegatecallcallcode,当发生异常时,这些函数返回false

警告:
作为EVM虚拟机设计的一部分,在一个不存在的账户(地址)上调用底层的函数calldelegatecallcallcode 也会返回成功,所以我们在进行调用时,应该总是优先检查地址是否存在。

注意:捕捉异常是不可能的,因为没有try...catch...

在下面的例子中,你可以了解到如何使用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;
    }
}

Remix中去执行上面的这段代码:

测试1:附加1wei (奇数)去调用sendHalf函数,这时会发生异常,如下图:


运行测试2:附加2wei 去调用sendHalf函数,运行正常。

assert类型异常

在以下情景中会产生assert类型的异常:

  1. 如果访问数组时发生了越界或者数组下标为负数(如i >= x.lengthi < 0时访问x[i])
  2. 如果序号越界,或负的序号值时访问一个定长的bytesN
  3. 被除数为0(如5/023 % 0)。
  4. 在移位运算中,对一个二进制移动一个负的值(如:5<<i; i为-1时)。
  5. 整数进行可以显式转换为枚举时,如果将过大值,负值转为枚举类型则抛出异常
  6. 如果调用未初始化的内部函数类型的变量。
  7. 如果调用assert,它的参数的计算结果为false

require类型异常

在以下场景中会产生require类型的异常:

1、调用throw
2、调用require,其参数的运算结果为false
3、如果你通过消息调用一个函数,但在调用的过程中,并没有正确结束(gas不足,没有匹配到对应的函数,或被调用的函数出现异常)。底层调用如call,send,delegatecallcallcode除外,这几个底层调用函数不会抛出异常,但它们会通过返回false来表示失败。
4、如果在使用new关键字创建一个新合约时,出现第3条的原因没有正常完成。
5、如果调用外部函数调用时,被调用的对象不包含代码。
6、如果你的合约是通过没有payable修饰符的public的函数来接收Ether(以太币)时(包括构造函数,和回退函数)。
7、如果合约通过一个publicgetter函数(public getter funciton)接收以太币。
8、如果.transfer()执行失败

  • 当发生require类型的异常时,Solidity会执行一个回退操作(指令0xfd)。
  • 当发生assert类型的异常时,Solidity会执行一个无效操作(指令0xfe)。

在上述的两种情况下,EVM都会撤回所有的状态改变。是因为期望的结果没有发生,就没法继续安全执行。必须保证交易的原子性(也就是一致性,要么全部执行,要么一点改变都没有,不能只改变一部分),所以最安全的做法就是撤销所有操作,让整个交易没有任何影响。

注意assert类型的异常会消耗掉用的所有的gas, 而require不会消耗任何gas,从Metropolis版本( 即目前主网所在的版本)起。

参考:
https://solidity.readthedocs.io/en/v0.4.21/control-structures.html#error-handling-assert-require-revert-and-exceptions

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容

  • 人在一起是团伙,心在一起是团队!在剽悍清单主题营的10天里,一群有着共同目标的战友们,在实现目标的道路上,彼此鼓励...
    独自美丽的SHOW阅读 235评论 3 3
  • 1.【我在本篇文章学到的重要概念】 人们仍然可以依赖与陌生人的善意 2.【文章中让我怦然心动的单词】 motto ...
    17数436申婧柔阅读 160评论 1 0
  • 2018年4月14日晚,美国以叙利亚制造化学武器为由联合英国、法国向叙利亚投放110枚导弹,多少人命丧黄泉...
    看门的二大爷阅读 828评论 3 3
  • 新年回家拍的一些照片 这是我家门前一颗柿子树,别说还长的不错哈 夏天,还能挡住炽热的太阳光,乘凉也是很好的胜地 那...
    菲随笔阅读 254评论 0 0
  • 由于父母的忽略,为了得到父母的爱,很多人从小把自己变成一个能干懂事的小孩,因为,能干和懂事=得到父母的欢心,为了不...
    Yling525阅读 260评论 0 0