跳到正文

以太坊虛擬機(EVM)

一份具備分叉意識的 EVM 指南,涵蓋交易、訊息呼叫框架、位元組碼、堆疊、記憶體、儲存空間、Gas、REVERT、DELEGATECALL、預先編譯合約、收據與狀態核對。

更新於

僅供教育參考,不構成投資或安全建議。EVM 行為取決於確切的鏈、分叉、狀態、位元組碼、呼叫情境、Gas 計費規則、用戶端及協定升級。

直接答案

以太坊虛擬機是執行層的狀態轉換機,依照特定分叉的規則解讀位元組碼。若給定相同的有效前狀態、頂層交易或訊息、區塊環境及分叉規範,符合規範的執行用戶端必須算出相同的後狀態或失敗結果。EVM 不決定交易順序、不提供共識最終性、不驗證前端,也不會讓每條 EVM 相容鏈都具備與以太坊相同的安全性。

由外部簽署的交易是頂層協定物件。合約之間的活動由巢狀訊息呼叫和呼叫框架組成,並非各自擁有交易 nonce、收據或雜湊值的獨立交易。每個框架都有程式碼、程式計數器、256 位元堆疊、記憶體、呼叫資料、回傳資料、Gas 與執行情境;持久儲存空間屬於帳戶,而暫時性儲存空間在相應分叉規則下只存在於一筆交易期間。

運作方式

  1. 鎖定執行快照:鏈及 chainId、網路、區塊編號與區塊雜湊值、標準或最終化狀態、分叉、執行用戶端與規範修訂版、前狀態或狀態根、交易類型、原始簽署位元組、交易雜湊值和收據。確定性以這一確切情境為條件。
  2. 解碼交易封套,並將執行前有效性與執行分開。驗證簽章和傳送者、nonce、目的位址或合約建立、移轉價值、呼叫資料、Gas 上限、費用欄位、存取清單及各交易類型特有的欄位。在納入區塊前遭拒絕的交易,並不是已納入並執行後發生狀態回復的交易。
  3. 建立頂層訊息和完整呼叫樹。記錄 CALLSTATICCALLDELEGATECALL、合約建立及預先編譯合約框架;呼叫者、情境位址、程式碼位址、msg.sendermsg.value、價值移轉、呼叫資料、回傳資料、轉送的 Gas 和成功旗標。區塊瀏覽器所稱的內部交易是追蹤檢視,並非簽署交易。
  4. 追蹤每個框架的程式計數器、堆疊、記憶體、呼叫資料、回傳資料、日誌、持久儲存空間和暫時性儲存日誌。CALL 使用被呼叫方的位址與儲存情境;DELEGATECALL 在呼叫方的位址與儲存情境中執行目標程式碼,同時保留上游傳送者與移轉價值;STATICCALL 禁止修改狀態。
  5. 套用目標分叉的 Gas 規則:固有 Gas、操作碼動態成本、記憶體擴張、冷存取與熱存取、呼叫轉送、津貼、預先編譯費用、退款及退款上限。接著依據已用 Gas 和有效 Gas 價格,將交易費用與移轉價值分開計算。Gas 估算具有條件限制,並非保證。
  6. 依作用範圍處理框架結果。若祖先框架最終提交,RETURN 會提交目前框架。REVERT 會回復目前框架及其後代框架、回傳資料,而且不一定耗盡目前框架的剩餘 Gas;異常停止的規則不同。父框架可以捕捉失敗的低階呼叫並繼續執行,因此即使子呼叫失敗,頂層收據狀態仍可能是 1。頂層交易納入後即使失敗,仍會耗用傳送者 nonce 與實際支付的 Gas。
  7. 將收據狀態、已用 Gas、日誌、建立位址與回傳資料,和前後狀態中的餘額、nonce、程式碼、持久與暫時性儲存空間、代幣帳本、執行追蹤及區塊狀態根進行核對。必要時以獨立用戶端重新執行;代理實作、儲存配置、預先編譯合約、編譯器 EVM 目標與分叉升級,應和共識最終性分開稽核。

