Skip to main content

reth_trie_common/
hashed_state.rs

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