diff --git a/packages/testing/src/execution_testing/forks/forks/eips/amsterdam/eip_7954.py b/packages/testing/src/execution_testing/forks/forks/eips/amsterdam/eip_7954.py index b5c23ce2752..29b8ad22dd6 100644 --- a/packages/testing/src/execution_testing/forks/forks/eips/amsterdam/eip_7954.py +++ b/packages/testing/src/execution_testing/forks/forks/eips/amsterdam/eip_7954.py @@ -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 """ @@ -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 diff --git a/packages/testing/src/execution_testing/tools/tools_code/generators.py b/packages/testing/src/execution_testing/tools/tools_code/generators.py index 9d15d425e59..3a5c79a0e07 100644 --- a/packages/testing/src/execution_testing/tools/tools_code/generators.py +++ b/packages/testing/src/execution_testing/tools/tools_code/generators.py @@ -53,8 +53,11 @@ def __new__( initcode = initcode_prefix code_length = len(deploy_code) - # PUSH2: length= - initcode += Op.PUSH2(code_length) + # PUSHN: 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) @@ -62,8 +65,8 @@ def __new__( # 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" ) diff --git a/src/ethereum/forks/amsterdam/vm/interpreter.py b/src/ethereum/forks/amsterdam/vm/interpreter.py index b8eb6b968db..bb01dad813f 100644 --- a/src/ethereum/forks/amsterdam/vm/interpreter.py +++ b/src/ethereum/forks/amsterdam/vm/interpreter.py @@ -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 diff --git a/tests/amsterdam/eip7954_increase_max_contract_size/spec.py b/tests/amsterdam/eip7954_increase_max_contract_size/spec.py index 7dac2d83625..bef566b2240 100644 --- a/tests/amsterdam/eip7954_increase_max_contract_size/spec.py +++ b/tests/amsterdam/eip7954_increase_max_contract_size/spec.py @@ -13,5 +13,5 @@ class ReferenceSpec: ref_spec_7954 = ReferenceSpec( git_path="EIPS/eip-7954.md", - version="b1f5bf8f70ba9306400f5e13313f781c35acc860", + version="1dc9bc870f864d7ad1095fc73ba8ca098d02c732", ) diff --git a/tests/amsterdam/eip7954_increase_max_contract_size/test_cases.md b/tests/amsterdam/eip7954_increase_max_contract_size/test_cases.md index 68d665d0c82..27ab07655c5 100644 --- a/tests/amsterdam/eip7954_increase_max_contract_size/test_cases.md +++ b/tests/amsterdam/eip7954_increase_max_contract_size/test_cases.md @@ -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 | diff --git a/tests/amsterdam/eip7954_increase_max_contract_size/test_fork_transition.py b/tests/amsterdam/eip7954_increase_max_contract_size/test_fork_transition.py index 10931708eeb..90aabfa5560 100644 --- a/tests/amsterdam/eip7954_increase_max_contract_size/test_fork_transition.py +++ b/tests/amsterdam/eip7954_increase_max_contract_size/test_fork_transition.py @@ -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) @@ -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) diff --git a/tests/ported_static/stRandom2/test_random_statetest646.py b/tests/ported_static/stRandom2/test_random_statetest646.py index 112c3e9e5ee..868470e3bce 100644 --- a/tests/ported_static/stRandom2/test_random_statetest646.py +++ b/tests/ported_static/stRandom2/test_random_statetest646.py @@ -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, @@ -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(