1use core::ops::Not;
2
3use crate::{
4 prefix_set::{PrefixSetMut, TriePrefixSetsMut},
5 KeyHasher, MultiProofTargets, Nibbles,
6};
7use alloc::{borrow::Cow, vec::Vec};
8use alloy_primitives::{
9 keccak256,
10 map::{hash_map, B256Map, B256Set, HashMap, HashSet},
11 Address, B256, U256,
12};
13use itertools::Itertools;
14#[cfg(feature = "rayon")]
15pub use rayon::*;
16use reth_primitives_traits::Account;
17
18#[cfg(feature = "rayon")]
19use rayon::prelude::{IntoParallelIterator, ParallelIterator};
20
21use revm_database::{AccountStatus, BundleAccount};
22use revm_state::FlaggedStorage;
23
24#[derive(PartialEq, Eq, Clone, Default, Debug)]
26pub struct HashedPostState {
27 pub accounts: B256Map<Option<Account>>,
29 pub storages: B256Map<HashedStorage>,
31}
32
33impl HashedPostState {
34 pub fn with_capacity(capacity: usize) -> Self {
36 Self {
37 accounts: B256Map::with_capacity_and_hasher(capacity, Default::default()),
38 storages: B256Map::with_capacity_and_hasher(capacity, Default::default()),
39 }
40 }
41
42 #[inline]
46 #[cfg(feature = "rayon")]
47 pub fn from_bundle_state<'a, KH: KeyHasher>(
48 state: impl IntoParallelIterator<Item = (&'a Address, &'a BundleAccount)>,
49 ) -> Self {
50 let hashed = state
51 .into_par_iter()
52 .map(|(address, account)| {
53 let hashed_address = KH::hash_key(address);
54 let hashed_account = account.info.as_ref().map(Into::into);
55 let hashed_storage = HashedStorage::from_plain_storage(
56 account.status,
57 account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
58 );
59 (hashed_address, (hashed_account, hashed_storage))
60 })
61 .collect::<Vec<(B256, (Option<Account>, HashedStorage))>>();
62
63 let mut accounts = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
64 let mut storages = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
65 for (address, (account, storage)) in hashed {
66 accounts.insert(address, account);
67 if !storage.is_empty() {
68 storages.insert(address, storage);
69 }
70 }
71 Self { accounts, storages }
72 }
73
74 #[cfg(not(feature = "rayon"))]
78 pub fn from_bundle_state<'a, KH: KeyHasher>(
79 state: impl IntoIterator<Item = (&'a Address, &'a BundleAccount)>,
80 ) -> Self {
81 let hashed = state
82 .into_iter()
83 .map(|(address, account)| {
84 let hashed_address = KH::hash_key(address);
85 let hashed_account = account.info.as_ref().map(Into::into);
86 let hashed_storage = HashedStorage::from_plain_storage(
87 account.status,
88 account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
89 );
90 (hashed_address, (hashed_account, hashed_storage))
91 })
92 .collect::<Vec<(B256, (Option<Account>, HashedStorage))>>();
93
94 let mut accounts = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
95 let mut storages = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
96 for (address, (account, storage)) in hashed {
97 accounts.insert(address, account);
98 if !storage.is_empty() {
99 storages.insert(address, storage);
100 }
101 }
102 Self { accounts, storages }
103 }
104
105 pub fn from_hashed_storage(hashed_address: B256, storage: HashedStorage) -> Self {
107 Self {
108 accounts: HashMap::default(),
109 storages: HashMap::from_iter([(hashed_address, storage)]),
110 }
111 }
112
113 pub fn with_accounts(
115 mut self,
116 accounts: impl IntoIterator<Item = (B256, Option<Account>)>,
117 ) -> Self {
118 self.accounts = HashMap::from_iter(accounts);
119 self
120 }
121
122 pub fn with_storages(
124 mut self,
125 storages: impl IntoIterator<Item = (B256, HashedStorage)>,
126 ) -> Self {
127 self.storages = HashMap::from_iter(storages);
128 self
129 }
130
131 pub fn is_empty(&self) -> bool {
133 self.accounts.is_empty() && self.storages.is_empty()
134 }
135
136 pub fn construct_prefix_sets(&self) -> TriePrefixSetsMut {
140 let mut account_prefix_set = PrefixSetMut::with_capacity(self.accounts.len());
142 let mut destroyed_accounts = HashSet::default();
143 for (hashed_address, account) in &self.accounts {
144 account_prefix_set.insert(Nibbles::unpack(hashed_address));
145
146 if account.is_none() {
147 destroyed_accounts.insert(*hashed_address);
148 }
149 }
150
151 let mut storage_prefix_sets =
153 HashMap::with_capacity_and_hasher(self.storages.len(), Default::default());
154 for (hashed_address, hashed_storage) in &self.storages {
155 account_prefix_set.insert(Nibbles::unpack(hashed_address));
156 storage_prefix_sets.insert(*hashed_address, hashed_storage.construct_prefix_set());
157 }
158
159 TriePrefixSetsMut { account_prefix_set, storage_prefix_sets, destroyed_accounts }
160 }
161
162 pub fn multi_proof_targets(&self) -> MultiProofTargets {
164 let mut targets = MultiProofTargets::with_capacity(self.accounts.len());
166 for hashed_address in self.accounts.keys() {
167 targets.insert(*hashed_address, Default::default());
168 }
169 for (hashed_address, storage) in &self.storages {
170 targets.entry(*hashed_address).or_default().extend(storage.storage.keys().copied());
171 }
172 targets
173 }
174
175 pub fn multi_proof_targets_difference(
181 &self,
182 excluded: &MultiProofTargets,
183 ) -> MultiProofTargets {
184 let mut targets = MultiProofTargets::default();
185 for hashed_address in self.accounts.keys() {
186 if !excluded.contains_key(hashed_address) {
187 targets.insert(*hashed_address, Default::default());
188 }
189 }
190 for (hashed_address, storage) in &self.storages {
191 let maybe_excluded_storage = excluded.get(hashed_address);
192 let mut hashed_slots_targets = storage
193 .storage
194 .keys()
195 .filter(|slot| !maybe_excluded_storage.is_some_and(|f| f.contains(*slot)))
196 .peekable();
197 if hashed_slots_targets.peek().is_some() {
198 targets.entry(*hashed_address).or_default().extend(hashed_slots_targets);
199 }
200 }
201 targets
202 }
203
204 pub fn partition_by_targets(mut self, targets: &MultiProofTargets) -> (Self, Self) {
211 let mut state_updates_not_in_targets = Self::default();
212
213 self.storages.retain(|&address, storage| {
214 let (retain, storage_not_in_targets) = match targets.get(&address) {
215 Some(storage_in_targets) => {
216 let mut storage_not_in_targets = HashedStorage::default();
217 storage.storage.retain(|&slot, value| {
218 if storage_in_targets.contains(&slot) {
219 return true
220 }
221
222 storage_not_in_targets.storage.insert(slot, *value);
223 false
224 });
225
226 let retain = !storage.storage.is_empty();
230
231 if !retain {
235 storage_not_in_targets.wiped = storage.wiped;
236 }
237
238 (
239 retain,
240 storage_not_in_targets.is_empty().not().then_some(storage_not_in_targets),
241 )
242 }
243 None => (false, Some(core::mem::take(storage))),
244 };
245
246 if let Some(storage_not_in_targets) = storage_not_in_targets {
247 state_updates_not_in_targets.storages.insert(address, storage_not_in_targets);
248 }
249
250 retain
251 });
252 self.accounts.retain(|&address, account| {
253 if targets.contains_key(&address) {
254 return true
255 }
256
257 state_updates_not_in_targets.accounts.insert(address, *account);
258 false
259 });
260
261 (self, state_updates_not_in_targets)
262 }
263
264 pub fn chunks(self, size: usize) -> ChunkedHashedPostState {
268 ChunkedHashedPostState::new(self, size)
269 }
270
271 pub fn extend(&mut self, other: Self) {
274 self.extend_inner(Cow::Owned(other));
275 }
276
277 pub fn extend_ref(&mut self, other: &Self) {
282 self.extend_inner(Cow::Borrowed(other));
283 }
284
285 fn extend_inner(&mut self, other: Cow<'_, Self>) {
286 self.accounts.extend(other.accounts.iter().map(|(&k, &v)| (k, v)));
287
288 self.storages.reserve(other.storages.len());
289 match other {
290 Cow::Borrowed(other) => {
291 self.extend_storages(other.storages.iter().map(|(k, v)| (*k, Cow::Borrowed(v))))
292 }
293 Cow::Owned(other) => {
294 self.extend_storages(other.storages.into_iter().map(|(k, v)| (k, Cow::Owned(v))))
295 }
296 }
297 }
298
299 fn extend_storages<'a>(
300 &mut self,
301 storages: impl IntoIterator<Item = (B256, Cow<'a, HashedStorage>)>,
302 ) {
303 for (hashed_address, storage) in storages {
304 match self.storages.entry(hashed_address) {
305 hash_map::Entry::Vacant(entry) => {
306 entry.insert(storage.into_owned());
307 }
308 hash_map::Entry::Occupied(mut entry) => {
309 entry.get_mut().extend(&storage);
310 }
311 }
312 }
313 }
314
315 pub fn into_sorted(self) -> HashedPostStateSorted {
317 let mut updated_accounts = Vec::new();
318 let mut destroyed_accounts = HashSet::default();
319 for (hashed_address, info) in self.accounts {
320 if let Some(info) = info {
321 updated_accounts.push((hashed_address, info));
322 } else {
323 destroyed_accounts.insert(hashed_address);
324 }
325 }
326 updated_accounts.sort_unstable_by_key(|(address, _)| *address);
327 let accounts = HashedAccountsSorted { accounts: updated_accounts, destroyed_accounts };
328
329 let storages = self
330 .storages
331 .into_iter()
332 .map(|(hashed_address, storage)| (hashed_address, storage.into_sorted()))
333 .collect();
334
335 HashedPostStateSorted { accounts, storages }
336 }
337}
338
339#[derive(PartialEq, Eq, Clone, Debug, Default)]
341pub struct HashedStorage {
342 pub wiped: bool,
344 pub storage: B256Map<FlaggedStorage>,
346}
347
348impl HashedStorage {
349 pub fn new(wiped: bool) -> Self {
351 Self { wiped, storage: HashMap::default() }
352 }
353
354 pub fn is_empty(&self) -> bool {
356 !self.wiped && self.storage.is_empty()
357 }
358
359 pub fn from_iter(wiped: bool, iter: impl IntoIterator<Item = (B256, FlaggedStorage)>) -> Self {
361 Self { wiped, storage: HashMap::from_iter(iter) }
362 }
363
364 pub fn from_plain_storage<'a>(
366 status: AccountStatus,
367 storage: impl IntoIterator<Item = (&'a U256, &'a FlaggedStorage)>,
368 ) -> Self {
369 Self::from_iter(
370 status.was_destroyed(),
371 storage.into_iter().map(|(key, value)| (keccak256(B256::from(*key)), *value)),
372 )
373 }
374
375 pub fn construct_prefix_set(&self) -> PrefixSetMut {
377 if self.wiped {
378 PrefixSetMut::all()
379 } else {
380 let mut prefix_set = PrefixSetMut::with_capacity(self.storage.len());
381 for hashed_slot in self.storage.keys() {
382 prefix_set.insert(Nibbles::unpack(hashed_slot));
383 }
384 prefix_set
385 }
386 }
387
388 pub fn extend(&mut self, other: &Self) {
391 if other.wiped {
392 self.wiped = true;
393 self.storage.clear();
394 }
395 self.storage.extend(other.storage.iter().map(|(&k, &v)| (k, v)));
396 }
397
398 pub fn into_sorted(self) -> HashedStorageSorted {
400 let mut non_zero_valued_slots = Vec::new();
401 let mut zero_valued_slots = HashSet::default();
402 for (hashed_slot, value) in self.storage {
403 if value.is_zero() {
404 zero_valued_slots.insert(hashed_slot);
405 } else {
406 non_zero_valued_slots.push((hashed_slot, value));
407 }
408 }
409 non_zero_valued_slots.sort_unstable_by_key(|(key, _)| *key);
410
411 HashedStorageSorted { non_zero_valued_slots, zero_valued_slots, wiped: self.wiped }
412 }
413}
414
415#[derive(PartialEq, Eq, Clone, Default, Debug)]
417pub struct HashedPostStateSorted {
418 pub accounts: HashedAccountsSorted,
420 pub storages: B256Map<HashedStorageSorted>,
422}
423
424impl HashedPostStateSorted {
425 pub const fn new(
427 accounts: HashedAccountsSorted,
428 storages: B256Map<HashedStorageSorted>,
429 ) -> Self {
430 Self { accounts, storages }
431 }
432
433 pub const fn accounts(&self) -> &HashedAccountsSorted {
435 &self.accounts
436 }
437
438 pub const fn account_storages(&self) -> &B256Map<HashedStorageSorted> {
440 &self.storages
441 }
442}
443
444#[derive(Clone, Eq, PartialEq, Default, Debug)]
446pub struct HashedAccountsSorted {
447 pub accounts: Vec<(B256, Account)>,
449 pub destroyed_accounts: B256Set,
451}
452
453impl HashedAccountsSorted {
454 pub fn accounts_sorted(&self) -> impl Iterator<Item = (B256, Option<Account>)> {
456 self.accounts
457 .iter()
458 .map(|(address, account)| (*address, Some(*account)))
459 .chain(self.destroyed_accounts.iter().map(|address| (*address, None)))
460 .sorted_by_key(|entry| *entry.0)
461 }
462}
463
464#[derive(Clone, Eq, PartialEq, Debug)]
466pub struct HashedStorageSorted {
467 pub non_zero_valued_slots: Vec<(B256, FlaggedStorage)>,
469 pub zero_valued_slots: B256Set,
471 pub wiped: bool,
473}
474
475impl HashedStorageSorted {
476 pub const fn is_wiped(&self) -> bool {
478 self.wiped
479 }
480
481 pub fn storage_slots_sorted(&self) -> impl Iterator<Item = (B256, FlaggedStorage)> {
483 self.non_zero_valued_slots
484 .iter()
485 .map(|(hashed_slot, value)| (*hashed_slot, *value))
486 .chain(
487 self.zero_valued_slots
488 .iter()
489 .map(|hashed_slot| (*hashed_slot, FlaggedStorage::ZERO)),
490 )
491 .sorted_by_key(|entry| *entry.0)
492 }
493}
494
495#[derive(Debug)]
503pub struct ChunkedHashedPostState {
504 flattened: alloc::vec::IntoIter<(B256, FlattenedHashedPostStateItem)>,
505 size: usize,
506}
507
508#[derive(Debug)]
509enum FlattenedHashedPostStateItem {
510 Account(Option<Account>),
511 StorageWipe,
512 StorageUpdate { slot: B256, value: FlaggedStorage },
513}
514
515impl ChunkedHashedPostState {
516 fn new(hashed_post_state: HashedPostState, size: usize) -> Self {
517 let flattened = hashed_post_state
518 .storages
519 .into_iter()
520 .flat_map(|(address, storage)| {
521 Some((address, FlattenedHashedPostStateItem::StorageWipe))
523 .filter(|_| storage.wiped)
524 .into_iter()
525 .chain(
526 storage.storage.into_iter().sorted_unstable_by_key(|(slot, _)| *slot).map(
527 move |(slot, value)| {
528 (
529 address,
530 FlattenedHashedPostStateItem::StorageUpdate { slot, value },
531 )
532 },
533 ),
534 )
535 })
536 .chain(hashed_post_state.accounts.into_iter().map(|(address, account)| {
537 (address, FlattenedHashedPostStateItem::Account(account))
538 }))
539 .sorted_by_key(|(address, _)| *address);
544
545 Self { flattened, size }
546 }
547}
548
549impl Iterator for ChunkedHashedPostState {
550 type Item = HashedPostState;
551
552 fn next(&mut self) -> Option<Self::Item> {
553 let mut chunk = HashedPostState::default();
554
555 let mut current_size = 0;
556 while current_size < self.size {
557 let Some((address, item)) = self.flattened.next() else { break };
558
559 match item {
560 FlattenedHashedPostStateItem::Account(account) => {
561 chunk.accounts.insert(address, account);
562 }
563 FlattenedHashedPostStateItem::StorageWipe => {
564 chunk.storages.entry(address).or_default().wiped = true;
565 }
566 FlattenedHashedPostStateItem::StorageUpdate { slot, value } => {
567 chunk.storages.entry(address).or_default().storage.insert(slot, value);
568 }
569 }
570
571 current_size += 1;
572 }
573
574 if chunk.is_empty() {
575 None
576 } else {
577 Some(chunk)
578 }
579 }
580}
581
582#[cfg(test)]
583mod tests {
584 use super::*;
585 use crate::KeccakKeyHasher;
586 use alloy_primitives::Bytes;
587 use revm_database::{states::StorageSlot, StorageWithOriginalValues};
588 use revm_state::{AccountInfo, Bytecode};
589
590 #[test]
591 fn hashed_state_wiped_extension() {
592 let hashed_address = B256::default();
593 let hashed_slot = B256::with_last_byte(64);
594 let hashed_slot2 = B256::with_last_byte(65);
595
596 let original_slot_value = FlaggedStorage::new(123, true);
598 let mut hashed_state = HashedPostState::default().with_storages([(
599 hashed_address,
600 HashedStorage::from_iter(
601 false,
602 [(hashed_slot, original_slot_value), (hashed_slot2, original_slot_value)],
603 ),
604 )]);
605
606 let updated_slot_value = FlaggedStorage::new_from_tuple((321, false));
608 let extension = HashedPostState::default().with_storages([(
609 hashed_address,
610 HashedStorage::from_iter(false, [(hashed_slot, updated_slot_value)]),
611 )]);
612 hashed_state.extend(extension);
613
614 let account_storage = hashed_state.storages.get(&hashed_address);
615 assert_eq!(
616 account_storage.and_then(|st| st.storage.get(&hashed_slot)),
617 Some(&updated_slot_value)
618 );
619 assert_eq!(
620 account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
621 Some(&original_slot_value)
622 );
623 assert_eq!(account_storage.map(|st| st.wiped), Some(false));
624
625 let wiped_extension =
627 HashedPostState::default().with_storages([(hashed_address, HashedStorage::new(true))]);
628 hashed_state.extend(wiped_extension);
629
630 let account_storage = hashed_state.storages.get(&hashed_address);
631 assert_eq!(account_storage.map(|st| st.storage.is_empty()), Some(true));
632 assert_eq!(account_storage.map(|st| st.wiped), Some(true));
633
634 hashed_state.extend(HashedPostState::default().with_storages([(
636 hashed_address,
637 HashedStorage::from_iter(false, [(hashed_slot, original_slot_value)]),
638 )]));
639 let account_storage = hashed_state.storages.get(&hashed_address);
640 assert_eq!(
641 account_storage.and_then(|st| st.storage.get(&hashed_slot)),
642 Some(&original_slot_value)
643 );
644 assert_eq!(account_storage.and_then(|st| st.storage.get(&hashed_slot2)), None);
645 assert_eq!(account_storage.map(|st| st.wiped), Some(true));
646
647 hashed_state.extend(HashedPostState::default().with_storages([(
649 hashed_address,
650 HashedStorage::from_iter(false, [(hashed_slot2, updated_slot_value)]),
651 )]));
652 let account_storage = hashed_state.storages.get(&hashed_address);
653 assert_eq!(
654 account_storage.and_then(|st| st.storage.get(&hashed_slot)),
655 Some(&original_slot_value)
656 );
657 assert_eq!(
658 account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
659 Some(&updated_slot_value)
660 );
661 assert_eq!(account_storage.map(|st| st.wiped), Some(true));
662 }
663
664 #[test]
665 fn test_hashed_post_state_from_bundle_state() {
666 let address = Address::random();
668
669 let account_info = AccountInfo {
671 balance: U256::from(123),
672 nonce: 42,
673 code_hash: B256::random(),
674 code: Some(Bytecode::new_raw(Bytes::from(vec![1, 2]))),
675 };
676
677 let mut storage = StorageWithOriginalValues::default();
678 storage.insert(
679 U256::from(1),
680 StorageSlot { present_value: FlaggedStorage::new_from_value(4), ..Default::default() },
681 );
682
683 let account = BundleAccount {
685 status: AccountStatus::Changed,
686 info: Some(account_info.clone()),
687 storage,
688 original_info: None,
689 };
690
691 let state = vec![(&address, &account)];
693
694 let hashed_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(state);
696
697 assert_eq!(hashed_state.accounts.len(), 1);
699 assert_eq!(hashed_state.storages.len(), 1);
700
701 assert_eq!(
703 *hashed_state.accounts.get(&keccak256(address)).unwrap(),
704 Some(account_info.into())
705 );
706 }
707
708 #[test]
709 fn test_hashed_post_state_with_accounts() {
710 let address_1 = Address::random();
712 let address_2 = Address::random();
713
714 let account_info_1 = AccountInfo {
715 balance: U256::from(1000),
716 nonce: 1,
717 code_hash: B256::random(),
718 code: None,
719 };
720
721 let account_1 = (keccak256(address_1), Some(account_info_1.into()));
723 let account_2 = (keccak256(address_2), None);
724
725 let hashed_state = HashedPostState::default().with_accounts(vec![account_1, account_2]);
727
728 assert_eq!(hashed_state.accounts.len(), 2);
730 assert!(hashed_state.accounts.contains_key(&keccak256(address_1)));
731 assert!(hashed_state.accounts.contains_key(&keccak256(address_2)));
732 }
733
734 #[test]
735 fn test_hashed_post_state_with_storages() {
736 let address_1 = Address::random();
738 let address_2 = Address::random();
739
740 let storage_1 = (keccak256(address_1), HashedStorage::new(false));
741 let storage_2 = (keccak256(address_2), HashedStorage::new(true));
742
743 let hashed_state = HashedPostState::default().with_storages(vec![storage_1, storage_2]);
745
746 assert_eq!(hashed_state.storages.len(), 2);
748 assert!(hashed_state.storages.contains_key(&keccak256(address_1)));
749 assert!(hashed_state.storages.contains_key(&keccak256(address_2)));
750 }
751
752 #[test]
753 fn test_hashed_post_state_is_empty() {
754 let empty_state = HashedPostState::default();
756 assert!(empty_state.is_empty());
757
758 let non_empty_state = HashedPostState::default()
760 .with_accounts(vec![(keccak256(Address::random()), Some(Account::default()))]);
761 assert!(!non_empty_state.is_empty());
762 }
763
764 fn create_state_for_multi_proof_targets() -> HashedPostState {
765 let mut state = HashedPostState::default();
766
767 let addr1 = B256::random();
768 let addr2 = B256::random();
769 state.accounts.insert(addr1, Some(Default::default()));
770 state.accounts.insert(addr2, Some(Default::default()));
771
772 let mut storage = HashedStorage::default();
773 let slot1 = B256::random();
774 let slot2 = B256::random();
775 storage.storage.insert(slot1, FlaggedStorage::new(0, false));
776 storage.storage.insert(slot2, FlaggedStorage::new(1, false));
777 state.storages.insert(addr1, storage);
778
779 state
780 }
781
782 #[test]
783 fn test_multi_proof_targets_difference_empty_state() {
784 let state = HashedPostState::default();
785 let excluded = MultiProofTargets::default();
786
787 let targets = state.multi_proof_targets_difference(&excluded);
788 assert!(targets.is_empty());
789 }
790
791 #[test]
792 fn test_multi_proof_targets_difference_new_account_targets() {
793 let state = create_state_for_multi_proof_targets();
794 let excluded = MultiProofTargets::default();
795
796 let targets = state.multi_proof_targets_difference(&excluded);
798 assert_eq!(targets.len(), state.accounts.len());
799 for addr in state.accounts.keys() {
800 assert!(targets.contains_key(addr));
801 }
802 }
803
804 #[test]
805 fn test_multi_proof_targets_difference_new_storage_targets() {
806 let state = create_state_for_multi_proof_targets();
807 let excluded = MultiProofTargets::default();
808
809 let targets = state.multi_proof_targets_difference(&excluded);
810
811 for (addr, storage) in &state.storages {
813 assert!(targets.contains_key(addr));
814 let target_slots = &targets[addr];
815 assert_eq!(target_slots.len(), storage.storage.len());
816 for slot in storage.storage.keys() {
817 assert!(target_slots.contains(slot));
818 }
819 }
820 }
821
822 #[test]
823 fn test_multi_proof_targets_difference_filter_excluded_accounts() {
824 let state = create_state_for_multi_proof_targets();
825 let mut excluded = MultiProofTargets::default();
826
827 let excluded_addr = state
829 .accounts
830 .keys()
831 .find(|&&addr| !state.storages.contains_key(&addr))
832 .expect("Should have an account without storage");
833
834 excluded.insert(*excluded_addr, HashSet::default());
836
837 let targets = state.multi_proof_targets_difference(&excluded);
838
839 assert!(!targets.contains_key(excluded_addr));
841 assert_eq!(targets.len(), state.accounts.len() - 1);
843 }
844
845 #[test]
846 fn test_multi_proof_targets_difference_filter_excluded_storage() {
847 let state = create_state_for_multi_proof_targets();
848 let mut excluded = MultiProofTargets::default();
849
850 let (addr, storage) = state.storages.iter().next().unwrap();
852 let mut excluded_slots = HashSet::default();
853 let excluded_slot = *storage.storage.keys().next().unwrap();
854 excluded_slots.insert(excluded_slot);
855 excluded.insert(*addr, excluded_slots);
856
857 let targets = state.multi_proof_targets_difference(&excluded);
858
859 let target_slots = &targets[addr];
861 assert!(!target_slots.contains(&excluded_slot));
862 assert_eq!(target_slots.len(), storage.storage.len() - 1);
863 }
864
865 #[test]
866 fn test_multi_proof_targets_difference_mixed_excluded_state() {
867 let mut state = HashedPostState::default();
868 let mut excluded = MultiProofTargets::default();
869
870 let addr1 = B256::random();
871 let addr2 = B256::random();
872 let slot1 = B256::random();
873 let slot2 = B256::random();
874
875 state.accounts.insert(addr1, Some(Default::default()));
876 state.accounts.insert(addr2, Some(Default::default()));
877
878 let mut storage = HashedStorage::default();
879 storage.storage.insert(slot1, FlaggedStorage::new(0, false));
880 storage.storage.insert(slot2, FlaggedStorage::new(1, false));
881 state.storages.insert(addr1, storage);
882
883 let mut excluded_slots = HashSet::default();
884 excluded_slots.insert(slot1);
885 excluded.insert(addr1, excluded_slots);
886
887 let targets = state.multi_proof_targets_difference(&excluded);
888
889 assert!(targets.contains_key(&addr2));
890 assert!(!targets[&addr1].contains(&slot1));
891 assert!(targets[&addr1].contains(&slot2));
892 }
893
894 #[test]
895 fn test_multi_proof_targets_difference_unmodified_account_with_storage() {
896 let mut state = HashedPostState::default();
897 let excluded = MultiProofTargets::default();
898
899 let addr = B256::random();
900 let slot1 = B256::random();
901 let slot2 = B256::random();
902
903 let mut storage = HashedStorage::default();
906 storage.storage.insert(slot1, FlaggedStorage::new_from_value(1));
907 storage.storage.insert(slot2, FlaggedStorage::new_from_value(2));
908 state.storages.insert(addr, storage);
909
910 assert!(!state.accounts.contains_key(&addr));
911 assert!(!excluded.contains_key(&addr));
912
913 let targets = state.multi_proof_targets_difference(&excluded);
914
915 assert!(targets.contains_key(&addr));
917
918 let target_slots = &targets[&addr];
919 assert_eq!(target_slots.len(), 2);
920 assert!(target_slots.contains(&slot1));
921 assert!(target_slots.contains(&slot2));
922 }
923
924 #[test]
925 fn test_partition_by_targets() {
926 let addr1 = B256::random();
927 let addr2 = B256::random();
928 let slot1 = B256::random();
929 let slot2 = B256::random();
930
931 let state = HashedPostState {
932 accounts: B256Map::from_iter([
933 (addr1, Some(Default::default())),
934 (addr2, Some(Default::default())),
935 ]),
936 storages: B256Map::from_iter([(
937 addr1,
938 HashedStorage {
939 wiped: true,
940 storage: B256Map::from_iter([
941 (slot1, FlaggedStorage::ZERO),
942 (slot2, FlaggedStorage::new_from_value(1)),
943 ]),
944 },
945 )]),
946 };
947 let targets = MultiProofTargets::from_iter([(addr1, HashSet::from_iter([slot1]))]);
948
949 let (with_targets, without_targets) = state.partition_by_targets(&targets);
950
951 assert_eq!(
952 with_targets,
953 HashedPostState {
954 accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
955 storages: B256Map::from_iter([(
956 addr1,
957 HashedStorage {
958 wiped: true,
959 storage: B256Map::from_iter([(slot1, FlaggedStorage::ZERO)])
960 }
961 )]),
962 }
963 );
964 assert_eq!(
965 without_targets,
966 HashedPostState {
967 accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
968 storages: B256Map::from_iter([(
969 addr1,
970 HashedStorage {
971 wiped: false,
972 storage: B256Map::from_iter([(slot2, FlaggedStorage::new_from_value(1))])
973 }
974 )]),
975 }
976 );
977 }
978
979 #[test]
980 fn test_chunks() {
981 let addr1 = B256::from([1; 32]);
982 let addr2 = B256::from([2; 32]);
983 let slot1 = B256::from([1; 32]);
984 let slot2 = B256::from([2; 32]);
985
986 let state = HashedPostState {
987 accounts: B256Map::from_iter([
988 (addr1, Some(Default::default())),
989 (addr2, Some(Default::default())),
990 ]),
991 storages: B256Map::from_iter([(
992 addr2,
993 HashedStorage {
994 wiped: true,
995 storage: B256Map::from_iter([
996 (slot1, FlaggedStorage::ZERO),
997 (slot2, FlaggedStorage::new_from_value(1)),
998 ]),
999 },
1000 )]),
1001 };
1002
1003 let mut chunks = state.chunks(2);
1004 assert_eq!(
1005 chunks.next(),
1006 Some(HashedPostState {
1007 accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
1008 storages: B256Map::from_iter([(addr2, HashedStorage::new(true)),])
1009 })
1010 );
1011 assert_eq!(
1012 chunks.next(),
1013 Some(HashedPostState {
1014 accounts: B256Map::default(),
1015 storages: B256Map::from_iter([(
1016 addr2,
1017 HashedStorage {
1018 wiped: false,
1019 storage: B256Map::from_iter([
1020 (slot1, FlaggedStorage::ZERO),
1021 (slot2, FlaggedStorage::new_from_value(1)),
1022 ]),
1023 },
1024 )])
1025 })
1026 );
1027 assert_eq!(
1028 chunks.next(),
1029 Some(HashedPostState {
1030 accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
1031 storages: B256Map::default()
1032 })
1033 );
1034 assert_eq!(chunks.next(), None);
1035 }
1036}