mirror of
https://github.com/status-im/EIPs.git
synced 2025-02-23 12:18:16 +00:00
Fixes to EIP 2929 formatting
Thanks to @lightclient and @MicahZoltu for all the suggestions/fixes!
This commit is contained in:
parent
51a1a713f0
commit
12e25bb307
@ -11,21 +11,23 @@ created: 2020-09-01
|
||||
|
||||
## Simple Summary
|
||||
|
||||
Increase the gas cost of SLOAD to 2100, and the CALL opcode family, BALANCE and the EXT* opcode family to 2600. Exempts (i) precompiles, and (ii) addresses and storage slots that have already been accessed in the same transaction. Additionally reforms SSTORE metering and SELFDESTRUCT to ensure "de-facto storage loads" inherent in those opcodes are priced correctly.
|
||||
Increases gas cost for `SLOAD`, `*CALL`, `BALANCE`, `EXT*` and `SELFEDESTRUCT` when used for the first time in a transaction.
|
||||
|
||||
This is done as a short-term security improvement to reduce the effectiveness of what is currently the most effective DoS strategy, reducing the theoretical max processing time of a block by ~3x, and also has the effect of being a stepping stone toward [bounding stateless witness sizes](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885).
|
||||
## Abstract
|
||||
|
||||
Increase the gas cost of `SLOAD` (`0x54`) to 2100, and the `*CALL` opcode family (`0xf1`, `f2`, `f4`, `fA`), `BALANCE` `0x31` and the `EXT*` opcode family (`0x3b`, `0x3c`, `0x3f`) to 2600. Exempts (i) precompiles, and (ii) addresses and storage slots that have already been accessed in the same transaction. Additionally reforms `SSTORE` metering and `SELFDESTRUCT` to ensure "de-facto storage loads" inherent in those opcodes are priced correctly.
|
||||
|
||||
## Motivation
|
||||
|
||||
Generally, the main function of gas costs of opcodes is to be an estimate of the time needed to process that opcode, the goal being for the gas limit to correspond to a limit on the time needed to process a block. However, storage-accessing opcodes (SLOAD, as well as the CALL, BALANCE and EXT* opcodes) have historically been underpriced. In the 2016 Shanghai DoS attacks, once the most serious client bugs were fixed, one of the more durably successful strategies used by the attacker was to simply send transactions that access or call a large number of accounts.
|
||||
Generally, the main function of gas costs of opcodes is to be an estimate of the time needed to process that opcode, the goal being for the gas limit to correspond to a limit on the time needed to process a block. However, storage-accessing opcodes (`SLOAD`, as well as the `*CALL`, `BALANCE` and `EXT*` opcodes) have historically been underpriced. In the 2016 Shanghai DoS attacks, once the most serious client bugs were fixed, one of the more durably successful strategies used by the attacker was to simply send transactions that access or call a large number of accounts.
|
||||
|
||||
Gas costs were increased to mitigate this, but recent numbers suggest they were not increased enough. Quoting [https://arxiv.org/pdf/1909.07220.pdf](https://arxiv.org/pdf/1909.07220.pdf):
|
||||
|
||||
> Although by itself, this issue might seem benign, EXTCODESIZE forces the client to search the contract ondisk, resulting in IO heavy transactions. While replaying the Ethereum history on our hardware, the malicious transactions took around 20 to 80 seconds to execute, compared to a few milliseconds for the average transactions
|
||||
> Although by itself, this issue might seem benign, `EXTCODESIZE` forces the client to search the contract ondisk, resulting in IO heavy transactions. While replaying the Ethereum history on our hardware, the malicious transactions took around 20 to 80 seconds to execute, compared to a few milliseconds for the average transactions
|
||||
|
||||
This proposed EIP increases the costs of these opcodes by a factor of ~3, reducing the worst-case processing time to ~7-27 seconds. Improvements in database layout that involve redesigning the client to read storage directly instead of hopping through the Merkle tree would decrease this further, though these technologies may take a long time to fully roll out, and even with such technologies the IO overhead of accessing storage would remain substantial.
|
||||
|
||||
A secondary benefit of this EIP is that it also performs most of the work needed to make [stateless witness sizes](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885) in Ethereum acceptable. Assuming [a switch to binary tries](https://ethresear.ch/t/binary-trie-format/7621), the theoretical maximum witness size not including code size (hence "most of the work" and not "all") would decrease from `(12500000 gas limit) / (700 gas per BALANCE) * (800 witness bytes per BALANCE) ~= 14.3M bytes` to `12500000 / 2600 * 800 ~= 3.85M bytes`. Pricing for code access could be changed when code Merklization is implemented.
|
||||
A secondary benefit of this EIP is that it also performs most of the work needed to make [stateless witness sizes](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885) in Ethereum acceptable. Assuming [a switch to binary tries](https://ethresear.ch/t/binary-trie-format/7621), the theoretical maximum witness size not including code size (hence "most of the work" and not "all") would decrease from `(12500000 gas limit) / (700 gas per BALANCE) * (800 witness bytes per BALANCE) ~= 14.3M bytes` to `12500000 / 2600 * 800 ~= 3.85M bytes`. Pricing for code access could be changed when code merklization is implemented.
|
||||
|
||||
In the further future, there are similar benefits in the case of SNARK/STARK witnesses. Recent numbers from Starkware suggest that they are able to prove 10000 Rescue hashes per second on a consumer desktop; assuming 25 hashes per Merkle branch, and a block full of state accesses, at present this would imply a witness would take `12500000 / 700 * 25 / 10000 ~= 44.64` seconds to generate, but after this EIP that would reduce to `12500000 / 2500 * 25 / 10000 ~= 12.5` seconds, meaning that a single desktop computer would be able to generate witnesses on time under any conditions. Future gains in STARK proving could be spent on either (i) using a more expensive but robust hash function or (ii) reducing proving times further, reducing the delay and hence improving user experience of stateless clients that rely on such witnesses.
|
||||
|
||||
@ -46,7 +48,7 @@ For blocks where `block.number >= FORK_BLOCK`, the following changes apply.
|
||||
|
||||
When executing a transaction, maintain a set `accessed_addresses: Set[Address]` and `accessed_storage_keys: Set[Tuple[Address, Bytes32]]` (this is a transaction-context-wide set, implemented identically to self-destructs, in particular reverting similarly to how self-destructs revert). When a transaction execution begins, `accessed_storage_keys` is initialized to empty, and `accessed_addresses` is initialized to include the `tx.sender`, `tx.to` (or the address being created if it is a contract creation transaction) and the set of all precompiles.
|
||||
|
||||
When an address is either the target of a (`EXTCODESIZE`, `EXTCODECOPY`, `EXTCODEHASH` or `BALANCE`) opcode or the target of a (`CALL`, `CALLCODE`, `STATICCALL`, `DELEGATECALL`) opcode, the gas costs are computed as follows:
|
||||
When an address is either the target of a (`EXTCODESIZE` (`0x3B`), `EXTCODECOPY` (`0x3C`), `EXTCODEHASH` (`0x3F`) or `BALANCE` (`0x31`)) opcode or the target of a (`CALL` (`0xF1`), `CALLCODE` (`0xF2`), `DELEGATECALL` (`0xF4`), `STATICCALL` (`0xFA`)) opcode, the gas costs are computed as follows:
|
||||
|
||||
* If the target is not in `accessed_addresses`, charge `COLD_ACCOUNT_ACCESS_COST` gas, and add the address to `accessed_addresses`.
|
||||
* Otherwise, charge `WARM_STORAGE_READ_COST` gas.
|
||||
@ -70,22 +72,6 @@ The other parameters defined in EIP 2200 are unchanged.
|
||||
|
||||
If the ETH recipient of a `SELFDESTRUCT` is not in `accessed_addresses` (regardless of whether or not the amount sent is nonzero), charge an additional `COLD_ACCOUNT_ACCESS_COST` on top of the existing gas costs, and add the ETH recipient to the set.
|
||||
|
||||
### Contract breakage mitigations
|
||||
|
||||
See the Security Considerations section for details on the tradeoffs between these versions.
|
||||
|
||||
#### Version 1
|
||||
|
||||
Do nothing (ie. implement only the changes described above).
|
||||
|
||||
#### Version 2
|
||||
|
||||
Add a `POKE` precompile, with cost 4500 gas, that takes two stack arguments, `address` and `storage_slot`, and adds `address` to `accessed_addresses` and `(address, storage_slot)` to `accessed_storage_keys`.
|
||||
|
||||
#### Version 3
|
||||
|
||||
Access lists: see [EIP 2930](https://github.com/ethereum/EIPs/pull/2930)
|
||||
|
||||
## Rationale
|
||||
|
||||
### Opcode costs vs charging per byte of witness data
|
||||
@ -136,8 +122,8 @@ Ideally we would test the following:
|
||||
* Sub-call, SLOAD, sub-call again, revert the inner sub-call, SLOAD the same storage slot
|
||||
* SSTORE the same storage slot {1, 2, 3} times, using all combinations of zero/nonzero for original value and the value being set
|
||||
* SSTORE then SLOAD the same storage slot
|
||||
* `OP_1` then `OP_2` to the same address where `OP_1` and `OP_2` are all combinations of (\*CALL, EXT\*, SELFDESTRUCT)
|
||||
* Try to CALL an address but with all possible failure modes (not enough gas, not enough ETH...), then (CALL | EXT*) that address again successfully
|
||||
* `OP_1` then `OP_2` to the same address where `OP_1` and `OP_2` are all combinations of (`*CALL`, `EXT*`, `SELFDESTRUCT`)
|
||||
* Try to `CALL` an address but with all possible failure modes (not enough gas, not enough ETH...), then (`CALL` | `EXT*`) that address again successfully
|
||||
|
||||
## Implementation
|
||||
|
||||
@ -151,15 +137,17 @@ As with any gas cost increasing EIP, there are three possible cases where it cou
|
||||
2. Applications relying on contract calls that consume close to the full gas limit
|
||||
3. The 2300 base limit given to the callee by ETH-transferring calls
|
||||
|
||||
These risks have been studied before in the context of an earlier gas cost increase, EIP 1884. See [Martin Swende's earlier report](https://github.com/holiman/eip-1884-security) and [Hubert Ritzdorf's analysis](https://gist.github.com/ritzdorf/1c6bd72955391e831f8a397d3152b4e0/) focusing on (1) and (3). (2) has received less analysis, though one can argue that it is very unlikely both because applications tend to very rarely use close to the entire gas limit in a transaction, and because gas limits were very recently raised from 10 million to 12.5 million. EIP 1884 in practice [did lead to a small number of contracts breaking](https://www.coindesk.com/ethereums-istanbul-upgrade-will-break-680-smart-contracts-on-aragon) for this reason.
|
||||
These risks have been studied before in the context of an earlier gas cost increase, EIP-1884. See [Martin Swende's earlier report](https://github.com/holiman/eip-1884-security) and [Hubert Ritzdorf's analysis](https://gist.github.com/ritzdorf/1c6bd72955391e831f8a397d3152b4e0/) focusing on (1) and (3). (2) has received less analysis, though one can argue that it is very unlikely both because applications tend to very rarely use close to the entire gas limit in a transaction, and because gas limits were very recently raised from 10 million to 12.5 million. EIP-1884 in practice [did lead to a small number of contracts breaking](https://www.coindesk.com/ethereums-istanbul-upgrade-will-break-680-smart-contracts-on-aragon) for this reason.
|
||||
|
||||
There are two ways to look at these risks. First, we can note that as of today developers have had years of warning; gas cost increases on storage-accessing opcodes have been [discussed for a long time](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885), with multiple statements made including to major dapp developers around the likelihood of such changes. EIP 1884 itself provided an important wake-up call. Hence, we can argue that risks this time will be significantly lower than EIP 1884.
|
||||
There are two ways to look at these risks. First, we can note that as of today developers have had years of warning; gas cost increases on storage-accessing opcodes have been [discussed for a long time](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885), with multiple statements made including to major dapp developers around the likelihood of such changes. EIP-1884 itself provided an important wake-up call. Hence, we can argue that risks this time will be significantly lower than EIP-1884.
|
||||
|
||||
A second way to look at the risks is to explore mitigations. First of all, the existence of an `accessed_addresses` and `accessed_storage_keys` map (present in this EIP, absent in EIP 1884) already makes some cases recoverable: in any case where a contract A needs to send funds to some address B, where that address accepts funds from any source but leaves a storage-dependent log, one can recover by first sending a separate call to B to pull it into the cache, and then call A, knowing that the execution of B triggered by A will only charge 100 gas per SLOAD. This fact does not fix all situations, but it does reduce risks significantly.
|
||||
### Contract breakage mitigations
|
||||
|
||||
Options (2) and (3) in "contract breakage mitigations" in the EIP attempt to further expand the usability of this pattern. Option (2), the `POKE` precompile, allows transactions that attempt to "rescue" stuck contracts by pre-poking all of the storage slots that they will access. This works even if the address only accepts transactions from the contract, and works in many other contexts with present gas limits. The only case where this will not work would be the case where a transaction call _must_ go from an EOA straight into a specific contract that then sub-calls another contract.
|
||||
A second way to look at the risks is to explore mitigations. First of all, the existence of an `accessed_addresses` and `accessed_storage_keys` map (present in this EIP, absent in EIP-1884) already makes some cases recoverable: in any case where a contract A needs to send funds to some address B, where that address accepts funds from any source but leaves a storage-dependent log, one can recover by first sending a separate call to B to pull it into the cache, and then call A, knowing that the execution of B triggered by A will only charge 100 gas per SLOAD. This fact does not fix all situations, but it does reduce risks significantly.
|
||||
|
||||
Option 3 (in-transaction access lists) has a similar effect to `POKE` but is more general: it also works for the EOA -> contract -> contract case, and generally should work for all known cases of breakage due to gas cost increases. Option (3) is more complex, though it is arguably a stepping stone toward access lists being used for other use cases (regenesis, account abstraction, SSA all demand access lists).
|
||||
But there are ways to further expand the usability of this pattern. One possibility is to add a `POKE` precompile, which would take an address and a storage key as input and allow transactions that attempt to "rescue" stuck contracts by pre-poking all of the storage slots that they will access. This works even if the address only accepts transactions from the contract, and works in many other contexts with present gas limits. The only case where this will not work would be the case where a transaction call _must_ go from an EOA straight into a specific contract that then sub-calls another contract.
|
||||
|
||||
Another option is [EIP-2930](./EIP-2930.md), which would have a similar effect to `POKE` but is more general: it also works for the EOA -> contract -> contract case, and generally should work for all known cases of breakage due to gas cost increases. This option is more complex, though it is arguably a stepping stone toward access lists being used for other use cases (regenesis, account abstraction, SSA all demand access lists).
|
||||
|
||||
## Copyright
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
Loading…
x
Reference in New Issue
Block a user