news 2026/9/15 13:17:16

EIP-3322 账户 Gas 存储操作码解析:SELFGAS / USEGAS / STOREGAS 的设计、定价与测试验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
EIP-3322 账户 Gas 存储操作码解析:SELFGAS / USEGAS / STOREGAS 的设计、定价与测试验证

EIP-3322 账户 Gas 存储操作码解析:SELFGAS / USEGAS / STOREGAS 的设计、定价与测试验证

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

导读

本文围绕以太坊改进提案 EIP-3322(Account gas storage opcodes) 展开,该提案为合约账户引入一个可持久化的"gas 退款计数器",并新增SELFGASUSEGASSTOREGAS三个操作码用于读取、消耗与充值该计数器,从而为 gas 市场提供一种比 GasToken 更高效、更少状态开销的 gas 存储机制。通过阅读本文,你将理解以太坊 refund(退款)机制的运作原理、GasToken 套利模式的缺陷,以及 EIP-3322 如何借助现有退款计数器的 50% DoS 防护来构建受控的 gas 弹性供给;同时我们会逐行验证提案附带的 EVM 字节码测试用例,并梳理其与 EIP-1884、EIP-2200、EIP-3529 等 EIP 的关联脉络。

提案概览与当前状态

EIP-3322 由 William Morriss(@wjmelements)于 2020-03-04 提出,类别为Standards Track / Core,当前状态为Stagnant(停滞),即该提案目前未被纳入任何以太坊网络升级,文中描述的操作码与 gas 定价均属于提案层面的设计,尚未在主网生效。该提案的核心主张可以浓缩为一句话:允许合约账户把 gas "存起来",并在之后转移给执行上下文的退款计数器使用

提案的 Abstract 明确给出了三层设计:

  1. 合约可以持久化(persist)gas,留待后续转移给退款计数器;
  2. 为此引入三个新操作码;
  3. 这三个操作码分别负责读取(read)、**增加(add to)使用(use)**这个 gas 计数器。

背景:refund 机制与 GasToken 的两难

要理解 EIP-3322 的价值,必须先理解它想解决的问题——GasToken 对退款机制的利用。

以太坊的 gas 退款(refund)机制最初由 EIP-2200(Structured Definitions for Net Gas Metering) 等提案系统性定义:当SSTORE将存储槽清零、或SELFDESTRUCT销毁合约时,会向执行上下文的退款计数器(refund counter)写入 gas,交易结束时统一结算。EIP-2200 定义了SLOAD_GASSSTORE_SET_GASSSTORE_RESET_GASSSTORE_CLEARS_SCHEDULE等定价参数,其中SSTORE_RESET_GAS5000SSTORE_CLEARS_SCHEDULE15000——这正是 EIP-3322 为USEGAS(5000)和STOREGAS(5000 + amount)定价时参考的两个锚点。

问题在于,退款机制被 GasToken 演变成了一种"跨区块的 gas 套利工具"。EIP-3322 的 Motivation 对此的表述非常直白:

The refund mechanism is currently being used by gas tokens to arbitrage gas price. This brings gas supply elasticity and price stability by moving gas from blocks with less demand to blocks with more demand. Unfortunately this rewards unnecessary state growth.

即:GasToken 通过在低费区块"囤积"gas、在高费区块"释放"gas,确实带来了 gas 供给弹性和价格稳定,但代价是奖励了不必要的状态增长——因为存储槽被当作"电池"来储存 gas,网络因此承担了大量本无意义的存储膨胀。这一判断在后续的 EIP-3529(Reduction in refunds) 中被再次确认:refunds 加剧了状态规模(state slots 被当作"battery")、增加了区块大小方差(理论上区块实际消耗的 gas 可接近账面上限的两倍)。

EIP-3322 提出的解决方案方向是:与其让 GasToken 通过污染状态来存储 gas,不如在协议层提供一个一等公民的 gas 存储机制——即账户级 gas 退款计数器。这样一来,gas 市场所需的存储与计算开销都会显著降低。

核心规范:账户级 gas 退款计数器与三个操作码

状态模型:每个合约账户新增一个计数器

规范的第一步是定义状态变化:

Contract accounts gain an unsigned gas refund counter, initially zero.

