From bd22e0839a996d1c085cd6804d2d0332f7834b2a Mon Sep 17 00:00:00 2001 From: kclowes Date: Thu, 14 May 2026 14:29:41 -0600 Subject: [PATCH 1/3] feat(tests): EIP-7843 Add test cases for multi-blocks and fork transition --- tests/amsterdam/eip7843_slotnum/spec.py | 22 ++++++ .../eip7843_slotnum/test_eip_mainnet.py | 53 ++++++++++++++ .../eip7843_slotnum/test_fork_transition.py | 73 +++++++++++++++++++ .../amsterdam/eip7843_slotnum/test_slotnum.py | 54 ++++++++++---- 4 files changed, 187 insertions(+), 15 deletions(-) create mode 100644 tests/amsterdam/eip7843_slotnum/spec.py create mode 100644 tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py create mode 100644 tests/amsterdam/eip7843_slotnum/test_fork_transition.py diff --git a/tests/amsterdam/eip7843_slotnum/spec.py b/tests/amsterdam/eip7843_slotnum/spec.py new file mode 100644 index 00000000000..a96172631f4 --- /dev/null +++ b/tests/amsterdam/eip7843_slotnum/spec.py @@ -0,0 +1,22 @@ +"""Reference spec for [EIP-7843: SLOTNUM](https://eips.ethereum.org/EIPS/eip-7843).""" + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class ReferenceSpec: + """Reference specification.""" + + git_path: str + version: str + + +ref_spec_7843 = ReferenceSpec( + git_path="EIPS/eip-7843.md", + version="6bc5d6b7acbc016a79fa573f98975093b5c2ca52", +) + + +@dataclass(frozen=True) +class Spec: + """Constants and parameters from EIP-7843.""" diff --git a/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py b/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py new file mode 100644 index 00000000000..90924e6f173 --- /dev/null +++ b/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py @@ -0,0 +1,53 @@ +""" +Mainnet marked execute checklist tests for +[EIP-7843: SLOTNUM](https://eips.ethereum.org/EIPS/eip-7843). +""" + +import pytest +from execution_testing import ( + Account, + Alloc, + Environment, + Op, + StateTestFiller, + Transaction, +) + +from .spec import ref_spec_7843 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7843.git_path +REFERENCE_SPEC_VERSION = ref_spec_7843.version + +pytestmark = [pytest.mark.valid_at("EIP7843"), pytest.mark.mainnet] + + +def test_slotnum_mainnet( + state_test: StateTestFiller, + pre: Alloc, +) -> None: + """ + Test that SLOTNUM is callable and returns a non-zero slot number. + + Asserts on ``ISZERO(ISZERO(SLOTNUM))`` rather than the slot value itself + so the test remains valid when ``execute``-ed against a live network, + where the slot number is whatever the consensus layer transmits and + cannot be controlled by the test. + """ + contract = pre.deploy_contract( + code=Op.SSTORE(0, Op.ISZERO(Op.ISZERO(Op.SLOTNUM))), + storage={"0x00": "0xdeadbeef"}, + ) + tx = Transaction( + ty=0x02, + to=contract, + sender=pre.fund_eoa(), + gas_limit=200_000, + ) + post = {contract: Account(storage={"0x00": 1})} + + state_test( + env=Environment(slot_number=1), + pre=pre, + tx=tx, + post=post, + ) diff --git a/tests/amsterdam/eip7843_slotnum/test_fork_transition.py b/tests/amsterdam/eip7843_slotnum/test_fork_transition.py new file mode 100644 index 00000000000..867f065ac95 --- /dev/null +++ b/tests/amsterdam/eip7843_slotnum/test_fork_transition.py @@ -0,0 +1,73 @@ +"""Tests for EIP-7843 fork transition behavior.""" + +import pytest +from execution_testing import ( + Account, + Alloc, + Block, + BlockchainTestFiller, + Op, + Transaction, +) + +from .spec import ref_spec_7843 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7843.git_path +REFERENCE_SPEC_VERSION = ref_spec_7843.version + + +@pytest.mark.valid_at_transition_to("EIP7843") +def test_slotnum_at_fork_transition( + blockchain_test: BlockchainTestFiller, + pre: Alloc, +) -> None: + """ + Test SLOTNUM behavior across the EIP-7843 fork transition. + + Before EIP-7843, opcode 0x4B is undefined: execution halts with an + invalid-opcode exception and consumes all gas, so no SSTORE is observed. + + From EIP-7843 onward, SLOTNUM pushes the block's slot number provided + by the consensus layer and the SSTORE succeeds. + + The contract keys storage by block number so each block's outcome is + independently visible in the final post-state: + + * block 1 (pre-fork): slot 1 stays 0 — execution halted before SSTORE. + * block 2 (transition): slot 2 == ``at_fork_slot``. + * block 3 (post-fork): slot 3 == ``post_fork_slot``. + """ + sender = pre.fund_eoa() + contract = pre.deploy_contract(Op.SSTORE(Op.NUMBER, Op.SLOTNUM) + Op.STOP) + + at_fork_slot = 200 + post_fork_slot = 201 + + blocks = [ + Block( + timestamp=14_999, + txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)], + ), + Block( + timestamp=15_000, + slot_number=at_fork_slot, + txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)], + ), + Block( + timestamp=15_001, + slot_number=post_fork_slot, + txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)], + ), + ] + + post = { + contract: Account( + storage={ + 1: 0, + 2: at_fork_slot, + 3: post_fork_slot, + }, + ), + } + + blockchain_test(pre=pre, blocks=blocks, post=post) diff --git a/tests/amsterdam/eip7843_slotnum/test_slotnum.py b/tests/amsterdam/eip7843_slotnum/test_slotnum.py index 65358ebb31c..c3387c957e4 100644 --- a/tests/amsterdam/eip7843_slotnum/test_slotnum.py +++ b/tests/amsterdam/eip7843_slotnum/test_slotnum.py @@ -1,11 +1,11 @@ """Tests for EIP-7843 (SLOTNUM).""" -from dataclasses import dataclass - import pytest from execution_testing import ( Account, Alloc, + Block, + BlockchainTestFiller, Environment, Fork, Op, @@ -13,19 +13,7 @@ Transaction, ) - -@dataclass(frozen=True) -class ReferenceSpec: - """Reference specification.""" - - git_path: str - version: str - - -ref_spec_7843 = ReferenceSpec( - git_path="EIPS/eip-7843.md", - version="6bc5d6b7acbc016a79fa573f98975093b5c2ca52", -) +from .spec import ref_spec_7843 REFERENCE_SPEC_GIT_PATH = ref_spec_7843.git_path REFERENCE_SPEC_VERSION = ref_spec_7843.version @@ -124,3 +112,39 @@ def test_slotnum_gas_cost( tx=tx, post=post, ) + + +def test_slotnum_distinct_per_block( + blockchain_test: BlockchainTestFiller, + pre: Alloc, +) -> None: + """ + Test that SLOTNUM returns each block's own slot number. + + Runs four consecutive blocks with deliberately non-monotonic slot + numbers to disprove any caching or ordering assumption in the opcode + implementation. Each block runs the same contract, which keys storage + by block ``NUMBER`` so every block's outcome is independently visible + in the final post-state. + """ + sender = pre.fund_eoa() + contract = pre.deploy_contract(Op.SSTORE(Op.NUMBER, Op.SLOTNUM) + Op.STOP) + + # Non-monotonic on purpose: decrease, increase, jump to large value. + slot_numbers = [100, 42, 7, 2**32] + + blocks = [ + Block( + slot_number=slot, + txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)], + ) + for slot in slot_numbers + ] + + post = { + contract: Account( + storage={i + 1: slot for i, slot in enumerate(slot_numbers)}, + ), + } + + blockchain_test(pre=pre, blocks=blocks, post=post) From 227bbb75d467ae5c11fab876695d6ee58514fd77 Mon Sep 17 00:00:00 2001 From: kclowes Date: Mon, 18 May 2026 14:47:46 -0600 Subject: [PATCH 2/3] PR feedback --- .../eip7843_slotnum/test_eip_mainnet.py | 4 ++-- .../eip7843_slotnum/test_fork_transition.py | 21 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py b/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py index 90924e6f173..98149b2bcb0 100644 --- a/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py +++ b/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py @@ -34,7 +34,7 @@ def test_slotnum_mainnet( cannot be controlled by the test. """ contract = pre.deploy_contract( - code=Op.SSTORE(0, Op.ISZERO(Op.ISZERO(Op.SLOTNUM))), + code=Op.POP(Op.SLOTNUM) + Op.SSTORE(0, 1), storage={"0x00": "0xdeadbeef"}, ) tx = Transaction( @@ -46,7 +46,7 @@ def test_slotnum_mainnet( post = {contract: Account(storage={"0x00": 1})} state_test( - env=Environment(slot_number=1), + env=Environment(), pre=pre, tx=tx, post=post, diff --git a/tests/amsterdam/eip7843_slotnum/test_fork_transition.py b/tests/amsterdam/eip7843_slotnum/test_fork_transition.py index 867f065ac95..210e3a5001b 100644 --- a/tests/amsterdam/eip7843_slotnum/test_fork_transition.py +++ b/tests/amsterdam/eip7843_slotnum/test_fork_transition.py @@ -45,21 +45,16 @@ def test_slotnum_at_fork_transition( blocks = [ Block( - timestamp=14_999, + timestamp=ts, + slot_number=slot, txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)], - ), - Block( - timestamp=15_000, - slot_number=at_fork_slot, - txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)], - ), - Block( - timestamp=15_001, - slot_number=post_fork_slot, - txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)], - ), + ) + for ts, slot in [ + (14_999, None), + (15_000, at_fork_slot), + (15_001, post_fork_slot), + ] ] - post = { contract: Account( storage={ From 16872ecea8825a68f829306a9892503fbdf2b297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=94=A1=E4=BD=B3=E8=AA=A0=20Louis=20Tsai?= <72684086+LouisTsai-Csie@users.noreply.github.com> Date: Tue, 19 May 2026 12:55:06 +0800 Subject: [PATCH 3/3] doc: update comment --- tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py b/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py index 98149b2bcb0..0cca8f2cd21 100644 --- a/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py +++ b/tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py @@ -28,7 +28,7 @@ def test_slotnum_mainnet( """ Test that SLOTNUM is callable and returns a non-zero slot number. - Asserts on ``ISZERO(ISZERO(SLOTNUM))`` rather than the slot value itself + Asserts on ``POP(SLOTNUM)`` rather than the slot value itself so the test remains valid when ``execute``-ed against a live network, where the slot number is whatever the consensus layer transmits and cannot be controlled by the test.