Title: EIP_2929__Gas_cost_increases_for_state_access_opco
Category: EIPs

Author: vbuterin
Date: 2020-09-01T12:23:04.582Z

As discussed in the calls the last couple of weeks:


github.com/ethereum/EIPs








Gas cost increases for state access opcodes


master ← vbuterin-patch-2



          opened 12:02PM - 01 Sep 20 UTC




            vbuterin
          



+165
-0






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](https://ethereum-magicians.org/t/protocol-changes-to-bound-witness-size/3885).







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.

Author: wjmelements
Date: 2020-09-02T19:54:48.901Z

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?

Author: vbuterin
Date: 2020-09-03T05:27:40.579Z

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

Author: ilanDoron
Date: 2020-09-03T09:33:38.948Z

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.

Author: vbuterin
Date: 2020-09-03T10:50:56.785Z

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.

Author: ilanDoron
Date: 2020-09-03T13:24:17.430Z

Oh



 vbuterin:

elements of an array map to totally distinct random-looking locations in reality


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

Author: ilanDoron
Date: 2020-09-07T11:14:55.706Z

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?

Author: axic
Date: 2020-09-07T14:42:27.767Z

@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.

Author: axic
Date: 2020-09-07T14:45:18.140Z

From the specification:


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.



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?

Author: axic
Date: 2020-09-07T15:03:04.866Z

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.

Author: vbuterin
Date: 2020-09-08T02:30:29.267Z

ilanDoron:

Is this behaviour essential, could it be modified?


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 EIP-1380: Reduced gas cost for call to self 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).

Author: ilanDoron
Date: 2020-09-09T12:22:19.556Z

vbuterin:

costs of accessing the Merkle tree of the storage


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.

Author: axic
Date: 2020-09-09T14:13:34.872Z

vbuterin:


@vbuterin @holiman could the functionality of EIP-1380: Reduced gas cost for call to self 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).


I wonder if the underpricing of bn128_ecadd/ecmul discovered during EIP-2046 would not have an effect here? The underpricing is described in Precompiles and Keccak256 repricing by shamatar · Pull Request #2666 · ethereum/EIPs · GitHub. 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.

Author: axic
Date: 2020-09-09T14:24:05.385Z

ilanDoron:




 vbuterin:

costs of accessing the Merkle tree of the storage


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.


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.

Author: ilanDoron
Date: 2020-09-10T06:37:21.294Z

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.

Author: holiman
Date: 2020-09-10T07:10:54.527Z

axic:

I wonder if the underpricing of bn128_ecadd/ecmul discovered during EIP-2046 would not have an effect here?


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

Author: mudgen
Date: 2020-09-13T16:44:23.569Z

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?

Author: wjmelements
Date: 2020-09-13T20:28:30.926Z

mudgen:

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.


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.



 mudgen:

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


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.

Author: mudgen
Date: 2020-09-13T21:21:37.064Z

@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?

Author: wjmelements
Date: 2020-09-14T00:46:02.940Z

mudgen:

It will still cost more gas units to call external functions on proxy contracts and diamonds.


As it should, because those patterns require more work.



 mudgen:

@wjmelements What do you mean by larger gas limits?


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.



 ilanDoron:

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.


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

Author: ilanDoron
Date: 2020-09-14T08:19:47.916Z

wjmelements:

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


Thanks. a good point.
but this is only for constant data. right?

Author: mudgen
Date: 2020-09-15T05:48:06.810Z

wjmelements:




 mudgen:

It will still cost more gas units to call external functions on proxy contracts and diamonds.


As it should, because those patterns require more work.


Yes, so I am asking for a solution or different pattern so that upgradeable contracts are not worse off gas-wise because of this EIP compared to regular contracts when calling external functions on them. And by worse off I mean that the gas gap between upgradeable contracts and regular contracts is increased.
I’m not saying that upgradeable contracts should require the same amount of gas as regular contracts when calling external functions on them.  I’m saying I’d like there to be way to prevent increasing the gas gap that is currently done by this EIP.

Author: wighawag
Date: 2020-09-15T09:55:36.496Z

I agree with @mudgen that it would be great if we could come up with an EIP that mitigate the issue of increase SLOAD cost for Proxy based contract.
It could for example be a mechanism by which a contract could update its bytecode.
But it could be other mechanisms too.
But I guess this is out of scope for this particular EIP.

