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
Expand Up @@ -10,6 +10,7 @@
| `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_high_jumpdest` | Enforce JUMP destination validity and code execution past the old size limits | Deploy a `MAX_CODE_SIZE` contract that jumps near the new limit (far beyond the old 24 KiB code and 48 KiB initcode limits), to a real `JUMPDEST` or to a `PUSH1` byte, and call it via a caller that records the call result. | Valid `JUMPDEST`: jump succeeds, the contract executes at the high offset and stores a sentinel. Non-`JUMPDEST`: jump is rejected, call fails, nothing is stored. | ✅ 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_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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,65 @@ def test_warm_after_failed_create_over_max_code_size(
}

state_test(pre=pre, tx=tx, post=post)


@pytest.mark.parametrize(
"valid_jumpdest",
[
pytest.param(True, id="valid_high_jumpdest"),
pytest.param(False, id="invalid_high_dest"),
],
)
def test_max_code_size_high_jumpdest(
state_test: StateTestFiller,
pre: Alloc,
fork: Fork,
valid_jumpdest: bool,
) -> None:
"""
Ensure jump destination validity is enforced past the old size limits.

Deploy a `MAX_CODE_SIZE` contract that stores a sentinel and then jumps
near the new limit, far beyond the old 24 KiB code and 48 KiB initcode
limits, then call it through a caller that records the call's success:

- ``valid_high_jumpdest``: the target byte is a real ``JUMPDEST``, so the
jump succeeds, the frame returns, and the sentinel store is kept.
- ``invalid_high_dest``: the target byte is a ``STOP`` (not a
``JUMPDEST``), so the jump is rejected, the frame reverts, and the
sentinel store is discarded.

A client whose jumpdest analysis or code execution does not cover the
full new code range fails one of the two cases. No existing test
executes a contract at a program counter beyond the old limit.
"""
if valid_jumpdest:
tail = Op.JUMPDEST
else:
# A bare STOP, not a JUMPDEST: jumping here is invalid. A client that
# wrongly accepts it halts normally and keeps the prefix store (1).
tail = Op.STOP

dest = fork.max_code_size() - len(tail)
push_size = (dest.bit_length() + 7) // 8
push_op = getattr(Op, f"PUSH{push_size}")
prefix = Op.SSTORE(0, 1) + push_op(dest) + Op.JUMP
target_code = prefix + Op.INVALID * (dest - len(prefix)) + tail
assert len(target_code) == fork.max_code_size()

target = pre.deploy_contract(target_code)
caller = pre.deploy_contract(
Op.SSTORE(0, Op.CALL(gas=Op.GAS, address=target)) + Op.STOP
)

tx = Transaction(sender=pre.fund_eoa(), to=caller)

# Valid: jump completes, call succeeds (1), and the store is kept.
# Invalid: jump reverts, call fails (0), and nothing is stored.
stored = 1 if valid_jumpdest else 0
post = {
caller: Account(storage={0: stored}),
target: Account(storage={0: stored}),
}

state_test(pre=pre, tx=tx, post=post)
Loading