Skip to main content

reth_trie_common/
hashed_state.rs

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