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_database::{states::StorageSlot, StorageWithOriginalValues};
910    use revm_state::{AccountInfo, Bytecode};
911
912    #[test]
913    fn hashed_state_wiped_extension() {
914        let hashed_address = B256::default();
915        let hashed_slot = B256::with_last_byte(64);
916        let hashed_slot2 = B256::with_last_byte(65);
917
918        // Initialize post state storage
919        let original_slot_value = U256::from(123);
920        let mut hashed_state = HashedPostState::default().with_storages([(
921            hashed_address,
922            HashedStorage::from_iter(
923                false,
924                [(hashed_slot, original_slot_value), (hashed_slot2, original_slot_value)],
925            ),
926        )]);
927
928        // Update single slot value
929        let updated_slot_value = U256::from(321);
930        let extension = HashedPostState::default().with_storages([(
931            hashed_address,
932            HashedStorage::from_iter(false, [(hashed_slot, updated_slot_value)]),
933        )]);
934        hashed_state.extend(extension);
935
936        let account_storage = hashed_state.storages.get(&hashed_address);
937        assert_eq!(
938            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
939            Some(&updated_slot_value)
940        );
941        assert_eq!(
942            account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
943            Some(&original_slot_value)
944        );
945        assert_eq!(account_storage.map(|st| st.wiped), Some(false));
946
947        // Wipe account storage
948        let wiped_extension =
949            HashedPostState::default().with_storages([(hashed_address, HashedStorage::new(true))]);
950        hashed_state.extend(wiped_extension);
951
952        let account_storage = hashed_state.storages.get(&hashed_address);
953        assert_eq!(account_storage.map(|st| st.storage.is_empty()), Some(true));
954        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
955
956        // Reinitialize single slot value
957        hashed_state.extend(HashedPostState::default().with_storages([(
958            hashed_address,
959            HashedStorage::from_iter(false, [(hashed_slot, original_slot_value)]),
960        )]));
961        let account_storage = hashed_state.storages.get(&hashed_address);
962        assert_eq!(
963            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
964            Some(&original_slot_value)
965        );
966        assert_eq!(account_storage.and_then(|st| st.storage.get(&hashed_slot2)), None);
967        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
968
969        // Reinitialize single slot value
970        hashed_state.extend(HashedPostState::default().with_storages([(
971            hashed_address,
972            HashedStorage::from_iter(false, [(hashed_slot2, updated_slot_value)]),
973        )]));
974        let account_storage = hashed_state.storages.get(&hashed_address);
975        assert_eq!(
976            account_storage.and_then(|st| st.storage.get(&hashed_slot)),
977            Some(&original_slot_value)
978        );
979        assert_eq!(
980            account_storage.and_then(|st| st.storage.get(&hashed_slot2)),
981            Some(&updated_slot_value)
982        );
983        assert_eq!(account_storage.map(|st| st.wiped), Some(true));
984    }
985
986    #[test]
987    fn test_hashed_post_state_from_bundle_state() {
988        // Prepare a random Ethereum address as a key for the account.
989        let address = Address::random();
990
991        // Create a mock account info object.
992        let account_info = AccountInfo {
993            balance: U256::from(123),
994            nonce: 42,
995            code_hash: B256::random(),
996            code: Some(Bytecode::new_raw(Bytes::from(vec![1, 2]))),
997            account_id: None,
998        };
999
1000        let mut storage = StorageWithOriginalValues::default();
1001        storage.insert(
1002            U256::from(1),
1003            StorageSlot { present_value: U256::from(4), ..Default::default() },
1004        );
1005
1006        // Create a `BundleAccount` struct to represent the account and its storage.
1007        let account = BundleAccount {
1008            status: AccountStatus::Changed,
1009            info: Some(account_info.clone()),
1010            storage,
1011            original_info: None,
1012        };
1013
1014        // Create a vector of tuples representing the bundle state.
1015        let state = vec![(&address, &account)];
1016
1017        // Convert the bundle state into a hashed post state.
1018        let hashed_state = HashedPostState::from_bundle_state::<KeccakKeyHasher>(state);
1019
1020        // Validate the hashed post state.
1021        assert_eq!(hashed_state.accounts.len(), 1);
1022        assert_eq!(hashed_state.storages.len(), 1);
1023
1024        // Validate the account info.
1025        assert_eq!(
1026            *hashed_state.accounts.get(&keccak256(address)).unwrap(),
1027            Some(account_info.into())
1028        );
1029    }
1030
1031    #[test]
1032    fn test_hashed_post_state_with_accounts() {
1033        // Prepare random addresses and mock account info.
1034        let address_1 = Address::random();
1035        let address_2 = Address::random();
1036
1037        let account_info_1 = AccountInfo {
1038            balance: U256::from(1000),
1039            nonce: 1,
1040            code_hash: B256::random(),
1041            code: None,
1042            account_id: None,
1043        };
1044
1045        // Create hashed accounts with addresses.
1046        let account_1 = (keccak256(address_1), Some(account_info_1.into()));
1047        let account_2 = (keccak256(address_2), None);
1048
1049        // Add accounts to the hashed post state.
1050        let hashed_state = HashedPostState::default().with_accounts(vec![account_1, account_2]);
1051
1052        // Validate the hashed post state.
1053        assert_eq!(hashed_state.accounts.len(), 2);
1054        assert!(hashed_state.accounts.contains_key(&keccak256(address_1)));
1055        assert!(hashed_state.accounts.contains_key(&keccak256(address_2)));
1056    }
1057
1058    #[test]
1059    fn test_hashed_post_state_with_storages() {
1060        // Prepare random addresses and mock storage entries.
1061        let address_1 = Address::random();
1062        let address_2 = Address::random();
1063
1064        let storage_1 = (keccak256(address_1), HashedStorage::new(false));
1065        let storage_2 = (keccak256(address_2), HashedStorage::new(true));
1066
1067        // Add storages to the hashed post state.
1068        let hashed_state = HashedPostState::default().with_storages(vec![storage_1, storage_2]);
1069
1070        // Validate the hashed post state.
1071        assert_eq!(hashed_state.storages.len(), 2);
1072        assert!(hashed_state.storages.contains_key(&keccak256(address_1)));
1073        assert!(hashed_state.storages.contains_key(&keccak256(address_2)));
1074    }
1075
1076    #[test]
1077    fn test_hashed_post_state_is_empty() {
1078        // Create an empty hashed post state and validate it's empty.
1079        let empty_state = HashedPostState::default();
1080        assert!(empty_state.is_empty());
1081
1082        // Add an account and validate the state is no longer empty.
1083        let non_empty_state = HashedPostState::default()
1084            .with_accounts(vec![(keccak256(Address::random()), Some(Account::default()))]);
1085        assert!(!non_empty_state.is_empty());
1086    }
1087
1088    fn create_state_for_multi_proof_targets() -> HashedPostState {
1089        let mut state = HashedPostState::default();
1090
1091        let addr1 = B256::random();
1092        let addr2 = B256::random();
1093        state.accounts.insert(addr1, Some(Default::default()));
1094        state.accounts.insert(addr2, Some(Default::default()));
1095
1096        let mut storage = HashedStorage::default();
1097        let slot1 = B256::random();
1098        let slot2 = B256::random();
1099        storage.storage.insert(slot1, U256::ZERO);
1100        storage.storage.insert(slot2, U256::from(1));
1101        state.storages.insert(addr1, storage);
1102
1103        state
1104    }
1105
1106    #[test]
1107    fn test_multi_proof_targets_difference_empty_state() {
1108        let state = HashedPostState::default();
1109        let excluded = MultiProofTargets::default();
1110
1111        let targets = state.multi_proof_targets_difference(&excluded);
1112        assert!(targets.is_empty());
1113    }
1114
1115    #[test]
1116    fn test_multi_proof_targets_difference_new_account_targets() {
1117        let state = create_state_for_multi_proof_targets();
1118        let excluded = MultiProofTargets::default();
1119
1120        // should return all accounts as targets since excluded is empty
1121        let targets = state.multi_proof_targets_difference(&excluded);
1122        assert_eq!(targets.len(), state.accounts.len());
1123        for addr in state.accounts.keys() {
1124            assert!(targets.contains_key(addr));
1125        }
1126    }
1127
1128    #[test]
1129    fn test_multi_proof_targets_difference_new_storage_targets() {
1130        let state = create_state_for_multi_proof_targets();
1131        let excluded = MultiProofTargets::default();
1132
1133        let targets = state.multi_proof_targets_difference(&excluded);
1134
1135        // verify storage slots are included for accounts with storage
1136        for (addr, storage) in &state.storages {
1137            assert!(targets.contains_key(addr));
1138            let target_slots = &targets[addr];
1139            assert_eq!(target_slots.len(), storage.storage.len());
1140            for slot in storage.storage.keys() {
1141                assert!(target_slots.contains(slot));
1142            }
1143        }
1144    }
1145
1146    #[test]
1147    fn test_multi_proof_targets_difference_filter_excluded_accounts() {
1148        let state = create_state_for_multi_proof_targets();
1149        let mut excluded = MultiProofTargets::default();
1150
1151        // select an account that has no storage updates
1152        let excluded_addr = state
1153            .accounts
1154            .keys()
1155            .find(|&&addr| !state.storages.contains_key(&addr))
1156            .expect("Should have an account without storage");
1157
1158        // mark the account as excluded
1159        excluded.insert(*excluded_addr, HashSet::default());
1160
1161        let targets = state.multi_proof_targets_difference(&excluded);
1162
1163        // should not include the already excluded account since it has no storage updates
1164        assert!(!targets.contains_key(excluded_addr));
1165        // other accounts should still be included
1166        assert_eq!(targets.len(), state.accounts.len() - 1);
1167    }
1168
1169    #[test]
1170    fn test_multi_proof_targets_difference_filter_excluded_storage() {
1171        let state = create_state_for_multi_proof_targets();
1172        let mut excluded = MultiProofTargets::default();
1173
1174        // mark one storage slot as excluded
1175        let (addr, storage) = state.storages.iter().next().unwrap();
1176        let mut excluded_slots = HashSet::default();
1177        let excluded_slot = *storage.storage.keys().next().unwrap();
1178        excluded_slots.insert(excluded_slot);
1179        excluded.insert(*addr, excluded_slots);
1180
1181        let targets = state.multi_proof_targets_difference(&excluded);
1182
1183        // should not include the excluded storage slot
1184        let target_slots = &targets[addr];
1185        assert!(!target_slots.contains(&excluded_slot));
1186        assert_eq!(target_slots.len(), storage.storage.len() - 1);
1187    }
1188
1189    #[test]
1190    fn test_multi_proof_targets_difference_mixed_excluded_state() {
1191        let mut state = HashedPostState::default();
1192        let mut excluded = MultiProofTargets::default();
1193
1194        let addr1 = B256::random();
1195        let addr2 = B256::random();
1196        let slot1 = B256::random();
1197        let slot2 = B256::random();
1198
1199        state.accounts.insert(addr1, Some(Default::default()));
1200        state.accounts.insert(addr2, Some(Default::default()));
1201
1202        let mut storage = HashedStorage::default();
1203        storage.storage.insert(slot1, U256::ZERO);
1204        storage.storage.insert(slot2, U256::from(1));
1205        state.storages.insert(addr1, storage);
1206
1207        let mut excluded_slots = HashSet::default();
1208        excluded_slots.insert(slot1);
1209        excluded.insert(addr1, excluded_slots);
1210
1211        let targets = state.multi_proof_targets_difference(&excluded);
1212
1213        assert!(targets.contains_key(&addr2));
1214        assert!(!targets[&addr1].contains(&slot1));
1215        assert!(targets[&addr1].contains(&slot2));
1216    }
1217
1218    #[test]
1219    fn test_multi_proof_targets_difference_unmodified_account_with_storage() {
1220        let mut state = HashedPostState::default();
1221        let excluded = MultiProofTargets::default();
1222
1223        let addr = B256::random();
1224        let slot1 = B256::random();
1225        let slot2 = B256::random();
1226
1227        // don't add the account to state.accounts (simulating unmodified account)
1228        // but add storage updates for this account
1229        let mut storage = HashedStorage::default();
1230        storage.storage.insert(slot1, U256::from(1));
1231        storage.storage.insert(slot2, U256::from(2));
1232        state.storages.insert(addr, storage);
1233
1234        assert!(!state.accounts.contains_key(&addr));
1235        assert!(!excluded.contains_key(&addr));
1236
1237        let targets = state.multi_proof_targets_difference(&excluded);
1238
1239        // verify that we still get the storage slots for the unmodified account
1240        assert!(targets.contains_key(&addr));
1241
1242        let target_slots = &targets[&addr];
1243        assert_eq!(target_slots.len(), 2);
1244        assert!(target_slots.contains(&slot1));
1245        assert!(target_slots.contains(&slot2));
1246    }
1247
1248    #[test]
1249    fn test_partition_by_targets() {
1250        let addr1 = B256::random();
1251        let addr2 = B256::random();
1252        let slot1 = B256::random();
1253        let slot2 = B256::random();
1254
1255        let state = HashedPostState {
1256            accounts: B256Map::from_iter([
1257                (addr1, Some(Default::default())),
1258                (addr2, Some(Default::default())),
1259            ]),
1260            storages: B256Map::from_iter([(
1261                addr1,
1262                HashedStorage {
1263                    wiped: true,
1264                    storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1265                },
1266            )]),
1267        };
1268        let targets = MultiProofTargets::from_iter([(addr1, HashSet::from_iter([slot1]))]);
1269
1270        let (with_targets, without_targets) =
1271            state.partition_by_targets(&targets, &MultiAddedRemovedKeys::new());
1272
1273        assert_eq!(
1274            with_targets,
1275            HashedPostState {
1276                accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
1277                storages: B256Map::from_iter([(
1278                    addr1,
1279                    HashedStorage {
1280                        wiped: true,
1281                        storage: B256Map::from_iter([(slot1, U256::ZERO)])
1282                    }
1283                )]),
1284            }
1285        );
1286        assert_eq!(
1287            without_targets,
1288            HashedPostState {
1289                accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
1290                storages: B256Map::from_iter([(
1291                    addr1,
1292                    HashedStorage {
1293                        wiped: false,
1294                        storage: B256Map::from_iter([(slot2, U256::from(1))])
1295                    }
1296                )]),
1297            }
1298        );
1299    }
1300
1301    #[test]
1302    fn test_chunks() {
1303        let addr1 = B256::from([1; 32]);
1304        let addr2 = B256::from([2; 32]);
1305        let slot1 = B256::from([1; 32]);
1306        let slot2 = B256::from([2; 32]);
1307
1308        let state = HashedPostState {
1309            accounts: B256Map::from_iter([
1310                (addr1, Some(Default::default())),
1311                (addr2, Some(Default::default())),
1312            ]),
1313            storages: B256Map::from_iter([(
1314                addr2,
1315                HashedStorage {
1316                    wiped: true,
1317                    storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1318                },
1319            )]),
1320        };
1321
1322        let mut chunks = state.chunks(2);
1323        assert_eq!(
1324            chunks.next(),
1325            Some(HashedPostState {
1326                accounts: B256Map::from_iter([(addr1, Some(Default::default()))]),
1327                storages: B256Map::from_iter([(addr2, HashedStorage::new(true)),])
1328            })
1329        );
1330        assert_eq!(
1331            chunks.next(),
1332            Some(HashedPostState {
1333                accounts: B256Map::default(),
1334                storages: B256Map::from_iter([(
1335                    addr2,
1336                    HashedStorage {
1337                        wiped: false,
1338                        storage: B256Map::from_iter([(slot1, U256::ZERO), (slot2, U256::from(1))]),
1339                    },
1340                )])
1341            })
1342        );
1343        assert_eq!(
1344            chunks.next(),
1345            Some(HashedPostState {
1346                accounts: B256Map::from_iter([(addr2, Some(Default::default()))]),
1347                storages: B256Map::default()
1348            })
1349        );
1350        assert_eq!(chunks.next(), None);
1351    }
1352
1353    #[test]
1354    fn test_chunks_ordering_guarantee() {
1355        // Test that chunks preserve the ordering: wipe -> storage updates -> account
1356        // Use chunk size of 1 to verify each item comes out in the correct order
1357        let addr = B256::from([1; 32]);
1358        let slot1 = B256::from([1; 32]);
1359        let slot2 = B256::from([2; 32]);
1360
1361        let state = HashedPostState {
1362            accounts: B256Map::from_iter([(addr, Some(Default::default()))]),
1363            storages: B256Map::from_iter([(
1364                addr,
1365                HashedStorage {
1366                    wiped: true,
1367                    storage: B256Map::from_iter([(slot1, U256::from(1)), (slot2, U256::from(2))]),
1368                },
1369            )]),
1370        };
1371
1372        let chunks: Vec<_> = state.chunks(1).collect();
1373
1374        // Should have 4 chunks: 1 wipe + 2 storage updates + 1 account
1375        assert_eq!(chunks.len(), 4);
1376
1377        // First chunk must be the storage wipe
1378        assert!(chunks[0].accounts.is_empty());
1379        assert_eq!(chunks[0].storages.len(), 1);
1380        assert!(chunks[0].storages.get(&addr).unwrap().wiped);
1381        assert!(chunks[0].storages.get(&addr).unwrap().storage.is_empty());
1382
1383        // Next two chunks must be storage updates (order between them doesn't matter)
1384        assert!(chunks[1].accounts.is_empty());
1385        assert!(!chunks[1].storages.get(&addr).unwrap().wiped);
1386        assert_eq!(chunks[1].storages.get(&addr).unwrap().storage.len(), 1);
1387
1388        assert!(chunks[2].accounts.is_empty());
1389        assert!(!chunks[2].storages.get(&addr).unwrap().wiped);
1390        assert_eq!(chunks[2].storages.get(&addr).unwrap().storage.len(), 1);
1391
1392        // Last chunk must be the account update
1393        assert_eq!(chunks[3].accounts.len(), 1);
1394        assert!(chunks[3].accounts.contains_key(&addr));
1395        assert!(chunks[3].storages.is_empty());
1396    }
1397
1398    #[test]
1399    fn test_hashed_post_state_sorted_extend_ref() {
1400        // Test extending accounts
1401        let mut state1 = HashedPostStateSorted {
1402            accounts: vec![
1403                (B256::from([1; 32]), Some(Account::default())),
1404                (B256::from([3; 32]), Some(Account::default())),
1405                (B256::from([5; 32]), None),
1406            ],
1407            storages: B256Map::default(),
1408        };
1409
1410        let state2 = HashedPostStateSorted {
1411            accounts: vec![
1412                (B256::from([2; 32]), Some(Account::default())),
1413                (B256::from([3; 32]), Some(Account { nonce: 1, ..Default::default() })), /* Override */
1414                (B256::from([4; 32]), Some(Account::default())),
1415                (B256::from([6; 32]), None),
1416            ],
1417            storages: B256Map::default(),
1418        };
1419
1420        state1.extend_ref_and_sort(&state2);
1421
1422        // Check accounts are merged and sorted
1423        assert_eq!(state1.accounts.len(), 6);
1424        assert_eq!(state1.accounts[0].0, B256::from([1; 32]));
1425        assert_eq!(state1.accounts[1].0, B256::from([2; 32]));
1426        assert_eq!(state1.accounts[2].0, B256::from([3; 32]));
1427        assert_eq!(state1.accounts[2].1.unwrap().nonce, 1); // Should have state2's value
1428        assert_eq!(state1.accounts[3].0, B256::from([4; 32]));
1429        assert_eq!(state1.accounts[4].0, B256::from([5; 32]));
1430        assert_eq!(state1.accounts[4].1, None);
1431        assert_eq!(state1.accounts[5].0, B256::from([6; 32]));
1432        assert_eq!(state1.accounts[5].1, None);
1433    }
1434
1435    #[test]
1436    fn test_hashed_storage_sorted_extend_ref() {
1437        // Test normal extension
1438        let mut storage1 = HashedStorageSorted {
1439            storage_slots: vec![
1440                (B256::from([1; 32]), U256::from(10)),
1441                (B256::from([3; 32]), U256::from(30)),
1442                (B256::from([5; 32]), U256::ZERO),
1443            ],
1444            wiped: false,
1445        };
1446
1447        let storage2 = HashedStorageSorted {
1448            storage_slots: vec![
1449                (B256::from([2; 32]), U256::from(20)),
1450                (B256::from([3; 32]), U256::from(300)), // Override
1451                (B256::from([4; 32]), U256::from(40)),
1452                (B256::from([6; 32]), U256::ZERO),
1453            ],
1454            wiped: false,
1455        };
1456
1457        storage1.extend_ref(&storage2);
1458
1459        assert_eq!(storage1.storage_slots.len(), 6);
1460        assert_eq!(storage1.storage_slots[0].0, B256::from([1; 32]));
1461        assert_eq!(storage1.storage_slots[0].1, U256::from(10));
1462        assert_eq!(storage1.storage_slots[1].0, B256::from([2; 32]));
1463        assert_eq!(storage1.storage_slots[1].1, U256::from(20));
1464        assert_eq!(storage1.storage_slots[2].0, B256::from([3; 32]));
1465        assert_eq!(storage1.storage_slots[2].1, U256::from(300)); // Should have storage2's value
1466        assert_eq!(storage1.storage_slots[3].0, B256::from([4; 32]));
1467        assert_eq!(storage1.storage_slots[3].1, U256::from(40));
1468        assert_eq!(storage1.storage_slots[4].0, B256::from([5; 32]));
1469        assert_eq!(storage1.storage_slots[4].1, U256::ZERO);
1470        assert_eq!(storage1.storage_slots[5].0, B256::from([6; 32]));
1471        assert_eq!(storage1.storage_slots[5].1, U256::ZERO);
1472        assert!(!storage1.wiped);
1473
1474        // Test wiped storage
1475        let mut storage3 = HashedStorageSorted {
1476            storage_slots: vec![
1477                (B256::from([1; 32]), U256::from(10)),
1478                (B256::from([2; 32]), U256::ZERO),
1479            ],
1480            wiped: false,
1481        };
1482
1483        let storage4 = HashedStorageSorted {
1484            storage_slots: vec![
1485                (B256::from([3; 32]), U256::from(30)),
1486                (B256::from([4; 32]), U256::ZERO),
1487            ],
1488            wiped: true,
1489        };
1490
1491        storage3.extend_ref(&storage4);
1492
1493        assert!(storage3.wiped);
1494        // When wiped, should only have storage4's values
1495        assert_eq!(storage3.storage_slots.len(), 2);
1496        assert_eq!(storage3.storage_slots[0].0, B256::from([3; 32]));
1497        assert_eq!(storage3.storage_slots[0].1, U256::from(30));
1498        assert_eq!(storage3.storage_slots[1].0, B256::from([4; 32]));
1499        assert_eq!(storage3.storage_slots[1].1, U256::ZERO);
1500    }
1501
1502    /// Test extending with sorted accounts merges correctly into `HashMap`
1503    #[test]
1504    fn test_hashed_post_state_extend_from_sorted_with_accounts() {
1505        let addr1 = B256::random();
1506        let addr2 = B256::random();
1507
1508        let mut state = HashedPostState::default();
1509        state.accounts.insert(addr1, Some(Default::default()));
1510
1511        let mut sorted_state = HashedPostStateSorted::default();
1512        sorted_state.accounts.push((addr2, Some(Default::default())));
1513
1514        state.extend_from_sorted(&sorted_state);
1515
1516        assert_eq!(state.accounts.len(), 2);
1517        assert!(state.accounts.contains_key(&addr1));
1518        assert!(state.accounts.contains_key(&addr2));
1519    }
1520
1521    /// Test destroyed accounts (None values) are inserted correctly
1522    #[test]
1523    fn test_hashed_post_state_extend_from_sorted_with_destroyed_accounts() {
1524        let addr1 = B256::random();
1525
1526        let mut state = HashedPostState::default();
1527
1528        let mut sorted_state = HashedPostStateSorted::default();
1529        sorted_state.accounts.push((addr1, None));
1530
1531        state.extend_from_sorted(&sorted_state);
1532
1533        assert!(state.accounts.contains_key(&addr1));
1534        assert_eq!(state.accounts.get(&addr1), Some(&None));
1535    }
1536
1537    /// Test non-wiped storage merges both zero and non-zero valued slots
1538    #[test]
1539    fn test_hashed_storage_extend_from_sorted_non_wiped() {
1540        let slot1 = B256::random();
1541        let slot2 = B256::random();
1542        let slot3 = B256::random();
1543
1544        let mut storage = HashedStorage::from_iter(false, [(slot1, U256::from(100))]);
1545
1546        let sorted = HashedStorageSorted {
1547            storage_slots: vec![(slot2, U256::from(200)), (slot3, U256::ZERO)],
1548            wiped: false,
1549        };
1550
1551        storage.extend_from_sorted(&sorted);
1552
1553        assert!(!storage.wiped);
1554        assert_eq!(storage.storage.len(), 3);
1555        assert_eq!(storage.storage.get(&slot1), Some(&U256::from(100)));
1556        assert_eq!(storage.storage.get(&slot2), Some(&U256::from(200)));
1557        assert_eq!(storage.storage.get(&slot3), Some(&U256::ZERO));
1558    }
1559
1560    /// Test wiped=true clears existing storage and only keeps new slots (critical edge case)
1561    #[test]
1562    fn test_hashed_storage_extend_from_sorted_wiped() {
1563        let slot1 = B256::random();
1564        let slot2 = B256::random();
1565
1566        let mut storage = HashedStorage::from_iter(false, [(slot1, U256::from(100))]);
1567
1568        let sorted =
1569            HashedStorageSorted { storage_slots: vec![(slot2, U256::from(200))], wiped: true };
1570
1571        storage.extend_from_sorted(&sorted);
1572
1573        assert!(storage.wiped);
1574        // After wipe, old storage should be cleared and only new storage remains
1575        assert_eq!(storage.storage.len(), 1);
1576        assert_eq!(storage.storage.get(&slot2), Some(&U256::from(200)));
1577    }
1578
1579    #[test]
1580    fn test_hashed_post_state_chunking_length() {
1581        let addr1 = B256::from([1; 32]);
1582        let addr2 = B256::from([2; 32]);
1583        let addr3 = B256::from([3; 32]);
1584        let addr4 = B256::from([4; 32]);
1585        let slot1 = B256::from([1; 32]);
1586        let slot2 = B256::from([2; 32]);
1587        let slot3 = B256::from([3; 32]);
1588
1589        let state = HashedPostState {
1590            accounts: B256Map::from_iter([(addr1, None), (addr2, None), (addr4, None)]),
1591            storages: B256Map::from_iter([
1592                (
1593                    addr1,
1594                    HashedStorage {
1595                        wiped: false,
1596                        storage: B256Map::from_iter([
1597                            (slot1, U256::ZERO),
1598                            (slot2, U256::ZERO),
1599                            (slot3, U256::ZERO),
1600                        ]),
1601                    },
1602                ),
1603                (
1604                    addr2,
1605                    HashedStorage {
1606                        wiped: true,
1607                        storage: B256Map::from_iter([
1608                            (slot1, U256::ZERO),
1609                            (slot2, U256::ZERO),
1610                            (slot3, U256::ZERO),
1611                        ]),
1612                    },
1613                ),
1614                (
1615                    addr3,
1616                    HashedStorage {
1617                        wiped: false,
1618                        storage: B256Map::from_iter([
1619                            (slot1, U256::ZERO),
1620                            (slot2, U256::ZERO),
1621                            (slot3, U256::ZERO),
1622                        ]),
1623                    },
1624                ),
1625            ]),
1626        };
1627
1628        let chunking_length = state.chunking_length();
1629        for size in 1..=state.clone().chunks(1).count() {
1630            let chunk_count = state.clone().chunks(size).count();
1631            let expected_count = chunking_length.div_ceil(size);
1632            assert_eq!(
1633                chunk_count, expected_count,
1634                "chunking_length: {}, size: {}",
1635                chunking_length, size
1636            );
1637        }
1638    }
1639
1640    #[test]
1641    fn test_clone_into_sorted_equivalence() {
1642        let addr1 = B256::from([1; 32]);
1643        let addr2 = B256::from([2; 32]);
1644        let addr3 = B256::from([3; 32]);
1645        let slot1 = B256::from([1; 32]);
1646        let slot2 = B256::from([2; 32]);
1647        let slot3 = B256::from([3; 32]);
1648
1649        let state = HashedPostState {
1650            accounts: B256Map::from_iter([
1651                (addr1, Some(Account { nonce: 1, balance: U256::from(100), bytecode_hash: None })),
1652                (addr2, None),
1653                (addr3, Some(Account::default())),
1654            ]),
1655            storages: B256Map::from_iter([
1656                (
1657                    addr1,
1658                    HashedStorage {
1659                        wiped: false,
1660                        storage: B256Map::from_iter([
1661                            (slot1, U256::from(10)),
1662                            (slot2, U256::from(20)),
1663                        ]),
1664                    },
1665                ),
1666                (
1667                    addr2,
1668                    HashedStorage {
1669                        wiped: true,
1670                        storage: B256Map::from_iter([(slot3, U256::ZERO)]),
1671                    },
1672                ),
1673            ]),
1674        };
1675
1676        // clone_into_sorted should produce the same result as clone().into_sorted()
1677        let sorted_via_clone = state.clone().into_sorted();
1678        let sorted_via_clone_into = state.clone_into_sorted();
1679
1680        assert_eq!(sorted_via_clone, sorted_via_clone_into);
1681
1682        // Verify the original state is not consumed
1683        assert_eq!(state.accounts.len(), 3);
1684        assert_eq!(state.storages.len(), 2);
1685    }
1686
1687    #[test]
1688    fn test_hashed_storage_clone_into_sorted_equivalence() {
1689        let slot1 = B256::from([1; 32]);
1690        let slot2 = B256::from([2; 32]);
1691        let slot3 = B256::from([3; 32]);
1692
1693        let storage = HashedStorage {
1694            wiped: true,
1695            storage: B256Map::from_iter([
1696                (slot1, U256::from(100)),
1697                (slot2, U256::ZERO),
1698                (slot3, U256::from(300)),
1699            ]),
1700        };
1701
1702        // clone_into_sorted should produce the same result as clone().into_sorted()
1703        let sorted_via_clone = storage.clone().into_sorted();
1704        let sorted_via_clone_into = storage.clone_into_sorted();
1705
1706        assert_eq!(sorted_via_clone, sorted_via_clone_into);
1707
1708        // Verify the original storage is not consumed
1709        assert_eq!(storage.storage.len(), 3);
1710        assert!(storage.wiped);
1711    }
1712}
1713
1714/// Bincode-compatible hashed state type serde implementations.
1715#[cfg(feature = "serde-bincode-compat")]
1716pub mod serde_bincode_compat {
1717    use super::Account;
1718    use alloc::borrow::Cow;
1719    use alloy_primitives::{map::B256Map, B256, U256};
1720    use serde::{Deserialize, Deserializer, Serialize, Serializer};
1721    use serde_with::{DeserializeAs, SerializeAs};
1722
1723    /// Bincode-compatible [`super::HashedPostState`] serde implementation.
1724    ///
1725    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
1726    /// ```rust
1727    /// use reth_trie_common::{serde_bincode_compat, HashedPostState};
1728    /// use serde::{Deserialize, Serialize};
1729    /// use serde_with::serde_as;
1730    ///
1731    /// #[serde_as]
1732    /// #[derive(Serialize, Deserialize)]
1733    /// struct Data {
1734    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostState")]
1735    ///     hashed_state: HashedPostState,
1736    /// }
1737    /// ```
1738    #[derive(Debug, Serialize, Deserialize)]
1739    pub struct HashedPostState<'a> {
1740        accounts: Cow<'a, B256Map<Option<Account>>>,
1741        storages: B256Map<HashedStorage<'a>>,
1742    }
1743
1744    impl<'a> From<&'a super::HashedPostState> for HashedPostState<'a> {
1745        fn from(value: &'a super::HashedPostState) -> Self {
1746            Self {
1747                accounts: Cow::Borrowed(&value.accounts),
1748                storages: value.storages.iter().map(|(k, v)| (*k, v.into())).collect(),
1749            }
1750        }
1751    }
1752
1753    impl<'a> From<HashedPostState<'a>> for super::HashedPostState {
1754        fn from(value: HashedPostState<'a>) -> Self {
1755            Self {
1756                accounts: value.accounts.into_owned(),
1757                storages: value.storages.into_iter().map(|(k, v)| (k, v.into())).collect(),
1758            }
1759        }
1760    }
1761
1762    impl SerializeAs<super::HashedPostState> for HashedPostState<'_> {
1763        fn serialize_as<S>(
1764            source: &super::HashedPostState,
1765            serializer: S,
1766        ) -> Result<S::Ok, S::Error>
1767        where
1768            S: Serializer,
1769        {
1770            HashedPostState::from(source).serialize(serializer)
1771        }
1772    }
1773
1774    impl<'de> DeserializeAs<'de, super::HashedPostState> for HashedPostState<'de> {
1775        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedPostState, D::Error>
1776        where
1777            D: Deserializer<'de>,
1778        {
1779            HashedPostState::deserialize(deserializer).map(Into::into)
1780        }
1781    }
1782
1783    /// Bincode-compatible [`super::HashedStorage`] serde implementation.
1784    ///
1785    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
1786    /// ```rust
1787    /// use reth_trie_common::{serde_bincode_compat, HashedStorage};
1788    /// use serde::{Deserialize, Serialize};
1789    /// use serde_with::serde_as;
1790    ///
1791    /// #[serde_as]
1792    /// #[derive(Serialize, Deserialize)]
1793    /// struct Data {
1794    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorage")]
1795    ///     hashed_storage: HashedStorage,
1796    /// }
1797    /// ```
1798    #[derive(Debug, Serialize, Deserialize)]
1799    pub struct HashedStorage<'a> {
1800        wiped: bool,
1801        storage: Cow<'a, B256Map<U256>>,
1802    }
1803
1804    impl<'a> From<&'a super::HashedStorage> for HashedStorage<'a> {
1805        fn from(value: &'a super::HashedStorage) -> Self {
1806            Self { wiped: value.wiped, storage: Cow::Borrowed(&value.storage) }
1807        }
1808    }
1809
1810    impl<'a> From<HashedStorage<'a>> for super::HashedStorage {
1811        fn from(value: HashedStorage<'a>) -> Self {
1812            Self { wiped: value.wiped, storage: value.storage.into_owned() }
1813        }
1814    }
1815
1816    impl SerializeAs<super::HashedStorage> for HashedStorage<'_> {
1817        fn serialize_as<S>(source: &super::HashedStorage, serializer: S) -> Result<S::Ok, S::Error>
1818        where
1819            S: Serializer,
1820        {
1821            HashedStorage::from(source).serialize(serializer)
1822        }
1823    }
1824
1825    impl<'de> DeserializeAs<'de, super::HashedStorage> for HashedStorage<'de> {
1826        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedStorage, D::Error>
1827        where
1828            D: Deserializer<'de>,
1829        {
1830            HashedStorage::deserialize(deserializer).map(Into::into)
1831        }
1832    }
1833
1834    /// Bincode-compatible [`super::HashedPostStateSorted`] serde implementation.
1835    ///
1836    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
1837    /// ```rust
1838    /// use reth_trie_common::{serde_bincode_compat, HashedPostStateSorted};
1839    /// use serde::{Deserialize, Serialize};
1840    /// use serde_with::serde_as;
1841    ///
1842    /// #[serde_as]
1843    /// #[derive(Serialize, Deserialize)]
1844    /// struct Data {
1845    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostStateSorted")]
1846    ///     hashed_state: HashedPostStateSorted,
1847    /// }
1848    /// ```
1849    #[derive(Debug, Serialize, Deserialize)]
1850    pub struct HashedPostStateSorted<'a> {
1851        accounts: Cow<'a, [(B256, Option<Account>)]>,
1852        storages: B256Map<HashedStorageSorted<'a>>,
1853    }
1854
1855    impl<'a> From<&'a super::HashedPostStateSorted> for HashedPostStateSorted<'a> {
1856        fn from(value: &'a super::HashedPostStateSorted) -> Self {
1857            Self {
1858                accounts: Cow::Borrowed(&value.accounts),
1859                storages: value.storages.iter().map(|(k, v)| (*k, v.into())).collect(),
1860            }
1861        }
1862    }
1863
1864    impl<'a> From<HashedPostStateSorted<'a>> for super::HashedPostStateSorted {
1865        fn from(value: HashedPostStateSorted<'a>) -> Self {
1866            Self {
1867                accounts: value.accounts.into_owned(),
1868                storages: value.storages.into_iter().map(|(k, v)| (k, v.into())).collect(),
1869            }
1870        }
1871    }
1872
1873    impl SerializeAs<super::HashedPostStateSorted> for HashedPostStateSorted<'_> {
1874        fn serialize_as<S>(
1875            source: &super::HashedPostStateSorted,
1876            serializer: S,
1877        ) -> Result<S::Ok, S::Error>
1878        where
1879            S: Serializer,
1880        {
1881            HashedPostStateSorted::from(source).serialize(serializer)
1882        }
1883    }
1884
1885    impl<'de> DeserializeAs<'de, super::HashedPostStateSorted> for HashedPostStateSorted<'de> {
1886        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedPostStateSorted, D::Error>
1887        where
1888            D: Deserializer<'de>,
1889        {
1890            HashedPostStateSorted::deserialize(deserializer).map(Into::into)
1891        }
1892    }
1893
1894    /// Bincode-compatible [`super::HashedStorageSorted`] serde implementation.
1895    ///
1896    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
1897    /// ```rust
1898    /// use reth_trie_common::{serde_bincode_compat, HashedStorageSorted};
1899    /// use serde::{Deserialize, Serialize};
1900    /// use serde_with::serde_as;
1901    ///
1902    /// #[serde_as]
1903    /// #[derive(Serialize, Deserialize)]
1904    /// struct Data {
1905    ///     #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorageSorted")]
1906    ///     hashed_storage: HashedStorageSorted,
1907    /// }
1908    /// ```
1909    #[derive(Debug, Serialize, Deserialize)]
1910    pub struct HashedStorageSorted<'a> {
1911        storage_slots: Cow<'a, [(B256, U256)]>,
1912        wiped: bool,
1913    }
1914
1915    impl<'a> From<&'a super::HashedStorageSorted> for HashedStorageSorted<'a> {
1916        fn from(value: &'a super::HashedStorageSorted) -> Self {
1917            Self { storage_slots: Cow::Borrowed(&value.storage_slots), wiped: value.wiped }
1918        }
1919    }
1920
1921    impl<'a> From<HashedStorageSorted<'a>> for super::HashedStorageSorted {
1922        fn from(value: HashedStorageSorted<'a>) -> Self {
1923            Self { storage_slots: value.storage_slots.into_owned(), wiped: value.wiped }
1924        }
1925    }
1926
1927    impl SerializeAs<super::HashedStorageSorted> for HashedStorageSorted<'_> {
1928        fn serialize_as<S>(
1929            source: &super::HashedStorageSorted,
1930            serializer: S,
1931        ) -> Result<S::Ok, S::Error>
1932        where
1933            S: Serializer,
1934        {
1935            HashedStorageSorted::from(source).serialize(serializer)
1936        }
1937    }
1938
1939    impl<'de> DeserializeAs<'de, super::HashedStorageSorted> for HashedStorageSorted<'de> {
1940        fn deserialize_as<D>(deserializer: D) -> Result<super::HashedStorageSorted, D::Error>
1941        where
1942            D: Deserializer<'de>,
1943        {
1944            HashedStorageSorted::deserialize(deserializer).map(Into::into)
1945        }
1946    }
1947
1948    #[cfg(test)]
1949    mod tests {
1950        use crate::{
1951            hashed_state::{
1952                HashedPostState, HashedPostStateSorted, HashedStorage, HashedStorageSorted,
1953            },
1954            serde_bincode_compat,
1955        };
1956        use alloy_primitives::{B256, U256};
1957        use reth_primitives_traits::Account;
1958        use serde::{Deserialize, Serialize};
1959        use serde_with::serde_as;
1960
1961        #[test]
1962        fn test_hashed_post_state_bincode_roundtrip() {
1963            #[serde_as]
1964            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1965            struct Data {
1966                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostState")]
1967                hashed_state: HashedPostState,
1968            }
1969
1970            let mut data = Data { hashed_state: HashedPostState::default() };
1971            let encoded = bincode::serialize(&data).unwrap();
1972            let decoded: Data = bincode::deserialize(&encoded).unwrap();
1973            assert_eq!(decoded, data);
1974
1975            data.hashed_state.accounts.insert(B256::random(), Some(Account::default()));
1976            let encoded = bincode::serialize(&data).unwrap();
1977            let decoded: Data = bincode::deserialize(&encoded).unwrap();
1978            assert_eq!(decoded, data);
1979
1980            data.hashed_state.storages.insert(B256::random(), HashedStorage::default());
1981            let encoded = bincode::serialize(&data).unwrap();
1982            let decoded: Data = bincode::deserialize(&encoded).unwrap();
1983            assert_eq!(decoded, data);
1984        }
1985
1986        #[test]
1987        fn test_hashed_storage_bincode_roundtrip() {
1988            #[serde_as]
1989            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1990            struct Data {
1991                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorage")]
1992                hashed_storage: HashedStorage,
1993            }
1994
1995            let mut data = Data { hashed_storage: HashedStorage::default() };
1996            let encoded = bincode::serialize(&data).unwrap();
1997            let decoded: Data = bincode::deserialize(&encoded).unwrap();
1998            assert_eq!(decoded, data);
1999
2000            data.hashed_storage.wiped = true;
2001            let encoded = bincode::serialize(&data).unwrap();
2002            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2003            assert_eq!(decoded, data);
2004
2005            data.hashed_storage.storage.insert(B256::random(), U256::from(1));
2006            let encoded = bincode::serialize(&data).unwrap();
2007            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2008            assert_eq!(decoded, data);
2009        }
2010
2011        #[test]
2012        fn test_hashed_post_state_sorted_bincode_roundtrip() {
2013            #[serde_as]
2014            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
2015            struct Data {
2016                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedPostStateSorted")]
2017                hashed_state: HashedPostStateSorted,
2018            }
2019
2020            let mut data = Data { hashed_state: HashedPostStateSorted::default() };
2021            let encoded = bincode::serialize(&data).unwrap();
2022            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2023            assert_eq!(decoded, data);
2024
2025            data.hashed_state.accounts.push((B256::random(), Some(Account::default())));
2026            data.hashed_state
2027                .accounts
2028                .push((B256::random(), Some(Account { nonce: 1, ..Default::default() })));
2029            let encoded = bincode::serialize(&data).unwrap();
2030            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2031            assert_eq!(decoded, data);
2032
2033            data.hashed_state.storages.insert(
2034                B256::random(),
2035                HashedStorageSorted {
2036                    storage_slots: vec![(B256::from([1; 32]), U256::from(10))],
2037                    wiped: false,
2038                },
2039            );
2040            let encoded = bincode::serialize(&data).unwrap();
2041            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2042            assert_eq!(decoded, data);
2043        }
2044
2045        #[test]
2046        fn test_hashed_storage_sorted_bincode_roundtrip() {
2047            #[serde_as]
2048            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
2049            struct Data {
2050                #[serde_as(as = "serde_bincode_compat::hashed_state::HashedStorageSorted")]
2051                hashed_storage: HashedStorageSorted,
2052            }
2053
2054            let mut data = Data {
2055                hashed_storage: HashedStorageSorted { storage_slots: Vec::new(), wiped: false },
2056            };
2057            let encoded = bincode::serialize(&data).unwrap();
2058            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2059            assert_eq!(decoded, data);
2060
2061            data.hashed_storage.wiped = true;
2062            let encoded = bincode::serialize(&data).unwrap();
2063            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2064            assert_eq!(decoded, data);
2065
2066            data.hashed_storage.storage_slots.push((B256::random(), U256::from(1)));
2067            let encoded = bincode::serialize(&data).unwrap();
2068            let decoded: Data = bincode::deserialize(&encoded).unwrap();
2069            assert_eq!(decoded, data);
2070        }
2071    }
2072}