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