Skip to main content

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, kway_merge_disjoint_sorted, kway_merge_sorted},
7    KeyHasher, MultiProofTargets, Nibbles,
8};
9use alloc::{borrow::Cow, vec::Vec};
10use alloy_primitives::{
11    keccak256,
12    map::{hash_map, B256Map, 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::{FromParallelIterator, IntoParallelIterator, ParallelIterator};
22
23use revm::database::BundleAccount;
24
25/// In-memory hashed state that stores account and storage changes with keccak256-hashed keys in
26/// hash maps.
27#[derive(PartialEq, Eq, Clone, Default, Debug)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct HashedPostState {
30    /// Mapping of hashed address to account info, `None` if destroyed.
31    pub accounts: B256Map<Option<Account>>,
32    /// Mapping of hashed address to hashed storage.
33    pub storages: B256Map<HashedStorage>,
34}
35
36impl HashedPostState {
37    /// Create new instance of [`HashedPostState`].
38    pub fn with_capacity(capacity: usize) -> Self {
39        Self {
40            accounts: B256Map::with_capacity_and_hasher(capacity, Default::default()),
41            storages: B256Map::with_capacity_and_hasher(capacity, Default::default()),
42        }
43    }
44
45    /// Initialize [`HashedPostState`] from bundle state.
46    /// Hashes all changed accounts and storage entries that are currently stored in the bundle
47    /// state.
48    #[inline]
49    pub fn from_bundle_state<'a, KH: KeyHasher>(
50        state: impl IntoIterator<Item = (&'a Address, &'a BundleAccount)>,
51    ) -> Self {
52        state
53            .into_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_iter(
58                    account
59                        .storage
60                        .iter()
61                        .map(|(slot, value)| (keccak256(B256::from(*slot)), value.present_value)),
62                );
63
64                (
65                    hashed_address,
66                    hashed_account,
67                    (!hashed_storage.is_empty()).then_some(hashed_storage),
68                )
69            })
70            .collect()
71    }
72
73    /// Construct [`HashedPostState`] from a single [`HashedStorage`].
74    pub fn from_hashed_storage(hashed_address: B256, storage: HashedStorage) -> Self {
75        Self {
76            accounts: HashMap::default(),
77            storages: HashMap::from_iter([(hashed_address, storage)]),
78        }
79    }
80
81    /// Set account entries on hashed state.
82    pub fn with_accounts(
83        mut self,
84        accounts: impl IntoIterator<Item = (B256, Option<Account>)>,
85    ) -> Self {
86        self.accounts = HashMap::from_iter(accounts);
87        self
88    }
89
90    /// Set storage entries on hashed state.
91    pub fn with_storages(
92        mut self,
93        storages: impl IntoIterator<Item = (B256, HashedStorage)>,
94    ) -> Self {
95        self.storages = HashMap::from_iter(storages);
96        self
97    }
98
99    /// Returns `true` if the hashed state is empty.
100    pub fn is_empty(&self) -> bool {
101        self.accounts.is_empty() && self.storages.is_empty()
102    }
103
104    /// Construct [`TriePrefixSetsMut`] from hashed post state.
105    /// The prefix sets contain the hashed account and storage keys that have been changed in the
106    /// post state.
107    pub fn construct_prefix_sets(&self) -> TriePrefixSetsMut {
108        // Populate account prefix set.
109        let mut account_prefix_set = PrefixSetMut::with_capacity(self.accounts.len());
110        let mut destroyed_accounts = HashSet::default();
111        for (hashed_address, account) in &self.accounts {
112            account_prefix_set.insert(Nibbles::unpack(hashed_address));
113
114            if account.is_none() {
115                destroyed_accounts.insert(*hashed_address);
116            }
117        }
118
119        // Populate storage prefix sets.
120        let mut storage_prefix_sets =
121            HashMap::with_capacity_and_hasher(self.storages.len(), Default::default());
122        for (hashed_address, hashed_storage) in &self.storages {
123            account_prefix_set.insert(Nibbles::unpack(hashed_address));
124            storage_prefix_sets.insert(*hashed_address, hashed_storage.construct_prefix_set());
125        }
126
127        TriePrefixSetsMut { account_prefix_set, storage_prefix_sets, destroyed_accounts }
128    }
129
130    /// Create multiproof targets for this state.
131    pub fn multi_proof_targets(&self) -> MultiProofTargets {
132        // Pre-allocate minimum capacity for the targets.
133        let mut targets = MultiProofTargets::with_capacity(self.accounts.len());
134        for hashed_address in self.accounts.keys() {
135            targets.insert(*hashed_address, Default::default());
136        }
137        for (hashed_address, storage) in &self.storages {
138            targets.entry(*hashed_address).or_default().extend(storage.storage.keys().copied());
139        }
140        targets
141    }
142
143    /// Create multiproof targets difference for this state,
144    /// i.e., the targets that are in targets create from `self` but not in `excluded`.
145    ///
146    /// This method is preferred to first calling `Self::multi_proof_targets` and the calling
147    /// `MultiProofTargets::retain_difference`, because it does not over allocate the targets map.
148    pub fn multi_proof_targets_difference(
149        &self,
150        excluded: &MultiProofTargets,
151    ) -> MultiProofTargets {
152        let mut targets = MultiProofTargets::default();
153        for hashed_address in self.accounts.keys() {
154            if !excluded.contains_key(hashed_address) {
155                targets.insert(*hashed_address, Default::default());
156            }
157        }
158        for (hashed_address, storage) in &self.storages {
159            let maybe_excluded_storage = excluded.get(hashed_address);
160            let mut hashed_slots_targets = storage
161                .storage
162                .keys()
163                .filter(|slot| !maybe_excluded_storage.is_some_and(|f| f.contains(*slot)))
164                .peekable();
165            if hashed_slots_targets.peek().is_some() {
166                targets.entry(*hashed_address).or_default().extend(hashed_slots_targets);
167            }
168        }
169        targets
170    }
171
172    /// Partition the state update into two state updates:
173    /// - First with accounts and storages slots that are present in the provided targets.
174    /// - Second with all other.
175    ///
176    /// CAUTION: The state updates are expected to be applied in order, so that the storage wipes
177    /// are done correctly.
178    pub fn partition_by_targets(
179        mut self,
180        targets: &MultiProofTargets,
181        added_removed_keys: &MultiAddedRemovedKeys,
182    ) -> (Self, Self) {
183        let mut state_updates_not_in_targets = Self::default();
184
185        self.storages.retain(|&address, storage| {
186            let storage_added_removed_keys = added_removed_keys.get_storage(&address);
187
188            let (retain, storage_not_in_targets) = match targets.get(&address) {
189                Some(storage_in_targets) => {
190                    let mut storage_not_in_targets = HashedStorage::default();
191                    storage.storage.retain(|&slot, value| {
192                        if storage_in_targets.contains(&slot) &&
193                            !storage_added_removed_keys.is_some_and(|k| k.is_removed(&slot))
194                        {
195                            return true
196                        }
197
198                        storage_not_in_targets.storage.insert(slot, *value);
199                        false
200                    });
201
202                    // We do not check the wiped flag here, because targets only contain addresses
203                    // and storage slots. So if there are no storage slots left, the storage update
204                    // can be fully removed.
205                    let retain = !storage.storage.is_empty();
206
207                    // Since state updates are expected to be applied in order, we can only set the
208                    // wiped flag in the second storage update if the first storage update is empty
209                    // and will not be retained.
210                    if !retain {
211                        storage_not_in_targets.wiped = storage.wiped;
212                    }
213
214                    (
215                        retain,
216                        storage_not_in_targets.is_empty().not().then_some(storage_not_in_targets),
217                    )
218                }
219                None => (false, Some(core::mem::take(storage))),
220            };
221
222            if let Some(storage_not_in_targets) = storage_not_in_targets {
223                state_updates_not_in_targets.storages.insert(address, storage_not_in_targets);
224            }
225
226            retain
227        });
228        self.accounts.retain(|&address, account| {
229            if targets.contains_key(&address) {
230                return true
231            }
232
233            state_updates_not_in_targets.accounts.insert(address, *account);
234            false
235        });
236
237        (self, state_updates_not_in_targets)
238    }
239
240    /// Returns an iterator that yields chunks of the specified size.
241    ///
242    /// See [`ChunkedHashedPostState`] for more information.
243    pub fn chunks(self, size: usize) -> ChunkedHashedPostState {
244        ChunkedHashedPostState::new(self, size)
245    }
246
247    /// Returns the number of items that will be considered during chunking in `[Self::chunks]`.
248    pub fn chunking_length(&self) -> usize {
249        self.accounts.len() +
250            self.storages
251                .values()
252                .map(|storage| if storage.wiped { 1 } else { 0 } + storage.storage.len())
253                .sum::<usize>()
254    }
255
256    /// Extend this hashed post state with contents of another.
257    /// Entries in the second hashed post state take precedence.
258    pub fn extend(&mut self, other: Self) {
259        self.extend_inner(Cow::Owned(other));
260    }
261
262    /// Extend this hashed post state with contents of another.
263    /// Entries in the second hashed post state take precedence.
264    ///
265    /// Slightly less efficient than [`Self::extend`], but preferred to `extend(other.clone())`.
266    pub fn extend_ref(&mut self, other: &Self) {
267        self.extend_inner(Cow::Borrowed(other));
268    }
269
270    fn extend_inner(&mut self, other: Cow<'_, Self>) {
271        self.accounts.extend(other.accounts.iter().map(|(&k, &v)| (k, v)));
272
273        self.storages.reserve(other.storages.len());
274        match other {
275            Cow::Borrowed(other) => {
276                self.extend_storages(other.storages.iter().map(|(k, v)| (*k, Cow::Borrowed(v))))
277            }
278            Cow::Owned(other) => {
279                self.extend_storages(other.storages.into_iter().map(|(k, v)| (k, Cow::Owned(v))))
280            }
281        }
282    }
283
284    fn extend_storages<'a>(
285        &mut self,
286        storages: impl IntoIterator<Item = (B256, Cow<'a, HashedStorage>)>,
287    ) {
288        for (hashed_address, storage) in storages {
289            match self.storages.entry(hashed_address) {
290                hash_map::Entry::Vacant(entry) => {
291                    entry.insert(storage.into_owned());
292                }
293                hash_map::Entry::Occupied(mut entry) => {
294                    entry.get_mut().extend(&storage);
295                }
296            }
297        }
298    }
299
300    /// Extend this hashed post state with sorted data, converting directly into the unsorted
301    /// `HashMap` representation. This is more efficient than first converting to `HashedPostState`
302    /// and then extending, as it avoids creating intermediate `HashMap` allocations.
303    pub fn extend_from_sorted(&mut self, sorted: &HashedPostStateSorted) {
304        // Reserve capacity for accounts
305        self.accounts.reserve(sorted.accounts.len());
306
307        // Insert accounts (Some = updated, None = destroyed)
308        for (address, account) in &sorted.accounts {
309            self.accounts.insert(*address, *account);
310        }
311
312        // Reserve capacity for storages
313        self.storages.reserve(sorted.storages.len());
314
315        // Extend storages
316        for (hashed_address, sorted_storage) in &sorted.storages {
317            match self.storages.entry(*hashed_address) {
318                hash_map::Entry::Vacant(entry) => {
319                    let mut new_storage = HashedStorage::new(false);
320                    new_storage.extend_from_sorted(sorted_storage);
321                    entry.insert(new_storage);
322                }
323                hash_map::Entry::Occupied(mut entry) => {
324                    entry.get_mut().extend_from_sorted(sorted_storage);
325                }
326            }
327        }
328    }
329
330    /// Converts hashed post state into [`HashedPostStateSorted`].
331    pub fn into_sorted(self) -> HashedPostStateSorted {
332        let mut accounts: Vec<_> = self.accounts.into_iter().collect();
333        accounts.sort_unstable_by_key(|(address, _)| *address);
334
335        let storages = self
336            .storages
337            .into_iter()
338            .map(|(hashed_address, storage)| (hashed_address, storage.into_sorted()))
339            .collect();
340
341        HashedPostStateSorted { accounts, storages }
342    }
343
344    /// Creates a sorted copy without consuming self.
345    /// More efficient than `.clone().into_sorted()` as it avoids cloning `HashMap` metadata.
346    pub fn clone_into_sorted(&self) -> HashedPostStateSorted {
347        let mut accounts: Vec<_> = self.accounts.iter().map(|(&k, &v)| (k, v)).collect();
348        accounts.sort_unstable_by_key(|(address, _)| *address);
349
350        let storages = self
351            .storages
352            .iter()
353            .map(|(&hashed_address, storage)| (hashed_address, storage.clone_into_sorted()))
354            .collect();
355
356        HashedPostStateSorted { accounts, storages }
357    }
358
359    /// Clears the account and storage maps of this `HashedPostState`.
360    pub fn clear(&mut self) {
361        self.accounts.clear();
362        self.storages.clear();
363    }
364}
365
366impl FromIterator<(B256, Option<Account>, Option<HashedStorage>)> for HashedPostState {
367    /// Constructs a [`HashedPostState`] from an iterator of tuples containing:
368    /// - Hashed address (B256)
369    /// - Optional account info (`None` indicates destroyed account)
370    /// - Optional hashed storage
371    ///
372    /// # Important
373    ///
374    /// - The iterator **assumes unique hashed addresses** (B256). If duplicate addresses are
375    ///   present, later entries will overwrite earlier ones for accounts, and storage will be
376    ///   merged.
377    /// - The [`HashedStorage`] **must not be empty** (as determined by
378    ///   [`HashedStorage::is_empty`]). Empty storage should be represented as `None` rather than
379    ///   `Some(empty_storage)`. This ensures the storage map only contains meaningful entries.
380    ///
381    /// Use `(!storage.is_empty()).then_some(storage)` to convert empty storage to `None`.
382    fn from_iter<T: IntoIterator<Item = (B256, Option<Account>, Option<HashedStorage>)>>(
383        iter: T,
384    ) -> Self {
385        let iter = iter.into_iter();
386        let (lower, _) = iter.size_hint();
387        let mut hashed_state = Self::with_capacity(lower);
388
389        for (hashed_address, info, hashed_storage) in iter {
390            hashed_state.accounts.insert(hashed_address, info);
391            if let Some(storage) = hashed_storage {
392                hashed_state.storages.insert(hashed_address, storage);
393            }
394        }
395
396        hashed_state
397    }
398}
399
400#[cfg(feature = "rayon")]
401impl FromParallelIterator<(B256, Option<Account>, Option<HashedStorage>)> for HashedPostState {
402    /// Parallel version of [`FromIterator`] for constructing [`HashedPostState`] from a parallel
403    /// iterator.
404    ///
405    /// See [`FromIterator::from_iter`] for details on the expected input format.
406    ///
407    /// # Important
408    ///
409    /// - The iterator **assumes unique hashed addresses** (B256). If duplicate addresses are
410    ///   present, later entries will overwrite earlier ones for accounts, and storage will be
411    ///   merged.
412    /// - The [`HashedStorage`] **must not be empty**. Empty storage should be `None`.
413    fn from_par_iter<I>(par_iter: I) -> Self
414    where
415        I: IntoParallelIterator<Item = (B256, Option<Account>, Option<HashedStorage>)>,
416    {
417        let vec: Vec<_> = par_iter.into_par_iter().collect();
418        vec.into_iter().collect()
419    }
420}
421
422/// Representation of in-memory hashed storage.
423#[derive(PartialEq, Eq, Clone, Debug, Default)]
424#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
425pub struct HashedStorage {
426    /// Flag indicating whether the storage was wiped or not.
427    pub wiped: bool,
428    /// Mapping of hashed storage slot to storage value.
429    pub storage: B256Map<U256>,
430}
431
432impl HashedStorage {
433    /// Create new instance of [`HashedStorage`].
434    pub fn new(wiped: bool) -> Self {
435        Self { wiped, storage: HashMap::default() }
436    }
437
438    /// Check if self is empty.
439    pub fn is_empty(&self) -> bool {
440        !self.wiped && self.storage.is_empty()
441    }
442
443    /// Create new hashed storage from iterator.
444    #[expect(clippy::should_implement_trait)]
445    pub fn from_iter(iter: impl IntoIterator<Item = (B256, U256)>) -> Self {
446        Self { wiped: false, storage: HashMap::from_iter(iter) }
447    }
448
449    /// Create new hashed storage from plain storage.
450    pub fn from_plain_storage<'a>(storage: impl IntoIterator<Item = (&'a U256, &'a U256)>) -> Self {
451        Self::from_iter(
452            storage.into_iter().map(|(key, value)| (keccak256(B256::from(*key)), *value)),
453        )
454    }
455
456    /// Construct [`PrefixSetMut`] from hashed storage.
457    pub fn construct_prefix_set(&self) -> PrefixSetMut {
458        if self.wiped {
459            PrefixSetMut::all()
460        } else {
461            let mut prefix_set = PrefixSetMut::with_capacity(self.storage.len());
462            for hashed_slot in self.storage.keys() {
463                prefix_set.insert(Nibbles::unpack(hashed_slot));
464            }
465            prefix_set
466        }
467    }
468
469    /// Extend hashed storage with contents of other.
470    /// The entries in second hashed storage take precedence.
471    pub fn extend(&mut self, other: &Self) {
472        if other.wiped {
473            self.wiped = true;
474            self.storage.clear();
475        }
476        self.storage.extend(other.storage.iter().map(|(&k, &v)| (k, v)));
477    }
478
479    /// Extend hashed storage with sorted data, converting directly into the unsorted `HashMap`
480    /// representation. This is more efficient than first converting to `HashedStorage` and
481    /// then extending, as it avoids creating intermediate `HashMap` allocations.
482    pub fn extend_from_sorted(&mut self, sorted: &HashedStorageSorted) {
483        if sorted.wiped {
484            self.wiped = true;
485            self.storage.clear();
486        }
487
488        // Reserve capacity for all slots
489        self.storage.reserve(sorted.storage_slots.len());
490
491        // Insert all storage slots
492        for (slot, value) in &sorted.storage_slots {
493            self.storage.insert(*slot, *value);
494        }
495    }
496
497    /// Converts hashed storage into [`HashedStorageSorted`].
498    pub fn into_sorted(self) -> HashedStorageSorted {
499        let mut storage_slots: Vec<_> = self.storage.into_iter().collect();
500        storage_slots.sort_unstable_by_key(|(key, _)| *key);
501
502        HashedStorageSorted { storage_slots, wiped: self.wiped }
503    }
504
505    /// Creates a sorted copy without consuming self.
506    /// More efficient than `.clone().into_sorted()` as it avoids cloning `HashMap` metadata.
507    pub fn clone_into_sorted(&self) -> HashedStorageSorted {
508        let mut storage_slots: Vec<_> = self.storage.iter().map(|(&k, &v)| (k, v)).collect();
509        storage_slots.sort_unstable_by_key(|(key, _)| *key);
510
511        HashedStorageSorted { storage_slots, wiped: self.wiped }
512    }
513}
514
515/// Sorted hashed post state optimized for iterating during state trie calculation.
516#[derive(PartialEq, Eq, Clone, Default, Debug)]
517#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
518pub struct HashedPostStateSorted {
519    /// Sorted collection of account updates. `None` indicates a destroyed account.
520    pub accounts: Vec<(B256, Option<Account>)>,
521    /// Map of hashed addresses to their sorted storage updates.
522    pub storages: B256Map<HashedStorageSorted>,
523}
524
525impl HashedPostStateSorted {
526    /// Create new instance of [`HashedPostStateSorted`]
527    pub const fn new(
528        accounts: Vec<(B256, Option<Account>)>,
529        storages: B256Map<HashedStorageSorted>,
530    ) -> Self {
531        Self { accounts, storages }
532    }
533
534    /// Returns reference to hashed accounts.
535    pub const fn accounts(&self) -> &Vec<(B256, Option<Account>)> {
536        &self.accounts
537    }
538
539    /// Returns reference to hashed account storages.
540    pub const fn account_storages(&self) -> &B256Map<HashedStorageSorted> {
541        &self.storages
542    }
543
544    /// Returns `true` if there are no account or storage updates.
545    pub fn is_empty(&self) -> bool {
546        self.accounts.is_empty() && self.storages.is_empty()
547    }
548
549    /// Returns the total number of updates including all accounts and storage updates.
550    pub fn total_len(&self) -> usize {
551        self.accounts.len() + self.storages.values().map(|s| s.len()).sum::<usize>()
552    }
553
554    /// Construct [`TriePrefixSetsMut`] from hashed post state.
555    ///
556    /// The prefix sets contain the hashed account and storage keys that have been changed in the
557    /// post state.
558    pub fn construct_prefix_sets(&self) -> TriePrefixSetsMut {
559        let mut account_prefix_set = PrefixSetMut::with_capacity(self.accounts.len());
560        let mut destroyed_accounts = HashSet::default();
561        for (hashed_address, account) in &self.accounts {
562            account_prefix_set.insert(Nibbles::unpack(hashed_address));
563            if account.is_none() {
564                destroyed_accounts.insert(*hashed_address);
565            }
566        }
567
568        let mut storage_prefix_sets =
569            B256Map::with_capacity_and_hasher(self.storages.len(), Default::default());
570        for (hashed_address, hashed_storage) in &self.storages {
571            // Ensure account trie covers storage overlays even if account map is empty.
572            account_prefix_set.insert(Nibbles::unpack(hashed_address));
573
574            let prefix_set = if hashed_storage.wiped {
575                PrefixSetMut::all()
576            } else {
577                let mut prefix_set =
578                    PrefixSetMut::with_capacity(hashed_storage.storage_slots.len());
579                prefix_set.extend_keys(
580                    hashed_storage
581                        .storage_slots
582                        .iter()
583                        .map(|(hashed_slot, _)| Nibbles::unpack(hashed_slot)),
584                );
585                prefix_set
586            };
587
588            storage_prefix_sets.insert(*hashed_address, prefix_set);
589        }
590
591        TriePrefixSetsMut { account_prefix_set, storage_prefix_sets, destroyed_accounts }
592    }
593
594    /// Extends this state with contents of another sorted state.
595    /// Entries in `other` take precedence for duplicate keys.
596    ///
597    /// Sorts the accounts after extending. Sorts the storage after extending, for each account.
598    pub fn extend_ref_and_sort(&mut self, other: &Self) {
599        // Extend accounts
600        extend_sorted_vec(&mut self.accounts, &other.accounts);
601
602        // Extend storages
603        for (hashed_address, other_storage) in &other.storages {
604            self.storages
605                .entry(*hashed_address)
606                .and_modify(|existing| existing.extend_ref(other_storage))
607                .or_insert_with(|| other_storage.clone());
608        }
609    }
610
611    /// Batch-merge sorted hashed post states. Iterator yields **newest to oldest**.
612    ///
613    /// For small batches, uses `extend_ref_and_sort` loop.
614    /// For large batches, uses k-way merge for O(n log k) complexity.
615    pub fn merge_batch<T: AsRef<Self> + From<Self>>(iter: impl IntoIterator<Item = T>) -> T {
616        let items: alloc::vec::Vec<_> = iter.into_iter().collect();
617        match items.len() {
618            0 => Self::default().into(),
619            1 => items.into_iter().next().expect("len == 1"),
620            _ => Self::merge_slice(&items).into(),
621        }
622    }
623
624    /// Batch-merge sorted hashed post states from a slice. Slice is **newest to oldest**.
625    ///
626    /// This variant takes a slice reference directly, avoiding iterator collection overhead.
627    /// For small batches, uses `extend_ref_and_sort` loop.
628    /// For large batches, uses k-way merge for O(n log k) complexity.
629    pub fn merge_slice<T: AsRef<Self>>(items: &[T]) -> Self {
630        const THRESHOLD: usize = 30;
631
632        let k = items.len();
633
634        if k == 0 {
635            return Self::default();
636        }
637        if k == 1 {
638            return items[0].as_ref().clone();
639        }
640
641        if k < THRESHOLD {
642            // Small k: extend loop, oldest-to-newest so newer overrides older.
643            let mut iter = items.iter().rev();
644            let mut acc = iter.next().expect("k > 0").as_ref().clone();
645            for next in iter {
646                acc.extend_ref_and_sort(next.as_ref());
647            }
648            return acc;
649        }
650
651        // Large k: k-way merge.
652        let accounts = kway_merge_sorted(items.iter().map(|i| i.as_ref().accounts.as_slice()));
653
654        struct StorageAcc<'a> {
655            wiped: bool,
656            sealed: bool,
657            slices: Vec<&'a [(B256, U256)]>,
658        }
659
660        let mut acc: B256Map<StorageAcc<'_>> = B256Map::default();
661
662        for item in items {
663            for (addr, storage) in &item.as_ref().storages {
664                let entry = acc.entry(*addr).or_insert_with(|| StorageAcc {
665                    wiped: false,
666                    sealed: false,
667                    slices: Vec::new(),
668                });
669
670                if entry.sealed {
671                    continue;
672                }
673
674                entry.slices.push(storage.storage_slots.as_slice());
675                if storage.wiped {
676                    entry.wiped = true;
677                    entry.sealed = true;
678                }
679            }
680        }
681
682        let storages = acc
683            .into_iter()
684            .map(|(addr, entry)| {
685                let storage_slots = kway_merge_sorted(entry.slices);
686                (addr, HashedStorageSorted { wiped: entry.wiped, storage_slots })
687            })
688            .collect();
689
690        Self { accounts, storages }
691    }
692
693    /// Merges the batch and removes overlapping keys whose mask values all differ from the merged
694    /// batch value.
695    ///
696    /// Account keys are masked at the top level, while storage entries are masked at the slot
697    /// level. For duplicate keys in the batch, later items take precedence over earlier ones. An
698    /// overlapping entry is retained if any mask value is equal to the merged batch value. The
699    /// order of the mask does not matter. An empty mask merges the batch without filtering.
700    ///
701    /// # Panics
702    ///
703    /// Panics if any batch or mask entry wipes an entire storage.
704    pub fn disjointed_merge_batch<'a>(batch: &[&'a Self], mask: &[&'a Self]) -> Self {
705        let account_count = batch.iter().map(|item| item.accounts.len()).sum();
706        let mut accounts = Vec::with_capacity(account_count);
707        accounts.extend(kway_merge_disjoint_sorted(
708            batch.iter().rev().map(|item| item.accounts.as_slice()),
709            mask.iter().map(|item| item.accounts.as_slice()),
710        ));
711
712        struct StorageAcc<'a> {
713            slot_count: usize,
714            slices: Vec<&'a [(B256, U256)]>,
715        }
716
717        #[derive(Default)]
718        struct StorageMaskAcc<'a> {
719            slices: Vec<&'a [(B256, U256)]>,
720        }
721
722        let mut storages = B256Map::with_capacity_and_hasher(
723            batch.iter().map(|item| item.storages.len()).sum(),
724            Default::default(),
725        );
726
727        for item in batch.iter().rev() {
728            for (hashed_address, storage) in &item.storages {
729                assert!(
730                    !storage.wiped,
731                    "storage wipes are not supported by disjointed_merge_batch"
732                );
733                let entry = storages
734                    .entry(*hashed_address)
735                    .or_insert_with(|| StorageAcc { slot_count: 0, slices: Vec::new() });
736                entry.slices.push(storage.storage_slots.as_slice());
737                entry.slot_count += storage.storage_slots.len();
738            }
739        }
740
741        let mut storage_masks: B256Map<StorageMaskAcc<'a>> = B256Map::with_capacity_and_hasher(
742            mask.iter().map(|item| item.storages.len()).sum(),
743            Default::default(),
744        );
745        for item in mask {
746            for (hashed_address, storage) in &item.storages {
747                assert!(
748                    !storage.wiped,
749                    "storage wipes are not supported by disjointed_merge_batch"
750                );
751                let entry = storage_masks.entry(*hashed_address).or_default();
752                entry.slices.push(storage.storage_slots.as_slice());
753            }
754        }
755
756        let storages = storages
757            .into_iter()
758            .filter_map(|(hashed_address, entry)| {
759                let slot_count = entry.slot_count;
760                let storage_slots = match storage_masks.get(&hashed_address) {
761                    Some(mask_entry) => {
762                        let mut storage_slots = Vec::with_capacity(slot_count);
763                        storage_slots.extend(kway_merge_disjoint_sorted(
764                            entry.slices,
765                            mask_entry.slices.iter().copied(),
766                        ));
767                        storage_slots
768                    }
769                    None => kway_merge_sorted(entry.slices),
770                };
771
772                (!storage_slots.is_empty() || mask.is_empty()).then_some((
773                    hashed_address,
774                    HashedStorageSorted { wiped: false, storage_slots },
775                ))
776            })
777            .collect();
778
779        Self { accounts, storages }
780    }
781
782    /// Clears all accounts and storage data.
783    pub fn clear(&mut self) {
784        self.accounts.clear();
785        self.storages.clear();
786    }
787}
788
789impl AsRef<Self> for HashedPostStateSorted {
790    fn as_ref(&self) -> &Self {
791        self
792    }
793}
794
795/// Sorted hashed storage optimized for iterating during state trie calculation.
796#[derive(Clone, Eq, PartialEq, Debug, Default)]
797#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
798pub struct HashedStorageSorted {
799    /// Sorted collection of updated storage slots. [`U256::ZERO`] indicates a deleted value.
800    pub storage_slots: Vec<(B256, U256)>,
801    /// Flag indicating whether the storage was wiped or not.
802    pub wiped: bool,
803}
804
805impl HashedStorageSorted {
806    /// Returns `true` if the account was wiped.
807    pub const fn is_wiped(&self) -> bool {
808        self.wiped
809    }
810
811    /// Returns reference to updated storage slots.
812    pub fn storage_slots_ref(&self) -> &[(B256, U256)] {
813        &self.storage_slots
814    }
815
816    /// Returns the total number of storage slot updates.
817    pub const fn len(&self) -> usize {
818        self.storage_slots.len()
819    }
820
821    /// Returns `true` if there are no storage slot updates.
822    pub const fn is_empty(&self) -> bool {
823        self.storage_slots.is_empty()
824    }
825
826    /// Extends the storage slots updates with another set of sorted updates.
827    ///
828    /// If `other` is marked as deleted, this will be marked as deleted and all slots cleared.
829    /// Otherwise, nodes are merged with `other`'s values taking precedence for duplicates.
830    pub fn extend_ref(&mut self, other: &Self) {
831        if other.wiped {
832            // If other is wiped, clear everything and copy from other
833            self.wiped = true;
834            self.storage_slots.clear();
835            self.storage_slots.extend(other.storage_slots.iter().copied());
836            return;
837        }
838
839        // Extend the sorted non-zero valued slots
840        extend_sorted_vec(&mut self.storage_slots, &other.storage_slots);
841    }
842
843    /// Batch-merge sorted hashed storage. Iterator yields **newest to oldest**.
844    /// If any update is wiped, prior data is discarded.
845    pub fn merge_batch<'a>(updates: impl IntoIterator<Item = &'a Self>) -> Self {
846        let updates: Vec<_> = updates.into_iter().collect();
847        if updates.is_empty() {
848            return Self::default();
849        }
850
851        let wipe_idx = updates.iter().position(|u| u.wiped);
852        let relevant = wipe_idx.map_or(&updates[..], |idx| &updates[..=idx]);
853        let storage_slots = kway_merge_sorted(relevant.iter().map(|u| u.storage_slots.as_slice()));
854
855        Self { wiped: wipe_idx.is_some(), storage_slots }
856    }
857}
858
859impl From<HashedStorageSorted> for HashedStorage {
860    fn from(sorted: HashedStorageSorted) -> Self {
861        let mut storage = B256Map::default();
862
863        // Add all storage slots (including zero-valued ones which indicate deletion)
864        for (slot, value) in sorted.storage_slots {
865            storage.insert(slot, value);
866        }
867
868        Self { wiped: sorted.wiped, storage }
869    }
870}
871
872impl From<HashedPostStateSorted> for HashedPostState {
873    fn from(sorted: HashedPostStateSorted) -> Self {
874        let mut accounts =
875            B256Map::with_capacity_and_hasher(sorted.accounts.len(), Default::default());
876
877        // Add all accounts (Some for updated, None for destroyed)
878        for (address, account) in sorted.accounts {
879            accounts.insert(address, account);
880        }
881
882        // Convert storages
883        let storages = sorted
884            .storages
885            .into_iter()
886            .map(|(address, storage)| (address, storage.into()))
887            .collect();
888
889        Self { accounts, storages }
890    }
891}
892
893/// An iterator that yields chunks of the state updates of at most `size` account and storage
894/// targets.
895///
896/// # Notes
897/// 1. Chunks are expected to be applied in order, because of storage wipes. If applied out of
898///    order, it's possible to wipe more storage than in the original state update.
899/// 2. For each account, chunks with storage updates come first, followed by account updates.
900#[derive(Debug)]
901pub struct ChunkedHashedPostState {
902    flattened: alloc::vec::IntoIter<(B256, FlattenedHashedPostStateItem)>,
903    size: usize,
904}
905
906/// Order discriminant for sorting flattened state items.
907/// Ordering: `StorageWipe` < `StorageUpdate` (by slot) < `Account`
908#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
909enum FlattenedStateOrder {
910    StorageWipe,
911    StorageUpdate(B256),
912    Account,
913}
914
915#[derive(Debug)]
916enum FlattenedHashedPostStateItem {
917    Account(Option<Account>),
918    StorageWipe,
919    StorageUpdate { slot: B256, value: U256 },
920}
921
922impl FlattenedHashedPostStateItem {
923    const fn order(&self) -> FlattenedStateOrder {
924        match self {
925            Self::StorageWipe => FlattenedStateOrder::StorageWipe,
926            Self::StorageUpdate { slot, .. } => FlattenedStateOrder::StorageUpdate(*slot),
927            Self::Account(_) => FlattenedStateOrder::Account,
928        }
929    }
930}
931
932impl ChunkedHashedPostState {
933    fn new(hashed_post_state: HashedPostState, size: usize) -> Self {
934        let flattened = hashed_post_state
935            .storages
936            .into_iter()
937            .flat_map(|(address, storage)| {
938                storage
939                    .wiped
940                    .then_some((address, FlattenedHashedPostStateItem::StorageWipe))
941                    .into_iter()
942                    .chain(storage.storage.into_iter().map(move |(slot, value)| {
943                        (address, FlattenedHashedPostStateItem::StorageUpdate { slot, value })
944                    }))
945            })
946            .chain(hashed_post_state.accounts.into_iter().map(|(address, account)| {
947                (address, FlattenedHashedPostStateItem::Account(account))
948            }))
949            // Sort by address, then by item order to ensure correct application sequence:
950            // 1. Storage wipes (must come first to clear storage)
951            // 2. Storage updates (sorted by slot for determinism)
952            // 3. Account updates (can be applied last)
953            .sorted_unstable_by_key(|(address, item)| (*address, item.order()));
954
955        Self { flattened, size }
956    }
957}
958
959impl Iterator for ChunkedHashedPostState {
960    type Item = HashedPostState;
961
962    fn next(&mut self) -> Option<Self::Item> {
963        let mut chunk = HashedPostState::default();
964
965        let mut current_size = 0;
966        while current_size < self.size {
967            let Some((address, item)) = self.flattened.next() else { break };
968
969            match item {
970                FlattenedHashedPostStateItem::Account(account) => {
971                    chunk.accounts.insert(address, account);
972                }
973                FlattenedHashedPostStateItem::StorageWipe => {
974                    chunk.storages.entry(address).or_default().wiped = true;
975                }
976                FlattenedHashedPostStateItem::StorageUpdate { slot, value } => {
977                    chunk.storages.entry(address).or_default().storage.insert(slot, value);
978                }
979            }
980
981            current_size += 1;
982        }
983
984        if chunk.is_empty() {
985            None
986        } else {
987            Some(chunk)
988        }
989    }
990}
991
992#[cfg(test)]
993mod tests {
994    use super::*;
995    use crate::KeccakKeyHasher;
996    use alloy_primitives::Bytes;
997    use revm::{
998        database::{states::StorageSlot, AccountStatus, StorageWithOriginalValues},
999        state::{AccountInfo, Bytecode},
1000    };
1001
1002    fn bundle_hashed_storage(account: &BundleAccount) -> Option<HashedStorage> {
1003        let address = Address::ZERO;
1004        let mut state =
1005            HashedPostState::from_bundle_state::<KeccakKeyHasher>([(&address, account)]);
1006        state.storages.remove(&keccak256(address))
1007    }
1008
1009    fn changed_storage(original: U256, present: U256) -> StorageWithOriginalValues {
1010        core::iter::once((U256::from(1), StorageSlot::new_changed(original, present))).collect()
1011    }
1012
1013    #[test]
1014    fn hashed_state_wiped_extension() {
1015        let hashed_address = B256::default();
1016        let hashed_slot = B256::with_last_byte(64);
1017        let hashed_slot2 = B256::with_last_byte(65);
1018
1019        // Initialize post state storage
1020        let original_slot_value = U256::from(123);
1021        let mut hashed_state = HashedPostState::default().with_storages([(
1022            hashed_address,
1023            HashedStorage::from_iter([
1024                (hashed_slot, original_slot_value),
1025                (hashed_slot2, original_slot_value),
1026            ]),
1027        )]);
1028
1029        // Update single slot value
1030        let updated_slot_value = U256::from(321);
1031        let extension = HashedPostState::default().with_storages([(
1032            hashed_address,
1033            HashedStorage::from_iter([(hashed_slot, updated_slot_value)]),
1034        )]);
1035        hashed_state.extend(extension);
1036
1037        let account_storage = hashed_state.storages.get(&hashed_address);
1038        assert_eq!(
1039            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
1040            Some(&updated_slot_value)
1041        );
1042        assert_eq!(
1043            account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
1044            Some(&original_slot_value)
1045        );
1046        assert_eq!(account_storage.map(|st| st.wiped), Some(false));
1047
1048        // Wipe account storage
1049        let wiped_extension =
1050            HashedPostState::default().with_storages([(hashed_address, HashedStorage::new(true))]);
1051        hashed_state.extend(wiped_extension);
1052
1053        let account_storage = hashed_state.storages.get(&hashed_address);
1054        assert_eq!(account_storage.map(|st| st.storage.is_empty()), Some(true));
1055        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
1056
1057        // Reinitialize single slot value
1058        hashed_state.extend(HashedPostState::default().with_storages([(
1059            hashed_address,
1060            HashedStorage::from_iter([(hashed_slot, original_slot_value)]),
1061        )]));
1062        let account_storage = hashed_state.storages.get(&hashed_address);
1063        assert_eq!(
1064            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
1065            Some(&original_slot_value)
1066        );
1067        assert_eq!(account_storage.and_then(|st| st.storage.get(&hashed_slot2)), None);
1068        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
1069
1070        // Reinitialize single slot value
1071        hashed_state.extend(HashedPostState::default().with_storages([(
1072            hashed_address,
1073            HashedStorage::from_iter([(hashed_slot2, updated_slot_value)]),
1074        )]));
1075        let account_storage = hashed_state.storages.get(&hashed_address);
1076        assert_eq!(
1077            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
1078            Some(&original_slot_value)
1079        );
1080        assert_eq!(
1081            account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
1082            Some(&updated_slot_value)
1083        );
1084        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
1085    }
1086
1087    #[test]
1088    fn test_hashed_post_state_from_bundle_state() {
1089        // Prepare a random Ethereum address as a key for the account.
1090        let address = Address::random();
1091
1092        // Create a mock account info object.
1093        let account_info = AccountInfo {
1094            balance: U256::from(123),
1095            nonce: 42,
1096            code_hash: B256::random(),
1097            code: Some(Bytecode::new_raw(Bytes::from(vec![1, 2]))),
1098            account_id: None,
1099        };
1100
1101        let mut storage = StorageWithOriginalValues::default();
1102        storage.insert(
1103            U256::from(1),
1104            StorageSlot { present_value: U256::from(4), ..Default::default() },
1105        );
1106
1107        // Create a `BundleAccount` struct to represent the account and its storage.
1108        let account = BundleAccount {
1109            status: AccountStatus::Changed,
1110            info: Some(account_info.clone()),
1111            storage,
1112            original_info: None,
1113        };
1114
1115        // Create a vector of tuples representing the bundle state.
1116        let state = vec![(&address, &account)];
1117
1118        // Convert the bundle state into a hashed post state.
1119        let hashed_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(state);
1120
1121        // Validate the hashed post state.
1122        assert_eq!(hashed_state.accounts.len(), 1);
1123        assert_eq!(hashed_state.storages.len(), 1);
1124
1125        // Validate the account info.
1126        assert_eq!(
1127            *hashed_state.accounts.get(&keccak256(address)).unwrap(),
1128            Some(account_info.into())
1129        );
1130    }
1131
1132    #[test]
1133    fn destroyed_prefunded_account_without_storage_emits_no_storage() {
1134        let original_info = AccountInfo { balance: U256::from(1), ..Default::default() };
1135        let account = BundleAccount::new(
1136            Some(original_info),
1137            None,
1138            StorageWithOriginalValues::default(),
1139            AccountStatus::Destroyed,
1140        );
1141
1142        assert!(bundle_hashed_storage(&account).is_none());
1143    }
1144
1145    #[test]
1146    fn destroyed_accounts_emit_zero_storage_changes_without_wipe() {
1147        let existing_contract =
1148            AccountInfo { code_hash: B256::repeat_byte(0x01), ..Default::default() };
1149        let legacy_empty_account = AccountInfo::default();
1150        let prefunded_account = AccountInfo { balance: U256::from(1), ..Default::default() };
1151
1152        for original_info in
1153            [Some(existing_contract), Some(legacy_empty_account), Some(prefunded_account), None]
1154        {
1155            let account = BundleAccount::new(
1156                original_info,
1157                None,
1158                changed_storage(U256::from(2), U256::ZERO),
1159                AccountStatus::Destroyed,
1160            );
1161
1162            let storage = bundle_hashed_storage(&account).unwrap();
1163            let hashed_slot = keccak256(B256::from(U256::from(1)));
1164            assert!(!storage.wiped);
1165            assert_eq!(storage.storage[&hashed_slot], U256::ZERO);
1166        }
1167    }
1168
1169    #[test]
1170    fn destroyed_recreated_accounts_preserve_storage_without_wipe() {
1171        let value = U256::from(2);
1172        let new_account = BundleAccount::new(
1173            None,
1174            Some(AccountInfo::default()),
1175            changed_storage(U256::ZERO, value),
1176            AccountStatus::DestroyedChanged,
1177        );
1178        let original_info =
1179            AccountInfo { code_hash: B256::repeat_byte(0x01), ..Default::default() };
1180        let existing_account = BundleAccount::new(
1181            Some(original_info),
1182            Some(AccountInfo::default()),
1183            changed_storage(U256::ZERO, value),
1184            AccountStatus::DestroyedChanged,
1185        );
1186
1187        let new_storage = bundle_hashed_storage(&new_account).unwrap();
1188        let existing_storage = bundle_hashed_storage(&existing_account).unwrap();
1189        let hashed_slot = keccak256(B256::from(U256::from(1)));
1190
1191        assert!(!new_storage.wiped);
1192        assert!(!existing_storage.wiped);
1193        assert_eq!(new_storage.storage[&hashed_slot], value);
1194        assert_eq!(existing_storage.storage[&hashed_slot], value);
1195    }
1196
1197    #[test]
1198    fn test_hashed_post_state_with_accounts() {
1199        // Prepare random addresses and mock account info.
1200        let address_1 = Address::random();
1201        let address_2 = Address::random();
1202
1203        let account_info_1 = AccountInfo {
1204            balance: U256::from(1000),
1205            nonce: 1,
1206            code_hash: B256::random(),
1207            code: None,
1208            account_id: None,
1209        };
1210
1211        // Create hashed accounts with addresses.
1212        let account_1 = (keccak256(address_1), Some(account_info_1.into()));
1213        let account_2 = (keccak256(address_2), None);
1214
1215        // Add accounts to the hashed post state.
1216        let hashed_state = HashedPostState::default().with_accounts(vec![account_1, account_2]);
1217
1218        // Validate the hashed post state.
1219        assert_eq!(hashed_state.accounts.len(), 2);
1220        assert!(hashed_state.accounts.contains_key(&keccak256(address_1)));
1221        assert!(hashed_state.accounts.contains_key(&keccak256(address_2)));
1222    }
1223
1224    #[test]
1225    fn test_hashed_post_state_with_storages() {
1226        // Prepare random addresses and mock storage entries.
1227        let address_1 = Address::random();
1228        let address_2 = Address::random();
1229
1230        let storage_1 = (keccak256(address_1), HashedStorage::new(false));
1231        let storage_2 = (keccak256(address_2), HashedStorage::new(true));
1232
1233        // Add storages to the hashed post state.
1234        let hashed_state = HashedPostState::default().with_storages(vec![storage_1, storage_2]);
1235
1236        // Validate the hashed post state.
1237        assert_eq!(hashed_state.storages.len(), 2);
1238        assert!(hashed_state.storages.contains_key(&keccak256(address_1)));
1239        assert!(hashed_state.storages.contains_key(&keccak256(address_2)));
1240    }
1241
1242    #[test]
1243    fn test_hashed_post_state_is_empty() {
1244        // Create an empty hashed post state and validate it's empty.
1245        let empty_state = HashedPostState::default();
1246        assert!(empty_state.is_empty());
1247
1248        // Add an account and validate the state is no longer empty.
1249        let non_empty_state = HashedPostState::default()
1250            .with_accounts(vec![(keccak256(Address::random()), Some(Account::default()))]);
1251        assert!(!non_empty_state.is_empty());
1252    }
1253
1254    fn create_state_for_multi_proof_targets() -> HashedPostState {
1255        let mut state = HashedPostState::default();
1256
1257        let addr1 = B256::random();
1258        let addr2 = B256::random();
1259        state.accounts.insert(addr1, Some(Default::default()));
1260        state.accounts.insert(addr2, Some(Default::default()));
1261
1262        let mut storage = HashedStorage::default();
1263        let slot1 = B256::random();
1264        let slot2 = B256::random();
1265        storage.storage.insert(slot1, U256::ZERO);
1266        storage.storage.insert(slot2, U256::from(1));
1267        state.storages.insert(addr1, storage);
1268
1269        state
1270    }
1271
1272    #[test]
1273    fn test_multi_proof_targets_difference_empty_state() {
1274        let state = HashedPostState::default();
1275        let excluded = MultiProofTargets::default();
1276
1277        let targets = state.multi_proof_targets_difference(&excluded);
1278        assert!(targets.is_empty());
1279    }
1280
1281    #[test]
1282    fn test_multi_proof_targets_difference_new_account_targets() {
1283        let state = create_state_for_multi_proof_targets();
1284        let excluded = MultiProofTargets::default();
1285
1286        // should return all accounts as targets since excluded is empty
1287        let targets = state.multi_proof_targets_difference(&excluded);
1288        assert_eq!(targets.len(), state.accounts.len());
1289        for addr in state.accounts.keys() {
1290            assert!(targets.contains_key(addr));
1291        }
1292    }
1293
1294    #[test]
1295    fn test_multi_proof_targets_difference_new_storage_targets() {
1296        let state = create_state_for_multi_proof_targets();
1297        let excluded = MultiProofTargets::default();
1298
1299        let targets = state.multi_proof_targets_difference(&excluded);
1300
1301        // verify storage slots are included for accounts with storage
1302        for (addr, storage) in &state.storages {
1303            assert!(targets.contains_key(addr));
1304            let target_slots = &targets[addr];
1305            assert_eq!(target_slots.len(), storage.storage.len());
1306            for slot in storage.storage.keys() {
1307                assert!(target_slots.contains(slot));
1308            }
1309        }
1310    }
1311
1312    #[test]
1313    fn test_multi_proof_targets_difference_filter_excluded_accounts() {
1314        let state = create_state_for_multi_proof_targets();
1315        let mut excluded = MultiProofTargets::default();
1316
1317        // select an account that has no storage updates
1318        let excluded_addr = state
1319            .accounts
1320            .keys()
1321            .find(|&&addr| !state.storages.contains_key(&addr))
1322            .expect("Should have an account without storage");
1323
1324        // mark the account as excluded
1325        excluded.insert(*excluded_addr, HashSet::default());
1326
1327        let targets = state.multi_proof_targets_difference(&excluded);
1328
1329        // should not include the already excluded account since it has no storage updates
1330        assert!(!targets.contains_key(excluded_addr));
1331        // other accounts should still be included
1332        assert_eq!(targets.len(), state.accounts.len() - 1);
1333    }
1334
1335    #[test]
1336    fn test_multi_proof_targets_difference_filter_excluded_storage() {
1337        let state = create_state_for_multi_proof_targets();
1338        let mut excluded = MultiProofTargets::default();
1339
1340        // mark one storage slot as excluded
1341        let (addr, storage) = state.storages.iter().next().unwrap();
1342        let mut excluded_slots = HashSet::default();
1343        let excluded_slot = *storage.storage.keys().next().unwrap();
1344        excluded_slots.insert(excluded_slot);
1345        excluded.insert(*addr, excluded_slots);
1346
1347        let targets = state.multi_proof_targets_difference(&excluded);
1348
1349        // should not include the excluded storage slot
1350        let target_slots = &targets[addr];
1351        assert!(!target_slots.contains(&excluded_slot));
1352        assert_eq!(target_slots.len(), storage.storage.len() - 1);
1353    }
1354
1355    #[test]
1356    fn test_multi_proof_targets_difference_mixed_excluded_state() {
1357        let mut state = HashedPostState::default();
1358        let mut excluded = MultiProofTargets::default();
1359
1360        let addr1 = B256::random();
1361        let addr2 = B256::random();
1362        let slot1 = B256::random();
1363        let slot2 = B256::random();
1364
1365        state.accounts.insert(addr1, Some(Default::default()));
1366        state.accounts.insert(addr2, Some(Default::default()));
1367
1368        let mut storage = HashedStorage::default();
1369        storage.storage.insert(slot1, U256::ZERO);
1370        storage.storage.insert(slot2, U256::from(1));
1371        state.storages.insert(addr1, storage);
1372
1373        let mut excluded_slots = HashSet::default();
1374        excluded_slots.insert(slot1);
1375        excluded.insert(addr1, excluded_slots);
1376
1377        let targets = state.multi_proof_targets_difference(&excluded);
1378
1379        assert!(targets.contains_key(&addr2));
1380        assert!(!targets[&addr1].contains(&slot1));
1381        assert!(targets[&addr1].contains(&slot2));
1382    }
1383
1384    #[test]
1385    fn test_multi_proof_targets_difference_unmodified_account_with_storage() {
1386        let mut state = HashedPostState::default();
1387        let excluded = MultiProofTargets::default();
1388
1389        let addr = B256::random();
1390        let slot1 = B256::random();
1391        let slot2 = B256::random();
1392
1393        // don't add the account to state.accounts (simulating unmodified account)
1394        // but add storage updates for this account
1395        let mut storage = HashedStorage::default();
1396        storage.storage.insert(slot1, U256::from(1));
1397        storage.storage.insert(slot2, U256::from(2));
1398        state.storages.insert(addr, storage);
1399
1400        assert!(!state.accounts.contains_key(&addr));
1401        assert!(!excluded.contains_key(&addr));
1402
1403        let targets = state.multi_proof_targets_difference(&excluded);
1404
1405        // verify that we still get the storage slots for the unmodified account
1406        assert!(targets.contains_key(&addr));
1407
1408        let target_slots = &targets[&addr];
1409        assert_eq!(target_slots.len(), 2);
1410        assert!(target_slots.contains(&slot1));
1411        assert!(target_slots.contains(&slot2));
1412    }
1413
1414    #[test]
1415    fn test_partition_by_targets() {
1416        let addr1 = B256::random();
1417        let addr2 = B256::random();
1418        let slot1 = B256::random();
1419        let slot2 = B256::random();
1420
1421        let state = HashedPostState {
1422            accounts: B256Map::from_iter([
1423                (addr1, Some(Default::default())),
1424                (addr2, Some(Default::default())),
1425            ]),
1426            storages: B256Map::from_iter([(
1427                addr1,
1428                HashedStorage {
1429                    wiped: true,
1430                    storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1431                },
1432            )]),
1433        };
1434        let targets = MultiProofTargets::from_iter([(addr1, HashSet::from_iter([slot1]))]);
1435
1436        let (with_targets, without_targets) =
1437            state.partition_by_targets(&targets, &MultiAddedRemovedKeys::new());
1438
1439        assert_eq!(
1440            with_targets,
1441            HashedPostState {
1442                accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
1443                storages: B256Map::from_iter([(
1444                    addr1,
1445                    HashedStorage {
1446                        wiped: true,
1447                        storage: B256Map::from_iter([(slot1, U256::ZERO)])
1448                    }
1449                )]),
1450            }
1451        );
1452        assert_eq!(
1453            without_targets,
1454            HashedPostState {
1455                accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
1456                storages: B256Map::from_iter([(
1457                    addr1,
1458                    HashedStorage {
1459                        wiped: false,
1460                        storage: B256Map::from_iter([(slot2, U256::from(1))])
1461                    }
1462                )]),
1463            }
1464        );
1465    }
1466
1467    #[test]
1468    fn test_chunks() {
1469        let addr1 = B256::from([1; 32]);
1470        let addr2 = B256::from([2; 32]);
1471        let slot1 = B256::from([1; 32]);
1472        let slot2 = B256::from([2; 32]);
1473
1474        let state = HashedPostState {
1475            accounts: B256Map::from_iter([
1476                (addr1, Some(Default::default())),
1477                (addr2, Some(Default::default())),
1478            ]),
1479            storages: B256Map::from_iter([(
1480                addr2,
1481                HashedStorage {
1482                    wiped: true,
1483                    storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1484                },
1485            )]),
1486        };
1487
1488        let mut chunks = state.chunks(2);
1489        assert_eq!(
1490            chunks.next(),
1491            Some(HashedPostState {
1492                accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
1493                storages: B256Map::from_iter([(addr2, HashedStorage::new(true)),])
1494            })
1495        );
1496        assert_eq!(
1497            chunks.next(),
1498            Some(HashedPostState {
1499                accounts: B256Map::default(),
1500                storages: B256Map::from_iter([(
1501                    addr2,
1502                    HashedStorage {
1503                        wiped: false,
1504                        storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1505                    },
1506                )])
1507            })
1508        );
1509        assert_eq!(
1510            chunks.next(),
1511            Some(HashedPostState {
1512                accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
1513                storages: B256Map::default()
1514            })
1515        );
1516        assert_eq!(chunks.next(), None);
1517    }
1518
1519    #[test]
1520    fn test_chunks_ordering_guarantee() {
1521        // Test that chunks preserve the ordering: wipe -> storage updates -> account
1522        // Use chunk size of 1 to verify each item comes out in the correct order
1523        let addr = B256::from([1; 32]);
1524        let slot1 = B256::from([1; 32]);
1525        let slot2 = B256::from([2; 32]);
1526
1527        let state = HashedPostState {
1528            accounts: B256Map::from_iter([(addr, Some(Default::default()))]),
1529            storages: B256Map::from_iter([(
1530                addr,
1531                HashedStorage {
1532                    wiped: true,
1533                    storage: B256Map::from_iter([(slot1, U256::from(1)), (slot2, U256::from(2))]),
1534                },
1535            )]),
1536        };
1537
1538        let chunks: Vec<_> = state.chunks(1).collect();
1539
1540        // Should have 4 chunks: 1 wipe + 2 storage updates + 1 account
1541        assert_eq!(chunks.len(), 4);
1542
1543        // First chunk must be the storage wipe
1544        assert!(chunks[0].accounts.is_empty());
1545        assert_eq!(chunks[0].storages.len(), 1);
1546        assert!(chunks[0].storages.get(&addr).unwrap().wiped);
1547        assert!(chunks[0].storages.get(&addr).unwrap().storage.is_empty());
1548
1549        // Next two chunks must be storage updates (order between them doesn't matter)
1550        assert!(chunks[1].accounts.is_empty());
1551        assert!(!chunks[1].storages.get(&addr).unwrap().wiped);
1552        assert_eq!(chunks[1].storages.get(&addr).unwrap().storage.len(), 1);
1553
1554        assert!(chunks[2].accounts.is_empty());
1555        assert!(!chunks[2].storages.get(&addr).unwrap().wiped);
1556        assert_eq!(chunks[2].storages.get(&addr).unwrap().storage.len(), 1);
1557
1558        // Last chunk must be the account update
1559        assert_eq!(chunks[3].accounts.len(), 1);
1560        assert!(chunks[3].accounts.contains_key(&addr));
1561        assert!(chunks[3].storages.is_empty());
1562    }
1563
1564    #[test]
1565    fn test_hashed_post_state_sorted_extend_ref() {
1566        // Test extending accounts
1567        let mut state1 = HashedPostStateSorted {
1568            accounts: vec![
1569                (B256::from([1; 32]), Some(Account::default())),
1570                (B256::from([3; 32]), Some(Account::default())),
1571                (B256::from([5; 32]), None),
1572            ],
1573            storages: B256Map::default(),
1574        };
1575
1576        let state2 = HashedPostStateSorted {
1577            accounts: vec![
1578                (B256::from([2; 32]), Some(Account::default())),
1579                (B256::from([3; 32]), Some(Account { nonce: 1, ..Default::default() })), /* Override */
1580                (B256::from([4; 32]), Some(Account::default())),
1581                (B256::from([6; 32]), None),
1582            ],
1583            storages: B256Map::default(),
1584        };
1585
1586        state1.extend_ref_and_sort(&state2);
1587
1588        // Check accounts are merged and sorted
1589        assert_eq!(state1.accounts.len(), 6);
1590        assert_eq!(state1.accounts[0].0, B256::from([1; 32]));
1591        assert_eq!(state1.accounts[1].0, B256::from([2; 32]));
1592        assert_eq!(state1.accounts[2].0, B256::from([3; 32]));
1593        assert_eq!(state1.accounts[2].1.unwrap().nonce, 1); // Should have state2's value
1594        assert_eq!(state1.accounts[3].0, B256::from([4; 32]));
1595        assert_eq!(state1.accounts[4].0, B256::from([5; 32]));
1596        assert_eq!(state1.accounts[4].1, None);
1597        assert_eq!(state1.accounts[5].0, B256::from([6; 32]));
1598        assert_eq!(state1.accounts[5].1, None);
1599    }
1600
1601    #[test]
1602    fn test_hashed_storage_sorted_extend_ref() {
1603        // Test normal extension
1604        let mut storage1 = HashedStorageSorted {
1605            storage_slots: vec![
1606                (B256::from([1; 32]), U256::from(10)),
1607                (B256::from([3; 32]), U256::from(30)),
1608                (B256::from([5; 32]), U256::ZERO),
1609            ],
1610            wiped: false,
1611        };
1612
1613        let storage2 = HashedStorageSorted {
1614            storage_slots: vec![
1615                (B256::from([2; 32]), U256::from(20)),
1616                (B256::from([3; 32]), U256::from(300)), // Override
1617                (B256::from([4; 32]), U256::from(40)),
1618                (B256::from([6; 32]), U256::ZERO),
1619            ],
1620            wiped: false,
1621        };
1622
1623        storage1.extend_ref(&storage2);
1624
1625        assert_eq!(storage1.storage_slots.len(), 6);
1626        assert_eq!(storage1.storage_slots[0].0, B256::from([1; 32]));
1627        assert_eq!(storage1.storage_slots[0].1, U256::from(10));
1628        assert_eq!(storage1.storage_slots[1].0, B256::from([2; 32]));
1629        assert_eq!(storage1.storage_slots[1].1, U256::from(20));
1630        assert_eq!(storage1.storage_slots[2].0, B256::from([3; 32]));
1631        assert_eq!(storage1.storage_slots[2].1, U256::from(300)); // Should have storage2's value
1632        assert_eq!(storage1.storage_slots[3].0, B256::from([4; 32]));
1633        assert_eq!(storage1.storage_slots[3].1, U256::from(40));
1634        assert_eq!(storage1.storage_slots[4].0, B256::from([5; 32]));
1635        assert_eq!(storage1.storage_slots[4].1, U256::ZERO);
1636        assert_eq!(storage1.storage_slots[5].0, B256::from([6; 32]));
1637        assert_eq!(storage1.storage_slots[5].1, U256::ZERO);
1638        assert!(!storage1.wiped);
1639
1640        // Test wiped storage
1641        let mut storage3 = HashedStorageSorted {
1642            storage_slots: vec![
1643                (B256::from([1; 32]), U256::from(10)),
1644                (B256::from([2; 32]), U256::ZERO),
1645            ],
1646            wiped: false,
1647        };
1648
1649        let storage4 = HashedStorageSorted {
1650            storage_slots: vec![
1651                (B256::from([3; 32]), U256::from(30)),
1652                (B256::from([4; 32]), U256::ZERO),
1653            ],
1654            wiped: true,
1655        };
1656
1657        storage3.extend_ref(&storage4);
1658
1659        assert!(storage3.wiped);
1660        // When wiped, should only have storage4's values
1661        assert_eq!(storage3.storage_slots.len(), 2);
1662        assert_eq!(storage3.storage_slots[0].0, B256::from([3; 32]));
1663        assert_eq!(storage3.storage_slots[0].1, U256::from(30));
1664        assert_eq!(storage3.storage_slots[1].0, B256::from([4; 32]));
1665        assert_eq!(storage3.storage_slots[1].1, U256::ZERO);
1666    }
1667
1668    /// Test extending with sorted accounts merges correctly into `HashMap`
1669    #[test]
1670    fn test_hashed_post_state_extend_from_sorted_with_accounts() {
1671        let addr1 = B256::random();
1672        let addr2 = B256::random();
1673
1674        let mut state = HashedPostState::default();
1675        state.accounts.insert(addr1, Some(Default::default()));
1676
1677        let mut sorted_state = HashedPostStateSorted::default();
1678        sorted_state.accounts.push((addr2, Some(Default::default())));
1679
1680        state.extend_from_sorted(&sorted_state);
1681
1682        assert_eq!(state.accounts.len(), 2);
1683        assert!(state.accounts.contains_key(&addr1));
1684        assert!(state.accounts.contains_key(&addr2));
1685    }
1686
1687    /// Test destroyed accounts (None values) are inserted correctly
1688    #[test]
1689    fn test_hashed_post_state_extend_from_sorted_with_destroyed_accounts() {
1690        let addr1 = B256::random();
1691
1692        let mut state = HashedPostState::default();
1693
1694        let mut sorted_state = HashedPostStateSorted::default();
1695        sorted_state.accounts.push((addr1, None));
1696
1697        state.extend_from_sorted(&sorted_state);
1698
1699        assert!(state.accounts.contains_key(&addr1));
1700        assert_eq!(state.accounts.get(&addr1), Some(&None));
1701    }
1702
1703    #[test]
1704    fn test_hashed_post_state_sorted_disjointed_merge_batch() {
1705        fn account(nonce: u64) -> Account {
1706            Account { nonce, balance: U256::ZERO, bytecode_hash: None }
1707        }
1708
1709        let kept_account = B256::with_last_byte(1);
1710        let removed_account = B256::with_last_byte(2);
1711        let kept_storage = B256::with_last_byte(3);
1712        let slot1 = B256::with_last_byte(11);
1713        let slot2 = B256::with_last_byte(12);
1714
1715        let older = HashedPostStateSorted::new(
1716            vec![(kept_account, Some(account(1))), (removed_account, Some(account(10)))],
1717            B256Map::from_iter([(
1718                kept_storage,
1719                HashedStorageSorted { wiped: false, storage_slots: vec![(slot1, U256::from(1))] },
1720            )]),
1721        );
1722
1723        let newer = HashedPostStateSorted::new(
1724            vec![(kept_account, Some(account(2)))],
1725            B256Map::from_iter([(
1726                kept_storage,
1727                HashedStorageSorted {
1728                    wiped: false,
1729                    storage_slots: vec![(slot1, U256::from(3)), (slot2, U256::from(4))],
1730                },
1731            )]),
1732        );
1733
1734        let remove_a = HashedPostStateSorted::new(
1735            vec![(removed_account, None)],
1736            B256Map::from_iter([(
1737                kept_storage,
1738                HashedStorageSorted { wiped: false, storage_slots: vec![(slot2, U256::ZERO)] },
1739            )]),
1740        );
1741
1742        let remove_b = HashedPostStateSorted::new(
1743            vec![(B256::with_last_byte(255), Some(account(99)))],
1744            B256Map::default(),
1745        );
1746
1747        let result = HashedPostStateSorted::disjointed_merge_batch(
1748            &[&older, &newer],
1749            &[&remove_b, &remove_a],
1750        );
1751
1752        assert_eq!(result.accounts, vec![(kept_account, Some(account(2)))]);
1753        assert_eq!(result.storages.len(), 1);
1754        assert_eq!(
1755            result.storages.get(&kept_storage),
1756            Some(&HashedStorageSorted {
1757                wiped: false,
1758                storage_slots: vec![(slot1, U256::from(3))],
1759            })
1760        );
1761    }
1762
1763    #[test]
1764    fn test_hashed_post_state_sorted_disjointed_merge_batch_empty_mask_merges_batch() {
1765        let address = B256::with_last_byte(1);
1766        let storage = B256::with_last_byte(2);
1767        let slot = B256::with_last_byte(3);
1768        let empty_storage = B256::with_last_byte(4);
1769        let older = HashedPostStateSorted::new(
1770            vec![(address, Some(Account { nonce: 1, ..Default::default() }))],
1771            B256Map::from_iter([
1772                (
1773                    storage,
1774                    HashedStorageSorted {
1775                        wiped: false,
1776                        storage_slots: vec![(slot, U256::from(1))],
1777                    },
1778                ),
1779                (empty_storage, HashedStorageSorted::default()),
1780            ]),
1781        );
1782        let newer = HashedPostStateSorted::new(
1783            vec![(address, Some(Account { nonce: 2, ..Default::default() }))],
1784            B256Map::from_iter([(
1785                storage,
1786                HashedStorageSorted { wiped: false, storage_slots: vec![(slot, U256::from(2))] },
1787            )]),
1788        );
1789        let expected = HashedPostStateSorted::merge_batch(vec![newer.clone(), older.clone()]);
1790
1791        let result = HashedPostStateSorted::disjointed_merge_batch(&[&older, &newer], &[]);
1792
1793        assert_eq!(result, expected);
1794    }
1795
1796    #[test]
1797    fn test_hashed_post_state_sorted_disjointed_merge_batch_removes_overlapping_batch_key() {
1798        fn account(nonce: u64) -> Account {
1799            Account { nonce, balance: U256::ZERO, bytecode_hash: None }
1800        }
1801
1802        let overlapping_account = B256::with_last_byte(21);
1803
1804        let older = HashedPostStateSorted::new(
1805            vec![(overlapping_account, Some(account(1)))],
1806            B256Map::default(),
1807        );
1808
1809        let newer = HashedPostStateSorted::new(
1810            vec![(overlapping_account, Some(account(2)))],
1811            B256Map::default(),
1812        );
1813
1814        let remove =
1815            HashedPostStateSorted::new(vec![(overlapping_account, None)], B256Map::default());
1816
1817        let result = HashedPostStateSorted::disjointed_merge_batch(&[&older, &newer], &[&remove]);
1818
1819        assert!(result.accounts.is_empty());
1820    }
1821
1822    #[test]
1823    fn test_hashed_post_state_sorted_disjointed_merge_batch_keeps_equal_overlaps() {
1824        fn account(nonce: u64) -> Account {
1825            Account { nonce, balance: U256::ZERO, bytecode_hash: None }
1826        }
1827
1828        let address = B256::with_last_byte(21);
1829        let deleted_address = B256::with_last_byte(24);
1830        let storage = B256::with_last_byte(22);
1831        let deleted_storage = B256::with_last_byte(25);
1832        let slot = B256::with_last_byte(23);
1833        let deleted_slot = B256::with_last_byte(26);
1834        let batch = HashedPostStateSorted::new(
1835            vec![(address, Some(account(1))), (deleted_address, None)],
1836            B256Map::from_iter([
1837                (
1838                    storage,
1839                    HashedStorageSorted {
1840                        wiped: false,
1841                        storage_slots: vec![(slot, U256::from(1))],
1842                    },
1843                ),
1844                (
1845                    deleted_storage,
1846                    HashedStorageSorted {
1847                        wiped: false,
1848                        storage_slots: vec![(deleted_slot, U256::ZERO)],
1849                    },
1850                ),
1851            ]),
1852        );
1853        let different_mask = HashedPostStateSorted::new(
1854            vec![(address, Some(account(2))), (deleted_address, Some(account(3)))],
1855            B256Map::from_iter([
1856                (
1857                    storage,
1858                    HashedStorageSorted {
1859                        wiped: false,
1860                        storage_slots: vec![(slot, U256::from(2))],
1861                    },
1862                ),
1863                (
1864                    deleted_storage,
1865                    HashedStorageSorted {
1866                        wiped: false,
1867                        storage_slots: vec![(deleted_slot, U256::from(3))],
1868                    },
1869                ),
1870            ]),
1871        );
1872        let equal_mask = batch.clone();
1873
1874        let result = HashedPostStateSorted::disjointed_merge_batch(
1875            &[&batch],
1876            &[&different_mask, &equal_mask],
1877        );
1878        let reversed = HashedPostStateSorted::disjointed_merge_batch(
1879            &[&batch],
1880            &[&equal_mask, &different_mask],
1881        );
1882
1883        assert_eq!(result, batch);
1884        assert_eq!(reversed, result);
1885    }
1886
1887    #[test]
1888    fn test_hashed_post_state_sorted_disjointed_merge_batch_ignores_empty_storage_mask() {
1889        let storage = B256::with_last_byte(31);
1890        let slot = B256::with_last_byte(32);
1891
1892        let batch = HashedPostStateSorted::new(
1893            vec![],
1894            B256Map::from_iter([(
1895                storage,
1896                HashedStorageSorted { wiped: false, storage_slots: vec![(slot, U256::from(1))] },
1897            )]),
1898        );
1899        let mask = HashedPostStateSorted::new(
1900            vec![],
1901            B256Map::from_iter([(
1902                storage,
1903                HashedStorageSorted { wiped: false, storage_slots: vec![] },
1904            )]),
1905        );
1906
1907        let result = HashedPostStateSorted::disjointed_merge_batch(&[&batch], &[&mask]);
1908
1909        assert_eq!(
1910            result.storages.get(&storage),
1911            Some(&HashedStorageSorted { wiped: false, storage_slots: vec![(slot, U256::from(1))] })
1912        );
1913    }
1914
1915    /// Test non-wiped storage merges both zero and non-zero valued slots
1916    #[test]
1917    fn test_hashed_storage_extend_from_sorted_non_wiped() {
1918        let slot1 = B256::random();
1919        let slot2 = B256::random();
1920        let slot3 = B256::random();
1921
1922        let mut storage = HashedStorage::from_iter([(slot1, U256::from(100))]);
1923
1924        let sorted = HashedStorageSorted {
1925            storage_slots: vec![(slot2, U256::from(200)), (slot3, U256::ZERO)],
1926            wiped: false,
1927        };
1928
1929        storage.extend_from_sorted(&sorted);
1930
1931        assert!(!storage.wiped);
1932        assert_eq!(storage.storage.len(), 3);
1933        assert_eq!(storage.storage.get(&slot1), Some(&U256::from(100)));
1934        assert_eq!(storage.storage.get(&slot2), Some(&U256::from(200)));
1935        assert_eq!(storage.storage.get(&slot3), Some(&U256::ZERO));
1936    }
1937
1938    /// Test wiped=true clears existing storage and only keeps new slots (critical edge case)
1939    #[test]
1940    fn test_hashed_storage_extend_from_sorted_wiped() {
1941        let slot1 = B256::random();
1942        let slot2 = B256::random();
1943
1944        let mut storage = HashedStorage::from_iter([(slot1, U256::from(100))]);
1945
1946        let sorted =
1947            HashedStorageSorted { storage_slots: vec![(slot2, U256::from(200))], wiped: true };
1948
1949        storage.extend_from_sorted(&sorted);
1950
1951        assert!(storage.wiped);
1952        // After wipe, old storage should be cleared and only new storage remains
1953        assert_eq!(storage.storage.len(), 1);
1954        assert_eq!(storage.storage.get(&slot2), Some(&U256::from(200)));
1955    }
1956
1957    #[test]
1958    fn test_hashed_post_state_chunking_length() {
1959        let addr1 = B256::from([1; 32]);
1960        let addr2 = B256::from([2; 32]);
1961        let addr3 = B256::from([3; 32]);
1962        let addr4 = B256::from([4; 32]);
1963        let slot1 = B256::from([1; 32]);
1964        let slot2 = B256::from([2; 32]);
1965        let slot3 = B256::from([3; 32]);
1966
1967        let state = HashedPostState {
1968            accounts: B256Map::from_iter([(addr1, None), (addr2, None), (addr4, None)]),
1969            storages: B256Map::from_iter([
1970                (
1971                    addr1,
1972                    HashedStorage {
1973                        wiped: false,
1974                        storage: B256Map::from_iter([
1975                            (slot1, U256::ZERO),
1976                            (slot2, U256::ZERO),
1977                            (slot3, U256::ZERO),
1978                        ]),
1979                    },
1980                ),
1981                (
1982                    addr2,
1983                    HashedStorage {
1984                        wiped: true,
1985                        storage: B256Map::from_iter([
1986                            (slot1, U256::ZERO),
1987                            (slot2, U256::ZERO),
1988                            (slot3, U256::ZERO),
1989                        ]),
1990                    },
1991                ),
1992                (
1993                    addr3,
1994                    HashedStorage {
1995                        wiped: false,
1996                        storage: B256Map::from_iter([
1997                            (slot1, U256::ZERO),
1998                            (slot2, U256::ZERO),
1999                            (slot3, U256::ZERO),
2000                        ]),
2001                    },
2002                ),
2003            ]),
2004        };
2005
2006        let chunking_length = state.chunking_length();
2007        for size in 1..=state.clone().chunks(1).count() {
2008            let chunk_count = state.clone().chunks(size).count();
2009            let expected_count = chunking_length.div_ceil(size);
2010            assert_eq!(
2011                chunk_count, expected_count,
2012                "chunking_length: {}, size: {}",
2013                chunking_length, size
2014            );
2015        }
2016    }
2017
2018    #[test]
2019    fn test_clone_into_sorted_equivalence() {
2020        let addr1 = B256::from([1; 32]);
2021        let addr2 = B256::from([2; 32]);
2022        let addr3 = B256::from([3; 32]);
2023        let slot1 = B256::from([1; 32]);
2024        let slot2 = B256::from([2; 32]);
2025        let slot3 = B256::from([3; 32]);
2026
2027        let state = HashedPostState {
2028            accounts: B256Map::from_iter([
2029                (addr1, Some(Account { nonce: 1, balance: U256::from(100), bytecode_hash: None })),
2030                (addr2, None),
2031                (addr3, Some(Account::default())),
2032            ]),
2033            storages: B256Map::from_iter([
2034                (
2035                    addr1,
2036                    HashedStorage {
2037                        wiped: false,
2038                        storage: B256Map::from_iter([
2039                            (slot1, U256::from(10)),
2040                            (slot2, U256::from(20)),
2041                        ]),
2042                    },
2043                ),
2044                (
2045                    addr2,
2046                    HashedStorage {
2047                        wiped: true,
2048                        storage: B256Map::from_iter([(slot3, U256::ZERO)]),
2049                    },
2050                ),
2051            ]),
2052        };
2053
2054        // clone_into_sorted should produce the same result as clone().into_sorted()
2055        let sorted_via_clone = state.clone().into_sorted();
2056        let sorted_via_clone_into = state.clone_into_sorted();
2057
2058        assert_eq!(sorted_via_clone, sorted_via_clone_into);
2059
2060        // Verify the original state is not consumed
2061        assert_eq!(state.accounts.len(), 3);
2062        assert_eq!(state.storages.len(), 2);
2063    }
2064
2065    #[test]
2066    fn test_hashed_storage_clone_into_sorted_equivalence() {
2067        let slot1 = B256::from([1; 32]);
2068        let slot2 = B256::from([2; 32]);
2069        let slot3 = B256::from([3; 32]);
2070
2071        let storage = HashedStorage {
2072            wiped: true,
2073            storage: B256Map::from_iter([
2074                (slot1, U256::from(100)),
2075                (slot2, U256::ZERO),
2076                (slot3, U256::from(300)),
2077            ]),
2078        };
2079
2080        // clone_into_sorted should produce the same result as clone().into_sorted()
2081        let sorted_via_clone = storage.clone().into_sorted();
2082        let sorted_via_clone_into = storage.clone_into_sorted();
2083
2084        assert_eq!(sorted_via_clone, sorted_via_clone_into);
2085
2086        // Verify the original storage is not consumed
2087        assert_eq!(storage.storage.len(), 3);
2088        assert!(storage.wiped);
2089    }
2090}
2091
2092/// Bincode-compatible hashed state type serde implementations.
2093#[cfg(feature = "serde-bincode-compat")]
2094pub mod serde_bincode_compat {
2095    use super::Account;
2096    use alloc::borrow::Cow;
2097    use alloy_primitives::{map::B256Map, B256, U256};
2098    use serde::{Deserialize, Deserializer, Serialize, Serializer};
2099    use serde_with::{DeserializeAs, SerializeAs};
2100
2101    /// Bincode-compatible [`super::HashedPostState`] serde implementation.
2102    ///
2103    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
2104    /// ```rust
2105    /// use reth_trie_common::{serde_bincode_compat, HashedPostState};
2106    /// use serde::{Deserialize, Serialize};
2107    /// use serde_with::serde_as;
2108    ///
2109    /// #[serde_as]
2110    /// #[derive(Serialize, Deserialize)]
2111    /// struct Data {
2112    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostState")]
2113    ///     hashed_state: HashedPostState,
2114    /// }
2115    /// ```
2116    #[derive(Debug, Serialize, Deserialize)]
2117    pub struct HashedPostState<'a> {
2118        accounts: Cow<'a, B256Map<Option<Account>>>,
2119        storages: B256Map<HashedStorage<'a>>,
2120    }
2121
2122    impl<'a> From<&'a super::HashedPostState> for HashedPostState<'a> {
2123        fn from(value: &'a super::HashedPostState) -> Self {
2124            Self {
2125                accounts: Cow::Borrowed(&value.accounts),
2126                storages: value.storages.iter().map(|(k, v)| (*k, v.into())).collect(),
2127            }
2128        }
2129    }
2130
2131    impl<'a> From<HashedPostState<'a>> for super::HashedPostState {
2132        fn from(value: HashedPostState<'a>) -> Self {
2133            Self {
2134                accounts: value.accounts.into_owned(),
2135                storages: value.storages.into_iter().map(|(k, v)| (k, v.into())).collect(),
2136            }
2137        }
2138    }
2139
2140    impl SerializeAs<super::HashedPostState> for HashedPostState<'_> {
2141        fn serialize_as<S>(
2142            source: &super::HashedPostState,
2143            serializer: S,
2144        ) -> Result<S::Ok, S::Error>
2145        where
2146            S: Serializer,
2147        {
2148            HashedPostState::from(source).serialize(serializer)
2149        }
2150    }
2151
2152    impl<'de> DeserializeAs<'de, super::HashedPostState> for HashedPostState<'de> {
2153        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedPostState, D::Error>
2154        where
2155            D: Deserializer<'de>,
2156        {
2157            HashedPostState::deserialize(deserializer).map(Into::into)
2158        }
2159    }
2160
2161    /// Bincode-compatible [`super::HashedStorage`] serde implementation.
2162    ///
2163    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
2164    /// ```rust
2165    /// use reth_trie_common::{serde_bincode_compat, HashedStorage};
2166    /// use serde::{Deserialize, Serialize};
2167    /// use serde_with::serde_as;
2168    ///
2169    /// #[serde_as]
2170    /// #[derive(Serialize, Deserialize)]
2171    /// struct Data {
2172    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorage")]
2173    ///     hashed_storage: HashedStorage,
2174    /// }
2175    /// ```
2176    #[derive(Debug, Serialize, Deserialize)]
2177    pub struct HashedStorage<'a> {
2178        wiped: bool,
2179        storage: Cow<'a, B256Map<U256>>,
2180    }
2181
2182    impl<'a> From<&'a super::HashedStorage> for HashedStorage<'a> {
2183        fn from(value: &'a super::HashedStorage) -> Self {
2184            Self { wiped: value.wiped, storage: Cow::Borrowed(&value.storage) }
2185        }
2186    }
2187
2188    impl<'a> From<HashedStorage<'a>> for super::HashedStorage {
2189        fn from(value: HashedStorage<'a>) -> Self {
2190            Self { wiped: value.wiped, storage: value.storage.into_owned() }
2191        }
2192    }
2193
2194    impl SerializeAs<super::HashedStorage> for HashedStorage<'_> {
2195        fn serialize_as<S>(source: &super::HashedStorage, serializer: S) -> Result<S::Ok, S::Error>
2196        where
2197            S: Serializer,
2198        {
2199            HashedStorage::from(source).serialize(serializer)
2200        }
2201    }
2202
2203    impl<'de> DeserializeAs<'de, super::HashedStorage> for HashedStorage<'de> {
2204        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedStorage, D::Error>
2205        where
2206            D: Deserializer<'de>,
2207        {
2208            HashedStorage::deserialize(deserializer).map(Into::into)
2209        }
2210    }
2211
2212    /// Bincode-compatible [`super::HashedPostStateSorted`] serde implementation.
2213    ///
2214    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
2215    /// ```rust
2216    /// use reth_trie_common::{serde_bincode_compat, HashedPostStateSorted};
2217    /// use serde::{Deserialize, Serialize};
2218    /// use serde_with::serde_as;
2219    ///
2220    /// #[serde_as]
2221    /// #[derive(Serialize, Deserialize)]
2222    /// struct Data {
2223    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostStateSorted")]
2224    ///     hashed_state: HashedPostStateSorted,
2225    /// }
2226    /// ```
2227    #[derive(Debug, Serialize, Deserialize)]
2228    pub struct HashedPostStateSorted<'a> {
2229        accounts: Cow<'a, [(B256, Option<Account>)]>,
2230        storages: B256Map<HashedStorageSorted<'a>>,
2231    }
2232
2233    impl<'a> From<&'a super::HashedPostStateSorted> for HashedPostStateSorted<'a> {
2234        fn from(value: &'a super::HashedPostStateSorted) -> Self {
2235            Self {
2236                accounts: Cow::Borrowed(&value.accounts),
2237                storages: value.storages.iter().map(|(k, v)| (*k, v.into())).collect(),
2238            }
2239        }
2240    }
2241
2242    impl<'a> From<HashedPostStateSorted<'a>> for super::HashedPostStateSorted {
2243        fn from(value: HashedPostStateSorted<'a>) -> Self {
2244            Self {
2245                accounts: value.accounts.into_owned(),
2246                storages: value.storages.into_iter().map(|(k, v)| (k, v.into())).collect(),
2247            }
2248        }
2249    }
2250
2251    impl SerializeAs<super::HashedPostStateSorted> for HashedPostStateSorted<'_> {
2252        fn serialize_as<S>(
2253            source: &super::HashedPostStateSorted,
2254            serializer: S,
2255        ) -> Result<S::Ok, S::Error>
2256        where
2257            S: Serializer,
2258        {
2259            HashedPostStateSorted::from(source).serialize(serializer)
2260        }
2261    }
2262
2263    impl<'de> DeserializeAs<'de, super::HashedPostStateSorted> for HashedPostStateSorted<'de> {
2264        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedPostStateSorted, D::Error>
2265        where
2266            D: Deserializer<'de>,
2267        {
2268            HashedPostStateSorted::deserialize(deserializer).map(Into::into)
2269        }
2270    }
2271
2272    /// Bincode-compatible [`super::HashedStorageSorted`] serde implementation.
2273    ///
2274    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
2275    /// ```rust
2276    /// use reth_trie_common::{serde_bincode_compat, HashedStorageSorted};
2277    /// use serde::{Deserialize, Serialize};
2278    /// use serde_with::serde_as;
2279    ///
2280    /// #[serde_as]
2281    /// #[derive(Serialize, Deserialize)]
2282    /// struct Data {
2283    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorageSorted")]
2284    ///     hashed_storage: HashedStorageSorted,
2285    /// }
2286    /// ```
2287    #[derive(Debug, Serialize, Deserialize)]
2288    pub struct HashedStorageSorted<'a> {
2289        storage_slots: Cow<'a, [(B256, U256)]>,
2290        wiped: bool,
2291    }
2292
2293    impl<'a> From<&'a super::HashedStorageSorted> for HashedStorageSorted<'a> {
2294        fn from(value: &'a super::HashedStorageSorted) -> Self {
2295            Self { storage_slots: Cow::Borrowed(&value.storage_slots), wiped: value.wiped }
2296        }
2297    }
2298
2299    impl<'a> From<HashedStorageSorted<'a>> for super::HashedStorageSorted {
2300        fn from(value: HashedStorageSorted<'a>) -> Self {
2301            Self { storage_slots: value.storage_slots.into_owned(), wiped: value.wiped }
2302        }
2303    }
2304
2305    impl SerializeAs<super::HashedStorageSorted> for HashedStorageSorted<'_> {
2306        fn serialize_as<S>(
2307            source: &super::HashedStorageSorted,
2308            serializer: S,
2309        ) -> Result<S::Ok, S::Error>
2310        where
2311            S: Serializer,
2312        {
2313            HashedStorageSorted::from(source).serialize(serializer)
2314        }
2315    }
2316
2317    impl<'de> DeserializeAs<'de, super::HashedStorageSorted> for HashedStorageSorted<'de> {
2318        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedStorageSorted, D::Error>
2319        where
2320            D: Deserializer<'de>,
2321        {
2322            HashedStorageSorted::deserialize(deserializer).map(Into::into)
2323        }
2324    }
2325
2326    #[cfg(test)]
2327    mod tests {
2328        use crate::{
2329            hashed_state::{
2330                HashedPostState, HashedPostStateSorted, HashedStorage, HashedStorageSorted,
2331            },
2332            serde_bincode_compat,
2333        };
2334        use alloy_primitives::{B256, U256};
2335        use reth_primitives_traits::Account;
2336        use serde::{Deserialize, Serialize};
2337        use serde_with::serde_as;
2338
2339        #[test]
2340        fn test_hashed_post_state_bincode_roundtrip() {
2341            #[serde_as]
2342            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
2343            struct Data {
2344                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostState")]
2345                hashed_state: HashedPostState,
2346            }
2347
2348            let mut data = Data { hashed_state: HashedPostState::default() };
2349            let encoded = bincode::serialize(&data).unwrap();
2350            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2351            assert_eq!(decoded, data);
2352
2353            data.hashed_state.accounts.insert(B256::random(), Some(Account::default()));
2354            let encoded = bincode::serialize(&data).unwrap();
2355            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2356            assert_eq!(decoded, data);
2357
2358            data.hashed_state.storages.insert(B256::random(), HashedStorage::default());
2359            let encoded = bincode::serialize(&data).unwrap();
2360            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2361            assert_eq!(decoded, data);
2362        }
2363
2364        #[test]
2365        fn test_hashed_storage_bincode_roundtrip() {
2366            #[serde_as]
2367            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
2368            struct Data {
2369                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorage")]
2370                hashed_storage: HashedStorage,
2371            }
2372
2373            let mut data = Data { hashed_storage: HashedStorage::default() };
2374            let encoded = bincode::serialize(&data).unwrap();
2375            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2376            assert_eq!(decoded, data);
2377
2378            data.hashed_storage.wiped = true;
2379            let encoded = bincode::serialize(&data).unwrap();
2380            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2381            assert_eq!(decoded, data);
2382
2383            data.hashed_storage.storage.insert(B256::random(), U256::from(1));
2384            let encoded = bincode::serialize(&data).unwrap();
2385            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2386            assert_eq!(decoded, data);
2387        }
2388
2389        #[test]
2390        fn test_hashed_post_state_sorted_bincode_roundtrip() {
2391            #[serde_as]
2392            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
2393            struct Data {
2394                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostStateSorted")]
2395                hashed_state: HashedPostStateSorted,
2396            }
2397
2398            let mut data = Data { hashed_state: HashedPostStateSorted::default() };
2399            let encoded = bincode::serialize(&data).unwrap();
2400            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2401            assert_eq!(decoded, data);
2402
2403            data.hashed_state.accounts.push((B256::random(), Some(Account::default())));
2404            data.hashed_state
2405                .accounts
2406                .push((B256::random(), Some(Account { nonce: 1, ..Default::default() })));
2407            let encoded = bincode::serialize(&data).unwrap();
2408            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2409            assert_eq!(decoded, data);
2410
2411            data.hashed_state.storages.insert(
2412                B256::random(),
2413                HashedStorageSorted {
2414                    storage_slots: vec![(B256::from([1; 32]), U256::from(10))],
2415                    wiped: false,
2416                },
2417            );
2418            let encoded = bincode::serialize(&data).unwrap();
2419            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2420            assert_eq!(decoded, data);
2421        }
2422
2423        #[test]
2424        fn test_hashed_storage_sorted_bincode_roundtrip() {
2425            #[serde_as]
2426            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
2427            struct Data {
2428                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorageSorted")]
2429                hashed_storage: HashedStorageSorted,
2430            }
2431
2432            let mut data = Data {
2433                hashed_storage: HashedStorageSorted { storage_slots: Vec::new(), wiped: false },
2434            };
2435            let encoded = bincode::serialize(&data).unwrap();
2436            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2437            assert_eq!(decoded, data);
2438
2439            data.hashed_storage.wiped = true;
2440            let encoded = bincode::serialize(&data).unwrap();
2441            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2442            assert_eq!(decoded, data);
2443
2444            data.hashed_storage.storage_slots.push((B256::random(), U256::from(1)));
2445            let encoded = bincode::serialize(&data).unwrap();
2446            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2447            assert_eq!(decoded, data);
2448        }
2449    }
2450}