EIP-3860 Initcode size limit and metering (Limit and meter initcode)
Final · EVM & execution · EIP status Final · created 2021-07-16
Limits the maximum size of the initialization code (initcode) used to create a contract to 49152 bytes and charges an additional 2 gas for each 32-byte chunk.
Why it is needed
When a new contract is created on Ethereum, the node software (client) scans the entire initialization code before execution to perform jumpdest-analysis. The amount of work grows in proportion to the initcode length, but today that cost is not charged in gas and the protocol sets no size limit. In particular, when a contract creates another contract with the CREATE or CREATE2 instruction, there is no cost attached to the initcode itself, so variants of malicious initcode can be produced in bulk at low cost. In the past there was in fact a vulnerability that exploited malicious initcode, which was fixed in geth 1.6.5 in 2017. The absence of a limit has also influenced the design of other EVM proposals, and has caused features to be delayed or cancelled. This EIP makes the cost proportional to initcode length to reduce future risk, and sets an explicit limit to simplify EVM implementations.
What changes
Today
- The protocol sets no limit on initcode size
- No gas is charged for analyzing initcode created by CREATE and CREATE2
- Without a limit, the worst-case attack scenario is hard to estimate
After EIP-3860
- Initcode is allowed only up to 49152 bytes (MAX_INITCODE_SIZE)
- An additional 2 gas is charged for each 32-byte chunk (word)
- Code size and jump positions fit into 16-bit values, which simplifies EVM implementations
Who is affected
- Client teams: Attacks targeting jumpdest-analysis become less threatening, which makes clients more robust. The explicit size limit also simplifies implementations.
- Contract developer: Most deployments are unaffected, but transactions that use initcode larger than 49152 bytes become invalid or have their execution aborted. Deploying multiple contracts in a single creation transaction may run into the limit.
- L2 and factory contracts: A factory that packs the code of multiple contracts into the initcode of the first contract and deploys them in a multi-layer structure could hit a failure case that did not exist before. However, the authors state that they know of no such contracts.
In depth
Constants and cost formula
There are two new constants. INITCODE_WORD_COST is 2, and MAX_INITCODE_SIZE is 2 * MAX_CODE_SIZE. MAX_CODE_SIZE is 24576 as set by EIP-170, so the initcode limit is 49152 bytes. The cost function is initcode_cost(initcode) = INITCODE_WORD_COST * ceil(len(initcode) / 32), which comes to 0.0625 gas per byte. The EVM does not allow fractional gas, so the cost is approximated by charging per word (32 bytes).
Four rules
First, if the data of a creation transaction (the initcode) exceeds MAX_INITCODE_SIZE, the transaction itself is invalid, similar to how a transaction with insufficient intrinsic gas is invalid. Second, initcode_cost is added to the data cost formula of a creation transaction. This is included in the intrinsic cost, so a transaction that cannot pay it is also invalid. Third, if the initcode passed to CREATE or CREATE2 exceeds the limit, execution is aborted exceptionally, as with running out of gas. Fourth, an additional initcode_cost of gas is charged for CREATE and CREATE2, and this deduction happens before the contract address is computed and before the initcode is executed. It comes before, or at the same point as, the hashing cost of CREATE2.
Why 2 gas per word was chosen
The benchmark baseline is the KECCAK256 hashing performance of geth 1.10.9, targeting a 70 Mgas/s gas limit on a 4.0GHz x86_64 CPU. The measured cost per 32 bytes was 2.0 for geth, 2.9 for evmone/Baseline, 13.8 for evmone/Advanced, and 6.0 for geth's KECCAK256, and 2 gas was chosen based on the geth implementation. Computing per word works the same way as the CREATE2 hashing cost (hashcost) defined in EIP-1014, so the implementation can be shared. The constants, which were CREATE 0 and CREATE2 6 before activation, become CREATE 2 and CREATE2 6 + 2 after activation.
Why a size limit is set and its effect
With a limit, it becomes easier to estimate and construct the worst case, because one variable that must be explored is greatly reduced. This allows the per-byte cost to be set much more optimistically. Without a limit, a higher price would have to be charged to cover unknown risks. Since most initcode does not exceed the proposed limit, it was judged unnecessary to penalize contracts with an overly conservative cost. The limit of 2 * MAX_CODE_SIZE allows MAX_CODE_SIZE for the runtime code and another MAX_CODE_SIZE for the constructor code in the basic case. However, it can be a practical constraint when deploying multiple contracts in a single creation transaction. The initcode cost of a creation transaction (0.0625 gas per byte) is negligible compared with the data cost (4 or 16 gas per byte), but it was included in the specification for consistency and future compatibility.
Why violations are handled as exceptional aborts
A limit violation in CREATE and CREATE2 is handled as an exceptional abort of execution. This puts the violation check into the group of early out-of-gas checks. This group includes stack underflow, memory expansion, static call violation, initcode hashing cost, and the initcode cost added by this EIP. These checks are performed before lighter checks such as call depth and balance. This choice keeps the check order consistent and lowers implementation complexity, since out-of-gas checks can be done in any order.
Backward compatibility and security effects
Because this changes consensus rules, a network upgrade (hard fork) is required. Already deployed contracts are not affected, but a transaction containing initcode over the limit can still be included in a block, and simply ends in an exceptional abort. For clients, attacks targeting jumpdest-analysis become less of a problem, which improves robustness. At the 30M gas limit on London at the time of writing the source, it was possible to trigger analysis of about 1.3GB of initcode in total, and with this EIP applied, the cost of such an attack increases by about 80M gas. The status of this EIP has been finalized as Final.