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#[derive(PartialEq, Eq, Clone, Default, Debug)]
13#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
14pub struct TrieUpdates {
15 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
17 pub account_nodes: HashMap<Nibbles, BranchNodeCompact>,
18 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_set"))]
20 pub removed_nodes: HashSet<Nibbles>,
21 pub storage_tries: B256Map<StorageTrieUpdates>,
23}
24
25impl TrieUpdates {
26 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 pub const fn account_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
35 &self.account_nodes
36 }
37
38 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
40 &self.removed_nodes
41 }
42
43 pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdates> {
45 &self.storage_tries
46 }
47
48 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 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 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 pub fn finalize(
91 &mut self,
92 hash_builder: HashBuilder,
93 removed_keys: HashSet<Nibbles>,
94 destroyed_accounts: B256Set,
95 ) {
96 let (_, updated_nodes) = hash_builder.split();
98 self.account_nodes.extend(exclude_empty_from_pair(updated_nodes));
99
100 self.removed_nodes.extend(exclude_empty(removed_keys));
102
103 for destroyed in destroyed_accounts {
105 self.storage_tries.entry(destroyed).or_default().set_deleted(true);
106 }
107 }
108
109 pub fn into_sorted(mut self) -> TrieUpdatesSorted {
111 self.drain_into_sorted()
112 }
113
114 pub fn drain_into_sorted(&mut self) -> TrieUpdatesSorted {
122 let mut account_nodes = self
123 .account_nodes
124 .drain()
125 .map(|(path, node)| {
126 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 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 pub fn clear(&mut self) {
161 self.account_nodes.clear();
162 self.removed_nodes.clear();
163 self.storage_tries.clear();
164 }
165}
166
167#[derive(PartialEq, Eq, Clone, Default, Debug)]
169#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
170pub struct StorageTrieUpdates {
171 pub is_deleted: bool,
173 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
175 pub storage_nodes: HashMap<Nibbles, BranchNodeCompact>,
176 #[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 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 pub fn deleted() -> Self {
192 Self {
193 is_deleted: true,
194 storage_nodes: HashMap::default(),
195 removed_nodes: HashSet::default(),
196 }
197 }
198
199 pub fn len(&self) -> usize {
201 (self.is_deleted as usize) + self.storage_nodes.len() + self.removed_nodes.len()
202 }
203
204 pub const fn is_deleted(&self) -> bool {
206 self.is_deleted
207 }
208
209 pub const fn storage_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
211 &self.storage_nodes
212 }
213
214 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
216 &self.removed_nodes
217 }
218
219 pub fn is_empty(&self) -> bool {
221 !self.is_deleted && self.storage_nodes.is_empty() && self.removed_nodes.is_empty()
222 }
223
224 pub const fn set_deleted(&mut self, deleted: bool) {
226 self.is_deleted = deleted;
227 }
228
229 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 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 pub fn finalize(&mut self, hash_builder: HashBuilder, removed_keys: HashSet<Nibbles>) {
258 let (_, updated_nodes) = hash_builder.split();
260 self.storage_nodes.extend(exclude_empty_from_pair(updated_nodes));
261
262 self.removed_nodes.extend(exclude_empty(removed_keys));
264 }
265
266 pub fn into_sorted(mut self) -> StorageTrieUpdatesSorted {
268 let mut storage_nodes = self
269 .storage_nodes
270 .into_iter()
271 .map(|(path, node)| {
272 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 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#[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#[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 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#[derive(PartialEq, Eq, Clone, Default, Debug)]
419#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
420pub struct TrieUpdatesSortedRef<'a> {
421 pub account_nodes: Vec<(&'a Nibbles, &'a BranchNodeCompact)>,
423 pub removed_nodes: BTreeSet<&'a Nibbles>,
425 pub storage_tries: BTreeMap<FixedBytes<32>, StorageTrieUpdatesSortedRef<'a>>,
427}
428
429#[derive(PartialEq, Eq, Clone, Default, Debug)]
431#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
432pub struct TrieUpdatesSorted {
433 account_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
436 storage_tries: B256Map<StorageTrieUpdatesSorted>,
438}
439
440impl TrieUpdatesSorted {
441 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 pub fn is_empty(&self) -> bool {
466 self.account_nodes.is_empty() && self.storage_tries.is_empty()
467 }
468
469 pub fn account_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
471 &self.account_nodes
472 }
473
474 pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdatesSorted> {
476 &self.storage_tries
477 }
478
479 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 pub fn extend_ref(&mut self, other: &Self) {
491 extend_sorted_vec(&mut self.account_nodes, &other.account_nodes);
493
494 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#[derive(PartialEq, Eq, Clone, Default, Debug)]
535#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
536pub struct StorageTrieUpdatesSortedRef<'a> {
537 pub is_deleted: bool,
539 pub storage_nodes: BTreeMap<&'a Nibbles, &'a BranchNodeCompact>,
541 pub removed_nodes: BTreeSet<&'a Nibbles>,
543}
544
545#[derive(PartialEq, Eq, Clone, Default, Debug)]
547#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
548pub struct StorageTrieUpdatesSorted {
549 pub is_deleted: bool,
551 pub storage_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
554}
555
556impl StorageTrieUpdatesSorted {
557 pub const fn is_deleted(&self) -> bool {
559 self.is_deleted
560 }
561
562 pub fn storage_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
564 &self.storage_nodes
565 }
566
567 pub const fn len(&self) -> usize {
569 self.storage_nodes.len()
570 }
571
572 pub const fn is_empty(&self) -> bool {
574 self.storage_nodes.is_empty()
575 }
576
577 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_sorted_vec(&mut self.storage_nodes, &other.storage_nodes);
591 self.is_deleted = self.is_deleted || other.is_deleted;
592 }
593}
594
595fn exclude_empty(iter: impl IntoIterator<Item = Nibbles>) -> impl Iterator<Item = Nibbles> {
597 iter.into_iter().filter(|n| !n.is_empty())
598}
599
600fn 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 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 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())), ],
651 storage_tries: B256Map::default(),
652 };
653 updates1.extend_ref(&updates2);
654 assert_eq!(updates1.account_nodes.len(), 3);
655 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 assert!(updates1.account_nodes[2].1.is_some());
661
662 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 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 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 assert!(storage1.is_deleted);
721 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 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 assert!(storage3.is_deleted);
747 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#[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 #[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 #[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}