# EIP-3540 電腦程式的新衣服 - EVM 物件格式 (EOF)

# 注意事項

本文並沒有經過作者外的其他審查者審核,因此若內容有誤,請到 issue 區提出問題,我會儘速修改,謝謝。

# 簡單說明

我們要給電腦程式一種新的「包裝方式」,就像是給程式穿上一件新衣服。這種新包裝讓EVM 可以快速辨識程式的不同部分,比如哪部分是實際要執行的指令,哪部分只是資料。而且這件新衣服還可以隨時升級,加入更多功能!

這種新包裝的程式長得像這樣:

魔法標記, 版本號, (區塊類型, 區塊大小)+, 0, <區塊內容>

# 為什麼需要這個?

現在的以太坊區塊鏈上,程式沒有固定的結構。導致 EVM 需要每次在執行期間都需要重新檢查整個程式,這樣會額外消耗執行時間與資源。

有了這個新包裝,電腦只需要在程式上傳到區塊鏈前檢查一次。這樣做有幾個好處:

  1. 可以區分「指令」和「資料」,就像分清楚「食譜步驟」和「食材」一樣
  2. 引入版本的概念,可以更容易地加入新功能或移除舊功能,不像以前需要透過硬分岔的區塊高度來控制行為
  3. 讓程式更安全,因為電腦可以事先確認程式是否正確

# 詳細規格

新的格式用特殊的開頭標記(魔法標記)來識別。這個標記是 0xEF00,這有點像是一個特殊印章,說明「這是一個使用 EOF 格式的程式碼」。

# 容器規格

這個新包裝(我們稱為 EOF 容器)包含:

| 說明 | 長度 | 值 | | |-------------|----------|------------| | 魔法標記 | 2位元組 | 0xEF00 | | 版本號 | 1位元組 | 0x01–0xFF |

EOF 容器的開頭後面跟著至少一個區塊標頭。每個區塊標頭包含兩個欄位:

  1. 區塊類型
  2. 區塊大小區塊大小列表,取決於該區塊的類型。當區塊類型可以是多個且重複時,區塊大小列表會依序列出每個區塊的大小,其長度與該區塊類型的數量相同。
說明 長度
區塊類型 1位元組 0x01–0xFF uint8
區塊大小 2位元組 0x0000–0xFFFF uint16
區塊大小列表 動態 n/a uint16, uint16+

區塊標頭列表以區塊標頭終止位元組 0x00 結束。區塊內容主體緊接在其後面。

# 容器驗證規則

  1. 版本號 不能為 0
  2. 區塊類型 不能為 0。數值 0 是為區塊標頭終止位元組保留的。
  3. 必須至少有一個區塊(因此至少有一個區塊標頭)。
  4. 區塊外不能有多餘的位元組。這包括最後一個區塊後多出來的尾端資料。

# EOF 第一版

EOF 第一版由數個 EIP 組成,包括本 EIP。由於細節都被詳細定義在各自的 EIP 中,因此本規範中只是簡要討論。要理解 EOF 的完整範圍,請深入檢查對應的 EIP。

# 容器

EOF 第一版容器由 標頭主體 組成。

容器 := 標頭, 主體
標頭 := 
    魔法標記, 版本號碼, 
    類型區塊標記, 類型區塊大小, 
    程式區塊標記, 程式區塊數量, 程式區塊大小+,
    [容器區塊標記, 容器區塊數量, 容器區塊大小+,]
    資料區塊標記, 資料區塊大小,
    終止符號
主體 := 類型區塊, 程式區塊+, 容器區塊*, 資料區塊
類型區塊 := (輸入, 輸出, 最大堆疊增加)+

注意:, 是連接運算符,+ 應解釋為「一個或更多」前一項,* 應解釋為「零個或更多」前一項,[物件] 應解釋為可選物件。

# 標頭