Author: vbuterin
Date: 2020-09-16T05:35:05.156Z

I think the realistic long-term option is just charging per witness byte instead of per access, so SLOAD from contracts with only a small amount of storage would remain fairly cheap. I presume that these contracts that store these contract addresses per function would not have more than ~100 storage slots or so?

Author: wighawag
Date: 2020-09-16T10:42:12.202Z

FYI: I actually came up with an trick to not use storage at all to get implementation address for proxy : https://twitter.com/wighawag/status/1306180888154251270
The implementation address is basically stored in an external contract’s bytecode that can self-destruct and being recreated at the same address with different bytecode (same creation code)
There is still an overhead (the call) but this is not dependent on SLOAD cost
EDIT: Note that it does not solve the proxy issue as CALL in EIP-2929 are even more expensive than SLOAD

Author: mudgen
Date: 2020-09-19T20:57:05.878Z

Yes, that long-term solution sounds good.  I was also hoping for a short-term solution.
How many storage slots are used per proxy pattern depends on the proxy pattern implementation. Diamonds currently have two reference implementations.
The diamond-1 and  diamond-2 implementations requires 58 storage slots for 50 external functions.
The diamond-3 implementation requires between 59 to 232 storage slots for 50 external functions (depending on how many facets are used).
Info about the three diamond reference implementations is here: https://github.com/mudgen/diamond

Author: tkstanczak
Date: 2020-09-22T22:24:46.532Z

The sets are transaction-context-wide, implemented identically to other transaction-scoped constructs such as the self-destruct-list and global  refund  counter. In particular, if a scope reverts, the access lists should be in the state they were in before that scope was entered.

In this case, when we talk about caching → it would be more reasonable to not clear the scope on reverts since it is still in the cache and access should still be cheaper. This would also reduce the memory requirements for the transaction as we would not have to keep up to 300 different states of the access lists.
In Nethermind, I think, we get it for three as for any account and storage we can get a single number with info when it was last time touched and at the same time each scope memorizes the position that can be compared with the ‘last touch’ number. This is specific to our implementation and probably not readily available in every client.

Author: AlexeyAkhunov
Date: 2020-09-22T22:42:09.413Z

wjmelements:

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.


This is not really going to help. Because if we implement this EIP, and then increase gas cost 3-4x to make the gas prices come down, such increase would negate the “protective” effect of this EIP as a DOS-attack deterrent. So the increase in gas limit needs to be much more conservative, if any. That is my main objection to the EIP - it only makes sense as a very short-term measure, but it introduces lots of extra long-term complexity. Not a good trade-off from my point of view

Author: holiman
Date: 2020-09-23T09:09:37.853Z

tkstanczak:

In this case, when we talk about caching → it would be more reasonable to not clear the scope on reverts since it is still in the cache and access should still be cheaper. This would also reduce the memory requirements for the transaction as we would not have to keep up to 300 different states of the access lists.


There are a couple of problems with this.

It’s IMO ‘semantically ugly’. A perfectly valid cheat, is that if you know that a transaction or scope exits on failure, it’s not necessary to actually execute it. It doesn’t matter how or where it exits, as long as it exits.

This became untrue back in the RipeMD touch OOG failure, since at that point a side-effect from within a reverting transaction did cause a side-effect, but that’s the only exception we have.


A perhaps larger problem, and definitely more practical than the one above, is that it introduces a way to cheat. Say you want to add N slots in contract A to the access list.

Call A with 0 gas (cost 2600). A is now added to acess list. A now tries to SLOAD slot 1. Fails on OOG. You now have A, (A:1) in access list
Call A again with 0 gas (cost 100). A now tries to SLOAD slot 1. Fails on OOG. You how have A, (A:1), (A:2) in access list.



So basically, you can add things to your access list at a cost of ~100 gas, instead of 2K+

it would be more reasonable to not clear the scope on reverts since it is still in the cache and access should still be cheaper.

In the scenario above, the failing SLOADs would not place the item in the cache, since the cost-check should be done before the item is loaded.

Author: vbuterin
Date: 2020-09-23T09:26:09.070Z

The intention behind clearing the scope is that we already have code paths for how to implement transaction-scoped variables (namely, the refunds counter and the selfdestructs list), and it’s cleaner and simpler if we just reuse those code paths for all the transaction-scoped variables that we do now and in the future than if we start customizing revert rules depending on what’s optimal for each specific situation.

