Skip to content

Commit 63af451

Browse files
authored
refactor(spec-specs): Refactor state changes and frame hierarchy (#1841)
* refactor(spec-specs): Refactor state changes and their frames * chore(spec-specs): cleanup BAL logic; organize gas check for SSTORE * refactor(spec-specs): Changes from comments on PR #1841 * enhance: don't set defaults for state_changes
1 parent 54d7dc0 commit 63af451

File tree

10 files changed

+488
-602
lines changed

10 files changed

+488
-602
lines changed

src/ethereum/forks/amsterdam/fork.py

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
from . import vm
3232
from .block_access_lists.builder import build_block_access_list
33-
from .block_access_lists.rlp_types import BlockAccessIndex
3433
from .block_access_lists.rlp_utils import compute_block_access_list_hash
3534
from .blocks import Block, Header, Log, Receipt, Withdrawal, encode_receipt
3635
from .bloom import logs_bloom
@@ -65,16 +64,16 @@
6564
state_root,
6665
)
6766
from .state_tracker import (
67+
StateChanges,
6868
capture_pre_balance,
6969
commit_transaction_frame,
7070
create_child_frame,
71-
get_block_access_index,
72-
handle_in_transaction_selfdestruct,
71+
filter_net_zero_frame_changes,
7372
increment_block_access_index,
74-
merge_on_success,
75-
normalize_balance_changes_for_transaction,
7673
track_address,
7774
track_balance_change,
75+
track_nonce_change,
76+
track_selfdestruct,
7877
)
7978
from .transactions import (
8079
AccessListTransaction,
@@ -247,6 +246,7 @@ def state_transition(chain: BlockChain, block: Block) -> None:
247246
prev_randao=block.header.prev_randao,
248247
excess_blob_gas=block.header.excess_blob_gas,
249248
parent_beacon_block_root=block.header.parent_beacon_block_root,
249+
state_changes=StateChanges(),
250250
)
251251

