【Solidity智能合約系列】09--Solidity錯誤處理

前言

我想錯誤處理這個詞骑丸,對于有過編程經(jīng)驗的人來說都不陌生舌仍,它是指程序運行過程中發(fā)生錯誤(Error)或者異常(Exception)的處理方式。在類似Java這樣的語言中通危,我們是通過try...catch...捕捉異常來處理錯誤的铸豁,然而Solidity處理錯誤和我們常見的語言不一樣,下面我們就一起來了解一下在Solidity中的錯誤處理是怎么樣的菊碟。

Solidity是通過回退狀態(tài)的方式节芥,發(fā)生異常時會撤消當(dāng)前調(diào)用(及其所有子調(diào)用)所改變的狀態(tài),同時給調(diào)用者返回一個錯誤標(biāo)識逆害。

我們可以把區(qū)塊鏈理解為是全球共享的分布式事務(wù)性數(shù)據(jù)庫头镊。全球共享意味著參與這個網(wǎng)絡(luò)的每一個人都可以讀寫其中的記錄。如果想修改這個數(shù)據(jù)庫中的內(nèi)容魄幕,就必須創(chuàng)建一個事務(wù)相艇,事務(wù)意味著要做的修改(假如我們想同時修改兩個值)只能被完全的應(yīng)用或者一點都沒有進行。Solidity錯誤處理就是要保證每次調(diào)用都是事務(wù)性的纯陨。

錯誤處理

Solidity提供了兩個函數(shù)assertrequire坛芽,用于條件檢查,如果條件不滿足則拋出異常翼抠。assert函數(shù)只能用于檢查內(nèi)部錯誤和不變量咙轩,require函數(shù)用于檢查輸入變量或合同狀態(tài)變量是否滿足條件以及驗證調(diào)用外部合約返回值。如果使用assert合理的話阴颖,有一個Solidity分析工具就可以幫我們分析出智能合約中的錯誤活喊,幫助我們發(fā)現(xiàn)合約中有邏輯錯誤的bug。

正確運行的代碼應(yīng)該永遠不會達到失敗的assert狀態(tài)膘盖,如果發(fā)生了這種情況胧弛,你應(yīng)該修復(fù)你合約中的bug。

還有2種方式可以觸發(fā)異常侠畔,revert函數(shù)可以用來標(biāo)識一個錯誤结缚,并且回退當(dāng)前調(diào)用。將來可能會在revert中包含有關(guān)錯誤的詳細(xì)信息软棺,throw關(guān)鍵字可以用作revert()的替代红竭。

注意:
從0.4.13版本,throw關(guān)鍵字已被棄用喘落,將來會被淘汰茵宪。

當(dāng)子調(diào)用中發(fā)生異常時,異常會自動向上“冒泡”瘦棋。 不過也有一些例外:send稀火,以及底層的函數(shù)調(diào)用call, delegatecallcallcode赌朋,當(dāng)發(fā)生異常時凰狞,這些函數(shù)返回false

警告:
作為EVM虛擬機設(shè)計的一部分沛慢,在一個不存在的賬戶(地址)上調(diào)用底層的函數(shù)call赡若,delegatecallcallcode 也會返回成功团甲,所以我們在進行調(diào)用時逾冬,應(yīng)該總是優(yōu)先檢查地址是否存在。

注意:捕捉異常是不可能的躺苦,因為沒有try...catch...

在下面的例子中身腻,你可以了解到如何使用require來輕松檢查輸入條件,以及assert用于內(nèi)部錯誤檢查:

pragma solidity ^0.4.0;

contract Sharer {
    function sendHalf(address addr) public payable returns (uint balance) {
        require(msg.value % 2 == 0); // 僅允許偶數(shù)
        uint balanceBeforeTransfer = this.balance;
        addr.transfer(msg.value / 2); 
         // 如果失敗圾另,會拋出異常霸株,下面的代碼就不執(zhí)行
        assert(this.balance == balanceBeforeTransfer - msg.value / 2);
        return this.balance;
    }
}

Remix中去執(zhí)行上面的這段代碼:

測試1:附加1wei (奇數(shù))去調(diào)用sendHalf函數(shù),這時會發(fā)生異常集乔,如下圖:


運行測試2:附加2wei 去調(diào)用sendHalf函數(shù)去件,運行正常。

assert類型異常

在以下情景中會產(chǎn)生assert類型的異常:

  1. 如果訪問數(shù)組時發(fā)生了越界或者數(shù)組下標(biāo)為負(fù)數(shù)(如i >= x.lengthi < 0時訪問x[i])
  2. 如果序號越界扰路,或負(fù)的序號值時訪問一個定長的bytesN尤溜。
  3. 被除數(shù)為0(如5/023 % 0)。
  4. 在移位運算中汗唱,對一個二進制移動一個負(fù)的值(如:5<<i; i為-1時)宫莱。
  5. 整數(shù)進行可以顯式轉(zhuǎn)換為枚舉時,如果將過大值哩罪,負(fù)值轉(zhuǎn)為枚舉類型則拋出異常
  6. 如果調(diào)用未初始化的內(nèi)部函數(shù)類型的變量授霸。
  7. 如果調(diào)用assert巡验,它的參數(shù)的計算結(jié)果為false

require類型異常

