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