智能合約語言 Solidity - 錯誤處理
大家好,這裏是鏈客區塊鏈技術問答社區,鏈客 ,有問必答!!
Solidity 是以太坊智能合約編程語言,閱讀本文前,你應該對以太坊、智能合約有所了解,如果你還不了解,建議你先看以太坊是什麽?
什麽是錯誤處理
錯誤處理是指在程序發生錯誤時的處理方式,Solidity處理的方式和常見的語言不一樣,其時通過回退狀態的方式來處理錯誤,發生異常時會撤銷當前調用所改變的狀態,同時給調用者返還一個錯誤標識。
為什麽要這麽設計呢?
我們把區塊鏈理解為全球分享的分布式事務性數據庫,全球共享意味著每個人都可以讀取其中記錄,想要修改這個數據庫的內容,就必須創建一個事務,意味著要的修改只能被完全應用或不進行。
如何處理
Solidity提供2個函數assert和requrie進行檢查,條件如果不滿足則拋出異常,assert函數通常用來檢查內部錯誤,另一個用來檢查輸入變量或合同狀態變量是否滿足條件及驗證調用外部合約返回值。如果正確使用assert,由一個solitity分析工具就可以分析出只能合約中的錯誤,幫助我們發現合約中邏輯錯誤的bug。
另外還有兩種方式來觸發異常:
revert函數可以用來標記錯誤並回退當前調用
使用throw關鍵字拋出異常
當子調用中發生異常時,異常會自動向上“冒泡”。 不過也有一些例外:send,和底層的函數調用call, delegatecall,callcode,當發生異常時,這些函數返回false。
在下面通過一個示例來說明如何使用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,運行正常。
運行測試3:附加2wei以及sendHalf參數為當前合約本身,在轉賬是發生異常,因為合約無法接收轉賬,錯誤提示上圖類似。
assert類型異常
在下述場景中自動產生assert類型的異常:
如果越界,或負的序號值訪問數組,如i >= x.length 或 i < 0時訪問x[i]
如果序號越界,或負的序號值時訪問一個定長的bytesN。
被除數為0, 如5/0 或 23 % 0。
對一個二進制移動一個負的值。如:5<<i; i為-1時。
require類型異常
在下述場景中自動產生require類型的異常:
調用throw
如果調用require的參數為false
如果在使用new創建一個新合約時出現第3條的原因沒有正常完成。
如果調用外部函數調用時,被調用的對象不包含代碼。
當發生require類型的異常時,Solidity會執行一個回退操作(指令0xfd)。
當發生assert類型的異常時,Solidity會執行一個無效操作(指令0xfe)。
在上述的兩種情況下,EVM都會撤回所有的狀態改變。是因為期望的結果沒有發生,就沒法繼續安全執行。註意assert類型的異常會消耗掉所有的gas, 而require從大都會版本(Metropolis, 即目前主網所在的版本)起不會消耗gas。
智能合約語言 Solidity - 錯誤處理