Bitroot部落格
返回官網 ↗
© 2026 Bitroot · 本站內容僅供一般參考,不構成財務、投資、法律或稅務建議。
編輯標準返回官網
← 全部文章
EVM 基礎·2026/09/25·約 14 分鐘

Gas 機制精讀:計量單位、費用市場與執行中止

Gas 是 EVM 的資源計量單位,價格則由費用市場決定;把兩者混為一談,會誤判擴容與執行中止的邊界。下面拆解固有成本與動態成本的構成,說明 out-of-gas 與 REVERT 各退什麼,EIP-1559 的 base fee 與 tip 各歸誰。

一筆最簡的 ETH 轉帳固定消耗 21000 gas(黃皮書附錄 G 的 Gtransaction);一筆 ERC-20 轉帳的消耗取決於合約實現與槽位冷熱狀態,通常落在數萬 gas 這個量級,這是量級估計而非實測值。這些數字由協議規定,只有硬分叉才會改動;而同樣一筆交易實際支付多少 ETH,可以在幾小時內變化十倍以上。前者是計量單位,後者是價格。把這兩個概念分開,費用討論、擴容敘事與執行中止的行為才談得清楚。

Gas 機制同時承擔三件事:把計算與狀態操作折算成統一單位,用一個價格市場分配有限的區塊空間,並在資源耗盡時給出確定的回退邊界。下面按這三條線展開:固有成本與動態成本各包含什麼,冷熱存取如何計價,交易在無效、REVERT 與 out-of-gas 三種中止下分別退回什麼,EIP-1559 之後 base fee、priority fee 與執行層 gas 的邊界在哪裡,以及為什麼 Gas 讓 DoS 變得可計價,卻無法讓定價永遠正確。

Gas 是計算與儲存的計量單位,與幣價是兩回事

Gas 是抽象的計價單位,對應執行一條指令、讀寫一次狀態、擴充一個記憶體字所消耗的抽象資源量。它的數值由協議常數給定,例如加法 3 gas(Gverylow)、柏林升級(區塊 12,244,000,2021 年 4 月 15 日)之後 SLOAD 熱存取 100 gas、SSTORE 從零寫非零 20000 gas。這些常數只有透過硬分叉才會變化,與市場供需無關。

Gas 的價格以 wei 計價,常用單位是 gwei,1 gwei 等於 10⁹ wei。一筆交易的費用等於 gas_used 乘以 effective_gas_price,前者由執行過程決定,後者由費用市場與交易簽章裡的出價共同決定。這個乘法關係是後面全部討論的基礎。

分清這兩層有幾個直接好處。討論「降 gas」時,先確認目標到底是降低 gas_used 還是降低 gas price:前者要靠減少操作、最佳化儲存布局或修改 EVM 計價規則,後者取決於當下需求與費用市場的形態,EIP-1559 讓後者更可預測,但沒有改變任何一條指令的價格。討論「手續費歸誰」時,base fee 部分被協議銷毀,不存在收款方,只有 priority fee 進入驗證者帳戶。討論「交易上限」時,gas_limit 是容量上限而非費用上限,費用上限是 max_fee_per_gas,兩者是不同的欄位。

固有成本:執行開始前就已扣掉的部分

每筆交易要先付固有成本(intrinsic gas),它在 EVM 執行第一條指令之前就被扣掉。黃皮書式 (64) 把它記為 g₀:21000 的固定底價(Gtransaction),加上資料欄位每個零位元組 4 gas、每個非零位元組 16 gas(非零位元組價由 EIP-2028 在伊斯坦堡升級,區塊 9,069,000,2019 年 12 月 8 日,從 68 降到 16),若為合約建立交易再加 32000(Gtxcreate);EIP-2930(柏林升級,區塊 12,244,000,2021 年 4 月 15 日)之後,access list 中的每個位址 2400 gas、每個儲存鍵 1900 gas;EIP-3860(上海升級,區塊 17,034,870,2023 年 4 月 12 日)之後,合約建立的 initcode 每 32 位元組字再收 2 gas。

