Skip to content
17 changes: 17 additions & 0 deletions src/ethereum/forks/amsterdam/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@
increment_nonce,
modify_state,
set_account_balance,
track_ancestor_access,
track_bytecode_access,
write_block_state_changes,
)
from .transactions import (
Expand Down Expand Up @@ -252,6 +254,8 @@ def state_transition(chain: BlockChain, block: Block) -> None:
storage_reads=set(),
account_writes={},
storage_writes={},
bytecode_accesses=set(),
ancestor_accesses=set(),
),
block_gas_limit=block.header.gas_limit,
block_hashes=get_last_256_block_hashes(chain),
Expand Down Expand Up @@ -671,6 +675,8 @@ def process_system_transaction(
account_writes={},
storage_writes={},
created_accounts=set(),
bytecode_accesses=set(),
ancestor_accesses=set(),
),
)

Expand Down Expand Up @@ -704,6 +710,8 @@ def process_system_transaction(
account_writes={},
storage_writes={},
created_accounts=set(),
bytecode_accesses=set(),
ancestor_accesses=set(),
),
transient_storage={},
)
Expand Down Expand Up @@ -746,6 +754,7 @@ def process_checked_system_transaction(
system_contract_code = get_account(
block_env.state_tracking, target_address
).code
track_bytecode_access(block_env.state_tracking, system_contract_code)

if len(system_contract_code) == 0:
raise InvalidBlock(
Expand Down Expand Up @@ -796,6 +805,7 @@ def process_unchecked_system_transaction(
system_contract_code = get_account(
block_env.state_tracking, target_address
).code
track_bytecode_access(block_env.state_tracking, system_contract_code)
return process_system_transaction(
block_env,
target_address,
Expand Down Expand Up @@ -851,6 +861,11 @@ def apply_body(
target_address=HISTORY_STORAGE_ADDRESS,
data=block_env.block_hashes[-1], # The parent hash
)
# Track parent block access for witness generation
track_ancestor_access(
block_env.state_tracking,
U64(block_env.number - Uint(1)),
)

for i, tx in enumerate(map(decode_transaction, transactions)):
process_transaction(block_env, block_output, tx, Uint(i))
Expand Down Expand Up @@ -956,6 +971,8 @@ def process_transaction(
account_writes={},
storage_writes={},
created_accounts=set(),
bytecode_accesses=set(),
ancestor_accesses=set(),
)

# EIP-7928: Create a transaction-level StateChanges frame
Expand Down
54 changes: 53 additions & 1 deletion src/ethereum/forks/amsterdam/state_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ethereum_types.bytes import Bytes, Bytes32
from ethereum_types.frozen import modify
from ethereum_types.numeric import U256, Uint
from ethereum_types.numeric import U64, U256, Uint

from . import state as state_
from .fork_types import EMPTY_ACCOUNT, Account, Address
Expand Down Expand Up @@ -35,6 +35,10 @@ class BlockStateTracking:
Address, Dict[Bytes32, List[Tuple[BlockAccessIndex, U256]]]
]

bytecode_accesses: Set[Bytes]

ancestor_accesses: Set[U64]


@dataclass
class TxStateTracking:
Expand All @@ -52,6 +56,10 @@ class TxStateTracking:

created_accounts: Set[Address]

bytecode_accesses: Set[Bytes]

ancestor_accesses: Set[U64]


# get_account_optional
# set_account
Expand Down Expand Up @@ -162,6 +170,8 @@ def incorporate_tx_state_into_parent(tx_state: TxStateTracking) -> None:
for address, items in tx_state.storage_writes.items():
for key, value in items.items():
set_storage(parent, address, key, value)
parent.bytecode_accesses |= tx_state.bytecode_accesses
parent.ancestor_accesses |= tx_state.ancestor_accesses


def write_block_state_changes(block_state: BlockStateTracking) -> None:
Expand Down Expand Up @@ -193,6 +203,8 @@ def copy_tx_state_tracking(tx_state: TxStateTracking) -> TxStateTracking:
account_writes=tx_state.account_writes.copy(),
storage_writes=new_storage_writes,
created_accounts=tx_state.created_accounts.copy(),
bytecode_accesses=tx_state.bytecode_accesses.copy(),
ancestor_accesses=tx_state.ancestor_accesses.copy(),
)


Expand Down Expand Up @@ -518,3 +530,43 @@ def get_storage_original(
return U256(0)

return get_storage(state.parent, address, key)


def track_bytecode_access(
state: TxStateTracking | BlockStateTracking,
code: Bytes,
) -> None:
"""
Record that bytecode was accessed during execution.

Parameters
----------
state :
The state tracking object.
code :
The bytecode that was accessed.

"""
state.bytecode_accesses.add(code)


def track_ancestor_access(
state: TxStateTracking | BlockStateTracking,
block_number: U64,
) -> None:
"""
Record that an ancestor block was accessed.

Called by:
- BLOCKHASH opcode (when returning valid hash)
- System contracts reading parent headers (EIP-2935)

Parameters
----------
state :
The state tracking object.
block_number :
The block number that was accessed.

"""
state.ancestor_accesses.add(block_number)
2 changes: 2 additions & 0 deletions src/ethereum/forks/amsterdam/vm/eoa_delegation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
get_account,
increment_nonce,
set_authority_code,
track_bytecode_access,
)
from ..utils.hexadecimal import hex_to_address
from ..vm.gas import GAS_COLD_ACCOUNT_ACCESS, GAS_WARM_ACCESS
Expand Down Expand Up @@ -145,6 +146,7 @@ def calculate_delegation_cost(
"""
state = evm.state_tracking
code = get_account(state, address).code
track_bytecode_access(evm.state_tracking, code)
track_address(evm.state_changes, address)

if not is_valid_delegation(code):
Expand Down
4 changes: 3 additions & 1 deletion src/ethereum/forks/amsterdam/vm/instructions/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@
Implementations of the EVM block instructions.
"""

from ethereum_types.numeric import U256, Uint
from ethereum_types.numeric import U64, U256, Uint

from ...state_tracking import track_ancestor_access
from .. import Evm
from ..gas import GAS_BASE, GAS_BLOCK_HASH, charge_gas
from ..stack import pop, push
Expand Down Expand Up @@ -57,6 +58,7 @@ def block_hash(evm: Evm) -> None:
current_block_hash = evm.message.block_env.block_hashes[
-(current_block_number - block_number)
]
track_ancestor_access(evm.state_tracking, U64(block_number))

push(evm.stack, U256.from_be_bytes(current_block_hash))

Expand Down
4 changes: 3 additions & 1 deletion src/ethereum/forks/amsterdam/vm/instructions/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# track_address_access removed - now using state_changes.track_address()
from ...fork_types import EMPTY_ACCOUNT
from ...state_tracker import track_address
from ...state_tracking import get_account
from ...state_tracking import get_account, track_bytecode_access
from ...utils.address import to_address_masked
from ...vm.memory import buffer_read, memory_write
from .. import Evm
Expand Down Expand Up @@ -356,6 +356,7 @@ def extcodesize(evm: Evm) -> None:

# OPERATION
code = get_account(evm.state_tracking, address).code
track_bytecode_access(evm.state_tracking, code)
track_address(evm.state_changes, address)

codesize = U256(len(code))
Expand Down Expand Up @@ -402,6 +403,7 @@ def extcodecopy(evm: Evm) -> None:
# OPERATION
evm.memory += b"\x00" * extend_memory.expand_by
code = get_account(evm.state_tracking, address).code
track_bytecode_access(evm.state_tracking, code)
track_address(evm.state_changes, address)

value = buffer_read(code, code_start_index, size)
Expand Down
5 changes: 5 additions & 0 deletions src/ethereum/forks/amsterdam/vm/instructions/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
is_account_alive,
move_ether,
set_account_balance,
track_bytecode_access,
)
from ...utils.address import (
compute_contract_address,
Expand Down Expand Up @@ -458,6 +459,7 @@ def call(evm: Evm) -> None:
evm.accessed_addresses.add(code_address)

code = get_account(state, code_address).code
track_bytecode_access(evm.state_tracking, code)

message_call_gas = calculate_message_call_gas(
value,
Expand Down Expand Up @@ -561,6 +563,7 @@ def callcode(evm: Evm) -> None:
evm.accessed_addresses.add(code_address)

code = get_account(state, code_address).code
track_bytecode_access(evm.state_tracking, code)

message_call_gas = calculate_message_call_gas(
value,
Expand Down Expand Up @@ -757,6 +760,7 @@ def delegatecall(evm: Evm) -> None:
evm.accessed_addresses.add(code_address)

code = get_account(state, code_address).code
track_bytecode_access(evm.state_tracking, code)

message_call_gas = calculate_message_call_gas(
U256(0),
Expand Down Expand Up @@ -847,6 +851,7 @@ def staticcall(evm: Evm) -> None:
evm.accessed_addresses.add(code_address)

code = get_account(state, code_address).code
track_bytecode_access(evm.state_tracking, code)

message_call_gas = calculate_message_call_gas(
U256(0),
Expand Down
2 changes: 2 additions & 0 deletions src/ethereum/forks/amsterdam/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
mark_account_created,
move_ether,
set_code,
track_bytecode_access,
)
from ..vm import Message
from ..vm.eoa_delegation import get_delegated_code_address, set_delegation
Expand Down Expand Up @@ -141,6 +142,7 @@ def process_message_call(message: Message) -> MessageCallOutput:
message.disable_precompiles = True
message.accessed_addresses.add(delegated_address)
message.code = get_account(state_tracking, delegated_address).code
track_bytecode_access(message.state_tracking, message.code)
message.code_address = delegated_address
track_address(message.block_env.state_changes, delegated_address)

Expand Down
7 changes: 7 additions & 0 deletions src/ethereum_spec_tools/evm_tools/t8n/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ def block_environment(self) -> Any:
storage_reads=set(),
account_writes={},
storage_writes={},
bytecode_accesses=set(),
ancestor_accesses=set(),
)

return block_environment(**kw_arguments)
Expand Down Expand Up @@ -397,6 +399,11 @@ def _run_blockchain_test(self, block_env: Any, block_output: Any) -> None:
target_address=self.fork.HISTORY_STORAGE_ADDRESS,
data=block_env.block_hashes[-1], # The parent hash
)
# Track parent block access for witness generation
self.fork._module("state_tracking").track_ancestor_access(
block_env.state_tracking,
U64(block_env.number - Uint(1)),
)

if self.fork.has_beacon_roots_address:
self.fork.process_unchecked_system_transaction(
Expand Down