以太坊 Yellow Paper 工程師視角導讀:從規格到實作的完整解析手冊

Yellow Paper 是以太坊的形式化規格文件,本文以工程師能理解的方式導讀其核心內容。涵蓋世界狀態、區塊結構、交易執行、EVM 執行模型、Gas 成本定義等章節的深度解析,配合 go-ethereum 原始碼對照,幫助讀者理解『為什麼』而非僅止於『是什麼』。適合想深入理解以太坊底層運作的進階開發者與研究者。

以太坊 Yellow Paper 工程師視角導讀:從規格到實作的完整解析手冊

老實說,第一次翻開 Yellow Paper 的時候,我以為自己打開了什麼奇怪的論文。那符號多到眼花撩亂,Σ、∈、→ 全部混在一起,感覺像在看埃及象形文字。

但後來我發現一件事:如果你真的想把以太坊搞懂,而不是停在「會用」的層次,Yellow Paper 是繞不過去的。很多 Medium 文章說「以太坊如何運作」,但只有 Yellow Paper 會告訴你「以太坊必須如何運作」。

這篇文章是我的學習筆記,用工程師能理解的方式,帶你走一遍 Yellow Paper 的核心內容。目標不是讓你背公式,而是讓你搞懂「這個符號到底在表達什麼」。


一、Yellow Paper 是什麼?

Yellow Paper 是以太坊的形式化規格文件,由 Gavin Wood 撰寫(2014 年),現在由以太坊基金會維護。它是用數學符號描述以太坊狀態機的文件。

官方定義(翻譯成人話):

「這份文件定義了以太坊的執行模型。
包括:區塊結構、共識機制、交易執行、狀態轉換函數。」

翻譯:
「這東西告訴你:以太坊的 state 是怎麼從 A 變成 B 的。
以及,什麼情況下 transaction 會被接受或拒絕。」

為什麼叫 Yellow Paper?因為比特幣的白皮書(White Paper)是用白紙,而以太坊的文件封面是黃色的——就這樣,沒有更深奧的意思。

1.1 讀 Yellow Paper 的正確姿勢

我讀了五遍 Yellow Paper,總結出來的竅門:

準備工具:
1. 一杯咖啡(很濃的那種)
2. Etherscan(用來對照實際區塊資料)
3. go-ethereum 原始碼(用來對照實作)
4. 便利貼(標記看不懂的地方)

閱讀順序(我的建議):
1. 先看 Section 2(區塊、狀態、交易)
2. 再看 Section 8(EVM)
3. 然後 Section 4(交易執行)
4. 最後 Section 10(共識)

千萬不要:
❌ 從頭讀到尾
❌ 一次性讀完全部
❌ 試圖搞懂每一個數學符號

二、核心概念:狀態、世界狀態、帳戶

Yellow Paper 的起點是世界狀態(World State)。這是以太坊區塊鏈的核心。

2.1 符號定義(Section 2.1)

σ ≡ 世界狀態(World State)
σ 是一個 mapping:address → account_state

σ[addr] ≡ 某地址的帳戶狀態
         = (nonce, balance, storage_root, code_hash)

address ≡ 160 位元的識別符

用 Python 翻譯:

# 這是以太坊世界狀態的簡化模型
class Account:
    nonce: int          # 交易計數器(防止 replay 攻擊)
    balance: int        # ETH 餘額(單位:Wei)
    storage_root: bytes # storage trie 的 Merkle root
    code_hash: bytes    # 合約程式碼的 hash(EOA 是空 bytes)

# 世界狀態就是:address → Account
world_state: dict[bytes20, Account]

重要理解:帳戶狀態存在 Merkle Patricia Trie 裡,不是存在區塊裡。區塊只存 trie 的 root hash。這就是所謂的「状态承诺」(State Commitment)。

2.2 兩種帳戶

┌─────────────────────────────────────────────────────────────┐
│                    外部擁有帳戶 (EOA)                          │
├─────────────────────────────────────────────────────────────┤
│  - 由私鑰控制的帳戶                                          │
│  - 沒有關聯的程式碼                                           │
│  - 可以發起交易                                               │
│  σ[addr].code_hash = EmptyCodeHash (= Keccak256(∅))        │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                    智能合約帳戶 (CA)                         │
├─────────────────────────────────────────────────────────────┤
│  - 由程式碼控制                                               │
│  - 有關聯的程式碼                                             │
│  - 只能響應收到的交易                                         │
│  - 可以建立新合約、呼叫其他合約                                │
└─────────────────────────────────────────────────────────────┘
def is_eoa(address, world_state):
    """判斷是否為外部擁有帳戶"""
    code_hash = world_state[address].code_hash
    return code_hash == EMPTY_CODE_HASH

