1use core::ops::Not;
2
3use crate::{
4 added_removed_keys::MultiAddedRemovedKeys,
5 prefix_set::{PrefixSetMut, TriePrefixSetsMut},
6 utils::extend_sorted_vec,
7 KeyHasher, MultiProofTargets, Nibbles,
8};
9use alloc::{borrow::Cow, vec::Vec};
10use alloy_primitives::{
11 keccak256,
12 map::{hash_map, B256Map, B256Set, HashMap, HashSet},
13 Address, B256, U256,
14};
15use itertools::Itertools;
16#[cfg(feature = "rayon")]
17pub use rayon::*;
18use reth_primitives_traits::Account;
19
20#[cfg(feature = "rayon")]
21use rayon::prelude::{IntoParallelIterator, ParallelIterator};
22
23use revm_database::{AccountStatus, BundleAccount};
24
25#[derive(PartialEq, Eq, Clone, Default, Debug)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct HashedPostState {
29 pub accounts: B256Map<Option<Account>>,
31 pub storages: B256Map<HashedStorage>,
33}
34
35impl HashedPostState {
36 pub fn with_capacity(capacity: usize) -> Self {
38 Self {
39 accounts: B256Map::with_capacity_and_hasher(capacity, Default::default()),
40 storages: B256Map::with_capacity_and_hasher(capacity, Default::default()),
41 }
42 }
43
44 #[inline]
48 #[cfg(feature = "rayon")]
49 pub fn from_bundle_state<'a, KH: KeyHasher>(
50 state: impl IntoParallelIterator<Item = (&'a Address, &'a BundleAccount)>,
51 ) -> Self {
52 let hashed = state
53 .into_par_iter()
54 .map(|(address, account)| {
55 let hashed_address = KH::hash_key(address);
56 let hashed_account = account.info.as_ref().map(Into::into);
57 let hashed_storage = HashedStorage::from_plain_storage(
58 account.status,
59 account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
60 );
61 (hashed_address, (hashed_account, hashed_storage))
62 })
63 .collect::<Vec<(B256, (Option<Account>, HashedStorage))>>();
64
65 let mut accounts = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
66 let mut storages = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
67 for (address, (account, storage)) in hashed {
68 accounts.insert(address, account);
69 if !storage.is_empty() {
70 storages.insert(address, storage);
71 }
72 }
73 Self { accounts, storages }
74 }
75
76 #[cfg(not(feature = "rayon"))]
80 pub fn from_bundle_state<'a, KH: KeyHasher>(
81 state: impl IntoIterator<Item = (&'a Address, &'a BundleAccount)>,
82 ) -> Self {
83 let hashed = state
84 .into_iter()
85 .map(|(address, account)| {
86 let hashed_address = KH::hash_key(address);
87 let hashed_account = account.info.as_ref().map(Into::into);
88 let hashed_storage = HashedStorage::from_plain_storage(
89 account.status,
90 account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
91 );
92 (hashed_address, (hashed_account, hashed_storage))
93 })
94 .collect::<Vec<(B256, (Option<Account>, HashedStorage))>>();
95
96 let mut accounts = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
97 let mut storages = HashMap::with_capacity_and_hasher(hashed.len(), Default::default());
98 for (address, (account, storage)) in hashed {
99 accounts.insert(address, account);
100 if !storage.is_empty() {
101 storages.insert(address, storage);
102 }
103 }
104 Self { accounts, storages }
105 }
106
107 pub fn from_hashed_storage(hashed_address: B256, storage: HashedStorage) -> Self {
109 Self {
110 accounts: HashMap::default(),
111 storages: HashMap::from_iter([(hashed_address, storage)]),
112 }
113 }
114
115 pub fn with_accounts(
117 mut self,
118 accounts: impl IntoIterator<Item = (B256, Option<Account>)>,
119 ) -> Self {
120 self.accounts = HashMap::from_iter(accounts);
121 self
122 }
123
124 pub fn with_storages(
126 mut self,
127 storages: impl IntoIterator<Item = (B256, HashedStorage)>,
128 ) -> Self {
129 self.storages = HashMap::from_iter(storages);
130 self
131 }
132
133 pub fn is_empty(&self) -> bool {
135 self.accounts.is_empty() && self.storages.is_empty()
136 }
137
138 pub fn construct_prefix_sets(&self) -> TriePrefixSetsMut {
142 let mut account_prefix_set = PrefixSetMut::with_capacity(self.accounts.len());
144 let mut destroyed_accounts = HashSet::default();
145 for (hashed_address, account) in &self.accounts {
146 account_prefix_set.insert(Nibbles::unpack(hashed_address));
147
148 if account.is_none() {
149 destroyed_accounts.insert(*hashed_address);
150 }
151 }
152
153 let mut storage_prefix_sets =
155 HashMap::with_capacity_and_hasher(self.storages.len(), Default::default());
156 for (hashed_address, hashed_storage) in &self.storages {
157 account_prefix_set.insert(Nibbles::unpack(hashed_address));
158 storage_prefix_sets.insert(*hashed_address, hashed_storage.construct_prefix_set());
159 }
160
161 TriePrefixSetsMut { account_prefix_set, storage_prefix_sets, destroyed_accounts }
162 }
163
164 pub fn multi_proof_targets(&self) -> MultiProofTargets {
166 let mut targets = MultiProofTargets::with_capacity(self.accounts.len());
168 for hashed_address in self.accounts.keys() {
169 targets.insert(*hashed_address, Default::default());
170 }
171 for (hashed_address, storage) in &self.storages {
172 targets.entry(*hashed_address).or_default().extend(storage.storage.keys().copied());
173 }
174 targets
175 }
176
177 pub fn multi_proof_targets_difference(
183 &self,
184 excluded: &MultiProofTargets,
185 ) -> MultiProofTargets {
186 let mut targets = MultiProofTargets::default();
187 for hashed_address in self.accounts.keys() {
188 if !excluded.contains_key(hashed_address) {
189 targets.insert(*hashed_address, Default::default());
190 }
191 }
192 for (hashed_address, storage) in &self.storages {
193 let maybe_excluded_storage = excluded.get(hashed_address);
194 let mut hashed_slots_targets = storage
195 .storage
196 .keys()
197 .filter(|slot| !maybe_excluded_storage.is_some_and(|f| f.contains(*slot)))
198 .peekable();
199 if hashed_slots_targets.peek().is_some() {
200 targets.entry(*hashed_address).or_default().extend(hashed_slots_targets);
201 }
202 }
203 targets
204 }
205
206 pub fn partition_by_targets(
213 mut self,
214 targets: &MultiProofTargets,
215 added_removed_keys: &MultiAddedRemovedKeys,
216 ) -> (Self, Self) {
217 let mut state_updates_not_in_targets = Self::default();
218
219 self.storages.retain(|&address, storage| {
220 let storage_added_removed_keys = added_removed_keys.get_storage(&address);
221
222 let (retain, storage_not_in_targets) = match targets.get(&address) {
223 Some(storage_in_targets) => {
224 let mut storage_not_in_targets = HashedStorage::default();
225 storage.storage.retain(|&slot, value| {
226 if storage_in_targets.contains(&slot) &&
227 !storage_added_removed_keys.is_some_and(|k| k.is_removed(&slot))
228 {
229 return true
230 }
231
232 storage_not_in_targets.storage.insert(slot, *value);
233 false
234 });
235
236 let retain = !storage.storage.is_empty();
240
241 if !retain {
245 storage_not_in_targets.wiped = storage.wiped;
246 }
247
248 (
249 retain,
250 storage_not_in_targets.is_empty().not().then_some(storage_not_in_targets),
251 )
252 }
253 None => (false, Some(core::mem::take(storage))),
254 };
255
256 if let Some(storage_not_in_targets) = storage_not_in_targets {
257 state_updates_not_in_targets.storages.insert(address, storage_not_in_targets);
258 }
259
260 retain
261 });
262 self.accounts.retain(|&address, account| {
263 if targets.contains_key(&address) {
264 return true
265 }
266
267 state_updates_not_in_targets.accounts.insert(address, *account);
268 false
269 });
270
271 (self, state_updates_not_in_targets)
272 }
273
274 pub fn chunks(self, size: usize) -> ChunkedHashedPostState {
278 ChunkedHashedPostState::new(self, size)
279 }
280
281 pub fn chunking_length(&self) -> usize {
283 self.accounts.len() +
284 self.storages
285 .values()
286 .map(|storage| if storage.wiped { 1 } else { 0 } + storage.storage.len())
287 .sum::<usize>()
288 }
289
290 pub fn extend(&mut self, other: Self) {
293 self.extend_inner(Cow::Owned(other));
294 }
295
296 pub fn extend_ref(&mut self, other: &Self) {
301 self.extend_inner(Cow::Borrowed(other));
302 }
303
304 fn extend_inner(&mut self, other: Cow<'_, Self>) {
305 self.accounts.extend(other.accounts.iter().map(|(&k, &v)| (k, v)));
306
307 self.storages.reserve(other.storages.len());
308 match other {
309 Cow::Borrowed(other) => {
310 self.extend_storages(other.storages.iter().map(|(k, v)| (*k, Cow::Borrowed(v))))
311 }
312 Cow::Owned(other) => {
313 self.extend_storages(other.storages.into_iter().map(|(k, v)| (k, Cow::Owned(v))))
314 }
315 }
316 }
317
318 fn extend_storages<'a>(
319 &mut self,
320 storages: impl IntoIterator<Item = (B256, Cow<'a, HashedStorage>)>,
321 ) {
322 for (hashed_address, storage) in storages {
323 match self.storages.entry(hashed_address) {
324 hash_map::Entry::Vacant(entry) => {
325 entry.insert(storage.into_owned());
326 }
327 hash_map::Entry::Occupied(mut entry) => {
328 entry.get_mut().extend(&storage);
329 }
330 }
331 }
332 }
333
334 pub fn into_sorted(self) -> HashedPostStateSorted {
336 let mut updated_accounts = Vec::new();
337 let mut destroyed_accounts = HashSet::default();
338 for (hashed_address, info) in self.accounts {
339 if let Some(info) = info {
340 updated_accounts.push((hashed_address, info));
341 } else {
342 destroyed_accounts.insert(hashed_address);
343 }
344 }
345 updated_accounts.sort_unstable_by_key(|(address, _)| *address);
346 let accounts = HashedAccountsSorted { accounts: updated_accounts, destroyed_accounts };
347
348 let storages = self
349 .storages
350 .into_iter()
351 .map(|(hashed_address, storage)| (hashed_address, storage.into_sorted()))
352 .collect();
353
354 HashedPostStateSorted { accounts, storages }
355 }
356
357 pub fn drain_into_sorted(&mut self) -> HashedPostStateSorted {
365 let mut updated_accounts = Vec::new();
366 let mut destroyed_accounts = HashSet::default();
367 for (hashed_address, info) in self.accounts.drain() {
368 if let Some(info) = info {
369 updated_accounts.push((hashed_address, info));
370 } else {
371 destroyed_accounts.insert(hashed_address);
372 }
373 }
374 updated_accounts.sort_unstable_by_key(|(address, _)| *address);
375 let accounts = HashedAccountsSorted { accounts: updated_accounts, destroyed_accounts };
376
377 let storages = self
378 .storages
379 .drain()
380 .map(|(hashed_address, storage)| (hashed_address, storage.into_sorted()))
381 .collect();
382
383 HashedPostStateSorted { accounts, storages }
384 }
385
386 pub fn clear(&mut self) {
388 self.accounts.clear();
389 self.storages.clear();
390 }
391}
392
393#[derive(PartialEq, Eq, Clone, Debug, Default)]
395#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
396pub struct HashedStorage {
397 pub wiped: bool,
399 pub storage: B256Map<U256>,
401}
402
403impl HashedStorage {
404 pub fn new(wiped: bool) -> Self {
406 Self { wiped, storage: HashMap::default() }
407 }
408
409 pub fn is_empty(&self) -> bool {
411 !self.wiped && self.storage.is_empty()
412 }
413
414 pub fn from_iter(wiped: bool, iter: impl IntoIterator<Item = (B256, U256)>) -> Self {
416 Self { wiped, storage: HashMap::from_iter(iter) }
417 }
418
419 pub fn from_plain_storage<'a>(
421 status: AccountStatus,
422 storage: impl IntoIterator<Item = (&'a U256, &'a U256)>,
423 ) -> Self {
424 Self::from_iter(
425 status.was_destroyed(),
426 storage.into_iter().map(|(key, value)| (keccak256(B256::from(*key)), *value)),
427 )
428 }
429
430 pub fn construct_prefix_set(&self) -> PrefixSetMut {
432 if self.wiped {
433 PrefixSetMut::all()
434 } else {
435 let mut prefix_set = PrefixSetMut::with_capacity(self.storage.len());
436 for hashed_slot in self.storage.keys() {
437 prefix_set.insert(Nibbles::unpack(hashed_slot));
438 }
439 prefix_set
440 }
441 }
442
443 pub fn extend(&mut self, other: &Self) {
446 if other.wiped {
447 self.wiped = true;
448 self.storage.clear();
449 }
450 self.storage.extend(other.storage.iter().map(|(&k, &v)| (k, v)));
451 }
452
453 pub fn into_sorted(self) -> HashedStorageSorted {
455 let mut non_zero_valued_slots = Vec::new();
456 let mut zero_valued_slots = HashSet::default();
457 for (hashed_slot, value) in self.storage {
458 if value.is_zero() {
459 zero_valued_slots.insert(hashed_slot);
460 } else {
461 non_zero_valued_slots.push((hashed_slot, value));
462 }
463 }
464 non_zero_valued_slots.sort_unstable_by_key(|(key, _)| *key);
465
466 HashedStorageSorted { non_zero_valued_slots, zero_valued_slots, wiped: self.wiped }
467 }
468}
469
470#[derive(PartialEq, Eq, Clone, Default, Debug)]
472pub struct HashedPostStateSorted {
473 pub accounts: HashedAccountsSorted,
475 pub storages: B256Map<HashedStorageSorted>,
477}
478
479impl HashedPostStateSorted {
480 pub const fn new(
482 accounts: HashedAccountsSorted,
483 storages: B256Map<HashedStorageSorted>,
484 ) -> Self {
485 Self { accounts, storages }
486 }
487
488 pub const fn accounts(&self) -> &HashedAccountsSorted {
490 &self.accounts
491 }
492
493 pub const fn account_storages(&self) -> &B256Map<HashedStorageSorted> {
495 &self.storages
496 }
497
498 pub fn is_empty(&self) -> bool {
500 self.accounts.accounts.is_empty() &&
501 self.accounts.destroyed_accounts.is_empty() &&
502 self.storages.is_empty()
503 }
504
505 pub fn total_len(&self) -> usize {
507 self.accounts.accounts.len() +
508 self.accounts.destroyed_accounts.len() +
509 self.storages.values().map(|storage| storage.len()).sum::<usize>()
510 }
511
512 pub fn extend_ref(&mut self, other: &Self) {
515 self.accounts.extend_ref(&other.accounts);
517
518 for (hashed_address, other_storage) in &other.storages {
520 self.storages
521 .entry(*hashed_address)
522 .and_modify(|existing| existing.extend_ref(other_storage))
523 .or_insert_with(|| other_storage.clone());
524 }
525 }
526}
527
528impl AsRef<Self> for HashedPostStateSorted {
529 fn as_ref(&self) -> &Self {
530 self
531 }
532}
533
534#[derive(Clone, Eq, PartialEq, Default, Debug)]
536pub struct HashedAccountsSorted {
537 pub accounts: Vec<(B256, Account)>,
539 pub destroyed_accounts: B256Set,
541}
542
543impl HashedAccountsSorted {
544 pub fn accounts_sorted(&self) -> impl Iterator<Item = (B256, Option<Account>)> {
546 self.accounts
547 .iter()
548 .map(|(address, account)| (*address, Some(*account)))
549 .chain(self.destroyed_accounts.iter().map(|address| (*address, None)))
550 .sorted_by_key(|entry| *entry.0)
551 }
552
553 pub fn extend_ref(&mut self, other: &Self) {
556 self.accounts.retain(|(addr, _)| !other.destroyed_accounts.contains(addr));
559
560 extend_sorted_vec(&mut self.accounts, &other.accounts);
562
563 self.destroyed_accounts.extend(&other.destroyed_accounts);
565 }
566}
567
568#[derive(Clone, Eq, PartialEq, Debug)]
570pub struct HashedStorageSorted {
571 pub non_zero_valued_slots: Vec<(B256, U256)>,
573 pub zero_valued_slots: B256Set,
575 pub wiped: bool,
577}
578
579impl HashedStorageSorted {
580 pub const fn is_wiped(&self) -> bool {
582 self.wiped
583 }
584
585 pub fn storage_slots_sorted(&self) -> impl Iterator<Item = (B256, U256)> {
587 self.non_zero_valued_slots
588 .iter()
589 .map(|(hashed_slot, value)| (*hashed_slot, *value))
590 .chain(self.zero_valued_slots.iter().map(|hashed_slot| (*hashed_slot, U256::ZERO)))
591 .sorted_by_key(|entry| *entry.0)
592 }
593
594 pub fn len(&self) -> usize {
596 self.non_zero_valued_slots.len() + self.zero_valued_slots.len()
597 }
598
599 pub fn is_empty(&self) -> bool {
601 self.non_zero_valued_slots.is_empty() && self.zero_valued_slots.is_empty()
602 }
603
604 pub fn extend_ref(&mut self, other: &Self) {
607 if other.wiped {
608 self.wiped = true;
610 self.non_zero_valued_slots.clear();
611 self.zero_valued_slots.clear();
612 self.non_zero_valued_slots.extend_from_slice(&other.non_zero_valued_slots);
613 self.zero_valued_slots.extend(&other.zero_valued_slots);
614 return;
615 }
616
617 self.non_zero_valued_slots.retain(|(slot, _)| !other.zero_valued_slots.contains(slot));
618
619 extend_sorted_vec(&mut self.non_zero_valued_slots, &other.non_zero_valued_slots);
621
622 self.zero_valued_slots.extend(&other.zero_valued_slots);
624 }
625}
626
627#[derive(Debug)]
635pub struct ChunkedHashedPostState {
636 flattened: alloc::vec::IntoIter<(B256, FlattenedHashedPostStateItem)>,
637 size: usize,
638}
639
640#[derive(Debug)]
641enum FlattenedHashedPostStateItem {
642 Account(Option<Account>),
643 StorageWipe,
644 StorageUpdate { slot: B256, value: U256 },
645}
646
647impl ChunkedHashedPostState {
648 fn new(hashed_post_state: HashedPostState, size: usize) -> Self {
649 let flattened = hashed_post_state
650 .storages
651 .into_iter()
652 .flat_map(|(address, storage)| {
653 Some((address, FlattenedHashedPostStateItem::StorageWipe))
655 .filter(|_| storage.wiped)
656 .into_iter()
657 .chain(
658 storage.storage.into_iter().sorted_unstable_by_key(|(slot, _)| *slot).map(
659 move |(slot, value)| {
660 (
661 address,
662 FlattenedHashedPostStateItem::StorageUpdate { slot, value },
663 )
664 },
665 ),
666 )
667 })
668 .chain(hashed_post_state.accounts.into_iter().map(|(address, account)| {
669 (address, FlattenedHashedPostStateItem::Account(account))
670 }))
671 .sorted_by_key(|(address, _)| *address);
676
677 Self { flattened, size }
678 }
679}
680
681impl Iterator for ChunkedHashedPostState {
682 type Item = HashedPostState;
683
684 fn next(&mut self) -> Option<Self::Item> {
685 let mut chunk = HashedPostState::default();
686
687 let mut current_size = 0;
688 while current_size < self.size {
689 let Some((address, item)) = self.flattened.next() else { break };
690
691 match item {
692 FlattenedHashedPostStateItem::Account(account) => {
693 chunk.accounts.insert(address, account);
694 }
695 FlattenedHashedPostStateItem::StorageWipe => {
696 chunk.storages.entry(address).or_default().wiped = true;
697 }
698 FlattenedHashedPostStateItem::StorageUpdate { slot, value } => {
699 chunk.storages.entry(address).or_default().storage.insert(slot, value);
700 }
701 }
702
703 current_size += 1;
704 }
705
706 if chunk.is_empty() {
707 None
708 } else {
709 Some(chunk)
710 }
711 }
712}
713
714#[cfg(test)]
715mod tests {
716 use super::*;
717 use crate::KeccakKeyHasher;
718 use alloy_primitives::Bytes;
719 use revm_database::{states::StorageSlot, StorageWithOriginalValues};
720 use revm_state::{AccountInfo, Bytecode};
721
722 #[test]
723 fn hashed_state_wiped_extension() {
724 let hashed_address = B256::default();
725 let hashed_slot = B256::with_last_byte(64);
726 let hashed_slot2 = B256::with_last_byte(65);
727
728 let original_slot_value = U256::from(123);
730 let mut hashed_state = HashedPostState::default().with_storages([(
731 hashed_address,
732 HashedStorage::from_iter(
733 false,
734 [(hashed_slot, original_slot_value), (hashed_slot2, original_slot_value)],
735 ),
736 )]);
737
738 let updated_slot_value = U256::from(321);
740 let extension = HashedPostState::default().with_storages([(
741 hashed_address,
742 HashedStorage::from_iter(false, [(hashed_slot, updated_slot_value)]),
743 )]);
744 hashed_state.extend(extension);
745
746 let account_storage = hashed_state.storages.get(&hashed_address);
747 assert_eq!(
748 account_storage.and_then(|st| st.storage.get(&hashed_slot)),
749 Some(&updated_slot_value)
750 );
751 assert_eq!(
752 account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
753 Some(&original_slot_value)
754 );
755 assert_eq!(account_storage.map(|st| st.wiped), Some(false));
756
757 let wiped_extension =
759 HashedPostState::default().with_storages([(hashed_address, HashedStorage::new(true))]);
760 hashed_state.extend(wiped_extension);
761
762 let account_storage = hashed_state.storages.get(&hashed_address);
763 assert_eq!(account_storage.map(|st| st.storage.is_empty()), Some(true));
764 assert_eq!(account_storage.map(|st| st.wiped), Some(true));
765
766 hashed_state.extend(HashedPostState::default().with_storages([(
768 hashed_address,
769 HashedStorage::from_iter(false, [(hashed_slot, original_slot_value)]),
770 )]));
771 let account_storage = hashed_state.storages.get(&hashed_address);
772 assert_eq!(
773 account_storage.and_then(|st| st.storage.get(&hashed_slot)),
774 Some(&original_slot_value)
775 );
776 assert_eq!(account_storage.and_then(|st| st.storage.get(&hashed_slot2)), None);
777 assert_eq!(account_storage.map(|st| st.wiped), Some(true));
778
779 hashed_state.extend(HashedPostState::default().with_storages([(
781 hashed_address,
782 HashedStorage::from_iter(false, [(hashed_slot2, updated_slot_value)]),
783 )]));
784 let account_storage = hashed_state.storages.get(&hashed_address);
785 assert_eq!(
786 account_storage.and_then(|st| st.storage.get(&hashed_slot)),
787 Some(&original_slot_value)
788 );
789 assert_eq!(
790 account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
791 Some(&updated_slot_value)
792 );
793 assert_eq!(account_storage.map(|st| st.wiped), Some(true));
794 }
795
796 #[test]
797 fn test_hashed_post_state_from_bundle_state() {
798 let address = Address::random();
800
801 let account_info = AccountInfo {
803 balance: U256::from(123),
804 nonce: 42,
805 code_hash: B256::random(),
806 code: Some(Bytecode::new_raw(Bytes::from(vec![1, 2]))),
807 };
808
809 let mut storage = StorageWithOriginalValues::default();
810 storage.insert(
811 U256::from(1),
812 StorageSlot { present_value: U256::from(4), ..Default::default() },
813 );
814
815 let account = BundleAccount {
817 status: AccountStatus::Changed,
818 info: Some(account_info.clone()),
819 storage,
820 original_info: None,
821 };
822
823 let state = vec![(&address, &account)];
825
826 let hashed_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(state);
828
829 assert_eq!(hashed_state.accounts.len(), 1);
831 assert_eq!(hashed_state.storages.len(), 1);
832
833 assert_eq!(
835 *hashed_state.accounts.get(&keccak256(address)).unwrap(),
836 Some(account_info.into())
837 );
838 }
839
840 #[test]
841 fn test_hashed_post_state_with_accounts() {
842 let address_1 = Address::random();
844 let address_2 = Address::random();
845
846 let account_info_1 = AccountInfo {
847 balance: U256::from(1000),
848 nonce: 1,
849 code_hash: B256::random(),
850 code: None,
851 };
852
853 let account_1 = (keccak256(address_1), Some(account_info_1.into()));
855 let account_2 = (keccak256(address_2), None);
856
857 let hashed_state = HashedPostState::default().with_accounts(vec![account_1, account_2]);
859
860 assert_eq!(hashed_state.accounts.len(), 2);
862 assert!(hashed_state.accounts.contains_key(&keccak256(address_1)));
863 assert!(hashed_state.accounts.contains_key(&keccak256(address_2)));
864 }
865
866 #[test]
867 fn test_hashed_post_state_with_storages() {
868 let address_1 = Address::random();
870 let address_2 = Address::random();
871
872 let storage_1 = (keccak256(address_1), HashedStorage::new(false));
873 let storage_2 = (keccak256(address_2), HashedStorage::new(true));
874
875 let hashed_state = HashedPostState::default().with_storages(vec![storage_1, storage_2]);
877
878 assert_eq!(hashed_state.storages.len(), 2);
880 assert!(hashed_state.storages.contains_key(&keccak256(address_1)));
881 assert!(hashed_state.storages.contains_key(&keccak256(address_2)));
882 }
883
884 #[test]
885 fn test_hashed_post_state_is_empty() {
886 let empty_state = HashedPostState::default();
888 assert!(empty_state.is_empty());
889
890 let non_empty_state = HashedPostState::default()
892 .with_accounts(vec![(keccak256(Address::random()), Some(Account::default()))]);
893 assert!(!non_empty_state.is_empty());
894 }
895
896 fn create_state_for_multi_proof_targets() -> HashedPostState {
897 let mut state = HashedPostState::default();
898
899 let addr1 = B256::random();
900 let addr2 = B256::random();
901 state.accounts.insert(addr1, Some(Default::default()));
902 state.accounts.insert(addr2, Some(Default::default()));
903
904 let mut storage = HashedStorage::default();
905 let slot1 = B256::random();
906 let slot2 = B256::random();
907 storage.storage.insert(slot1, U256::ZERO);
908 storage.storage.insert(slot2, U256::from(1));
909 state.storages.insert(addr1, storage);
910
911 state
912 }
913
914 #[test]
915 fn test_multi_proof_targets_difference_empty_state() {
916 let state = HashedPostState::default();
917 let excluded = MultiProofTargets::default();
918
919 let targets = state.multi_proof_targets_difference(&excluded);
920 assert!(targets.is_empty());
921 }
922
923 #[test]
924 fn test_multi_proof_targets_difference_new_account_targets() {
925 let state = create_state_for_multi_proof_targets();
926 let excluded = MultiProofTargets::default();
927
928 let targets = state.multi_proof_targets_difference(&excluded);
930 assert_eq!(targets.len(), state.accounts.len());
931 for addr in state.accounts.keys() {
932 assert!(targets.contains_key(addr));
933 }
934 }
935
936 #[test]
937 fn test_multi_proof_targets_difference_new_storage_targets() {
938 let state = create_state_for_multi_proof_targets();
939 let excluded = MultiProofTargets::default();
940
941 let targets = state.multi_proof_targets_difference(&excluded);
942
943 for (addr, storage) in &state.storages {
945 assert!(targets.contains_key(addr));
946 let target_slots = &targets[addr];
947 assert_eq!(target_slots.len(), storage.storage.len());
948 for slot in storage.storage.keys() {
949 assert!(target_slots.contains(slot));
950 }
951 }
952 }
953
954 #[test]
955 fn test_multi_proof_targets_difference_filter_excluded_accounts() {
956 let state = create_state_for_multi_proof_targets();
957 let mut excluded = MultiProofTargets::default();
958
959 let excluded_addr = state
961 .accounts
962 .keys()
963 .find(|&&addr| !state.storages.contains_key(&addr))
964 .expect("Should have an account without storage");
965
966 excluded.insert(*excluded_addr, HashSet::default());
968
969 let targets = state.multi_proof_targets_difference(&excluded);
970
971 assert!(!targets.contains_key(excluded_addr));
973 assert_eq!(targets.len(), state.accounts.len() - 1);
975 }
976
977 #[test]
978 fn test_multi_proof_targets_difference_filter_excluded_storage() {
979 let state = create_state_for_multi_proof_targets();
980 let mut excluded = MultiProofTargets::default();
981
982 let (addr, storage) = state.storages.iter().next().unwrap();
984 let mut excluded_slots = HashSet::default();
985 let excluded_slot = *storage.storage.keys().next().unwrap();
986 excluded_slots.insert(excluded_slot);
987 excluded.insert(*addr, excluded_slots);
988
989 let targets = state.multi_proof_targets_difference(&excluded);
990
991 let target_slots = &targets[addr];
993 assert!(!target_slots.contains(&excluded_slot));
994 assert_eq!(target_slots.len(), storage.storage.len() - 1);
995 }
996
997 #[test]
998 fn test_multi_proof_targets_difference_mixed_excluded_state() {
999 let mut state = HashedPostState::default();
1000 let mut excluded = MultiProofTargets::default();
1001
1002 let addr1 = B256::random();
1003 let addr2 = B256::random();
1004 let slot1 = B256::random();
1005 let slot2 = B256::random();
1006
1007 state.accounts.insert(addr1, Some(Default::default()));
1008 state.accounts.insert(addr2, Some(Default::default()));
1009
1010 let mut storage = HashedStorage::default();
1011 storage.storage.insert(slot1, U256::ZERO);
1012 storage.storage.insert(slot2, U256::from(1));
1013 state.storages.insert(addr1, storage);
1014
1015 let mut excluded_slots = HashSet::default();
1016 excluded_slots.insert(slot1);
1017 excluded.insert(addr1, excluded_slots);
1018
1019 let targets = state.multi_proof_targets_difference(&excluded);
1020
1021 assert!(targets.contains_key(&addr2));
1022 assert!(!targets[&addr1].contains(&slot1));
1023 assert!(targets[&addr1].contains(&slot2));
1024 }
1025
1026 #[test]
1027 fn test_multi_proof_targets_difference_unmodified_account_with_storage() {
1028 let mut state = HashedPostState::default();
1029 let excluded = MultiProofTargets::default();
1030
1031 let addr = B256::random();
1032 let slot1 = B256::random();
1033 let slot2 = B256::random();
1034
1035 let mut storage = HashedStorage::default();
1038 storage.storage.insert(slot1, U256::from(1));
1039 storage.storage.insert(slot2, U256::from(2));
1040 state.storages.insert(addr, storage);
1041
1042 assert!(!state.accounts.contains_key(&addr));
1043 assert!(!excluded.contains_key(&addr));
1044
1045 let targets = state.multi_proof_targets_difference(&excluded);
1046
1047 assert!(targets.contains_key(&addr));
1049
1050 let target_slots = &targets[&addr];
1051 assert_eq!(target_slots.len(), 2);
1052 assert!(target_slots.contains(&slot1));
1053 assert!(target_slots.contains(&slot2));
1054 }
1055
1056 #[test]
1057 fn test_partition_by_targets() {
1058 let addr1 = B256::random();
1059 let addr2 = B256::random();
1060 let slot1 = B256::random();
1061 let slot2 = B256::random();
1062
1063 let state = HashedPostState {
1064 accounts: B256Map::from_iter([
1065 (addr1, Some(Default::default())),
1066 (addr2, Some(Default::default())),
1067 ]),
1068 storages: B256Map::from_iter([(
1069 addr1,
1070 HashedStorage {
1071 wiped: true,
1072 storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1073 },
1074 )]),
1075 };
1076 let targets = MultiProofTargets::from_iter([(addr1, HashSet::from_iter([slot1]))]);
1077
1078 let (with_targets, without_targets) =
1079 state.partition_by_targets(&targets, &MultiAddedRemovedKeys::new());
1080
1081 assert_eq!(
1082 with_targets,
1083 HashedPostState {
1084 accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
1085 storages: B256Map::from_iter([(
1086 addr1,
1087 HashedStorage {
1088 wiped: true,
1089 storage: B256Map::from_iter([(slot1, U256::ZERO)])
1090 }
1091 )]),
1092 }
1093 );
1094 assert_eq!(
1095 without_targets,
1096 HashedPostState {
1097 accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
1098 storages: B256Map::from_iter([(
1099 addr1,
1100 HashedStorage {
1101 wiped: false,
1102 storage: B256Map::from_iter([(slot2, U256::from(1))])
1103 }
1104 )]),
1105 }
1106 );
1107 }
1108
1109 #[test]
1110 fn test_chunks() {
1111 let addr1 = B256::from([1; 32]);
1112 let addr2 = B256::from([2; 32]);
1113 let slot1 = B256::from([1; 32]);
1114 let slot2 = B256::from([2; 32]);
1115
1116 let state = HashedPostState {
1117 accounts: B256Map::from_iter([
1118 (addr1, Some(Default::default())),
1119 (addr2, Some(Default::default())),
1120 ]),
1121 storages: B256Map::from_iter([(
1122 addr2,
1123 HashedStorage {
1124 wiped: true,
1125 storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1126 },
1127 )]),
1128 };
1129
1130 let mut chunks = state.chunks(2);
1131 assert_eq!(
1132 chunks.next(),
1133 Some(HashedPostState {
1134 accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
1135 storages: B256Map::from_iter([(addr2, HashedStorage::new(true)),])
1136 })
1137 );
1138 assert_eq!(
1139 chunks.next(),
1140 Some(HashedPostState {
1141 accounts: B256Map::default(),
1142 storages: B256Map::from_iter([(
1143 addr2,
1144 HashedStorage {
1145 wiped: false,
1146 storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1147 },
1148 )])
1149 })
1150 );
1151 assert_eq!(
1152 chunks.next(),
1153 Some(HashedPostState {
1154 accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
1155 storages: B256Map::default()
1156 })
1157 );
1158 assert_eq!(chunks.next(), None);
1159 }
1160
1161 #[test]
1162 fn test_hashed_post_state_sorted_extend_ref() {
1163 let mut state1 = HashedPostStateSorted {
1165 accounts: HashedAccountsSorted {
1166 accounts: vec![
1167 (B256::from([1; 32]), Account::default()),
1168 (B256::from([3; 32]), Account::default()),
1169 ],
1170 destroyed_accounts: B256Set::from_iter([B256::from([5; 32])]),
1171 },
1172 storages: B256Map::default(),
1173 };
1174
1175 let state2 = HashedPostStateSorted {
1176 accounts: HashedAccountsSorted {
1177 accounts: vec![
1178 (B256::from([2; 32]), Account::default()),
1179 (B256::from([3; 32]), Account { nonce: 1, ..Default::default() }), (B256::from([4; 32]), Account::default()),
1181 ],
1182 destroyed_accounts: B256Set::from_iter([B256::from([6; 32])]),
1183 },
1184 storages: B256Map::default(),
1185 };
1186
1187 state1.extend_ref(&state2);
1188
1189 assert_eq!(state1.accounts.accounts.len(), 4);
1191 assert_eq!(state1.accounts.accounts[0].0, B256::from([1; 32]));
1192 assert_eq!(state1.accounts.accounts[1].0, B256::from([2; 32]));
1193 assert_eq!(state1.accounts.accounts[2].0, B256::from([3; 32]));
1194 assert_eq!(state1.accounts.accounts[2].1.nonce, 1); assert_eq!(state1.accounts.accounts[3].0, B256::from([4; 32]));
1196
1197 assert!(state1.accounts.destroyed_accounts.contains(&B256::from([5; 32])));
1199 assert!(state1.accounts.destroyed_accounts.contains(&B256::from([6; 32])));
1200 }
1201
1202 #[test]
1203 fn test_hashed_storage_sorted_extend_ref() {
1204 let mut storage1 = HashedStorageSorted {
1206 non_zero_valued_slots: vec![
1207 (B256::from([1; 32]), U256::from(10)),
1208 (B256::from([3; 32]), U256::from(30)),
1209 ],
1210 zero_valued_slots: B256Set::from_iter([B256::from([5; 32])]),
1211 wiped: false,
1212 };
1213
1214 let storage2 = HashedStorageSorted {
1215 non_zero_valued_slots: vec![
1216 (B256::from([2; 32]), U256::from(20)),
1217 (B256::from([3; 32]), U256::from(300)), (B256::from([4; 32]), U256::from(40)),
1219 ],
1220 zero_valued_slots: B256Set::from_iter([B256::from([6; 32])]),
1221 wiped: false,
1222 };
1223
1224 storage1.extend_ref(&storage2);
1225
1226 assert_eq!(storage1.non_zero_valued_slots.len(), 4);
1227 assert_eq!(storage1.non_zero_valued_slots[0].0, B256::from([1; 32]));
1228 assert_eq!(storage1.non_zero_valued_slots[1].0, B256::from([2; 32]));
1229 assert_eq!(storage1.non_zero_valued_slots[2].0, B256::from([3; 32]));
1230 assert_eq!(storage1.non_zero_valued_slots[2].1, U256::from(300)); assert_eq!(storage1.non_zero_valued_slots[3].0, B256::from([4; 32]));
1232 assert!(storage1.zero_valued_slots.contains(&B256::from([5; 32])));
1233 assert!(storage1.zero_valued_slots.contains(&B256::from([6; 32])));
1234 assert!(!storage1.wiped);
1235
1236 let mut storage3 = HashedStorageSorted {
1238 non_zero_valued_slots: vec![(B256::from([1; 32]), U256::from(10))],
1239 zero_valued_slots: B256Set::from_iter([B256::from([2; 32])]),
1240 wiped: false,
1241 };
1242
1243 let storage4 = HashedStorageSorted {
1244 non_zero_valued_slots: vec![(B256::from([3; 32]), U256::from(30))],
1245 zero_valued_slots: B256Set::from_iter([B256::from([4; 32])]),
1246 wiped: true,
1247 };
1248
1249 storage3.extend_ref(&storage4);
1250
1251 assert!(storage3.wiped);
1252 assert_eq!(storage3.non_zero_valued_slots.len(), 1);
1254 assert_eq!(storage3.non_zero_valued_slots[0].0, B256::from([3; 32]));
1255 assert_eq!(storage3.zero_valued_slots.len(), 1);
1256 assert!(storage3.zero_valued_slots.contains(&B256::from([4; 32])));
1257 }
1258
1259 #[test]
1260 fn test_hashed_post_state_chunking_length() {
1261 let addr1 = B256::from([1; 32]);
1262 let addr2 = B256::from([2; 32]);
1263 let addr3 = B256::from([3; 32]);
1264 let addr4 = B256::from([4; 32]);
1265 let slot1 = B256::from([1; 32]);
1266 let slot2 = B256::from([2; 32]);
1267 let slot3 = B256::from([3; 32]);
1268
1269 let state = HashedPostState {
1270 accounts: B256Map::from_iter([(addr1, None), (addr2, None), (addr4, None)]),
1271 storages: B256Map::from_iter([
1272 (
1273 addr1,
1274 HashedStorage {
1275 wiped: false,
1276 storage: B256Map::from_iter([
1277 (slot1, U256::ZERO),
1278 (slot2, U256::ZERO),
1279 (slot3, U256::ZERO),
1280 ]),
1281 },
1282 ),
1283 (
1284 addr2,
1285 HashedStorage {
1286 wiped: true,
1287 storage: B256Map::from_iter([
1288 (slot1, U256::ZERO),
1289 (slot2, U256::ZERO),
1290 (slot3, U256::ZERO),
1291 ]),
1292 },
1293 ),
1294 (
1295 addr3,
1296 HashedStorage {
1297 wiped: false,
1298 storage: B256Map::from_iter([
1299 (slot1, U256::ZERO),
1300 (slot2, U256::ZERO),
1301 (slot3, U256::ZERO),
1302 ]),
1303 },
1304 ),
1305 ]),
1306 };
1307
1308 let chunking_length = state.chunking_length();
1309 for size in 1..=state.clone().chunks(1).count() {
1310 let chunk_count = state.clone().chunks(size).count();
1311 let expected_count = chunking_length.div_ceil(size);
1312 assert_eq!(
1313 chunk_count, expected_count,
1314 "chunking_length: {}, size: {}",
1315 chunking_length, size
1316 );
1317 }
1318 }
1319}