即每个合约账户新增一个无符号 gas 退款计数器(unsigned gas refund counter),初始值为 0。该计数器属于账户状态,随账户持久化。同时提案强调,由于"大多数账户不会存储任何 gas",每个账户的存储开销在常规情况下应当极小甚至为零——这为客户端实现提供了状态布局上的优化空间(例如采用稀疏存储或默认零值压缩)。

三个新操作码

操作码字节码栈行为Gas 成本语义
SELFGAS0x49将当前账户的 gas 退款计数器压入栈顶SELFBALANCE相同(即GasFastStep,5 gas,见 EIP-1884)只读,不修改计数器
USEGAS0x4a弹出amount,将min(amount, 当前账户计数器)转入执行上下文的退款计数器5000消耗账户中存储的 gas,转移给本交易的退款计数器
STOREGAS0x4b弹出amount,将当前账户的退款计数器增加amount5000 + amount把 gas "存入"账户,供未来交易使用

三个操作码的职责边界非常清晰:SELFGAS是纯查询、USEGAS是"取款"、STOREGAS是"存款"。值得注意的是USEGAS的取款语义是截断式的——它取amount与账户计数器两者的最小值,因此不会把计数器取成负数;而STOREGAS的成本与存入量线性相关(5000 + amount),意味着"存款"行为本身要按存入额计价,从机制上避免了无限囤积。

字节码定位:与 SELFBALANCE、GASLIMIT 同处 0x4X 区间

Rationale 中专门解释了操作码编号的选择:

The opcode numbers chosen are in the same0x4Xrange asSELFBALANCEandGASLIMIT.

0x4X区间是 EIP-1884(Istanbul 升级)开辟的"新操作码安置区":由于0x3X区间已满,SELFBALANCE被安排在0x47,定价为GasFastStep(5 gas)。EIP-1884 当时给出的理由是SELFBALANCE仍需在(缓存的)trie 中查找余额,且余额在执行期间并非恒定,因此比同类快速操作略贵。EIP-3322 延续这一编号惯例,将三个新操作码紧邻SELFBALANCE之后放置,既保持编码空间整洁,也与同类账户状态操作在语义上聚拢。

设计原理:为什么这样做是安全的、便宜的、高效的

复用退款计数器的 50% DoS 防护

这是整个提案最精妙的一处设计。Rationale 写道:

By reusing the execution context's refund counter we can reuse its 50% DoS protection, which limits its block elasticity contribution to 2x.

退款机制自引入起就带有一层 DoS 防护:交易的退款上限为实际使用 gas 的一定比例(最初是 50%,EIP-3529 将其收紧为 1/5,即MAX_REFUND_QUOTIENT = 5)。由于退款只发生在交易结束结算时、且被上限钳制,攻击者无法借助退款让区块实际消耗超过限额的执行资源。EIP-3322 让USEGAS直接把账户中存储的 gas 转入执行上下文已有的退款计数器,而不是另起炉灶——这样它就自动继承了既有的 50%(后为 20%)钳制,将整个机制对区块弹性(block elasticity)的贡献严格限制在 2 倍以内,无需设计新的防护逻辑。

定价参照:SELFBALANCE 与 SSTORE

Rationale 明确说明 gas 成本的设计依据:

The gas costs are based on similar opcodesSELFBALANCEandSSTORE.

  • SELFGASSELFBALANCE同价:两者都是对当前账户自身状态的只读访问,无需从栈上取地址、天然命中缓存,工作量相当;
  • USEGAS定价 5000,对齐SSTORE_RESET_GAS(EIP-2200 定义的非零值覆写成本):因为它本质上是修改账户状态(消耗计数器),与一次存储重置的成本相当;
  • STOREGAS定价5000 + amount,其中 5000 对应状态写入的基准成本,amount则是对"存入额"的线性计价,类似SSTORE中按写入内容量级计费的思路。

常规情况零开销

Most accounts will store no gas, so the per-account storage overhead should be minimal or even zero in the normal case.

对于绝大多数不参与 gas 存储的账户,其计数器恒为 0,客户端可采用默认值压缩或稀疏索引的方式,避免为每个账户引入固定存储开销。

测试用例逐行验证

