Skip to content
Draft
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
22 changes: 22 additions & 0 deletions tests/amsterdam/eip7843_slotnum/spec.py
Original file line number Diff line number Diff line change
@@ -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."""
53 changes: 53 additions & 0 deletions tests/amsterdam/eip7843_slotnum/test_eip_mainnet.py
Original file line number Diff line number Diff line change
@@ -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,
)
73 changes: 73 additions & 0 deletions tests/amsterdam/eip7843_slotnum/test_fork_transition.py
Original file line number Diff line number Diff line change
@@ -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)
88 changes: 73 additions & 15 deletions tests/amsterdam/eip7843_slotnum/test_slotnum.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
"""Tests for EIP-7843 (SLOTNUM)."""

from dataclasses import dataclass

import pytest
from execution_testing import (
Account,
Alloc,
Block,
BlockchainTestFiller,
Environment,
Fork,
Op,
StateTestFiller,
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
Expand Down Expand Up @@ -124,3 +112,73 @@ 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)


def test_slotnum_repeated_same_slot(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
) -> None:
"""
Test SLOTNUM returns the same value across blocks that share a
``slot_number``.

Guards against a future bug where SLOTNUM caches a stale value between
blocks β€” such a bug would still appear correct on tests with distinct
per-block values, but would be visible here.
"""
sender = pre.fund_eoa()
contract = pre.deploy_contract(Op.SSTORE(Op.NUMBER, Op.SLOTNUM) + Op.STOP)

shared_slot = 555

blocks = [
Block(
slot_number=shared_slot,
txs=[Transaction(sender=sender, to=contract, gas_limit=100_000)],
)
for _ in range(2)
]

post = {
contract: Account(
storage={1: shared_slot, 2: shared_slot},
),
}

blockchain_test(pre=pre, blocks=blocks, post=post)
Loading