assert() , require() 和 revert() 函數(shù)
Solidity 的錯誤處理模式
傳統(tǒng)方法:采用 throw 和 if ... throw 模式
例:
這行代碼:
if(msg.sender != owner) { throw; }
完全等價于如下三種形式:
if(msg.sender != owner) { revert(); }
assert(msg.sender == owner);
require(msg.sender == owner);
注意在 assert() 和 require() 例子中的條件聲明,是 if 例子中條件塊取反,也就是用 ==代替了 != 。
區(qū)別:
assert():想象為一個過于自信的實現(xiàn)方式,即使有錯誤,也會執(zhí)行并扣除gas。
require():想象為一個更有禮貌些的實現(xiàn)方式,會發(fā)現(xiàn)錯誤,并且原諒所犯錯誤(譯注:不扣除 gas)。
revert():碰到無效代碼后,仍將回滾所有狀態(tài),但是會用兩種不同于“無效代碼”方式處理:允許返回一個數(shù)值,
將剩余gas返還調(diào)用者。
require()用于:
確認有效條件,例如輸入,
確認合約聲明變量是一致的
從調(diào)用到外部合約返回有效值
如果正確使用,分析工具會評估合約并分辨出引起assert調(diào)用錯誤的條件和函數(shù)。正確函數(shù)代碼將會避免引起調(diào)用錯誤的 assert 聲明;如果發(fā)生就意味著合約中存在需要修復(fù)的bug。
為了更清楚地解釋:require() 聲明失敗應(yīng)該被認為是正常和健壯的情況(跟 revert() 一樣);而當 assert() 聲明失敗時,則意味著有些東西失控了,需要修復(fù)代碼中的問題。
判斷正確使用場景:
require():
- 驗證用戶輸入,即:
require(input<20); - 驗證外部合約響應(yīng),即:
require(external.send(amount)); - 執(zhí)行合約前,驗證狀態(tài)條件,即:
require(block.number > SOME_BLOCK_NUMBER)或者require(balance[msg.sender]>=amount) - 一般地,盡量使用
require函數(shù) - 一般地,
require應(yīng)該在函數(shù)最開始的地方使用
revert():
- 處理與
require()同樣的類型,但是需要更復(fù)雜處理邏輯的場景
如果有復(fù)雜的 if/else 邏輯流,那么應(yīng)該考慮使用 revert() 函數(shù)而不是require()。記住,復(fù)雜邏輯意味著更多的代碼。
assert():
- 檢查 overflow/underflow,即:
c = a+b; assert(c > b) - 檢查非變量(invariants),即:
assert(this.balance >= totalSupply); - 驗證改變后的狀態(tài)
- 預(yù)防不應(yīng)該發(fā)生的條件
- 一般地,盡量少使用
assert調(diào)用 - 一般地,
assert應(yīng)該在函數(shù)結(jié)尾處使用
基本上,require() 應(yīng)該被用于函數(shù)中檢查條件,assert() 用于預(yù)防不應(yīng)該發(fā)生的情況,但不應(yīng)該使條件錯誤。
另外,“除非認為之前的檢查(用 if 或 require )會導(dǎo)致無法驗證 overflow,否則不應(yīng)該盲目使用 assert 來檢查 overflow”