在以下場景中會產(chǎn)生require類型的異常:

1、調(diào)用throw
2碘耳、調(diào)用require显设,其參數(shù)的運算結(jié)果為false
3、如果你通過消息調(diào)用一個函數(shù)辛辨,但在調(diào)用的過程中捕捂,并沒有正確結(jié)束(gas不足,沒有匹配到對應(yīng)的函數(shù)斗搞,或被調(diào)用的函數(shù)出現(xiàn)異常)指攒。底層調(diào)用如call,send,delegatecallcallcode除外,這幾個底層調(diào)用函數(shù)不會拋出異常僻焚,但它們會通過返回false來表示失敗允悦。
4、如果在使用new關(guān)鍵字創(chuàng)建一個新合約時溅呢,出現(xiàn)第3條的原因沒有正常完成澡屡。
5、如果調(diào)用外部函數(shù)調(diào)用時咐旧,被調(diào)用的對象不包含代碼驶鹉。
6、如果你的合約是通過沒有payable修飾符的public的函數(shù)來接收Ether(以太幣)時(包括構(gòu)造函數(shù)铣墨,和回退函數(shù))室埋。
7、如果合約通過一個publicgetter函數(shù)(public getter funciton)接收以太幣伊约。
8姚淆、如果.transfer()執(zhí)行失敗

  • 當(dāng)發(fā)生require類型的異常時,Solidity會執(zhí)行一個回退操作(指令0xfd)屡律。
  • 當(dāng)發(fā)生assert類型的異常時腌逢,Solidity會執(zhí)行一個無效操作(指令0xfe)。

在上述的兩種情況下超埋,EVM都會撤回所有的狀態(tài)改變搏讶。是因為期望的結(jié)果沒有發(fā)生,就沒法繼續(xù)安全執(zhí)行霍殴。必須保證交易的原子性(也就是一致性媒惕,要么全部執(zhí)行,要么一點改變都沒有来庭,不能只改變一部分)妒蔚,所以最安全的做法就是撤銷所有操作,讓整個交易沒有任何影響。

注意assert類型的異常會消耗掉用的所有的gas, 而require不會消耗任何gas肴盏,從Metropolis版本( 即目前主網(wǎng)所在的版本)起科盛。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市菜皂,隨后出現(xiàn)的幾起案子土涝,更是在濱河造成了極大的恐慌,老刑警劉巖幌墓,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冀泻,居然都是意外死亡常侣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門弹渔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胳施,“玉大人,你說我怎么就攤上這事肢专∥杷粒” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵博杖,是天一觀的道長。 經(jīng)常有香客問我剃根,道長哩盲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任狈醉,我火速辦了婚禮廉油,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苗傅。我一直安慰自己抒线,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布渣慕。 她就那樣靜靜地躺著嘶炭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摇庙。 梳的紋絲不亂的頭發(fā)上旱物,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音卫袒,去河邊找鬼宵呛。 笑死,一個胖子當(dāng)著我的面吹牛夕凝,可吹牛的內(nèi)容都是我干的宝穗。 我是一名探鬼主播户秤,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逮矛!你這毒婦竟也來了鸡号?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤须鼎,失蹤者是張志新(化名)和其女友劉穎鲸伴,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晋控,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡汞窗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赡译。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仲吏。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蝌焚,靈堂內(nèi)的尸體忽然破棺而出裹唆,到底是詐尸還是另有隱情,我是刑警寧澤只洒,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布许帐,位于F島的核電站,受9級特大地震影響毕谴,放射性物質(zhì)發(fā)生泄漏舞吭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一析珊、第九天 我趴在偏房一處隱蔽的房頂上張望羡鸥。 院中可真熱鬧,春花似錦忠寻、人聲如沸惧浴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衷旅。三九已至,卻和暖如春纵朋,著一層夾襖步出監(jiān)牢的瞬間柿顶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工操软, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘁锯,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像家乘,于是被迫代替她去往敵國和親蝗羊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 人在一起是團伙,心在一起是團隊业崖!在剽悍清單主題營的10天里野芒,一群有著共同目標(biāo)的戰(zhàn)友們,在實現(xiàn)目標(biāo)的道路上双炕,彼此鼓勵...
    獨自美麗的SHOW閱讀 233評論 3 3
  • 1.【我在本篇文章學(xué)到的重要概念】 人們?nèi)匀豢梢砸蕾嚺c陌生人的善意 2.【文章中讓我怦然心動的單詞】 motto ...
    17數(shù)436申婧柔閱讀 158評論 1 0
  • 2018年4月14日晚雄家,美國以敘利亞制造化學(xué)武器為由聯(lián)合英國、法國向敘利亞投放110枚導(dǎo)彈胀滚,多少人命喪黃泉...
    看門的二大爺閱讀 820評論 3 3
  • 新年回家拍的一些照片 這是我家門前一顆柿子樹趟济,別說還長的不錯哈 夏天,還能擋住熾熱的太陽光咽笼,乘涼也是很好的勝地 那...
    菲隨筆閱讀 250評論 0 0
  • 由于父母的忽略顷编,為了得到父母的愛,很多人從小把自己變成一個能干懂事的小孩剑刑,因為媳纬,能干和懂事=得到父母的歡心,為了不...
    Yling525閱讀 257評論 0 0