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(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 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 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 pub fn clear(&mut self) {
163 self.account_nodes.clear();
164 self.removed_nodes.clear();
165 self.storage_tries.clear();
166 }
167}
168
169#[derive(PartialEq, Eq, Clone, Default, Debug)]
171#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
172pub struct StorageTrieUpdates {
173 pub is_deleted: bool,
175 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
177 pub storage_nodes: HashMap<Nibbles, BranchNodeCompact>,
178 #[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 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 pub fn deleted() -> Self {
194 Self {
195 is_deleted: true,
196 storage_nodes: HashMap::default(),
197 removed_nodes: HashSet::default(),
198 }
199 }
200
201 pub fn len(&self) -> usize {
203 (self.is_deleted as usize) + self.storage_nodes.len() + self.removed_nodes.len()
204 }
205
206 pub const fn is_deleted(&self) -> bool {
208 self.is_deleted
209 }
210
211 pub const fn storage_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
213 &self.storage_nodes
214 }
215
216 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
218 &self.removed_nodes
219 }
220
221 pub fn is_empty(&self) -> bool {
223 !self.is_deleted && self.storage_nodes.is_empty() && self.removed_nodes.is_empty()
224 }
225
226 pub const fn set_deleted(&mut self, deleted: bool) {
228 self.is_deleted = deleted;
229 }
230
231 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 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 pub fn finalize(&mut self, hash_builder: HashBuilder, removed_keys: HashSet<Nibbles>) {
260 let (_, updated_nodes) = hash_builder.split();
262 self.storage_nodes.extend(exclude_empty_from_pair(updated_nodes));
263
264 self.removed_nodes.extend(exclude_empty(removed_keys));
266 }
267
268 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 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#[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#[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 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#[derive(PartialEq, Eq, Clone, Default, Debug)]
414#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
415pub struct TrieUpdatesSortedRef<'a> {
416 pub account_nodes: Vec<(&'a Nibbles, &'a BranchNodeCompact)>,
418 pub removed_nodes: BTreeSet<&'a Nibbles>,
420 pub storage_tries: BTreeMap<FixedBytes<32>, StorageTrieUpdatesSortedRef<'a>>,
422}
423
424#[derive(PartialEq, Eq, Clone, Default, Debug)]
426#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
427pub struct TrieUpdatesSorted {
428 pub account_nodes: Vec<(Nibbles, BranchNodeCompact)>,
430 pub removed_nodes: HashSet<Nibbles>,
432 pub storage_tries: B256Map<StorageTrieUpdatesSorted>,
434}
435
436impl TrieUpdatesSorted {
437 pub fn account_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] {
439 &self.account_nodes
440 }
441
442 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
444 &self.removed_nodes
445 }
446
447 pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdatesSorted> {
449 &self.storage_tries
450 }
451}
452
453#[derive(PartialEq, Eq, Clone, Default, Debug)]
455#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
456pub struct StorageTrieUpdatesSortedRef<'a> {
457 pub is_deleted: bool,
459 pub storage_nodes: BTreeMap<&'a Nibbles, &'a BranchNodeCompact>,
461 pub removed_nodes: BTreeSet<&'a Nibbles>,
463}
464
465#[derive(PartialEq, Eq, Clone, Default, Debug)]
467#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
468pub struct StorageTrieUpdatesSorted {
469 pub is_deleted: bool,
471 pub storage_nodes: Vec<(Nibbles, BranchNodeCompact)>,
473 pub removed_nodes: HashSet<Nibbles>,
475}
476
477impl StorageTrieUpdatesSorted {
478 pub const fn is_deleted(&self) -> bool {
480 self.is_deleted
481 }
482
483 pub fn storage_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] {
485 &self.storage_nodes
486 }
487
488 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
490 &self.removed_nodes
491 }
492}
493
494fn exclude_empty(iter: impl IntoIterator<Item = Nibbles>) -> impl Iterator<Item = Nibbles> {
496 iter.into_iter().filter(|n| !n.is_empty())
497}
498
499fn 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#[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 #[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 #[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}