計算範例

  • 已納入區塊的頂層回復與費用帳本。 一筆類型 2 交易的 Gas 上限為 80,000,已用 Gas 為 52,000,基本費用為 20 gwei,最高優先費為 3 gwei,最高費用為 40 gwei。有效 Gas 價格是 min(40, 20 + 3) = 23 gwei;實際費用為 52,000 * 23 gwei = 0.001196 ETH,其中銷毀 52,000 * 20 gwei = 0.001040 ETH,優先費為 52,000 * 3 gwei = 0.000156 ETH。未使用的 28,000 gas 不收費。若頂層執行回復,其儲存、價值移轉和日誌影響都會撤銷,但傳送者 nonce 與實際費用仍會保留。
  • 被捕捉的子呼叫失敗。 合約 A 初始為 A.x = 5。它呼叫 B;B 寫入 B.y = 9,發出日誌後執行 REVERT。B 的寫入和日誌均被回復。A 觀察到 success = false,寫入 A.x = 7 並正常回傳。最終收據狀態為 1A.x = 7,B 則保留呼叫前的值。因此,頂層成功不能證明每個子呼叫都成功。
  • DELEGATECALL 的儲存情境。 代理的 slot0 = 5,實作帳戶的 slot0 = 99。實作程式碼讀取槽位 0,加上 7 後寫回。透過 DELEGATECALL 執行時,代理變為 slot0 = 12,實作帳戶仍為 slot0 = 99;所採用的是代理位址和儲存情境,上游傳送者及移轉價值保持不變。不相容的儲存配置可能損壞代理狀態。
  • 受分叉約束的 SELFDESTRUCT。 在 EIP-6780 規則下,一個既有合約持有 2 ETH,並向受益人 B 執行 SELFDESTRUCT。B 收到 2 ETH,合約餘額變為零,但這個既有帳戶、程式碼和儲存空間不會刪除。只有合約在同一筆交易中建立並自行銷毀時,才仍適用刪除行為。這是受分叉約束的規則,不能向過去或每條 EVM 鏈外推。

風險

  • 在錯誤的鏈、區塊、分叉、用戶端或前狀態上重播。
  • 把非標準或已由鏈重組取代的狀態當成最終執行情境。
  • 混淆執行前驗證拒絕與納入區塊後的執行回復。
  • 假設待處理交易的模擬結果會與之後納入時的狀態相同。
  • 忽略頂層回復、異常停止或 Gas 耗盡。
  • 遺漏被父框架捕捉的子呼叫失敗。
  • 誤讀呼叫情境、程式碼位址、呼叫方、傳送者或移轉價值。
  • DELEGATECALL 儲存配置不相符而損壞代理狀態。
  • 允許重入攻擊,或不安全地向外移轉價值與控制權。
  • 信任未經驗證的回傳資料、成功旗標或自訂錯誤。
  • 把日誌或執行追蹤當成權威最終狀態。
  • 錯算冷存取、熱存取、記憶體或呼叫轉送的 Gas。
  • 錯用退款、退款上限、津貼或 63/64 規則。
  • 使用錯誤的預先編譯位址、輸入、Gas 規則或分叉語意。
  • 混淆持久儲存空間、記憶體和暫時性儲存空間的生命週期。
  • 預期在 STATICCALL 內能成功修改狀態。
  • 沿用 EIP-6780 之前關於 SELFDESTRUCT 刪除狀態的假設。
  • 遺漏代理實作、管理者或編譯器目標的變動。
  • 執行規則分歧或未升級的執行用戶端。
  • 從 EVM 相容性推論共識、跨鏈橋、代幣、治理或最終性安全。

常見誤解

  • Solidity 原始碼就是鏈上直接執行的物件。
  • 已納入區塊的交易發生狀態回復後不花費任何費用,nonce 也不變。
  • 收據狀態為 1 就證明每個內部呼叫都如預期成功。
  • 事件或執行追蹤就是權威的資產與儲存狀態。
  • EVM 相容性保證操作碼、Gas、預先編譯合約、共識和安全性完全相同。

相關主題

資料來源

導覽

搜尋知識庫...