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 @@ -41,6 +41,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Add essential tests for coverage gaps in EIP-7951 (`p256verify` precompile) ([#2179](https://github.com/ethereum/execution-spec-tests/pull/2159), [#2203](https://github.com/ethereum/execution-spec-tests/pull/2203), [#2215](https://github.com/ethereum/execution-spec-tests/pull/2215), [#2216](https://github.com/ethereum/execution-spec-tests/pull/2216), [#2217](https://github.com/ethereum/execution-spec-tests/pull/2217), [#2218](https://github.com/ethereum/execution-spec-tests/pull/2218), [#2221](https://github.com/ethereum/execution-spec-tests/pull/2221), [#2229](https://github.com/ethereum/execution-spec-tests/pull/2229), [#2230](https://github.com/ethereum/execution-spec-tests/pull/2230), [#2237](https://github.com/ethereum/execution-spec-tests/pull/2237), [#2238](https://github.com/ethereum/execution-spec-tests/pull/2238)).
- ✨ Add EIP-7928 successful and OOG single-opcode tests ([#2118](https://github.com/ethereum/execution-spec-tests/pull/2118)).
- ✨ Add EIP-7928 tests for EIP-2930 interactions ([#2167](https://github.com/ethereum/execution-spec-tests/pull/2167)).
- ✨ Add EIP-7928 tests for NOOP operations ([#2178](https://github.com/ethereum/execution-spec-tests/pull/2178)).

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

Expand Down
52 changes: 44 additions & 8 deletions src/ethereum_test_types/block_access_list/expectations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
BAL values in tests.
"""

from typing import Any, Callable, Dict, List, Optional
from typing import Any, Callable, ClassVar, Dict, List, Optional

from pydantic import Field, PrivateAttr

Expand Down Expand Up @@ -53,6 +53,33 @@ class BalAccountExpectation(CamelModel):
default=None, description="Explicit absent value expectations using BalAccountAbsentValues"
)

_EMPTY: ClassVar[Optional["BalAccountExpectation"]] = None

@classmethod
def empty(cls) -> "BalAccountExpectation":
"""
Create an expectation that validates the account has NO changes.

This is distinct from `BalAccountExpectation()` with no fields set,
which is ambiguous and clashes with `model_fields_set` logic, and
will raise a clarifying error if used in expectations.

Returns:
A BalAccountExpectation instance with all change lists empty.
This uses a classvar to facilitate identity checks across
multiple expectation instances.

"""
if cls._EMPTY is None:
cls._EMPTY = cls(
nonce_changes=[],
balance_changes=[],
code_changes=[],
storage_changes=[],
storage_reads=[],
)
return cls._EMPTY


def compose(
*modifiers: Callable[["BlockAccessList"], "BlockAccessList"],
Expand Down Expand Up @@ -168,20 +195,29 @@ def verify_against(self, actual_bal: "BlockAccessList") -> None:
raise BlockAccessListValidationError(
f"Address {address} should not be in BAL but was found"
)
elif expectation == BalAccountExpectation():
# explicit check for NO account changes for the address
if actual_accounts_by_addr.get(address) != BalAccountChange(address=address):
raise BlockAccessListValidationError(
f"No account changes expected for {address} but found "
f"changes: {actual_accounts_by_addr[address]}"
)
elif not expectation.model_fields_set:
# Disallow ambiguous BalAccountExpectation() with no fields set
raise BlockAccessListValidationError(
f"Address {address}: BalAccountExpectation() with no fields set is "
f"ambiguous. Use BalAccountExpectation.empty() to validate no changes, "
f"or explicitly set the fields to validate "
f"(e.g., nonce_changes=[...])."
)
else:
# check address is present and validate changes
if address not in actual_accounts_by_addr:
raise BlockAccessListValidationError(
f"Expected address {address} not found in actual BAL"
)

if expectation is BalAccountExpectation.empty():
# explicit check for "no changes" validation w/ .empty()
if actual_accounts_by_addr.get(address) != BalAccountChange(address=address):
raise BlockAccessListValidationError(
f"No account changes expected for {address} but found "
f"changes: {actual_accounts_by_addr[address]}"
)

actual_account = actual_accounts_by_addr[address]
try:
self._compare_account_expectations(expectation, actual_account)
Expand Down
22 changes: 16 additions & 6 deletions src/ethereum_test_types/tests/test_block_access_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,18 @@ def test_address_exclusion_validation_raises_when_address_is_present():
expectation.verify_against(actual_bal)


def test_empty_account_changes_raises_when_changes_are_present():
@pytest.mark.parametrize(
"empty_changes_definition,exception_message",
[
[BalAccountExpectation(), "ambiguous. Use BalAccountExpectation.empty()"],
[BalAccountExpectation.empty(), "No account changes expected for "],
],
ids=["BalAccountExpectation()", "BalAccountExpectation.empty()"],
)
def test_empty_account_changes_definitions(
empty_changes_definition,
exception_message,
):
"""
Test that validation fails when expected empty changes but actual
has changes.
Expand All @@ -85,12 +96,11 @@ def test_empty_account_changes_raises_when_changes_are_present():
]
)

expectation = BlockAccessListExpectation(account_expectations={alice: BalAccountExpectation()})
expectation = BlockAccessListExpectation(
account_expectations={alice: empty_changes_definition}
)

with pytest.raises(
BlockAccessListValidationError,
match=f"No account changes expected for {alice}",
):
with pytest.raises(BlockAccessListValidationError, match=exception_message):
expectation.verify_against(actual_bal)


Expand Down
Loading
Loading