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
) 操作从200
gas 修改为800
gas,BALANCE
(0x31
) 操作从400
gas 修改为700
gas,EXTCODEHASH
(0x3F
) 操作从400
gas 修改为700
gas,- 在
0x47
引入一个新的操作码SELFBALANCE
。SELFBALANCE
从堆栈中弹出0
个参数,SELFBALANCE
将当前地址的balance
推送到堆栈中,SELFBALANCE
的价格为GasFastStep
,即5
gas。
原理
以下是两张图表,取自使用 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
向后兼容性
这些更改需要硬分叉。这些更改具有以下后果:
- 某些调用将变得更加昂贵。
- 访问存储的默认函数,在某些情况下可能需要超过
2300
gas(调用中始终可用的最小 gas)。 - 假设调用(或内部部分)具有特定固定 gas 成本的合约可能会停止运行。
- 在 ERC-165 中指定了固定的 gas 成本,并且此接口的实现确实使用了受影响的操作码。
- ERC-165 方法
supportsInterface
必须返回一个bool
,并且最多使用30,000
gas。 - 在撰写本文时,EIP 中的两个示例实现是
- 任何输入都需要
586
gas,并且 236
gas,但随着支持的接口数量的增加而线性增加
- 任何输入都需要
- ERC-165 方法
- 任何 ERC-165
supportsInterface
实现都不太可能超过30.000
gas。这将需要使用第二个变体,并且支持三十多个接口。 - 但是,这些操作已经更早地被重定价,因此有一个历史先例,即“这些操作的 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.