1use crate::{
6 SlotChanges, balance_change::BalanceChange, code_change::CodeChange, nonce_change::NonceChange,
7};
8use alloc::vec::Vec;
9use alloy_primitives::{
10 Address, U256,
11 map::{HashMap, HashSet},
12};
13
14#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
16#[cfg_attr(feature = "rlp", derive(alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable))]
17#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
19#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
20#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
21pub struct AccountChanges {
22 pub address: Address,
24 pub storage_changes: Vec<SlotChanges>,
26 pub storage_reads: Vec<U256>,
28 pub balance_changes: Vec<BalanceChange>,
30 pub nonce_changes: Vec<NonceChange>,
32 pub code_changes: Vec<CodeChange>,
34}
35
36impl AccountChanges {
37 pub const fn new(address: Address) -> Self {
39 Self {
40 address,
41 storage_changes: Vec::new(),
42 storage_reads: Vec::new(),
43 balance_changes: Vec::new(),
44 nonce_changes: Vec::new(),
45 code_changes: Vec::new(),
46 }
47 }
48
49 pub fn with_capacity(address: Address, capacity: usize) -> Self {
51 Self {
52 address,
53 storage_changes: Vec::with_capacity(capacity),
54 storage_reads: Vec::with_capacity(capacity),
55 balance_changes: Vec::with_capacity(capacity),
56 nonce_changes: Vec::with_capacity(capacity),
57 code_changes: Vec::with_capacity(capacity),
58 }
59 }
60
61 #[inline]
63 pub const fn address(&self) -> Address {
64 self.address
65 }
66
67 #[inline]
69 pub fn storage_changes(&self) -> &[SlotChanges] {
70 &self.storage_changes
71 }
72
73 #[inline]
77 pub fn storage_post_states(&self) -> impl Iterator<Item = (U256, U256)> + '_ {
78 self.storage_changes.iter().filter_map(|changes| {
79 changes.changes.last().map(|change| (changes.slot, change.new_value))
80 })
81 }
82
83 pub fn merge(&mut self, incoming: Self) {
98 assert_eq!(
99 self.address, incoming.address,
100 "cannot merge account changes for different addresses"
101 );
102
103 merge_slot_changes(&mut self.storage_changes, incoming.storage_changes);
104 self.storage_reads.extend(incoming.storage_reads);
105 self.balance_changes.extend(incoming.balance_changes);
106 self.nonce_changes.extend(incoming.nonce_changes);
107 self.code_changes.extend(incoming.code_changes);
108
109 let written = self
110 .storage_changes
111 .iter()
112 .map(|slot_changes| slot_changes.slot)
113 .collect::<HashSet<_>>();
114 self.storage_reads.retain(|slot| !written.contains(slot));
115
116 let mut seen = HashSet::with_capacity(self.storage_reads.len());
117 self.storage_reads.retain(|slot| seen.insert(*slot));
118 }
119
120 #[inline]
122 pub fn storage_reads(&self) -> &[U256] {
123 &self.storage_reads
124 }
125
126 #[inline]
128 pub fn balance_changes(&self) -> &[BalanceChange] {
129 &self.balance_changes
130 }
131
132 #[inline]
134 pub fn nonce_changes(&self) -> &[NonceChange] {
135 &self.nonce_changes
136 }
137
138 #[inline]
140 pub fn code_changes(&self) -> &[CodeChange] {
141 &self.code_changes
142 }
143
144 pub fn sort(&mut self) {
161 self.storage_changes.sort_unstable_by_key(|changes| changes.slot);
162 for slot_changes in &mut self.storage_changes {
163 slot_changes.sort();
164 }
165
166 self.storage_reads.sort_unstable();
167 self.balance_changes.sort_unstable_by_key(|change| change.block_access_index);
168 self.nonce_changes.sort_unstable_by_key(|change| change.block_access_index);
169 self.code_changes.sort_unstable_by_key(|change| change.block_access_index);
170 }
171
172 pub const fn with_address(mut self, address: Address) -> Self {
174 self.address = address;
175 self
176 }
177
178 pub fn with_storage_read(mut self, key: U256) -> Self {
180 self.storage_reads.push(key);
181 self
182 }
183
184 pub fn with_storage_change(mut self, change: SlotChanges) -> Self {
186 self.storage_changes.push(change);
187 self
188 }
189
190 pub fn with_balance_change(mut self, change: BalanceChange) -> Self {
192 self.balance_changes.push(change);
193 self
194 }
195
196 pub fn with_nonce_change(mut self, change: NonceChange) -> Self {
198 self.nonce_changes.push(change);
199 self
200 }
201
202 pub fn with_code_change(mut self, change: CodeChange) -> Self {
204 self.code_changes.push(change);
205 self
206 }
207
208 pub fn extend_storage_reads<I>(mut self, iter: I) -> Self
210 where
211 I: IntoIterator<Item = U256>,
212 {
213 self.storage_reads.extend(iter);
214 self
215 }
216
217 pub fn extend_storage_changes<I>(mut self, iter: I) -> Self
219 where
220 I: IntoIterator<Item = SlotChanges>,
221 {
222 self.storage_changes.extend(iter);
223 self
224 }
225}
226
227fn merge_slot_changes(existing: &mut Vec<SlotChanges>, incoming: Vec<SlotChanges>) {
228 let mut slot_positions = existing
229 .iter()
230 .enumerate()
231 .map(|(idx, slot_changes)| (slot_changes.slot, idx))
232 .collect::<HashMap<_, _>>();
233
234 for slot_changes in incoming {
235 if let Some(&idx) = slot_positions.get(&slot_changes.slot) {
236 existing[idx].changes.extend(slot_changes.changes);
237 } else {
238 slot_positions.insert(slot_changes.slot, existing.len());
239 existing.push(slot_changes);
240 }
241 }
242}
243
244#[cfg(test)]
245mod merge_tests {
246 use crate::{BlockAccessIndex, StorageChange};
247
248 use super::*;
249 use alloy_primitives::Bytes;
250
251 #[test]
252 fn merge_groups_slot_changes_and_appends_account_changes() {
253 let address = Address::from([0x11; 20]);
254 let mut existing = AccountChanges {
255 address,
256 storage_changes: vec![SlotChanges::new(
257 U256::from(1),
258 vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(10))],
259 )],
260 storage_reads: vec![U256::from(3)],
261 balance_changes: vec![BalanceChange::new(BlockAccessIndex::new(1), U256::from(100))],
262 nonce_changes: vec![NonceChange::new(BlockAccessIndex::new(2), 7)],
263 code_changes: vec![],
264 };
265 let incoming = AccountChanges {
266 address,
267 storage_changes: vec![
268 SlotChanges::new(
269 U256::from(1),
270 vec![StorageChange::new(BlockAccessIndex::new(3), U256::from(20))],
271 ),
272 SlotChanges::new(
273 U256::from(2),
274 vec![StorageChange::new(BlockAccessIndex::new(4), U256::from(30))],
275 ),
276 ],
277 storage_reads: vec![U256::from(4)],
278 balance_changes: vec![BalanceChange::new(BlockAccessIndex::new(5), U256::from(150))],
279 nonce_changes: vec![NonceChange::new(BlockAccessIndex::new(6), 8)],
280 code_changes: vec![CodeChange::new(
281 BlockAccessIndex::new(7),
282 Bytes::from_static(&[0xaa]),
283 )],
284 };
285
286 existing.merge(incoming);
287
288 assert_eq!(existing.storage_reads, vec![U256::from(3), U256::from(4)]);
289 assert_eq!(
290 existing.storage_changes.iter().map(|changes| changes.slot).collect::<Vec<_>>(),
291 vec![U256::from(1), U256::from(2)]
292 );
293 assert_eq!(
294 existing.storage_changes[0]
295 .changes
296 .iter()
297 .map(|change| change.new_value)
298 .collect::<Vec<_>>(),
299 vec![U256::from(10), U256::from(20)]
300 );
301 assert_eq!(existing.balance_changes.len(), 2);
302 assert_eq!(existing.nonce_changes.len(), 2);
303 assert_eq!(existing.code_changes.len(), 1);
304 }
305
306 #[test]
307 fn merge_normalizes_storage_reads_after_cross_block_merge() {
308 let address = Address::from([0x33; 20]);
309 const A: U256 = U256::from_limbs([1, 0, 0, 0]);
310 const B: U256 = U256::from_limbs([2, 0, 0, 0]);
311 const C: U256 = U256::from_limbs([3, 0, 0, 0]);
312 const D: U256 = U256::from_limbs([4, 0, 0, 0]);
313
314 let mut existing = AccountChanges {
315 address,
316 storage_changes: vec![SlotChanges::new(
317 A,
318 vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(10))],
319 )],
320 storage_reads: vec![B, C],
321 balance_changes: vec![],
322 nonce_changes: vec![],
323 code_changes: vec![],
324 };
325 let incoming = AccountChanges {
326 address,
327 storage_changes: vec![SlotChanges::new(
328 B,
329 vec![StorageChange::new(BlockAccessIndex::new(1), U256::from(20))],
330 )],
331 storage_reads: vec![A, C, D],
332 balance_changes: vec![],
333 nonce_changes: vec![],
334 code_changes: vec![],
335 };
336
337 existing.merge(incoming);
338
339 assert_eq!(
340 existing
341 .storage_changes
342 .iter()
343 .map(|slot_changes| slot_changes.slot)
344 .collect::<Vec<_>>(),
345 vec![A, B]
346 );
347 assert_eq!(existing.storage_reads, vec![C, D]);
348 assert!(existing.storage_reads.iter().all(|read_slot| {
349 !existing.storage_changes.iter().any(|slot_changes| slot_changes.slot == *read_slot)
350 }));
351 }
352
353 #[test]
354 #[should_panic(expected = "cannot merge account changes for different addresses")]
355 fn merge_rejects_different_addresses() {
356 let mut existing = AccountChanges::new(Address::from([0x11; 20]));
357 let incoming = AccountChanges::new(Address::from([0x22; 20]));
358
359 existing.merge(incoming);
360 }
361}
362
363#[cfg(test)]
364mod sort_tests {
365 use crate::{BlockAccessIndex, StorageChange};
366
367 use super::*;
368 use alloy_primitives::Bytes;
369
370 #[test]
371 fn sort_orders_account_local_eip7928_lists() {
372 let mut account = AccountChanges {
373 address: Address::from([0x11; 20]),
374 storage_changes: vec![
375 SlotChanges::new(
376 U256::from(3),
377 vec![
378 StorageChange::new(BlockAccessIndex::new(8), U256::from(0x80)),
379 StorageChange::new(BlockAccessIndex::new(2), U256::from(0x20)),
380 ],
381 ),
382 SlotChanges::new(
383 U256::from(1),
384 vec![
385 StorageChange::new(BlockAccessIndex::new(5), U256::from(0x50)),
386 StorageChange::new(BlockAccessIndex::new(1), U256::from(0x10)),
387 ],
388 ),
389 ],
390 storage_reads: vec![U256::from(4), U256::from(2)],
391 balance_changes: vec![
392 BalanceChange::new(BlockAccessIndex::new(6), U256::from(600)),
393 BalanceChange::new(BlockAccessIndex::new(3), U256::from(300)),
394 ],
395 nonce_changes: vec![
396 NonceChange::new(BlockAccessIndex::new(7), 70),
397 NonceChange::new(BlockAccessIndex::new(4), 40),
398 ],
399 code_changes: vec![
400 CodeChange::new(BlockAccessIndex::new(9), Bytes::from_static(&[0x60, 0x09])),
401 CodeChange::new(BlockAccessIndex::new(5), Bytes::from_static(&[0x60, 0x05])),
402 ],
403 };
404
405 account.sort();
406
407 assert_eq!(
408 account.storage_changes.iter().map(|changes| changes.slot).collect::<Vec<_>>(),
409 vec![U256::from(1), U256::from(3)]
410 );
411 assert_eq!(
412 account.storage_changes[0]
413 .changes
414 .iter()
415 .map(|change| change.block_access_index)
416 .collect::<Vec<_>>(),
417 vec![BlockAccessIndex::new(1), BlockAccessIndex::new(5)]
418 );
419 assert_eq!(
420 account.storage_changes[1]
421 .changes
422 .iter()
423 .map(|change| change.block_access_index)
424 .collect::<Vec<_>>(),
425 vec![BlockAccessIndex::new(2), BlockAccessIndex::new(8)]
426 );
427 assert_eq!(account.storage_reads, vec![U256::from(2), U256::from(4)]);
428 assert_eq!(
429 account
430 .balance_changes
431 .iter()
432 .map(|change| change.block_access_index)
433 .collect::<Vec<_>>(),
434 vec![BlockAccessIndex::new(3), BlockAccessIndex::new(6)]
435 );
436 assert_eq!(
437 account
438 .nonce_changes
439 .iter()
440 .map(|change| change.block_access_index)
441 .collect::<Vec<_>>(),
442 vec![BlockAccessIndex::new(4), BlockAccessIndex::new(7)]
443 );
444 assert_eq!(
445 account.code_changes.iter().map(|change| change.block_access_index).collect::<Vec<_>>(),
446 vec![BlockAccessIndex::new(5), BlockAccessIndex::new(9)]
447 );
448 }
449}
450
451#[cfg(test)]
452mod post_state_tests {
453 use crate::{BlockAccessIndex, StorageChange};
454
455 use super::*;
456
457 #[test]
458 fn storage_post_states_yields_last_change_per_slot() {
459 let account = AccountChanges::new(Address::from([0x11; 20]))
460 .with_storage_change(SlotChanges::new(
461 U256::from(1),
462 vec![
463 StorageChange::new(BlockAccessIndex::new(0), U256::from(0xaa)),
464 StorageChange::new(BlockAccessIndex::new(2), U256::from(0xbb)),
465 ],
466 ))
467 .with_storage_change(SlotChanges::new(
468 U256::from(3),
469 vec![
470 StorageChange::new(BlockAccessIndex::new(1), U256::from(0xcc)),
471 StorageChange::new(BlockAccessIndex::new(3), U256::from(0xdd)),
472 ],
473 ));
474
475 let post_states = account.storage_post_states().collect::<Vec<_>>();
476
477 assert_eq!(
478 post_states,
479 vec![(U256::from(1), U256::from(0xbb)), (U256::from(3), U256::from(0xdd))]
480 );
481 }
482}
483
484#[cfg(all(test, feature = "serde"))]
485mod tests {
486 use crate::{BlockAccessIndex, StorageChange};
487
488 use super::*;
489 use alloy_primitives::Bytes;
490 use serde_json;
491
492 #[test]
493 fn test_account_changes_serde() {
494 let acc = AccountChanges {
495 address: Address::from([0x11; 20]),
496 storage_changes: vec![SlotChanges {
497 slot: U256::from(1),
498 changes: vec![StorageChange {
499 block_access_index: BlockAccessIndex::new(0),
500 new_value: U256::from(100),
501 }],
502 }],
503 storage_reads: vec![U256::from(2)],
504 balance_changes: vec![BalanceChange {
505 block_access_index: BlockAccessIndex::new(1),
506 post_balance: U256::from(1000),
507 }],
508 nonce_changes: vec![NonceChange {
509 block_access_index: BlockAccessIndex::new(2),
510 new_nonce: 42,
511 }],
512 code_changes: vec![CodeChange {
513 block_access_index: BlockAccessIndex::new(3),
514 new_code: Bytes::from(vec![0x60, 0x00]),
515 }],
516 };
517
518 let json = serde_json::to_string(&acc).unwrap();
519 let decoded: AccountChanges = serde_json::from_str(&json).unwrap();
520
521 assert_eq!(acc, decoded);
522 }
523
524 #[test]
525 fn test_vec_account_changes_serde() {
526 let acc1 = AccountChanges::new(Address::from([0x11; 20]))
527 .with_storage_read(U256::from(1))
528 .with_balance_change(BalanceChange {
529 block_access_index: BlockAccessIndex::new(0),
530 post_balance: U256::from(100),
531 });
532
533 let acc2 = AccountChanges::new(Address::from([0x22; 20]))
534 .with_storage_change(SlotChanges {
535 slot: U256::from(2),
536 changes: vec![StorageChange {
537 block_access_index: BlockAccessIndex::new(1),
538 new_value: U256::from(200),
539 }],
540 })
541 .with_nonce_change(NonceChange {
542 block_access_index: BlockAccessIndex::new(2),
543 new_nonce: 42,
544 });
545
546 let acc3 = AccountChanges::new(Address::from([0x33; 20])).with_code_change(CodeChange {
547 block_access_index: BlockAccessIndex::new(3),
548 new_code: Bytes::from(vec![0x60, 0x00]),
549 });
550
551 let vec_acc = vec![acc1, acc2, acc3];
552
553 let json = serde_json::to_string(&vec_acc).unwrap();
554 let decoded: Vec<AccountChanges> = serde_json::from_str(&json).unwrap();
555
556 assert_eq!(vec_acc, decoded);
557 }
558}