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