reth_trie_common/
updates.rs

1use crate::{utils::extend_sorted_vec, 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(mut self) -> TrieUpdatesSorted {
111        self.drain_into_sorted()
112    }
113
114    /// Converts trie updates into [`TrieUpdatesSorted`], but keeping the maps allocated by
115    /// draining.
116    ///
117    /// This effectively clears all the fields in the [`TrieUpdatesSorted`].
118    ///
119    /// This allows us to reuse the allocated space. This allocates new space for the sorted
120    /// updates, like `into_sorted`.
121    pub fn drain_into_sorted(&mut self) -> TrieUpdatesSorted {
122        let mut account_nodes = self
123            .account_nodes
124            .drain()
125            .map(|(path, node)| {
126                // Updated nodes take precedence over removed nodes.
127                self.removed_nodes.remove(&path);
128                (path, Some(node))
129            })
130            .collect::<Vec<_>>();
131
132        account_nodes.extend(self.removed_nodes.drain().map(|path| (path, None)));
133        account_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
134
135        let storage_tries = self
136            .storage_tries
137            .drain()
138            .map(|(hashed_address, updates)| (hashed_address, updates.into_sorted()))
139            .collect();
140        TrieUpdatesSorted { account_nodes, storage_tries }
141    }
142
143    /// Converts trie updates into [`TrieUpdatesSortedRef`].
144    pub fn into_sorted_ref<'a>(&'a self) -> TrieUpdatesSortedRef<'a> {
145        let mut account_nodes = self.account_nodes.iter().collect::<Vec<_>>();
146        account_nodes.sort_unstable_by(|a, b| a.0.cmp(b.0));
147
148        TrieUpdatesSortedRef {
149            removed_nodes: self.removed_nodes.iter().collect::<BTreeSet<_>>(),
150            account_nodes,
151            storage_tries: self
152                .storage_tries
153                .iter()
154                .map(|m| (*m.0, m.1.into_sorted_ref().clone()))
155                .collect(),
156        }
157    }
158
159    /// Clears the nodes and storage trie maps in this `TrieUpdates`.
160    pub fn clear(&mut self) {
161        self.account_nodes.clear();
162        self.removed_nodes.clear();
163        self.storage_tries.clear();
164    }
165}
166
167/// Trie updates for storage trie of a single account.
168#[derive(PartialEq, Eq, Clone, Default, Debug)]
169#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
170pub struct StorageTrieUpdates {
171    /// Flag indicating whether the trie was deleted.
172    pub is_deleted: bool,
173    /// Collection of updated storage trie nodes.
174    #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
175    pub storage_nodes: HashMap<Nibbles, BranchNodeCompact>,
176    /// Collection of removed storage trie nodes.
177    #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_set"))]
178    pub removed_nodes: HashSet<Nibbles>,
179}
180
181#[cfg(feature = "test-utils")]
182impl StorageTrieUpdates {
183    /// Creates a new storage trie updates that are not marked as deleted.
184    pub fn new(updates: impl IntoIterator<Item = (Nibbles, BranchNodeCompact)>) -> Self {
185        Self { storage_nodes: exclude_empty_from_pair(updates).collect(), ..Default::default() }
186    }
187}
188
189impl StorageTrieUpdates {
190    /// Returns empty storage trie updates with `deleted` set to `true`.
191    pub fn deleted() -> Self {
192        Self {
193            is_deleted: true,
194            storage_nodes: HashMap::default(),
195            removed_nodes: HashSet::default(),
196        }
197    }
198
199    /// Returns the length of updated nodes.
200    pub fn len(&self) -> usize {
201        (self.is_deleted as usize) + self.storage_nodes.len() + self.removed_nodes.len()
202    }
203
204    /// Returns `true` if the trie was deleted.
205    pub const fn is_deleted(&self) -> bool {
206        self.is_deleted
207    }
208
209    /// Returns reference to updated storage nodes.
210    pub const fn storage_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
211        &self.storage_nodes
212    }
213
214    /// Returns reference to removed storage nodes.
215    pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
216        &self.removed_nodes
217    }
218
219    /// Returns `true` if storage updates are empty.
220    pub fn is_empty(&self) -> bool {
221        !self.is_deleted && self.storage_nodes.is_empty() && self.removed_nodes.is_empty()
222    }
223
224    /// Sets `deleted` flag on the storage trie.
225    pub const fn set_deleted(&mut self, deleted: bool) {
226        self.is_deleted = deleted;
227    }
228
229    /// Extends storage trie updates.
230    pub fn extend(&mut self, other: Self) {
231        self.extend_common(&other);
232        self.storage_nodes.extend(exclude_empty_from_pair(other.storage_nodes));
233        self.removed_nodes.extend(exclude_empty(other.removed_nodes));
234    }
235
236    /// Extends storage trie updates.
237    ///
238    /// Slightly less efficient than [`Self::extend`], but preferred to `extend(other.clone())`.
239    pub fn extend_ref(&mut self, other: &Self) {
240        self.extend_common(other);
241        self.storage_nodes.extend(exclude_empty_from_pair(
242            other.storage_nodes.iter().map(|(k, v)| (*k, v.clone())),
243        ));
244        self.removed_nodes.extend(exclude_empty(other.removed_nodes.iter().copied()));
245    }
246
247    fn extend_common(&mut self, other: &Self) {
248        if other.is_deleted {
249            self.storage_nodes.clear();
250            self.removed_nodes.clear();
251        }
252        self.is_deleted |= other.is_deleted;
253        self.storage_nodes.retain(|nibbles, _| !other.removed_nodes.contains(nibbles));
254    }
255
256    /// Finalize storage trie updates for by taking updates from walker and hash builder.
257    pub fn finalize(&mut self, hash_builder: HashBuilder, removed_keys: HashSet<Nibbles>) {
258        // Retrieve updated nodes from hash builder.
259        let (_, updated_nodes) = hash_builder.split();
260        self.storage_nodes.extend(exclude_empty_from_pair(updated_nodes));
261
262        // Add deleted node paths.
263        self.removed_nodes.extend(exclude_empty(removed_keys));
264    }
265
266    /// Convert storage trie updates into [`StorageTrieUpdatesSorted`].
267    pub fn into_sorted(mut self) -> StorageTrieUpdatesSorted {
268        let mut storage_nodes = self
269            .storage_nodes
270            .into_iter()
271            .map(|(path, node)| {
272                // Updated nodes take precedence over removed nodes.
273                self.removed_nodes.remove(&path);
274                (path, Some(node))
275            })
276            .collect::<Vec<_>>();
277
278        storage_nodes.extend(self.removed_nodes.into_iter().map(|path| (path, None)));
279        storage_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
280
281        StorageTrieUpdatesSorted { is_deleted: self.is_deleted, storage_nodes }
282    }
283
284    /// Convert storage trie updates into [`StorageTrieUpdatesSortedRef`].
285    pub fn into_sorted_ref(&self) -> StorageTrieUpdatesSortedRef<'_> {
286        StorageTrieUpdatesSortedRef {
287            is_deleted: self.is_deleted,
288            removed_nodes: self.removed_nodes.iter().collect::<BTreeSet<_>>(),
289            storage_nodes: self.storage_nodes.iter().collect::<BTreeMap<_, _>>(),
290        }
291    }
292}
293
294/// Serializes and deserializes any [`HashSet`] that includes [`Nibbles`] elements, by using the
295/// hex-encoded packed representation.
296///
297/// This also sorts the set before serializing.
298#[cfg(any(test, feature = "serde"))]
299mod serde_nibbles_set {
300    use crate::Nibbles;
301    use alloc::{
302        string::{String, ToString},
303        vec::Vec,
304    };
305    use alloy_primitives::map::HashSet;
306    use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
307
308    pub(super) fn serialize<S>(map: &HashSet<Nibbles>, serializer: S) -> Result<S::Ok, S::Error>
309    where
310        S: Serializer,
311    {
312        let mut storage_nodes =
313            map.iter().map(|elem| alloy_primitives::hex::encode(elem.pack())).collect::<Vec<_>>();
314        storage_nodes.sort_unstable();
315        storage_nodes.serialize(serializer)
316    }
317
318    pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<HashSet<Nibbles>, D::Error>
319    where
320        D: Deserializer<'de>,
321    {
322        Vec::<String>::deserialize(deserializer)?
323            .into_iter()
324            .map(|node| {
325                Ok(Nibbles::unpack(
326                    alloy_primitives::hex::decode(node)
327                        .map_err(|err| D::Error::custom(err.to_string()))?,
328                ))
329            })
330            .collect::<Result<HashSet<_>, _>>()
331    }
332}
333
334/// Serializes and deserializes any [`HashMap`] that uses [`Nibbles`] as keys, by using the
335/// hex-encoded packed representation.
336///
337/// This also sorts the map's keys before encoding and serializing.
338#[cfg(any(test, feature = "serde"))]
339mod serde_nibbles_map {
340    use crate::Nibbles;
341    use alloc::{
342        string::{String, ToString},
343        vec::Vec,
344    };
345    use alloy_primitives::{hex, map::HashMap};
346    use core::marker::PhantomData;
347    use serde::{
348        de::{Error, MapAccess, Visitor},
349        ser::SerializeMap,
350        Deserialize, Deserializer, Serialize, Serializer,
351    };
352
353    pub(super) fn serialize<S, T>(
354        map: &HashMap<Nibbles, T>,
355        serializer: S,
356    ) -> Result<S::Ok, S::Error>
357    where
358        S: Serializer,
359        T: Serialize,
360    {
361        let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
362        let mut storage_nodes = Vec::from_iter(map);
363        storage_nodes.sort_unstable_by_key(|node| node.0);
364        for (k, v) in storage_nodes {
365            // pack, then hex encode the Nibbles
366            let packed = alloy_primitives::hex::encode(k.pack());
367            map_serializer.serialize_entry(&packed, &v)?;
368        }
369        map_serializer.end()
370    }
371
372    pub(super) fn deserialize<'de, D, T>(deserializer: D) -> Result<HashMap<Nibbles, T>, D::Error>
373    where
374        D: Deserializer<'de>,
375        T: Deserialize<'de>,
376    {
377        struct NibblesMapVisitor<T> {
378            marker: PhantomData<T>,
379        }
380
381        impl<'de, T> Visitor<'de> for NibblesMapVisitor<T>
382        where
383            T: Deserialize<'de>,
384        {
385            type Value = HashMap<Nibbles, T>;
386
387            fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
388                formatter.write_str("a map with hex-encoded Nibbles keys")
389            }
390
391            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
392            where
393                A: MapAccess<'de>,
394            {
395                let mut result = HashMap::with_capacity_and_hasher(
396                    map.size_hint().unwrap_or(0),
397                    Default::default(),
398                );
399
400                while let Some((key, value)) = map.next_entry::<String, T>()? {
401                    let decoded_key =
402                        hex::decode(&key).map_err(|err| Error::custom(err.to_string()))?;
403
404                    let nibbles = Nibbles::unpack(&decoded_key);
405
406                    result.insert(nibbles, value);
407                }
408
409                Ok(result)
410            }
411        }
412
413        deserializer.deserialize_map(NibblesMapVisitor { marker: PhantomData })
414    }
415}
416
417/// Sorted trie updates reference used for serializing trie to file.
418#[derive(PartialEq, Eq, Clone, Default, Debug)]
419#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
420pub struct TrieUpdatesSortedRef<'a> {
421    /// Sorted collection of updated state nodes with corresponding paths.
422    pub account_nodes: Vec<(&'a Nibbles, &'a BranchNodeCompact)>,
423    /// The set of removed state node keys.
424    pub removed_nodes: BTreeSet<&'a Nibbles>,
425    /// Storage tries stored by hashed address of the account the trie belongs to.
426    pub storage_tries: BTreeMap<FixedBytes<32>, StorageTrieUpdatesSortedRef<'a>>,
427}
428
429/// Sorted trie updates used for lookups and insertions.
430#[derive(PartialEq, Eq, Clone, Default, Debug)]
431#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
432pub struct TrieUpdatesSorted {
433    /// Sorted collection of updated state nodes with corresponding paths. None indicates that a
434    /// node was removed.
435    account_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
436    /// Storage tries stored by hashed address of the account the trie belongs to.
437    storage_tries: B256Map<StorageTrieUpdatesSorted>,
438}
439
440impl TrieUpdatesSorted {
441    /// Creates a new `TrieUpdatesSorted` with the given account nodes and storage tries.
442    ///
443    /// # Panics
444    ///
445    /// In debug mode, panics if `account_nodes` is not sorted by the `Nibbles` key,
446    /// or if any storage trie's `storage_nodes` is not sorted by its `Nibbles` key.
447    pub fn new(
448        account_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
449        storage_tries: B256Map<StorageTrieUpdatesSorted>,
450    ) -> Self {
451        debug_assert!(
452            account_nodes.is_sorted_by_key(|item| &item.0),
453            "account_nodes must be sorted by Nibbles key"
454        );
455        debug_assert!(
456            storage_tries.values().all(|storage_trie| {
457                storage_trie.storage_nodes.is_sorted_by_key(|item| &item.0)
458            }),
459            "all storage_nodes in storage_tries must be sorted by Nibbles key"
460        );
461        Self { account_nodes, storage_tries }
462    }
463
464    /// Returns `true` if the updates are empty.
465    pub fn is_empty(&self) -> bool {
466        self.account_nodes.is_empty() && self.storage_tries.is_empty()
467    }
468
469    /// Returns reference to updated account nodes.
470    pub fn account_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
471        &self.account_nodes
472    }
473
474    /// Returns reference to updated storage tries.
475    pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdatesSorted> {
476        &self.storage_tries
477    }
478
479    /// Returns the total number of updates including account nodes and all storage updates.
480    pub fn total_len(&self) -> usize {
481        self.account_nodes.len() +
482            self.storage_tries.values().map(|storage| storage.len()).sum::<usize>()
483    }
484
485    /// Extends the trie updates with another set of sorted updates.
486    ///
487    /// This merges the account nodes and storage tries from `other` into `self`.
488    /// Account nodes are merged and re-sorted, with `other`'s values taking precedence
489    /// for duplicate keys.
490    pub fn extend_ref(&mut self, other: &Self) {
491        // Extend account nodes
492        extend_sorted_vec(&mut self.account_nodes, &other.account_nodes);
493
494        // Merge storage tries
495        for (hashed_address, storage_trie) in &other.storage_tries {
496            self.storage_tries
497                .entry(*hashed_address)
498                .and_modify(|existing| existing.extend_ref(storage_trie))
499                .or_insert_with(|| storage_trie.clone());
500        }
501    }
502}
503
504impl AsRef<Self> for TrieUpdatesSorted {
505    fn as_ref(&self) -> &Self {
506        self
507    }
508}
509
510impl From<TrieUpdatesSorted> for TrieUpdates {
511    fn from(sorted: TrieUpdatesSorted) -> Self {
512        let mut account_nodes = HashMap::default();
513        let mut removed_nodes = HashSet::default();
514
515        for (nibbles, node) in sorted.account_nodes {
516            if let Some(node) = node {
517                account_nodes.insert(nibbles, node);
518            } else {
519                removed_nodes.insert(nibbles);
520            }
521        }
522
523        let storage_tries = sorted
524            .storage_tries
525            .into_iter()
526            .map(|(address, storage)| (address, storage.into()))
527            .collect();
528
529        Self { account_nodes, removed_nodes, storage_tries }
530    }
531}
532
533/// Sorted storage trie updates reference used for serializing to file.
534#[derive(PartialEq, Eq, Clone, Default, Debug)]
535#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
536pub struct StorageTrieUpdatesSortedRef<'a> {
537    /// Flag indicating whether the trie has been deleted/wiped.
538    pub is_deleted: bool,
539    /// Sorted collection of updated storage nodes with corresponding paths.
540    pub storage_nodes: BTreeMap<&'a Nibbles, &'a BranchNodeCompact>,
541    /// The set of removed storage node keys.
542    pub removed_nodes: BTreeSet<&'a Nibbles>,
543}
544
545/// Sorted trie updates used for lookups and insertions.
546#[derive(PartialEq, Eq, Clone, Default, Debug)]
547#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
548pub struct StorageTrieUpdatesSorted {
549    /// Flag indicating whether the trie has been deleted/wiped.
550    pub is_deleted: bool,
551    /// Sorted collection of updated storage nodes with corresponding paths. None indicates a node
552    /// is removed.
553    pub storage_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
554}
555
556impl StorageTrieUpdatesSorted {
557    /// Returns `true` if the trie was deleted.
558    pub const fn is_deleted(&self) -> bool {
559        self.is_deleted
560    }
561
562    /// Returns reference to updated storage nodes.
563    pub fn storage_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
564        &self.storage_nodes
565    }
566
567    /// Returns the total number of storage node updates.
568    pub const fn len(&self) -> usize {
569        self.storage_nodes.len()
570    }
571
572    /// Returns `true` if there are no storage node updates.
573    pub const fn is_empty(&self) -> bool {
574        self.storage_nodes.is_empty()
575    }
576
577    /// Extends the storage trie updates with another set of sorted updates.
578    ///
579    /// If `other` is marked as deleted, this will be marked as deleted and all nodes cleared.
580    /// Otherwise, nodes are merged with `other`'s values taking precedence for duplicates.
581    pub fn extend_ref(&mut self, other: &Self) {
582        if other.is_deleted {
583            self.is_deleted = true;
584            self.storage_nodes.clear();
585            self.storage_nodes.extend(other.storage_nodes.iter().cloned());
586            return;
587        }
588
589        // Extend storage nodes
590        extend_sorted_vec(&mut self.storage_nodes, &other.storage_nodes);
591        self.is_deleted = self.is_deleted || other.is_deleted;
592    }
593}
594
595/// Excludes empty nibbles from the given iterator.
596fn exclude_empty(iter: impl IntoIterator<Item = Nibbles>) -> impl Iterator<Item = Nibbles> {
597    iter.into_iter().filter(|n| !n.is_empty())
598}
599
600/// Excludes empty nibbles from the given iterator of pairs where the nibbles are the key.
601fn exclude_empty_from_pair<V>(
602    iter: impl IntoIterator<Item = (Nibbles, V)>,
603) -> impl Iterator<Item = (Nibbles, V)> {
604    iter.into_iter().filter(|(n, _)| !n.is_empty())
605}
606
607impl From<StorageTrieUpdatesSorted> for StorageTrieUpdates {
608    fn from(sorted: StorageTrieUpdatesSorted) -> Self {
609        let mut storage_nodes = HashMap::default();
610        let mut removed_nodes = HashSet::default();
611
612        for (nibbles, node) in sorted.storage_nodes {
613            if let Some(node) = node {
614                storage_nodes.insert(nibbles, node);
615            } else {
616                removed_nodes.insert(nibbles);
617            }
618        }
619
620        Self { is_deleted: sorted.is_deleted, storage_nodes, removed_nodes }
621    }
622}
623
624#[cfg(test)]
625mod tests {
626    use super::*;
627    use alloy_primitives::B256;
628
629    #[test]
630    fn test_trie_updates_sorted_extend_ref() {
631        // Test extending with empty updates
632        let mut updates1 = TrieUpdatesSorted::default();
633        let updates2 = TrieUpdatesSorted::default();
634        updates1.extend_ref(&updates2);
635        assert_eq!(updates1.account_nodes.len(), 0);
636        assert_eq!(updates1.storage_tries.len(), 0);
637
638        // Test extending account nodes
639        let mut updates1 = TrieUpdatesSorted {
640            account_nodes: vec![
641                (Nibbles::from_nibbles_unchecked([0x01]), Some(BranchNodeCompact::default())),
642                (Nibbles::from_nibbles_unchecked([0x03]), None),
643            ],
644            storage_tries: B256Map::default(),
645        };
646        let updates2 = TrieUpdatesSorted {
647            account_nodes: vec![
648                (Nibbles::from_nibbles_unchecked([0x02]), Some(BranchNodeCompact::default())),
649                (Nibbles::from_nibbles_unchecked([0x03]), Some(BranchNodeCompact::default())), /* Override */
650            ],
651            storage_tries: B256Map::default(),
652        };
653        updates1.extend_ref(&updates2);
654        assert_eq!(updates1.account_nodes.len(), 3);
655        // Should be sorted: 0x01, 0x02, 0x03
656        assert_eq!(updates1.account_nodes[0].0, Nibbles::from_nibbles_unchecked([0x01]));
657        assert_eq!(updates1.account_nodes[1].0, Nibbles::from_nibbles_unchecked([0x02]));
658        assert_eq!(updates1.account_nodes[2].0, Nibbles::from_nibbles_unchecked([0x03]));
659        // 0x03 should have Some value from updates2 (override)
660        assert!(updates1.account_nodes[2].1.is_some());
661
662        // Test extending storage tries
663        let storage_trie1 = StorageTrieUpdatesSorted {
664            is_deleted: false,
665            storage_nodes: vec![(
666                Nibbles::from_nibbles_unchecked([0x0a]),
667                Some(BranchNodeCompact::default()),
668            )],
669        };
670        let storage_trie2 = StorageTrieUpdatesSorted {
671            is_deleted: false,
672            storage_nodes: vec![(Nibbles::from_nibbles_unchecked([0x0b]), None)],
673        };
674
675        let hashed_address1 = B256::from([1; 32]);
676        let hashed_address2 = B256::from([2; 32]);
677
678        let mut updates1 = TrieUpdatesSorted {
679            account_nodes: vec![],
680            storage_tries: B256Map::from_iter([(hashed_address1, storage_trie1.clone())]),
681        };
682        let updates2 = TrieUpdatesSorted {
683            account_nodes: vec![],
684            storage_tries: B256Map::from_iter([
685                (hashed_address1, storage_trie2),
686                (hashed_address2, storage_trie1),
687            ]),
688        };
689        updates1.extend_ref(&updates2);
690        assert_eq!(updates1.storage_tries.len(), 2);
691        assert!(updates1.storage_tries.contains_key(&hashed_address1));
692        assert!(updates1.storage_tries.contains_key(&hashed_address2));
693        // Check that storage trie for hashed_address1 was extended
694        let merged_storage = &updates1.storage_tries[&hashed_address1];
695        assert_eq!(merged_storage.storage_nodes.len(), 2);
696    }
697
698    #[test]
699    fn test_storage_trie_updates_sorted_extend_ref_deleted() {
700        // Test case 1: Extending with a deleted storage trie that has nodes
701        let mut storage1 = StorageTrieUpdatesSorted {
702            is_deleted: false,
703            storage_nodes: vec![
704                (Nibbles::from_nibbles_unchecked([0x01]), Some(BranchNodeCompact::default())),
705                (Nibbles::from_nibbles_unchecked([0x02]), None),
706            ],
707        };
708
709        let storage2 = StorageTrieUpdatesSorted {
710            is_deleted: true,
711            storage_nodes: vec![
712                (Nibbles::from_nibbles_unchecked([0x03]), Some(BranchNodeCompact::default())),
713                (Nibbles::from_nibbles_unchecked([0x04]), None),
714            ],
715        };
716
717        storage1.extend_ref(&storage2);
718
719        // Should be marked as deleted
720        assert!(storage1.is_deleted);
721        // Original nodes should be cleared, but other's nodes should be added
722        assert_eq!(storage1.storage_nodes.len(), 2);
723        assert_eq!(storage1.storage_nodes[0].0, Nibbles::from_nibbles_unchecked([0x03]));
724        assert_eq!(storage1.storage_nodes[1].0, Nibbles::from_nibbles_unchecked([0x04]));
725
726        // Test case 2: Extending a deleted storage trie with more nodes
727        let mut storage3 = StorageTrieUpdatesSorted {
728            is_deleted: true,
729            storage_nodes: vec![(
730                Nibbles::from_nibbles_unchecked([0x05]),
731                Some(BranchNodeCompact::default()),
732            )],
733        };
734
735        let storage4 = StorageTrieUpdatesSorted {
736            is_deleted: true,
737            storage_nodes: vec![
738                (Nibbles::from_nibbles_unchecked([0x06]), Some(BranchNodeCompact::default())),
739                (Nibbles::from_nibbles_unchecked([0x07]), None),
740            ],
741        };
742
743        storage3.extend_ref(&storage4);
744
745        // Should remain deleted
746        assert!(storage3.is_deleted);
747        // Should have nodes from other (original cleared then extended)
748        assert_eq!(storage3.storage_nodes.len(), 2);
749        assert_eq!(storage3.storage_nodes[0].0, Nibbles::from_nibbles_unchecked([0x06]));
750        assert_eq!(storage3.storage_nodes[1].0, Nibbles::from_nibbles_unchecked([0x07]));
751    }
752}
753
754/// Bincode-compatible trie updates type serde implementations.
755#[cfg(feature = "serde-bincode-compat")]
756pub mod serde_bincode_compat {
757    use crate::{BranchNodeCompact, Nibbles};
758    use alloc::borrow::Cow;
759    use alloy_primitives::map::{B256Map, HashMap, HashSet};
760    use serde::{Deserialize, Deserializer, Serialize, Serializer};
761    use serde_with::{DeserializeAs, SerializeAs};
762
763    /// Bincode-compatible [`super::TrieUpdates`] serde implementation.
764    ///
765    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
766    /// ```rust
767    /// use reth_trie_common::{serde_bincode_compat, updates::TrieUpdates};
768    /// use serde::{Deserialize, Serialize};
769    /// use serde_with::serde_as;
770    ///
771    /// #[serde_as]
772    /// #[derive(Serialize, Deserialize)]
773    /// struct Data {
774    ///     #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")]
775    ///     trie_updates: TrieUpdates,
776    /// }
777    /// ```
778    #[derive(Debug, Serialize, Deserialize)]
779    pub struct TrieUpdates<'a> {
780        account_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
781        removed_nodes: Cow<'a, HashSet<Nibbles>>,
782        storage_tries: B256Map<StorageTrieUpdates<'a>>,
783    }
784
785    impl<'a> From<&'a super::TrieUpdates> for TrieUpdates<'a> {
786        fn from(value: &'a super::TrieUpdates) -> Self {
787            Self {
788                account_nodes: Cow::Borrowed(&value.account_nodes),
789                removed_nodes: Cow::Borrowed(&value.removed_nodes),
790                storage_tries: value.storage_tries.iter().map(|(k, v)| (*k, v.into())).collect(),
791            }
792        }
793    }
794
795    impl<'a> From<TrieUpdates<'a>> for super::TrieUpdates {
796        fn from(value: TrieUpdates<'a>) -> Self {
797            Self {
798                account_nodes: value.account_nodes.into_owned(),
799                removed_nodes: value.removed_nodes.into_owned(),
800                storage_tries: value
801                    .storage_tries
802                    .into_iter()
803                    .map(|(k, v)| (k, v.into()))
804                    .collect(),
805            }
806        }
807    }
808
809    impl SerializeAs<super::TrieUpdates> for TrieUpdates<'_> {
810        fn serialize_as<S>(source: &super::TrieUpdates, serializer: S) -> Result<S::Ok, S::Error>
811        where
812            S: Serializer,
813        {
814            TrieUpdates::from(source).serialize(serializer)
815        }
816    }
817
818    impl<'de> DeserializeAs<'de, super::TrieUpdates> for TrieUpdates<'de> {
819        fn deserialize_as<D>(deserializer: D) -> Result<super::TrieUpdates, D::Error>
820        where
821            D: Deserializer<'de>,
822        {
823            TrieUpdates::deserialize(deserializer).map(Into::into)
824        }
825    }
826
827    /// Bincode-compatible [`super::StorageTrieUpdates`] serde implementation.
828    ///
829    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
830    /// ```rust
831    /// use reth_trie_common::{serde_bincode_compat, updates::StorageTrieUpdates};
832    /// use serde::{Deserialize, Serialize};
833    /// use serde_with::serde_as;
834    ///
835    /// #[serde_as]
836    /// #[derive(Serialize, Deserialize)]
837    /// struct Data {
838    ///     #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")]
839    ///     trie_updates: StorageTrieUpdates,
840    /// }
841    /// ```
842    #[derive(Debug, Serialize, Deserialize)]
843    pub struct StorageTrieUpdates<'a> {
844        is_deleted: bool,
845        storage_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
846        removed_nodes: Cow<'a, HashSet<Nibbles>>,
847    }
848
849    impl<'a> From<&'a super::StorageTrieUpdates> for StorageTrieUpdates<'a> {
850        fn from(value: &'a super::StorageTrieUpdates) -> Self {
851            Self {
852                is_deleted: value.is_deleted,
853                storage_nodes: Cow::Borrowed(&value.storage_nodes),
854                removed_nodes: Cow::Borrowed(&value.removed_nodes),
855            }
856        }
857    }
858
859    impl<'a> From<StorageTrieUpdates<'a>> for super::StorageTrieUpdates {
860        fn from(value: StorageTrieUpdates<'a>) -> Self {
861            Self {
862                is_deleted: value.is_deleted,
863                storage_nodes: value.storage_nodes.into_owned(),
864                removed_nodes: value.removed_nodes.into_owned(),
865            }
866        }
867    }
868
869    impl SerializeAs<super::StorageTrieUpdates> for StorageTrieUpdates<'_> {
870        fn serialize_as<S>(
871            source: &super::StorageTrieUpdates,
872            serializer: S,
873        ) -> Result<S::Ok, S::Error>
874        where
875            S: Serializer,
876        {
877            StorageTrieUpdates::from(source).serialize(serializer)
878        }
879    }
880
881    impl<'de> DeserializeAs<'de, super::StorageTrieUpdates> for StorageTrieUpdates<'de> {
882        fn deserialize_as<D>(deserializer: D) -> Result<super::StorageTrieUpdates, D::Error>
883        where
884            D: Deserializer<'de>,
885        {
886            StorageTrieUpdates::deserialize(deserializer).map(Into::into)
887        }
888    }
889
890    #[cfg(test)]
891    mod tests {
892        use crate::{
893            serde_bincode_compat,
894            updates::{StorageTrieUpdates, TrieUpdates},
895            BranchNodeCompact, Nibbles,
896        };
897        use alloy_primitives::B256;
898        use serde::{Deserialize, Serialize};
899        use serde_with::serde_as;
900
901        #[test]
902        fn test_trie_updates_bincode_roundtrip() {
903            #[serde_as]
904            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
905            struct Data {
906                #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")]
907                trie_updates: TrieUpdates,
908            }
909
910            let mut data = Data { trie_updates: TrieUpdates::default() };
911            let encoded = bincode::serialize(&data).unwrap();
912            let decoded: Data = bincode::deserialize(&encoded).unwrap();
913            assert_eq!(decoded, data);
914
915            data.trie_updates
916                .removed_nodes
917                .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
918            let encoded = bincode::serialize(&data).unwrap();
919            let decoded: Data = bincode::deserialize(&encoded).unwrap();
920            assert_eq!(decoded, data);
921
922            data.trie_updates.account_nodes.insert(
923                Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
924                BranchNodeCompact::default(),
925            );
926            let encoded = bincode::serialize(&data).unwrap();
927            let decoded: Data = bincode::deserialize(&encoded).unwrap();
928            assert_eq!(decoded, data);
929
930            data.trie_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
931            let encoded = bincode::serialize(&data).unwrap();
932            let decoded: Data = bincode::deserialize(&encoded).unwrap();
933            assert_eq!(decoded, data);
934        }
935
936        #[test]
937        fn test_storage_trie_updates_bincode_roundtrip() {
938            #[serde_as]
939            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
940            struct Data {
941                #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")]
942                trie_updates: StorageTrieUpdates,
943            }
944
945            let mut data = Data { trie_updates: StorageTrieUpdates::default() };
946            let encoded = bincode::serialize(&data).unwrap();
947            let decoded: Data = bincode::deserialize(&encoded).unwrap();
948            assert_eq!(decoded, data);
949
950            data.trie_updates
951                .removed_nodes
952                .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
953            let encoded = bincode::serialize(&data).unwrap();
954            let decoded: Data = bincode::deserialize(&encoded).unwrap();
955            assert_eq!(decoded, data);
956
957            data.trie_updates.storage_nodes.insert(
958                Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
959                BranchNodeCompact::default(),
960            );
961            let encoded = bincode::serialize(&data).unwrap();
962            let decoded: Data = bincode::deserialize(&encoded).unwrap();
963            assert_eq!(decoded, data);
964        }
965    }
966}
967
968#[cfg(all(test, feature = "serde"))]
969mod serde_tests {
970    use super::*;
971
972    #[test]
973    fn test_trie_updates_serde_roundtrip() {
974        let mut default_updates = TrieUpdates::default();
975        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
976        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
977        assert_eq!(updates_deserialized, default_updates);
978
979        default_updates
980            .removed_nodes
981            .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
982        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
983        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
984        assert_eq!(updates_deserialized, default_updates);
985
986        default_updates.account_nodes.insert(
987            Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
988            BranchNodeCompact::default(),
989        );
990        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
991        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
992        assert_eq!(updates_deserialized, default_updates);
993
994        default_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
995        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
996        let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
997        assert_eq!(updates_deserialized, default_updates);
998    }
999
1000    #[test]
1001    fn test_storage_trie_updates_serde_roundtrip() {
1002        let mut default_updates = StorageTrieUpdates::default();
1003        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1004        let updates_deserialized: StorageTrieUpdates =
1005            serde_json::from_str(&updates_serialized).unwrap();
1006        assert_eq!(updates_deserialized, default_updates);
1007
1008        default_updates
1009            .removed_nodes
1010            .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
1011        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1012        let updates_deserialized: StorageTrieUpdates =
1013            serde_json::from_str(&updates_serialized).unwrap();
1014        assert_eq!(updates_deserialized, default_updates);
1015
1016        default_updates.storage_nodes.insert(
1017            Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
1018            BranchNodeCompact::default(),
1019        );
1020        let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1021        let updates_deserialized: StorageTrieUpdates =
1022            serde_json::from_str(&updates_serialized).unwrap();
1023        assert_eq!(updates_deserialized, default_updates);
1024    }
1025}