最新內容會更新在主站深刻淺出區塊鏈社區
原文連接:智能合約語言 Solidity 教程系列9 - 錯誤處理html
這是Solidity教程系列文章第9篇介紹Solidity 錯誤處理。
Solidity系列完整的文章列表請查看分類-Solidity。數據庫
Solidity 是以太坊智能合約編程語言,閱讀本文前,你應該對以太坊、智能合約有所瞭解,
若是你還不瞭解,建議你先看以太坊是什麼編程
歡迎訂閱區塊鏈技術專欄閱讀更全面的分析文章。數組
錯誤處理是指在程序發生錯誤時的處理方式,Solidity處理錯誤和咱們常見的語言不同,Solidity是經過回退狀態的方式來處理錯誤。發生異常時會撤消當前調用(及其全部子調用)所改變的狀態,同時給調用者返回一個錯誤標識。注意捕捉異常是不可能的,所以沒有try ... catch...。安全
爲何Solidity處理錯誤要這樣設計呢?
咱們能夠把區塊鏈理解爲是全球共享的分佈式事務性數據庫。全球共享意味着參與這個網絡的每個人均可以讀寫其中的記錄。若是想修改這個數據庫中的內容,就必須建立一個事務,事務意味着要作的修改(假如咱們想同時修改兩個值)只能被徹底的應用或者一點都沒有進行。
學習過數據庫的同窗,應該理解事務的含義,若是你對事務一詞不是很理解,建議你搜索一下「數據庫事務「。
Solidity錯誤處理就是要保證每次調用都是事務性的。網絡
Solidity提供了兩個函數assert和require來進行條件檢查,若是條件不知足則拋出異常。assert函數一般用來檢查(測試)內部錯誤,而require函數來檢查輸入變量或合同狀態變量是否知足條件以及驗證調用外部合約返回值。
另外,若是咱們正確使用assert,有一個Solidity分析工具就能夠幫咱們分析出智能合約中的錯誤,幫助咱們發現合約中有邏輯錯誤的bug。編程語言
除了能夠兩個函數assert和require來進行條件檢查,另外還有兩種方式來觸發異常:分佈式
當子調用中發生異常時,異常會自動向上「冒泡」。 不過也有一些例外:send,和底層的函數調用call, delegatecall,callcode,當發生異常時,這些函數返回false。函數
注意:在一個不存在的地址上調用底層的函數call,delegatecall,callcode 也會返回成功,因此咱們在進行調用時,應該老是優先進行函數存在性檢查。工具
在下面經過一個示例來講明如何使用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,這時會發生異常,以下圖:
在下述場景中自動產生assert類型的異常:
在下述場景中自動產生require類型的異常:
當發生require類型的異常時,Solidity會執行一個回退操做(指令0xfd)。
當發生assert類型的異常時,Solidity會執行一個無效操做(指令0xfe)。
在上述的兩種狀況下,EVM都會撤回全部的狀態改變。是由於指望的結果沒有發生,就無法繼續安全執行。必須保證交易的原子性(一致性,要麼所有執行,要麼一點改變都沒有,不能只改變一部分),因此須要撤銷全部操做,讓整個交易沒有任何影響。
注意assert類型的異常會消耗掉全部的gas, 而require從大都會版本(Metropolis, 即目前主網所在的版本)起不會消耗gas。
咱們也推出了目前市面上最全的視頻教程:深刻詳解以太坊智能合約語言Solidity
目前咱們也在招募體驗師,能夠點擊連接瞭解。
歡迎來個人知識星球深刻淺出區塊鏈討論區塊鏈技術,同時我也會爲你們提供區塊鏈技術解答,做爲星友福利,星友可加入區塊鏈技術付費交流羣。
深刻淺出區塊鏈 - 系統學習區塊鏈,打造最好的區塊鏈技術博客。