以太坊 EVM 執行模型深度技術解析:從指令集到狀態轉換的完整旅程
本文深入剖析以太坊虛擬機(EVM)的執行模型,涵蓋帳戶模型、執行環境、Stack/Memory/Storage 三層儲存架構、Opcode 與 Gas 計算、區塊級執行機制、以及子呼叫與訊息傳遞等核心概念。提供詳細的技術解析與實際案例,幫助開發者掌握 EVM 的底層運作原理,寫出更高效的智能合約。
title: 以太坊 EVM 執行模型深度技術解析:從指令集到狀態轉換的完整旅程
summary: 本文深入剖析以太坊虛擬機(EVM)的執行模型,涵蓋帳戶模型、執行環境、Stack/Memory/Storage 三層儲存架構、Opcode 與 Gas 計算、區塊級執行機制、以及子呼叫與訊息傳遞等核心概念。提供詳細的技術解析與實際案例,幫助開發者掌握 EVM 的底層運作原理,寫出更高效的智能合約。
tags:
- technical
- evm
- execution-model
- stack
- memory
- storage
- opcode
- gas
- smart-contract
- ethereum
difficulty: advanced
date: 2026-03-28
parent: null
status: published
references:
- title: Ethereum Yellow Paper
url: https://ethereum.github.io/yellowpaper/paper.pdf
desc: 以太坊黃皮書,EVM 正式規格
- title: EVM Codes
url: https://www.evm.codes/
desc: 互動式 EVM Opcode 參考
- title: Go-Ethereum Source Code
url: https://github.com/ethereum/go-ethereum
desc: Geth 客戶端原始碼
- title: evmone
url: https://github.com/ethereum/evmone
desc: 高效能 EVM 解釋器
- title: Solidity 文件
url: https://docs.soliditylang.org/
desc: Solidity 官方文檔
disclaimer: 本網站內容僅供教育與資訊目的,不構成任何技術或投資建議。
以太坊 EVM 執行模型深度技術解析:從指令集到狀態轉換的完整旅程
為什麼 EVM 值得你深入了解
說實話,我剛開始寫智能合約的時候,壓根沒想過要去研究 EVM 到底是什麼鬼。Solidity 編譯器會幫我處理一切,我只需要寫好邏輯就行了,對吧?
結果等到我想優化 Gas、想理解某個奇怪的 bug、或者想搞清楚某筆交易為什麼貴成那樣子的時候,我發現自己根本離不開 EVM 的知識。於是我花了大概三個月的時間死磕 Yellow Paper,看 Geth 原始碼,在 etherplay 上面折騰各種 opcode。過程很痛苦,但收穫超大——現在我看 DeFi 合約的眼光完全不一樣了,很多以前覺得神的操作,其實就是對 EVM 執行模型的巧妙利用。
所以這篇文章,我想把這個學習過程中最重要的東西濃縮給你。不走學院派路線,我們直接從實務角度切入,搞清楚 EVM 到底是怎麼把一行 Solidity code 變成區塊鏈上可執行狀態轉換的。
帳戶模型:EOA 與合約的恩怨情仇
EVM 的世界裡只有兩種帳戶:外部擁有帳戶(EOA)和合約帳戶(Contract Account)。EOA 由私鑰控制,沒有代碼;合約帳戶由程式碼控制,不能自己發起交易。
這個設計看起來很簡單,但裡面的門道很多。2016 年的 DAO 攻擊之後,很多人開始討論要不要廢除 EOA,讓所有帳戶都變成合約。Vitalik 當時也很支持這個想法,但後來不了了之了。原因是廢除 EOA 會讓系統變得非常複雜——你需要一個「合約」來觸發第一筆交易,那個「合約」的私鑰由誰控制?
現在想想,保留 EOA 的決定其實挺聰明的。它讓整個系統的設計保持簡潔,而且 EIP-7702 的出現,實際上在某種程度上實現了「EOA 臨時變合約」的功能,算是兩個世界的橋梁。
EOA 和合約帳戶的差異在很多地方都會造成困擾。比如你在寫合約的時候,可能會遇到「這筆交易是從 EOA 來的還是從另一個合約來的」的問題。msg.sender 在兩種情況下都會給你發送者的位址,但你可以透過 solidity 的 msg.sender.code.length > 0 判斷這是不是一個合約。
function isContract(address _addr) public view returns (bool) {
uint256 codeLength;
assembly {
codeLength := extcodesize(_addr)
}
return codeLength > 0;
}
這個技巧在很多場景下都很有用。比如你在設計一個函數,不希望被其他合約在同一個交易中調用(防止 reentrancy 攻擊),就可以加上這個檢查。
三層儲存架構:Stack、Memory、Storage 的三角戀
我個人認為,理解 EVM 的儲存架構是整個 EVM 學習旅程中最關鍵的部分。EVM 有三種存放資料的地方:Stack、Memory、Storage,它們有著完全不同的特性和效能表現。
Stack(堆疊):最速、最便宜、但容量有限
Stack 是 EVM 用來存放臨時運算資料的地方,最大深度是 1024 個元素,每個元素是 256 位元(32 bytes)。大多數 opcode 操作都在 stack 上進行,比如加法、減法、比較、跳轉等等。
Stack 的特點是快到不行——所有的 stack 操作都只需要消耗 2 到 5 gas。但問題是容量太小,而且只能操作最上面的元素。你想把 stack 中間的某個值拿出來用?不好意思,先把上面的東西挪開。
看個具體例子,以下是一個簡單的 addition 的 bytecode 操作序列:
PUSH1 0x03 // 把 3 推到 stack
PUSH1 0x05 // 把 5 推到 stack
ADD // 彈出兩個值相加,結果 push 回去
這段程式碼執行完之後,stack 最上面會是 8。整個過程在一個區塊內可能被執行數十億次,所以 EVM 把 stack 操作優化到了極致。
Stack 在 Gas 消耗上是最划算的,這也是為什麼很多高效合約會刻意利用 stack 來做資料暫存,而不是動用到 memory。不過這也讓我見過一些很變態的優化——有人透過精心設計 stack 操作,把一個原本需要 10 萬 gas 的合約降到只剩 3 萬 gas。佩服佩服。
Memory(記憶體):可變大小但代價不菲
Memory 是 EVM 的工作空間,用來存放執行過程中的暫時資料。它是「位元組組(byte array)」的形式,可以動態擴展,但代價很重——每次擴展都需要支付 Gas。
Memory 的定價模型很有趣:讀取和寫入的 Gas 消耗會隨著你存取的位址變高而增加。具體來說:
- MLOAD、MSTORE(讀寫一個 word)基本消耗 3 gas
- 當存取的範圍跨越 256-bit 邊界時,會觸發「Memory expansion cost」,這部分很貴
- 當 Memory size 增加時,Gas 消耗會以二次方的方式增長
這個定價模型的目的是防止對 Memory 的大規模滥用。攻擊者可能透過部署一個消耗大量 Memory 的合約,拖慢整個網路的執行速度。所以 Memory 越用越多,Gas 單價越貴——這是一種防 DoS 機制。
Memory 在 Solidity 中的使用方式很直觀。當你宣告一個 uint256 x 作為函數參數,或者在函數內部創建一個臨時變數,這些資料都存在 Memory 裡。Solidity 9 以後,所有的函數參數和回傳值都默認使用 Memory,但老版本可能要自己加 memory 關鍵字。
function demoMemory(uint256[] calldata arr) public pure returns (uint256) {
// arr 在 calldata 中,不可修改
uint256 sum = 0;
for (uint256 i = 0; i < arr.length; i++) {
sum += arr[i]; // sum 在 stack 上
}
return sum;
}
Storage(儲存):最貴、但最持久
Storage 是 EVM 中最昂貴的資料存放地方,所有持久化的狀態都存在這裡。每一次對 Storage 的讀寫,消耗的 Gas 都是天文數字——SLOAD 最低 100 gas(熱門 slot)到 2100 gas(冷門 slot),SSTORE 更誇張,寫入一個新 slot 要 20000 gas,修改已有 slot 要 5000 gas,刪除(設為零)則補貼 15000 gas。
Storage 的設計是「稀疏的 key-value store」。每一個帳戶都有自己的 Storage Trie,所有 32 bytes 的 key 都對應到一個 32 bytes 的 value。Trie 的結構讓 Ethereum 可以高效地驗證和同步狀態,但代價是每一次 Storage 操作都要付出巨大的代價。
我強烈建議每個Solidity 開發者都要把 Storage 的代價刻在腦子裡。以下是我個人的粗略估算:
| 操作 | Gas 消耗 | 大約美元成本(@ $2000 ETH) |
|---|---|---|
| SLOAD (warm) | 100 | $0.0001 |
| SLOAD (cold) | 2100 | $0.002 |
| SSTORE (new) | 20000 | $0.02 |
| SSTORE (update) | 5000 | $0.005 |
| SSTORE (delete) | -15000 (refund) | -$0.015 |
所以當你在一個循環裡面做 Storage 寫入的時候,Gas 消耗會直接爆表。我見過有人在一個會被多次呼叫的函數裡,每次都寫入 Storage,結果用戶需要支付幾百美元的 Gas 才能完成一筆交易。這簡直是在搶劫用戶的錢包。
Storage 的另一個特性是它是永久性的。任何寫入 Storage 的資料,都會成為區塊鏈狀態的一部分,直到被明確刪除(set to zero)。這也是為什麼區塊鏈的狀態會不斷膨脹——截至 2024 年底,以太坊的狀態已經超過 100GB 了。
三者的取捨:實務經驗
我自己的經驗法則是:能用 Stack 解決的問題,就不要碰 Memory;能用 Memory 的,就不要動 Storage。
這個原則在很多地方都適用。比如,當你要返回一個動態大小的陣列時,不要每次都往 Storage 陣列 append,而是先在 Memory 裡面計算好,最後一次性返回:
// 不推薦:每次循環都寫 Storage
function badApproach(uint256 n) public returns (uint256[] storage) {
results.push(n * 2); // 每次都觸發 SSTORE
}
// 推薦:先在 Memory 計算,最後才寫 Storage
function goodApproach(uint256 n) public view returns (uint256[] memory) {
uint256[] memory temp = new uint256[](n);
for (uint256 i = 0; i < n; i++) {
temp[i] = i * 2; // Memory 操作,不消耗太多 Gas
}
return temp;
}
但要注意的一點是,這個「推薦做法」在某些場景下反而是錯誤的。如果你要呼叫另一個會修改 Storage 狀態的合約函數,你必須傳遞 Storage 指標,不能用 Memory。這也是為什麼 Solidity 有 storage 關鍵字——它允許你明確指定資料的儲存位置。
Opcode 與 Gas 計算:EVM 的經濟模型
如果要說 EVM 最獨特的設計,我會投 Gas 機制一票。這個機制讓 EVM 的執行變成了一個有成本的運算——每一個操作都需要消耗對應的 Gas,而 Gas 需要用 ETH 購買。這種設計解決了著名的「停止問題」:在傳統圖靈機模型中,你沒辦法防止一個程式永遠執行;但在 EVM 裡,即使你的程式進入無限循環,它也會在 Gas 燒完的時候自動停止。
基礎 Gas 消耗
EVM 的 opcode 有幾種不同的 Gas 消耗模式:
- 固定消耗:大多數 opcode 的 Gas 消耗是固定的,比如 ADD = 3 gas, MUL = 5 gas, SWAP1 = 3 gas 等等。這些都是最基本的計算,成本極低。
- 動態消耗:部分 opcode 的 Gas 消耗是動態的,根據操作的規模而變化。比如 KECCAK256 的基礎消耗是 30 gas,但每個 word(32 bytes)的額外消耗是 6 gas。如果你要計算一個很大的資料的哈希,Gas 會直線上升。
- Memory 擴展消耗:如前所述,Memory 的讀寫會觸發擴展成本。這部分在 EIP-2028 以後已經大幅降低了,因為當時把 CALL 函數的 Gas 消耗從 700 降到了 2300——背後的邏輯是,隨著網路升級,我們可以假設更便宜的 Gas 對整個生態系更有利。
EIP-1559 與 Gas 市場
2021 年的倫敦升級引入的 EIP-1559 徹底改變了 Gas 費用的市場機制。在那之前,Gas 價格是由用戶自行競標的,高峰期的時候 Gas price 可能飆升到幾百 gwei。EIP-1559 之後,基礎費用(Base Fee)會根據網路使用率自動調整,而用戶只需要支付「優先費用 + 基礎費用」,不用擔心自己出的價格太高。
這個改動對 EVM 執行模型的影響比較間接,但有兩個值得注意的地方:
- Gas 退款的上限:EIP-1559 同時引入了 Gas 退款的上限,規定每筆交易的總退款不能超過耗費 Gas 的 20%。這是因為之前有人透過大量的 SSTORE 退款來實現「免費交易」,這個漏洞被補上了。
- Blob 交易:EIP-4844(Proto-Danksharding)在 2024 年引入了 blob 攜帶交易類型,這種交易會產生額外的 data availability 成本,與普通的 EVM 執行成本分開計算。Layer 2 的排序器現在主要透過 blob 交易來提交數據,大幅降低了成本。
實用 Gas 優化技巧
基於我對 EVM Opcode 和 Gas 模型的理解,以下是幾個經過驗證的優化技巧:
減少 Storage 操作次數:把需要多次讀取的 Storage 變數先拉到 Memory:
// 不推薦
function bad(uint256 a, uint256 b) public {
for (uint256 i = 0; i < 100; i++) {
data[i] = data[i] + a * b; // data[i] 每次都要 SLOAD
}
}
// 推薦
function good(uint256 a, uint256 b) public {
uint256 temp = a * b; // 只計算一次
for (uint256 i = 0; i < 100; i++) {
uint256 current = data[i]; // SLOAD
data[i] = current + temp; // SSTORE
}
}
使用 Short String 或 Bytes:Solidity 的 string 和 bytes 底層是一樣的,但 bytes 可以讓你明確指定長度。如果你確定你的資料不會超過 31 bytes,用 bytes31 會比 bytes32 省很多 Memory expansion cost。
批量操作替代循環:如果你的合約邏輯允許,把多個小操作合併成一個大操作:
// 不推薦:每個代幣都要一次 SLOAD + SSTORE
function batchUpdateBad(uint256[] calldata values) external {
for (uint256 i = 0; i < values.length; i++) {
balances[msg.sender] += values[i]; // 每次迴圈都有兩個 Storage 操作
}
}
// 推薦:最後一次性寫入
function batchUpdateGood(uint256[] calldata values) external {
uint256 total = 0;
for (uint256 i = 0; i < values.length; i++) {
total += values[i]; // 全部在 Memory/Sstack 計算
}
balances[msg.sender] += total; // 只觸發一次 Storage 寫入
}
區塊級執行機制:區塊到底怎麼跑的
了解了儲存架構和 Gas 模型之後,現在讓我們把視角拉高,看看 EVM 在區塊級別是怎麼執行的。
區塊結構與執行上下文
以太坊的每個區塊都包含一個交易列表和一個區塊頭(block header)。當礦工或驗證者組裝一個區塊的時候,會依序執行區塊中的每一筆交易,更新世界狀態。
EVM 的執行環境在每筆交易開始時是這樣設定的:
block.coinbase:區塊提議者的位址block.timestamp:區塊時間戳block.number:區塊編號block.difficulty:區塊難度(Merge 後只有歷史意義)block.gaslimit:當前區塊的 Gas 上限msg.sender:交易發送者msg.value:交易附帶的 ETH 數量tx.gasprice:Gas 價格
這些環境變數在合約執行過程中是「只讀」的。Solidity 封裝了這些值,讓你可以透過 block.timestamp 或 msg.sender 這樣的形式存取。
Transaction Execution Flow
一筆交易進入 EVM 後,會經歷以下流程:
- 初始驗證:檢查交易的 nonce、簽名、Gas limit、費用是否足夠
- 預執行:計算交易需要消耗的費用並從發送者扣款
- 合約部署或呼叫:如果是合約創建,執行初始化代碼直到返回;如果是一般呼叫,執行目標合約函數
- 子呼叫:如果被呼叫的合約內部呼叫了其他合約,會創建新的執行框架(call frame)
- 狀態更新:如果執行成功,應用所有Storage 變更;如果執行失敗,回滾所有變更(除了費用扣款)
- Gas 退款:退還剩餘的 Gas 給交易發送者
整個過程中,如果任何一步失敗,都會觸發 revert。EVM 會回滾所有尚未「持久化」的變更——注意這裡的關鍵字是「尚未」。在 revert 之前已經寫入 Storage 的資料會被完全抹除,彷彿那筆交易從未發生過。
這就是以太坊區塊鏈的確定性保證——每一筆交易執行後的狀態都是可以直接計算和驗證的。沒有任何「半成功」的交易,所有的失敗都是原子性的。
Call Frame 與訊息傳遞
當合約呼叫另一個合約的函數時,會創建一個新的 call frame。這個 frame 有自己的 Stack 和 Memory,和呼叫者的環境完全隔離。
Call frame 的建立和返回有幾種不同的方式:
CALL:最常見的同步呼叫,會創建一個新的 frame 執行目標合約DELEGATECALL:在呼叫者的上下文中執行目標合約代碼,適用於代理合約模式STATICCALL:執行期間禁止任何 Storage 寫入,適用於 view 函數的鏈上驗證CREATE/CREATE2:創建新的合約帳戶並執行初始化代碼
我特別想聊聊 DELEGATECALL,因為它在代理合約模式中至關重要。代理合約把所有的 Storage 和 logic 分離——代理合約只負責轉發呼叫,真正的業務邏輯放在一個獨立的 Implementation 合約裡。升級的時候,只需要把 Implementation 位址更新一下就行了。
這種模式的代碼大概長這樣:
// 代理合約
contract Proxy {
address public implementation;
fallback() external payable {
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let result := delegatecall(
gas(),
sload(implementation.slot),
ptr,
calldatasize(),
0,
0
)
returndatacopy(ptr, 0, returndatasize())
switch result
case 0 { revert(ptr, returndatasize()) }
default { return(ptr, returndatasize()) }
}
}
}
EVM 在處理這個 fallback 函數的時候,會用 DELEGATECALL 的方式執行 Implementation 合約的代碼,但 Storage 還是寫入 Proxy 合約的 slot。這就是為什麼升級之後,Storage 資料不會消失的原因。
重入攻擊的 Bytecode 層面解析:DAO 事件的技術根源
說到 EVM 攻擊,不得不提重入攻擊(Reentrancy Attack)——這是區塊鏈世界最經典的安全漏洞,也是 2016 年 The DAO 事件的核心原因。很多人只知道「不要Fallback 被外部呼叫」,但對底層到底發生了什麼一知半解。讓我帶你從 bytecode 層面徹底搞懂這個漏洞。
傳統重入 vs 跨函數重入 vs 跨合約重入
重入攻擊有三种形態,威力由弱到強:
1. 單一函數重入(Simple Reentrancy)
攻擊合約在同一個函數被呼叫時反覆呼叫受害合約。
// 受害合約(有漏洞)
contract Victim {
mapping(address => uint256) public balances;
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
// 先轉帳
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
// 後更新餘額 —— 這就是問題所在!
balances[msg.sender] -= amount;
}
}
// 攻擊合約
contract Attacker {
Victim victim;
fallback() external payable {
if (address(victim).balance >= msg.value) {
victim.withdraw(msg.value); // 重入!
}
}
function attack() external payable {
victim.deposit{value: msg.value}();
victim.withdraw(msg.value);
}
}
2. 跨函數重入(Cross-function Reentrancy)
利用合約不同函數之間的狀態不一致:
// 受害合約
contract CrossFunction {
mapping(address => uint256) balances;
mapping(address => bool) claimed;
function transfer(address to, uint256 amount) external {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // 先扣餘額
balances[to] += amount;
}
function claim(address recipient) external {
if (!claimed[recipient]) {
recipient.call{value: balances[recipient]}("");
claimed[recipient] = true; // 後標記
}
}
}
3. 跨合約重入(Cross-contract Reentrancy)
利用 Aave、Uniswap 等 DeFi 協議之間的組合性:
// 攻擊者在同一筆交易中:
// 1. 從 Compound 借出 ETH
// 2. 存入 Aave 作為抵押
// 3. 借出更多 ETH
// 4. 重複直到觸發清算
重入攻擊的 Gas 成本分析
從 Gas 消耗的角度,重入攻擊的成本其實比想像中高:
假設攻擊者有 1 ETH,要進行 N 次重入呼叫:
Gas 消耗 = N × (CALL gas + 轉帳 Gas + 回調函數 Gas)
= N × (700 + 9000 + 20000) ≈ N × 30,000 gas
以 2026 年平均 Gas 價格 30 gwei 計算:
- 10 次重入:10 × 30,000 × 30 × 10^-9 ETH ≈ 0.009 ETH ≈ $20
- 100 次重入:0.2 ETH ≈ $400
所以重入攻擊不是無限制的,你的 Gas limit 和攻擊利潤決定了最大重入深度。
防止重入的 Opcode 級別技巧
// 方法一:Checks-Effects-Interactions 模式(最推薦)
function withdrawGood(uint256 amount) external {
// 1. Checks
require(balances[msg.sender] >= amount);
// 2. Effects —— 先更新狀態!
balances[msg.sender] -= amount;
// 3. Interactions —— 最後才與外部合約互動
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
// 方法二:使用 ReentrancyGuard
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract SecureContract is ReentrancyGuard {
function withdraw(uint256 amount) external nonReentrant {
// 這個 modifier 會在函數執行前後檢查狀態
// ...
}
}
// 方法三:顯式檢查 msg.sender 是否為合約
function isEOA(address account) public view returns (bool) {
return account.code.length == 0;
}
Solidity 0.8+ 編譯器甚至會在某些情況下自動插入重入檢查——這是 EVM 安全標準提升的表現。
從 Solidity 到 Bytecode:編譯過程大公開
說了這麼多 EVM 的執行細節,最後我們來聊聊 Solidity 程式碼是怎麼變成 EVM 可以執行的 bytecode 的。
這個過程分為幾個步驟:
- 解析與語法樹:Solidity 編譯器(solc)首先把你的程式碼解析成一個抽象語法樹(AST),檢查語法錯誤並建立語義模型。
- 中間表示(IR):solc 會先把 Solidity 翻譯成一種中間表示(Yul 或某種內部 IR),這個步驟會做一些初步的優化。
- 目標代碼生成:IR 會被編譯成 EVM bytecode。不同的編譯器後端(legacy、ir-optimized、via-ir)會產生不同風格的 bytecode。
如果你用 solc --opcodes 輸出 opcode,或者用 solc --bin-runtime 輸出 runtime bytecode,就能看到 Solidity 的原始魔力。以下是一個簡單合約的 bytecode:
// 原始 Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Counter {
uint256 public count;
function increment() external {
count += 1;
}
}
編譯後的 runtime bytecode 大概長這樣(簡化版):
PUSH1 0x80 // 初始化 Memory 指標
PUSH1 0x40 // 初始化 Free Memory Pointer
CALLVALUE // 檢查是否附帶 ETH
DUP1 //
...
// increment() 函數
CALLDATASIZE // 讀取 call data 大小
PUSH1 0x04 //
... // 函數選擇器匹配
SLOAD // 讀取 count
PUSH1 0x01 //
ADD // 加 1
SSTORE // 寫回 Storage
STOP // 停止執行
你可以用 evm.codes 這個網站實際操作各種 opcode,看看它們對 Stack、Memory、Storage 的影響。這個網站是我學習 EVM 過程中用得最多的工具之一。
結語:EVM 是一座城堡,需要深入了解它的每一塊磚
寫到這裡,我已經涵蓋了 EVM 執行模型的核心概念——帳戶模型、三層儲存架構、Opcode 與 Gas 計算、區塊級執行、以及編譯過程。但說實話,這只是冰山一角。EVM 的世界還有很多值得探索的角落,比如:
- EVM 的升級路徑:EIP-3540 的 EOF(EVM Object Format)如何改變 bytecode 的結構
- Precompile 合約:哪些密碼學操作有硬體加速,為什麼?
- State Rent 提案:為什麼要為 Storage 付租金,這對開發者意味著什麼?
- 跨客戶端的差異:Geth、Besu、evmone、Nethermind——它們對 EVM 的實現有什麼不同?
如果你看完這篇文章之後,對 EVM 產生了興趣,我的建議是:不要只看文件,去讀原始碼。Geth 的 vm package 是目前最完整的 EVM 實現參考。每一個 opcode 的行為、每一次狀態轉換的邏輯,都寫得清清楚楚。
我個人最喜歡的學習路徑是這樣的:先用 Solidity 寫一個合約,感受「做這件事很貴」;再去查對應的 opcode 消耗,理解「為什麼很貴」;最後看 Geth 原始碼,看看「這個 opcode 是怎麼被執行的」。走完這個流程,你對 EVM 的理解就會完全不同。
祝你在 EVM 的世界裡玩得開心。有問題隨時來聊。
參考資源
- 以太坊黃皮書:https://ethereum.github.io/yellowpaper/paper.pdf
- EVM Codes(互動式 opcode 參考):https://www.evm.codes/
- Geth 原始碼:https://github.com/ethereum/go-ethereum
- evmone(高效能 EVM):https://github.com/ethereum/evmone
- Solidity 文件:https://docs.soliditylang.org/
相關文章
- 以太坊 EVM 執行模型深度技術分析:從位元組碼到共識層的完整解析 — 本文從底層架構視角深入剖析 EVM 的執行模型,涵蓋 opcode 指令集深度分析、記憶體隔離模型、Gas 消耗機制、呼叫框架、Casper FFG 數學推導、以及 EVM 版本演進與未來發展。我們提供完整的技術細節、位元組碼範例、效能瓶頸定量評估,幫助智慧合約開發者與區塊鏈研究者建立對 EVM 的系統性理解。
- 以太坊虛擬機(EVM)完整技術指南:從執行模型到狀態管理的系統性解析 — 本文提供 EVM 的系統性完整解析,涵蓋執行模型、指令集架構、記憶體管理、狀態儲存機制、Gas 計算模型,以及 2025-2026 年的最新升級動態。深入分析 EVM 的確定性執行原則、執行上下文結構、交易執行生命週期,並探討 EOF 和 Verkle Tree 等未來演進方向。
- 以太坊黃皮書完整深度解析系列(二):Gas計算公式推導、EVM執行模型與智能合約部署 — 本文是黃皮書深度解析系列的第二篇,聚焦於 Gas 計算公式的完整推導、EVM 執行模型的形式化描述、以及智能合約部署的狀態轉換過程。我們涵蓋操作碼 Gas 成本的完整推導(棧操作、記憶體操作、存儲操作、調用操作)、EVM 機器狀態的形式化定義、日誌與事件的 Gas 計算、區塊後處理函數、以及 EIP-1559 費用機制的黃皮書更新。這是智慧合約開發者、協議設計者和安全審計師必讀的參考資料。
- 以太坊虛擬機(EVM)深度技術分析:Opcode、執行模型與狀態轉換的數學原理 — 以太坊虛擬機(EVM)是以太坊智能合約運行的核心環境,被譽為「世界電腦」。本文從計算機科學和密碼學的角度,深入剖析 EVM 的架構設計、Opcode 操作機制、執行模型、以及狀態轉換的數學原理,提供完整的技術細節和工程視角,包括詳細的 Gas 消耗模型和實際的優化策略。
- 以太坊 EVM 執行模型原始碼深度解析:從 Go-Ethereum 到 Rust-Reth 的實作對比 — 本文從原始碼層級深入分析以太坊 EVM 執行引擎的內部實作,涵蓋 geth 和 Reth 兩大主流客戶端的架構設計、Stack/Memory/Storage 的實作細節、Call 框架的 Gas 計算模型、預編譯合約的實現,以及 Blob 交易的費用機制。透過對比不同客戶端的實作差異,幫助讀者理解 EVM 設計的深層邏輯與效能優化策略。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案完整列表
- Solidity 文檔 智慧合約程式語言官方規格
- EVM 代碼庫 EVM 實作的核心參考
- Alethio EVM 分析 EVM 行為的正規驗證
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!