Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
292ac3c
✨ feat(tests): EIP-7928 SELFDESTRUCT test
raxhvl Sep 16, 2025
796eb7e
🥢 nit:
raxhvl Sep 16, 2025
548c4ba
✨ feat(tests): EIP-7928 SELFDESTRUCT Sendall
raxhvl Sep 16, 2025
a93ca27
🥢 nit: implicit post state
raxhvl Sep 16, 2025
a28abc3
🧹 chore(tests): Move to new test file
raxhvl Sep 19, 2025
effed86
fix: Imports after rebase
fselmo Sep 22, 2025
9cc5df1
♻️ refactor: Abstract bytecode
raxhvl Sep 23, 2025
d156095
nit
raxhvl Sep 23, 2025
88024d2
🐞 fix: Post state
raxhvl Sep 23, 2025
8a0f97e
🐞 fix: contract creation to use initcode
raxhvl Sep 23, 2025
da8d568
feat: point to latest commit in BALs specs
fselmo Sep 23, 2025
1250227
✨ feat: update nonce check
raxhvl Sep 23, 2025
80ec494
♻️ refactor: move to test_block_access_lists
raxhvl Sep 23, 2025
656df69
feat: Validate t8n BAL does not have duplicate entries for the same t…
fselmo Sep 23, 2025
27599f7
📄 docs: Changelog entry
raxhvl Sep 23, 2025
a15735c
📄 docs: Changelog entry
raxhvl Sep 23, 2025
44b2d4b
chore: point to latest specs commit
fselmo Sep 24, 2025
c4fb252
chore: avoid extra fields in BAL classes, related to #2197
fselmo Sep 24, 2025
b6075f2
point to latest commit in specs
fselmo Sep 28, 2025
097e36c
feat(tests): add 7028 test for pre-funded selfdestruct account
fselmo Sep 25, 2025
366e454
fix: rebase with upstream and fix lint
fselmo Sep 29, 2025
f39106f
✨ feat(tests): test_bal_self_destruct
raxhvl Sep 29, 2025
31cdd9b
✨ feat(tests): test_bal_self_destruct
raxhvl Sep 29, 2025
b290901
Add more complex selfdestruct tests (#2207)
nerolation Sep 29, 2025
ac1e471
fix(tests): Fix expectations for self-destruct tests
fselmo Sep 29, 2025
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
2 changes: 1 addition & 1 deletion .github/configs/eels_resolutions.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@
"Amsterdam": {
"git_url": "https://github.com/fselmo/execution-specs.git",
"branch": "feat/amsterdam-fork-and-block-access-lists",
"commit": "39e0b59613be4100d2efc86702ff594c54e5bd81"
"commit": "6abe0ecb792265211d93bc3fea2f932e5d2cfa90"
}
}
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Test fixtures for use by clients are available for each release on the [Github r

### 🧪 Test Cases

- ✨ Add an EIP-7928 test case targeting the `SELFDESTRUCT` opcode. ([#2159](https://github.com/ethereum/execution-spec-tests/pull/2159)).

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

## 🇯🇵 Summary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class BalAccountAbsentValues(CamelModel):

"""

model_config = CamelModel.model_config | {"extra": "forbid"}

nonce_changes: List[BalNonceChange] = Field(
default_factory=list,
description="List of nonce changes that should NOT exist in the BAL. "
Expand Down
12 changes: 12 additions & 0 deletions src/ethereum_test_types/block_access_list/account_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
class BalNonceChange(CamelModel, RLPSerializable):
"""Represents a nonce change in the block access list."""

model_config = CamelModel.model_config | {"extra": "forbid"}

tx_index: HexNumber = Field(
HexNumber(1),
description="Transaction index where the change occurred",
Expand All @@ -34,6 +36,8 @@ class BalNonceChange(CamelModel, RLPSerializable):
class BalBalanceChange(CamelModel, RLPSerializable):
"""Represents a balance change in the block access list."""

model_config = CamelModel.model_config | {"extra": "forbid"}

tx_index: HexNumber = Field(
HexNumber(1),
description="Transaction index where the change occurred",
Expand All @@ -46,6 +50,8 @@ class BalBalanceChange(CamelModel, RLPSerializable):
class BalCodeChange(CamelModel, RLPSerializable):
"""Represents a code change in the block access list."""

model_config = CamelModel.model_config | {"extra": "forbid"}

tx_index: HexNumber = Field(
HexNumber(1),
description="Transaction index where the change occurred",
Expand All @@ -58,6 +64,8 @@ class BalCodeChange(CamelModel, RLPSerializable):
class BalStorageChange(CamelModel, RLPSerializable):
"""Represents a change to a specific storage slot."""

model_config = CamelModel.model_config | {"extra": "forbid"}

tx_index: HexNumber = Field(
HexNumber(1),
description="Transaction index where the change occurred",
Expand All @@ -70,6 +78,8 @@ class BalStorageChange(CamelModel, RLPSerializable):
class BalStorageSlot(CamelModel, RLPSerializable):
"""Represents all changes to a specific storage slot."""

model_config = CamelModel.model_config | {"extra": "forbid"}

slot: StorageKey = Field(..., description="Storage slot key")
slot_changes: List[BalStorageChange] = Field(
default_factory=list, description="List of changes to this slot"
Expand All @@ -81,6 +91,8 @@ class BalStorageSlot(CamelModel, RLPSerializable):
class BalAccountChange(CamelModel, RLPSerializable):
"""Represents all changes to a specific account in a block."""

model_config = CamelModel.model_config | {"extra": "forbid"}

address: Address = Field(..., description="Account address")
nonce_changes: List[BalNonceChange] = Field(
default_factory=list, description="List of nonce changes"
Expand Down
74 changes: 49 additions & 25 deletions src/ethereum_test_types/block_access_list/expectations.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class BalAccountExpectation(CamelModel):
used for expectations.
"""

model_config = CamelModel.model_config | {"extra": "forbid"}

nonce_changes: List[BalNonceChange] = Field(
default_factory=list, description="List of expected nonce changes"
)
Expand Down Expand Up @@ -199,21 +201,33 @@ def _validate_bal_ordering(bal: "BlockAccessList") -> None:
f"{bal.root[i - 1].address} >= {bal.root[i].address}"
)

# Check transaction index ordering within accounts
# Check transaction index ordering and uniqueness within accounts
for account in bal.root:
change_lists: List[BlockAccessListChangeLists] = [
account.nonce_changes,
account.balance_changes,
account.code_changes,
changes_to_check: List[tuple[str, BlockAccessListChangeLists]] = [
("nonce_changes", account.nonce_changes),
("balance_changes", account.balance_changes),
("code_changes", account.code_changes),
]
for change_list in change_lists:
for i in range(1, len(change_list)):
if change_list[i - 1].tx_index >= change_list[i].tx_index:
raise BlockAccessListValidationError(
f"Transaction indices not in ascending order in account "
f"{account.address}: {change_list[i - 1].tx_index} >= "
f"{change_list[i].tx_index}"
)

for field_name, change_list in changes_to_check:
if not change_list:
continue

tx_indices = [c.tx_index for c in change_list]

# Check both ordering and duplicates
if tx_indices != sorted(tx_indices):
raise BlockAccessListValidationError(
f"Transaction indices not in ascending order in {field_name} of account "
f"{account.address}. Got: {tx_indices}, Expected: {sorted(tx_indices)}"
)

if len(tx_indices) != len(set(tx_indices)):
duplicates = sorted({idx for idx in tx_indices if tx_indices.count(idx) > 1})
raise BlockAccessListValidationError(
f"Duplicate transaction indices in {field_name} of account "
f"{account.address}. Duplicates: {duplicates}"
)

# Check storage slot ordering
for i in range(1, len(account.storage_changes)):
Expand All @@ -224,19 +238,29 @@ def _validate_bal_ordering(bal: "BlockAccessList") -> None:
f"{account.storage_changes[i].slot}"
)

# Check transaction index ordering within storage slots
# Check transaction index ordering and uniqueness within storage
# slots
for storage_slot in account.storage_changes:
for i in range(1, len(storage_slot.slot_changes)):
if (
storage_slot.slot_changes[i - 1].tx_index
>= storage_slot.slot_changes[i].tx_index
):
raise BlockAccessListValidationError(
f"Transaction indices not in ascending order in storage slot "
f"{storage_slot.slot} of account {account.address}: "
f"{storage_slot.slot_changes[i - 1].tx_index} >= "
f"{storage_slot.slot_changes[i].tx_index}"
)
if not storage_slot.slot_changes:
continue

tx_indices = [c.tx_index for c in storage_slot.slot_changes]

# Check both ordering and duplicates
if tx_indices != sorted(tx_indices):
raise BlockAccessListValidationError(
f"Transaction indices not in ascending order in storage slot "
f"{storage_slot.slot} of account {account.address}. "
f"Got: {tx_indices}, Expected: {sorted(tx_indices)}"
)

if len(tx_indices) != len(set(tx_indices)):
duplicates = sorted({idx for idx in tx_indices if tx_indices.count(idx) > 1})
raise BlockAccessListValidationError(
f"Duplicate transaction indices in storage slot "
f"{storage_slot.slot} of account {account.address}. "
f"Duplicates: {duplicates}"
)

# Check storage reads ordering
for i in range(1, len(account.storage_reads)):
Expand Down
77 changes: 77 additions & 0 deletions src/ethereum_test_types/tests/test_block_access_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,83 @@ def test_actual_bal_tx_indices_ordering(field_name):
expectation.verify_against(actual_bal)


@pytest.mark.parametrize(
"field_name",
["nonce_changes", "balance_changes", "code_changes"],
)
def test_actual_bal_duplicate_tx_indices(field_name):
"""
Test that actual BAL must not have duplicate tx indices in change lists.
"""
addr = Address(0xA)

# Duplicate tx_index=1
changes = []
if field_name == "nonce_changes":
changes = [
BalNonceChange(tx_index=1, post_nonce=1),
BalNonceChange(tx_index=1, post_nonce=2), # duplicate tx_index
BalNonceChange(tx_index=2, post_nonce=3),
]
elif field_name == "balance_changes":
changes = [
BalBalanceChange(tx_index=1, post_balance=100),
BalBalanceChange(tx_index=1, post_balance=200), # duplicate tx_index
BalBalanceChange(tx_index=2, post_balance=300),
]
elif field_name == "code_changes":
changes = [
BalCodeChange(tx_index=1, new_code=b"code1"),
BalCodeChange(tx_index=1, new_code=b""), # duplicate tx_index
BalCodeChange(tx_index=2, new_code=b"code2"),
]

actual_bal = BlockAccessList([BalAccountChange(address=addr, **{field_name: changes})])

expectation = BlockAccessListExpectation(account_expectations={})

with pytest.raises(
BlockAccessListValidationError,
match=f"Duplicate transaction indices in {field_name}.*Duplicates: \\[1\\]",
):
expectation.verify_against(actual_bal)


def test_actual_bal_storage_duplicate_tx_indices():
"""
Test that storage changes must not have duplicate tx indices within same
slot.
"""
addr = Address(0xA)

# Create storage changes with duplicate tx_index within the same slot
actual_bal = BlockAccessList(
[
BalAccountChange(
address=addr,
storage_changes=[
BalStorageSlot(
slot=0x01,
slot_changes=[
BalStorageChange(tx_index=1, post_value=0x100),
BalStorageChange(tx_index=1, post_value=0x200), # duplicate tx_index
BalStorageChange(tx_index=2, post_value=0x300),
],
)
],
)
]
)

expectation = BlockAccessListExpectation(account_expectations={})

with pytest.raises(
BlockAccessListValidationError,
match="Duplicate transaction indices in storage slot.*Duplicates: \\[1\\]",
):
expectation.verify_against(actual_bal)


def test_expected_addresses_auto_sorted():
"""
Test that expected addresses are automatically sorted before comparison.
Expand Down
2 changes: 1 addition & 1 deletion src/pytest_plugins/eels_resolutions.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@
"Amsterdam": {
"git_url": "https://github.com/fselmo/execution-specs.git",
"branch": "feat/amsterdam-fork-and-block-access-lists",
"commit": "39e0b59613be4100d2efc86702ff594c54e5bd81"
"commit": "6abe0ecb792265211d93bc3fea2f932e5d2cfa90"
}
}
Loading
Loading