Skip to content
Open
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
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Expand cases to test *CALL opcodes causing OOG ([#1703](https://github.com/ethereum/execution-specs/pull/1703)).
- ✨ Add tests for `modexp` and `ripemd` precompiled contracts ([#1691](https://github.com/ethereum/execution-specs/pull/1691)).
- ✨ Add `ecrecover` precompile tests originating form `evmone` unittests ([#1685](https://github.com/ethereum/execution-specs/pull/1685)).
- ✨ Add stack overflow tests and expand `BLOCKHASH` tests ([#1728](https://github.com/ethereum/execution-specs/pull/1728)).

## [v5.3.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.3.0) - 2025-10-09

Expand Down
60 changes: 59 additions & 1 deletion tests/frontier/opcodes/test_all_opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
opcode is supported by the fork supports and fails otherwise.
"""

from typing import Dict
from typing import Dict, Iterator

import pytest
from execution_testing import (
Expand All @@ -19,6 +19,7 @@
Transaction,
UndefinedOpcodes,
)
from execution_testing.forks import Byzantium

REFERENCE_SPEC_GIT_PATH = "N/A"
REFERENCE_SPEC_VERSION = "N/A"
Expand Down Expand Up @@ -135,3 +136,60 @@ def test_cover_revert(state_test: StateTestFiller, pre: Alloc) -> None:
)

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


def fork_opcodes_increasing_stack(
fork: Fork,
) -> Iterator[Op]:
"""
Yields opcodes which are valid for `fork` and increase the operand stack.
"""
for opcode in fork.valid_opcodes():
if opcode.pushed_stack_items > opcode.popped_stack_items:
yield opcode
Comment on lines +147 to +149
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach!



@pytest.mark.parametrize_by_fork("opcode", fork_opcodes_increasing_stack)
@pytest.mark.parametrize("fails", [True, False])
def test_stack_overflow(
state_test: StateTestFiller,
pre: Alloc,
fork: Fork,
opcode: Op,
fails: bool,
env: Environment,
) -> None:
"""Test that opcodes which leave new items on the stack can overflow."""
pre_stack_items = fork.max_stack_height()
if not fails:
pre_stack_items -= (
opcode.pushed_stack_items - opcode.popped_stack_items
)
slot_code_worked = 1
value_code_failed = 0xDEADBEEF
value_code_worked = 1

contract = pre.deploy_contract(
code=Op.SSTORE(slot_code_worked, value_code_worked)
+ Op.PUSH1(0) * pre_stack_items
+ opcode
+ Op.STOP,
storage={slot_code_worked: value_code_failed},
)

tx = Transaction(
gas_limit=100_000,
to=contract,
sender=pre.fund_eoa(),
protected=fork >= Byzantium,
)
expected_storage = {
slot_code_worked: value_code_failed if fails else value_code_worked
}

state_test(
env=env,
pre=pre,
tx=tx,
post={contract: Account(storage=expected_storage)},
)
84 changes: 53 additions & 31 deletions tests/frontier/opcodes/test_blockhash.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,28 @@
Block,
BlockchainTestFiller,
Op,
Storage,
Transaction,
)
from execution_testing.forks import Byzantium
from execution_testing.forks.helpers import Fork


@pytest.mark.valid_from("Frontier")
@pytest.mark.parametrize(
"setup_blocks_num,setup_blocks_empty",
[
pytest.param(0, True, id="no_blocks"),
pytest.param(1, False, id="one_empty_block"),
pytest.param(1, True, id="one_block_with_tx"),
pytest.param(256, True, id="256_empty_blocks"),
],
)
def test_genesis_hash_available(
blockchain_test: BlockchainTestFiller, pre: Alloc
blockchain_test: BlockchainTestFiller,
pre: Alloc,
fork: Fork,
setup_blocks_num: int,
setup_blocks_empty: bool,
) -> None:
"""
Verify BLOCKHASH returns genesis and block 1 hashes.
Expand All @@ -29,45 +43,53 @@ def test_genesis_hash_available(
Bug context: revm blockchaintest runner wasn't inserting block_hashes,
causing failures in tests with BLOCKHASH-derived addresses.
"""
storage = Storage()

# Store ISZERO(BLOCKHASH(0)) and ISZERO(BLOCKHASH(1))
# Both should be 0 (false) if hashes exist
code = Op.SSTORE(
storage.store_next(0), Op.ISZERO(Op.BLOCKHASH(0))
) + Op.SSTORE(storage.store_next(0), Op.ISZERO(Op.BLOCKHASH(1)))
code = Op.SSTORE(0, Op.ISZERO(Op.BLOCKHASH(0))) + Op.SSTORE(
1, Op.ISZERO(Op.BLOCKHASH(1))
)

contract = pre.deploy_contract(code=code)
sender = pre.fund_eoa()

blocks = [
Block(
txs=[
Transaction(
sender=sender,
to=contract,
gas_limit=100_000,
protected=False,
)
]
),
Block(
txs=[
Transaction(
sender=sender,
to=contract,
gas_limit=100_000,
protected=False,
)
]
),
]
blocks = (
[
Block(
txs=[
Transaction(
sender=sender,
to=contract,
gas_limit=100_000,
protected=fork >= Byzantium,
)
]
if not setup_blocks_empty
else []
)
for _ in range(setup_blocks_num)
]
) + (
[
Block(
txs=[
Transaction(
sender=sender,
to=contract,
gas_limit=100_000,
protected=fork >= Byzantium,
)
]
)
]
)

post = {
contract: Account(
storage={
0: 0, # ISZERO(BLOCKHASH(0)) = 0 (genesis hash exists)
1: 0, # ISZERO(BLOCKHASH(1)) = 0 (block 1 hash exists)
# ISZERO(BLOCKHASH(0)) = 0 (genesis hash exists)
0: 1 if setup_blocks_num >= 256 else 0,
# ISZERO(BLOCKHASH(1)) = 0 (if block 1 hash exists)
1: 1 if setup_blocks_num == 0 else 0,
}
)
}
Expand Down
Loading