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
30 changes: 0 additions & 30 deletions src/ethereum/forks/amsterdam/block_access_lists/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,17 @@
rlp_encode_block_access_list,
validate_block_access_list_against_execution,
)
from .tracker import (
StateChangeTracker,
begin_call_frame,
commit_call_frame,
handle_in_transaction_selfdestruct,
normalize_balance_changes,
prepare_balance_tracking,
rollback_call_frame,
set_block_access_index,
track_address_access,
track_balance_change,
track_code_change,
track_nonce_change,
track_storage_read,
track_storage_write,
)

__all__ = [
"BlockAccessListBuilder",
"StateChangeTracker",
"add_balance_change",
"add_code_change",
"add_nonce_change",
"add_storage_read",
"add_storage_write",
"add_touched_account",
"begin_call_frame",
"build_block_access_list",
"commit_call_frame",
"compute_block_access_list_hash",
"handle_in_transaction_selfdestruct",
"normalize_balance_changes",
"prepare_balance_tracking",
"rollback_call_frame",
"set_block_access_index",
"rlp_encode_block_access_list",
"track_address_access",
"track_balance_change",
"track_code_change",
"track_nonce_change",
"track_storage_read",
"track_storage_write",
"validate_block_access_list_against_execution",
]
94 changes: 86 additions & 8 deletions src/ethereum/forks/amsterdam/block_access_lists/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""

from dataclasses import dataclass, field
from typing import Dict, List, Set
from typing import TYPE_CHECKING, Dict, List, Set

from ethereum_types.bytes import Bytes, Bytes32
from ethereum_types.numeric import U64, U256
Expand All @@ -31,6 +31,9 @@
StorageChange,
)

if TYPE_CHECKING:
from ..state_tracker import StateChanges


@dataclass
class AccountData:
Expand Down Expand Up @@ -275,14 +278,15 @@ def add_nonce_change(
ensure_account(builder, address)

# Check if we already have a nonce change for this tx_index and update it
# This ensures we only track the final nonce per transaction
# This ensures we only track the final (highest) nonce per transaction
existing_changes = builder.accounts[address].nonce_changes
for i, existing in enumerate(existing_changes):
if existing.block_access_index == block_access_index:
# Update the existing nonce change with the new nonce
existing_changes[i] = NonceChange(
block_access_index=block_access_index, new_nonce=new_nonce
)
# Keep the highest nonce value
if new_nonce > existing.new_nonce:
existing_changes[i] = NonceChange(
block_access_index=block_access_index, new_nonce=new_nonce
)
return

# No existing change for this tx_index, add a new one
Expand Down Expand Up @@ -374,11 +378,11 @@ def add_touched_account(
ensure_account(builder, address)


def build_block_access_list(
def _build_from_builder(
builder: BlockAccessListBuilder,
) -> BlockAccessList:
"""
Build the final [`BlockAccessList`] from accumulated changes.
Build the final [`BlockAccessList`] from a builder (internal helper).

Constructs a deterministic block access list by sorting all accumulated
changes. The resulting list is ordered by:
Expand Down Expand Up @@ -445,3 +449,77 @@ def build_block_access_list(
account_changes_list.sort(key=lambda x: x.address)

return BlockAccessList(account_changes=tuple(account_changes_list))


def build_block_access_list(
state_changes: "StateChanges",
) -> BlockAccessList:
"""
Build a [`BlockAccessList`] from a StateChanges frame.

Converts the accumulated state changes from the frame-based architecture
into the final deterministic BlockAccessList format.

Parameters
----------
state_changes :
The block-level StateChanges frame containing all changes from the block.

Returns
-------
block_access_list :
The final sorted and encoded block access list.

[`BlockAccessList`]: ref:ethereum.forks.amsterdam.block_access_lists.rlp_types.BlockAccessList # noqa: E501
[`StateChanges`]: ref:ethereum.forks.amsterdam.state_tracker.StateChanges

"""
builder = BlockAccessListBuilder()

# Add all touched addresses
for address in state_changes.touched_addresses:
add_touched_account(builder, address)

# Add all storage reads
for address, slot in state_changes.storage_reads:
add_storage_read(builder, address, slot)

# Add all storage writes, filtering net-zero changes
for (
address,
slot,
block_access_index,
), value in state_changes.storage_writes.items():
# Check if this is a net-zero change by comparing with pre-state
if (address, slot) in state_changes.pre_storage:
if state_changes.pre_storage[(address, slot)] == value:
# Net-zero change - convert to read only
add_storage_read(builder, address, slot)
continue

# Convert U256 to Bytes32 for storage
value_bytes = Bytes32(value.to_bytes(U256(32), "big"))
add_storage_write(
builder, address, slot, block_access_index, value_bytes
)

# Add all balance changes (balance_changes is keyed by (address, index))
for (
address,
block_access_index,
), new_balance in state_changes.balance_changes.items():
add_balance_change(builder, address, block_access_index, new_balance)

# Add all nonce changes
for address, block_access_index, new_nonce in state_changes.nonce_changes:
add_nonce_change(builder, address, block_access_index, new_nonce)

# Add all code changes
# Filtering happens at transaction level in eoa_delegation.py
for (
address,
block_access_index,
), new_code in state_changes.code_changes.items():
add_code_change(builder, address, block_access_index, new_code)

return _build_from_builder(builder)
4 changes: 2 additions & 2 deletions src/ethereum/forks/amsterdam/block_access_lists/rlp_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,10 @@ def validate_block_access_list_against_execution(
# 4. If Block Access List builder provided, validate against it
# by comparing hashes
if block_access_list_builder is not None:
from .builder import build_block_access_list
from .builder import _build_from_builder

# Build a Block Access List from the builder
expected_block_access_list = build_block_access_list(
expected_block_access_list = _build_from_builder(
block_access_list_builder
)

Expand Down
Loading