提案附带的测试用例表是理解操作码行为的最佳素材。表格各列含义如下:

  • Code:EVM 字节码序列;
  • Used Gas:执行该序列消耗的 gas;
  • Refund:执行结束后,执行上下文退款计数器中的值;
  • Original:执行前,账户 gas 退款计数器的初始值;
  • Final:执行结束后,账户 gas 退款计数器的终值。
CodeUsed GasRefundOriginalFinal
0x600049005003000
0x600349005003220
0x600349005003330
0x600349005003341
0x6003496003490010006440
0x6003496003490010006660
0x48490050101000001000000
0x61ffff4a00705380065535

用规范中的操作码语义逐一验算,可以确认这些测试的行为逻辑:

  1. 0x60004900(PUSH1 0 → 0x49 → STOP):Used Gas = 3(PUSH1)+ 5000(0x49)= 5003。若此处 0x49 为读取型操作,则账户计数器不变:Original = 0、Final = 0、Refund = 0。✓
  2. 0x60034900(PUSH1 3 → 0x49 → STOP)的三种变体,Used Gas 恒为 5003:若 0x49 对应USEGAS的取款语义(取min(amount, counter)):
    • Original = 2 时:min(3, 2) = 2转入 Refund,Final = 2 − 2 = 0;✓
    • Original = 3 时:min(3, 3) = 3转入 Refund,Final = 0;✓
    • Original = 4 时:min(3, 4) = 3转入 Refund,Final = 4 − 3 = 1。✓
  3. 0x60034960034900(连续两次取款,每次 amount = 3):Used Gas = 2 × (3 + 5000) = 10006。Original = 4 时,第一次取min(3,4)=3,计数器余 1;第二次取min(3,1)=1,计数器归零,Refund 累计 4。✓ Original = 6 时,两次各取 3,Refund = 6、Final = 0。✓
  4. 0x61ffff4a00(PUSH2 0xffff → 0x4a → STOP):若 0x4a 对应STOREGAS的存款语义,成本为5000 + 65535,加上 PUSH2 的 3 gas,恰好等于 70538;账户计数器从 0 增至 65535,Refund 保持 0。✓

以上验算显示,提案的测试表直接验证了USEGAS(取款)与STOREGAS(存款)的核心行为:取款受账户余额钳制、存款按量计费、读写均产生 5000 gas 级别的状态写入成本。需要说明的是,测试表中的操作码字节(0x49 表现出取款行为、0x4a 表现出存款行为)与最终规范正文中的编号(0x49=SELFGAS、0x4a=USEGAS、0x4b=STOREGAS)存在对应关系上的偏差,这反映了提案在停滞前编号方案曾有过调整,阅读测试用例时应以其行为语义为准。

兼容性与安全考量

向后兼容

Because the gas is added to the refund counter, no compatibility issues are anticipated.

EIP-3322 认为自身几乎不引入兼容性问题,理由有二:其一,新增操作码与新增账户字段不影响既有操作码的语义;其二,由于USEGAS把 gas 注入的是交易结算时统一处理的退款计数器,而非执行过程中的可用 gas(refund 机制自 EIP-3529 起就明确"refunds are only applied after transaction execution"),因此不会改变任何调用帧在执行期间可用的 gas 量,已部署合约的执行路径不受影响。

安全考量

DoS is already limited by the 50% refund limit.

提案的 Security Considerations 只有这一句,却点明了整个设计的安全基础:gas 存储机制最危险的滥用场景——通过"低价存储、高价释放"制造区块执行资源波动——已经被退款计数器既有的 50% 上限(提案写作时)天然约束。后续 EIP-3529 将钳制进一步收紧为gas_used // 5,并论证了退款上限对存储写密集型 DoS 攻击的限制能力(最多放大 25% 的存储写操作量),如果 EIP-3322 在未来被重新激活,其安全边界会随之一并收紧。

生态定位:与相关 EIP 的关系图谱

