EIP-7709: 从存储读取 BLOCKHASH 并更新成本
从 EIP-2935 系统合约存储读取 `BLOCKHASH (0x40)` 操作码,并调整其 gas 成本以反映存储访问。
Authors | Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria), Ignacio Hagopian (@jsign), Jochem Brouwer (@jochem-brouwer), Gabriel Rocheleau (@gabrocheleau) |
---|---|
Created | 2024-05-18 |
Discussion Link | https://ethereum-magicians.org/t/eip-7709-read-blockhash-opcode-from-storage-and-adjust-gas-cost/20052 |
Requires | EIP-2935 |
摘要
更新 BLOCKHASH (0x40)
操作码,使其从系统合约存储中读取并提供服务,并收取额外(冷或热)存储成本。
动机
BLOCKHASH (0x40)
操作码目前假定客户端了解之前的区块,这在 Verkle EIP-6800 中会阻止无状态执行。然而,通过 EIP-2935,可以从其系统合约存储中检索和提供 blockhash,这允许 Verkle 区块包含用于无状态执行的存储访问见证。
规范
参数 | 值 |
---|---|
FORK_TIMESTAMP |
待定 |
HISTORY_STORAGE_ADDRESS |
0x0000F90827F1C53a10cb7A02335B175320002935 |
BLOCKHASH_SERVE_WINDOW |
256 |
BLOCKHASH
操作码语义与之前保持不变。从 fork_block
(定义为 fork_block.timestamp >= FORK_TIMESTAMP and fork_block.parent.timestamp < FORK_TIMESTAMP
),应该更新 BLOCKHASH
指令以以下列方式解析区块哈希:
def resolve_blockhash(block: Block, state: State, arg: uint64):
# 注意,在 BLOCKHASH_SERVE_WINDOW 之外,我们继续返回 0
# 尽管 2935 历史合约能够提供更多的哈希值
if arg >= block.number or (arg + BLOCKHASH_SERVE_WINDOW) < block.number
return 0
# 对 arg % HISTORY_SERVE_WINDOW 执行 sload,包括 gas 费用、
# 预热效果以及执行访问
#
# 请注意,`BLOCKHASH_SERVE_WINDOW` 和用于插槽计算的 2935 环形缓冲区窗口
# `HISTORY_SERVE_WINDOW` 是不同的
return state.load_slot(HISTORY_STORAGE_ADDRESS, arg % HISTORY_SERVE_WINDOW)
仅当 arg
在正确的 BLOCKHASH
窗口内时,客户端可以选择
- 直接从状态执行
SLOAD
,或者 - 通过其
get
机制(调用者不是SYSTEM_ADDRESS
)对 EIP-2935 合约执行系统调用,或者 - 从内存提供服务,或者按照当前设计(例如,维护所需历史记录的完整客户端)
但是,如果 arg
在正确的 BLOCKHASH
窗口内,则需要按照当前分叉应用 SLOAD
操作的全部语义和后续影响:
SLOAD
gas 成本(冷或热)用于arg % HISTORY_SERVE_WINDOW
插槽。SLOAD
对插槽的后续影响(预热插槽)- 如果激活了 Verkle(EIP-6800 和 EIP-4762),则将
SLOAD
访问添加到执行见证中
激活
此 EIP 指定过渡到新逻辑,假设 EIP-2935 已被激活:
- 在此 EIP 激活之前足够的时间(>=
BLOCKHASH_SERVE_WINDOW
),或者 - 对于测试网/开发网,此 EIP 也可以在创世块激活
目前的提案是激活带有 Verkle 的此 EIP,以允许区块的无状态执行。
Gas 成本
如上所述,如果要解析的 arg
在正确的窗口内,则应为插槽 arg % HISTORY_SERVE_WINDOW
应用相应的 SLOAD
费用和访问。 请注意,HISTORY_SERVE_WINDOW
和 BLOCKHASH_SERVE_WINDOW
是不同的。
从系统合约读取
即使客户端选择通过系统调用 EIP-2935 合约来解析 BLOCKHASH
,也不会应用系统代码执行的 gas 成本(以及如果 Verkle 激活,则也不会应用代码见证)。 如上所述,仅应用 SLOAD
的效果。
理由
- 更新 gas 成本的原因是为了匹配实际操作,这等效于
SLOAD
。 - 不应用 EIP-2935 系统合约执行费用(和访问),以保持 gas 较低,并使选择以其他方式(直接或通过内存/维护的历史记录)解析
BLOCKHASH
的客户端的工作保持简单
请注意,BLOCKHASH
操作码仅提供有限的 BLOCKHASH_SERVE_WINDOW
以实现向后兼容(并且不扩展上述豁免)。 对于更深入的访问,将需要直接调用 EIP-2935 系统合约,这将导致正常的合约执行(以及费用和访问)
向后兼容性
此 EIP 引入了 BLOCKHASH
成本的显着增加,这可能会破坏依赖于先前 gas 成本的用例。 此外,如果 EIP-2935 分叉和此 EIP 的分叉之间经过的时间少于 BLOCKHASH_SERVE_WINDOW
,则此 EIP 会引入重大更改(除非 EIP-2935 在创世块中激活,例如在测试网/开发网中),因为 EIP-2935 系统合约将没有保存所需的历史记录。
测试用例
- 如果任何交易都没有调用
BLOCKHASH
或没有 EIP-2935 合约调用,则如果 Verkle 激活,则只有父哈希的 EIP-2935 系统更新才会出现在见证中。 - 如果调用了
BLOCKHASH
,则必须对存储槽收取存储访问 gas 费用(如果 Verkle 激活,则必须有相应的访问见证),但前提是BLOCKHASH
查询是针对最后BLOCKHASH_SERVE_WINDOW
祖先的。 这与客户端选择如何解析BLOCKHASH
(直接、通过系统合约或通过内存)无关 - 除了每次查找的
SLOAD
成本(如果执行)之外,还应收取每次BLOCKHASH
操作的 gas 成本 - 如果直接调用 EIP-2935 合约(即不是通过
BLOCKHASH
),则按照当前分叉的正常合约执行应用见证和 gas 成本(包括与合约代码相关的成本)。 - 如果在此 EIP 在 EIP-2935 之后正确激活
>= BLOCKHASH_SERVE_WINDOW
,则应一致地解析BLOCKHASH
安全注意事项
截至目前,除了 EIP-2935 中包含的安全注意事项外,没有确定其他安全注意事项。
版权
通过 CC0 放弃版权和相关权利。
Citation
Please cite this document as:
Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria), Ignacio Hagopian (@jsign), Jochem Brouwer (@jochem-brouwer), Gabriel Rocheleau (@gabrocheleau), "EIP-7709: 从存储读取 BLOCKHASH 并更新成本 [DRAFT]," Ethereum Improvement Proposals, no. 7709, May 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7709.