38
38
class AccountData :
39
39
"""
40
40
Account data stored in the builder during block execution.
41
-
41
+
42
42
This dataclass tracks all changes made to a single account throughout
43
43
the execution of a block, organized by the type of change and the
44
44
transaction index where it occurred.
45
45
"""
46
- storage_changes : Dict [Bytes , List [StorageChange ]] = field (default_factory = dict )
46
+
47
+ storage_changes : Dict [Bytes , List [StorageChange ]] = field (
48
+ default_factory = dict
49
+ )
47
50
"""
48
51
Mapping from storage slot to list of changes made to that slot.
49
52
Each change includes the transaction index and new value.
50
53
"""
51
-
54
+
52
55
storage_reads : Set [Bytes ] = field (default_factory = set )
53
56
"""
54
57
Set of storage slots that were read but not modified.
55
58
"""
56
-
59
+
57
60
balance_changes : List [BalanceChange ] = field (default_factory = list )
58
61
"""
59
62
List of balance changes for this account, ordered by transaction index.
60
63
"""
61
-
64
+
62
65
nonce_changes : List [NonceChange ] = field (default_factory = list )
63
66
"""
64
67
List of nonce changes for this account, ordered by transaction index.
65
68
"""
66
-
69
+
67
70
code_changes : List [CodeChange ] = field (default_factory = list )
68
71
"""
69
72
List of code changes (contract deployments) for this account,
@@ -76,14 +79,15 @@ class BlockAccessListBuilder:
76
79
"""
77
80
Builder for constructing [`BlockAccessList`] efficiently during transaction
78
81
execution.
79
-
82
+
80
83
The builder accumulates all account and storage accesses during block
81
84
execution and constructs a deterministic access list. Changes are tracked
82
85
by address, field type, and transaction index to enable efficient
83
86
reconstruction of state changes.
84
-
87
+
85
88
[`BlockAccessList`]: ref:ethereum.osaka.ssz_types.BlockAccessList
86
89
"""
90
+
87
91
accounts : Dict [Address , AccountData ] = field (default_factory = dict )
88
92
"""
89
93
Mapping from account address to its tracked changes during block execution.
@@ -93,18 +97,18 @@ class BlockAccessListBuilder:
93
97
def ensure_account (builder : BlockAccessListBuilder , address : Address ) -> None :
94
98
"""
95
99
Ensure an account exists in the builder's tracking structure.
96
-
100
+
97
101
Creates an empty [`AccountData`] entry for the given address if it
98
102
doesn't already exist. This function is idempotent and safe to call
99
103
multiple times for the same address.
100
-
104
+
101
105
Parameters
102
106
----------
103
107
builder :
104
108
The block access list builder instance.
105
109
address :
106
110
The account address to ensure exists.
107
-
111
+
108
112
[`AccountData`]: ref:ethereum.osaka.block_access_lists.builder.AccountData
109
113
"""
110
114
if address not in builder .accounts :
@@ -113,18 +117,18 @@ def ensure_account(builder: BlockAccessListBuilder, address: Address) -> None:
113
117
114
118
def add_storage_write (
115
119
builder : BlockAccessListBuilder ,
116
- address : Address ,
117
- slot : Bytes ,
118
- tx_index : U32 ,
119
- new_value : Bytes
120
+ address : Address ,
121
+ slot : Bytes ,
122
+ tx_index : U32 ,
123
+ new_value : Bytes ,
120
124
) -> None :
121
125
"""
122
126
Add a storage write operation to the block access list.
123
-
127
+
124
128
Records a storage slot modification for a given address at a specific
125
129
transaction index. Multiple writes to the same slot are tracked
126
130
separately, maintaining the order and transaction index of each change.
127
-
131
+
128
132
Parameters
129
133
----------
130
134
builder :
@@ -139,26 +143,24 @@ def add_storage_write(
139
143
The new value being written to the storage slot.
140
144
"""
141
145
ensure_account (builder , address )
142
-
146
+
143
147
if slot not in builder .accounts [address ].storage_changes :
144
148
builder .accounts [address ].storage_changes [slot ] = []
145
-
149
+
146
150
change = StorageChange (tx_index = tx_index , new_value = new_value )
147
151
builder .accounts [address ].storage_changes [slot ].append (change )
148
152
149
153
150
154
def add_storage_read (
151
- builder : BlockAccessListBuilder ,
152
- address : Address ,
153
- slot : Bytes
155
+ builder : BlockAccessListBuilder , address : Address , slot : Bytes
154
156
) -> None :
155
157
"""
156
158
Add a storage read operation to the block access list.
157
-
159
+
158
160
Records that a storage slot was read during execution. Storage slots
159
161
that are both read and written will only appear in the storage changes
160
162
list, not in the storage reads list, as per [EIP-7928].
161
-
163
+
162
164
Parameters
163
165
----------
164
166
builder :
@@ -167,7 +169,7 @@ def add_storage_read(
167
169
The account address whose storage is being read.
168
170
slot :
169
171
The storage slot being read.
170
-
172
+
171
173
[EIP-7928]: https://eips.ethereum.org/EIPS/eip-7928
172
174
"""
173
175
ensure_account (builder , address )
@@ -176,17 +178,17 @@ def add_storage_read(
176
178
177
179
def add_balance_change (
178
180
builder : BlockAccessListBuilder ,
179
- address : Address ,
180
- tx_index : U32 ,
181
- post_balance : Bytes
181
+ address : Address ,
182
+ tx_index : U32 ,
183
+ post_balance : Bytes ,
182
184
) -> None :
183
185
"""
184
186
Add a balance change to the block access list.
185
-
187
+
186
188
Records the post-transaction balance for an account after it has been
187
189
modified. This includes changes from transfers, gas fees, block rewards,
188
190
and any other balance-affecting operations.
189
-
191
+
190
192
Parameters
191
193
----------
192
194
builder :
@@ -199,24 +201,24 @@ def add_balance_change(
199
201
The account balance after the change, encoded as bytes.
200
202
"""
201
203
ensure_account (builder , address )
202
-
204
+
203
205
change = BalanceChange (tx_index = tx_index , post_balance = post_balance )
204
206
builder .accounts [address ].balance_changes .append (change )
205
207
206
208
207
209
def add_nonce_change (
208
210
builder : BlockAccessListBuilder ,
209
- address : Address ,
210
- tx_index : U32 ,
211
- new_nonce : U64
211
+ address : Address ,
212
+ tx_index : U32 ,
213
+ new_nonce : U64 ,
212
214
) -> None :
213
215
"""
214
216
Add a nonce change to the block access list.
215
-
217
+
216
218
Records a nonce increment for an account. This occurs when an EOA sends
217
219
a transaction or when a contract performs [`CREATE`] or [`CREATE2`]
218
220
operations.
219
-
221
+
220
222
Parameters
221
223
----------
222
224
builder :
@@ -227,29 +229,29 @@ def add_nonce_change(
227
229
The index of the transaction causing this change.
228
230
new_nonce :
229
231
The new nonce value after the change.
230
-
232
+
231
233
[`CREATE`]: ref:ethereum.osaka.vm.instructions.system.create
232
234
[`CREATE2`]: ref:ethereum.osaka.vm.instructions.system.create2
233
235
"""
234
236
ensure_account (builder , address )
235
-
237
+
236
238
change = NonceChange (tx_index = tx_index , new_nonce = new_nonce )
237
239
builder .accounts [address ].nonce_changes .append (change )
238
240
239
241
240
242
def add_code_change (
241
243
builder : BlockAccessListBuilder ,
242
- address : Address ,
243
- tx_index : U32 ,
244
- new_code : Bytes
244
+ address : Address ,
245
+ tx_index : U32 ,
246
+ new_code : Bytes ,
245
247
) -> None :
246
248
"""
247
249
Add a code change to the block access list.
248
-
250
+
249
251
Records contract code deployment or modification. This typically occurs
250
252
during contract creation via [`CREATE`], [`CREATE2`], or [`SETCODE`]
251
253
operations.
252
-
254
+
253
255
Parameters
254
256
----------
255
257
builder :
@@ -260,33 +262,35 @@ def add_code_change(
260
262
The index of the transaction deploying the code.
261
263
new_code :
262
264
The deployed contract bytecode.
263
-
265
+
264
266
[`CREATE`]: ref:ethereum.osaka.vm.instructions.system.create
265
267
[`CREATE2`]: ref:ethereum.osaka.vm.instructions.system.create2
266
268
[`SETCODE`]: ref:ethereum.osaka.vm.instructions.system.setcode
267
269
"""
268
270
ensure_account (builder , address )
269
-
271
+
270
272
change = CodeChange (tx_index = tx_index , new_code = new_code )
271
273
builder .accounts [address ].code_changes .append (change )
272
274
273
275
274
- def add_touched_account (builder : BlockAccessListBuilder , address : Address ) -> None :
276
+ def add_touched_account (
277
+ builder : BlockAccessListBuilder , address : Address
278
+ ) -> None :
275
279
"""
276
280
Add an account that was accessed but not modified.
277
-
281
+
278
282
Records that an account was accessed during execution without any state
279
283
changes. This is used for operations like [`EXTCODEHASH`], [`BALANCE`],
280
284
[`EXTCODESIZE`], and [`EXTCODECOPY`] that read account data without
281
285
modifying it.
282
-
286
+
283
287
Parameters
284
288
----------
285
289
builder :
286
290
The block access list builder instance.
287
291
address :
288
292
The account address that was accessed.
289
-
293
+
290
294
[`EXTCODEHASH`]: ref:ethereum.osaka.vm.instructions.environment.extcodehash
291
295
[`BALANCE`]: ref:ethereum.osaka.vm.instructions.environment.balance
292
296
[`EXTCODESIZE`]: ref:ethereum.osaka.vm.instructions.environment.extcodesize
@@ -298,58 +302,68 @@ def add_touched_account(builder: BlockAccessListBuilder, address: Address) -> No
298
302
def build (builder : BlockAccessListBuilder ) -> BlockAccessList :
299
303
"""
300
304
Build the final [`BlockAccessList`] from accumulated changes.
301
-
305
+
302
306
Constructs a deterministic block access list by sorting all accumulated
303
307
changes. The resulting list is ordered by:
304
-
308
+
305
309
1. Account addresses (lexicographically)
306
310
2. Within each account:
307
311
- Storage slots (lexicographically)
308
312
- Transaction indices (numerically) for each change type
309
-
313
+
310
314
Parameters
311
315
----------
312
316
builder :
313
317
The block access list builder containing all tracked changes.
314
-
318
+
315
319
Returns
316
320
-------
317
321
block_access_list :
318
322
The final sorted and encoded block access list.
319
-
323
+
320
324
[`BlockAccessList`]: ref:ethereum.osaka.ssz_types.BlockAccessList
321
325
"""
322
326
account_changes_list = []
323
-
327
+
324
328
for address , changes in builder .accounts .items ():
325
329
storage_changes = []
326
330
for slot , slot_changes in changes .storage_changes .items ():
327
- sorted_changes = tuple (sorted (slot_changes , key = lambda x : x .tx_index ))
328
- storage_changes .append (SlotChanges (slot = slot , changes = sorted_changes ))
329
-
331
+ sorted_changes = tuple (
332
+ sorted (slot_changes , key = lambda x : x .tx_index )
333
+ )
334
+ storage_changes .append (
335
+ SlotChanges (slot = slot , changes = sorted_changes )
336
+ )
337
+
330
338
storage_reads = []
331
339
for slot in changes .storage_reads :
332
340
if slot not in changes .storage_changes :
333
341
storage_reads .append (slot )
334
-
335
- balance_changes = tuple (sorted (changes .balance_changes , key = lambda x : x .tx_index ))
336
- nonce_changes = tuple (sorted (changes .nonce_changes , key = lambda x : x .tx_index ))
337
- code_changes = tuple (sorted (changes .code_changes , key = lambda x : x .tx_index ))
338
-
342
+
343
+ balance_changes = tuple (
344
+ sorted (changes .balance_changes , key = lambda x : x .tx_index )
345
+ )
346
+ nonce_changes = tuple (
347
+ sorted (changes .nonce_changes , key = lambda x : x .tx_index )
348
+ )
349
+ code_changes = tuple (
350
+ sorted (changes .code_changes , key = lambda x : x .tx_index )
351
+ )
352
+
339
353
storage_changes .sort (key = lambda x : x .slot )
340
354
storage_reads .sort ()
341
-
355
+
342
356
account_change = AccountChanges (
343
357
address = address ,
344
358
storage_changes = tuple (storage_changes ),
345
359
storage_reads = tuple (storage_reads ),
346
360
balance_changes = balance_changes ,
347
361
nonce_changes = nonce_changes ,
348
- code_changes = code_changes
362
+ code_changes = code_changes ,
349
363
)
350
-
364
+
351
365
account_changes_list .append (account_change )
352
-
366
+
353
367
account_changes_list .sort (key = lambda x : x .address )
354
-
355
- return BlockAccessList (account_changes = tuple (account_changes_list ))
368
+
369
+ return BlockAccessList (account_changes = tuple (account_changes_list ))
0 commit comments