EIP-2929: Gas cost increases for state access opcodes

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.

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.

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.

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

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

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.

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.

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.

Yes.

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)

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

1 Like

Ah I didn’t think of this, very good point.

Thanks for the other answers as well!

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.

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

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?

1 Like

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

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

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