reth_trie_common/
hashed_state.rs

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/// Representation of in-memory hashed state.
26#[derive(PartialEq, Eq, Clone, Default, Debug)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct HashedPostState {
29    /// Mapping of hashed address to account info, `None` if destroyed.
30    pub accounts: B256Map<Option<Account>>,
31    /// Mapping of hashed address to hashed storage.
32    pub storages: B256Map<HashedStorage>,
33}
34
35impl HashedPostState {
36    /// Create new instance of [`HashedPostState`].
37    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    /// Initialize [`HashedPostState`] from bundle state.
45    /// Hashes all changed accounts and storage entries that are currently stored in the bundle
46    /// state.
47    #[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    /// Initialize [`HashedPostState`] from bundle state.
77    /// Hashes all changed accounts and storage entries that are currently stored in the bundle
78    /// state.
79    #[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    /// Construct [`HashedPostState`] from a single [`HashedStorage`].
108    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    /// Set account entries on hashed state.
116    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    /// Set storage entries on hashed state.
125    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    /// Returns `true` if the hashed state is empty.
134    pub fn is_empty(&self) -> bool {
135        self.accounts.is_empty() && self.storages.is_empty()
136    }
137
138    /// Construct [`TriePrefixSetsMut`] from hashed post state.
139    /// The prefix sets contain the hashed account and storage keys that have been changed in the
140    /// post state.
141    pub fn construct_prefix_sets(&self) -> TriePrefixSetsMut {
142        // Populate account prefix set.
143        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        // Populate storage prefix sets.
154        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    /// Create multiproof targets for this state.
165    pub fn multi_proof_targets(&self) -> MultiProofTargets {
166        // Pre-allocate minimum capacity for the targets.
167        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    /// Create multiproof targets difference for this state,
178    /// i.e., the targets that are in targets create from `self` but not in `excluded`.
179    ///
180    /// This method is preferred to first calling `Self::multi_proof_targets` and the calling
181    /// `MultiProofTargets::retain_difference`, because it does not over allocate the targets map.
182    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    /// Partition the state update into two state updates:
207    /// - First with accounts and storages slots that are present in the provided targets.
208    /// - Second with all other.
209    ///
210    /// CAUTION: The state updates are expected to be applied in order, so that the storage wipes
211    /// are done correctly.
212    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                    // We do not check the wiped flag here, because targets only contain addresses
237                    // and storage slots. So if there are no storage slots left, the storage update
238                    // can be fully removed.
239                    let retain = !storage.storage.is_empty();
240
241                    // Since state updates are expected to be applied in order, we can only set the
242                    // wiped flag in the second storage update if the first storage update is empty
243                    // and will not be retained.
244                    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    /// Returns an iterator that yields chunks of the specified size.
275    ///
276    /// See [`ChunkedHashedPostState`] for more information.
277    pub fn chunks(self, size: usize) -> ChunkedHashedPostState {
278        ChunkedHashedPostState::new(self, size)
279    }
280
281    /// Returns the number of items that will be considered during chunking in `[Self::chunks]`.
282    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    /// Extend this hashed post state with contents of another.
291    /// Entries in the second hashed post state take precedence.
292    pub fn extend(&mut self, other: Self) {
293        self.extend_inner(Cow::Owned(other));
294    }
295
296    /// Extend this hashed post state with contents of another.
297    /// Entries in the second hashed post state take precedence.
298    ///
299    /// Slightly less efficient than [`Self::extend`], but preferred to `extend(other.clone())`.
300    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    /// Converts hashed post state into [`HashedPostStateSorted`].
335    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    /// Converts hashed post state into [`HashedPostStateSorted`], but keeping the maps allocated by
358    /// draining.
359    ///
360    /// This effectively clears all the fields in the [`HashedPostStateSorted`].
361    ///
362    /// This allows us to reuse the allocated space. This allocates new space for the sorted hashed
363    /// post state, like `into_sorted`.
364    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    /// Clears the account and storage maps of this `HashedPostState`.
387    pub fn clear(&mut self) {
388        self.accounts.clear();
389        self.storages.clear();
390    }
391}
392
393/// Representation of in-memory hashed storage.
394#[derive(PartialEq, Eq, Clone, Debug, Default)]
395#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
396pub struct HashedStorage {
397    /// Flag indicating whether the storage was wiped or not.
398    pub wiped: bool,
399    /// Mapping of hashed storage slot to storage value.
400    pub storage: B256Map<U256>,
401}
402
403impl HashedStorage {
404    /// Create new instance of [`HashedStorage`].
405    pub fn new(wiped: bool) -> Self {
406        Self { wiped, storage: HashMap::default() }
407    }
408
409    /// Check if self is empty.
410    pub fn is_empty(&self) -> bool {
411        !self.wiped && self.storage.is_empty()
412    }
413
414    /// Create new hashed storage from iterator.
415    pub fn from_iter(wiped: bool, iter: impl IntoIterator<Item = (B256, U256)>) -> Self {
416        Self { wiped, storage: HashMap::from_iter(iter) }
417    }
418
419    /// Create new hashed storage from account status and plain storage.
420    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    /// Construct [`PrefixSetMut`] from hashed storage.
431    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    /// Extend hashed storage with contents of other.
444    /// The entries in second hashed storage take precedence.
445    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    /// Converts hashed storage into [`HashedStorageSorted`].
454    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/// Sorted hashed post state optimized for iterating during state trie calculation.
471#[derive(PartialEq, Eq, Clone, Default, Debug)]
472pub struct HashedPostStateSorted {
473    /// Updated state of accounts.
474    pub accounts: HashedAccountsSorted,
475    /// Map of hashed addresses to hashed storage.
476    pub storages: B256Map<HashedStorageSorted>,
477}
478
479impl HashedPostStateSorted {
480    /// Create new instance of [`HashedPostStateSorted`]
481    pub const fn new(
482        accounts: HashedAccountsSorted,
483        storages: B256Map<HashedStorageSorted>,
484    ) -> Self {
485        Self { accounts, storages }
486    }
487
488    /// Returns reference to hashed accounts.
489    pub const fn accounts(&self) -> &HashedAccountsSorted {
490        &self.accounts
491    }
492
493    /// Returns reference to hashed account storages.
494    pub const fn account_storages(&self) -> &B256Map<HashedStorageSorted> {
495        &self.storages
496    }
497
498    /// Returns `true` if there are no account or storage updates.
499    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    /// Returns the total number of updates including all accounts and storage updates.
506    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    /// Extends this state with contents of another sorted state.
513    /// Entries in `other` take precedence for duplicate keys.
514    pub fn extend_ref(&mut self, other: &Self) {
515        // Extend accounts
516        self.accounts.extend_ref(&other.accounts);
517
518        // Extend storages
519        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/// Sorted account state optimized for iterating during state trie calculation.
535#[derive(Clone, Eq, PartialEq, Default, Debug)]
536pub struct HashedAccountsSorted {
537    /// Sorted collection of hashed addresses and their account info.
538    pub accounts: Vec<(B256, Account)>,
539    /// Set of destroyed account keys.
540    pub destroyed_accounts: B256Set,
541}
542
543impl HashedAccountsSorted {
544    /// Returns a sorted iterator over updated accounts.
545    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    /// Extends this collection with contents of another sorted collection.
554    /// Entries in `other` take precedence for duplicate keys.
555    pub fn extend_ref(&mut self, other: &Self) {
556        // Updates take precedence over removals, so we want removals from `other` to only apply to
557        // the previous accounts.
558        self.accounts.retain(|(addr, _)| !other.destroyed_accounts.contains(addr));
559
560        // Extend the sorted accounts vector
561        extend_sorted_vec(&mut self.accounts, &other.accounts);
562
563        // Merge destroyed accounts sets
564        self.destroyed_accounts.extend(&other.destroyed_accounts);
565    }
566}
567
568/// Sorted hashed storage optimized for iterating during state trie calculation.
569#[derive(Clone, Eq, PartialEq, Debug)]
570pub struct HashedStorageSorted {
571    /// Sorted hashed storage slots with non-zero value.
572    pub non_zero_valued_slots: Vec<(B256, U256)>,
573    /// Slots that have been zero valued.
574    pub zero_valued_slots: B256Set,
575    /// Flag indicating whether the storage was wiped or not.
576    pub wiped: bool,
577}
578
579impl HashedStorageSorted {
580    /// Returns `true` if the account was wiped.
581    pub const fn is_wiped(&self) -> bool {
582        self.wiped
583    }
584
585    /// Returns a sorted iterator over updated storage slots.
586    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    /// Returns the total number of storage slot updates.
595    pub fn len(&self) -> usize {
596        self.non_zero_valued_slots.len() + self.zero_valued_slots.len()
597    }
598
599    /// Returns `true` if there are no storage slot updates.
600    pub fn is_empty(&self) -> bool {
601        self.non_zero_valued_slots.is_empty() && self.zero_valued_slots.is_empty()
602    }
603
604    /// Extends this storage with contents of another sorted storage.
605    /// Entries in `other` take precedence for duplicate keys.
606    pub fn extend_ref(&mut self, other: &Self) {
607        if other.wiped {
608            // If other is wiped, clear everything and copy from other
609            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 the sorted non-zero valued slots
620        extend_sorted_vec(&mut self.non_zero_valued_slots, &other.non_zero_valued_slots);
621
622        // Merge zero valued slots sets
623        self.zero_valued_slots.extend(&other.zero_valued_slots);
624    }
625}
626
627/// An iterator that yields chunks of the state updates of at most `size` account and storage
628/// targets.
629///
630/// # Notes
631/// 1. Chunks are expected to be applied in order, because of storage wipes. If applied out of
632///    order, it's possible to wipe more storage than in the original state update.
633/// 2. For each account, chunks with storage updates come first, followed by account updates.
634#[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                // Storage wipes should go first
654                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            // We need stable sort here to preserve the order for each address:
672            // 1. Storage wipes
673            // 2. Storage updates
674            // 3. Account update
675            .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        // Initialize post state storage
729        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        // Update single slot value
739        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        // Wipe account storage
758        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        // Reinitialize single slot value
767        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        // Reinitialize single slot value
780        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        // Prepare a random Ethereum address as a key for the account.
799        let address = Address::random();
800
801        // Create a mock account info object.
802        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        // Create a `BundleAccount` struct to represent the account and its storage.
816        let account = BundleAccount {
817            status: AccountStatus::Changed,
818            info: Some(account_info.clone()),
819            storage,
820            original_info: None,
821        };
822
823        // Create a vector of tuples representing the bundle state.
824        let state = vec![(&address, &account)];
825
826        // Convert the bundle state into a hashed post state.
827        let hashed_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(state);
828
829        // Validate the hashed post state.
830        assert_eq!(hashed_state.accounts.len(), 1);
831        assert_eq!(hashed_state.storages.len(), 1);
832
833        // Validate the account info.
834        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        // Prepare random addresses and mock account info.
843        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        // Create hashed accounts with addresses.
854        let account_1 = (keccak256(address_1), Some(account_info_1.into()));
855        let account_2 = (keccak256(address_2), None);
856
857        // Add accounts to the hashed post state.
858        let hashed_state = HashedPostState::default().with_accounts(vec![account_1, account_2]);
859
860        // Validate the hashed post state.
861        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        // Prepare random addresses and mock storage entries.
869        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        // Add storages to the hashed post state.
876        let hashed_state = HashedPostState::default().with_storages(vec![storage_1, storage_2]);
877
878        // Validate the hashed post state.
879        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        // Create an empty hashed post state and validate it's empty.
887        let empty_state = HashedPostState::default();
888        assert!(empty_state.is_empty());
889
890        // Add an account and validate the state is no longer empty.
891        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        // should return all accounts as targets since excluded is empty
929        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        // verify storage slots are included for accounts with storage
944        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        // select an account that has no storage updates
960        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        // mark the account as excluded
967        excluded.insert(*excluded_addr, HashSet::default());
968
969        let targets = state.multi_proof_targets_difference(&excluded);
970
971        // should not include the already excluded account since it has no storage updates
972        assert!(!targets.contains_key(excluded_addr));
973        // other accounts should still be included
974        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        // mark one storage slot as excluded
983        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        // should not include the excluded storage slot
992        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        // don't add the account to state.accounts (simulating unmodified account)
1036        // but add storage updates for this account
1037        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        // verify that we still get the storage slots for the unmodified account
1048        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        // Test extending accounts
1164        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() }), // Override
1180                    (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        // Check accounts are merged and sorted
1190        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); // Should have state2's value
1195        assert_eq!(state1.accounts.accounts[3].0, B256::from([4; 32]));
1196
1197        // Check destroyed accounts are merged
1198        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        // Test normal extension
1205        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)), // Override
1218                (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)); // Should have storage2's value
1231        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        // Test wiped storage
1237        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        // When wiped, should only have storage4's values
1253        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}