名稱 長度 說明
魔法標記 2 位元組 0xEF00
版本號 1 位元組 0x01 EOF 版本
類型區塊標記 1 位元組 0x01 類型區塊的標記
類型區塊大小 2 位元組 0x0004-0x1000 16位無符號(unsigned)大端(big-endian)整數,表示類型區塊內容的長度,每個程式區塊4位元組
程式區塊標記 1 位元組 0x02 程式區塊大小區塊的標記
程式區塊數量 2 位元組 0x0001-0x0400 16位無符號大端整數,表示程式區塊的數量
程式區塊大小 2 位元組 0x0001-0xFFFF 16位無符號大端整數,表示程式區塊內容的長度
容器區塊標記 1 位元組 0x03 容器區塊大小區塊的標記
容器區塊數量 2 位元組 0x0001-0x0100 16位無符號大端整數,表示容器區塊的數量
容器區塊大小 4 位元組 0x00000001-0xFFFFFFFF 32位無符號大端整數,表示容器區塊內容的長度
資料區塊標記 1 位元組 0xFF 資料區塊大小區塊的標記
資料區塊大小 2 位元組 0x0000-0xFFFF 16位無符號大端整數,表示資料區塊內容的長度 (*)
終止符號 1 位元組 0x00 標記標頭的結束

(*) 對於尚未部署的容器,這可能大於實際內容長度,我們在部署的章節會特別討論。

# 主體

名稱 長度 說明
類型區塊 可變 n/a 存儲程式區塊詮釋資料
輸入 1 位元組 0x00-0x7F 程式區塊消耗的堆疊元素數量
輸出 1 位元組 0x00-0x7F 程式區塊回傳的堆疊元素數量
最大堆疊增加 2 位元組 0x0000-0x03FF 程式執行期間堆疊可能增加的最大高度
程式區塊 可變 n/a 任意位元碼
容器區塊 可變 n/a 任意 EOF 格式的容器
資料區塊 可變 n/a 任意位元組序列

注意輸出 的特殊值 0x80 被指定用於表示不回傳的函數,在單獨的 EIP 中定義。

# EOF 第一版驗證規則

對容器格式有以下有效性約束:

  • 類型區塊大小 可被 4 整除
  • 程式區塊的數量必須等於 類型區塊大小 / 4
  • 對於尚未部署的容器,資料主體長度可能短於 資料區塊大小
  • 容器的總大小不得超過 MAX_INITCODE_SIZE(如 EIP-3860 中定義)

# 執行語意的更改

對於 EOF 合約:

  • 執行從程式區塊 0 的第一個位元組開始
  • CODESIZECODECOPYEXTCODESIZEEXTCODECOPYEXTCODEHASHGAS 在 EOF 合約中被禁止,且將無後續的對應替代指令,驗證階段將被拒絕
  • CALLDELEGATECALLSTATICCALL 在 EOF 合約中也被禁止使用,其相關的替代指令將在單獨的 EIP 中另外定義。
  • 從 EOF 合約到非 EOF 合約(傳統合約、EOA、空帳戶)的 DELEGATECALL(或任何 EOF 的替代指令)是不允許的,如被觸發,將以呼叫深度檢查失敗的相同模式觸發錯誤。反之,允許從傳統合約到 EOF 合約的 DELEGATECALL,這樣能使現存的代理合約能夠使用 EOF 升級。

對於傳統合約:

  • 如果 EXTCODECOPY 的目標帳戶是 EOF 合約,那麼它將複製最多 2 個位元組的 EF00
  • 如果 EXTCODEHASH 的目標帳戶是 EOF 合約,那麼它將回傳 0x9dbf3648db8210552e9c4f75c6a1c3057c0ca432043bd648be15fe7be05646f5EF00 的雜湊值)。
  • 如果 EXTCODESIZE 的目標帳戶是 EOF 合約,那麼它將回傳 2。

注意 與傳統合約一樣,上述 EXTCODECOPYEXTCODEHASHEXTCODESIZE 的行為不適用於正在部署中的 EOF 合約目標。

# 設計原因

  1. 魔法標記的第一個位元組 0xEF 是特別保留的
  2. 第二個位元組 0x00 是為了避免與現有的程式衝突
  3. 版本號從 1 開始,這樣我們可以把舊的程式稱為「版本0」
  4. 把資料區塊放在最後,因為這是在部署程式時最可能會添加內容的部分,比如有部分的資料需要在初始化階段才能計算出來
Last Updated: 2025/5/21 下午3:18:25