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#[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 pub account_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
436 pub storage_tries: B256Map<StorageTrieUpdatesSorted>,
438}
439
440impl TrieUpdatesSorted {
441 pub fn account_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
443 &self.account_nodes
444 }
445
446 pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdatesSorted> {
448 &self.storage_tries
449 }
450}
451
452#[derive(PartialEq, Eq, Clone, Default, Debug)]
454#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
455pub struct StorageTrieUpdatesSortedRef<'a> {
456 pub is_deleted: bool,
458 pub storage_nodes: BTreeMap<&'a Nibbles, &'a BranchNodeCompact>,
460 pub removed_nodes: BTreeSet<&'a Nibbles>,
462}
463
464#[derive(PartialEq, Eq, Clone, Default, Debug)]
466#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
467pub struct StorageTrieUpdatesSorted {
468 pub is_deleted: bool,
470 pub storage_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
473}
474
475impl StorageTrieUpdatesSorted {
476 pub const fn is_deleted(&self) -> bool {
478 self.is_deleted
479 }
480
481 pub fn storage_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
483 &self.storage_nodes
484 }
485}
486
487fn exclude_empty(iter: impl IntoIterator<Item = Nibbles>) -> impl Iterator<Item = Nibbles> {
489 iter.into_iter().filter(|n| !n.is_empty())
490}
491
492fn exclude_empty_from_pair<V>(
494 iter: impl IntoIterator<Item = (Nibbles, V)>,
495) -> impl Iterator<Item = (Nibbles, V)> {
496 iter.into_iter().filter(|(n, _)| !n.is_empty())
497}
498
499#[cfg(feature = "serde-bincode-compat")]
501pub mod serde_bincode_compat {
502 use crate::{BranchNodeCompact, Nibbles};
503 use alloc::borrow::Cow;
504 use alloy_primitives::map::{B256Map, HashMap, HashSet};
505 use serde::{Deserialize, Deserializer, Serialize, Serializer};
506 use serde_with::{DeserializeAs, SerializeAs};
507
508 #[derive(Debug, Serialize, Deserialize)]
524 pub struct TrieUpdates<'a> {
525 account_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
526 removed_nodes: Cow<'a, HashSet<Nibbles>>,
527 storage_tries: B256Map<StorageTrieUpdates<'a>>,
528 }
529
530 impl<'a> From<&'a super::TrieUpdates> for TrieUpdates<'a> {
531 fn from(value: &'a super::TrieUpdates) -> Self {
532 Self {
533 account_nodes: Cow::Borrowed(&value.account_nodes),
534 removed_nodes: Cow::Borrowed(&value.removed_nodes),
535 storage_tries: value.storage_tries.iter().map(|(k, v)| (*k, v.into())).collect(),
536 }
537 }
538 }
539
540 impl<'a> From<TrieUpdates<'a>> for super::TrieUpdates {
541 fn from(value: TrieUpdates<'a>) -> Self {
542 Self {
543 account_nodes: value.account_nodes.into_owned(),
544 removed_nodes: value.removed_nodes.into_owned(),
545 storage_tries: value
546 .storage_tries
547 .into_iter()
548 .map(|(k, v)| (k, v.into()))
549 .collect(),
550 }
551 }
552 }
553
554 impl SerializeAs<super::TrieUpdates> for TrieUpdates<'_> {
555 fn serialize_as<S>(source: &super::TrieUpdates, serializer: S) -> Result<S::Ok, S::Error>
556 where
557 S: Serializer,
558 {
559 TrieUpdates::from(source).serialize(serializer)
560 }
561 }
562
563 impl<'de> DeserializeAs<'de, super::TrieUpdates> for TrieUpdates<'de> {
564 fn deserialize_as<D>(deserializer: D) -> Result<super::TrieUpdates, D::Error>
565 where
566 D: Deserializer<'de>,
567 {
568 TrieUpdates::deserialize(deserializer).map(Into::into)
569 }
570 }
571
572 #[derive(Debug, Serialize, Deserialize)]
588 pub struct StorageTrieUpdates<'a> {
589 is_deleted: bool,
590 storage_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
591 removed_nodes: Cow<'a, HashSet<Nibbles>>,
592 }
593
594 impl<'a> From<&'a super::StorageTrieUpdates> for StorageTrieUpdates<'a> {
595 fn from(value: &'a super::StorageTrieUpdates) -> Self {
596 Self {
597 is_deleted: value.is_deleted,
598 storage_nodes: Cow::Borrowed(&value.storage_nodes),
599 removed_nodes: Cow::Borrowed(&value.removed_nodes),
600 }
601 }
602 }
603
604 impl<'a> From<StorageTrieUpdates<'a>> for super::StorageTrieUpdates {
605 fn from(value: StorageTrieUpdates<'a>) -> Self {
606 Self {
607 is_deleted: value.is_deleted,
608 storage_nodes: value.storage_nodes.into_owned(),
609 removed_nodes: value.removed_nodes.into_owned(),
610 }
611 }
612 }
613
614 impl SerializeAs<super::StorageTrieUpdates> for StorageTrieUpdates<'_> {
615 fn serialize_as<S>(
616 source: &super::StorageTrieUpdates,
617 serializer: S,
618 ) -> Result<S::Ok, S::Error>
619 where
620 S: Serializer,
621 {
622 StorageTrieUpdates::from(source).serialize(serializer)
623 }
624 }
625
626 impl<'de> DeserializeAs<'de, super::StorageTrieUpdates> for StorageTrieUpdates<'de> {
627 fn deserialize_as<D>(deserializer: D) -> Result<super::StorageTrieUpdates, D::Error>
628 where
629 D: Deserializer<'de>,
630 {
631 StorageTrieUpdates::deserialize(deserializer).map(Into::into)
632 }
633 }
634
635 #[cfg(test)]
636 mod tests {
637 use crate::{
638 serde_bincode_compat,
639 updates::{StorageTrieUpdates, TrieUpdates},
640 BranchNodeCompact, Nibbles,
641 };
642 use alloy_primitives::B256;
643 use serde::{Deserialize, Serialize};
644 use serde_with::serde_as;
645
646 #[test]
647 fn test_trie_updates_bincode_roundtrip() {
648 #[serde_as]
649 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
650 struct Data {
651 #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")]
652 trie_updates: TrieUpdates,
653 }
654
655 let mut data = Data { trie_updates: TrieUpdates::default() };
656 let encoded = bincode::serialize(&data).unwrap();
657 let decoded: Data = bincode::deserialize(&encoded).unwrap();
658 assert_eq!(decoded, data);
659
660 data.trie_updates
661 .removed_nodes
662 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
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.account_nodes.insert(
668 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
669 BranchNodeCompact::default(),
670 );
671 let encoded = bincode::serialize(&data).unwrap();
672 let decoded: Data = bincode::deserialize(&encoded).unwrap();
673 assert_eq!(decoded, data);
674
675 data.trie_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
676 let encoded = bincode::serialize(&data).unwrap();
677 let decoded: Data = bincode::deserialize(&encoded).unwrap();
678 assert_eq!(decoded, data);
679 }
680
681 #[test]
682 fn test_storage_trie_updates_bincode_roundtrip() {
683 #[serde_as]
684 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
685 struct Data {
686 #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")]
687 trie_updates: StorageTrieUpdates,
688 }
689
690 let mut data = Data { trie_updates: StorageTrieUpdates::default() };
691 let encoded = bincode::serialize(&data).unwrap();
692 let decoded: Data = bincode::deserialize(&encoded).unwrap();
693 assert_eq!(decoded, data);
694
695 data.trie_updates
696 .removed_nodes
697 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
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.storage_nodes.insert(
703 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
704 BranchNodeCompact::default(),
705 );
706 let encoded = bincode::serialize(&data).unwrap();
707 let decoded: Data = bincode::deserialize(&encoded).unwrap();
708 assert_eq!(decoded, data);
709 }
710 }
711}
712
713#[cfg(all(test, feature = "serde"))]
714mod tests {
715 use super::*;
716
717 #[test]
718 fn test_trie_updates_serde_roundtrip() {
719 let mut default_updates = TrieUpdates::default();
720 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
721 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
722 assert_eq!(updates_deserialized, default_updates);
723
724 default_updates
725 .removed_nodes
726 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
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.account_nodes.insert(
732 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
733 BranchNodeCompact::default(),
734 );
735 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
736 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
737 assert_eq!(updates_deserialized, default_updates);
738
739 default_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
740 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
741 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
742 assert_eq!(updates_deserialized, default_updates);
743 }
744
745 #[test]
746 fn test_storage_trie_updates_serde_roundtrip() {
747 let mut default_updates = StorageTrieUpdates::default();
748 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
749 let updates_deserialized: StorageTrieUpdates =
750 serde_json::from_str(&updates_serialized).unwrap();
751 assert_eq!(updates_deserialized, default_updates);
752
753 default_updates
754 .removed_nodes
755 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
756 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
757 let updates_deserialized: StorageTrieUpdates =
758 serde_json::from_str(&updates_serialized).unwrap();
759 assert_eq!(updates_deserialized, default_updates);
760
761 default_updates.storage_nodes.insert(
762 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
763 BranchNodeCompact::default(),
764 );
765 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
766 let updates_deserialized: StorageTrieUpdates =
767 serde_json::from_str(&updates_serialized).unwrap();
768 assert_eq!(updates_deserialized, default_updates);
769 }
770}