def is_contract(address, world_state):
    """判斷是否為智能合約"""
    code_hash = world_state[address].code_hash
    return code_hash != EMPTY_CODE_HASH

三、區塊結構(Section 4.3)

3.1 區塊的數學定義

B ≡ (BlockHeader, TransactionList, UncleList)

BlockHeader ≡ (
    prevBlocksHash,    # ← 父區塊的 hash
    ommersHash,        # ← Uncle 區塊的 hash
    beneficiary,       # ← 礦工地址(PoW)/ 提議者(PoS)
    stateRoot,         # ← 交易後的世界狀態 root
    transactionsRoot,  # ← 交易清單的 root
    receiptsRoot,      # ← 交易收據的 root
    logsBloom,         # ← Bloom filter,用來快速搜尋 event
    difficulty,        # ← 區塊難度(PoW)/ 預測值(PoS)
    number,            # ← 區塊高度
    gasLimit,          # ← 區塊 Gas 上限
    gasUsed,           # ← 區塊實際使用的 Gas
    timestamp,         # ← 區塊時間戳
    extraData,         # ← 額外資料(最多 32 bytes)
    mixHash,           # ← PoW 用的 mix hash
    nonce               # ← PoW 用的 nonce
)

我第一次看到這個定義的時候,只有一個想法:原來區塊就是這些東西組合起來的!

實務對照:

打開 Etherscan,随便點一個區塊:
https://etherscan.io/block/19500000

你會看到:
- Block Height: 19500000
- Timestamp: 2026-03-28 12:00:00
- Transactions: 150
- Gas Used: 15,000,000 / 30,000,000
- Miner: 0x1234...abcd
- Difficulty: 5.5 PH
- Extra Data: 0x

這些全部對應 Yellow Paper 裡的 BlockHeader 欄位!

3.2 Uncle 的數學意義

Yellow Paper 定義了 Uncle 的格式:

ommerHash = Keccak256(RLP(ommerList))

Uncle 是「差一點成為主鏈區塊的分叉區塊」。在 PoW 時代,叔塊獎勵是一個有點奇怪的設計——它獎勵那些「輸掉競爭」的礦工。

為什麼要有 Uncle?

PoW 以太坊的平均區塊時間是 ~13 秒
網路延遲導致很多「合法的」區塊成為孤塊

如果沒有叔塊獎勵:
- 大礦池有優勢(因為離礦池近,孤塊率低)
- 小礦池更難生存
- 網路去中心化程度降低

有了叔塊獎勵:
- 叔塊礦工也能獲得部分獎勵
- 鼓勵礦工廣播區塊,而不是只廣播給礦池
- 提高了網路的去中心化程度

四、交易執行(Section 6)

4.1 交易類型

T ≡ (T_nonce, T_gasPrice, T_gasLimit, T_to, T_value, T_data, T_v, T_r, T_s)

其中:
T_nonce     ≡ 交易計數器,防止 replay 攻擊
T_gasPrice  ≡ 用戶願意支付的 Gas 單價
T_gasLimit  ≡ 用戶願意支付的最大 Gas
T_to        ≡ 目標地址(EOA 或 合約)
T_value     ≡ 轉帳金額(單位:Wei)
T_data      ≡ 調用的資料(合約方法、參數等)
T_v, T_r, T_s ≡ 交易的電子簽章
class Transaction:
    nonce: int      # 帳戶的交易計數
    gas_price: int  # Gwei 轉 Wei
    gas_limit: int  # 最大 Gas
    to: bytes20     # 目標地址
    value: int      # 轉帳金額
    data: bytes     # 呼叫資料
    v: int          # 簽章 recovery id
    r: int          # 簽章 R 值
    s: int          # 簽章 S 值
    
    def sender(self) -> bytes20:
        """從簽章還原發送者地址"""
        return ecrecover(self.v, self.r, self.s)

4.2 狀態轉換函數(State Transition Function)

這是 Yellow Paper 最核心的部分。狀態轉換函數定義了:給定當前狀態 σ 和交易 T,如何得到新狀態 σ'。

σ' = Ξ(σ, T)

Ξ 就是狀態轉換函數
σ 是當前世界狀態
T 是待執行的交易
σ' 是執行後的新世界狀態

具體來說:

