EIP-7907: Meter Contract Code Size And Increase Limit

discussions-to for EIP-7907: Meter Contract Code Size And Increase Limit

(original PR: Add EIP: Meter Contract Code Size And Increase Limit by charles-cooper · Pull Request #9483 · ethereum/EIPs · GitHub)

2 Likes

I performed jumpdest analysis benchmarks for eip 7903, which are relevant here.

2 Likes

Twitter is cheering up.

But this incentivises tha lazy inclusion of libraries and goes against the principle of maximum reuse of what is already deployed on chain.

Ofc, the question is whether there is even a common agreement on such a principle of deployed code reusability.

1 Like

If the account is warm, no change to the gas schedule occurs.

The account being warm doesn’t mean the code is loaded:

  • Access Lists: account will be warm if added to access list but no code is pre-loaded just the account (including codehash).
  • EXTCODEHASH also warms the account but doesn’t load the code

Other actions loading the code are not included in the pricing:

  • EXTCODESIZE loads the code
  • Basic tx calling a contract also loads code, but isn’t in the pricing

Do you need to add all these to pricing, or add as second “warm” list that records if code is loaded?

Hmm, interesting. What do you think is the cleanest way in the spec to specify that the cost is only incurred on the first load of the code?

It doesn’t have to btw, the EIP specifies that clients should keep an out-of-band index for codesize –

Clients should add an efficient way to determine the code size without loading the entire code, e.g. storing it in a separate table keyed by code hash. This way, they can charge for the access cost before physically loading the code. Otherwise, a client may load a contract, even when there is not enough gas left to pay for the code load.

So should loading from this new table incur the cost of an SLOAD?

Making EXTCODESIZE cost warm account (get hasd) + 2100gas (get data) rather than 20 gas?

How does this work for statelessness and zk; since it is an unwitnessed data with no root, how is it proved to be correct?

It’s witnessed by codehash, but I’m not sure where and when in the protocol the proof would be provided.

Actually I’m checking go-ethereum and calling EXTCODESIZE triggers addition of the full code to the witness. Suggesting that yes, EXTCODESIZE should also trigger the per-byte cost in the EIP.

Nice catch! I think we should introduce an additional warm state for the code so that

  • account warm but code cold. This applies to optional access lists in EIP-2930. Calling such a contract will enjoy WARM_STORAGE_READ_COST = 100 vs 2600 for the first 24KB of the code. If the code size > 24KB, then we charge 2 gas per byte as in EIP-7907 and put the account as code warm;
  • account warm and code warm, where calling such a contract will take WARM_STORAGE_READ_COST = 100 gas.
1 Like

Yes, this makes sense to me. It looks inelegant at first to have two different warm states, but it makes sense bc code is stored in an auxiliary table separate from accounts.

1 Like

Yeah is a bit ugly; but EXTCODESIZE etc has been a pain point in many scenarios; so it is a pragmatic fix

I spoke a bit about the EXTCODESIZE issue offline with @matt, and here are my thoughts –

As a user of the EVM, it’s a bit of a gotcha for EXTCODESIZE to be priced differently from other account querying opcodes, e.g. EXTCODEHASH. It is also uniquely determined by codehash (up to hash collision resistance), so if there are any tricks that the implementation can pull to make this happen, I think it would be good for users.

However, I recognize this might not be copacetic with how implementations actually work. For example, for stateless clients, they probably need the full code in order to prove codesize, even though it is “witnessed” (uniquely determined) by codehash.

I think there are basically three options:

  1. Apply the code load cost from this EIP to EXTCODESIZE as well
  2. Make it the same as EXTCODEHASH
  3. Add @benaadams’ proposed 2100 gas as a separate cost for EXTCODESIZE

I prefer 2, 1 and then 3, in that order. I don’t think an additional cost needs to be added for the codesize query on top of account loading cost, since it can be prefetched in parallel with the other account data (or simply added into the db entry for an account, just without affecting the state root).

All that being said, this issue probably doesn’t matter too much in the big picture, since EXTCODESIZE usually happens near a CALL as part of the contract existence check. So the code is typically going to be loaded, anyways!

Meanwhile, option 1 seems most friendly for stateless clients. I think it would also be fine to ship option 1 instead of option 2, and figure out how to bring the gas cost down for EXTCODESIZE down later (since intuitively it looks like it should be cheap).

Paging @matt, @qizhou, @jochem-brouwer as well who have also been very helpful with feedback to see what their thoughts are here. I’d also like everybody to be on the same page so there aren’t surprise disagreements in the spec later.

My preference order is also 2,1,3. My argument is that the gas pricing for stateful, statelessness, and zk may be completely different - not only for EXTCODESIZE but almost all other OP codes and precompiles. For example, statelessness needs full code to prove codesize, while zk can prove the relationship between codehash => codesize with a much smaller prove size. Given our priority is for the current stateful EVM upgrade in Fukasa, I would choose 2 considering the negligible cost of the parallel lookup of codehash => codesize in stateful DB as Charles explained.

One of the Shanghai DoS attacks was EXTCODESIZE,POP; so while this is a fair assumption for well meaning code; it isn’t for intentional attacks

1 Like

Ah, I meant for the user – as in the typical user won’t experience a large change in pricing depending on the two schemes.

An early draft implementing EIP-7907 in Geth can be found here add EIP-7903 by qizhou · Pull Request #1 · qizhou/go-ethereum · GitHub

2 Likes

The EIP does not reference 7702 delegations. Might be a good idea to explicitly clarify the access costs for delegated accounts.

3 Likes

Change the gas schedule for opcodes which load code. Specifically, the CALL, STATICCALL, DELEGATECALL, CALLCODE and EXTCODECOPY opcodes are modified so that ceil32(excess_contract_size) * 2 // 32 gas is added to the cold access cost, where excess_contract_size = max(0, contract_size - 0x6000). (Cf. initcode metering: EELS). If the account is warm, no change to the gas schedule occurs.

It is a bit unclear if the value of 2 used in the formula ceil32(excess_contract_size) * 2 // 32 is the same as the INITCODE_WORD_COST that is defined in EIP-3860 or if this EIP wishes to define a new constant with the same value. Explicitly clarifying this will help avoid ambiguity in case of future changes/updates.

2 Likes

Good point. It should be the same as INITCODE_WORD_COST. I’ll update the EIP

@gurukamath please check: clarify that 2 == GAS_INIT_CODE_WORD_COST by charles-cooper · Pull Request #9793 · ethereum/EIPs · GitHub

1 Like