天天看點

深入了解Solidity——Assert, Require, Revert 和 ExceptionsAssert, 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 ^;

contract Sharer {
    function sendHalf(address addr) public payable returns (uint balance) {
        require(msg.value %  == ); // 隻允許偶數
        uint balanceBeforeTransfer = this.balance;
        addr.transfer(msg.value / );
        //因為轉賬失敗而引發異常
        //不能在這裡回調,我們不可能
        //仍然有一半的錢。
        assert(this.balance == balanceBeforeTransfer - msg.value / );
        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——作用域和聲明

下一篇:深入了解Solidity——建立合約

繼續閱讀