跳至內容
0%

Solidity 錯誤處理

作者 Shray Jain

發布於 2022年10月4日閱讀時間 1 分鐘

Solidity 中的錯誤處理如何運作?

Solidity 使用狀態還原例外(state-reverting exceptions)來處理錯誤,此類例外會撤銷目前呼叫中對狀態所做的所有變更,同時向呼叫者標記錯誤。

Solidity 是一種物件導向程式語言,用於在 Ethereum 等區塊鏈上實作智能合約,其具有多種函式可用來處理編譯時期或執行時期可能發生的問題。雖然語法錯誤檢查發生在編譯時期,但執行時期錯誤較難捕捉,主要發生在合約執行過程中。執行時期錯誤的例子包括除以零型錯誤、陣列超出索引錯誤等等。

實際上,Solidity 的錯誤處理確保了**原子性(atomicity)**這項特性。當智能合約呼叫以錯誤終止時,所有狀態變更(即對變數、餘額等所做的變更)都會被還原,並沿著合約呼叫鏈一路往上還原。

值得注意的是,開發者可以透過宣告介面(interface)直接與其他合約互動。在 Ethereum 區塊鏈上,交易具有原子性,意味著交易要嘛完全成功,要嘛對狀態沒有任何影響並完全還原。

Solidity 三大主要錯誤處理函式是什麼?

Solidity 的錯誤處理原則上由三個特殊函式管理:assert、require 和 revert。在 0.4.10 版之前,Solidity 只提供單一的 throw 陳述式。

Solidity 的設計目標是 Ethereum Virtual Machine(EVM),並受到 C++、Javascript 和 Python 的影響。使用 Solidity,開發者可以建立用於投票、群眾募資、多重簽章錢包,甚至盲拍等用途的合約。

實務上,這代表開發者必須撰寫多個測試函式來檢查底層數值並拋出錯誤,而這種做法並未針對 gas 進行最佳化。在 Solidity 0.4.10 版發布時,引入了新的錯誤處理結構 assert、require 和 revert,而 throw 則被淘汰。

require 函式是什麼?

require 函式用於在執行前驗證輸入與條件。舉例來說,若條件為 false,require 函式會立即停止執行。換句話說,require 就像閘門檢查修飾詞(gate check modifier),阻止邏輯繼續執行函式,進而產生錯誤。require 非常適合用於邏輯流程的閘門控管,以及驗證函式的使用者輸入。

require 陳述式宣告了執行函式前應滿足的先決條件,這些條件必須在程式碼執行前得到滿足。require 函式接受單一參數,經過評估後會回傳布林值 true 或 false。若因條件為 false 而終止執行,未使用的 gas 會退還給呼叫者,狀態也會還原至原始狀態。此外還可以加入自訂的字串訊息。

以下是 Solidity 中 require 陳述式的範例:

基本範例展示如何使用 require 陳述式
基本範例展示如何使用 require 陳述式

require 陳述式的使用情境

出於實務考量,開發者可在下列情境使用 require

  1. 驗證來自外部合約的回應
  2. 在最終執行前驗證狀態條件
  3. 驗證使用者輸入

revert 陳述式是什麼?

revert 不評估任何條件,也不依賴任何狀態或陳述式。revert 陳述式與 require 陳述式類似,revert 函式可處理與 require 函式相同類型的錯誤,但更適合用於複雜的邏輯閘門。

若呼叫了 revert 陳述式,未使用的 gas 會被退還,狀態也會還原至原始狀態。加入自訂訊息的能力與 require 函式相同。

以下是 Solidity 中 revert 陳述式的範例:

assert 函式是什麼?

assert 是用來檢查不應該為 false 的程式碼的函式,在防止不可能發生的情況上扮演重要角色。若 assert 函式回傳布林值 true,就會顯示終端錯誤(terminal bug),程式將不會執行。

requirerevert 函式不同的是,assert 不會退還任何未使用的 gas,反而是 assert 函式會先消耗全部的 gas 供應,才將程式還原至原始狀態。有趣的是,在拜占庭(Byzantium)分岔之前,requireassert 函式的行為完全相同,但兩者會編譯成不同的操作碼(opcode)。

assert 類型例外

  1. 數值進行取模或除以零
  2. 呼叫了函式中零初始化的變數
  3. 將負數或過大的值轉換為列舉(enum)
  4. 存取陣列時索引為負數或超出預期範圍

assert 陳述式範例:

assert 陳述式的使用情境

理論上,assert 的使用頻率應低於 require 函式。開發者應考慮在以下情境使用 assert 函式:

  • 在進行變更後驗證合約狀態
  • 避免不應該發生的情況
  • 檢查溢位(overflow)與下溢(underflow)參數
  • 檢查不變量(invariant)

require vs. revert vs. assert

以下摘要簡要說明了 Solidity 三種錯誤處理函式:requirerevertassert

require

  • 用於函式開頭
  • 驗證非法輸入
  • 在執行前驗證狀態條件
  • 退還剩餘的 gas

revert

  • 與 require 相同
  • 適用於更複雜的邏輯流程閘門(例如複雜的 if-then 區塊)
  • 退還剩餘的 gas

assert

  • 用於函式結尾
  • 驗證不可能發生的情況
  • 對靜態程式碼分析工具至關重要
  • 不退還剩餘的 gas

如何深入了解 require 與 Solidity 錯誤處理

有興趣深入了解 Solidity 錯誤處理的開發者,可以報名 Alchemy University 的免費 Solidity 開發者速成課程。這個為期 7 週的免費新手訓練營,能幫助 web2 開發者以及完全的新手學習如何撰寫 Solidity 智能合約。若開發者對開發完全陌生,Alchemy University 的3 週 JavaScript 速成課程是進入 Ethereum 訓練營前很好的先修課程。

Background gradient

打造區塊鏈魔法

Alchemy 結合最強大的 Web3 開發者產品與工具,並提供資源、社群與卓越的支援。