EIP-1884 Rebalancing gas costs for state size dependent instructions (Repricing for trie-size-dependent opcodes)
Final · Fees & gas · EIP status Final · created 2019-03-28
Raises the gas cost of the state reading instructions SLOAD, BALANCE, and EXTCODEHASH, and adds a new instruction, SELFBALANCE, that reads the caller's own balance for 5 gas.
Why it is needed
As Ethereum's state has grown, some state reading instructions now consume far more resources (CPU time, memory, and so on) in practice than their priced cost reflects. When price and resource consumption diverge, two problems arise. First, an attacker can fill a block with underpriced instructions to excessively inflate block processing time. Second, blocks that use the same amount of gas end up finishing at very different speeds, making processing time inconsistent. In fact, measurements taken while fully syncing blocks with Geth showed that storage reads (storage_reads) and account reads (account_reads) accounted for the largest share of block processing time. When instruction prices are balanced with resource consumption, block processing time can stay stable even while using the full block gas limit.
What changes
Today
- SLOAD (storage read) costs 200 gas
- BALANCE (balance lookup) and EXTCODEHASH (code hash lookup) each cost 400 gas
- A contract also pays the same 400 gas using BALANCE even when checking its own balance
- These are priced below their actual processing cost, making block processing time unstable and leaving room for them to be exploited in attacks
After EIP-1884
- SLOAD rises to 800 gas
- BALANCE and EXTCODEHASH each rise to 700 gas
- The new instruction SELFBALANCE (0x47) lets a contract read its own balance for 5 gas
- Instruction prices become balanced with resource consumption, making block processing time more stable
Who is affected
- Contract developer: Calls that read state become more expensive. Patterns such as repeatedly SLOADing the same value or logging unnecessary values may shift toward being reduced.
- Existing deployed contracts: Contracts that assume fixed gas costs, especially code that reads storage while receiving ether within the 2,300 gas stipend, may stop working.
- Client teams: Prices must be changed via a hard fork. Both instructions have been rebalanced before, so the internal machinery for handling price changes is already in place.
- Users: Gas usage increases for transactions that do a lot of state reading.
In depth
Measurement basis: SLOAD execution time rose again during a Geth full sync
Execution time for each opcode was aggregated in 10K block increments during a Geth full sync. In the 5 to 7 million block range, SLOAD climbed into the top ranks of heavy opcodes, and storage_reads and account_reads were the two largest contributors to overall block processing time. SLOAD's cost had been raised from 50 to 200 in EIP-150, dropping its execution time metric from about 67 to about 23, but it returned to pre-EIP-150 levels around block 5 million and by block 7 million averaged about 150, more than double its pre-increase level. Raising the cost fourfold (from 200 to 800) is expected to bring this down to around 40, but the source notes that without state clearing it could rise again, requiring further rebalancing. GASPRICE (0x3a) also ranks high, but unlike SLOAD and BALANCE, it can be eliminated through client side internal optimization.
BALANCE and EXTCODEHASH are aligned at 700 gas
BALANCE is an operation that fetches data from the state trie, and was raised from 20 to 400 gas in EIP-150. Among the account lookup instructions EXTCODESIZE and EXTCODEHASH, EXTCODESIZE is already 700, so BALANCE is raised to match at 700. EXTCODEHASH was priced at 400 when it was introduced in Constantinople via EIP-1052, on the grounds that it 'requires an account lookup like BALANCE', so raising BALANCE requires raising EXTCODEHASH together with it. Meanwhile, BALANCE mixes inherently cheap uses, such as looking up one's own (this) balance, with uses that require trie (disk) access, such as looking up an arbitrary account, resulting in a large spread in cost. The source notes that, in hindsight, it would have been better to split this into two opcodes with different prices: EXTBALANCE(address) and SELFBALANCE.
Specification of the new instruction SELFBALANCE (0x47)
SELFBALANCE pops 0 arguments from the stack and pushes the balance of the current address onto the stack. Its price is GasFastStep, 5 gas. The reason it costs 5 gas rather than the GasQuickStep (2 gas) used by similar lookup instructions is that the EVM execution engine still needs to perform one lookup against the cached trie, and balance, unlike gasPrice or timeStamp, can change during execution, adding its own extra overhead. The opcode number falls in the 0x4X range because the 0x3X range, where balance lookup type opcodes are clustered, is already full. Defining SELFBALANCE as 'BALANCE that uses its own address instead of popping an address from the stack' introduces no special edge cases, since BALANCE is already well defined.
Backwards compatibility: requires a hard fork and may break some contracts
This change requires a hard fork and has three consequences. First, certain calls become more expensive. Second, a default receive function that accesses storage within the 2,300 gas minimum always guaranteed on a call may run out of gas. Third, contracts that assume a fixed gas cost for calls or internal sections may stop working. ERC-165's supportsInterface returns a bool and is capped at a fixed 30,000 gas, and uses the affected opcodes, but the two example implementations at the time of writing used 586 gas (independent of input) and 236 gas (increasing proportionally with the number of supported interfaces) respectively, so exceeding the cap under the second approach would require supporting around thirty interfaces, making it unlikely. It is also noted that these operations have already been rebalanced before, so assuming a fixed gas cost for them should have been avoided in the first place.
Security considerations: the pattern of logging after receiving ether can fail
A recipient of ether via CALL commonly tries to record a LOG. LOG costs 375 gas plus 375 gas per topic. Combined with an SLOAD (800 gas), this can exceed 2,300 gas, causing some ether transfers to fail. The source also notes that it should be investigated whether Solidity has any hardcoded expectations about the gas costs of these operations. In terms of development patterns, the source's author expects a shift toward merging multiple modifiers that SLOAD the same value into one, or toward not logging SLOAD values that are not strictly necessary.
Status and tests: Final, and SELFBALANCE is invalid before Istanbul
This EIP's status is Final. The original text requires five tests: whether selfbalance returns the same value as balance(address), whether the cost of balance(this) is charged the same way as before, whether selfbalance does not pop a value off the stack, whether the gas costs of SLOAD, EXTCODEHASH, and SELFBALANCE match the specification, and whether SELFBALANCE is an invalid instruction on blocks before Istanbul. Some tests are implemented as state tests at github.com/holiman/IstanbulTests. The original text also includes example implementation code for SELFBALANCE in go-ethereum.