Solidity 錯誤處理
作者 Shray Jain
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:
- 驗證來自外部合約的回應
- 在最終執行前驗證狀態條件
- 驗證使用者輸入
revert 陳述式是什麼?
revert 不評估任何條件,也不依賴任何狀態或陳述式。revert 陳述式與 require 陳述式類似,revert 函式可處理與 require 函式相同類型的錯誤,但更適合用於複雜的邏輯閘門。
若呼叫了 revert 陳述式,未使用的 gas 會被退還,狀態也會還原至原始狀態。加入自訂訊息的能力與 require 函式相同。
以下是 Solidity 中 revert 陳述式的範例:
assert 函式是什麼?
assert 是用來檢查不應該為 false 的程式碼的函式,在防止不可能發生的情況上扮演重要角色。若 assert 函式回傳布林值 true,就會顯示終端錯誤(terminal bug),程式將不會執行。
與 require 及 revert 函式不同的是,assert 不會退還任何未使用的 gas,反而是 assert 函式會先消耗全部的 gas 供應,才將程式還原至原始狀態。有趣的是,在拜占庭(Byzantium)分岔之前,require 與 assert 函式的行為完全相同,但兩者會編譯成不同的操作碼(opcode)。
assert 類型例外
- 數值進行取模或除以零
- 呼叫了函式中零初始化的變數
- 將負數或過大的值轉換為列舉(enum)
- 存取陣列時索引為負數或超出預期範圍
assert 陳述式範例:
assert 陳述式的使用情境
理論上,assert 的使用頻率應低於 require 函式。開發者應考慮在以下情境使用 assert 函式:
- 在進行變更後驗證合約狀態
- 避免不應該發生的情況
- 檢查溢位(overflow)與下溢(underflow)參數
- 檢查不變量(invariant)
require vs. revert vs. assert
以下摘要簡要說明了 Solidity 三種錯誤處理函式:require、revert 與 assert。
require
- 用於函式開頭
- 驗證非法輸入
- 在執行前驗證狀態條件
- 退還剩餘的 gas
revert
- 與 require 相同
- 適用於更複雜的邏輯流程閘門(例如複雜的 if-then 區塊)
- 退還剩餘的 gas
assert
- 用於函式結尾
- 驗證不可能發生的情況
- 對靜態程式碼分析工具至關重要
- 不退還剩餘的 gas
如何深入了解 require 與 Solidity 錯誤處理
有興趣深入了解 Solidity 錯誤處理的開發者,可以報名 Alchemy University 的免費 Solidity 開發者速成課程。這個為期 7 週的免費新手訓練營,能幫助 web2 開發者以及完全的新手學習如何撰寫 Solidity 智能合約。若開發者對開發完全陌生,Alchemy University 的3 週 JavaScript 速成課程是進入 Ethereum 訓練營前很好的先修課程。
相關總覽

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