def state_transition(world_state, transaction):
    """
    Yellow Paper 中的 Ξ 函數
    
    步驟:
    1. 驗證交易簽章
    2. 扣 Gas(預扣 gas_limit × gas_price)
    3. 執行交易
    4. 計算實際 Gas 消耗
    5. 退還多餘的 Gas
    """
    
    # Step 1: 簽章驗證
    sender = transaction.sender()
    assert sender in world_state, "Sender must exist"
    assert transaction.nonce == world_state[sender].nonce, "Nonce mismatch"
    
    # Step 2: 預扣 Gas
    gas_initial = transaction.gas_limit * transaction.gas_price
    world_state[sender].balance -= gas_initial
    
    # Step 3: 執行(複雜度最高的部分)
    # 這裡呼叫 EVM...
    success, gas_used, logs = execute_evm(
        world_state,
        transaction
    )
    
    # Step 4: 計算退款
    gas_refund = (transaction.gas_limit - gas_used) * transaction.gas_price
    
    # Step 5: 退還未使用的 Gas
    if success:
        world_state[sender].balance += gas_refund
        # 燒毀 base fee(EIP-1559 後)
        burn_amount = gas_used * base_fee
        # miner/consolidator 獲得 tip
        beneficiary_reward = gas_used * (priority_fee)
    else:
        # 失敗:只退還未執行的部分
        world_state[sender].balance += (transaction.gas_limit - gas_used) * transaction.gas_price
    
    return world_state

五、EVM 執行模型(Section 8)

5.1 EVM 作為狀態轉換函數

Yellow Paper 把 EVM 定義成一個狀態機:

