# EIP-5450: EOF - 堆疊驗證
# 注意事項
本文並沒有經過作者外的其他審查者審核,因此若內容有誤,請到 issue 區提出問題,我會儘速修改,謝謝。
# 簡介
本文以 EIP-5450 EOF: Stack Validation 作為說明。
各位寫過 Solidity 嗎?或者執行過 EVM 程式碼嗎?還記得那個萬惡的「堆疊溢位」嗎? 今天我們將透過這個新的 EIP-5450 盡可能地消除這些問題,讓我們的程式碼更安全、更簡單!考試都考一百分呢(=゚ω゚)ノ
# 什麼是堆疊(Stack)?
想像一下,你的手邊有一堆積木要堆起來。如果堆得不穩,積木可能會倒塌,對吧? 在電腦世界裡,也有一種類似的結構,就叫做「堆疊」,就像是積木堆一樣,放置的時候要從底層往上方層層疊上;而當要取出積木時,則是從最上面開始向下一一拿取,這樣才能確保不會把底下的積木弄倒。 而在 EVM 裡,這些積木就是我們的程式碼執行途中放置的數值,可能是某個函式的參數,又或是某個指令計算出來的結果等。 然而,如果我們不小心把積木堆得太高,超過上限導致整個堆疊倒塌,就會發生「堆疊溢位」的問題。而在 EVM 的設計中,就要求堆疊的深度不能超過 1024 層。
# 這個新功能能怎麼幫助我們
這就像是在指考或者學測時,寫完試卷後,我們會多檢查一次,希望透過這次自我檢查,來避免不小心犯下的錯誤,就不會在最後的成績被扣分一樣。 同樣的模式,便是在我們將合約部署到鏈上之前,透過堆疊驗證來提前檢查與堆疊相關的問題,這樣就能避免在合約執行時發生錯誤,讓我們的程式碼更安全、更不容易到執行期間才慘遭毀滅性的打擊。
師爺,你給解釋解釋,好處有哪些?
- EVM 不需要每次都檢查堆疊是不是不夠被拿取(叫做「堆疊下溢」)。
- EVM 也不需要一直擔心堆疊會堆太高(叫做「堆疊上溢」),除了以下兩個例外
CALLF
與JUMPF
外。 - 確保每個 EVM 能正確結束,也就是執行的結尾需要是中止指令(terminating instructions)。
- 不讓人亂放資料在不應該放的地方,因此不鼓勵在程式碼中放置無法被執行的指令作為附加資料。
當然,不能只說好話,帶來好處的同時,也伴隨著一些缺點:
- 程式碼存在必要的規範,與過去 EVM 的程式碼是隨便開發者自由隨喜,現在需要像樂高積木一樣要按照零件的基礎規則來組成,如同其他常見的語言:JVM Bytecode、WebAssembly 等。
- 檢查的方式可能看起來有點複雜,但在這個 EIP 中規範的檢查方式其實很簡單!且只為線性掃描,不會帶來過多的負擔。
# 怎麼運作的?
每一段程式碼都會被單獨檢查。
# 第一次檢查
首先,檢查每一個指令是不是合法有效的指令(以 EOFv1 所定義的有效為主,請注意,你可能需要查詢其他 EOFv1 的 EIPs 來看哪些指令已經被棄用了)。
# 第二次檢查
然後,從頭到尾掃描所有指令,確保指令能正確地堆在一起。
想像一下,電腦會記錄每一步可能的積木高度。如果任何時候積木會倒塌或堆太高,電腦就會說「不行!這樣不安全!」
# 為什麼要這樣做?
# 經過檢查的程式碼有這些特點
- 沒有永遠用不到的指令(就像沒有永遠不會用到的積木)
- 沒有只能從後面跳過去的指令
- 積木永遠不會不夠用
- 積木只有在特定情況下才可能堆太高
- 可以從不同的積木高度跳到同一個地方
- 往回跳的時候,積木高度必須一樣(這樣就不會無限堆高了)
- 最後一個指令一定是結束指令或跳躍指令
# 積木堆太高的檢查
這個新功能讓電腦只需要在特定指令時檢查積木會不會堆太高,這樣可以讓程式跑得更快!
# 用不到的程式碼
這個檢查不允許有永遠用不到的指令,就像你不會買一組樂高但永遠不用其中某些積木一樣。這讓檢查變得更快,也避免了怪怪的程式碼。
# 結束時的乾淨堆疊
當函數結束時,堆疊必須乾乾淨淨的,就像你玩完玩具要收好一樣。這樣可以讓程式更簡單,也更容易理解。
# 積木順序
指令的順序很重要!就像樂高說明書一樣,你不能跳著步驟做,否則可能會搭錯。
# 這對現存的 EVM 會有影響嗎?
不用擔心!這個改變只會影響對啟用了 EOFv1 格式的新合約生效,現存與使用傳統的 EVM 格式不會受到任何影響。
# 一個簡單的例子
讓我們用一個遊戲模型來了解這是怎麼運作的:
# 積木遊戲範例
想像你有一個特殊的積木遊戲,有這些指令:
放上
- 放一塊積木到堆疊上拿走
- 從堆疊上拿走一塊積木跳到
- 跳到遊戲的另一個步驟如果高就跳
- 如果堆疊夠高,就跳到另一個步驟結束
- 遊戲結束
現在,我們來看看這個積木遊戲的指令:
步驟1: 放上 (堆疊高度變成1)
步驟2: 放上 (堆疊高度變成2)
步驟3: 如果高就跳到步驟6 (因為有2塊積木,所以會跳)
步驟4: 放上 (這步不會執行)
步驟5: 拿走 (這步不會執行)
步驟6: 拿走 (堆疊高度變成1)
步驟7: 跳到步驟9 (直接跳到步驟9)
步驟8: 拿走 (這步不會執行)
步驟9: 拿走 (堆疊高度變成0)
步驟10: 結束
# 電腦如何檢查這個遊戲
現在,想像電腦在你玩之前先檢查這個遊戲:
第一次檢查:確認所有指令都是有效的(放上、拿走、跳到、如果高就跳、結束)✓ 沒問題!
第二次檢查(堆疊驗證):
- TODO: 使用表格的方式來說明驗證機制
這個遊戲通過了檢查!因為:
- 沒有任何時候會拿走不存在的積木(堆疊下溢)
- 堆疊高度不會超過限制(堆疊上溢)
- 所有的指令都能被執行到(沒有無用的指令)
- 遊戲能夠正確結束
# 問題遊戲範例
再看一個有問題的遊戲:
步驟1: 放上 (堆疊高度變成1)
步驟2: 跳到步驟4 (直接跳到步驟4)
步驟3: 放上 (這步不會執行)
步驟4: 拿走 (堆疊高度變成0)
步驟5: 拿走 (糟糕!沒有積木可以拿了!)
步驟6: 結束
電腦檢查時會發現:
- 到了步驟5,堆疊已經是0了,再拿走就會變成-1,這是不可能的!
- 電腦會說:「這個遊戲有問題,不能玩!」
這就是堆疊驗證的作用!它可以在你開始玩之前,就找出遊戲中可能出現的問題,讓你玩得更安全、更順暢。