Author: rmeissner
Date: 2020-09-23T11:34:51.122Z

Do I understand it correctly that calling any account that has not been called before costs at least 2600 gas. Assuming this is correct it will not only increase the price gap, but break any contract that uses transfer or send. The mentioned reports focus only on the receivers that use too much gas, but I would say this might need to be reevaluated, as it is not even possible to perform the solidity call for transfer or send to an EOA. While I see that EIP-2930: Optional access lists can prevent this from happening, but this requires that wallets and dapps support this.
There is a solidity issue to remove these limits from the compiler (Remove .send and .transfer. · Issue #7455 · ethereum/solidity · GitHub), but I think this is still a quite open discussion.
Looking at all of this I have to agree with Alexey



 AlexeyAkhunov:

That is my main objection to the EIP - it only makes sense as a very short-term measure, but it introduces lots of extra long-term complexity. Not a good trade-off from my point of view


Edit: correct link

Author: tkstanczak
Date: 2020-09-23T11:35:47.702Z

There are a couple of problems with this.

I see. This can be prevented by changing the access list after successful execution of SLOAD / SSTORE.

Author: tkstanczak
Date: 2020-09-23T11:39:22.539Z

Not reverting changes on failing calls would be transaction scoped but not call-scoped.
The destroy lists and refund counters are transaction-scoped but also have the call scoped clones that are committed on exiting a call.
I think my approach actually simplifies the code on the client side (at least in Nethermind). This EIP is quite complex by itself.
image885×611 16.2 KB
Call scope is cloned up to 300 times on deep calls.
image580×571 7.7 KB

Author: holiman
Date: 2020-09-23T11:46:36.959Z

rmeissner:

Do I understand it correctly that calling any account that has not been called before costs at least 2600 gas. Assuming this is correct it will not only increase the price gap, but break any contract that uses transfer or send


That is correct. Most transactions will not break, it seems, but some will require more gas. A transaction which does a lot of “reaching out to new places” will cost more, but transactions which “interacts with few other contracts multiple times” will cost less.

While I see that EIP-2930: Optional access lists can prevent this from happening, but this requires that wallets and dapps support this.

Yes, it does. I personally don’t think 2929 is going to go live without 2930, for specifically that reason. However, 2930 doesn’t require widespread support, if the only usecase is to be able to salvage a tiny subset of txs that would otherwise fail.  As long as there are some signers/wallets that support it, that’s enough to make it work.

Because if we implement this EIP, and then increase gas cost 3-4x to make the gas prices come down, such increase would negate the “protective” effect of this EIP as a DOS-attack deterrent.

Yeah I don’t see why we would increase the gas cost to 3-4x. Experimentation shows that the actual gas spending doesn’t change more than a few percent. In other words: an 8M block pre-2929, is still an 8M block post-2929.

Author: holiman
Date: 2020-09-23T11:48:55.900Z

tkstanczak:

The destroy lists and refund counters are transaction-scoped but also have the call scoped clones that are committed on exiting a call.


Geth doesn’t operate like that. We did, once upon a time, and even used clone-copies of the state. That bit us hard in the shanghai attacks, and we eventually had to implement journalling instead. I don’t agree that clone-commit/discard is easier or better.

Author: vbuterin
Date: 2020-09-23T11:49:08.543Z

rmeissner:

Do I understand it correctly that calling any account that has not been called before costs at least 2600 gas. Assuming this is correct it will not only increase the price gap, but break any contract that uses transfer or send


I don’t understand this argument. It would only break contracts that use transfer or send if those contracts themselves are called by other contracts and those other contracts make the call with a fixed gas limit (a very rare pattern nowadays). If you mean a contract calling send to immediately forward received funds inside the pre-allotted 2300 gas, I don’t think that’s possible anyway because value-transferring calls have a gas cost of 9000.

Author: rmeissner
Date: 2020-09-23T11:55:30.348Z

vbuterin:

those other contracts make the call with a fixed gas limit (a very rare pattern nowadays)


This is the current implementation in Solidity (setting the gas limit to the stipend) as far as I remember (I would need to read up again on the concrete limits used by solidity).
My main point was that the previous analysis done for EIP-1884 are not 1:1 valid for this EIP and I would love if something similar would be done again.
Edit: Just to avoid that I am misunderstanding something, afaik EIP-1884 only had a breaking effect when the receiving contract did additional logic (e.g. a proxy or emitting events) → this would result in an OOG is the receiver contract. This EIP would break in the calling contract (with an OOG). Would that be correct?
Edit 2: Stipend docs from Solidity → Security Considerations — Solidity 0.7.1 documentation

Author: chfast
Date: 2021-02-16T12:24:14.328Z

Here I have interesting case for a CALL with non-zero value.

The cold destination D is added to accessed_accounts.
It turns out D does not exist: additional penalty of 25000.
This penalty causes out-of-gas exception.

Now we repeat the above steps. The difference is D is warm now although it still does not exist.

Author: holiman
Date: 2021-02-16T13:37:22.342Z

There’s nothing that says something in the accesslist must exist (e.g. 2930 can add whatever that doesn’t exist).
So it shouldn’t be an edgecase (as in, semantically it’s pretty clear-cut, IMO), but it may very well be, so good catch. We should make a test for this, and I’ll also try it out on Yolov3.

Author: chfast
Date: 2021-02-16T13:53:28.568Z

Actually, you can simplify this case by not relying on 25000 penalty. The call can simply fail because of the cold account access penalty of 2600. Still the D will be added to the accessed_accounts on the first call.
I don’t understand all consequences of this, but try exploring this mechanism as a way to bypass the cold account access cost. For a series of failing calls to non-existing account D only the first one will consider the D cold. But in practice, a node implementation must do full D lookup unless it maintain a cache of non-existing accounts. Of course, these failing calls must be wrapped with another calls to proceed.
I think this would work “safer” if the 2600 gas is strictly required up front to proceed with any call, independently of the actually initial call cost computed later. Similarly to the required minimal gas amount to proceed with a SSTORE.

Author: shemnon
Date: 2021-02-16T16:49:56.845Z

Clients maintain a cache of non-existing accounts within a transaction call.  Or at least keep in memory all the trie nodes needed to read to that account, so disk access doesn’t happen after the first lookup.

Author: holiman
Date: 2021-02-16T17:43:37.983Z

shemnon:

Or at least keep in memory all the trie nodes needed to read to that account, so disk access doesn’t happen after the first lookup.


Yep, at least that’s what happens in geth – if it’s trie-backed, the resolved nodes will be in memory after the first time, even though the state-object doesn’t exist in “object” form in the cache.
If reads are done from the flat-db-backed snapshot, the data will similarly be cached in the diskLayer, and avoid hitting disk again.

Author: vbuterin
Date: 2021-02-17T18:51:53.222Z

If you use trie nodes, is there a risk that, because addresses N and N+1 share the same trie node, that if you access address N then address N+1 will appear warm?
Also, what happens if address N gets accessed, the sub-call that did the access gets reverted, and then N gets accessed again? The spec says that you should pay the higher cold-access cost twice.

Author: holiman
Date: 2021-02-17T21:08:11.241Z

vbuterin:

If you use trie nodes, is there a risk that, because addresses N and N+1 share the same trie node, that if you access address N then address N+1 will appear warm?


No, you misunderstood me. So hot and cold are tracked by an explicit list. I mean that even if we don’t explicitly cache the account in object-form in the ‘state’, but are forced to lookup the thing from the trie the second, and third time, it’s still a cheap operation, because the trie is sufficiently preloaded that we don’t actually have to resolve any hashes. We have the trie in memory (or, if the snapshot is used, we have the empty slice of bytes representing the account in memory).



 vbuterin:

Also, what happens if address N gets accessed, the sub-call that did the access gets reverted, and then N gets accessed again? The spec says that you should pay the higher cold-access cost twice.


Yes. That’s what happens… I guess the question was based on some misunderstanding on how we use tries?

Author: jochem-brouwer
Date: 2021-02-20T21:47:45.852Z

I’m gonna need a clarification here what the “set of precompiles” is. Is this the set of active precompiles (which thus have special code assigned to it)? I do remember an EIP (which I sadly cannot find again) which defines the addresses as 0x00…00 to 0x00…00ffff (or something similar) as the precompile addresses.

Author: jochem-brouwer
Date: 2021-02-20T22:47:10.056Z

I also don’t understand why a slot is cold when a sub-call which accessed that slot reverts and made that slot warm. It doesn’t make sense to me. We can cache the value of this slot during the transaction and therefore we should not need to charge the cold cost of accessing it twice. It also means we have to do complex bookkeeping with committing/checkpointing/reverting to track the accessed slots/addresses.

Author: jochem-brouwer
Date: 2021-02-20T23:19:22.985Z

Another point: why is the target of CREATE2 added to warm addresses immediately? We need to do a disk read on CREATE2, especially because we can try to deploy a contract on the same address, i.e. we have to check that the account is empty and thus do an IO read.

Author: holiman
Date: 2021-02-23T11:14:40.467Z

jochem-brouwer:

Is this the set of active precompiles (which thus have special code assigned to it)?


Yes.



 jochem-brouwer:

I also don’t understand why a slot is cold when a sub-call which accessed that slot reverts and made that slot warm.


Because otherwise you could do a 100 gas call to X, and X tries to CALL an account Y, but doesn’t have the funds to succeed. If it now reverts, suddenly you have Y in the hotlist although you didn’t pay the price for it. So it would be a backdoor, basically.
(And that same case can be made analoguous with slots)



 jochem-brouwer:

Another point: why is the target of CREATE2 added to warm addresses immediately? We need to do a disk read on CREATE2, especially because we can try to deploy a contract on the same address, i.e. we have to check that the account is empty and thus do an IO read.


I think the reasoning is that CREATE2 is sufficiently expensive, and it was in line with how create worked. I don’t know if there’s any more elaborate reason behind it cc @vbuterin

Author: jochem-brouwer
Date: 2021-02-23T13:19:08.359Z

holiman:

Because otherwise you could do a 100 gas call to X, and X tries to CALL an account Y, but doesn’t have the funds to succeed. If it now reverts, suddenly you have Y in the hotlist although you didn’t pay the price for it. So it would be a backdoor, basically.


Ah I didn’t think of this, very good point.
Thanks for the other answers as well!

Author: jochem-brouwer
Date: 2021-03-27T14:31:22.157Z

Small note here: this EIP1352 and EIP2929 are not very consistent and we should be careful with terminology here.
Excerpt from EIP2929:
We should initialize the accessed accounts as:
and the set of all precompiles.
In combination with EIP 1352, one could interpret it as “all precompiles” being 0x00…00 to 0x00…00ffff, but this is not the case, since we only should add precompiles which have code.

Author: chfast
Date: 2021-04-08T07:48:10.980Z

The tests at testcases.md · GitHub use gas limit of 18446744073709551615 which exceeds the gas limit gap of 9223372036854775807 (2**63-1).

Author: axic
Date: 2021-05-21T13:35:14.834Z

IIUIC the coinbase is not part of the starting accessed_addresses, i.e. the cold cost is paid when transferring to it from a contract. I would assume the coinbase is always warm in a block, and as a result in a transaction?
Is this just an a) oversight, b) simplification, or c) there is a definitive reason for this?

