From b48144807188afa83491d31c1825c68cf236737a Mon Sep 17 00:00:00 2001 From: spencer Date: Wed, 11 Mar 2026 20:32:40 +0000 Subject: [PATCH 1/2] feat(tests): port goevmlab regression tests for EELS consensus bugs (#2457) * feat(tests): port goevmlab regression tests for EELS consensus bugs (#1482) * fix(tests): address review comments for goevmlab regression tests * fix(tests): use pytest.mark.parametrize for create opcode * fix(tests): remove redundant env=Environment() from tstore create context tests --- .../test_tstorage_create_contexts.py | 90 +++++++++++++++++-- .../test_tstorage_execution_contexts.py | 48 ++++++++++ .../eip3860_initcode/test_initcode.py | 53 +++++++++++ 3 files changed, 185 insertions(+), 6 deletions(-) diff --git a/tests/cancun/eip1153_tstore/test_tstorage_create_contexts.py b/tests/cancun/eip1153_tstore/test_tstorage_create_contexts.py index 6161c2caa0a..1dc82ea1ed4 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_create_contexts.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_create_contexts.py @@ -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 @@ -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) diff --git a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py index 3d2111fd3d0..b1ef41469d9 100644 --- a/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py +++ b/tests/cancun/eip1153_tstore/test_tstorage_execution_contexts.py @@ -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) diff --git a/tests/shanghai/eip3860_initcode/test_initcode.py b/tests/shanghai/eip3860_initcode/test_initcode.py index 952f3727cf5..951741420d4 100644 --- a/tests/shanghai/eip3860_initcode/test_initcode.py +++ b/tests/shanghai/eip3860_initcode/test_initcode.py @@ -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) From 227d12d3389279fafeb3735ade6fe022940217a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 11 Mar 2026 23:16:31 +0100 Subject: [PATCH 2/2] feat(tests): port stChainId static tests and delete originals (#2478) --- .../istanbul/eip1344_chainid/test_chainid.py | 5 ++ .../static/state_tests/stChainId/__init__.py | 4 -- .../state_tests/stChainId/chainIdFiller.json | 51 ------------------ .../stChainId/chainIdGasCostFiller.json | 53 ------------------- 4 files changed, 5 insertions(+), 108 deletions(-) delete mode 100644 tests/static/state_tests/stChainId/__init__.py delete mode 100644 tests/static/state_tests/stChainId/chainIdFiller.json delete mode 100644 tests/static/state_tests/stChainId/chainIdGasCostFiller.json diff --git a/tests/istanbul/eip1344_chainid/test_chainid.py b/tests/istanbul/eip1344_chainid/test_chainid.py index 5d365cc2b70..963a7b3ba4e 100644 --- a/tests/istanbul/eip1344_chainid/test_chainid.py +++ b/tests/istanbul/eip1344_chainid/test_chainid.py @@ -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, diff --git a/tests/static/state_tests/stChainId/__init__.py b/tests/static/state_tests/stChainId/__init__.py deleted file mode 100644 index 9c81318aef8..00000000000 --- a/tests/static/state_tests/stChainId/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -"""Static State Tests Fillers from ethereum/tests repo.""" - -REFERENCE_SPEC_GIT_PATH = "" -REFERENCE_SPEC_VERSION = "" diff --git a/tests/static/state_tests/stChainId/chainIdFiller.json b/tests/static/state_tests/stChainId/chainIdFiller.json deleted file mode 100644 index 1876280e99b..00000000000 --- a/tests/static/state_tests/stChainId/chainIdFiller.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "chainId": { - "env": { - "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty": "0x20000", - "currentGasLimit": "10000000000", - "currentNumber": "1", - "currentTimestamp": "1000" - }, - "expect": [ - { - "indexes": { - "data": -1, - "gas": -1, - "value": -1 - }, - "network": [">=Cancun"], - "result": { - "": { - "storage": { - "0x01": "0x01" - } - } - } - } - ], - "pre": { - "": { - "balance": "", - "code": "{ [[ 1 ]] (CHAINID) }", - "nonce": "0", - "storage": {} - }, - "": { - "balance": "1000000000000000000000", - "code": "", - "nonce": "0", - "storage": {} - } - }, - "transaction": { - "data": ["0x"], - "gasLimit": ["100000"], - "gasPrice": "10", - "nonce": "0", - "secretKey": "", - "to": "", - "value": ["0"] - } - } -} diff --git a/tests/static/state_tests/stChainId/chainIdGasCostFiller.json b/tests/static/state_tests/stChainId/chainIdGasCostFiller.json deleted file mode 100644 index b98df1423cb..00000000000 --- a/tests/static/state_tests/stChainId/chainIdGasCostFiller.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "chainIdGasCost": { - "env": { - "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "currentDifficulty": "0x20000", - "currentGasLimit": "10000000000", - "currentNumber": "1", - "currentTimestamp": "1000" - }, - "expect": [ - { - "indexes": { - "data": -1, - "gas": -1, - "value": -1 - }, - "network": [">=Cancun"], - "result": { - "": { - "storage": { - "0x01": "2" - } - } - } - } - ], - "pre": { - "": { - "balance": "", - "//code": "record the gas, run the operation, then record the gas again. Drop output, subtract 2 for GAS, what is left is the cost of the operation, store in storage 0x01.", - "code": "(asm GAS CHAINID GAS SWAP1 POP SWAP1 SUB 2 SWAP1 SUB 0x01 SSTORE)", - "nonce": "0", - "storage": {} - }, - "": { - "balance": "1000000000000000000000", - "code": "", - "nonce": "0", - "storage": {} - } - }, - "transaction": { - "data": ["0x"], - "gasLimit": ["100000"], - "gasPrice": "10", - "nonce": "0", - "secretKey": "", - "to": "", - "value": ["0"] - } - } - } -