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
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- πŸ”€ Relabel `@pytest.mark.repricing` markers in benchmark tests to reflect configurations requested for gas repricing analysis ([#1971](https://github.com/ethereum/execution-specs/pull/1971)).
- ✨ New EIP-7702 test cases added ([#1974](https://github.com/ethereum/execution-specs/pull/1974)).
- ✨ Add missing benchmark configurations / opcode to benchmark tests for repricing analysis([#2006](https://github.com/ethereum/execution-specs/pull/2006)).
- ✨ Port STATICCALL to CALL tests with zero and non-zero value transfer from `tests/static`, extending coverage with `pytest.mark.with_all_precompiles` ([#1960](https://github.com/ethereum/execution-specs/pull/1960)).

## [v5.4.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.4.0) - 2025-12-07

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1123,14 +1123,64 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
parametrize_fork(metafunc, pytest_params)


def get_param_level_min_valid_fork(metafunc: Metafunc) -> Fork | None:
"""
Extract the minimum valid fork from param-level valid_from markers.

Returns the earliest fork from any valid_from marker inside pytest.param,
or None if no such markers exist.
"""
min_fork: Fork | None = None

for marker in metafunc.definition.iter_markers("parametrize"):
if len(marker.args) < 2:
continue

for value in marker.args[1]:
if not isinstance(value, ParameterSet) or not value.marks:
continue

for mark in value.marks:
mark_obj = mark.mark if hasattr(mark, "mark") else mark
if mark_obj.name == "valid_from" and mark_obj.args:
fork_name = mark_obj.args[0]
try:
for fork in ALL_FORKS:
if fork.name() == fork_name:
if min_fork is None or fork < min_fork:
min_fork = fork
break
except (ValueError, InvalidForkError):
pass

return min_fork


def add_fork_covariant_parameters(
metafunc: Metafunc, fork_parametrizers: List[ForkParametrizer]
) -> None:
"""
Iterate over the fork covariant descriptors and add their values to the
test function.
"""
# Process all covariant decorators uniformly
# Check if any covariant markers are present
has_covariant_markers = any(
list(metafunc.definition.iter_markers(cd.marker_name))
for cd in fork_covariant_decorators
) or any(
marker.name == "parametrize_by_fork"
for marker in metafunc.definition.iter_markers()
)

# Filter forks before any param-level valid_from to avoid covariant
# assertion errors
if has_covariant_markers:
param_min_fork = get_param_level_min_valid_fork(metafunc)
if param_min_fork:
fork_parametrizers[:] = [
fp for fp in fork_parametrizers if fp.fork >= param_min_fork
]

for covariant_descriptor in fork_covariant_decorators:
if list(
metafunc.definition.iter_markers(covariant_descriptor.marker_name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@
| `test_selfdestruct_created_in_same_tx_with_revert` | Ensure BAL tracks selfdestruct with revert correctly (pre-Amsterdam test with BAL) | Contract created and selfdestructed in same tx with nested revert | BAL **MUST** track storage reads and balance changes for selfdestruct even with reverts | βœ… Completed |
| `test_value_transfer_gas_calculation` | Ensure BAL correctly tracks OOG scenarios for CALL/CALLCODE/DELEGATECALL/STATICCALL (pre-Amsterdam test with BAL) | Nested calls with precise gas limits to test OOG behavior. For CALL with OOG: target account is read. For CALLCODE/DELEGATECALL/STATICCALL with OOG: target account **NOT** read (OOG before state access) | For CALL: target in BAL even with OOG. For CALLCODE/DELEGATECALL/STATICCALL: target **NOT** in BAL when OOG (state access deferred until after gas check) | βœ… Completed |
| `test_bal_call_with_value_in_static_context` | Ensure BAL does NOT include target when CALL with value fails in static context | `static_caller` uses `STATICCALL` to call `caller`. `caller` attempts `CALL(target, value=1)` which must fail due to static context. Target is an empty account. | BAL **MUST NOT** include target because static context check (`is_static && value > 0`) must happen BEFORE any account access or BAL tracking. BAL **MUST** include `static_caller` with `storage_changes` (STATICCALL succeeded), `caller` with empty changes. | βœ… Completed |
| `test_staticcall_reentrant_call_to_precompile` | Ensure BAL captures STATICCALL reentry with CALL to precompile | Contract STATICCALLs itself. On reentry (CALLVALUE=0), attempts CALL to precompile with parametrized value. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: target with `storage_changes` (slot 0=1), precompile with empty changes. call_value>0: target with `storage_reads` (slot 0), precompile **NOT** in BAL (reverted before accessed). | βœ… Completed |
| `test_staticcall_call_to_precompile` | Ensure BAL captures STATICCALL β†’ CALL to precompile chain | Contract A STATICCALLs contract B. B attempts CALL to precompile. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: contract_a with markers, contract_b empty (STATICCALLed), precompile empty. call_value>0: contract_a with `storage_reads` for slot 1, precompile **NOT** in BAL. | βœ… Completed |
| `test_staticcall_nested_call_to_precompile` | Ensure BAL captures nested CALL β†’ STATICCALL β†’ CALL to precompile | Contract B CALLs A. A STATICCALLs C. C attempts CALL to precompile. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: all contracts with markers/empty, precompile empty. call_value>0: contract_a with `storage_reads` for slot 1, precompile **NOT** in BAL. | βœ… Completed |
| `test_staticcall_call_to_precompile_from_contract_init` | Ensure BAL captures STATICCALL to precompile during CREATE init | Contract A CREATEs contract. Init code STATICCALLs B which CALLs precompile. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: contract_a with markers/nonce, created_contract with markers/nonce, contract_b empty, precompile empty. call_value>0: created_contract with `storage_reads` for slot 1, precompile **NOT** in BAL. | βœ… Completed |
| `test_bal_4788_simple` | Ensure BAL captures beacon root storage writes during pre-execution system call | Block with 2 normal user transactions: Alice sends 10 wei to Charlie, Bob sends 10 wei to Charlie. At block start (pre-execution), `SYSTEM_ADDRESS` calls `BEACON_ROOTS_ADDRESS` to store parent beacon root | BAL **MUST** include at `block_access_index=0`: `BEACON_ROOTS_ADDRESS` with two `storage_changes` (timestamp slot and beacon root slot); `SYSTEM_ADDRESS` **MUST NOT** be included in BAL. At `block_access_index=1`: Alice with `nonce_changes`, Charlie with `balance_changes` (10 wei). At `block_access_index=2`: Bob with `nonce_changes`, Charlie with `balance_changes` (20 wei total). | βœ… Completed |
| `test_bal_4788_empty_block` | Ensure BAL captures beacon root storage writes in empty block | Block with no transactions. At block start (pre-execution), `SYSTEM_ADDRESS` calls `BEACON_ROOTS_ADDRESS` to store parent beacon root | BAL **MUST** include at `block_access_index=0`: `BEACON_ROOTS_ADDRESS` with two `storage_changes` (timestamp slot and beacon root slot); `SYSTEM_ADDRESS` **MUST NOT** be included in BAL. No transaction-related BAL entries. | βœ… Completed |
| `test_bal_4788_query` | Ensure BAL captures storage reads when querying beacon root (valid and invalid queries) with optional value transfer | Parameterized test: Block 1 stores beacon root at timestamp 12. Block 2 queries with three timestamp scenarios (valid=12, invalid non-zero=42, invalid zero=0) and value (0 or 100 wei). Valid query (timestamp=12): reads both timestamp and root slots, writes returned value. If value > 0, beacon root contract receives balance. Invalid query with non-zero timestamp (timestamp=42): reads only timestamp slot before reverting, query contract has implicit SLOAD recorded (SSTORE reverts), no value transferred. Invalid query with zero timestamp (timestamp=0): reverts immediately without any storage access, query contract has implicit SLOAD recorded, no value transferred. | Block 1 BAL: System call writes. Block 2 BAL **MUST** include at `block_access_index=0`: System call writes for block 2. Valid case (timestamp=12) at `block_access_index=1`: `BEACON_ROOTS_ADDRESS` with `storage_reads` [timestamp_slot, root_slot] and `balance_changes` if value > 0, query contract with `storage_changes`. Invalid non-zero case (timestamp=42) at `block_access_index=1`: `BEACON_ROOTS_ADDRESS` with `storage_reads` [timestamp_slot only] and NO `balance_changes` (reverted), query contract with `storage_reads` [0] and NO `storage_changes`. Invalid zero case (timestamp=0) at `block_access_index=1`: `BEACON_ROOTS_ADDRESS` with NO `storage_reads` (reverts before access) and NO `balance_changes`, query contract with `storage_reads` [0] and NO `storage_changes`. | βœ… Completed |
Expand Down
7 changes: 7 additions & 0 deletions tests/byzantium/eip214_staticcall/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""
Test cases for EIP-214: STATICCALL opcode.

EIP-214 introduced the STATICCALL opcode which creates a read-only call
context. Any state-modifying operations (including CALL with non-zero
value) within a STATICCALL context will cause the call to fail.
"""
17 changes: 17 additions & 0 deletions tests/byzantium/eip214_staticcall/spec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Defines EIP-214 specification reference."""

from dataclasses import dataclass


@dataclass(frozen=True)
class ReferenceSpec:
"""Defines the reference spec version and git path."""

git_path: str
version: str


ref_spec_214 = ReferenceSpec(
git_path="EIPS/eip-214.md",
version="009d0e1ce76b2c171c34bacdb2f13d606c9918b0",
)
Loading
Loading