Author: axic
Date: 2021-05-24T11:25:24.904Z

I know it is late to ask, but were any benchmarks published on this matter?

Author: vbuterin
Date: 2021-05-25T12:54:19.671Z

holiman:

I think the reasoning is that CREATE2 is sufficiently expensive, and it was in line with how create worked. I don’t know if there’s any more elaborate reason behind it cc @vbuterin


Yeah basically this, CREATE2 is sufficiently expensive that we don’t have to care.

Author: holiman
Date: 2021-05-26T09:47:49.259Z

axic:

Is this just an a) oversight, b) simplification, or c) there is a definitive reason for this?


Somewhere between a) and b), I’d say.

Author: CPerezz
Date: 2025-03-11T11:19:40.038Z

Hey.
Unsure what the status of this is atm.
Definitely ZKEVMs bring a lot more considerations to take when we talk about state-access opcodes.
I wanted to highlight Towards safe optimization the storage related gas cost - #3 by hzysvilla - Execution Layer Research - Ethereum Research which looks like a superset of this. And which brings 30% improvements overall with somehow simple solutions.
There’s an interesting matrix with all the optimization combinations that apply. But generally, I think it would be interesting identifying if it makes sense to go for the superset of this. Or simply it’s just better to move forward with this (which doesn’t looks like it’s got the traction needed).
Any opinions on whether the gast cost here is still relevant discussion? If so, should we just go with the improvement? Or this is a more reasonable and smaller-scoped EIP to move forward?

Author: wjmelements
Date: 2025-03-11T17:07:55.766Z

EIP-2929 is final and was included in a 2021 mainnet upgrade.