reth_trie_common/
updates.rs

1use crate::{BranchNodeCompact, HashBuilder, Nibbles};
2use alloc::{
3    collections::{btree_map::BTreeMap, btree_set::BTreeSet},
4    vec::Vec,
5};
6use alloy_primitives::{
7    map::{B256Map, B256Set, HashMap, HashSet},
8    FixedBytes, B256,
9};
10
11/// The aggregation of trie updates.
12#[derive(PartialEq, Eq, Clone, Default, Debug)]
13#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
14pub struct TrieUpdates {
15    /// Collection of updated intermediate account nodes indexed by full path.
16    #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
17    pub account_nodes: HashMap<Nibbles, BranchNodeCompact>,
18    /// Collection of removed intermediate account nodes indexed by full path.
19    #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_set"))]
20    pub removed_nodes: HashSet<Nibbles>,
21    /// Collection of updated storage tries indexed by the hashed address.
22    pub storage_tries: B256Map<StorageTrieUpdates>,
23}
24
25impl TrieUpdates {
26    /// Returns `true` if the updates are empty.
27    pub fn is_empty(&self) -> bool {
28        self.account_nodes.is_empty() &&
29            self.removed_nodes.is_empty() &&
30            self.storage_tries.is_empty()
31    }
32
33    /// Returns reference to updated account nodes.
34    pub const fn account_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
35        &self.account_nodes
36    }
37
38    /// Returns a reference to removed account nodes.
39    pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
40        &self.removed_nodes
41    }
42
43    /// Returns a reference to updated storage tries.
44    pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdates> {
45        &self.storage_tries
46    }
47
48    /// Extends the trie updates.
49    pub fn extend(&mut self, other: Self) {
50        self.extend_common(&other);
51        self.account_nodes.extend(exclude_empty_from_pair(other.account_nodes));
52        self.removed_nodes.extend(exclude_empty(other.removed_nodes));
53        for (hashed_address, storage_trie) in other.storage_tries {
54            self.storage_tries.entry(hashed_address).or_default().extend(storage_trie);
55        }
56    }
57
58    /// Extends the trie updates.
59    ///
60    /// Slightly less efficient than [`Self::extend`], but preferred to `extend(other.clone())`.
61    pub fn extend_ref(&mut self, other: &Self) {
62        self.extend_common(other);
63        self.account_nodes.extend(exclude_empty_from_pair(
64            other.account_nodes.iter().map(|(k, v)| (*k, v.clone())),
65        ));
66        self.removed_nodes.extend(exclude_empty(other.removed_nodes.iter().copied()));
67        for (hashed_address, storage_trie) in &other.storage_tries {
68            self.storage_tries.entry(*hashed_address).or_default().extend_ref(storage_trie);
69        }
70    }
71
72    fn extend_common(&mut self, other: &Self) {
73        self.account_nodes.retain(|nibbles, _| !other.removed_nodes.contains(nibbles));
74    }
75
76    /// Insert storage updates for a given hashed address.
77    pub fn insert_storage_updates(
78        &mut self,
79        hashed_address: B256,
80        storage_updates: StorageTrieUpdates,
81    ) {
82        if storage_updates.is_empty() {
83            return;
84        }
85        let existing = self.storage_tries.insert(hashed_address, storage_updates);
86        debug_assert!(existing.is_none());
87    }
88
89    /// Finalize state trie updates.
90    pub fn finalize(
91        &mut self,
92        hash_builder: HashBuilder,
93        removed_keys: HashSet<Nibbles>,
94        destroyed_accounts: B256Set,
95    ) {
96        // Retrieve updated nodes from hash builder.
97        let (_, updated_nodes) = hash_builder.split();
98        self.account_nodes.extend(exclude_empty_from_pair(updated_nodes));
99
100        // Add deleted node paths.
101        self.removed_nodes.extend(exclude_empty(removed_keys));
102
103        // Add deleted storage tries for destroyed accounts.
104        for destroyed in destroyed_accounts {
105            self.storage_tries.entry(destroyed).or_default().set_deleted(true);
106        }
107    }
108
109    /// Converts trie updates into [`TrieUpdatesSorted`].
110    pub fn into_sorted(self) -> TrieUpdatesSorted {
111        let mut account_nodes = Vec::from_iter(self.account_nodes);
112        account_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
113        let storage_tries = self
114            .storage_tries
115            .into_iter()
116            .map(|(hashed_address, updates)| (hashed_address, updates.into_sorted()))
117            .collect();
118        TrieUpdatesSorted { removed_nodes: self.removed_nodes, account_nodes, storage_tries }
119    }
120
121    /// Converts trie updates into [`TrieUpdatesSorted`], but keeping the maps allocated by
122    /// draining.
123    ///
124    /// This effectively clears all the fields in the [`TrieUpdatesSorted`].
125    ///
126    /// This allows us to reuse the allocated space. This allocates new space for the sorted
127    /// updates, like `into_sorted`.
128    pub fn drain_into_sorted(&mut self) -> TrieUpdatesSorted {
129        let mut account_nodes = self.account_nodes.drain().collect::<Vec<_>>();
130        account_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
131
132        let storage_tries = self
133            .storage_tries
134            .drain()
135            .map(|(hashed_address, updates)| (hashed_address, updates.into_sorted()))
136            .collect();
137
138        TrieUpdatesSorted {
139            removed_nodes: self.removed_nodes.clone(),
140            account_nodes,
141            storage_tries,
142        }
143    }
144
145    /// Converts trie updates into [`TrieUpdatesSortedRef`].
146    pub fn into_sorted_ref<'a>(&'a self) -> TrieUpdatesSortedRef<'a> {
147        let mut account_nodes = self.account_nodes.iter().collect::<Vec<_>>();
148        account_nodes.sort_unstable_by(|a, b| a.0.cmp(b.0));
149
150        TrieUpdatesSortedRef {
151            removed_nodes: self.removed_nodes.iter().collect::<BTreeSet<_>>(),
152            account_nodes,
153            storage_tries: self
154                .storage_tries
155                .iter()
156                .map(|m| (*m.0, m.1.into_sorted_ref().clone()))
157                .collect(),
158        }
159    }
160
161    /// Clears the nodes and storage trie maps in this `TrieUpdates`.
162    pub fn clear(&mut self) {
163        self.account_nodes.clear();
164        self.removed_nodes.clear();
165        self.storage_tries.clear();
166    }
167}
168
169/// Trie updates for storage trie of a single account.
170#[derive(PartialEq, Eq, Clone, Default, Debug)]
171#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
172pub struct StorageTrieUpdates {
173    /// Flag indicating whether the trie was deleted.
174    pub is_deleted: bool,
175    /// Collection of updated storage trie nodes.
176    #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
177    pub storage_nodes: HashMap<Nibbles, BranchNodeCompact>,
178    /// Collection of removed storage trie nodes.
179    #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_set"))]
180    pub removed_nodes: HashSet<Nibbles>,
181}
182
183#[cfg(feature = "test-utils")]
184impl StorageTrieUpdates {
185    /// Creates a new storage trie updates that are not marked as deleted.
186    pub fn new(updates: impl IntoIterator<Item = (Nibbles, BranchNodeCompact)>) -> Self {
187        Self { storage_nodes: exclude_empty_from_pair(updates).collect(), ..Default::default() }
188    }
189}
190
191impl StorageTrieUpdates {
192    /// Returns empty storage trie updates with `deleted` set to `true`.
193    pub fn deleted() -> Self {
194        Self {
195            is_deleted: true,
196            storage_nodes: HashMap::default(),
197            removed_nodes: HashSet::default(),
198        }
199    }
200
201    /// Returns the length of updated nodes.
202    pub fn len(&self) -> usize {
203        (self.is_deleted as usize) + self.storage_nodes.len() + self.removed_nodes.len()
204    }
205
206    /// Returns `true` if the trie was deleted.
207    pub const fn is_deleted(&self) -> bool {
208        self.is_deleted
209    }
210
211    /// Returns reference to updated storage nodes.
212    pub const fn storage_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
213        &self.storage_nodes
214    }
215
216    /// Returns reference to removed storage nodes.
217    pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
218        &self.removed_nodes
219    }
220
221    /// Returns `true` if storage updates are empty.
222    pub fn is_empty(&self) -> bool {
223        !self.is_deleted && self.storage_nodes.is_empty() && self.removed_nodes.is_empty()
224    }
225
226    /// Sets `deleted` flag on the storage trie.
227    pub const fn set_deleted(&mut self, deleted: bool) {
228        self.is_deleted = deleted;
229    }
230
231    /// Extends storage trie updates.
232    pub fn extend(&mut self, other: Self) {
233        self.extend_common(&other);
234        self.storage_nodes.extend(exclude_empty_from_pair(other.storage_nodes));
235        self.removed_nodes.extend(exclude_empty(other.removed_nodes));
236    }
237
238    /// Extends storage trie updates.
239    ///
240    /// Slightly less efficient than [`Self::extend`], but preferred to `extend(other.clone())`.
241    pub fn extend_ref(&mut self, other: &Self) {
242        self.extend_common(other);
243        self.storage_nodes.extend(exclude_empty_from_pair(
244            other.storage_nodes.iter().map(|(k, v)| (*k, v.clone())),
245        ));
246        self.removed_nodes.extend(exclude_empty(other.removed_nodes.iter().copied()));
247    }
248
249    fn extend_common(&mut self, other: &Self) {
250        if other.is_deleted {
251            self.storage_nodes.clear();
252            self.removed_nodes.clear();
253        }
254        self.is_deleted |= other.is_deleted;
255        self.storage_nodes.retain(|nibbles, _| !other.removed_nodes.contains(nibbles));
256    }
257
258    /// Finalize storage trie updates for by taking updates from walker and hash builder.
259    pub fn finalize(&mut self, hash_builder: HashBuilder, removed_keys: HashSet<Nibbles>) {
260        // Retrieve updated nodes from hash builder.
261        let (_, updated_nodes) = hash_builder.split();
262        self.storage_nodes.extend(exclude_empty_from_pair(updated_nodes));
263
264        // Add deleted node paths.
265        self.removed_nodes.extend(exclude_empty(removed_keys));
266    }
267
268    /// Convert storage trie updates into [`StorageTrieUpdatesSorted`].
269    pub fn into_sorted(self) -> StorageTrieUpdatesSorted {
270        let mut storage_nodes = Vec::from_iter(self.storage_nodes);
271        storage_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
272        StorageTrieUpdatesSorted {
273            is_deleted: self.is_deleted,
274            removed_nodes: self.removed_nodes,
275            storage_nodes,
276        }
277    }
278
279    /// Convert storage trie updates into [`StorageTrieUpdatesSortedRef`].
280    pub fn into_sorted_ref(&self) -> StorageTrieUpdatesSortedRef<'_> {
281        StorageTrieUpdatesSortedRef {
282            is_deleted: self.is_deleted,
283            removed_nodes: self.removed_nodes.iter().collect::<BTreeSet<_>>(),
284            storage_nodes: self.storage_nodes.iter().collect::<BTreeMap<_, _>>(),
285        }
286    }
287}
288
289/// Serializes and deserializes any [`HashSet`] that includes [`Nibbles`] elements, by using the
290/// hex-encoded packed representation.
291///
292/// This also sorts the set before serializing.
293#[cfg(any(test, feature = "serde"))]
294mod serde_nibbles_set {
295    use crate::Nibbles;
296    use alloc::{
297        string::{String, ToString},
298        vec::Vec,
299    };
300    use alloy_primitives::map::HashSet;
301    use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
302
303    pub(super) fn serialize<S>(map: &HashSet<Nibbles>, serializer: S) -> Result<S::Ok, S::Error>
304    where
305        S: Serializer,
306    {
307        let mut storage_nodes =
308            map.iter().map(|elem| alloy_primitives::hex::encode(elem.pack())).collect::<Vec<_>>();
309        storage_nodes.sort_unstable();
310        storage_nodes.serialize(serializer)
311    }
312
313    pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<HashSet<Nibbles>, D::Error>
314    where
315        D: Deserializer<'de>,
316    {
317        Vec::<String>::deserialize(deserializer)?
318            .into_iter()
319            .map(|node| {
320                Ok(Nibbles::unpack(
321                    alloy_primitives::hex::decode(node)
322                        .map_err(|err| D::Error::custom(err.to_string()))?,
323                ))
324            })
325            .collect::<Result<HashSet<_>, _>>()
326    }
327}
328
329/// Serializes and deserializes any [`HashMap`] that uses [`Nibbles`] as keys, by using the
330/// hex-encoded packed representation.
331///
332/// This also sorts the map's keys before encoding and serializing.
333#[cfg(any(test, feature = "serde"))]
334mod serde_nibbles_map {
335    use crate::Nibbles;
336    use alloc::{
337        string::{String, ToString},
338        vec::Vec,
339    };
340    use alloy_primitives::{hex, map::HashMap};
341    use core::marker::PhantomData;
342    use serde::{
343        de::{Error, MapAccess, Visitor},
344        ser::SerializeMap,
345        Deserialize, Deserializer, Serialize, Serializer,
346    };
347
348    pub(super) fn serialize<S, T>(
349        map: &HashMap<Nibbles, T>,
350        serializer: S,
351    ) -> Result<S::Ok, S::Error>
352    where
353        S: Serializer,
354        T: Serialize,
355    {
356        let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
357        let mut storage_nodes = Vec::from_iter(map);
358        storage_nodes.sort_unstable_by_key(|node| node.0);
359        for (k, v) in storage_nodes {
360            // pack, then hex encode the Nibbles
361            let packed = alloy_primitives::hex::encode(k.pack());
362            map_serializer.serialize_entry(&packed, &v)?;
363        }
364        map_serializer.end()
365    }
366
367    pub(super) fn deserialize<'de, D, T>(deserializer: D) -> Result<HashMap<Nibbles, T>, D::Error>
368    where
369        D: Deserializer<'de>,
370        T: Deserialize<'de>,
371    {
372        struct NibblesMapVisitor<T> {
373            marker: PhantomData<T>,
374        }
375
376        impl<'de, T> Visitor<'de> for NibblesMapVisitor<T>
377        where
378            T: Deserialize<'de>,
379        {
380            type Value = HashMap<Nibbles, T>;
381
382            fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
383                formatter.write_str("a map with hex-encoded Nibbles keys")
384            }
385
386            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
387            where
388                A: MapAccess<'de>,
389            {
390                let mut result = HashMap::with_capacity_and_hasher(
391                    map.size_hint().unwrap_or(0),
392                    Default::default(),
393                );
394
395                while let Some((key, value)) = map.next_entry::<String, T>()? {
396                    let decoded_key =
397                        hex::decode(&key).map_err(|err| Error::custom(err.to_string()))?;
398
399                    let nibbles = Nibbles::unpack(&decoded_key);
400
401                    result.insert(nibbles, value);
402                }
403
404                Ok(result)
405            }
406        }
407
408        deserializer.deserialize_map(NibblesMapVisitor { marker: PhantomData })
409    }
410}
411
412/// Sorted trie updates reference used for serializing trie to file.
413#[derive(PartialEq, Eq, Clone, Default, Debug)]
414#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
415pub struct TrieUpdatesSortedRef<'a> {
416    /// Sorted collection of updated state nodes with corresponding paths.
417    pub account_nodes: Vec<(&'a Nibbles, &'a BranchNodeCompact)>,
418    /// The set of removed state node keys.
419    pub removed_nodes: BTreeSet<&'a Nibbles>,
420    /// Storage tries stored by hashed address of the account the trie belongs to.
421    pub storage_tries: BTreeMap<FixedBytes<32>, StorageTrieUpdatesSortedRef<'a>>,
422}
423
424/// Sorted trie updates used for lookups and insertions.
425#[derive(PartialEq, Eq, Clone, Default, Debug)]
426#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
427pub struct TrieUpdatesSorted {
428    /// Sorted collection of updated state nodes with corresponding paths.
429    pub account_nodes: Vec<(Nibbles, BranchNodeCompact)>,
430    /// The set of removed state node keys.
431    pub removed_nodes: HashSet<Nibbles>,
432    /// Storage tries stored by hashed address of the account the trie belongs to.
433    pub storage_tries: B256Map<StorageTrieUpdatesSorted>,
434}
435
436impl TrieUpdatesSorted {
437    /// Returns reference to updated account nodes.
438    pub fn account_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] {
439        &self.account_nodes
440    }
441
442    /// Returns reference to removed account nodes.
443    pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
444        &self.removed_nodes
445    }
446
447    /// Returns reference to updated storage tries.
448    pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdatesSorted> {
449        &self.storage_tries
450    }
451}
452
453/// Sorted storage trie updates reference used for serializing to file.
454#[derive(PartialEq, Eq, Clone, Default, Debug)]
455#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
456pub struct StorageTrieUpdatesSortedRef<'a> {
457    /// Flag indicating whether the trie has been deleted/wiped.
458    pub is_deleted: bool,
459    /// Sorted collection of updated storage nodes with corresponding paths.
460    pub storage_nodes: BTreeMap<&'a Nibbles, &'a BranchNodeCompact>,
461    /// The set of removed storage node keys.
462    pub removed_nodes: BTreeSet<&'a Nibbles>,
463}
464
465/// Sorted trie updates used for lookups and insertions.
466#[derive(PartialEq, Eq, Clone, Default, Debug)]
467#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
468pub struct StorageTrieUpdatesSorted {
469    /// Flag indicating whether the trie has been deleted/wiped.
470    pub is_deleted: bool,
471    /// Sorted collection of updated storage nodes with corresponding paths.
472    pub storage_nodes: Vec<(Nibbles, BranchNodeCompact)>,
473    /// The set of removed storage node keys.
474    pub removed_nodes: HashSet<Nibbles>,
475}
476
477impl StorageTrieUpdatesSorted {
478    /// Returns `true` if the trie was deleted.
479    pub const fn is_deleted(&self) -> bool {
480        self.is_deleted
481    }
482
483    /// Returns reference to updated storage nodes.
484    pub fn storage_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] {
485        &self.storage_nodes
486    }
487
488    /// Returns reference to removed storage nodes.
489    pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
490        &self.removed_nodes
491    }
492}
493
494/// Excludes empty nibbles from the given iterator.
495fn exclude_empty(iter: impl IntoIterator<Item = Nibbles>) -> impl Iterator<Item = Nibbles> {
496    iter.into_iter().filter(|n| !n.is_empty())
497}
498
499/// Excludes empty nibbles from the given iterator of pairs where the nibbles are the key.
500fn exclude_empty_from_pair<V>(
501    iter: impl IntoIterator<Item = (Nibbles, V)>,
502) -> impl Iterator<Item = (Nibbles, V)> {
503    iter.into_iter().filter(|(n, _)| !n.is_empty())
504}
505
506/// Bincode-compatible trie updates type serde implementations.
507#[cfg(feature = "serde-bincode-compat")]
508pub mod serde_bincode_compat {
509    use crate::{BranchNodeCompact, Nibbles};
510    use alloc::borrow::Cow;
511    use alloy_primitives::map::{B256Map, HashMap, HashSet};
512    use serde::{Deserialize, Deserializer, Serialize, Serializer};
513    use serde_with::{DeserializeAs, SerializeAs};
514
515    /// Bincode-compatible [`super::TrieUpdates`] serde implementation.
516    ///
517    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
518    /// ```rust
519    /// use reth_trie_common::{serde_bincode_compat, updates::TrieUpdates};
520    /// use serde::{Deserialize, Serialize};
521    /// use serde_with::serde_as;
522    ///
523    /// #[serde_as]
524    /// #[derive(Serialize, Deserialize)]
525    /// struct Data {
526    ///     #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")]
527    ///     trie_updates: TrieUpdates,
528    /// }
529    /// ```
530    #[derive(Debug, Serialize, Deserialize)]
531    pub struct TrieUpdates<'a> {
532        account_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
533        removed_nodes: Cow<'a, HashSet<Nibbles>>,
534        storage_tries: B256Map<StorageTrieUpdates<'a>>,
535    }
536
537    impl<'a> From<&'a super::TrieUpdates> for TrieUpdates<'a> {
538        fn from(value: &'a super::TrieUpdates) -> Self {
539            Self {
540                account_nodes: Cow::Borrowed(&value.account_nodes),
541                removed_nodes: Cow::Borrowed(&value.removed_nodes),
542                storage_tries: value.storage_tries.iter().map(|(k, v)| (*k, v.into())).collect(),
543            }
544        }
545    }
546
547    impl<'a> From<TrieUpdates<'a>> for super::TrieUpdates {
548        fn from(value: TrieUpdates<'a>) -> Self {
549            Self {
550                account_nodes: value.account_nodes.into_owned(),
551                removed_nodes: value.removed_nodes.into_owned(),
552                storage_tries: value
553                    .storage_tries
554                    .into_iter()
555                    .map(|(k, v)| (k, v.into()))
556                    .collect(),
557            }
558        }
559    }
560
561    impl SerializeAs<super::TrieUpdates> for TrieUpdates<'_> {
562        fn serialize_as<S>(source: &super::TrieUpdates, serializer: S) -> Result<S::Ok, S::Error>
563        where
564            S: Serializer,
565        {
566            TrieUpdates::from(source).serialize(serializer)
567        }
568    }
569
570    impl<'de> DeserializeAs<'de, super::TrieUpdates> for TrieUpdates<'de> {
571        fn deserialize_as<D>(deserializer: D) -> Result<super::TrieUpdates, D::Error>
572        where
573            D: Deserializer<'de>,
574        {
575            TrieUpdates::deserialize(deserializer).map(Into::into)
576        }
577    }
578
579    /// Bincode-compatible [`super::StorageTrieUpdates`] serde implementation.
580    ///
581    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
582    /// ```rust
583    /// use reth_trie_common::{serde_bincode_compat, updates::StorageTrieUpdates};
584    /// use serde::{Deserialize, Serialize};
585    /// use serde_with::serde_as;
586    ///
587    /// #[serde_as]
588    /// #[derive(Serialize, Deserialize)]
589    /// struct Data {
590    ///     #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")]
591    ///     trie_updates: StorageTrieUpdates,
592    /// }
593    /// ```
594    #[derive(Debug, Serialize, Deserialize)]
595    pub struct StorageTrieUpdates<'a> {
596        is_deleted: bool,
597        storage_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
598        removed_nodes: Cow<'a, HashSet<Nibbles>>,
599    }
600
601    impl<'a> From<&'a super::StorageTrieUpdates> for StorageTrieUpdates<'a> {
602        fn from(value: &'a super::StorageTrieUpdates) -> Self {
603            Self {
604                is_deleted: value.is_deleted,
605                storage_nodes: Cow::Borrowed(&value.storage_nodes),
606                removed_nodes: Cow::Borrowed(&value.removed_nodes),
607            }
608        }
609    }
610
611    impl<'a> From<StorageTrieUpdates<'a>> for super::StorageTrieUpdates {
612        fn from(value: StorageTrieUpdates<'a>) -> Self {
613            Self {
614                is_deleted: value.is_deleted,
615                storage_nodes: value.storage_nodes.into_owned(),
616                removed_nodes: value.removed_nodes.into_owned(),
617            }
618        }
619    }
620
621    impl SerializeAs<super::StorageTrieUpdates> for StorageTrieUpdates<'_> {
622        fn serialize_as<S>(
623            source: &super::StorageTrieUpdates,
624            serializer: S,
625        ) -> Result<S::Ok, S::Error>
626        where
627            S: Serializer,
628        {
629            StorageTrieUpdates::from(source).serialize(serializer)
630        }
631    }
632
633    impl<'de> DeserializeAs<'de, super::StorageTrieUpdates> for StorageTrieUpdates<'de> {
634        fn deserialize_as<D>(deserializer: D) -> Result<super::StorageTrieUpdates, D::Error>
635        where
636            D: Deserializer<'de>,
637        {
638            StorageTrieUpdates::deserialize(deserializer).map(Into::into)
639        }
640    }
641
642    #[cfg(test)]
643    mod tests {
644        use crate::{
645            serde_bincode_compat,
646            updates::{StorageTrieUpdates, TrieUpdates},
647            BranchNodeCompact, Nibbles,
648        };
649        use alloy_primitives::B256;
650        use serde::{Deserialize, Serialize};
651        use serde_with::serde_as;
652
653        #[test]
654        fn test_trie_updates_bincode_roundtrip() {
655            #[serde_as]
656            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
657            struct Data {
658                #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")]
659                trie_updates: TrieUpdates,
660            }
661
662            let mut data = Data { trie_updates: TrieUpdates::default() };
663            let encoded = bincode::serialize(&data).unwrap();
664            let decoded: Data = bincode::deserialize(&encoded).unwrap();
665            assert_eq!(decoded, data);
666
667            data.trie_updates
668                .removed_nodes
669                .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
670            let encoded = bincode::serialize(&data).unwrap();
671            let decoded: Data = bincode::deserialize(&encoded).unwrap();
672            assert_eq!(decoded, data);
673
674            data.trie_updates.account_nodes.insert(
675                Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
676                BranchNodeCompact::default(),
677            );
678            let encoded = bincode::serialize(&data).unwrap();
679            let decoded: Data = bincode::deserialize(&encoded).unwrap();
680            assert_eq!(decoded, data);
681
682            data.trie_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
683            let encoded = bincode::serialize(&data).unwrap();
684            let decoded: Data = bincode::deserialize(&encoded).unwrap();
685            assert_eq!(decoded, data);
686        }
687
688        #[test]
689        fn test_storage_trie_updates_bincode_roundtrip() {
690            #[serde_as]
691            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
692            struct Data {
693                #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")]
694                trie_updates: StorageTrieUpdates,
695            }
696
697            let mut data = Data { trie_updates: StorageTrieUpdates::default() };
698            let encoded = bincode::serialize(&data).unwrap();
699            let decoded: Data = bincode::deserialize(&encoded).unwrap();
700            assert_eq!(decoded, data);
701
702            data.trie_updates
703                .removed_nodes
704                .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
705            let encoded = bincode::serialize(&data).unwrap();
706            let decoded: Data = bincode::deserialize(&encoded).unwrap();
707            assert_eq!(decoded, data);
708
709            data.trie_updates.storage_nodes.insert(
710                Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
711                BranchNodeCompact::default(),
712            );
713            let encoded = bincode::serialize(&data).unwrap();
714            let decoded: Data = bincode::deserialize(&encoded).unwrap();
715            assert_eq!(decoded, data);
716        }
717    }
718}
719
720#[cfg(all(test, feature = "serde"))]
721mod tests {
722    use super::*;
723
724    #[test]
725    fn test_trie_updates_serde_roundtrip() {
726        let mut default_updates = TrieUpdates::default();
727        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
728        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
729        assert_eq!(updates_deserialized, default_updates);
730
731        default_updates
732            .removed_nodes
733            .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
734        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
735        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
736        assert_eq!(updates_deserialized, default_updates);
737
738        default_updates.account_nodes.insert(
739            Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
740            BranchNodeCompact::default(),
741        );
742        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
743        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
744        assert_eq!(updates_deserialized, default_updates);
745
746        default_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
747        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
748        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
749        assert_eq!(updates_deserialized, default_updates);
750    }
751
752    #[test]
753    fn test_storage_trie_updates_serde_roundtrip() {
754        let mut default_updates = StorageTrieUpdates::default();
755        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
756        let updates_deserialized: StorageTrieUpdates =
757            serde_json::from_str(&updates_serialized).unwrap();
758        assert_eq!(updates_deserialized, default_updates);
759
760        default_updates
761            .removed_nodes
762            .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
763        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
764        let updates_deserialized: StorageTrieUpdates =
765            serde_json::from_str(&updates_serialized).unwrap();
766        assert_eq!(updates_deserialized, default_updates);
767
768        default_updates.storage_nodes.insert(
769            Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
770            BranchNodeCompact::default(),
771        );
772        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
773        let updates_deserialized: StorageTrieUpdates =
774            serde_json::from_str(&updates_serialized).unwrap();
775        assert_eq!(updates_deserialized, default_updates);
776    }
777}