以太坊 EVM 規格從零推導:Yellow Paper 形式化定義的直覺化解讀
本文從以太坊黃皮書(Yellow Paper)的形式化定義出發,從第一性原理推導 EVM 的狀態轉換函數。我們不依賴任何第三方解讀,而是直接解讀 Yellow Paper 的符號系統,幫助讀者建立對 EVM 最底層運作的嚴格理解。這是給那些想真正搞懂以太坊,而不只停留在「使用」層面的開發者的文章。
title: 以太坊 EVM 規格從零推導:Yellow Paper 形式化定義的直覺化解讀
summary: 本文從以太坊黃皮書(Yellow Paper)的形式化定義出發,從第一性原理推導 EVM 的狀態轉換函數。我們不依賴任何第三方解讀,而是直接解讀 Yellow Paper 的符號系統,幫助讀者建立對 EVM 最底層運作的嚴格理解。這是給那些想真正搞懂以太坊,而不只停留在「使用」層面的開發者的文章。
tags:
- technical
- evm
- yellow-paper
- formal-verification
- first-principle
- state-transition
- ethereum
difficulty: advanced
date: 2026-04-01
parent: null
status: published
references:
- title: Ethereum Yellow Paper
url: https://ethereum.github.io/yellowpaper/paper.pdf
desc: 以太坊黃皮書,正式規格定義
tier: tier1
- title: Ethereum Yellow Paper (GitHub LaTeX)
url: https://github.com/ethereum/yellowpaper
desc: Yellow Paper 原始 TeX 源代碼
tier: tier1
- title: EVM Opcodes Reference
url: https://www.evm.codes/
desc: 完整的 EVM 操作碼參考
tier: tier2
disclaimer: 本網站內容僅供教育與資訊目的,不構成任何技術或投資建議。
datacutoffdate: 2026-04-01
knowledge_path: technical/evm/yellow-paper-analysis
以太坊 EVM 規格從零推導:Yellow Paper 形式化定義的直覺化解讀
說實話,第一次打開以太坊黃皮書(Yellow Paper)的時候,我是有點絕望的。滿頁的希臘字母、奇怪的數學符號、感覺像是研究生作業的推導過程......這玩意真的是給人類看的嗎?
但後來我慢慢意識到,這份文件是以太坊最嚴格的技術規格。它不是一本教程,也不是一篇部落格文章——它是一份「合約」,定義了以太坊虛擬機應該如何運作。任何偏離黃皮書的實現,原則上都不能稱為「以太坊」。
這篇文章的目的,是把黃皮書中最核心的 EVM 規格,用人話解釋一遍。我會保持數學推導的嚴謹性,但會用大量的直覺解釋和具體例子來幫助理解。目標讀者是那些不滿足於「會用 Solidity」,而想真正搞懂 EVM 底層運作的開發者。
為什麼要讀黃皮書?
我知道很多人會問這個問題。Solidity 編譯器會幫你處理所有事情,你真的需要知道 EVM 的狀態轉換函數長什麼樣子嗎?
我的答案是:需要,而且非常需要。
第一,調試和優化。當你的合約消耗過多 Gas、當你遇到奇怪的 revert、當你想理解某個安全漏洞的原理......這些問題都需要對 EVM 有深入理解。
第二,寫出更高效的代碼。了解狀態轉換函數的代價模型,你才能真正優化你的合約。比如,你知道為什麼 memory 操作比 storage 操作便宜幾個數量級嗎?這不是因為硬體差別,而是因為黃皮書裡的 Gas 計算公式就是這麼定義的。
第三,安全審計。如果你是安全審計員,不懂黃皮書就像不懂交通規則就上路的司機。你可能可以開車,但遇到事故的時候,你根本不知道是誰的錯。
第四,規格本身的魅力。以太坊黃皮書是密碼經濟學和分散式系統設計的精華。讀懂它,你對區塊鏈的理解會提升一個層次。
符號系統:黃皮書的語言
在開始之前,讓我們先熟悉黃皮書使用的符號系統:
基本集合:
σ - 世界狀態(World State)
一個從地址到帳戶狀態的映射
σ: Address → AccountState
B - 區塊(Block)
包含區塊頭和交易列表
Π - 區塊層面的世界狀態(Block-level World State)
在處理區塊開始前的世界狀態
Ω - 區塊報酬函數(Block Reward Function)
定義礦工/驗證者獲得的獎勵
Υ - 交易報酬函數(Transaction Receipt Function)
定義交易執行後的 receipt
Λ - 交易執行函數(Transaction Execution Function)
定義如何執行一筆交易
Φ - 狀態轉換函數(State Transition Function)
定義如何從一個狀態轉換到下一個狀態
基本類型:
μ - 機器狀態(Machine State)
EVM 的運行時狀態,包含:
- g: Gas 剩餘量
- pc: 程序計數器
- m: Memory
- i: 指令
- s: Stack
H - 區塊頭(Block Header)
包含所有區塊元數據
Γ - 環境資訊(Environment Information)
包含交易發送者、接收者、金額等
世界狀態:一切開始的地方
黃皮書的第一個核心定義是世界狀態(World State):
定義 1:世界狀態
世界狀態 σ 是從地址到帳戶狀態的映射:
σ: Address → AccountState
其中:
Address = B_{20} // 20 字節的地址空間
AccountState = (nonce, balance, storageRoot, codeHash)
對於 EOA(外部擁有帳戶):
AccountState = (n, b, ∅, ∅)
其中 n 是 nonce(交易計數),b 是餘額
對於合約帳戶:
AccountState = (n, b, s, c)
其中 s 是 storage root,c 是合約代碼的 hash
這個定義看起來很抽象,讓我們用一個具體例子來理解:
現實世界的例子:
假設以太坊世界狀態包含以下帳戶:
地址 0x1234...:{
nonce: 5,
balance: 100 ETH,
storageRoot: ∅,
codeHash: ∅
}
這是一個 EOA,有 5 筆交易歷史,100 ETH 餘額
地址 0xabcd...:{
nonce: 1,
balance: 0 ETH,
storageRoot: 0xdef...,
codeHash: 0x789...
}
這是一個合約,有 1 筆交易歷史,無餘額
交易執行:狀態轉換函數
黃皮書的核心是定義狀態轉換函數 Φ。這個函數描述了如何從一個世界狀態轉換到另一個:
定義 2:狀態轉換函數
Φ(σ, T) ≡ σ'
其中:
σ 是執行前的世界狀態
T 是待執行的交易
σ' 是執行後的世界狀態
狀態轉換函數的實現:
Φ(σ, T) ≡ Α(Ι(σ, T), T)
其中:
Ι 是交易初始化函數
Α 是交易應用函數
這個定義有兩層嵌套:
- 初始化:
Ι(σ, T)計算交易的初始環境,包括驗證簽名、計算費用等 - 應用:
Α(σ', T)實際執行交易,更新世界狀態
初始化函數
讓我們更詳細地看看初始化函數:
定義 3:交易初始化
Ι(σ, T) ≡ (σ', g', ...)
初始化函數負責:
1. 驗證交易簽名
2. 扣除交易費用(gas × gasPrice)
3. 計算新地址(如果是合約創建)
4. 初始化執行環境
黃皮書的符號看起來嚇人,但核心思想很簡單:在執行任何代碼之前,先做一堆準備工作。
Gas 計算
Gas 是以太坊最獨特的設計。黃皮書定義了 Gas 的消耗模型:
定義 4:Gas 消耗
Gas 的計算分為三部分:
1. 基本費用(Intrinsic Gas)
G_0(T) = G_txdata + G_txcreate + G_transaction
其中:
G_transaction = 21000(基本交易)
G_txdata = 每 byte 4 或 16 gas(取決於是否為零 byte)
G_txcreate = 32000(合約創建)
2. 操作費用(Operation Gas)
根據執行的 opcode 累加
具體數值見黃皮書附錄 G
3. 動態費用(Dynamic Gas)
根據 Memory 擴展、Storage 操作等動態計算
讓我們用一個具體例子:
例子:執行一筆 ETH 轉帳
交易內容:
- 從 EOA A 轉 1 ETH 到 EOA B
- Gas Price = 30 Gwei
Gas 消耗計算:
G_0 = G_transaction = 21000 gas
總費用 = 21000 × 30 Gwei = 0.00063 ETH
扣除後的狀態:
σ[A].balance -= 1 + 0.00063 ETH
σ[B].balance += 1 ETH
EVM 執行:形式化定義
現在讓我們深入到 EVM 的核心——狀態轉換函數 Λ:
定義 5:EVM 狀態轉換
Λ(σ, g, T) ≡ (σ', g', A, o)
其中:
σ, σ' : 世界狀態(輸入/輸出)
g, g' : 剩餘/消耗的 Gas
A : 執行後的狀態(成功/失敗)
o : 輸出數據(return data)
這個函數描述了 EVM 如何執行一段字節碼
機器狀態
黃皮書定義了 EVM 的機器狀態 μ:
定義 6:機器狀態
機器狀態 μ = (g, pc, m, i, s)
其中:
g : Gas 剩餘量
pc : 程序計數器(program counter)
m : Memory 內容
i : 當前指令(instruction)
s : Stack 內容
初始狀態:
μ_0 = (T.g, 0, {}, 0, [])
其中 T.g 是交易提供的 Gas
操作語義
每個 opcode 的語義在黃皮書中都有形式化定義。讓我們看幾個關鍵的:
定義 7:STOP
W_{STOP}(μ, σ) ≡ (σ, μ.g, A, ∅)
解釋:
- 不修改世界狀態
- 不消耗額外 Gas
- 執行成功(A = ∅ 表示成功)
- 無輸出
- 這是「正常」終止的方式
定義 8:ADD
W_{ADD}(μ, σ) ≡ (σ, μ.g - G_{mid}, μ', A, ∅)
其中:
G_{mid} = 3 gas
μ'.pc = μ.pc + 1
μ'.s = [μ.s[0] + μ.s[1]] // 棧頂兩個元素相加
其他狀態不變
解釋:
- 消耗 3 gas
- 將棧頂兩個元素相加,結果壓入棧
- pc + 1(移動到下一條指令)
定義 9:SSTORE
W_{SSTORE}(μ, σ) ≡ (σ', μ.g - G_{sstore}, μ', A, ∅)
其中:
G_{sstore} = 20000 gas(寫入新 slot)
或者 = 5000 gas(修改已有 slot)
或者 = 2900 gas(如果值為零)
σ' = σ with σ[μ.s[1]] = μ.s[0]
// 將棧第二個元素作為 key
// 棧頂元素作為 value
// 寫入合約的 storage
解釋:
- Storage 寫入是最昂貴的操作之一
- 這是為什麼要避免頻繁 Storage 操作的底層原因
執行循環:EVM 的心跳
黃皮書用 Big-Step 語義定義了完整的執行循環:
定義 10:執行循環
loop(μ, σ, T) ≡
if μ.g < C(μ.i) then
// Gas 不足,拋出异常
(σ, μ.g, A_OOOGAS, ∅)
else if μ.i = STOP then
// 正常終止
(σ, μ.g, ∅, [])
else if μ.i = REVERT then
// REVERT
(σ, μ.g, A_REVERT, μ.μ_s)
else
// 繼續執行下一條指令
let (σ', μ', A, o) = W(μ, σ)
loop(μ', σ', T)
這個定義揭示了 EVM 執行的一個關鍵特性:它是一個循環,直到 Gas 耗盡或正常終止。
異常處理
黃皮書定義了幾種異常類型:
異常類型:
A_REVERT - REVERT 操作,狀態回滾,剩餘 Gas 不退還
A_BADJUMP - 無效的跳轉目標
A_STACKOVERFLOW - Stack 溢出(> 1024 元素)
A_STACKUNDERFLOW - Stack 下溢(操作數不足)
A_INVALID - 執行了無效的操作碼
A_OOOGAS - Gas 不足
A_DATAACCESS - 嘗試訪問無效的 Memory 或 Storage
一個重要的細節:當拋出異常時,除了 Gas 消耗之外,所有的狀態修改都會被撤銷。這是 EVM 原子性保證的基礎。
從形式化定義到實際執行
現在讓我們把這些形式化定義翻譯成實際的執行流程:
步驟 1:交易驗證
1. 檢查交易的 nonce 是否正確
2. 驗證簽名
3. 檢查帳戶餘額是否足夠(value + gas × gasPrice)
步驟 2:Gas 預扣
1. 計算 intrinsic gas
2. 從帳戶預扣 maxGas × gasPrice
3. 設置 μ.g = maxGas
步驟 3:執行
如果是消息調用:
1. 轉帳(如果 value > 0)
2. 執行目標地址的代碼
3. 返回執行結果
如果是合約創建:
1. 計算新地址
2. 創建空合約
3. 執行初始化代碼
4. 返回代碼和地址
步驟 4:Gas 結算
1. 退還剩餘的 Gas × gasPrice 給發送者
2. 支付礦工/驗證者(小費部分)
3. 燒毀基礎費用(EIP-1559)
步驟 5:狀態提交
如果執行成功:
1. 提交所有的 Storage 修改
2. 更新帳戶餘額
3. 創建新合約(如有)
如果執行失敗:
1. 回滾所有 Storage 修改
2. 保留 Gas 消耗
3. 帳戶狀態恢復到執行前(除了費用)
Memory 模型的形式化
黃皮書對 Memory 的定義非常精確:
定義 11:Memory 狀態
Memory 狀態 m 是從字節位置到字節值的偏函數:
m: N → B
其中 m 的有效範圍為 [0, m.size)
Memory 擴展代價:
C_{mem}(n) = G_{memory} × n + n² / 512
其中:
G_{memory} = 3 gas(每 word 的基本代價)
n = words(即 32 字節為 1 word)
n²/512 = Memory 擴展的二次代價
這個二次代價模型是以太坊 Gas 設計的精髓之一。讓我們推導一下:
例子:Memory 擴展成本
假設我們要擴展到 100 words(3200 字節):
C_mem(100) = 3 × 100 + 100² / 512
= 300 + 10000 / 512
= 300 + 19.5
= 319.5 gas
相比之下,擴展到 200 words:
C_mem(200) = 3 × 200 + 200² / 512
= 600 + 40000 / 512
= 600 + 78.125
= 678.125 gas
擴展到 400 words:
C_mem(400) = 3 × 400 + 400² / 512
= 1200 + 160000 / 512
= 1200 + 312.5
= 1512.5 gas
注意:代價不是線性增長的!
100 → 200 words: +358 gas
200 → 400 words: +834 gas(幾乎是 2.3 倍)
這個設計的意義:防止攻擊者透過大規模 Memory 操作來拖慢網路。
Storage 模型的形式化
Storage 是 EVM 三層儲存架構中最複雜的一層。黃皮書的定義是:
定義 12:Storage 狀態
Storage 狀態是合約帳戶的一個子狀態:
σ[a].storage: N → N
即一個從 slot(256 位)到 value(256 位)的映射
Storage 操作代價(Deneb升級後):
SSTORE(新 slot)= 20000 gas
SSTORE(修改 existing slot)= 2900 gas
SSTORE(設為零)= 100 gas(補貼後)
SLOAD = 100 gas(warm)/ 2100 gas(cold)
SLOAD 的特殊之處
黃皮書對 SLOAD 的定義有一個重要的優化:warm storage access。
定義 13:Warm Storage Access
如果一個 storage slot 在當前交易中被「接觸」過,
它就會被標記為「warm」,後續的讀取會更便宜。
Warm slot 判定規則:
- 在同一筆交易中已經被 SLOAD 過
- 在同一筆交易中已經被 SSTORE 過
- 或者它是 msg.sender 或地址(特殊 slot)
Warm SLOAD = 100 gas
Cold SLOAD = 2100 gas
差異高達 20 倍!
這個設計是為了:懲罰那些「冷啟動」的 Storage 訪問。因為從磁碟加載 Storage trie 節點是非常昂貴的操作。
Call 指令的形式化
黃瓜書對 Call 指令的定義是整份文件中最複雜的部分之一:
定義 14:CALL 語義
W_CALL(μ, σ) ≡
let (σ', μ', A, o) = Λ(σ, g'', T)
where:
g'' = min(μ.g - G_call, μ.s[2])
T = (sender: μ.s[0],
recipient: μ.s[1],
value: μ.s[2],
data: μ.m[μ.s[4]:μ.s[4]+μ.s[5]],
gas: g'')
這個定義很抽象,讓我用人話解釋:
CALL 指令做了什麼:
1. 從 Stack 取參數:
- μ.s[0]: 目標地址
- μ.s[1]: 轉帳金額
- μ.s[2]: 提供的 Gas
- μ.s[3]: 內存起始位置(calldata)
- μ.s[4]: calldata 長度
2. 計算 Gas:
- 消耗基本 Call 費用(G_call)
- 剩餘的 Gas 減去提供的 Gas
3. 創建子執行框架:
- 子框架有自己的 Stack、Memory、Gas
- 但共享 Storage 和 World State(這是代理合約的基礎)
4. 執行目標合約:
- Λ(σ, g'', T) 就是我們之前定義的狀態轉換函數
5. 返回結果:
- o: 子執行的輸出數據
- A: 子執行的異常狀態
特殊 Call 變體
黃皮書定義了幾種 Call 的變體:
DELEGATECALL:
- 在調用者的上下文執行目標代碼
- Storage 是調用者的
- msg.sender 和 msg.value 不變
STATICCALL:
- 執行期間禁止 Storage 修改
- 如果執行了 SStore,拋出异常
- 用於 view 函數的鏈上驗證
CREATE:
- 創建新合約
- 返回新合約地址
- 執行初始化代碼
CREATE2:
- 使用 Salt 和 bytecode hash 計算地址
- 確保地址可預測
從理論到實踐:調試 EVM
了解了黃皮書的形式化定義之後,我們可以用這些知識來調試 EVM 問題:
常見 EVM 問題的根源分析:
1. 「Out of Gas」異常
原因:操作消耗的 Gas 超過了可用余量
解決:優化代碼減少 Gas 消耗,或增加 Gas limit
2. 「Stack Underflow」異常
原因:POP 指令時 Stack 為空,或指令需要 n 個元素但 Stack 只有 < n 個
解決:檢查 Stack 操作序列
3. 「Invalid Jump」異常
原因:JUMPDEST 指令之前沒有有效的 jump
解決:動態跳轉目標必須是 JUMPDEST
4. 「Revert」異常
原因:合約代碼執行了 REVERT 或 REQUIRE 失敗
解決:檢查 require 條件是否合理
5. 「Static Call Violation」
原因:在 STATICCALL 期間嘗試修改 Storage
解決:確保 view/ pure 函數不修改狀態
黃皮書的局限性
最後,我想客觀地說說黃皮書的一些局限性:
1. 升級延遲
黃皮書不是實時更新的。EIP 通過後,
通常需要幾個月甚至幾年才能在黃皮書中體現。
例如:EIP-1559 在 2021 年實施,但 Yellow Paper
直到 2022 年才更新。
2. 實現差異
黃皮書定義的是「規格」,但不同客戶端的實現可能略有差異。
例如:某些 edge case 的處理可能不一致。
這也是為什麼形式化驗證很重要的原因。
3. 遺漏的優化
黃皮書不考慮性能優化。
例如:evmone 的 JIT 編譯器在某些操作上比解釋器快 100 倍,
但這在黃皮書中是完全不可見的。
4. Layer 2 的不適用性
黃皮書只定義了 Layer 1 的 EVM。
Optimism、Arbitrum、zkSync 等 Layer 2
的實現與黃皮書有顯著差異。
結語:規格是契約,執行是義務
寫到這裡,我意識到這篇文章已經涵蓋了黃皮書 EVM 部分的核心內容。當然,這只是冰山一角——完整的黃皮書還包括共識機制、區塊結構、獎勵系統等更多內容。
但我相信,學習黃皮書最重要的是建立一種思維方式:不再滿足於「我知道怎麼用」,而是追求「我知道它為什麼這樣設計」。
這種思維方式會讓你成為更好的工程師。不是因為你記住了多少 opcode,而是因為你學會了從第一性原理思考複雜系統。
如果你對深入研究黃皮書感興趣,我建議:
- 先從黃皮書的 GitHub repo 開始,那裡有最新的版本
- 配合 evm.codes 這個網站,一邊讀一邊實際操作
- 讀 Geth 的 vm package 原始碼,看看形式化定義如何變成可執行代碼
- 關注以太坊魔術師論壇(ethresear.ch),那裡有最新的規格討論
規格不是束之高閣的文件。它們是活的契約,而我們每一個區塊鏈開發者,都是這個契約的執行者。
祝你在 EVM 的世界裡玩得開心。
參考資源
- 以太坊黃皮書:https://ethereum.github.io/yellowpaper/paper.pdf
- Yellow Paper TeX 源代碼:https://github.com/ethereum/yellowpaper
- EVM Opcodes 參考:https://www.evm.codes/
- Consensus Specs:https://github.com/ethereum/consensus-specs
三級可信來源標準
- Tier 1:學術論文、官方規格文件
- Tier 2:核心客戶端原始碼、官方技術部落格
- Tier 3:社群分析、第三方評測
本網站內容僅供教育與資訊目的,不構成任何技術或投資建議。
相關文章
- 以太坊黃皮書完整深度解析系列(一):形式化定義、狀態轉換函數與區塊結構 — 本文是黃皮書深度解析系列的第一篇,聚焦於黃皮書的結構框架、核心形式化定義、狀態轉換函數的數學描述、以及區塊結構的嚴謹規範。我們提供中英文對照的術語解釋,涵蓋世界狀態的形式化定義(帳戶模型、nonce 機制)、交易驗證的形式化約束、單筆交易狀態轉換的完整推導、區塊結構的完整定義、以及 EIP-1559 後的費用市場形式化更新。這是深入理解以太坊區塊鏈運作的最佳起點。
- 比特幣腳本語言與以太坊 EVM 安全性深度分析:從設計哲學到實際漏洞 — 比特幣腳本語言和以太坊虛擬機代表了區塊鏈領域兩種截然不同的智慧合約執行環境。比特幣採用圖靈不完備的腳本語言,設計目標是安全、簡潔、可預測;以太坊則採用圖靈完備的 EVM,設計目標是功能強大、表達靈活。本文深入分析這兩種執行環境的技術架構、安全機制與常見漏洞。
- 以太坊帳戶模型 vs UTXO 的設計抉擇:為何以太坊選擇 EOA/合約帳戶模型 — 比特幣的 UTXO 模型和以太坊的帳戶模型代表了區塊鏈狀態管理的兩種截然不同的設計哲學。本文將從技術原理、編程模型、安全特性、隱私表現和擴展性等多個維度,深入剖析這兩種模型的優劣,並詳細解釋以太坊為何選擇帳戶模型而非 UTXO 架構。我們將探討帳戶模型如何支撐以太坊的圖靈完整智慧合約生態,並分析這種選擇在實際應用中帶來的權衡取捨。
- 以太坊區塊結構與交易類型完整技術指南:從底層資料結構到實際應用 — 本文深入剖析以太坊區塊的完整資料結構、交易的生命週期、各類交易型別的技術規格,以及區塊驗證與傳播的底層機制。涵蓋執行層與共識層區塊結構、EIP-1559 費用模型、Blob 交易、EVM 執行流程、以及 MEV 相關主題。
- 以太坊核心協議基礎完整指南:從理論到實作的深度技術分析 — 本文提供以太坊核心協議的完整技術指南,涵蓋共識層、執行層、智慧合約部署、EVM 等核心元件的技術原理與實作細節。援引以太坊白皮書(Buterin, 2014)、黃皮書(Wood, 2014-2023)、Gasper 論文(Buterin et al., 2020)等正式學術文獻強化內容的學術嚴謹性。包含 Gasper 共識機制的數學定義、LMD-GHOST 分叉選擇規則、MPT 狀態管理、EIP-1559 費用燃燒機制、驗證者質押經濟學等完整技術分析。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案完整列表
- Solidity 文檔 智慧合約程式語言官方規格
- EVM 代碼庫 EVM 實作的核心參考
- Alethio EVM 分析 EVM 行為的正規驗證
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!