ethereum.forks.amsterdam.block_access_lists
Block access lists (BALs), originally defined in EIP-7928, record all accounts and storage locations accessed during block execution along with their post-execution values.
BALs enable parallel disk reads, parallel transaction validation, parallel state root computation, and applying state updates without executing bytecode.
See BlockAccessList for more detail.
StorageKey¶
Slot within an Account's storage.
| 31 | StorageKey: TypeAlias = U256 |
|---|
StorageValue¶
Value associated with a StorageKey within an Account's storage.
| 36 | StorageValue: TypeAlias = U256 |
|---|
CodeData¶
Bytecode associated with an Account.
| 44 | CodeData: TypeAlias = Bytes |
|---|
BlockAccessIndex¶
Position within the set of all changes in a Block.
| 49 | BlockAccessIndex: TypeAlias = U16 |
|---|
Balance¶
Balance associated with an Account, in wei.
| 56 | Balance: TypeAlias = U256 |
|---|
Nonce¶
Nonce associated with an Account.
| 63 | Nonce: TypeAlias = U64 |
|---|
StorageChange ¶
In a SlotChanges, represents a single change in an Account's
storage slot.
| 69 | @slotted_freezable |
|---|
| 70 | @dataclass |
|---|
class StorageChange:
block_access_index¶
Position within the set of all changes in a Block.
| 80 | block_access_index: BlockAccessIndex |
|---|
new_value¶
Value of an Account's storage slot after this change has been applied.
| 87 | new_value: StorageValue |
|---|
BalanceChange ¶
In a BlockAccessList, represents a change in an Account's
balance.
| 95 | @slotted_freezable |
|---|
| 96 | @dataclass |
|---|
class BalanceChange:
block_access_index¶
Position within the set of all changes in a Block.
| 106 | block_access_index: BlockAccessIndex |
|---|
post_balance¶
Balance of an Account after this change has been applied.
| 113 | post_balance: Balance |
|---|
NonceChange ¶
In a BlockAccessList, represents a change in an Account's
nonce.
| 121 | @slotted_freezable |
|---|
| 122 | @dataclass |
|---|
class NonceChange:
block_access_index¶
Position within the set of all changes in a Block.
| 132 | block_access_index: BlockAccessIndex |
|---|
new_nonce¶
Nonce of an Account after this change has been applied.
| 139 | new_nonce: Nonce |
|---|
CodeChange ¶
In a BlockAccessList, represents a change in an Account's
code.
| 147 | @slotted_freezable |
|---|
| 148 | @dataclass |
|---|
class CodeChange:
block_access_index¶
Position within the set of all changes in a Block.
| 158 | block_access_index: BlockAccessIndex |
|---|
new_code¶
Code of an Account after this change has been applied.
| 165 | new_code: CodeData |
|---|
SlotChanges ¶
In a BlockAccessList, represents a change in an Account's
storage.
| 173 | @slotted_freezable |
|---|
| 174 | @dataclass |
|---|
class SlotChanges:
slot¶
Location within an Account's storage that has been modified.
| 184 | slot: StorageKey |
|---|
changes¶
Sequence of changes that have been made to one particular storage slot.
| 191 | changes: Tuple[StorageChange, ...] |
|---|
AccountChanges ¶
All changes for a single Account, grouped by field type.
| 197 | @slotted_freezable |
|---|
| 198 | @dataclass |
|---|
class AccountChanges:
address¶
Address of the account containing these changes.
| 206 | address: Address |
|---|
storage_changes¶
Writes to the storage of the associated Account.
| 211 | storage_changes: Tuple[SlotChanges, ...] |
|---|
storage_reads¶
Storage slots of the associated Account that have been read but not
changed.
| 218 | storage_reads: Tuple[StorageKey, ...] |
|---|
balance_changes¶
Writes to the balance of the associated Account.
| 226 | balance_changes: Tuple[BalanceChange, ...] |
|---|
nonce_changes¶
Writes to the nonce of the associated Account.
| 233 | nonce_changes: Tuple[NonceChange, ...] |
|---|
code_changes¶
Writes to the code of the associated Account.
| 240 | code_changes: Tuple[CodeChange, ...] |
|---|
BlockAccessList¶
List of state changes recorded across a Block.
The hash of a block's access list is included in its Header, though the
access list itself is not included in the block body.
A BlockAccessList includes, for example, the targets of:
BALANCE,EXTCODESIZE,EXTCODECOPY, andEXTCODEHASHinstructions;the call family of instructions even if they revert;
the create family of instructions if the target is accessed;
etc.
| 248 | BlockAccessList: TypeAlias = List[AccountChanges] |
|---|
AccountData ¶
Account data stored in the builder during block execution.
This dataclass tracks all changes made to a single account throughout the execution of a block, organized by the type of change and the transaction index where it occurred.
| 274 | @dataclass |
|---|
class AccountData:
storage_changes¶
Mapping from storage slot to list of changes made to that slot. Each change includes the transaction index and new value.
| 284 | storage_changes: Dict[U256, List[StorageChange]] = field( |
|---|---|
| 285 | default_factory=dict |
| 286 | ) |
storage_reads¶
Set of storage slots that were read but not modified.
| 292 | storage_reads: Set[U256] = field(default_factory=set) |
|---|
balance_changes¶
List of balance changes for this account, ordered by transaction index.
| 297 | balance_changes: List[BalanceChange] = field(default_factory=list) |
|---|
nonce_changes¶
List of nonce changes for this account, ordered by transaction index.
| 302 | nonce_changes: List[NonceChange] = field(default_factory=list) |
|---|
code_changes¶
List of code changes (contract deployments) for this account, ordered by transaction index.
| 307 | code_changes: List[CodeChange] = field(default_factory=list) |
|---|
BlockAccessListBuilder ¶
Builder for constructing BlockAccessList efficiently during transaction
execution.
The builder accumulates all account and storage accesses during block execution and constructs a deterministic access list. Changes are tracked by address, field type, and transaction index to enable efficient reconstruction of state changes.
The builder follows a two-phase approach:
Collection Phase: During transaction execution, all state accesses are recorded via the tracking functions.
Build Phase: After block execution, the accumulated data is sorted and encoded into the final deterministic format.
| 314 | @dataclass |
|---|
class BlockAccessListBuilder:
block_access_index¶
Current block access index. Set by the caller before each
incorporate_tx_into_block call (0 for system txs, i+1 for the
i-th user tx, N+1 for post-execution operations).
| 335 | block_access_index: BlockAccessIndex = BlockAccessIndex(0) |
|---|
accounts¶
Mapping from account address to its tracked changes during block execution.
| 344 | accounts: Dict[Address, AccountData] = field(default_factory=dict) |
|---|
ensure_account ¶
Ensure an account exists in the builder's tracking structure.
Creates an empty AccountData entry for the given address if it
doesn't already exist. This function is idempotent and safe to call
multiple times for the same address.
def ensure_account(builder: BlockAccessListBuilder, address: Address) -> None:
| 351 | """ |
|---|---|
| 352 | Ensure an account exists in the builder's tracking structure. |
| 353 | |
| 354 | Creates an empty [`AccountData`][ad] entry for the given address if it |
| 355 | doesn't already exist. This function is idempotent and safe to call |
| 356 | multiple times for the same address. |
| 357 | |
| 358 | [ad]: ref:ethereum.forks.amsterdam.block_access_lists.AccountData |
| 359 | """ |
| 360 | if address not in builder.accounts: |
| 361 | builder.accounts[address] = AccountData() |
add_storage_write ¶
Add a storage write operation to the block access list.
Records a storage slot modification for a given address at a specific
transaction index. If multiple writes occur to the same slot within the
same transaction (same block_access_index), only the final value is kept.
def add_storage_write(builder: BlockAccessListBuilder, address: Address, slot: U256, block_access_index: BlockAccessIndex, new_value: U256) -> None:
| 371 | """ |
|---|---|
| 372 | Add a storage write operation to the block access list. |
| 373 | |
| 374 | Records a storage slot modification for a given address at a specific |
| 375 | transaction index. If multiple writes occur to the same slot within the |
| 376 | same transaction (same `block_access_index`), only the final value is kept. |
| 377 | """ |
| 378 | ensure_account(builder, address) |
| 379 | |
| 380 | if slot not in builder.accounts[address].storage_changes: |
| 381 | builder.accounts[address].storage_changes[slot] = [] |
| 382 | |
| 383 | # Check if there's already an entry with the same block_access_index |
| 384 | # If so, update it with the new value, keeping only the final write |
| 385 | changes = builder.accounts[address].storage_changes[slot] |
| 386 | for i, existing_change in enumerate(changes): |
| 387 | if existing_change.block_access_index == block_access_index: |
| 388 | # Update the existing entry with the new value |
| 389 | changes[i] = StorageChange( |
| 390 | block_access_index=block_access_index, new_value=new_value |
| 391 | ) |
| 392 | return |
| 393 | |
| 394 | # No existing entry found, append new change |
| 395 | change = StorageChange( |
| 396 | block_access_index=block_access_index, new_value=new_value |
| 397 | ) |
| 398 | builder.accounts[address].storage_changes[slot].append(change) |
add_storage_read ¶
Add a storage read operation to the block access list.
Records that a storage slot was read during execution. Storage slots that are both read and written will only appear in the storage changes list, not in the storage reads list, as per [EIP-7928].
def add_storage_read(builder: BlockAccessListBuilder, address: Address, slot: U256) -> None:
| 404 | """ |
|---|---|
| 405 | Add a storage read operation to the block access list. |
| 406 | |
| 407 | Records that a storage slot was read during execution. Storage slots |
| 408 | that are both read and written will only appear in the storage changes |
| 409 | list, not in the storage reads list, as per [EIP-7928]. |
| 410 | """ |
| 411 | ensure_account(builder, address) |
| 412 | builder.accounts[address].storage_reads.add(slot) |
add_balance_change ¶
Add a balance change to the block access list.
Records the post-transaction balance for an account after it has been modified. This includes changes from transfers, gas fees, block rewards, and any other balance-affecting operations.
def add_balance_change(builder: BlockAccessListBuilder, address: Address, block_access_index: BlockAccessIndex, post_balance: U256) -> None:
| 421 | """ |
|---|---|
| 422 | Add a balance change to the block access list. |
| 423 | |
| 424 | Records the post-transaction balance for an account after it has been |
| 425 | modified. This includes changes from transfers, gas fees, block rewards, |
| 426 | and any other balance-affecting operations. |
| 427 | """ |
| 428 | ensure_account(builder, address) |
| 429 | |
| 430 | # Balance value is already U256 |
| 431 | balance_value = post_balance |
| 432 | |
| 433 | # Check if we already have a balance change for this tx_index and update it |
| 434 | # This ensures we only track the final balance per transaction |
| 435 | existing_changes = builder.accounts[address].balance_changes |
| 436 | for i, existing in enumerate(existing_changes): |
| 437 | if existing.block_access_index == block_access_index: |
| 438 | # Update the existing balance change with the new balance |
| 439 | existing_changes[i] = BalanceChange( |
| 440 | block_access_index=block_access_index, |
| 441 | post_balance=balance_value, |
| 442 | ) |
| 443 | return |
| 444 | |
| 445 | # No existing change for this tx_index, add a new one |
| 446 | change = BalanceChange( |
| 447 | block_access_index=block_access_index, post_balance=balance_value |
| 448 | ) |
| 449 | builder.accounts[address].balance_changes.append(change) |
add_nonce_change ¶
def add_nonce_change(builder: BlockAccessListBuilder, address: Address, block_access_index: BlockAccessIndex, new_nonce: U64) -> None:
| 458 | """ |
|---|---|
| 459 | Add a nonce change to the block access list. |
| 460 | |
| 461 | Records a nonce increment for an account. This occurs when an EOA sends |
| 462 | a transaction or when a contract performs [`CREATE`] or [`CREATE2`] |
| 463 | operations. |
| 464 | |
| 465 | [`CREATE`]: ref:ethereum.forks.amsterdam.vm.instructions.system.create |
| 466 | [`CREATE2`]: ref:ethereum.forks.amsterdam.vm.instructions.system.create2 |
| 467 | """ |
| 468 | ensure_account(builder, address) |
| 469 | |
| 470 | # Check if we already have a nonce change for this tx_index and update it |
| 471 | # This ensures we only track the final (highest) nonce per transaction |
| 472 | existing_changes = builder.accounts[address].nonce_changes |
| 473 | for i, existing in enumerate(existing_changes): |
| 474 | if existing.block_access_index == block_access_index: |
| 475 | # Keep the highest nonce value |
| 476 | if new_nonce > existing.new_nonce: |
| 477 | existing_changes[i] = NonceChange( |
| 478 | block_access_index=block_access_index, new_nonce=new_nonce |
| 479 | ) |
| 480 | return |
| 481 | |
| 482 | # No existing change for this tx_index, add a new one |
| 483 | change = NonceChange( |
| 484 | block_access_index=block_access_index, new_nonce=new_nonce |
| 485 | ) |
| 486 | builder.accounts[address].nonce_changes.append(change) |
add_code_change ¶
Add a code change to the block access list.
Records contract code deployment or modification. This typically occurs
during contract creation via CREATE, CREATE2, or
SetCodeTransaction operations.
def add_code_change(builder: BlockAccessListBuilder, address: Address, block_access_index: BlockAccessIndex, new_code: Bytes) -> None:
| 495 | """ |
|---|---|
| 496 | Add a code change to the block access list. |
| 497 | |
| 498 | Records contract code deployment or modification. This typically occurs |
| 499 | during contract creation via [`CREATE`], [`CREATE2`], or |
| 500 | [`SetCodeTransaction`][sct] operations. |
| 501 | |
| 502 | [`CREATE`]: ref:ethereum.forks.amsterdam.vm.instructions.system.create |
| 503 | [`CREATE2`]: ref:ethereum.forks.amsterdam.vm.instructions.system.create2 |
| 504 | [sct]: ref:ethereum.forks.amsterdam.transactions.SetCodeTransaction |
| 505 | """ |
| 506 | ensure_account(builder, address) |
| 507 | |
| 508 | # Check if we already have a code change for this block_access_index |
| 509 | # This handles the case of in-transaction selfdestructs where code is |
| 510 | # first deployed and then cleared in the same transaction |
| 511 | existing_changes = builder.accounts[address].code_changes |
| 512 | for i, existing in enumerate(existing_changes): |
| 513 | if existing.block_access_index == block_access_index: |
| 514 | # Replace the existing code change with the new one |
| 515 | # For selfdestructs, this ensures we only record the final |
| 516 | # state (empty code) |
| 517 | existing_changes[i] = CodeChange( |
| 518 | block_access_index=block_access_index, new_code=new_code |
| 519 | ) |
| 520 | return |
| 521 | |
| 522 | # No existing change for this block_access_index, add a new one |
| 523 | change = CodeChange( |
| 524 | block_access_index=block_access_index, new_code=new_code |
| 525 | ) |
| 526 | builder.accounts[address].code_changes.append(change) |
add_touched_account ¶
Add an account that was accessed but not modified.
Records that an account was accessed during execution without any state
changes. This is used for operations like EXTCODEHASH, BALANCE,
EXTCODESIZE, and EXTCODECOPY that read account data without
modifying it.
def add_touched_account(builder: BlockAccessListBuilder, address: Address) -> None:
| 532 | """ |
|---|---|
| 533 | Add an account that was accessed but not modified. |
| 534 | |
| 535 | Records that an account was accessed during execution without any state |
| 536 | changes. This is used for operations like [`EXTCODEHASH`], [`BALANCE`], |
| 537 | [`EXTCODESIZE`], and [`EXTCODECOPY`] that read account data without |
| 538 | modifying it. |
| 539 | |
| 540 | [`EXTCODEHASH`]: ref:ethereum.forks.amsterdam.vm.instructions.environment.extcodehash |
| 541 | [`BALANCE`]: ref:ethereum.forks.amsterdam.vm.instructions.environment.balance |
| 542 | [`EXTCODESIZE`]: ref:ethereum.forks.amsterdam.vm.instructions.environment.extcodesize |
| 543 | [`EXTCODECOPY`]: ref:ethereum.forks.amsterdam.vm.instructions.environment.extcodecopy |
| 544 | """ # noqa: E501 |
| 545 | ensure_account(builder, address) |
_build_from_builder ¶
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:
Account addresses (lexicographically)
Within each account:
Storage slots (lexicographically)
Transaction indices (numerically) for each change type
Addresses, storage slots, and block access indices are unique. Storage reads that also appear in storage changes are excluded.
def _build_from_builder(builder: BlockAccessListBuilder) -> BlockAccessList:
| 551 | """ |
|---|---|
| 552 | Build the final [`BlockAccessList`] from a builder (internal helper). |
| 553 | |
| 554 | Constructs a deterministic block access list by sorting all accumulated |
| 555 | changes. The resulting list is ordered by: |
| 556 | |
| 557 | 1. Account addresses (lexicographically) |
| 558 | 2. Within each account: |
| 559 | - Storage slots (lexicographically) |
| 560 | - Transaction indices (numerically) for each change type |
| 561 | |
| 562 | Addresses, storage slots, and block access indices are unique. |
| 563 | Storage reads that also appear in storage changes are excluded. |
| 564 | |
| 565 | [`BlockAccessList`]: ref:ethereum.forks.amsterdam.block_access_lists.BlockAccessList |
| 566 | """ # noqa: E501 |
| 567 | block_access_list: BlockAccessList = [] |
| 568 | |
| 569 | for address, changes in builder.accounts.items(): |
| 570 | storage_changes = [] |
| 571 | for slot, slot_changes in changes.storage_changes.items(): |
| 572 | sorted_changes = tuple( |
| 573 | sorted(slot_changes, key=lambda x: x.block_access_index) |
| 574 | ) |
| 575 | storage_changes.append( |
| 576 | SlotChanges(slot=slot, changes=sorted_changes) |
| 577 | ) |
| 578 | |
| 579 | storage_reads = [] |
| 580 | for slot in changes.storage_reads: |
| 581 | if slot not in changes.storage_changes: |
| 582 | storage_reads.append(slot) |
| 583 | |
| 584 | balance_changes = tuple( |
| 585 | sorted(changes.balance_changes, key=lambda x: x.block_access_index) |
| 586 | ) |
| 587 | nonce_changes = tuple( |
| 588 | sorted(changes.nonce_changes, key=lambda x: x.block_access_index) |
| 589 | ) |
| 590 | code_changes = tuple( |
| 591 | sorted(changes.code_changes, key=lambda x: x.block_access_index) |
| 592 | ) |
| 593 | |
| 594 | storage_changes.sort(key=lambda x: x.slot) |
| 595 | storage_reads.sort() |
| 596 | |
| 597 | account_change = AccountChanges( |
| 598 | address=address, |
| 599 | storage_changes=tuple(storage_changes), |
| 600 | storage_reads=tuple(storage_reads), |
| 601 | balance_changes=balance_changes, |
| 602 | nonce_changes=nonce_changes, |
| 603 | code_changes=code_changes, |
| 604 | ) |
| 605 | |
| 606 | block_access_list.append(account_change) |
| 607 | |
| 608 | block_access_list.sort(key=lambda x: x.address) |
| 609 | |
| 610 | return block_access_list |
_get_pre_tx_account ¶
Look up an account in cumulative state, falling back to pre_state.
The cumulative account state (pre_tx_accounts) should contain state up
to (but not including) the current transaction.
Returns None if the address does not exist.
def _get_pre_tx_account(pre_tx_accounts: Dict[Address, Optional[Account]], pre_state: PreState, address: Address) -> Optional[Account]:
| 618 | """ |
|---|---|
| 619 | Look up an account in cumulative state, falling back to `pre_state`. |
| 620 | |
| 621 | The cumulative account state (`pre_tx_accounts`) should contain state up |
| 622 | to (but not including) the current transaction. |
| 623 | |
| 624 | Returns `None` if the `address` does not exist. |
| 625 | """ |
| 626 | if address in pre_tx_accounts: |
| 627 | return pre_tx_accounts[address] |
| 628 | return pre_state.get_account_optional(address) |
_get_pre_tx_storage ¶
Look up a storage value in cumulative state, falling back to pre_state.
Returns 0 if not set.
def _get_pre_tx_storage(pre_tx_storage: Dict[Address, Dict[Bytes32, U256]], pre_state: PreState, address: Address, key: Bytes32) -> U256:
| 637 | """ |
|---|---|
| 638 | Look up a storage value in cumulative state, falling back to `pre_state`. |
| 639 | |
| 640 | Returns `0` if not set. |
| 641 | """ |
| 642 | if address in pre_tx_storage and key in pre_tx_storage[address]: |
| 643 | return pre_tx_storage[address][key] |
| 644 | return pre_state.get_storage(address, key) |
update_builder_from_tx ¶
Update the BAL builder with changes from a single transaction.
Compare the transaction's writes against the block's cumulative
state (falling back to pre_state) to extract balance, nonce, code, and
storage changes. Net-zero filtering is automatic: if the pre-tx value
equals the post-tx value, no change is recorded.
Must be called before the transaction's writes are merged into the block state.
def update_builder_from_tx(builder: BlockAccessListBuilder, tx_state: TransactionState) -> None:
| 651 | """ |
|---|---|
| 652 | Update the BAL builder with changes from a single transaction. |
| 653 | |
| 654 | Compare the transaction's writes against the block's cumulative |
| 655 | state (falling back to `pre_state`) to extract balance, nonce, code, and |
| 656 | storage changes. Net-zero filtering is automatic: if the pre-tx value |
| 657 | equals the post-tx value, no change is recorded. |
| 658 | |
| 659 | Must be called **before** the transaction's writes are merged into |
| 660 | the block state. |
| 661 | """ |
| 662 | block_state = tx_state.parent |
| 663 | pre_state = block_state.pre_state |
| 664 | idx = builder.block_access_index |
| 665 | |
| 666 | # Compare account writes against block cumulative state |
| 667 | for address, post_account in tx_state.account_writes.items(): |
| 668 | pre_account = _get_pre_tx_account( |
| 669 | block_state.account_writes, pre_state, address |
| 670 | ) |
| 671 | |
| 672 | pre_balance = pre_account.balance if pre_account else U256(0) |
| 673 | post_balance = post_account.balance if post_account else U256(0) |
| 674 | if pre_balance != post_balance: |
| 675 | add_balance_change(builder, address, idx, post_balance) |
| 676 | |
| 677 | pre_nonce = pre_account.nonce if pre_account else Uint(0) |
| 678 | post_nonce = post_account.nonce if post_account else Uint(0) |
| 679 | if pre_nonce != post_nonce: |
| 680 | add_nonce_change(builder, address, idx, U64(post_nonce)) |
| 681 | |
| 682 | pre_code_hash = ( |
| 683 | pre_account.code_hash if pre_account else EMPTY_CODE_HASH |
| 684 | ) |
| 685 | post_code_hash = ( |
| 686 | post_account.code_hash if post_account else EMPTY_CODE_HASH |
| 687 | ) |
| 688 | if pre_code_hash != post_code_hash: |
| 689 | post_code = get_code(tx_state, post_code_hash) |
| 690 | add_code_change(builder, address, idx, post_code) |
| 691 | |
| 692 | # Compare storage writes against block cumulative state |
| 693 | for address, slots in tx_state.storage_writes.items(): |
| 694 | for key, post_value in slots.items(): |
| 695 | pre_value = _get_pre_tx_storage( |
| 696 | block_state.storage_writes, pre_state, address, key |
| 697 | ) |
| 698 | if pre_value != post_value: |
| 699 | # Convert slot from internal Bytes32 format to U256 for BAL. |
| 700 | # EIP-7928 uses U256 as it's more space-efficient in RLP. |
| 701 | u256_slot = U256.from_be_bytes(key) |
| 702 | add_storage_write(builder, address, u256_slot, idx, post_value) |
build_block_access_list ¶
Build a BlockAccessList from the builder and block state.
Feed accumulated reads from the block state into the builder, then produce the final sorted and encoded block access list.
def build_block_access_list(builder: BlockAccessListBuilder, block_state: BlockState) -> BlockAccessList:
| 709 | """ |
|---|---|
| 710 | Build a [`BlockAccessList`] from the builder and block state. |
| 711 | |
| 712 | Feed accumulated reads from the block state into the builder, then produce |
| 713 | the final sorted and encoded block access list. |
| 714 | |
| 715 | [`BlockAccessList`]: ref:ethereum.forks.amsterdam.block_access_lists.BlockAccessList |
| 716 | """ # noqa: E501 |
| 717 | # Add storage reads (convert Bytes32 to U256 for BAL encoding) |
| 718 | for address, slot in block_state.storage_reads: |
| 719 | add_storage_read(builder, address, U256.from_be_bytes(slot)) |
| 720 | |
| 721 | # Add touched addresses |
| 722 | for address in block_state.account_reads: |
| 723 | add_touched_account(builder, address) |
| 724 | |
| 725 | return _build_from_builder(builder) |
hash_block_access_list ¶
Compute the hash of a Block Access List.
def hash_block_access_list(block_access_list: BlockAccessList) -> Hash32:
| 731 | """ |
|---|---|
| 732 | Compute the hash of a Block Access List. |
| 733 | """ |
| 734 | return keccak256(rlp.encode(block_access_list)) |
validate_block_access_list_gas_limit ¶
Validate that the block access list does not exceed the gas limit.
The total number of items (addresses + unique storage keys) must not
exceed block_gas_limit // GAS_BLOCK_ACCESS_LIST_ITEM.
def validate_block_access_list_gas_limit(block_access_list: BlockAccessList, block_gas_limit: Uint) -> None:
| 741 | """ |
|---|---|
| 742 | Validate that the block access list does not exceed the gas limit. |
| 743 | |
| 744 | The total number of items (addresses + unique storage keys) must not |
| 745 | exceed ``block_gas_limit // GAS_BLOCK_ACCESS_LIST_ITEM``. |
| 746 | """ |
| 747 | from .vm.gas import GAS_BLOCK_ACCESS_LIST_ITEM |
| 748 | |
| 749 | bal_items = Uint(0) |
| 750 | for account in block_access_list: |
| 751 | # Count each address as one item |
| 752 | bal_items += Uint(1) |
| 753 | |
| 754 | # Collect unique storage keys across both |
| 755 | # reads and writes |
| 756 | unique_slots: Set[U256] = set() |
| 757 | for slot_change in account.storage_changes: |
| 758 | unique_slots.add(slot_change.slot) |
| 759 | for slot in account.storage_reads: |
| 760 | unique_slots.add(slot) |
| 761 | |
| 762 | # Count each unique storage key as one item |
| 763 | bal_items += Uint(len(unique_slots)) |
| 764 | |
| 765 | if bal_items > block_gas_limit // GAS_BLOCK_ACCESS_LIST_ITEM: |
| 766 | raise BlockAccessListGasLimitExceededError( |
| 767 | f"Block access list exceeds gas limit, {bal_items} items " |
| 768 | f"exceeds limit of " |
| 769 | f"{block_gas_limit // GAS_BLOCK_ACCESS_LIST_ITEM}." |
| 770 | ) |