EIP-3322 并不是孤立的提案,它处于一条关于"gas 弹性与退款机制"的演进脉络中,理解这条脉络有助于把握它的设计取舍:

  • EIP-1884(Repricing for trie-size-dependent opcodes):引入SELFBALANCE(0x47,5 gas),并说明0x4X是新操作码安置区间——EIP-3322 的三个操作码正是沿用了这一编号空间与定价风格。
  • EIP-2200(Net Gas Metering):系统定义了退款计数器的增删规则与SSTORE定价参数(SSTORE_RESET_GAS = 5000),是 EIP-3322 定价与"复用退款计数器"设计的直接参考。
  • EIP-3529(Reduction in refunds)/EIP-3403(Partial removal of refunds)/EIP-3298(Remove storage-clear refund and refund cap):这三份提案代表了以太坊社区对退款机制的另一条治理路径——收缩乃至移除refund,以消除 GasToken 与区块大小方差问题。EIP-3322 则代表了与之相对的路径:把 gas 存储内建于协议,让弹性供给以更低的状态开销实现。两者目标一致(降低 GasToken 对网络的损害),手段相反。
  • EIP-2583(Penalty for account trie misses):同期针对账户状态访问的定价讨论,其操作码影响面分析中提到SELFBALANCE不受 trie-miss 惩罚影响(因为访问的是自身账户)——这一分析同样适用于SELFGAS这类自引用操作码的定价合理性。

从最终结果看,以太坊选择了 EIP-3529 收缩退款的路径,EIP-3322 因而长期处于 Stagnant 状态。但作为一篇研究性文档,它系统展示了一种"用协议内建状态替代隐性状态滥用"的 EVM 扩展设计范式:新的状态字段如何最小化常规开销、新操作码如何复用既有安全机制、gas 定价如何锚定既有操作码——这些方法论至今仍值得 EVM 设计者与客户端实现者参考。

总结

EIP-3322 提出了一套完整、自洽的账户级 gas 存储方案:以每个合约账户一个无符号计数器为状态基础,以SELFGAS(读取,5 gas)、USEGAS(取款,5000 gas,受余额钳制)、STOREGAS(存款,5000 + amount gas)为操作接口,通过复用执行上下文退款计数器及其 50%(后为 20%)钳制,在不引入全新安全机制的前提下实现了受控的 gas 弹性供给。其测试用例表给出了可直接验证的字节码与预期值,文档中的定价锚点(SELFBALANCE、SSTORE)与编号策略(0x4X 区间)也都能在 EIP-1884 与 EIP-2200 中找到明确的出处。尽管该提案最终未获采纳,它仍是研究以太坊退款机制治理历史与 EVM 操作码设计原则的重要文献。

版权说明:本提案内容遵循 CC0 协议放弃版权,参见 LICENSE.md。

【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 13:12:52

OpenGL性能优化:用PBO异步回读彻底解决glReadPixels卡顿

做了几年 OpenGL 开发之后,你会发现很多性能问题到最后都不在“算得多快”上,而卡在“数据怎么出来”这一步。屏幕上的画面是 GPU 渲染出来的,但如果你要把这帧画面读回 CPU 端做分析、录屏、编码,或者给后续的计算机视觉算法用&a…

作者头像 李华
网站建设 2026/9/15 13:11:52

H5获取GPS坐标实战:坐标系转换与微信定位兼容方案

做 H5 获取手机 GPS 坐标这件事,表面上看就是一行写死的navigator.geolocation.getCurrentPosition(),真正落地才会发现里面全是细节:什么样的浏览器能调通、什么样的场景拿不到权限、安卓和苹果的差异化表现、微信内置浏览器和老版本系统不按…

作者头像 李华
网站建设 2026/9/15 13:09:28

Halcon局部阈值分割dyn_threshold:原理、参数调试与缺陷检测实战

1. 从一次失败的分割说起三年前我接过一个光伏板表面缺陷检测的小项目,甲方要求找出电池片上指甲盖大小的隐裂。第一批图用threshold跑下来,效果惨不忍睹——光照从图片左边到右边有一个明显的渐变,固定阈值把左边一半硅片全切成了“缺陷”&a…

作者头像 李华
网站建设 2026/9/15 13:07:11

MCP Toolbox 中 singlestore-sql 工具实战:从参数化查询到向量检索

MCP Toolbox 中 singlestore-sql 工具实战:从参数化查询到向量检索 【免费下载链接】mcp-toolbox MCP Toolbox for Databases is an open source MCP server for databases. 项目地址: https://gitcode.com/GitHub_Trending/ge/mcp-toolbox 本指南以 MCP Too…

作者头像 李华