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
90 changes: 84 additions & 6 deletions tests/cancun/eip1153_tstore/test_tstorage_create_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@

import pytest
from execution_testing import (
AccessList,
Account,
Address,
Alloc,
Bytecode,
Environment,
Initcode,
Op,
StateTestFiller,
Transaction,
compute_create_address,
)
from execution_testing import Macros as Om

from . import CreateOpcodeParams, PytestParameterEnum
from .spec import ref_spec_1153
Expand Down Expand Up @@ -257,9 +258,86 @@ def test_contract_creation(
),
}

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


@pytest.mark.ported_from(
[
"https://github.com/holiman/goevmlab/blob/master/examples/tstore_bug-2/main.go",
],
)
@pytest.mark.parametrize("create_opcode", [Op.CREATE, Op.CREATE2])
def test_tstore_rollback_on_failed_create(
state_test: StateTestFiller,
pre: Alloc,
create_opcode: Op,
) -> None:
"""
Test TSTORE is rolled back after failed CREATE/CREATE2 initcode.

Regression test for
https://github.com/ethereum/execution-specs/issues/917

Initcode does TLOAD(1) to compute a return size, then does
TSTORE(1, 0x6000), then returns data of the computed size.
When TLOAD(1) is 0, the return size is 0x600a (exceeds max code
size 0x6000), so creation fails.

The caller invokes CREATE/CREATE2 twice with the same initcode.
If TSTORE from the first (failed) creation is properly rolled
back, the second creation also sees TLOAD(1)==0 and fails the
same way. If not rolled back, TLOAD(1)==0x6000 and the second
creation succeeds.
"""
# Initcode:
# return_size = 0x600a - TLOAD(1)
# TSTORE(1, 0x6000)
# RETURN(offset=0, size=return_size)
#
# TLOAD(1)==0: return_size = 0x600a > max code size -> fail
# TLOAD(1)==0x6000: return_size = 0x0a <= max code size -> succeed
initcode = (
Op.TLOAD(1)
+ Op.PUSH2(0x600A)
+ Op.SUB
+ Op.TSTORE(1, 0x6000)
+ Op.PUSH1(0)
+ Op.RETURN
)
initcode_bytes = bytes(initcode)
initcode_len = len(initcode_bytes)

caller_code = (
Om.MSTORE(initcode_bytes, 0)
+ Op.SSTORE(
0,
create_opcode(0, 0, initcode_len, 0)
if create_opcode == Op.CREATE2
else create_opcode(0, 0, initcode_len),
)
+ Op.SSTORE(
1,
create_opcode(0, 0, initcode_len, 0)
if create_opcode == Op.CREATE2
else create_opcode(0, 0, initcode_len),
)
)
caller_address = pre.deploy_contract(caller_code, storage={0: 1, 1: 1})

sender = pre.fund_eoa()
tx = Transaction(
sender=sender,
to=caller_address,
gas_limit=16_000_000,
access_list=[
AccessList(address=caller_address, storage_keys=[0, 1]),
],
)

post = {
# Both creations fail because TSTORE is rolled back;
# initial storage {0: 1, 1: 1} is overwritten to zeros
caller_address: Account(storage={0: 0, 1: 0}),
}

state_test(pre=pre, post=post, tx=tx)
48 changes: 48 additions & 0 deletions tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,3 +385,51 @@ def test_subcall(
- `STATICCALL`
"""
state_test(env=env, pre=pre, post=post, tx=tx)


@pytest.mark.ported_from(
[
"https://github.com/holiman/goevmlab/blob/master/examples/tstore_bug/main.go",
],
)
def test_tstore_rollback_on_callcode_revert(
state_test: StateTestFiller,
pre: Alloc,
) -> None:
"""
Test TSTORE is rolled back after CALLCODE sub-call reverts.

Regression test for
https://github.com/ethereum/execution-specs/issues/911

Contract `callee` does TSTORE(4, 1), calls a precompile, then
REVERTs. Contract `caller` uses CALLCODE to invoke `callee`, then
checks TLOAD(4). Because CALLCODE executes in the caller's context
and the sub-call reverted, TLOAD(4) must return 0.
"""
callee_code = (
Op.TSTORE(4, 1)
+ Op.CALL(address=0x06) # call identity precompile
+ Op.POP
+ Op.REVERT(0, 0)
)
callee_address = pre.deploy_contract(callee_code)

caller_code = Op.SSTORE(
0, Op.CALLCODE(address=callee_address)
) + Op.SSTORE(1, Op.TLOAD(4))
caller_address = pre.deploy_contract(caller_code)

sender = pre.fund_eoa()
tx = Transaction(
sender=sender,
to=caller_address,
gas_limit=1_000_000,
)

post = {
# CALLCODE returns 0 (reverted), TLOAD(4) = 0 (rolled back)
caller_address: Account(storage={0: 0, 1: 0}),
}

state_test(pre=pre, post=post, tx=tx)
5 changes: 5 additions & 0 deletions tests/istanbul/eip1344_chainid/test_chainid.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
else None
)
)
@pytest.mark.ported_from(
[
"https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stChainId/chainIdFiller.json",
],
)
@pytest.mark.valid_from("Istanbul")
def test_chainid(
state_test: StateTestFiller,
Expand Down
53 changes: 53 additions & 0 deletions tests/shanghai/eip3860_initcode/test_initcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,3 +586,56 @@ def test_create_opcode_initcode(
post=post,
tx=tx,
)


@pytest.mark.ported_from(
[
"https://github.com/holiman/goevmlab/blob/master/examples/create2_bug/main.go",
],
)
@pytest.mark.parametrize(
"initcode_oversize,expected_storage_1",
[
pytest.param(True, 0, id="initcode_oversize"),
pytest.param(False, 1, id="initcode_within_limit"),
],
)
def test_create2_oversized_initcode_with_insufficient_balance(
state_test: StateTestFiller,
pre: Alloc,
initcode_oversize: bool,
expected_storage_1: int,
) -> None:
"""
Test CREATE2 with oversized initcode and insufficient balance.

Regression test for
https://github.com/ethereum/execution-specs/issues/914

CREATE2 is called with an endowment of 1123123123 (exceeds the
contract's zero balance). The initcode size check must take
priority over the balance check:

- Initcode too large: consumes all gas, exits scope, SSTORE(1, 1)
is never reached, so storage slot 1 remains 0.
- Initcode within limit: insufficient balance pushes 0, execution
continues, SSTORE(1, 1) executes, so storage slot 1 becomes 1.
"""
initcode_size = 0x20000 if initcode_oversize else 0x100
caller_code = (
Op.CREATE2(1123123123, 0, initcode_size, 0) + Op.POP + Op.SSTORE(1, 1)
)
caller_address = pre.deploy_contract(caller_code)

sender = pre.fund_eoa()
tx = Transaction(
sender=sender,
to=caller_address,
gas_limit=10_000_000,
)

post = {
caller_address: Account(storage={1: expected_storage_1}),
}

state_test(pre=pre, post=post, tx=tx)
4 changes: 0 additions & 4 deletions tests/static/state_tests/stChainId/__init__.py

This file was deleted.

51 changes: 0 additions & 51 deletions tests/static/state_tests/stChainId/chainIdFiller.json

This file was deleted.

53 changes: 0 additions & 53 deletions tests/static/state_tests/stChainId/chainIdGasCostFiller.json

This file was deleted.

Loading