EIP-2929: Gas cost increases for state access opcodes

As discussed in the calls the last couple of weeks:


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.

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.

1 Like

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.

Why is it important to add the recipient to the access list if there is no ETH transferred?

It’s technically not, but it reduces the amount of code and special cases if it is done that way.

1 Like

Seems this gas scheme doesn’t take into account reading of storage slots that are already cached by the machine. Example when reading an array from storage.
One would assume cached data should have much cheaper gas cost for storage reads.

Seems this gas scheme doesn’t take into account reading of storage slots that are already cached by the machine. Example when reading an array from storage.

It does! If a storage slot was already accessed in the transaction, the next access costs 100 less. If you mean sequential access, that wouldn’t actually work because storage keys are all hashed, so the elements of an array map to totally distinct random-looking locations in reality.

Oh

Is this behaviour essential, could it be modified?
storing an array in memory in a sequential manner and then lowering gas price for sequential access could be very helpful. Especially with current gas price

if a few different contracts query same storage. like when querying token balance after trade,
or a few contracts query same account balance.
is it considered a warm storage read?

@vbuterin @holiman could the functionality of https://eips.ethereum.org/EIPS/eip-1380 be covered by this EIP? At the time EIP-1380 was discussed, the idea of access list was brought up, which it seems to be implemented by EIP-2929.

From the specification:

It is unclear to me whether the above is a cost on top of the current cost or replacing it. For EXTCODESIZE, etc. it seems to be replacing, but for CALLs I believe it may only be replacing the base cost? Can this be clarified further?

Never mind, ever since I wrote this blurb my mind has been tainted that we have complex base costs for CALL* opcodes, when in fact we do not. We just have a complex set of rules, but the base cost is fixed, and EIP-2929 is changing the base cost.

It’s essential because otherwise attackers could place a bunch of accounts or storage keys close to each other so as to massively increase the data costs of accessing the Merkle tree of the storage.

if a few different contracts query same storage. like when querying token balance after trade,
or a few contracts query same account balance.

Yes. As long as you’re reading something that was already read in the same transaction, it’s a warm storage read.

@vbuterin @holiman could the functionality of https://eips.ethereum.org/EIPS/eip-1380 be covered by this EIP?

The behavior of EIP-1380 is already automatically a part of this EIP, because if an account is the caller, it must already be in the access list, so a call-to-self would only cost the WARM_STORAGE_READ_COST. This EIP also subsumes EIP-2046 (make calls to precompiles cheaper).

Is there a way, as part of this design to enable larger storage slots. Slot size could be aligned to cache sizes of avg. mining machines, so it does not introduce new EVM attack vectors. From reading analysis of loading and calculating state by clients, seems main delaying factor is related to disk reads. For linux default page size for disk reads is 4K. So maybe one solution is creating memory chunks with that size.

The costs of storage reads create great burden on any dapp in the space. Note some dapps took long months to write. The magnitude and pace of changes is quick (we just had Istanbul with x4 cost not long ago).
It seems no development efforts are directed to solve the high costs of storage reads. Hence there are missing solutions for contract designers/writers.

so some support would be appreciated.

I wonder if the underpricing of bn128_ecadd/ecmul discovered during EIP-2046 would not have an effect here? The underpricing is described in https://github.com/ethereum/EIPs/pull/2666. The cost proposed by 2046 is significantly lower at 40, while 2929 proposes 100, so it may not have such an effect on it, but would be good to have clarity on this.

Variable length storage was discussed previously a few times, and as recently as EthCC this year.

Two weeks ago I looked into this again, because my hunch was that at least two major use cases (multisig wallets and decentralised exchanges) would benefit from it. The sources I checked were uniswap, gnosis-multisig, and gnosis-safe.

A comprehensive analysis would mean looking at the generated code to see if subsequent storage locations are used (by subsequent here I do not mean by hash, but rather pre-hash, i.e. the key solidity uses for cases like byte arrays or structs).

While this is not a comprehensive analysis and only checked source codes, what I found is that only the multisig use case would benefit from variable length storage, all the other contracts make heavy use of mappings with value types, so they would not benefit from this. I’m sure it would open the door for new best practices or perhaps these contracts could be rewritten in a manner to benefit from it, but it is not looking as promising as I initially hoped.

That being said I would be interested to collaborate on researching this further, but my time is restricted.

I did come up with some ideas how to represent this nicely in Solidity, but that is besides the point currently.

Thanks for all details. wasn’t aware.

I am part of Kyber smart contract development.
In our code base we also have excessive usage of mappings. But not always.
Some contracts do use structs and arrays (not mapped).
Adding to that, most of the contracts has a list of configurable addresses and configuration flags (enable / disable), which could benefit from a cheaper storage method. Adding to that, best practices are dynamic and will be modified.
would love to set up a call to farther discuss.

Yes, that’s a good point. I’m doing some more benchmarking to check all precompiles under these rules.

I am concerned that this EIP increases the gas cost to call external functions on proxy contracts and diamonds.

Currently an external function call on a proxy contract or diamond requires an SLOAD to get a contract address and requires executing DELEGATECALL. The gas costs is 800 + 700 = 1500

So currently it costs at least 1500 more gas to call an external function on a proxy contract or diamond than on a regular contract.

This EIP would increase the gas cost to 2100 + 2600 = 4700. So it would cost 4700 more gas to call an external function on a proxy contract or diamond than on a regular contract. This increased gas gap between proxies/diamonds and regular contracts hurts the utility and usefulness of proxy contracts and diamonds.

I understand this EIP is necessary and is temporary and I support it for Ethereum’s long-term health and scalability. But these changes could stay in effect for a year or longer.

I would like to know if it is possible that this EIP also require something in it that can be used to mitigate or reduce the gas costs for calling external functions on proxy contracts and diamonds.

One idea is for this EIP to require EIP-2936, which provides a new way to create proxy contracts and diamonds.

What other solutions and mitigations are possible? Can EIP-2930 be used to solve the problem?

Gas costs are relative. Patterns that are more burdensome on validation should cost more. Correcting issues with pricing will allow larger gas limits and thereby lower gas prices, making the effective difference comparable to before.

As the author of 2936, naturally I support this solution. It reduces the burden on validators for the CREATE2 reincarnation upgrade pattern and makes reincarnation a viable alternative to SLOAD+DELEGATECALL proxies, the current dominant paradigm.

@wjmelements I understand that gas prices may drop because of this EIP, therefore easing the burden of gas costs for calling external functions on proxy contracts and diamonds. But what concerns me specifically is the increased gas gap between calling external functions on regular contracts and calling them on proxies/diamonds. The gas price may be less but the gas gap is still wider than before. It will still cost more gas units to call external functions on proxy contracts and diamonds.

@wjmelements What do you mean by larger gas limits?

As it should, because those patterns require more work.

Increasing gas costs of these operations would allow higher gas limits for the same uncle rate and state growth rate. A better way to view this change is that every other operation uses less gas, relatively.

If you want sequential storage you have to use contract code. You can use EXTCODECOPY to read the data into memory.