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