EIP-1087: SSTORE操作的网络 gas 计量
Authors | Nick Johnson (@arachnid) |
---|---|
Created | 2018-05-17 |
Discussion Link | https://ethereum-magicians.org/t/eip-net-storage-gas-metering-for-the-evm/383 |
摘要
本 EIP 提议更改 EVM SSTORE
操作的 gas 收费方式,以减少在不必要的情况下过多的 gas 成本,并为合约存储启用新的用例。
动机
目前,SSTORE
(0x55
) 操作的收费方式如下:
- 将插槽从 0 设置为非 0 时,收取 20,000 gas
- 对于任何其他更改,收取 5,000 gas
- 当插槽从非 0 设置为 0 时,退还 10,000 gas。退款在交易结束时申请。
在单个更新被应用到交易中的存储值的情况下,这些 gas 成本已被确定为公平地反映了操作所消耗的资源。但是,对于执行多次更新的操作序列,这会导致过高的 gas 成本。
以下是一些例子来说明这个问题:
- 如果一个具有空存储的合约将插槽 0 设置为 1,然后再设置回 0,则将被收取
20000 + 5000 - 10000 = 15000
gas,尽管此操作序列不需要任何磁盘写入。 - 一个具有空存储的合约将插槽 0 递增 5 次,将被收取
20000 + 5 * 5000 = 45000
gas,尽管此操作序列所需的磁盘活动并不比单次写入多,而单次写入的收费为 20000 gas。 - 从账户 A 到账户 B 的余额转移,然后从 B 转移到 C,所有账户都具有非零的起始和结束余额,将花费
5000 * 4 = 20000
gas。
解决此问题还将启用当前成本过高的新用例,其中一系列操作不会导致交易结束时存储的净变化。例如,用于防止重入的互斥锁,或在对同一合约的多次调用之间传递的上下文信息。一个这样的例子是 approveAndCall
操作,它允许在单个交易中发送到合约并调用合约,而无需为新的 token 标准更新该合约。
规范
EVM 进行了以下更改:
- 为每个交易维护一个“脏图”,跟踪当前交易中已修改的所有合约中的所有存储插槽。脏图的作用域与存储更新的方式相同,这意味着对稍后恢复的调用中对脏图的更改不会被保留。
- 当使用已包含的值写入存储插槽时,将扣除 200 gas。
- 当第一次更改存储插槽的值时,该插槽将被标记为脏。如果该插槽先前设置为 0,则扣除 20000 gas;否则,扣除 5000 gas。
- 当写入已在脏图中的存储插槽时,将扣除 200 gas。
- 在交易结束时,对于脏图中的每个插槽:
- 如果该插槽在交易前为 0 且现在为 0,则退还 19800 gas。
- 如果该插槽在交易前为非零值且其值未更改,则退还 4800 gas。
- 如果该插槽在交易前为非零值且现在为 0,则退还 15000 gas。
在这些更改之后,仅对存储插槽进行单个更改的交易将保留其现有成本。但是,进行多次更改的合约将看到显着降低的成本。重复动机部分中的示例:
- 如果一个具有空存储的合约将插槽 0 设置为 1,然后再设置回 0,则将被收取
20000 + 200 - 19800 = 400
gas,低于 15000。 - 一个具有空存储的合约将插槽 0 递增 5 次,将被收取
20000 + 5 * 200 = 21000
gas,低于 45000。 - 从账户 A 到账户 B 的余额转移,然后从 B 转移到 C,所有账户都具有非零的起始和结束余额,将花费
5000 * 3 + 200 - 4800 = 10400
gas,低于 20000。
理由
我们认为,所提出的机制代表了在存储 gas 成本未反映节点承担的实际成本的情况下,降低存储 gas 成本的最简单方法。我们考虑并驳回了几种替代设计:
- 对
SSTORE
操作收取 200 gas 的固定费用,并在交易结束时对新的或修改后的值收取额外的 19800 / 4800,这更简单,并且消除了对脏图的需求,但是将 gas 消耗的一个重要来源从 EVM 堆栈中移出,并在交易结束时应用,这可能会使调试复杂化,并降低合约限制被调用者 gas 消耗的能力,以及向 EVM 引入一种新机制。 - 为存储 gas 退款保留单独的退款计数器可以避免退款被限制为消耗 gas 的一半的问题(这里不需要),但是会增加跟踪此值的复杂性。
- 每次将存储插槽设置回其初始值时都退还 gas 会引入一种新机制(即时退款)并使调用其他合约的合约的 gas 计数复杂化;它还允许合约调用具有负执行成本的可能性。
向后兼容性
此 EIP 需要进行硬分叉才能实施。
对于此更改,任何合约都不应看到 gas 成本的增加,并且许多合约将看到 gas 消耗的减少,因此预计不会出现合约层面的向后兼容性问题。
测试用例
- 将 x 写入包含 0 的存储插槽,其中 x != 0(20k gas,无退款)
- 将 y 写入包含 x 的存储插槽,其中 x != y 且 x != 0(5k gas,无退款)
- 将 0 写入包含 x 的存储插槽,其中 x != 0(5k gas,10k 退款)
- 将 0 写入已包含零的存储插槽(200 gas,无退款)
- 将 x 写入已包含 x 的存储插槽,其中 x != 0(200 gas,无退款)
- 将 x,然后将 y 写入包含 0 的存储插槽,其中 x != y(20200 gas,无退款)
- 将 x,然后将 y 写入包含 0 的存储插槽,其中 x != y != z 且 x != 0(5200 gas,无退款)
- 将 x,然后将 0 写入包含 0 的存储插槽,其中 x != 0(20200 gas,19800 退款)
- 将 x,然后将 y 写入包含 y 的存储插槽,其中 x != y != 0(5200 gas,4800 退款)
- 在嵌套帧中,将 x 写入包含 0 的存储插槽,然后恢复发生写入的堆栈帧(20200 gas,无退款)
- 将 x,然后将 y 写入包含 y 的存储插槽,然后恢复发生写入的堆栈帧(5200 gas,无退款)
- 在嵌套帧中,将 x 写入包含 0 的存储插槽,然后返回,并将 0 写入该插槽(20200 gas,19800 退款)
实施
待定
版权
通过 CC0 放弃版权及相关权利。
Citation
Please cite this document as:
Nick Johnson (@arachnid), "EIP-1087: SSTORE操作的网络 gas 计量 [DRAFT]," Ethereum Improvement Proposals, no. 1087, May 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1087.