(σ, μ, A, i) → (σ', μ', A', o)

σ    ≡ 世界狀態
μ    ≡ 機器狀態(PC, Stack, Memory, Gas)
A    ≡ 執行環境(msg.sender, msg.value, block info...)
i    ≡ 程式碼

σ'   ≡ 新的世界狀態
μ'   ≡ 新的機器狀態
A'   ≡ 新的執行環境
o    ≡ 輸出(return data)
class EVMState:
    pc: int              # Program Counter(指令指標)
    stack: list[int]    # 堆疊(最多 1024 層)
    memory: bytearray    # 記憶體(可擴展)
    gas: int             # 剩餘 Gas
    storage: dict        # 合約 storage
    
def evm_step(state: EVMState, code: bytes) -> EVMState:
    """
    執行一個 EVM 指令
    
    這就是 Yellow Paper 中 Λ 函數的作用
    """
    opcode = code[state.pc]
    
    if opcode == 0x00:  # STOP
        return state  # 停止執行
    
    elif opcode == 0x01:  # ADD
        a = state.stack.pop()
        b = state.stack.pop()
        state.stack.append((a + b) % 2**256)
        state.pc += 1
        state.gas -= G_add  # 3 Gas
        
    elif opcode == 0x02:  # MUL
        a = state.stack.pop()
        b = state.stack.pop()
        state.stack.append((a * b) % 2**256)
        state.pc += 1
        state.gas -= G_mul  # 5 Gas
        
    # ... 其他 opcode ...
    
    return state

5.2 Gas 成本定義(Section 9)

Yellow Paper 用數學符號定義了每個 opcode 的 Gas 成本:

┌─────────────────────────────────────────────────────────────┐
│                    符號定義表                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  G_base      ≡ 2     基礎操作 Gas(STOP, ADD, ...)          │
│  G_verylow   ≡ 3     很簡單的操作(SLT, BYTE, ...)         │
│  G_low       ≡ 5     乘法等操作(MUL, DIV, ...)             │
│  G_mid       ≡ 8     稍微複雜的操作(SDIV, MOD, ...)       │
│  G_high      ≡ 10    複雜操作(JUMP, JUMPI, ...)           │
│  G_extcode   ≡ 700   讀取代碼(SSTORE, CREATE, ...)         │
│  G_balance   ≡ 400   讀取餘額                                 │
│  G_sload     ≡ 800   Storage 讀取                            │
│  G_sstore    ≡ 20000  新增 storage slot                       │
│                                                             │
│  W_memory    ≡ 3     每 word 記憶體成本                      │
│  G_memory    ≡ 0     基礎記憶體成本                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
# 對照 go-ethereum 的實作
# core/vm/gas.go

const (
    GasQuickStep   uint64 = 2
    GasFastestStep uint64 = 3
    GasFastStep    uint64 = 5
    GasMidStep     uint64 = 8
    GasSlowStep    uint64 = 10
    GasExtStep     uint64 = 20
)

# core/vm/gas_table.go

func GasSStore(evm *EVM) uint64 {
    if evm.StateDB.GetState(addr, key) == (common.Hash{}) {
        // 新增 slot
        return 20000
    }
    // 編輯現有 slot
    return 5000
}

六、實際閱讀範例

6.1 追蹤一筆實際交易

讓我們用 Yellow Paper 的框架來分析一筆實際交易:

目標:追蹤 Uniswap 的某筆 Swap 交易
TX Hash: 0x1234...abcd

Step 1: 找到交易的基本資訊
- From: 0xABC... (EOA)
- To: 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D (Uniswap V2 Router)
- Value: 0 ETH
- Data: 0x38ed1739... (函數調用 + 參數)

Step 2: 套用狀態轉換函數
Ξ(σ_before, T) = σ_after

σ_before:
- From balance: 1.5 ETH
- From nonce: 42
- Uniswap Router storage: ...

Step 3: 執行 EVM
1. CALLER: μ_0 = msg.sender
2. CALLVALUE: μ_1 = 0
3. CALLDATALOAD: 從 calldata 讀取參數
4. SWAP: 交換堆疊元素
5. CALL: 呼叫目標合約
...

Step 4: 理解輸出
- Return data: swap 的輸出數量
- Status: Success
- Gas used: 145,000

6.2 理解 EIP-1559 的數學

Yellow Paper 裡 Base Fee 的定義是這樣的:

BASE_FEE = Parent.GasLimit * (1 - exp(-Parent.GasUsed / Parent.GasLimit))

實際上現在的實作是:

BASE_FEE = Parent.BASE_FEE * (1 + delta)

delta = (Parent.GasUsed - Parent.GasLimit * TARGET) / (Parent.GasLimit / 32)

如果 Parent 用太多 Gas → Base Fee 上升
如果 Parent 用太少 Gas → Base Fee 下降

這個公式控制了區塊空間的供需平衡。

def calculate_base_fee(parent_gas_used, parent_gas_limit, parent_base_fee, 
                        target_gas_limit, min_base_fee):
    """
    計算下一個區塊的 Base Fee
    對應 EIP-1559 的實作
    """
    # Gas 使用量偏離目標的程度
    delta = parent_gas_used - target_gas_limit
    
    # 每個區塊最多改變 12.5%
    if delta > 0:
        # 用太多 Gas,Base Fee 要漲
        adjustment = max(delta // parent_gas_limit * parent_gas_limit // 8, 1)
        new_base_fee = parent_base_fee + adjustment
    else:
        # 用太少 Gas,Base Fee 要跌
        adjustment = max(delta // parent_gas_limit * parent_gas_limit // 8, 1)
        new_base_fee = parent_base_fee - adjustment
    
    # Base Fee 不能低於最低值
    return max(new_base_fee, min_base_fee)

七、常見問題 Q&A

Q: Yellow Paper 和 Beige Paper 有什麼不同?

A: Beige Paper 是社群維護的「易讀版」Yellow Paper,把數學符號翻譯成 Python/Go 程式碼。適合當入門,但可能跟最新版本脫節。

Q: 需要把整篇背下來嗎?

A: 當然不需要。我讀了五遍,也不是每個公式都能背。重點是理解框架,知道去哪裡查。

Q: 讀 Yellow Paper 對寫合約有幫助嗎?

A: 有,但間接的。你不會直接用 Yellow Paper 寫合約,但你會更理解「為什麼 Gas 要這樣算」、「為什麼某個操作會貴」。這幫助你寫出更省 Gas 的程式碼。

Q: 有什麼配套資源推薦?

A:


八、結語

Yellow Paper 就像一本武林秘籍——大多數人不需要從頭到尾背下來,但每個認真想搞懂以太坊的人,都應該翻過幾遍。

讀 Yellow Paper 的最大收穫,不是記住那些數學符號,而是學會用「形式化」的方式思考。以太坊的每個行為都有明確定義,這就是區塊鏈的厲害之處——沒有模糊地帶,只有 spec 和 implementation。

有空的話,找一杯咖啡,開 Etherscan,對照著 Yellow Paper 看一筆交易。你會發現:原來這一切都是這麼優雅。


免責聲明:本網站內容僅供教育與資訊目的,不構成任何投資建議或推薦。在進行任何加密貨幣相關操作前,請自行研究並諮詢專業人士意見。所有投資均有風險,請謹慎評估您的風險承受能力。

數據截止日期:2026-03-28

標籤technical, yellow-paper, ethereum-specification, evm, consensus, source-code, engineering

分類:technical

難度:advanced

相關文章

延伸閱讀

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!