21000 的含義容易被誤讀成「轉帳的手續費」。準確的說法是,它表示「有一筆交易」的底價,與轉帳金額無關。最簡 ETH 轉帳的 calldata 為空,資料部分不產生額外成本,所以正好是 21000。

EIP-2929(柏林升級,區塊 12,244,000,2021 年 4 月 15 日)還規定了交易開始時的熱集合:發送方位址、接收方位址(建立交易則是被建立的位址)以及所有預編譯合約位址已經在 accessed_addresses 中,對它們不收取冷存取費用。這是為什麼一筆轉帳調向普通位址時,接收方的帳戶存取不會額外收費。

固有成本在執行前扣除,因此有三條行為邊界。gas_limit 低於固有成本的交易直接無效,不會被包含進區塊,也不扣任何 gas。執行中途因 out-of-gas 中止時,固有成本不會退回。access list 的 2400 與 1900(柏林之後的價格)屬於預付費:走訪一遍將要存取的位址與槽位,把它們提前置為熱,但如果實際執行沒有存取,這筆錢不退。

動態成本:記憶體擴充與冷熱存取如何累加

執行階段的成本由靜態部分與動態部分相加。靜態部分是操作碼的固定價,黃皮書附錄 G 的費率表用幾個檔位表示:Gverylow 3 gas(ADD、SUB、MLOAD、MSTORE、PUSH 系列等)、Glow 5、Gmid 8、Ghigh 10、Gbase 2、Gjumpdest 1。

動態部分取決於運算元規模與狀態存取冷熱。記憶體擴充按 C_mem(a) = 3a + ⌊a² / 512⌋ 計價(黃皮書式 (328),Gmemory = 3),a 是字數,實際扣費為擴充前後之差,這項在 704 位元組(22 個字)以內近似線性,之後二次項開始生效。複製類操作在基礎價上按字數加收 3 gas/字(Gcopy);KECCAK256 收 30 gas(Gkeccak256)加 6 gas/字(Gkeccak256word);LOG 收 375 gas(Glog)加每個 topic 375 gas(Glogtopic),再加 8 gas/位元組(Glogdata);EXP 收 10 gas(Gexp)加 50 gas/指數位元組(Gexpbyte)。

冷熱存取由 EIP-2929(柏林升級)定義。首次存取某個 (位址, 槽位) 對是冷存取,SLOAD 收 2100 gas;同一交易內再次存取是熱存取,收 100 gas。帳戶類存取的冷價是 2600 gas,覆蓋 CALL、CALLCODE、DELEGATECALL、STATICCALL、BALANCE 與 EXTCODESIZE、EXTCODECOPY、EXTCODEHASH。這套集合是交易作用域的,作用域回退時集合一併回退。

操作(柏林升級之後口徑)冷存取熱存取
SLOAD2100100
CALL 系列、BALANCE、EXT*2600100
SSTORE 的附加冷存取費2100不加收

冷熱機制的直接來源是兩次 DoS 攻擊留下的教訓。EIP-150(Tangerine Whistle,區塊 2,463,000,2016 年 10 月 18 日)在 2016 年 9 月與 10 月的攻擊後,已經把 SLOAD 從 50 gas 抬到 200、CALL 從 40 抬到 700、SELFDESTRUCT 從 0 抬到 5000。EIP-2929 的動機引用了 2019 年論文 arXiv:1909.07220 中的一次測量:在作者那套硬體上重放以太坊歷史時,同一批惡意交易要跑 20 到 80 秒,而普通交易只需幾毫秒,說明狀態讀取類操作碼仍然定價過低。這是一次性測量,只代表 2019 年的硬體與負載,不能當作持續追蹤的口徑。

執行中止的三種結局:無效交易、REVERT 與 out-of-gas

交易可能在三個不同階段結束,回退範圍與費用處理各不相同。

第一種是無效交易,包括 nonce 不匹配、簽章錯誤、餘額不足以覆蓋 gas 預付款、gas_limit 低於固有成本等情況。這類交易根本不會進入區塊,不消耗 gas;它屬於鏈下拒絕,與執行中止是兩回事。