252252
block_output = apply_body(
@@ -634,7 +634,7 @@ def process_system_transaction(
634634
"""
635635
# EIP-7928: Create a child frame for system transaction
636636
# This allows proper pre-state capture for net-zero filtering
637-
system_tx_state_changes = create_child_frame(block_env.block_state_changes)
637+
system_tx_state_changes = create_child_frame(block_env.state_changes)
638638

639639
tx_env = vm.TransactionEnvironment(
640640
origin=SYSTEM_ADDRESS,
@@ -647,8 +647,12 @@ def process_system_transaction(
647647
authorizations=(),
648648
index_in_block=None,
649649
tx_hash=None,
650+
state_changes=system_tx_state_changes,
650651
)
651652

653+
# Create call frame as child of tx frame
654+
call_frame = create_child_frame(tx_env.state_changes)
655+
652656
system_tx_message = Message(
653657
block_env=block_env,
654658
tx_env=tx_env,
@@ -667,14 +671,15 @@ def process_system_transaction(
667671
accessed_storage_keys=set(),
668672
disable_precompiles=False,
669673
parent_evm=None,
670-
transaction_state_changes=system_tx_state_changes,
674+
is_create=False,
675+
state_changes=call_frame,
671676
)
672677

673678
system_tx_output = process_message_call(system_tx_message)
674679

675-
# Merge system transaction changes back to block frame
680+
# Commit system transaction changes to block frame
676681
# System transactions always succeed (or block is invalid)
677-
merge_on_success(system_tx_state_changes)
682+
commit_transaction_frame(tx_env.state_changes)
678683

679684
return system_tx_output
680685

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

819824
process_withdrawals(block_env, block_output, withdrawals)
820825

821826
process_general_purpose_requests(
822827
block_env=block_env,
823828
block_output=block_output,
824829
)
825-
# Build block access list from block_env.block_state_changes
830+
# Build block access list from block_env.state_changes
826831
block_output.block_access_list = build_block_access_list(
827-
block_env.block_state_changes
832+
block_env.state_changes
828833
)
829834

830835
return block_output
@@ -907,9 +912,10 @@ def process_transaction(
907912
"""
908913
# EIP-7928: Create a transaction-level StateChanges frame
909914
# The frame will read the current block_access_index from the block frame
910-
increment_block_access_index(block_env.block_state_changes)
911-
tx_state_changes = create_child_frame(block_env.block_state_changes)
915+
increment_block_access_index(block_env.state_changes)
916+
tx_state_changes = create_child_frame(block_env.state_changes)
912917

918+
# Capture coinbase pre-balance for net-zero filtering
913919
coinbase_pre_balance = get_account(
914920
block_env.state, block_env.coinbase
915921
).balance
@@ -947,16 +953,27 @@ def process_transaction(
947953
effective_gas_fee = tx.gas * effective_gas_price
948954

949955
gas = tx.gas - intrinsic_gas
950-
increment_nonce(block_env.state, sender, tx_state_changes)
956+
957+
# Track sender nonce increment
958+
increment_nonce(block_env.state, sender)
959+
sender_nonce_after = get_account(block_env.state, sender).nonce
960+
track_nonce_change(tx_state_changes, sender, U64(sender_nonce_after))
961+
962+
# Track sender balance deduction for gas fee
963+
sender_balance_before = get_account(block_env.state, sender).balance
964+
track_address(tx_state_changes, sender)
965+
capture_pre_balance(tx_state_changes, sender, sender_balance_before)
951966

952967
sender_balance_after_gas_fee = (
953968
Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
954969
)
955970
set_account_balance(
956-
block_env.state,
971+
block_env.state, sender, U256(sender_balance_after_gas_fee)
972+
)
973+
track_balance_change(
974+
tx_state_changes,
957975
sender,
958976
U256(sender_balance_after_gas_fee),
959-
tx_state_changes,
960977
)
961978

962979
access_list_addresses = set()
@@ -991,13 +1008,13 @@ def process_transaction(
9911008
authorizations=authorizations,
9921009
index_in_block=index,
9931010
tx_hash=get_transaction_hash(encode_transaction(tx)),
1011+
state_changes=tx_state_changes,
9941012
)
9951013

9961014
message = prepare_message(
9971015
block_env,
9981016
tx_env,
9991017
tx,
1000-
tx_state_changes,
10011018
)
10021019

10031020
tx_output = process_message_call(message)
@@ -1027,22 +1044,24 @@ def process_transaction(
10271044
sender_balance_after_refund = get_account(
10281045
block_env.state, sender
10291046
).balance + U256(gas_refund_amount)
1030-
set_account_balance(
1031-
block_env.state,
1047+
set_account_balance(block_env.state, sender, sender_balance_after_refund)
1048+
track_balance_change(
1049+
tx_env.state_changes,
10321050
sender,
10331051
sender_balance_after_refund,
1034-
tx_state_changes,
10351052
)
10361053

10371054
coinbase_balance_after_mining_fee = get_account(
10381055
block_env.state, block_env.coinbase
10391056
).balance + U256(transaction_fee)
10401057

10411058
set_account_balance(
1042-
block_env.state,
1059+
block_env.state, block_env.coinbase, coinbase_balance_after_mining_fee
1060+
)
1061+
track_balance_change(
1062+
tx_env.state_changes,
10431063
block_env.coinbase,
10441064
coinbase_balance_after_mining_fee,
1045-
tx_state_changes,
10461065
)
10471066

10481067
if coinbase_balance_after_mining_fee == 0 and account_exists_and_is_empty(
@@ -1068,35 +1087,19 @@ def process_transaction(
10681087

10691088
block_output.block_logs += tx_output.logs
10701089

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

1077-
# EIP-7928: Normalize balance changes for this transaction before merging
1078-
# into block frame. Must happen AFTER destroy_account so net-zero filtering
1079-
# sees the correct post-transaction balance (0 for destroyed accounts).
1080-
normalize_balance_changes_for_transaction(
1081-
tx_state_changes,
1082-
BlockAccessIndex(
1083-
get_block_access_index(block_env.block_state_changes)
1084-
),
1085-
block_env.state,
1086-
)
1093+
# EIP-7928: Filter net-zero changes before committing to block frame.
1094+
# Must happen AFTER destroy_account so filtering sees correct state.
1095+
filter_net_zero_frame_changes(tx_env.state_changes, block_env.state)
10871096

1088-
commit_transaction_frame(tx_state_changes)
1097+
commit_transaction_frame(tx_env.state_changes)
10891098

1090-
# EIP-7928: Handle in-transaction self-destruct normalization AFTER merge
1099+
# EIP-7928: Track in-transaction self-destruct normalization AFTER merge
10911100
# Convert storage writes to reads and remove nonce/code changes
10921101
for address in tx_output.accounts_to_delete:
1093-
handle_in_transaction_selfdestruct(
1094-
block_env.block_state_changes,
1095-
address,
1096-
BlockAccessIndex(
1097-
get_block_access_index(block_env.block_state_changes)
1098-
),
1099-
)
1102+
track_selfdestruct(block_env.state_changes, address)
11001103

11011104

11021105
def process_withdrawals(
@@ -1107,13 +1110,12 @@ def process_withdrawals(
11071110
"""
11081111
Increase the balance of the withdrawing account.
11091112
"""
1113+
# Capture pre-state for withdrawal balance filtering
11101114
withdrawal_addresses = {wd.address for wd in withdrawals}
11111115
for address in withdrawal_addresses:
11121116
pre_balance = get_account(block_env.state, address).balance
1113-
track_address(block_env.block_state_changes, address)
1114-
capture_pre_balance(
1115-
block_env.block_state_changes, address, pre_balance
1116-
)
1117+
track_address(block_env.state_changes, address)
1118+
capture_pre_balance(block_env.state_changes, address, pre_balance)
11171119

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

11301132
new_balance = get_account(block_env.state, wd.address).balance
11311133
track_balance_change(
1132-
block_env.block_state_changes, wd.address, new_balance
1134+
block_env.state_changes,
1135+
wd.address,
1136+
new_balance,
11331137
)
11341138

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

1138-
# EIP-7928: Normalize balance changes after all withdrawals
1139-
# Filters out net-zero changes
1140-
1141-
normalize_balance_changes_for_transaction(
1142-
block_env.block_state_changes,
1143-
BlockAccessIndex(
1144-
get_block_access_index(block_env.block_state_changes)
1145-
),
1146-
block_env.state,
1147-
)
1142+
# EIP-7928: Filter net-zero balance changes for withdrawals
1143+
filter_net_zero_frame_changes(block_env.state_changes, block_env.state)
11481144

11491145

11501146
def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool:

0 commit comments

Comments
 (0)