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
114 changes: 54 additions & 60 deletions src/ethereum/forks/amsterdam/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

from . import vm
from .block_access_lists.builder import build_block_access_list
from .block_access_lists.rlp_types import BlockAccessIndex
from .block_access_lists.rlp_utils import compute_block_access_list_hash
from .blocks import Block, Header, Log, Receipt, Withdrawal, encode_receipt
from .bloom import logs_bloom
Expand Down Expand Up @@ -68,13 +67,12 @@
capture_pre_balance,
commit_transaction_frame,
create_child_frame,
get_block_access_index,
handle_in_transaction_selfdestruct,
filter_net_zero_frame_changes,
increment_block_access_index,
merge_on_success,
normalize_balance_changes_for_transaction,
track_address,
track_balance_change,
track_nonce_change,
track_selfdestruct,
)
from .transactions import (
AccessListTransaction,
Expand Down Expand Up @@ -634,7 +632,7 @@ def process_system_transaction(
"""
# EIP-7928: Create a child frame for system transaction
# This allows proper pre-state capture for net-zero filtering
system_tx_state_changes = create_child_frame(block_env.block_state_changes)
system_tx_state_changes = create_child_frame(block_env.state_changes)

tx_env = vm.TransactionEnvironment(
origin=SYSTEM_ADDRESS,
Expand All @@ -647,8 +645,12 @@ def process_system_transaction(
authorizations=(),
index_in_block=None,
tx_hash=None,
state_changes=system_tx_state_changes,
)

# Create call frame as child of tx frame
call_frame = create_child_frame(tx_env.state_changes)

system_tx_message = Message(
block_env=block_env,
tx_env=tx_env,
Expand All @@ -667,14 +669,15 @@ def process_system_transaction(
accessed_storage_keys=set(),
disable_precompiles=False,
parent_evm=None,
transaction_state_changes=system_tx_state_changes,
is_create=False,
state_changes=call_frame,
)

system_tx_output = process_message_call(system_tx_message)

# Merge system transaction changes back to block frame
# Commit system transaction changes to block frame
# System transactions always succeed (or block is invalid)
merge_on_success(system_tx_state_changes)
commit_transaction_frame(tx_env.state_changes)

return system_tx_output

Expand Down Expand Up @@ -814,17 +817,17 @@ def apply_body(
# EIP-7928: Increment block frame to post-execution index
# After N transactions, block frame is at index N
# Post-execution operations (withdrawals, etc.) use index N+1
increment_block_access_index(block_env.block_state_changes)
increment_block_access_index(block_env.state_changes)

process_withdrawals(block_env, block_output, withdrawals)

process_general_purpose_requests(
block_env=block_env,
block_output=block_output,
)
# Build block access list from block_env.block_state_changes
# Build block access list from block_env.state_changes
block_output.block_access_list = build_block_access_list(
block_env.block_state_changes
block_env.state_changes
)

return block_output
Expand Down Expand Up @@ -907,9 +910,10 @@ def process_transaction(
"""
# EIP-7928: Create a transaction-level StateChanges frame
# The frame will read the current block_access_index from the block frame
increment_block_access_index(block_env.block_state_changes)
tx_state_changes = create_child_frame(block_env.block_state_changes)
increment_block_access_index(block_env.state_changes)
tx_state_changes = create_child_frame(block_env.state_changes)

# Capture coinbase pre-balance for net-zero filtering
coinbase_pre_balance = get_account(
block_env.state, block_env.coinbase
).balance
Expand Down Expand Up @@ -947,16 +951,27 @@ def process_transaction(
effective_gas_fee = tx.gas * effective_gas_price

gas = tx.gas - intrinsic_gas
increment_nonce(block_env.state, sender, tx_state_changes)

# Track sender nonce increment
increment_nonce(block_env.state, sender)
sender_nonce_after = get_account(block_env.state, sender).nonce
track_nonce_change(tx_state_changes, sender, U64(sender_nonce_after))

# Track sender balance deduction for gas fee
sender_balance_before = get_account(block_env.state, sender).balance
track_address(tx_state_changes, sender)
capture_pre_balance(tx_state_changes, sender, sender_balance_before)

sender_balance_after_gas_fee = (
Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
)
set_account_balance(
block_env.state,
block_env.state, sender, U256(sender_balance_after_gas_fee)
)
track_balance_change(
tx_state_changes,
sender,
U256(sender_balance_after_gas_fee),
tx_state_changes,
)

access_list_addresses = set()
Expand Down Expand Up @@ -991,13 +1006,13 @@ def process_transaction(
authorizations=authorizations,
index_in_block=index,
tx_hash=get_transaction_hash(encode_transaction(tx)),
state_changes=tx_state_changes,
)

message = prepare_message(
block_env,
tx_env,
tx,
tx_state_changes,
)

tx_output = process_message_call(message)
Expand Down Expand Up @@ -1027,22 +1042,24 @@ def process_transaction(
sender_balance_after_refund = get_account(
block_env.state, sender
).balance + U256(gas_refund_amount)
set_account_balance(
block_env.state,
set_account_balance(block_env.state, sender, sender_balance_after_refund)
track_balance_change(
tx_env.state_changes,
sender,
sender_balance_after_refund,
tx_state_changes,
)

coinbase_balance_after_mining_fee = get_account(
block_env.state, block_env.coinbase
).balance + U256(transaction_fee)

set_account_balance(
block_env.state,
block_env.state, block_env.coinbase, coinbase_balance_after_mining_fee
)
track_balance_change(
tx_env.state_changes,
block_env.coinbase,
coinbase_balance_after_mining_fee,
tx_state_changes,
)

if coinbase_balance_after_mining_fee == 0 and account_exists_and_is_empty(
Expand All @@ -1068,35 +1085,19 @@ def process_transaction(

block_output.block_logs += tx_output.logs

# EIP-7928: Handle in-transaction self-destruct BEFORE normalization
# Destroy accounts first so normalization sees correct post-tx state
# Only accounts created in same tx are in accounts_to_delete per EIP-6780
for address in tx_output.accounts_to_delete:
destroy_account(block_env.state, address)

# EIP-7928: Normalize balance changes for this transaction before merging
# into block frame. Must happen AFTER destroy_account so net-zero filtering
# sees the correct post-transaction balance (0 for destroyed accounts).
normalize_balance_changes_for_transaction(
tx_state_changes,
BlockAccessIndex(
get_block_access_index(block_env.block_state_changes)
),
block_env.state,
)
# EIP-7928: Filter net-zero changes before committing to block frame.
# Must happen AFTER destroy_account so filtering sees correct state.
filter_net_zero_frame_changes(tx_env.state_changes, block_env.state)

commit_transaction_frame(tx_state_changes)
commit_transaction_frame(tx_env.state_changes)

# EIP-7928: Handle in-transaction self-destruct normalization AFTER merge
# EIP-7928: Track in-transaction self-destruct normalization AFTER merge
# Convert storage writes to reads and remove nonce/code changes
for address in tx_output.accounts_to_delete:
handle_in_transaction_selfdestruct(
block_env.block_state_changes,
address,
BlockAccessIndex(
get_block_access_index(block_env.block_state_changes)
),
)
track_selfdestruct(block_env.state_changes, address)


def process_withdrawals(
Expand All @@ -1107,13 +1108,12 @@ def process_withdrawals(
"""
Increase the balance of the withdrawing account.
"""
# Capture pre-state for withdrawal balance filtering
withdrawal_addresses = {wd.address for wd in withdrawals}
for address in withdrawal_addresses:
pre_balance = get_account(block_env.state, address).balance
track_address(block_env.block_state_changes, address)
capture_pre_balance(
block_env.block_state_changes, address, pre_balance
)
track_address(block_env.state_changes, address)
capture_pre_balance(block_env.state_changes, address, pre_balance)

def increase_recipient_balance(recipient: Account) -> None:
recipient.balance += wd.amount * U256(10**9)
Expand All @@ -1129,22 +1129,16 @@ def increase_recipient_balance(recipient: Account) -> None:

new_balance = get_account(block_env.state, wd.address).balance
track_balance_change(
block_env.block_state_changes, wd.address, new_balance
block_env.state_changes,
wd.address,
new_balance,
)

if account_exists_and_is_empty(block_env.state, wd.address):
destroy_account(block_env.state, wd.address)

# EIP-7928: Normalize balance changes after all withdrawals
# Filters out net-zero changes

normalize_balance_changes_for_transaction(
block_env.block_state_changes,
BlockAccessIndex(
get_block_access_index(block_env.block_state_changes)
),
block_env.state,
)
# EIP-7928: Filter net-zero balance changes for withdrawals
filter_net_zero_frame_changes(block_env.state_changes, block_env.state)


def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool:
Expand Down
Loading
Loading