BEP217: Implement EIP3860 Limit and meter initcode #217
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
BEP-217: Implement EIP3860 Limit and meter initcode
1. Summary
As part of Shanghai upgrade, EIP-3860: Limit and meter initcode is required to be implemented to BSC.
2. Abstract
The change introduces a maximum size limit for initcode (MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE = 49152). Which extended the EIP-170: Contract code size limit
Furthermore, EIP-3860 introduced a charge of 2 gas for every 32-byte chunk of initcode to represent the cost of jumpdest-analysis.
Lastly, the size limit results in the nice-to-have property that EVM code size, code offset (PC), and jump offset fits a 16-bit value.
3. Motivation
Refer to EIP-3860 description:
During contract creation the client has to perform jumpdest-analysis on the initcode prior to execution. The work performed scales linearly with the size of the initcode. This work currently is not metered, nor is there a protocol enforced upper bound for the size.
There are three costs charged today:
Only the first cost applies to initcode, but only in the case of contract creation transactions. For the case of CREATE/CREATE2 there is no such cost, and it is possible to programmatically generate variations of initcode in a relatively cheap manner. In the past it was possible to craft malicious initcode due to a vulnerability fixed in 2017 by geth 1.6.5.
Furthermore, the lack of a limit has caused lengthy discussions for some EVM proposals, influencing the design, or even causing a delay or cancellation of a feature.
It is motivated by three reasons:
4. Specificaton
Parameters
Where MAX_CODE_SIZE is defined by EIP-170 as 24576.
We define initcode_cost(initcode) to equal INITCODE_WORD_COST * ceil(len(initcode) / 32).
Rules
5. Rationale
Gas cost constant
The value of INITCODE_WORD_COST is selected based on performance benchmarks of differing worst-cases per implementation. The baseline for the benchmarks is the performance of KECCAK256 hashing in geth 1.10.9, which matches the 70 Mgas/s gas limit target on a 4.0 GHz x86_64 CPU.
Gas cost per word (32-byte chunk)
We have chosen the cost of 2 gas per word based on Geth’s implementation and comparing with KECCAK256 performance. This means the per byte cost is 0.0625. While fractional gas costs are not permitted in the EVM, we can approximate it by charging per-word.
Moreover, calculating gas per word is compatible with the calculation of CREATE2’s hashcost of EIP-1014. Therefore, the same implementation may be used for CREATE and CREATE2 with different cost constants: before activation 0 for CREATE and 6 for CREATE2, after activation 2 for CREATE and 6 + 2 for CREATE2.
Reason for size limit of initcode
Estimating and creating worst case scenarios is easier with an upper bound in place, given one parameter for the search is greatly reduced. This allows for selecting a much more optimistic gas per byte.
Should there be no upper bound, the cost would need to be higher accounting for unknown unknowns. Given most initcode (TODO: state maximum initcode size resulting in deployment seen on mainnet here) does not exceed the proposed limit, penalizing contracts by overly conservative costs seems unnecessary.
Effect of size limit of initcode
In most, if not all cases when a new contract is being created, the resulting runtime code is copied from the initcode itself. For the basic case the 2 * MAX_CODE_SIZE limit allows MAX_CODE_SIZE for runtime code and another MAX_CODE_SIZE for contract constructor code. However, the limit may have practical implications for cases where multiple contracts are deployed in a single create transaction.
Initcode cost for create transaction
The initcode cost for create transaction data (0.0625 gas per byte) is negligible compared to the transaction data cost (4 or 16 gas per byte). Despite that, we decided to include it in the specification for consistency, and more importantly for forward compatibility.
How to report initcode limit violation?
We specified that initcode size limit violation for CREATE/CREATE2 results in exceptional abort of the execution. This places it in the group of early out-of-gas checks, including: stack underflow, memory expansion, static call violation, initcode hashing cost, and initcode cost introduced by this EIP. They precede the later “light” checks: call depth and balance. The choice gives consistency to the order of checks and lowers implementation complexity (out-of-gas checks can be performed in any order).
6. Backwards Compatibility
This BEP requires a “network upgrade”, since it modifies consensus rules.
Already deployed contracts should not be affected, but certain transactions (with initcode beyond the proposed limit) would still be includable in a block, but result in an exceptional abort.
7. Test Cases
Tests should include the following cases:
8. License
The content is licensed under CC0.
9. Reference
Most description of this BEP refer to EIP-3860:
Martin Holst Swende (@holiman), Paweł Bylica (@chfast), Alex Beregszaszi (@axic), Andrei Maiboroda (@gumb0), "EIP-3860: Limit and meter initcode [DRAFT]," Ethereum Improvement Proposals, no. 3860, July 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3860.