EIP-8125 临时合约存储(Temporary Contract Storage)全解析:TMPLOAD/TMPSTORE 操作码与双账户周期滚动机制
EIP-8125 临时合约存储Temporary Contract Storage全解析TMPLOAD/TMPSTORE 操作码与双账户周期滚动机制【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs本文以 Ethereum Improvement Proposal 仓库EIPs中处于 Draft 状态的 EIP-8125《Temporary Contract Storage》 为骨架系统拆解其提出的有界寿命合约存储方案新增TMPSTORE(key, value)/TMPLOAD(key)两个操作码利用两个保留系统账户实现跨交易、跨区块持久但按固定周期自动清空的键值存储。读完本文你将掌握 EIP-8125 的完整规范语义数据模型、周期滚动公式、操作码读写规则、燃气定价约束、其与 EIP-1153 瞬态存储 的边界划分以及它如何依托 EIP-2200 净燃气计量 与 EIP-2929 状态访问定价 的既有约定来设计成本模型。概述什么是临时存储EIP-8125 引入了一个全新的、合约可访问的键值存储层——临时存储temporary storage。它的核心特征有三点跨交易、跨区块持久与瞬态存储不同临时存储的值不会在交易结束时被丢弃而是会保留一段时间协议级定时清空数据按协议定义的固定周期自动清除合约无需显式执行删除操作有界生命周期语义为只需在有限时间窗口内有价值的数据提供更安全的替代方案使这一类状态的整体增长保持有界。提案通过两个新操作码对外暴露能力TMPSTORE(key, value)为当前执行合约写入临时存储TMPLOAD(key)读取当前执行合约的临时存储。临时存储面向的是不需要无限期保留的数据。把这类瞬时数据写进永久状态不仅昂贵还会长期占用节点资源磁盘、I/O、状态维护而临时存储为这类用例提供了自动过期的出口是降低链上状态增长的基础构件。动机永久存储正在为一次性数据买单EIP-8125 的动机部分给出了一张关键的数据分布图见下图源自 assets/eip-8125/1.pngFigure 1链上超过 60% 的存储槽只被写入一次、之后从未再被访问。这张图按活动跨度Activity Span统计了以太坊存储槽的分布Zero Activity Span零活动跨度约 8.28 亿个存储槽占比63.3%——被写入后从未再被读取Up to 1 Year不超过 1 年约 4.35 亿个存储槽占比33.3%1 到 2 年约 2950 万个占比 2.3%活动跨度越长存储槽数量急剧衰减超过 8 年的仅 105 个。也就是说96.6% 的存储槽在写入后一年内便不再活跃。这些数据对写入方而言只在短暂的时间窗口内有价值却要作为永久状态被每个全节点永远保存。永久存储成本高昂因为它直接推高节点的长期资源需求而大量应用写入的数据其实只在有界时间窗口内有意义。EIP-8125 提供的解决思路可以概括为三点有界生命周期语义无需合约显式删除协议自动处理过期可预测的清空时机应用层可以依赖确定性的清理计划状态增长缩减构件为瞬时数据类用例提供降低状态增长的积木。规范常量与参数EIP-8125 定义了一组待定的协议参数当前版本均标记为 TBD将在激活前确定| 名称 | 值 | 描述 | |-|:-:|-| |FORK_BLOCK| TBD | 本 EIP 的激活区块号 | |TEMP_STORAGE_PERIOD| TBD | 临时存储保留一个周期所对应的区块数 | |TEMP_STORAGE_SYSTEM_ADDRESS_0| TBD | 用于存放临时存储的第一个保留地址 | |TEMP_STORAGE_SYSTEM_ADDRESS_1| TBD | 用于存放临时存储的第二个保留地址 | |TMPLOAD_GAS| TBD | 临时存储槽热读取warm read的燃气成本 | |COLD_TMPLOAD_COST| TBD | 临时存储冷访问cold access的燃气附加费 | |TMPSTORE_SET_GAS| TBD | 将槽从零置为非零的燃气成本 | |TMPSTORE_RESET_GAS| TBD | 将非零槽改为另一值的燃气成本 |从参数命名可以看出EIP-8125 刻意对齐了既有存储操作码的定价范式TMPLOAD_GAS/COLD_TMPLOAD_COST对应 EIP-2929 中SLOAD的 warm/cold 两级定价WARM_STORAGE_READ_COST 100COLD_SLOAD_COST 2100而TMPSTORE_SET_GAS/TMPSTORE_RESET_GAS对应 EIP-2200 中SSTORE的 set/reset 两级定价SSTORE_SET_GAS 20000SSTORE_RESET_GAS 5000。规范临时存储数据模型临时存储的底层载体是两个保留系统账户TEMP_STORAGE_SYSTEM_ADDRESS_0与TEMP_STORAGE_SYSTEM_ADDRESS_1。这与 EIP-1153 把瞬态存储实现为执行层内存结构、完全不落盘的方案有本质区别——EIP-8125 的临时存储是真实写入状态树、再按周期整体清除的。临时存储的键空间是一个映射(contract_address, key) - value其中contract_address是写入方合约的地址key是合约自定义的槽位键。二者组合后经 keccak256 派生出真正的状态槽键derived_slot_key keccak256(contract_address || key)派生出的derived_slot_key作为槽键写入到上述两个保留系统账户的**常规存储树storage trie**中。换言之从客户端实现的角度看这不过是对两个普通账户的常规SSTORE/SLOAD操作只是槽键被约定为合约地址 键的哈希——这保证了不同合约的临时存储天然隔离互不可见、互不覆盖。规范周期滚动Storage Reset周期划分临时存储按周期period组织每个周期长度为TEMP_STORAGE_PERIOD个区块自激活区块起算。对于block.number FORK_BLOCK的区块period_index(block_number) floor((block_number - FORK_BLOCK) / TEMP_STORAGE_PERIOD)当前/上一系统账户的确定对于区块B满足B.number FORK_BLOCK定义其当前与上一周期的系统账户current_index period_index(B.number) % 2 if current_index 0: CURRENT_SYSADDR TEMP_STORAGE_SYSTEM_ADDRESS_0 PREVIOUS_SYSADDR TEMP_STORAGE_SYSTEM_ADDRESS_1 else if current_index 1: CURRENT_SYSADDR TEMP_STORAGE_SYSTEM_ADDRESS_1 PREVIOUS_SYSADDR TEMP_STORAGE_SYSTEM_ADDRESS_0两个保留地址按周期奇偶交替扮演当前账户与上一周期账户每两个周期轮换一次角色。周期边界上的清空动作在每个新周期的首个区块协议必须清空即将成为当前账户的那个系统账户的存储。形式化表述为处理区块B父区块为P时若B与P均 FORK_BLOCK且period_index(B.number) period_index(P.number)则在执行B中的交易之前协议必须执行CURRENT_SYSADDR.storageRoot EMPTY_TRIE_ROOT其中CURRENT_SYSADDR按上文公式由B.number计算得出。该操作只修改这一个账户其余账户不受滚动影响。滚动效果在紧邻的上一周期写入的条目会通过PREVIOUS_SYSADDR继续可读一个额外的周期超过一个周期的旧条目被彻底移除。也就是说一笔数据从写入到彻底消失至少保留一个完整周期、至多保留两个周期。规范新操作码TMPLOAD与TMPSTORETMPLOAD(key)读取流程先由(contract_address, key)计算derived_slot_key优先从CURRENT_SYSADDR读取若该槽不存在即value 0则回退读取PREVIOUS_SYSADDR。这一当前优先、上一周期兜底的读取路径正是双账户滚动机制能在滚动边界处保持数据连续性的关键。TMPSTORE(key, value)写入流程同样先计算derived_slot_key然后若value ! 0写入CURRENT_SYSADDR的存储若value 0同时删除CURRENT_SYSADDR与PREVIOUS_SYSADDR中的该槽。为什么清空要同时作用于两个账户因为读取会从当前回退到上一周期。如果只清空当前存储上一周期遗留的旧值就会被回退路径读到导致删除无效。同时清除两个账户后TMPSTORE(key, 0)就能保证后续TMPLOAD(key)返回0直到有新的非零值写入语义与永久存储的SSTORE(key, 0)保持一致。调用与回滚规则调用规则遵循SSTORE与SLOAD的既有约定包括STATICCALL上下文中禁止写入等约束与 EIP-1153 中TSTORE在STATICCALL中异常、TLOAD允许的规则同构。燃气规则燃气规则遵循 EIP-2929 与 EIP-2200 中参照SSTORE/SLOAD的惯例但有以下关键差异成本显著更低低于SSTORE/SLOAD但高于瞬态存储操作码文档中写作TSLOAD/TSTORE不提供退款清空临时存储不给任何 gas 退款与 EIP-2200 中SSTORE_CLEARS_SCHEDULE 15000的退款机制形成对比TMPLOAD最多执行 2 次存储读取当前账户一次、必要时上一周期账户一次燃气计价必须覆盖这一行为TMPSTORE中的删除比创建/修改更昂贵因为删除会同时更新两个系统账户的存储根燃气计价必须覆盖这一行为。值得说明的是EIP-2929 的 warm/cold 访问集合机制accessed_storage_keys: Set[Tuple[Address, Bytes32]]为TMPLOAD的冷读溢价 热读折扣提供了现成的范式首次访问某derived_slot_key时收取COLD_TMPLOAD_COST同一交易内再次访问时仅收取TMPLOAD_GAS。规范激活与保留地址处理在FORK_BLOCK时链必须将TEMP_STORAGE_SYSTEM_ADDRESS_0与TEMP_STORAGE_SYSTEM_ADDRESS_1视为保留地址若其中任一地址在状态树中不存在则以以下初始状态创建nonce 1balance 0codeHash EMPTY_CODE_HASHstorageRoot EMPTY_TRIE_ROOT注意这些地址上不部署任何合约代码——它们是纯数据载体仅通过协议层逻辑被读写普通交易无法以合约调用方式与之交互。设计权衡为什么这样设计为什么不直接用瞬态存储EIP-1153EIP-1153瞬态存储TLOAD/TSTORE在每个交易结束时即被丢弃适合单交易内的帧间通信重入锁、ERC-20 临时授权等场景。而 EIP-8125 瞄准的是另一类需求数据必须跨多个交易、多个区块存活但无需无限期保留——例如需要跨区块生效的临时折扣、限时凭证、短期价格信息等。两者生命周期不同面向的用例互补而非竞争。为什么采用双周期滚动双周期滚动提供了更强的可用性保证一条条目被写入后至少保留一整个TEMP_STORAGE_PERIOD下一周期仍可通过上一周期系统账户读到一条条目至多保留两个周期一旦当前系统账户被清空并复用即消失。这对合约开发者是一个非常简洁的心智模型只要写入临时存储它至少能存活TEMP_STORAGE_PERIOD个区块。相比之下如果采用单一全局滚动数据的实际存活期取决于它在周期内的写入时机——靠近周期末尾的写入几乎会立刻被清空这种不确定性对开发者很不友好。为什么使用两个系统账户两个保留系统账户让滚动操作变成常数时间操作每个周期边界上只需把当前周期系统账户的storageRoot置为EMPTY_TRIE_ROOT即完成清空上一周期系统账户原封不动继续服务一个周期超过一个周期的存储变为不可达可被客户端安全地修剪prune。单次storageRoot EMPTY_TRIE_ROOT的赋值即可完成整批数据的逻辑删除避免了逐槽扫描删除的 O(n) 开销这是该设计最直接的工程收益。向后兼容性EIP-8125 的实施需要一次硬分叉涉及新的操作码、保留地址与状态树语义。但它不改变任何既有操作码的行为因此对所有既有合约账户完全向后兼容——存量合约无需迁移新能力仅通过新增操作码对外暴露。安全考量EIP 在安全方面明确了三点约束DoS 面临时存储的写入仍然会产生真实状态且至少需要被处理和保存 1 个周期。燃气成本必须经过校准确保最坏情况下的临时写入不会在每区块磁盘 I/O维度上制造超出既有存储写入的新 DoS 攻击向量——这正是TMPSTORE_SET_GAS/TMPSTORE_RESET_GAS/COLD_TMPLOAD_COST等参数必须精确定价的原因。重组安全性客户端应保留足够的历史数据以应对合理的重组深度reorg depth——因为临时存储数据一旦被周期滚动清除就无法从历史区块重新推导。应用层安全合约必须将临时存储视为易失数据并正确处理条目意外缺失的情况——即读取时应假定可能读到 0应用逻辑不能依赖临时存储中一定存在某条数据。在仓库中的定位与关联 EIP在本仓库中EIP-8125 的 frontmatter 声明其依赖requires两个已定稿的 EIPEIPS/eip-2929.md状态访问操作码的冷/热两级定价COLD_SLOAD_COST 2100、WARM_STORAGE_READ_COST 100为TMPLOAD的冷热计价与访问集合机制提供基准EIPS/eip-2200.mdSSTORE净燃气计量SSTORE_SET_GAS 20000、SSTORE_RESET_GAS 5000、SSTORE_CLEARS_SCHEDULE 15000为TMPSTORE的 set/reset 计价提供参照但 EIP-8125 明确取消了退款路径。而作为设计对照EIPS/eip-1153.mdFinal 状态提供了交易级易失的瞬态存储方案EIP-8125 站在区块级易失、双账户周期滚动的位置上补齐了永久存储与瞬态存储之间的中间档位。总结EIP-8125 为以太坊引入了一档全新的存储语义跨交易/跨区块持久、按固定周期自动回收、有界增长。其工程实现高度务实——不引入新的状态树类型而是用两个保留系统账户的常规存储树承载数据通过keccak256(contract_address || key)派生槽键保证合约间隔离再以周期边界上的单次storageRoot EMPTY_TRIE_ROOT实现常数时间滚动清空。结合链上超过 60% 存储槽只写一次的观测数据该提案为瞬时价值数据提供了比永久存储更安全、比瞬态存储更持久的第三条路径。当前该 EIP 仍处于 Draft 状态FORK_BLOCK、TEMP_STORAGE_PERIOD、保留地址及全部燃气参数均待定后续实现与测试用例仍有待补充。说明本文内容以仓库内 EIPS/eip-8125.md 为准文中涉及的关联文档与数据图表均可在本仓库对应路径查阅相关内容版权遵循 LICENSE.md 的 CC0 声明。【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考