EIP-1884: 重定价依赖于trie大小的操作码
| Authors | Martin Holst Swende (@holiman) |
|---|---|
| Created | 2019-03-28 |
| Requires | EIP-150, EIP-1052 |
简单总结
本 EIP 提议对某些操作码进行重定价,以在 gas 消耗和资源消耗之间取得良好的平衡。
摘要
以太坊状态的增长导致某些操作码在当前比以前消耗更多的资源。本 EIP 提议提高这些操作码的 gasCost。
动机
操作的价格与资源消耗(CPU 时间、内存等)之间的不平衡有几个缺点:
- 它可能被用于攻击,通过用低估的操作填充区块,导致过度的区块处理时间。
- 低估的操作码会导致区块 gas 限制倾斜,有时区块完成速度很快,但其他 gas 使用量相似的区块完成速度却很慢。
如果操作是平衡的,我们可以最大化区块 gas 限制并获得更稳定的处理时间。
规范
在区块 N,
SLOAD(0x54) 操作从200gas 修改为800gas,BALANCE(0x31) 操作从400gas 修改为700gas,EXTCODEHASH(0x3F) 操作从400gas 修改为700gas,- 在
0x47引入一个新的操作码SELFBALANCE。SELFBALANCE从堆栈中弹出0个参数,SELFBALANCE将当前地址的balance推送到堆栈中,SELFBALANCE的价格为GasFastStep,即5gas。
原理
以下是两张图表,取自使用 Geth 进行的完整同步。测量了每个操作码的执行时间,并汇总了 1 万个区块的数据。这些条形图显示了 5M 到 6M 和 6M 到 7M 范围内前 25 个“重”操作码:

注意:还可以看到 SLOAD 正在向顶部位置移动。我认为 GASPRICE (0x3a) 操作码位于第一位,可以在客户端内部进行优化,而 SLOAD/BALANCE 则不能。
这是另一张图表,显示了使用 Geth 进行的完整同步。它表示区块 0 到 5.7M,并突出显示了区块处理时间花费在哪里。

可以看出,storage_reads 和 account_reads 是导致区块处理时间的最重要因素。
SLOAD
SLOAD 在 EIP-150 中被重定价,从 50 到 200。
以下图表显示了 go-ethereum 的完整同步,其中每个数据点代表
1 万个区块。在这 1 万个区块期间,汇总了操作码的执行时间。

可以看出,在 EIP-150 处进行重定价导致了急剧下降,从大约 67 降至 23。
在区块 5M 附近,它开始达到 EIP-150 之前的水平,而在区块 7M
它平均约为 150 - 是 eip-150 之前水平的两倍多。
将 SLOAD 的成本提高 4 会将其降至 40 左右。
预计将来它将再次上升,并且可能需要将来的重定价,除非
在此之前实施状态清除工作。
BALANCE
BALANCE(也称为 EXTBALANCE)是一种从状态 trie 中获取数据的操作。它在 EIP-150 中被重定价,从 20 到 400。

它与 EXTCODESIZE 和 EXTCODEHASH 相当,后者的价格已经为 700。
它具有内置的高方差,因为它通常用于检查 this 的余额,
这是一种固有廉价的操作,但是,它可以用于查找任意帐户的余额,这通常需要访问 trie(磁盘)。
事后看来,最好有两个
操作码:EXTBALANCE(address) 和 SELFBALANCE,并且有两个不同的价格。
- 本 EIP 提议扩展当前的操作码集。
- 不幸的是,操作码范围
0x3X已经满了,因此建议将SELFBALANCE放在0x4X范围内。 - 至于为什么它的价格为
5(GasFastStep) 而不是2(GasQuickStep),与其他类似操作一样:EVM 执行引擎仍然需要查找(缓存的)trie,并且balance与gasPrice或timeStamp不同,在执行期间不是恒定的,因此它具有更多的固有开销。
- 不幸的是,操作码范围
EXTCODEHASH
EXTCODEHASH 是在 Constantinople 中引入的,带有 EIP-1052。它的定价为 400,理由如下:
gas 成本与
BALANCE操作码的 gas 成本相同,因为EXTCODEHASH的执行需要与BALANCE相同的帐户查找。
因此,如果我们增加 BALANCE,我们也应该增加 EXTCODEHASH
向后兼容性
这些更改需要硬分叉。这些更改具有以下后果:
- 某些调用将变得更加昂贵。
- 访问存储的默认函数,在某些情况下可能需要超过
2300gas(调用中始终可用的最小 gas)。 - 假设调用(或内部部分)具有特定固定 gas 成本的合约可能会停止运行。
- 在 ERC-165 中指定了固定的 gas 成本,并且此接口的实现确实使用了受影响的操作码。
- ERC-165 方法
supportsInterface必须返回一个bool,并且最多使用30,000gas。 - 在撰写本文时,EIP 中的两个示例实现是
- 任何输入都需要
586gas,并且 236gas,但随着支持的接口数量的增加而线性增加
- 任何输入都需要
- ERC-165 方法
- 任何 ERC-165
supportsInterface实现都不太可能超过30.000gas。这将需要使用第二个变体,并且支持三十多个接口。 - 但是,这些操作已经更早地被重定价,因此有一个历史先例,即“这些操作的 gas 成本可能会更改”,这应该可以防止实现这种固定 gas 成本的假设。
- 在 ERC-165 中指定了固定的 gas 成本,并且此接口的实现确实使用了受影响的操作码。
我预计某些模式的使用会减少,例如,将 SLOAD 相同操作码的多个修饰符合并为一个。它还可能导致包含并非绝对必要的 SLOAD 值的 log 操作减少。
测试用例
应实现的测试用例:
- 测试
selfbalance == balance(address), - 测试
balance(this)的成本与之前相同, - 测试
selfbalance不从堆栈中弹出 SLOAD、EXTCODEHASH和SELFBALANCE的 gas 成本验证- 验证
SELFBALANCE在 Istanbul 之前无效
一些测试用例已在 https://github.com/holiman/IstanbulTests/tree/master/GeneralStateTests 中作为状态测试实现
实现
此 EIP 尚未在任何客户端中实现。 这两个操作码之前都已重定价,并且用于管理重定价的客户端内部结构已就绪。
SELFBALANCE
这是 go-ethereum 中新操作码的实现:
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
stack.push(interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(contract.Address())
return nil, nil
}
安全考虑
- 请参阅向后兼容性部分。
- 如果我们将
SELFBALANCE定义为具有address而不是从堆栈中弹出一个地址的BALANCE,则没有关于SELFBALANCE的特殊边缘情况 - 因为BALANCE已经被很好地定义。 - 应该调查 Solidity 是否包含对这些操作的 gas 成本的任何硬编码的期望。
- 在许多情况下,来自
CALL的ether的接收者将想要发出一个LOG。LOG操作的成本为375加上每个主题375。如果LOG还想执行SLOAD,则此更改可能会导致某些此类传输失败。
版权
版权及相关权利通过 CC0 放弃。
Citation
Please cite this document as:
Martin Holst Swende (@holiman), "EIP-1884: 重定价依赖于trie大小的操作码," Ethereum Improvement Proposals, no. 1884, March 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1884.