Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""
EIP-7954: Increase Maximum Contract Size.

Raise the maximum contract code size from 24KiB to 32KiB and initcode size from
48KiB to 64KiB.
Raise the maximum contract code size from 24KiB to 64KiB and initcode size from
48KiB to 128KiB.

https://eips.ethereum.org/EIPS/eip-7954
"""
Expand All @@ -15,10 +15,10 @@ class EIP7954(BaseFork):

@classmethod
def max_code_size(cls) -> int:
"""Max contract code size is 32 KiB."""
return 32 * 1024
"""Max contract code size is 64 KiB."""
return 64 * 1024

@classmethod
def max_initcode_size(cls) -> int:
"""Max initcode size is 64 KiB."""
return 64 * 1024
"""Max initcode size is 128 KiB."""
return 128 * 1024
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,20 @@ def __new__(
initcode = initcode_prefix
code_length = len(deploy_code)

# PUSH2: length=<bytecode length>
initcode += Op.PUSH2(code_length)
# PUSHN: length=<bytecode length>. PUSH2 by default, widening to a
# larger PUSH only when the deploy code exceeds 64KiB - 1 bytes.
push_length_size = max(2, (code_length.bit_length() + 7) // 8)
push_length = getattr(Op, f"PUSH{push_length_size}")
initcode += push_length(code_length)

# PUSH1: offset=0
initcode += Op.PUSH1(0)

# DUP2
initcode += Op.DUP2

# PUSH1: initcode_length=11 + len(initcode_prefix_bytes) (constant)
no_prefix_length = 0x0B
# PUSH1: initcode_length=9 + push_length_size + initcode_prefix_bytes
no_prefix_length = 0x09 + push_length_size
assert no_prefix_length + len(initcode_prefix) <= 0xFF, (
"initcode prefix too long"
)
Expand Down
2 changes: 1 addition & 1 deletion src/ethereum/forks/amsterdam/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
from .runtime import get_valid_jump_destinations

STACK_DEPTH_LIMIT = Uint(1024)
MAX_CODE_SIZE = 0x8000
MAX_CODE_SIZE = 0x10000
MAX_INIT_CODE_SIZE = 2 * MAX_CODE_SIZE


Expand Down
2 changes: 1 addition & 1 deletion tests/amsterdam/eip7954_increase_max_contract_size/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ class ReferenceSpec:

ref_spec_7954 = ReferenceSpec(
git_path="EIPS/eip-7954.md",
version="b1f5bf8f70ba9306400f5e13313f781c35acc860",
version="1dc9bc870f864d7ad1095fc73ba8ca098d02c732",
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
| `test_max_initcode_size` | Enforce new `MAX_INITCODE_SIZE` boundary for contract creation transactions | Alice sends creation transactions with initcode at the new max and one byte over. | New max: transaction accepted, contract deployed. Over max: transaction rejected. | ✅ Completed |
| `test_max_initcode_size_via_create` | Enforce new `MAX_INITCODE_SIZE` boundary via CREATE/CREATE2 opcodes | Same as above but initcode is passed through a factory contract using CREATE and CREATE2. | New max: child contract deployed. Over max: CREATE returns 0, child contract does not exist. | ✅ Completed |
| `test_max_initcode_size_gas_metering` | Verify initcode gas metering at the new max (transaction level) | Alice sends a creation transaction with max-size initcode. Gas limit set to exact intrinsic cost, then one short. | Exact gas: contract deployed. One short: transaction rejected. | ✅ Completed |
| `test_max_initcode_size_gas_metering_via_create` | Verify initcode gas metering at the new max (opcode level) | Caller forwards computed exact gas to a factory that runs CREATE with max-size initcode. Tested with exact gas and one short. | Exact gas: CREATE succeeds, contract deployed. One short: factory runs out of gas. | ✅ Completed |
| `test_max_code_size_deposit_gas` | Verify code deposit gas is charged correctly at the new max | Alice deploys a contract with exactly `MAX_CODE_SIZE` bytes. Gas set to exact deposit cost, then one short. | Exact gas: contract deployed. One short: deployment fails (out of gas during code deposit). | ✅ Completed |
| `test_max_code_size_external_opcodes` | Verify external code opcodes work with max-size contracts | Deterministically pre-deploy a max-size self-checking contract. Call it to run EXTCODESIZE, EXTCODEHASH, and EXTCODECOPY on itself via ADDRESS. | Each opcode returns the correct value for the max-size contract. | ✅ Completed |
| `test_max_code_size_self_opcodes` | Verify self code opcodes work with max-size contracts | Pre-deploy a max-size contract with CODESIZE and CODECOPY checker logic. Call via DELEGATECALL so opcodes operate on the large contract's own code. | CODESIZE returns the correct length, CODECOPY produces the correct hash. | ✅ Completed |
| `test_max_code_size_with_max_initcode` | Deploy max-size code when initcode is also at max size | Alice deploys a contract with `MAX_CODE_SIZE` bytes of runtime code using initcode padded to `MAX_INITCODE_SIZE`. | Contract deployed with the full max-size runtime code. | ✅ Completed |
| `test_max_code_size_fork_transition` | New `MAX_CODE_SIZE` activates exactly at the fork boundary | Before the fork, deploy a contract with the new `MAX_CODE_SIZE` bytes of runtime code. After the fork, attempt the same deployment. | Pre-fork: deployment fails (exceeds old limit). Post-fork: deployment succeeds. | ✅ Completed |
| `test_warm_after_failed_create_over_max_code_size` | A failed CREATE/CREATE2 over max code size leaves the would-be address warm | A creator runs CREATE/CREATE2 whose initcode returns `MAX_CODE_SIZE + 1` bytes; a checker then measures the gas of a `BALANCE` on that address. | The address is warm: the post-RETURN size-check rejection still leaves it in the access list. | ✅ Completed |
| `test_max_code_size_fork_transition` | New `MAX_CODE_SIZE` activates exactly at the fork boundary | Before and after the fork, deploy a contract one byte over the parent fork's max code size (valid under the new limit; its initcode stays within both forks' initcode limits). | Pre-fork: deployment fails at code deposit (exceeds old limit). Post-fork: deployment succeeds. | ✅ Completed |
| `test_max_code_size_via_create_fork_transition` | New `MAX_CODE_SIZE` activates at the fork boundary via CREATE/CREATE2 opcodes | Same as above but deployment is done through a factory contract using CREATE and CREATE2. | Pre-fork: child contract does not exist. Post-fork: child contract deployed. | ✅ Completed |
| `test_max_initcode_size_fork_transition` | New `MAX_INITCODE_SIZE` activates exactly at the fork boundary for transactions | Before the fork, send a creation transaction with the new `MAX_INITCODE_SIZE` bytes of initcode. After the fork, send the same transaction. | Pre-fork: block rejected (initcode exceeds old limit). Post-fork: transaction accepted, contract deployed. | ✅ Completed |
| `test_max_initcode_size_via_create_fork_transition` | New `MAX_INITCODE_SIZE` activates at the fork boundary via CREATE/CREATE2 opcodes | Same as above but initcode is passed through a factory contract using CREATE and CREATE2. | Pre-fork: CREATE fails (initcode exceeds old limit). Post-fork: child contract deployed. | ✅ Completed |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ def test_max_code_size_fork_transition(
fork: TransitionFork,
) -> None:
"""Ensure the new max code size limit activates at the fork boundary."""
code_size = fork.transitions_to().max_code_size()
parent = fork.transitions_from()
assert parent is not None, "Parent fork must be defined for this test"
code_size = parent.max_code_size() + 1
deploy_code = Op.JUMPDEST * code_size
initcode = Initcode(deploy_code=deploy_code)

Expand Down Expand Up @@ -87,7 +89,9 @@ def test_max_code_size_via_create_fork_transition(
create_opcode: Op,
) -> None:
"""Ensure the new max code size limit activates at the fork via opcodes."""
code_size = fork.transitions_to().max_code_size()
parent = fork.transitions_from()
assert parent is not None, "Parent fork must be defined for this test"
code_size = parent.max_code_size() + 1
deploy_code = Op.JUMPDEST * code_size
initcode = Initcode(deploy_code=deploy_code)
initcode_bytes = bytes(initcode)
Expand Down
6 changes: 6 additions & 0 deletions tests/ported_static/stRandom2/test_random_statetest646.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
["state_tests/stRandom2/randomStatetest646Filler.json"],
)
@pytest.mark.valid_from("Cancun")
@pytest.mark.valid_before("EIP7954")
@pytest.mark.pre_alloc_mutable
def test_random_statetest646(
state_test: StateTestFiller,
Expand Down Expand Up @@ -96,6 +97,11 @@ def test_random_statetest646(
value=0x5684B90A,
)

# Capped at EIP7954: the 0x13FFA-byte CREATE initcode exceeds
# MAX_INITCODE_SIZE only before the limit is raised, so the inner CREATE
# reverts the frame and the created address never persists. EIP-7954's
# raised limit (where this initcode is valid) is covered by the dedicated
# tests in tests/amsterdam/eip7954_increase_max_contract_size.
post = {
sender: Account(storage={}, code=b"", nonce=1),
compute_create_address(
Expand Down
Loading