Solidity語言學習筆記————26、Assert, Require, Revert 和 Exceptions
Assert, Require, Revert 和 Exceptions
Solidity使用state-reverting異常來處理錯誤。 這種異常將回滾當前呼叫(及其所有子呼叫)狀態的所有變化,並將錯誤標誌給呼叫者。
函式assert
和require
可以用於檢查條件,如果條件不滿足則丟擲異常。 assert
函式只能用於測試內部錯誤,並檢查不變數。
應該使用require
函式來確認input或合約狀態變數滿足條件,或者驗證呼叫外部合約的返回值。
如果正確使用,分析工具可以評估合約和函式呼叫是否assert
失敗。 正常執行的程式碼不應該assert
失敗;如果發生這種情況,則需要修復合約中的bug。
還有另外兩種方法可以觸發異常:
revert
函式可用於標記錯誤並回滾當前的呼叫。throw
關鍵字也可以用作revert()
的替代方法。
註解 |
---|
從0.4.13版本,throw 關鍵字已被棄用,將來會被淘汰。 |
當異常發生在子呼叫中時,它們會自動“冒泡”(即異常被重新丟擲)。
但也有例外:send
和底層函式call
, delegatecall
和 callcode
——在異常情況下返回false
,而不是“冒泡”。
警告 |
---|
如果呼叫的帳戶不存在,call , delegatecall 和 callcode 的返回值會是true ,EVM就是這麼設計的。如果需要,必須在呼叫之前檢查賬戶是否存在。 |
捕捉異常目前還做不到。
在下面的示例中,您可以看到如何使用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;
}
}
在以下情況下會生成assert
風格異常:
- 以過大或負數索引訪問陣列(比如
x[i]
,i >= x.length
或i < 0
) - 以過大或負數索引訪問固定長度的
bytesN
。 - 除數或模數為零(例如
5/0
或23%0
)。 - 對負數進行位移
- 將過大或負數轉化為列舉型別。
- 呼叫內部函式型別的0初始化(zero-initialized)變數。
assert
的條件為false
在以下情況下會生成require
風格異常:
- 呼叫
throw
- 呼叫
require
並且條件為false
- 通過訊息呼叫來呼叫函式,但是它沒有正確完成(即,用盡了gas,沒有匹配的函式,或者丟擲了異常),除非使用底層別的操作
call
,send
,delegatecall
或callcode
。 底層別的操作不會丟擲異常,而是通過返回false
來表示失敗。 - 使用
new
關鍵字建立合約但是失敗。 - 執行一個外部函式呼叫,其指向不包含程式碼的合約。
- 合約通過
public
函式接收Ether,但沒有payable
修飾符。包括建構函式和回退函式。 - 合約通過一個
public
的getter
函式接收Ether。 - 如果
.transfer()
失敗。
在內部,Solidity對require
-style異常執行一個revert操作(0xfd
指令),並執行一個無效操作(指令0xfe
)來丟擲一個assert
-style異常。 在這兩種情況下,這將導致EVM revert對狀態所做的所有更改。 revert的原因是沒有安全的方式來繼續執行,因為沒有發生預期的效果。 因為我們要保留交易的原子性,所以最安全的做法是恢復所有的變化,並使整個事務(或至少呼叫)無效。 請注意,assert
風格的異常消耗呼叫中可用的所有gas,而require
風格的異常將不會消耗從Metropolis版本開始的任何gas。
相關文章
- Solidity中的assert()、require()與revert()SolidUI
- Solidity語言學習筆記————26、回退函式Solid筆記函式
- Solidity語言學習筆記————1、初識Solidity語言Solid筆記
- Solidity語言學習筆記————42、提示和技巧Solid筆記
- Solidity語言學習筆記————38、Solidity彙編Solid筆記
- Solidity語言學習筆記————36、 庫Solid筆記
- Solidity語言學習筆記————37、Using forSolid筆記
- Solidity語言學習筆記————4、常量Solid筆記
- Solidity語言學習筆記————25、作用域和宣告Solid筆記
- Solidity語言學習筆記————18、字串和函式Solid筆記字串函式
- Solidity語言學習筆記————33、事件(Events)Solid筆記事件
- Solidity語言學習筆記————12、陣列Solid筆記陣列
- Solidity語言學習筆記————34、繼承Solid筆記繼承
- Solidity語言學習筆記————35、抽象合約和介面Solid筆記抽象
- Solidity語言學習筆記————22、可見性和GettersSolid筆記
- Solidity語言學習筆記————43、安全考量Solid筆記
- Solidity語言學習筆記————28、純函式Solid筆記函式
- Solidity語言學習筆記————16、對映MappingSolid筆記APP
- Solidity語言學習筆記————32、建立合約Solid筆記
- Solidity語言學習筆記————23、函式呼叫和賦值Solid筆記函式賦值
- Solidity語言學習筆記————41、記憶體佈局Solid筆記記憶體
- Solidity語言學習筆記————7、單位和全域性變數Solid筆記變數
- Solidity語言學習筆記————39、獨立彙編Solid筆記
- Solidity語言學習筆記————27、檢視函式Solid筆記函式
- Solidity語言學習筆記————10、布林型、整型Solid筆記
- Solidity語言學習筆記————3、Remix的基本使用Solid筆記REM
- Solidity語言學習筆記————15、結構體StructSolid筆記結構體Struct
- Solidity語言學習筆記————14、左值運算子Solid筆記
- Solidity語言學習筆記————9、左值運算子Solid筆記
- Solidity語言學習筆記————17、原始檔對映Solid筆記
- Solidity語言學習筆記————5、全域性變數Solid筆記變數
- Solidity語言學習筆記————2、使用編譯器Solid筆記編譯
- Solidity語言學習筆記————30、函式過載Solid筆記函式
- Pytest學習筆記4-assert斷言筆記
- Solidity語言學習筆記————20、函式修飾符Solid筆記函式
- Solidity語言學習筆記————11、隱式轉換和顯式轉換Solid筆記
- Solidity語言學習筆記————24、輸入輸出引數Solid筆記
- Solidity語言學習筆記————13、固定大小位元組陣列Solid筆記陣列