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