---
id: 24
title: "Gas 机制精读：计量单位、费用市场与执行中止"
slug: 0-7-gas-mechanics
date: 2026/09/25
summary: Gas 是 EVM 的资源计量单位，价格则由费用市场决定；把两者混为一谈，会误判扩容与执行中止的边界。下面拆解固有成本与动态成本的构成，说明 out-of-gas 与 REVERT 各退什么，EIP-1559 的 base fee 与 tip 各归谁。
keywords: Gas计量,EIP-1559,固有成本,out-of-gas,EIP-2929
heroImage: /images/articles/photos/0-7-gas-mechanics.jpg
---

一笔最简的 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。这套集合是交易作用域的，作用域回滚时集合一并回滚。

| 操作（柏林升级之后口径） | 冷访问 | 热访问 |
|--------------------------|--------|--------|
| SLOAD | 2100 | 100 |
| CALL 系列、BALANCE、EXT* | 2600 | 100 |
| 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 归零，与整条链的余额无关。

```solidity
// 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 问题。

```solidity
// 最多向子调用转发 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、确认延迟、最终性、冲突率》](/zh/blog/performance-metrics-glossary)
- [《区块链扩容地图：L1 并行、L2、分片与 DA 各自解决什么》](/zh/blog/blockchain-scaling-map)
- [《去中心化与性能的张力：验证者门槛、硬件与地理分布》](/zh/blog/decentralization-performance-tradeoff)

本篇属于 EVM 基础系列，前置篇目为 0.5《三类存储别搞混：Memory、Storage 与 Transient Storage》。