第二種是 REVERT,由 revert 語句、require 失敗或內嵌組譯的 revert 觸發。當前框架的狀態變更全部回退,剩餘 gas 退回給上一層,已經消耗的部分照常支付;退款計數器也回退到進入該框架之前。REVERT 是「有理由的失敗」,可以把錯誤資料回傳,適合需要讓呼叫方區分失敗原因的場景。

第三種是異常中止(exceptional halt),包括 out-of-gas、非法操作碼、堆疊溢位、靜態呼叫中寫狀態、initcode 超限等。狀態同樣回退,但當前框架剩餘的 gas 全部銷毀。若異常發生在頂層框架,交易的 gas_used 等於 gas_limit,費用按滿額扣除,沒有退回的部分。這就是「OOG 之後 gas 全部消耗」的確切含義:當前框架的剩餘 gas 歸零,與整條鏈的餘額無關。

// REVERT:狀態回退,剩餘 gas 退回,已消耗的照付
function guarded(uint256 x) external pure returns (uint256) {
    require(x != 0, "zero");
    return 1e18 / x;
}

// 異常中止:無限迴圈觸發 out-of-gas,當前框架剩餘 gas 全部銷毀
function oog() external pure {
    while (true) {}
}

子呼叫的失敗是否牽連父框架,由 EIP-150(Tangerine Whistle)引入的「保留 1/64」規則決定。呼叫子框架時,父框架最多只能轉發自己剩餘 gas 的 63/64(即 gas - gas // 64),至少保留 1/64;CREATE 與 CREATE2 同樣只提供 63/64。因此子框架 OOG 不會連帶父框架 OOG,父框架可以用保留的 gas 捕捉失敗並繼續執行。這條規則在 2016 年替換了原先的呼叫深度限制,把「深度炸彈」從一類結構性攻擊降級為純 gas 問題。

// 最多向子呼叫轉發 gasleft() 的 63/64,父框架保留 1/64
(bool ok, ) = target.call{gas: gasleft()}("");

帶 value 的 CALL 還有兩個附加項(黃皮書附錄 G):非零轉帳收 Gcallvalue 9000 gas,若目標帳戶不存在再收 Gnewaccount 25000 gas;被呼叫方會得到 2300 gas 的呼叫補貼(Gcallstipend),用於最簡單的接收邏輯。EIP-2200(伊斯坦堡升級,區塊 9,069,000,2019 年 12 月 8 日)規定 gasleft 小於等於 2300 時 SSTORE 直接以 OOG 失敗,正是為了防止這段補貼路徑被用來寫入狀態。

退款(refund)在交易執行結束後結算,執行期間不可見。EIP-3529(倫敦升級,區塊 12,965,000,2021 年 8 月 5 日)把非零改寫為零的退款從 15000 降到 4800,取消了 SELFDESTRUCT 退款,並把單筆交易的總退款上限壓到 gas_used // 5。由於退款在執行期間不可用,合約不能依賴它來墊付交易中途需要的 gas。

EIP-1559:base fee、tip 與執行層的邊界

EIP-1559(倫敦升級,區塊 12,965,000,2021 年 8 月 5 日)把交易費用拆成兩段。base fee 由協議按父區塊的 gas_used 與目標值(gas_limit 的一半)計算,單塊最多上下調整 12.5%,且被銷毀。priority fee(tip)是使用者給驗證者的額外單價,用於爭取更早打包。交易簽章裡給出 max_fee_per_gas 與 max_priority_fee_per_gas 兩個上限,實際生效單價是:priority fee 取 max_priority_fee_per_gas 與 (max_fee_per_gas - base fee) 的較小值,effective_gas_price 等於 priority fee 加 base fee。未使用的 gas 按這個單價退回。

執行層的邊界也要說清。GASPRICE 操作碼在 EIP-1559 之後回傳的是 effective_gas_price,也就是發送方實付單價;驗證者實得的部分無法從執行環境裡直接讀到。這也意味著任何依賴 GASPRICE 判斷「礦工收入」的舊邏輯都不再成立。

兩段費用回答的是不同問題:base fee 決定進入區塊的最低門檻,tip 決定同一區塊內的優先順序。區塊空間總量沒有變化,當持續用量超過目標值時,base fee 會逐塊上升把需求擠出去,這是排隊機制,與產能擴張無關。把 EIP-1559 當作降低 gas_used 的方案是常見誤讀,它只改變價格形成方式。

還有一層容易混淆的邊界:blob 費用不屬於執行層 gas。EIP-4844(坎昆升級,區塊 19,426,587,2024 年 3 月 13 日)為 blob 資料引入了獨立的基礎費與計價單位(blob gas),EIP-7516 增加了回傳 blob base fee 的操作碼。Rollup 的資料發布成本討論的是 blob 市場,與合約執行消耗的 gas 是兩套帳本。

DoS 的「可計價」不等於定價正確

Gas 的核心安全屬性是將資源佔用折算成成本。要讓一個區塊被垃圾交易佔滿,攻擊者必須支付與該區塊全部 gas 對應的費用,這是「可計價」的含義。相比不收費的網路,攻擊的邊際成本從零變成與佔用成正比。

但這只在定價與真實成本相符時才有效。2016 年 9 月與 10 月的 DoS 攻擊利用的正是被低估的狀態讀取與呼叫類操作碼,直接催生了 Tangerine Whistle 硬分叉;此後 EIP-1884(伊斯坦堡升級,區塊 9,069,000,2019 年 12 月 8 日)把 SLOAD 從 200 抬到 800,BALANCE 與 EXTCODEHASH 從 400 抬到 700,EIP-2929 又把狀態存取拆成冷熱兩檔並大幅提高冷存取價,理由仍是同樣的模式可以把單塊處理時間拉到幾十秒。EIP-3860 管的是 initcode 的跳轉目標分析,此前這段工作量完全沒有計量。計價規則與實現成本之間存在結構性落後:客戶端的資料庫布局與執行引擎持續最佳化,gas 常數卻要等下一次硬分叉才會重估。

退款機制提供了反向的例證。它本來鼓勵合約清理不再使用的狀態,實際卻催生了 GasToken 這類把狀態槽位當電池、在低費率時囤積 gas、在高費率時釋放的方案,造成狀態膨脹與區塊 gas 使用量的額外變異數。EIP-3529 削減與封頂退款,就是為了關掉這條通道。

機制失效的條件有幾處需要單獨指出。定價是治理產物,常數通常按各實現的最壞用例挑選(EIP-3860 就寫明其 2 gas/字來自不同實現的最壞情況基準),而不同客戶端在同一批操作上的耗時不同,同一 gas 價格對應的真實資源成本因此並不一致。這是一個定性判斷;EIP-2929 為抬價給出的定量證據是 2019 年在某套測試硬體上的一次性測量,不能當作持續口徑。排序市場的價值並不完全由 tip 承載,MEV 拍賣與客戶端策略都會影響打包順序,tip 高只是更可能進入下一塊的激勵之一。區塊 gas 上限本身就是吞吐量與驗證成本的權衡,抬高上限會同時抬高全節點的硬體門檻。

這一節的落點在於說明它是一套需要定期校準的機制:「gas 定價可靠」與「gas 定價不可靠」都是過強的結論。EIP-150、EIP-1884、EIP-2929、EIP-3860 都是校準動作。至於下一次校準由什麼觸發,只能給推斷而不是結論:四次校準的直接誘因都是被公開觀測到的資源放大攻擊,據此推斷下一次大概率仍是同類事件。這個推斷沒有公開依據,也不排除因狀態增長、區塊容量等其他考量提前重估。

邊界與不確定的地方

引用的口徑是黃皮書附錄 G 費率表與式 (64)、式 (328)(下載版本的頁腳標註為 Shanghai 版本),以及 EIP-150、EIP-1884、EIP-2028、EIP-2200、EIP-1559、EIP-2929、EIP-2930、EIP-3529、EIP-3860 的規範文本。具體數字隨分叉變化,正文標出的分叉與區塊高度是:伊斯坦堡(2019 年 12 月 8 日,區塊 9,069,000)、柏林(2021 年 4 月 15 日,區塊 12,244,000)、倫敦(2021 年 8 月 5 日,區塊 12,965,000)、上海(2023 年 4 月 12 日,區塊 17,034,870)、坎昆(2024 年 3 月 13 日,區塊 19,426,587)。在 EVM 相容鏈上討論 gas 之前,需要先確認該鏈啟用了哪些分叉,計價常數可能與以太坊主網不同。Tangerine Whistle 的區塊 2,463,000 與 2016 年 10 月 18 日同樣來自 ethereum.org 的升級歷史。

關於「定價落後」的嚴重程度,只能給出旁證,給不出量化結論:公開材料裡沒有一份持續追蹤「協議 gas 常數與實際執行耗時比值」的時間序列,因此無法判斷落後是在擴大還是收斂。這一點應作為不確定項保留,不用它反推「正在惡化」或「已經收斂」之類的結論。

另外,費用市場的行為受 MEV 與客戶端策略影響,沒有展開排序市場的機制細節,也沒有把 blob 費用市場與執行層 gas 放在同一框架裡比較,兩者在 EIP-4844 之後是分開的計價體系。引用這些數字做成本模型時,需要先確認口徑與版本。

資料來源

  • EIP-1559: Fee market change for ETH 1.0 chain,https://eips.ethereum.org/EIPS/eip-1559
  • EIP-2929: Gas cost increases for state access opcodes,https://eips.ethereum.org/EIPS/eip-2929
  • EIP-3529: Reduction in refunds,https://eips.ethereum.org/EIPS/eip-3529
  • EIP-150: Gas cost changes for IO-heavy operations,https://eips.ethereum.org/EIPS/eip-150
  • EIP-1884: Repricing for trie-size-dependent opcodes,https://eips.ethereum.org/EIPS/eip-1884
  • EIP-2028: Transaction data gas cost reduction,https://eips.ethereum.org/EIPS/eip-2028
  • EIP-2200: Structured Definitions for Net Gas Metering,https://eips.ethereum.org/EIPS/eip-2200
  • EIP-2930: Optional access lists,https://eips.ethereum.org/EIPS/eip-2930
  • EIP-3860: Limit and meter initcode,https://eips.ethereum.org/EIPS/eip-3860
  • EIP-4844: Shard Blob Transactions,https://eips.ethereum.org/EIPS/eip-4844
  • EIP-7516: BLOBBASEFEE opcode,https://eips.ethereum.org/EIPS/eip-7516
  • Ethereum Yellow Paper,附錄 G 費率表、式 (64) 固有成本、式 (328) 記憶體計價函式,https://ethereum.github.io/yellowpaper/paper.pdf
  • ethereum.org 開發者文件:Gas 與費用,https://ethereum.org/en/developers/docs/gas/
  • ethereum.org 操作碼參考,https://ethereum.org/en/developers/docs/evm/opcodes/
  • ethereum.org 網路升級歷史(各分叉區塊高度與日期),https://ethereum.org/en/history/
  • arXiv:1909.07220,EIP-2929 引用的狀態存取耗時測量,https://arxiv.org/abs/1909.07220

延伸閱讀

效能指標詞典:TPS、BPS、確認延遲、最終性、衝突率區塊鏈擴容地圖:L1 平行、L2、分片與 DA 各自解決什麼去中心化與效能的張力:驗證者門檻、硬體與地理分布

本篇屬於 EVM 基礎系列,前置篇目為 0.5《三類儲存別搞混:Memory、Storage 與 Transient Storage》。

← 上一篇三類儲存別搞混:Memory、Storage 與 Transient Storage
本文目錄
Gas 是計算與儲存的計量單位,與幣價是兩回事固有成本:執行開始前就已扣掉的部分動態成本:記憶體擴充與冷熱存取如何累加執行中止的三種結局:無效交易、REVERT 與 out-of-gasEIP-1559:base fee、tip 與執行層的邊界DoS 的「可計價」不等於定價正確邊界與不確定的地方資料來源延伸閱讀
閱讀設定