恢復 Solidity v0.8 中算術溢出的原因
function nice(uint8 x) public returns(uint8 z) { uint8 z = 0; z = z + 240; require(z = z + x, "overflow"); }
在 0.8 更新之前,我一直在將此程式碼與 Solidity 一起使用。如果發生溢出,事務將通過消息恢復
overflow
。現在,我要升級到 0.8。如果我使用相同的程式碼,我的測試案例將失敗,因為它們不再收到
overfow
回复消息。如果我按照編譯器的建議刪除require
並離開z = z + x
,我不知道它將返回什麼消息。我想要實現的是使用 0.8 並且仍然得到overflow
恢復消息。注意我不想使用
unchecked
.
一點解釋
首先,您應該熟悉“未經檢查的算術”的概念,它是v0.8 重大更改列表的一部分:
算術運算在下溢和上溢時恢復。您可以使用unchecked { … }來使用之前的包裝行為。
重要的是,上溢和下溢使用 REVERT 操作碼而不是 INVALID 操作碼:
失敗的斷言和其他內部檢查(如除以零或算術溢出)不使用無效操作碼,而是使用恢復操作碼。
如果您是Hardhat的使用者,在測試您的合約時,您將在終端中收到以下 JavaScript/TypeScript 錯誤:
錯誤:處理事務時出現 VM 異常:使用緊急程式碼 0x11 恢復(算術運算在未檢查塊之外下溢或溢出)
看起來 Solidity 選擇了十六進制數字 0x11 作為與算術上溢和下溢相關的恐慌程式碼。
程式碼更新
unchecked
不幸的是,如果不將程式碼包裝在一個塊中,就無法實現您想要的。首先你這樣做,然後你做SafeMath所做的事情:檢查總和是否大於
x
; 如果不是,則操作已超出最大 uint8。function nice(uint8 x) public returns(uint8 z){ uint8 z = 0; z = z + 240; unchecked { z = z + x; require(z >= x, "Your custom message here"); } }
建議
當您現在可以定義自定義錯誤時,為什麼還要使用還原原因?
error Overflow(uint8 z, uint8 x); function nice(uint8 x) public returns(uint8 z){ uint8 z = 0; z = z + 240; unchecked { z = z + x; if (z < x) { revert Overflow(x, z); } } }
我認為這是 Solidity 開發的天賜之物。與還原原因字元串相比,自定義錯誤更容易處理、更高效、更優雅。
Solidity 0.8 包括以下關於算術上溢/下溢的更改:
算術運算在下溢和上溢時恢復。您可以使用 unchecked { … } 來使用之前的包裝行為。
在完全評估 require 語句之前,會觸發 Solidity 0.8 算術異常。我相信如果不使用
unchecked
. 但好消息是,在 Solidity 0.8 中,您甚至不再需要該 require 語句,但您需要調整測試以擷取拋出的算術 Solidity 異常,而不是“溢出”錯誤。