1use crate::{
6 BalAccountInfo, BlockAccessIndex, SlotChanges, balance_change::BalanceChange,
7 code_change::CodeChange, nonce_change::NonceChange,
8};
9use alloc::vec::Vec;
10use alloy_primitives::{
11 Address, B256, Bytes, KECCAK256_EMPTY, U256, keccak256,
12 map::{HashMap, HashSet},
13};
14
15#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
17#[cfg_attr(feature = "rlp", derive(alloy_rlp::RlpEncodable, alloy_rlp::RlpDecodable))]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
20#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
21#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
22pub struct AccountChanges {
23 pub address: Address,
25 pub storage_changes: Vec<SlotChanges>,
27 pub storage_reads: Vec<U256>,
29 pub balance_changes: Vec<BalanceChange>,
31 pub nonce_changes: Vec<NonceChange>,
33 pub code_changes: Vec<CodeChange>,
35}
36
37impl AccountChanges {
38 pub const fn new(address: Address) -> Self {
40 Self {
41 address,
42 storage_changes: Vec::new(),
43 storage_reads: Vec::new(),
44 balance_changes: Vec::new(),
45 nonce_changes: Vec::new(),
46 code_changes: Vec::new(),
47 }
48 }
49
50 pub fn with_capacity(address: Address, capacity: usize) -> Self {
52 Self {
53 address,
54 storage_changes: Vec::with_capacity(capacity),
55 storage_reads: Vec::with_capacity(capacity),
56 balance_changes: Vec::with_capacity(capacity),
57 nonce_changes: Vec::with_capacity(capacity),
58 code_changes: Vec::with_capacity(capacity),
59 }
60 }
61
62 #[inline]
64 pub const fn address(&self) -> Address {
65 self.address
66 }
67
68 pub fn is_empty(&self) -> bool {
73 let Self {
74 address: _,
75 storage_changes,
76 storage_reads,
77 balance_changes,
78 nonce_changes,
79 code_changes,
80 } = self;
81 storage_changes.iter().all(SlotChanges::is_empty)
82 && storage_reads.is_empty()
83 && balance_changes.is_empty()
84 && nonce_changes.is_empty()
85 && code_changes.is_empty()
86 }
87
88 #[inline]
90 pub fn storage_changes(&self) -> &[SlotChanges] {
91 &self.storage_changes
92 }
93
94 #[inline]
98 pub fn storage_slots(&self) -> impl Iterator<Item = U256> + '_ {
99 self.storage_changes
100 .iter()
101 .map(|changes| changes.slot)
102 .chain(self.storage_reads.iter().copied())
103 }
104
105 #[inline]
109 pub fn storage_post_states(&self) -> impl Iterator<Item = (U256, U256)> + '_ {
110 self.storage_changes.iter().filter_map(|changes| {
111 changes.changes.last().map(|change| (changes.slot, change.new_value))
112 })
113 }
114
115 #[inline]
117 pub fn balance_post_state(&self) -> Option<U256> {
118 self.balance_changes.last().map(|change| change.post_balance)
119 }
120
121 #[inline]
123 pub fn nonce_post_state(&self) -> Option<u64> {
124 self.nonce_changes.last().map(|change| change.new_nonce)
125 }
126
127 #[inline]
129 pub fn code_post_state(&self) -> Option<&Bytes> {
130 self.code_changes.last().map(|change| &change.new_code)
131 }
132
133 #[inline]
137 pub fn code_hash_post_state(&self) -> Option<B256> {
138 self.code_post_state().map(|code| code_hash(code))
139 }
140
141 #[inline]
147 pub fn code_post_state_with_hash(&self) -> Option<(B256, &Bytes)> {
148 self.code_post_state().map(|code| (code_hash(code), code))
149 }
150
151 #[inline]
153 pub fn account_info(&self) -> BalAccountInfo {
154 BalAccountInfo::from_changes(self)
155 }
156
157 pub fn has_storage_changes(&self) -> bool {
161 self.storage_changes.iter().any(|changes| !changes.is_empty())
162 }
163
164 pub fn has_changes(&self) -> bool {
169 !self.balance_changes.is_empty()
170 || !self.nonce_changes.is_empty()
171 || !self.code_changes.is_empty()
172 || self.has_storage_changes()
173 }
174
175 pub fn merge(&mut self, incoming: Self) {
190 assert_eq!(
191 self.address, incoming.address,
192 "cannot merge account changes for different addresses"
193 );
194
195 merge_slot_changes(&mut self.storage_changes, incoming.storage_changes);
196 self.storage_reads.extend(incoming.storage_reads);
197 self.balance_changes.extend(incoming.balance_changes);
198 self.nonce_changes.extend(incoming.nonce_changes);
199 self.code_changes.extend(incoming.code_changes);
200
201 let written = self
202 .storage_changes
203 .iter()
204 .map(|slot_changes| slot_changes.slot)
205 .collect::<HashSet<_>>();
206 self.storage_reads.retain(|slot| !written.contains(slot));
207
208 let mut seen = HashSet::with_capacity(self.storage_reads.len());
209 self.storage_reads.retain(|slot| seen.insert(*slot));
210 }
211
212 #[inline]
214 pub fn storage_reads(&self) -> &[U256] {
215 &self.storage_reads
216 }
217
218 #[inline]
220 pub fn balance_changes(&self) -> &[BalanceChange] {
221 &self.balance_changes
222 }
223
224 #[inline]
226 pub fn nonce_changes(&self) -> &[NonceChange] {
227 &self.nonce_changes
228 }
229
230 #[inline]
232 pub fn code_changes(&self) -> &[CodeChange] {
233 &self.code_changes
234 }
235
236 pub fn sort(&mut self) {
253 self.storage_changes.sort_unstable_by_key(|changes| changes.slot);
254 for slot_changes in &mut self.storage_changes {
255 slot_changes.sort();
256 }
257
258 self.storage_reads.sort_unstable();
259 self.balance_changes.sort_unstable_by_key(|change| change.block_access_index);
260 self.nonce_changes.sort_unstable_by_key(|change| change.block_access_index);
261 self.code_changes.sort_unstable_by_key(|change| change.block_access_index);
262 }
263
264 pub fn normalize(&mut self) {
271 self.storage_changes.retain(|slot_changes| !slot_changes.is_empty());
272 let incoming = core::mem::replace(self, Self::new(self.address));
273 self.merge(incoming);
274 }
275
276 pub fn collapse_changes_at(&mut self, at: BlockAccessIndex) {
282 let Self {
283 address: _,
284 storage_changes,
285 storage_reads: _,
286 balance_changes,
287 nonce_changes,
288 code_changes,
289 } = self;
290 for slot_changes in storage_changes.iter_mut() {
291 keep_last(&mut slot_changes.changes, |change| change.block_access_index = at);
292 }
293 keep_last(balance_changes, |change| change.block_access_index = at);
294 keep_last(nonce_changes, |change| change.block_access_index = at);
295 keep_last(code_changes, |change| change.block_access_index = at);
296 }
297
298 pub fn shift_indices_from(&mut self, from: BlockAccessIndex) {
302 let Self {
303 address: _,
304 storage_changes,
305 storage_reads: _,
306 balance_changes,
307 nonce_changes,
308 code_changes,
309 } = self;
310 let storage = storage_changes
311 .iter_mut()
312 .flat_map(|slot_changes| slot_changes.changes.iter_mut())
313 .map(|change| &mut change.block_access_index);
314 let balances = balance_changes.iter_mut().map(|change| &mut change.block_access_index);
315 let nonces = nonce_changes.iter_mut().map(|change| &mut change.block_access_index);
316 let codes = code_changes.iter_mut().map(|change| &mut change.block_access_index);
317 for index in storage.chain(balances).chain(nonces).chain(codes) {
318 if *index >= from {
319 index.saturating_increment();
320 }
321 }
322 }
323
324 pub const fn with_address(mut self, address: Address) -> Self {
326 self.address = address;
327 self
328 }
329
330 pub fn with_storage_read(mut self, key: U256) -> Self {
332 self.storage_reads.push(key);
333 self
334 }
335
336 pub fn with_storage_change(mut self, change: SlotChanges) -> Self {
338 self.storage_changes.push(change);
339 self
340 }
341
342 pub fn with_balance_change(mut self, change: BalanceChange) -> Self {
344 self.balance_changes.push(change);
345 self
346 }
347
348 pub fn with_nonce_change(mut self, change: NonceChange) -> Self {
350 self.nonce_changes.push(change);
351 self
352 }
353
354 pub fn with_code_change(mut self, change: CodeChange) -> Self {
356 self.code_changes.push(change);
357 self
358 }
359
360 pub fn extend_storage_reads<I>(mut self, iter: I) -> Self
362 where
363 I: IntoIterator<Item = U256>,
364 {
365 self.storage_reads.extend(iter);
366 self
367 }
368
369 pub fn extend_storage_changes<I>(mut self, iter: I) -> Self
371 where
372 I: IntoIterator<Item = SlotChanges>,
373 {
374 self.storage_changes.extend(iter);
375 self
376 }
377}
378
379fn code_hash(code: &[u8]) -> B256 {
381 if code.is_empty() { KECCAK256_EMPTY } else { keccak256(code) }
382}
383
384fn keep_last<T>(changes: &mut Vec<T>, stamp: impl FnOnce(&mut T)) {
386 if let Some(mut change) = changes.pop() {
387 stamp(&mut change);
388 changes.clear();
389 changes.push(change);
390 }
391}
392
393fn merge_slot_changes(existing: &mut Vec<SlotChanges>, incoming: Vec<SlotChanges>) {
394 let mut slot_positions = existing
395 .iter()
396 .enumerate()
397 .map(|(idx, slot_changes)| (slot_changes.slot, idx))
398 .collect::<HashMap<_, _>>();
399
400 for slot_changes in incoming {
401 if let Some(&idx) = slot_positions.get(&slot_changes.slot) {
402 existing[idx].changes.extend(slot_changes.changes);
403 } else {
404 slot_positions.insert(slot_changes.slot, existing.len());
405 existing.push(slot_changes);
406 }
407 }
408}
409
410#[cfg(test)]
411mod merge_tests {
412 use crate::{BlockAccessIndex, StorageChange};
413
414 use super::*;
415 use alloy_primitives::Bytes;
416
417 #[test]
418 fn merge_groups_slot_changes_and_appends_account_changes() {
419 let address = Address::from([0x11; 20]);
420 let mut existing = AccountChanges {
421 address,
422 storage_changes: vec![SlotChanges::new(
423 U256::from(1),
424 vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(10))],
425 )],
426 storage_reads: vec![U256::from(3)],
427 balance_changes: vec![BalanceChange::new(BlockAccessIndex::new(1), U256::from(100))],
428 nonce_changes: vec![NonceChange::new(BlockAccessIndex::new(2), 7)],
429 code_changes: vec![],
430 };
431 let incoming = AccountChanges {
432 address,
433 storage_changes: vec![
434 SlotChanges::new(
435 U256::from(1),
436 vec![StorageChange::new(BlockAccessIndex::new(3), U256::from(20))],
437 ),
438 SlotChanges::new(
439 U256::from(2),
440 vec![StorageChange::new(BlockAccessIndex::new(4), U256::from(30))],
441 ),
442 ],
443 storage_reads: vec![U256::from(4)],
444 balance_changes: vec![BalanceChange::new(BlockAccessIndex::new(5), U256::from(150))],
445 nonce_changes: vec![NonceChange::new(BlockAccessIndex::new(6), 8)],
446 code_changes: vec![CodeChange::new(
447 BlockAccessIndex::new(7),
448 Bytes::from_static(&[0xaa]),
449 )],
450 };
451
452 existing.merge(incoming);
453
454 assert_eq!(existing.storage_reads, vec![U256::from(3), U256::from(4)]);
455 assert_eq!(
456 existing.storage_changes.iter().map(|changes| changes.slot).collect::<Vec<_>>(),
457 vec![U256::from(1), U256::from(2)]
458 );
459 assert_eq!(
460 existing.storage_changes[0]
461 .changes
462 .iter()
463 .map(|change| change.new_value)
464 .collect::<Vec<_>>(),
465 vec![U256::from(10), U256::from(20)]
466 );
467 assert_eq!(existing.balance_changes.len(), 2);
468 assert_eq!(existing.nonce_changes.len(), 2);
469 assert_eq!(existing.code_changes.len(), 1);
470 }
471
472 #[test]
473 fn merge_normalizes_storage_reads_after_cross_block_merge() {
474 let address = Address::from([0x33; 20]);
475 const A: U256 = U256::from_limbs([1, 0, 0, 0]);
476 const B: U256 = U256::from_limbs([2, 0, 0, 0]);
477 const C: U256 = U256::from_limbs([3, 0, 0, 0]);
478 const D: U256 = U256::from_limbs([4, 0, 0, 0]);
479
480 let mut existing = AccountChanges {
481 address,
482 storage_changes: vec![SlotChanges::new(
483 A,
484 vec![StorageChange::new(BlockAccessIndex::new(0), U256::from(10))],
485 )],
486 storage_reads: vec![B, C],
487 balance_changes: vec![],
488 nonce_changes: vec![],
489 code_changes: vec![],
490 };
491 let incoming = AccountChanges {
492 address,
493 storage_changes: vec![SlotChanges::new(
494 B,
495 vec![StorageChange::new(BlockAccessIndex::new(1), U256::from(20))],
496 )],
497 storage_reads: vec![A, C, D],
498 balance_changes: vec![],
499 nonce_changes: vec![],
500 code_changes: vec![],
501 };
502
503 existing.merge(incoming);
504
505 assert_eq!(
506 existing
507 .storage_changes
508 .iter()
509 .map(|slot_changes| slot_changes.slot)
510 .collect::<Vec<_>>(),
511 vec![A, B]
512 );
513 assert_eq!(existing.storage_reads, vec![C, D]);
514 assert!(existing.storage_reads.iter().all(|read_slot| {
515 !existing.storage_changes.iter().any(|slot_changes| slot_changes.slot == *read_slot)
516 }));
517 }
518
519 #[test]
520 #[should_panic(expected = "cannot merge account changes for different addresses")]
521 fn merge_rejects_different_addresses() {
522 let mut existing = AccountChanges::new(Address::from([0x11; 20]));
523 let incoming = AccountChanges::new(Address::from([0x22; 20]));
524
525 existing.merge(incoming);
526 }
527}
528
529#[cfg(test)]
530mod sort_tests {
531 use crate::{BlockAccessIndex, StorageChange};
532
533 use super::*;
534 use alloy_primitives::Bytes;
535
536 #[test]
537 fn sort_orders_account_local_eip7928_lists() {
538 let mut account = AccountChanges {
539 address: Address::from([0x11; 20]),
540 storage_changes: vec![
541 SlotChanges::new(
542 U256::from(3),
543 vec![
544 StorageChange::new(BlockAccessIndex::new(8), U256::from(0x80)),
545 StorageChange::new(BlockAccessIndex::new(2), U256::from(0x20)),
546 ],
547 ),
548 SlotChanges::new(
549 U256::from(1),
550 vec![
551 StorageChange::new(BlockAccessIndex::new(5), U256::from(0x50)),
552 StorageChange::new(BlockAccessIndex::new(1), U256::from(0x10)),
553 ],
554 ),
555 ],
556 storage_reads: vec![U256::from(4), U256::from(2)],
557 balance_changes: vec![
558 BalanceChange::new(BlockAccessIndex::new(6), U256::from(600)),
559 BalanceChange::new(BlockAccessIndex::new(3), U256::from(300)),
560 ],
561 nonce_changes: vec![
562 NonceChange::new(BlockAccessIndex::new(7), 70),
563 NonceChange::new(BlockAccessIndex::new(4), 40),
564 ],
565 code_changes: vec![
566 CodeChange::new(BlockAccessIndex::new(9), Bytes::from_static(&[0x60, 0x09])),
567 CodeChange::new(BlockAccessIndex::new(5), Bytes::from_static(&[0x60, 0x05])),
568 ],
569 };
570
571 account.sort();
572
573 assert_eq!(
574 account.storage_changes.iter().map(|changes| changes.slot).collect::<Vec<_>>(),
575 vec![U256::from(1), U256::from(3)]
576 );
577 assert_eq!(
578 account.storage_changes[0]
579 .changes
580 .iter()
581 .map(|change| change.block_access_index)
582 .collect::<Vec<_>>(),
583 vec![BlockAccessIndex::new(1), BlockAccessIndex::new(5)]
584 );
585 assert_eq!(
586 account.storage_changes[1]
587 .changes
588 .iter()
589 .map(|change| change.block_access_index)
590 .collect::<Vec<_>>(),
591 vec![BlockAccessIndex::new(2), BlockAccessIndex::new(8)]
592 );
593 assert_eq!(account.storage_reads, vec![U256::from(2), U256::from(4)]);
594 assert_eq!(
595 account
596 .balance_changes
597 .iter()
598 .map(|change| change.block_access_index)
599 .collect::<Vec<_>>(),
600 vec![BlockAccessIndex::new(3), BlockAccessIndex::new(6)]
601 );
602 assert_eq!(
603 account
604 .nonce_changes
605 .iter()
606 .map(|change| change.block_access_index)
607 .collect::<Vec<_>>(),
608 vec![BlockAccessIndex::new(4), BlockAccessIndex::new(7)]
609 );
610 assert_eq!(
611 account.code_changes.iter().map(|change| change.block_access_index).collect::<Vec<_>>(),
612 vec![BlockAccessIndex::new(5), BlockAccessIndex::new(9)]
613 );
614 }
615}
616
617#[cfg(test)]
618mod post_state_tests {
619 use crate::{BlockAccessIndex, StorageChange};
620
621 use super::*;
622
623 #[test]
624 fn account_post_states_are_absent_for_unchanged_fields() {
625 let account = AccountChanges::new(Address::ZERO)
626 .with_storage_read(U256::from(1))
627 .with_storage_change(SlotChanges::new(
628 U256::from(2),
629 vec![StorageChange::new(BlockAccessIndex::new(1), U256::from(3))],
630 ));
631
632 assert_eq!(account.balance_post_state(), None);
633 assert_eq!(account.nonce_post_state(), None);
634 assert_eq!(account.code_post_state(), None);
635 }
636
637 #[test]
638 fn account_post_states_use_last_recorded_change() {
639 for indices in [&[0][..], &[0, 1, 2][..], &[2, 1, 0][..]] {
640 let mut account = AccountChanges::new(Address::ZERO);
641 for (position, &index) in indices.iter().enumerate() {
642 let index = BlockAccessIndex::new(index);
643 let value = (position + 1) as u64;
644 account.balance_changes.push(BalanceChange::new(index, U256::from(value)));
645 account.nonce_changes.push(NonceChange::new(index, value));
646 account.code_changes.push(CodeChange::new(index, Bytes::from(vec![value as u8])));
647 }
648
649 let expected = indices.len() as u64;
650 assert_eq!(account.balance_post_state(), Some(U256::from(expected)));
651 assert_eq!(account.nonce_post_state(), Some(expected));
652 assert_eq!(account.code_post_state(), Some(&Bytes::from(vec![expected as u8])));
653 }
654 }
655
656 #[test]
657 fn account_post_states_preserve_zero_values_and_cleared_code() {
658 let mut account = AccountChanges::new(Address::ZERO)
659 .with_balance_change(BalanceChange::new(BlockAccessIndex::new(0), U256::from(10)))
660 .with_nonce_change(NonceChange::new(BlockAccessIndex::new(0), 1))
661 .with_code_change(CodeChange::new(
662 BlockAccessIndex::new(0),
663 Bytes::from_static(&[0x60]),
664 ));
665 let index = BlockAccessIndex::new(1);
666 account.balance_changes.push(BalanceChange::new(index, U256::ZERO));
667 account.nonce_changes.push(NonceChange::new(index, 0));
668 account.code_changes.push(CodeChange::new(index, Bytes::new()));
669
670 assert_eq!(account.balance_post_state(), Some(U256::ZERO));
671 assert_eq!(account.nonce_post_state(), Some(0));
672 assert_eq!(account.code_post_state(), Some(&Bytes::new()));
673 }
674
675 #[test]
676 fn storage_post_states_yields_last_change_per_slot() {
677 let account = AccountChanges::new(Address::from([0x11; 20]))
678 .with_storage_change(SlotChanges::new(
679 U256::from(1),
680 vec![
681 StorageChange::new(BlockAccessIndex::new(0), U256::from(0xaa)),
682 StorageChange::new(BlockAccessIndex::new(2), U256::from(0xbb)),
683 ],
684 ))
685 .with_storage_change(SlotChanges::new(
686 U256::from(3),
687 vec![
688 StorageChange::new(BlockAccessIndex::new(1), U256::from(0xcc)),
689 StorageChange::new(BlockAccessIndex::new(3), U256::from(0xdd)),
690 ],
691 ));
692
693 let post_states = account.storage_post_states().collect::<Vec<_>>();
694
695 assert_eq!(
696 post_states,
697 vec![(U256::from(1), U256::from(0xbb)), (U256::from(3), U256::from(0xdd))]
698 );
699 }
700
701 #[test]
702 fn code_post_state_hash_matches_the_last_recorded_code() {
703 let code = Bytes::from_static(&[0x60, 0x00, 0x56]);
704 let account = AccountChanges::new(Address::ZERO)
705 .with_code_change(CodeChange::new(
706 BlockAccessIndex::new(0),
707 Bytes::from_static(&[0x00]),
708 ))
709 .with_code_change(CodeChange::new(BlockAccessIndex::new(1), code.clone()));
710
711 assert_eq!(account.code_hash_post_state(), Some(keccak256(&code)));
712 assert_eq!(account.code_post_state_with_hash(), Some((keccak256(&code), &code)));
713 }
714
715 #[test]
716 fn cleared_code_post_state_hashes_to_the_empty_code_hash() {
717 let account = AccountChanges::new(Address::ZERO)
718 .with_code_change(CodeChange::new(BlockAccessIndex::new(0), Bytes::new()));
719
720 assert_eq!(account.code_hash_post_state(), Some(KECCAK256_EMPTY));
721 assert_eq!(account.code_post_state_with_hash(), Some((KECCAK256_EMPTY, &Bytes::new())));
722 }
723
724 #[test]
725 fn unchanged_code_has_no_post_state_hash() {
726 let account = AccountChanges::new(Address::ZERO).with_storage_read(U256::from(1));
727
728 assert_eq!(account.code_hash_post_state(), None);
729 assert_eq!(account.code_post_state_with_hash(), None);
730 }
731}
732
733#[cfg(test)]
734mod has_changes_tests {
735 use super::*;
736 use crate::{BlockAccessIndex, StorageChange};
737
738 #[test]
739 fn read_only_entries_have_no_changes() {
740 let account = AccountChanges::new(Address::ZERO).with_storage_read(U256::from(1));
741
742 assert!(!account.has_changes());
743 assert!(!account.is_empty());
744 assert!(account.account_info().is_empty());
745 }
746
747 #[test]
748 fn empty_slot_entries_have_no_changes() {
749 let account = AccountChanges::new(Address::ZERO)
750 .with_storage_change(SlotChanges::new(U256::from(1), Vec::new()));
751
752 assert!(!account.has_changes());
753 }
754
755 #[test]
756 fn every_change_kind_counts_as_a_change() {
757 let index = BlockAccessIndex::new(0);
758 let entries = [
759 AccountChanges::new(Address::ZERO).with_storage_change(SlotChanges::new(
760 U256::from(1),
761 vec![StorageChange::new(index, U256::from(2))],
762 )),
763 AccountChanges::new(Address::ZERO)
764 .with_balance_change(BalanceChange::new(index, U256::from(1))),
765 AccountChanges::new(Address::ZERO).with_nonce_change(NonceChange::new(index, 1)),
766 AccountChanges::new(Address::ZERO)
767 .with_code_change(CodeChange::new(index, Bytes::new())),
768 ];
769
770 for account in entries {
771 assert!(account.has_changes());
772 }
773 }
774
775 #[test]
776 fn account_info_matches_the_post_state_accessors() {
777 let account = AccountChanges::new(Address::ZERO)
778 .with_balance_change(BalanceChange::new(BlockAccessIndex::new(0), U256::from(7)));
779
780 assert_eq!(account.account_info(), BalAccountInfo::from_changes(&account));
781 assert_eq!(account.account_info().balance, account.balance_post_state());
782 }
783}
784
785#[cfg(test)]
786mod storage_slots_tests {
787 use crate::{BlockAccessIndex, StorageChange};
788
789 use super::*;
790
791 #[test]
792 fn storage_slots_yields_changed_then_read_slots() {
793 let account = AccountChanges::new(Address::ZERO)
794 .with_storage_change(SlotChanges::new(
795 U256::from(1),
796 vec![StorageChange::new(BlockAccessIndex::new(0), U256::ZERO)],
797 ))
798 .with_storage_change(SlotChanges::new(
799 U256::from(2),
800 vec![StorageChange::new(BlockAccessIndex::new(1), U256::ZERO)],
801 ))
802 .extend_storage_reads([U256::from(3), U256::from(4)]);
803
804 assert_eq!(
805 account.storage_slots().collect::<Vec<_>>(),
806 vec![U256::from(1), U256::from(2), U256::from(3), U256::from(4)]
807 );
808 }
809}
810
811#[cfg(all(test, feature = "serde"))]
812mod tests {
813 use crate::{BlockAccessIndex, BlockAccessList, StorageChange};
814
815 use super::*;
816 use alloy_primitives::Bytes;
817 use serde_json;
818
819 #[test]
820 fn test_account_changes_serde() {
821 let acc = AccountChanges {
822 address: Address::from([0x11; 20]),
823 storage_changes: vec![SlotChanges {
824 slot: U256::from(1),
825 changes: vec![StorageChange {
826 block_access_index: BlockAccessIndex::new(0),
827 new_value: U256::from(100),
828 }],
829 }],
830 storage_reads: vec![U256::from(2)],
831 balance_changes: vec![BalanceChange {
832 block_access_index: BlockAccessIndex::new(1),
833 post_balance: U256::from(1000),
834 }],
835 nonce_changes: vec![NonceChange {
836 block_access_index: BlockAccessIndex::new(2),
837 new_nonce: 42,
838 }],
839 code_changes: vec![CodeChange {
840 block_access_index: BlockAccessIndex::new(3),
841 new_code: Bytes::from(vec![0x60, 0x00]),
842 }],
843 };
844
845 let json = serde_json::to_string(&acc).unwrap();
846 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
847 assert_eq!(value["storageChanges"][0]["key"], "0x1");
848 assert_eq!(value["storageChanges"][0]["changes"][0]["value"], "0x64");
849 assert_eq!(value["storageReads"][0], "0x2");
850 assert_eq!(value["balanceChanges"][0]["value"], "0x3e8");
851 assert_eq!(value["nonceChanges"][0]["value"], "0x2a");
852 assert_eq!(value["codeChanges"][0]["code"], "0x6000");
853 let decoded: AccountChanges = serde_json::from_str(&json).unwrap();
854
855 assert_eq!(acc, decoded);
856 }
857
858 #[test]
859 fn test_storage_reads_deserialize_compact_quantities() {
860 let fixture = r#"
861 {
862 "address": "0x1111111111111111111111111111111111111111",
863 "storageChanges": [],
864 "storageReads": [
865 "0x00",
866 "0x01",
867 "0x02"
868 ],
869 "balanceChanges": [],
870 "nonceChanges": [],
871 "codeChanges": []
872 }
873 "#;
874
875 let decoded: AccountChanges = serde_json::from_str(fixture).unwrap();
876 assert_eq!(decoded.storage_reads, vec![U256::ZERO, U256::from(1), U256::from(2)]);
877
878 assert_eq!(
879 serde_json::to_value(decoded).unwrap()["storageReads"],
880 serde_json::json!(["0x0", "0x1", "0x2"])
881 );
882 }
883
884 #[test]
885 fn test_eest_storage_fields_deserialize_compact_quantities() {
886 let fixture = r#"
888 [
889 {
890 "address": "0x0000f90827f1c53a10cb7a02335b175320002935",
891 "storageChanges": [
892 {
893 "slot": "0x01",
894 "slotChanges": [
895 {
896 "blockAccessIndex": "0x00",
897 "postValue": "0x27330b1c525088b9b5ed2ced86b42d53378c8f6b384e8c3897493e851bc026df"
898 }
899 ]
900 }
901 ],
902 "storageReads": [],
903 "balanceChanges": [],
904 "nonceChanges": [],
905 "codeChanges": []
906 },
907 {
908 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
909 "storageChanges": [
910 {
911 "slot": "0x0e22",
912 "slotChanges": [
913 {
914 "blockAccessIndex": "0x00",
915 "postValue": "0x4e20"
916 }
917 ]
918 }
919 ],
920 "storageReads": ["0x2e21"],
921 "balanceChanges": [],
922 "nonceChanges": [],
923 "codeChanges": []
924 }
925 ]
926 "#;
927
928 let decoded: BlockAccessList = serde_json::from_str(fixture).unwrap();
929 assert_eq!(decoded[0].storage_changes[0].slot, U256::from(1));
930 assert_eq!(decoded[1].storage_changes[0].slot, U256::from(0x0e22));
931 assert_eq!(decoded[1].storage_changes[0].changes[0].new_value, U256::from(0x4e20));
932 assert_eq!(decoded[1].storage_reads, vec![U256::from(0x2e21)]);
933 }
934
935 #[test]
936 fn test_vec_account_changes_serde() {
937 let acc1 = AccountChanges::new(Address::from([0x11; 20]))
938 .with_storage_read(U256::from(1))
939 .with_balance_change(BalanceChange {
940 block_access_index: BlockAccessIndex::new(0),
941 post_balance: U256::from(100),
942 });
943
944 let acc2 = AccountChanges::new(Address::from([0x22; 20]))
945 .with_storage_change(SlotChanges {
946 slot: U256::from(2),
947 changes: vec![StorageChange {
948 block_access_index: BlockAccessIndex::new(1),
949 new_value: U256::from(200),
950 }],
951 })
952 .with_nonce_change(NonceChange {
953 block_access_index: BlockAccessIndex::new(2),
954 new_nonce: 42,
955 });
956
957 let acc3 = AccountChanges::new(Address::from([0x33; 20])).with_code_change(CodeChange {
958 block_access_index: BlockAccessIndex::new(3),
959 new_code: Bytes::from(vec![0x60, 0x00]),
960 });
961
962 let vec_acc = vec![acc1, acc2, acc3];
963
964 let json = serde_json::to_string(&vec_acc).unwrap();
965 let decoded: Vec<AccountChanges> = serde_json::from_str(&json).unwrap();
966
967 assert_eq!(vec_acc, decoded);
968 }
969
970 #[test]
971 fn test_block_access_list_serde_roundtrip_from_populated_fixture() {
972 let fixture = r#"
973[
974 {
975 "address": "0x1111111111111111111111111111111111111111",
976 "storageChanges": [
977 {
978 "key": "0x1",
979 "changes": [
980 {
981 "index": "0x1",
982 "value": "0x10"
983 },
984 {
985 "index": "0x2",
986 "value": "0x20"
987 }
988 ]
989 }
990 ],
991 "storageReads": [
992 "0x2"
993 ],
994 "balanceChanges": [
995 {
996 "index": "0x3",
997 "value": "0x3e8"
998 }
999 ],
1000 "nonceChanges": [
1001 {
1002 "index": "0x4",
1003 "value": "0x2a"
1004 }
1005 ],
1006 "codeChanges": [
1007 {
1008 "index": "0x5",
1009 "code": "0x6000"
1010 }
1011 ]
1012 }
1013]
1014"#;
1015
1016 let decoded: BlockAccessList = serde_json::from_str(fixture).unwrap();
1017 let serialized = serde_json::to_string(&decoded).unwrap();
1018 let fixture_value: serde_json::Value = serde_json::from_str(fixture).unwrap();
1019 let serialized_value: serde_json::Value = serde_json::from_str(&serialized).unwrap();
1020
1021 assert!(fixture_value.is_array());
1022 assert_eq!(fixture_value, serialized_value);
1023 }
1024
1025 #[test]
1026 fn test_block_access_list_serde_roundtrip_from_empty_fixture() {
1027 let fixture = r#"
1028[
1029 {
1030 "address": "0x2222222222222222222222222222222222222222",
1031 "storageChanges": [],
1032 "storageReads": [],
1033 "balanceChanges": [],
1034 "nonceChanges": [],
1035 "codeChanges": []
1036 }
1037]
1038"#;
1039
1040 let decoded: BlockAccessList = serde_json::from_str(fixture).unwrap();
1041 let serialized = serde_json::to_string(&decoded).unwrap();
1042 let fixture_value: serde_json::Value = serde_json::from_str(fixture).unwrap();
1043 let serialized_value: serde_json::Value = serde_json::from_str(&serialized).unwrap();
1044
1045 assert!(fixture_value.is_array());
1046 assert_eq!(fixture_value, serialized_value);
1047 assert_eq!(serialized_value[0]["storageChanges"], serde_json::json!([]));
1048 assert_eq!(serialized_value[0]["storageReads"], serde_json::json!([]));
1049 assert_eq!(serialized_value[0]["balanceChanges"], serde_json::json!([]));
1050 assert_eq!(serialized_value[0]["nonceChanges"], serde_json::json!([]));
1051 assert_eq!(serialized_value[0]["codeChanges"], serde_json::json!([]));
1052 }
1053}