Alert Source Discuss
⚠️ Draft Standards Track: Core

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-6800EIP-4762),则将 SLOAD 访问添加到执行见证中

激活

此 EIP 指定过渡到新逻辑,假设 EIP-2935 已被激活:

  • 在此 EIP 激活之前足够的时间(>= BLOCKHASH_SERVE_WINDOW),或者
  • 对于测试网/开发网,此 EIP 也可以在创世块激活

目前的提案是激活带有 Verkle 的此 EIP,以允许区块的无状态执行。

Gas 成本

如上所述,如果要解析的 arg 在正确的窗口内,则应为插槽 arg % HISTORY_SERVE_WINDOW 应用相应的 SLOAD 费用和访问。 请注意,HISTORY_SERVE_WINDOWBLOCKHASH_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.