Skip to main content

reth_trie_common/
hashed_state.rs

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