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 extend_from_sorted(&mut self, sorted: &TrieUpdatesSorted) {
83 let new_nodes_count = sorted.account_nodes.len();
85 self.account_nodes.reserve(new_nodes_count);
86
87 for (nibbles, maybe_node) in &sorted.account_nodes {
89 if nibbles.is_empty() {
90 continue;
91 }
92 match maybe_node {
93 Some(node) => {
94 self.removed_nodes.remove(nibbles);
95 self.account_nodes.insert(*nibbles, node.clone());
96 }
97 None => {
98 self.account_nodes.remove(nibbles);
99 self.removed_nodes.insert(*nibbles);
100 }
101 }
102 }
103
104 self.storage_tries.reserve(sorted.storage_tries.len());
106 for (hashed_address, sorted_storage) in &sorted.storage_tries {
107 self.storage_tries
108 .entry(*hashed_address)
109 .or_default()
110 .extend_from_sorted(sorted_storage);
111 }
112 }
113
114 pub fn insert_storage_updates(
116 &mut self,
117 hashed_address: B256,
118 storage_updates: StorageTrieUpdates,
119 ) {
120 if storage_updates.is_empty() {
121 return;
122 }
123 let existing = self.storage_tries.insert(hashed_address, storage_updates);
124 debug_assert!(existing.is_none());
125 }
126
127 pub fn finalize(
129 &mut self,
130 hash_builder: HashBuilder,
131 removed_keys: HashSet<Nibbles>,
132 destroyed_accounts: B256Set,
133 ) {
134 let (_, updated_nodes) = hash_builder.split();
136 self.account_nodes.extend(exclude_empty_from_pair(updated_nodes));
137
138 self.removed_nodes.extend(exclude_empty(removed_keys));
140
141 for destroyed in destroyed_accounts {
143 self.storage_tries.entry(destroyed).or_default().set_deleted(true);
144 }
145 }
146
147 pub fn into_sorted(mut self) -> TrieUpdatesSorted {
149 let mut account_nodes = self
150 .account_nodes
151 .drain()
152 .map(|(path, node)| {
153 self.removed_nodes.remove(&path);
155 (path, Some(node))
156 })
157 .collect::<Vec<_>>();
158
159 account_nodes.extend(self.removed_nodes.drain().map(|path| (path, None)));
160 account_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
161
162 let storage_tries = self
163 .storage_tries
164 .drain()
165 .map(|(hashed_address, updates)| (hashed_address, updates.into_sorted()))
166 .collect();
167 TrieUpdatesSorted { account_nodes, storage_tries }
168 }
169
170 pub fn into_sorted_ref<'a>(&'a self) -> TrieUpdatesSortedRef<'a> {
172 let mut account_nodes = self.account_nodes.iter().collect::<Vec<_>>();
173 account_nodes.sort_unstable_by(|a, b| a.0.cmp(b.0));
174
175 TrieUpdatesSortedRef {
176 removed_nodes: self.removed_nodes.iter().collect::<BTreeSet<_>>(),
177 account_nodes,
178 storage_tries: self
179 .storage_tries
180 .iter()
181 .map(|m| (*m.0, m.1.into_sorted_ref().clone()))
182 .collect(),
183 }
184 }
185
186 pub fn clear(&mut self) {
188 self.account_nodes.clear();
189 self.removed_nodes.clear();
190 self.storage_tries.clear();
191 }
192}
193
194#[derive(PartialEq, Eq, Clone, Default, Debug)]
196#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
197pub struct StorageTrieUpdates {
198 pub is_deleted: bool,
200 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
202 pub storage_nodes: HashMap<Nibbles, BranchNodeCompact>,
203 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_set"))]
205 pub removed_nodes: HashSet<Nibbles>,
206}
207
208#[cfg(feature = "test-utils")]
209impl StorageTrieUpdates {
210 pub fn new(updates: impl IntoIterator<Item = (Nibbles, BranchNodeCompact)>) -> Self {
212 Self { storage_nodes: exclude_empty_from_pair(updates).collect(), ..Default::default() }
213 }
214}
215
216impl StorageTrieUpdates {
217 pub fn deleted() -> Self {
219 Self {
220 is_deleted: true,
221 storage_nodes: HashMap::default(),
222 removed_nodes: HashSet::default(),
223 }
224 }
225
226 pub fn len(&self) -> usize {
228 (self.is_deleted as usize) + self.storage_nodes.len() + self.removed_nodes.len()
229 }
230
231 pub const fn is_deleted(&self) -> bool {
233 self.is_deleted
234 }
235
236 pub const fn storage_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
238 &self.storage_nodes
239 }
240
241 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
243 &self.removed_nodes
244 }
245
246 pub fn is_empty(&self) -> bool {
248 !self.is_deleted && self.storage_nodes.is_empty() && self.removed_nodes.is_empty()
249 }
250
251 pub const fn set_deleted(&mut self, deleted: bool) {
253 self.is_deleted = deleted;
254 }
255
256 pub fn extend(&mut self, other: Self) {
258 self.extend_common(&other);
259 self.storage_nodes.extend(exclude_empty_from_pair(other.storage_nodes));
260 self.removed_nodes.extend(exclude_empty(other.removed_nodes));
261 }
262
263 pub fn extend_ref(&mut self, other: &Self) {
267 self.extend_common(other);
268 self.storage_nodes.extend(exclude_empty_from_pair(
269 other.storage_nodes.iter().map(|(k, v)| (*k, v.clone())),
270 ));
271 self.removed_nodes.extend(exclude_empty(other.removed_nodes.iter().copied()));
272 }
273
274 fn extend_common(&mut self, other: &Self) {
275 if other.is_deleted {
276 self.storage_nodes.clear();
277 self.removed_nodes.clear();
278 }
279 self.is_deleted |= other.is_deleted;
280 self.storage_nodes.retain(|nibbles, _| !other.removed_nodes.contains(nibbles));
281 }
282
283 pub fn extend_from_sorted(&mut self, sorted: &StorageTrieUpdatesSorted) {
290 if sorted.is_deleted {
291 self.storage_nodes.clear();
292 self.removed_nodes.clear();
293 }
294 self.is_deleted |= sorted.is_deleted;
295
296 let new_nodes_count = sorted.storage_nodes.len();
298 self.storage_nodes.reserve(new_nodes_count);
299
300 for (nibbles, maybe_node) in &sorted.storage_nodes {
302 if nibbles.is_empty() {
303 continue;
304 }
305 if let Some(node) = maybe_node {
306 self.removed_nodes.remove(nibbles);
307 self.storage_nodes.insert(*nibbles, node.clone());
308 } else {
309 self.storage_nodes.remove(nibbles);
310 self.removed_nodes.insert(*nibbles);
311 }
312 }
313 }
314
315 pub fn finalize(&mut self, hash_builder: HashBuilder, removed_keys: HashSet<Nibbles>) {
317 let (_, updated_nodes) = hash_builder.split();
319 self.storage_nodes.extend(exclude_empty_from_pair(updated_nodes));
320
321 self.removed_nodes.extend(exclude_empty(removed_keys));
323 }
324
325 pub fn into_sorted(mut self) -> StorageTrieUpdatesSorted {
327 let mut storage_nodes = self
328 .storage_nodes
329 .into_iter()
330 .map(|(path, node)| {
331 self.removed_nodes.remove(&path);
333 (path, Some(node))
334 })
335 .collect::<Vec<_>>();
336
337 storage_nodes.extend(self.removed_nodes.into_iter().map(|path| (path, None)));
338 storage_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
339
340 StorageTrieUpdatesSorted { is_deleted: self.is_deleted, storage_nodes }
341 }
342
343 pub fn into_sorted_ref(&self) -> StorageTrieUpdatesSortedRef<'_> {
345 StorageTrieUpdatesSortedRef {
346 is_deleted: self.is_deleted,
347 removed_nodes: self.removed_nodes.iter().collect::<BTreeSet<_>>(),
348 storage_nodes: self.storage_nodes.iter().collect::<BTreeMap<_, _>>(),
349 }
350 }
351}
352
353#[cfg(any(test, feature = "serde"))]
358mod serde_nibbles_set {
359 use crate::Nibbles;
360 use alloc::{
361 string::{String, ToString},
362 vec::Vec,
363 };
364 use alloy_primitives::map::HashSet;
365 use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
366
367 pub(super) fn serialize<S>(map: &HashSet<Nibbles>, serializer: S) -> Result<S::Ok, S::Error>
368 where
369 S: Serializer,
370 {
371 let mut storage_nodes =
372 map.iter().map(|elem| alloy_primitives::hex::encode(elem.pack())).collect::<Vec<_>>();
373 storage_nodes.sort_unstable();
374 storage_nodes.serialize(serializer)
375 }
376
377 pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<HashSet<Nibbles>, D::Error>
378 where
379 D: Deserializer<'de>,
380 {
381 Vec::<String>::deserialize(deserializer)?
382 .into_iter()
383 .map(|node| {
384 Ok(Nibbles::unpack(
385 alloy_primitives::hex::decode(node)
386 .map_err(|err| D::Error::custom(err.to_string()))?,
387 ))
388 })
389 .collect::<Result<HashSet<_>, _>>()
390 }
391}
392
393#[cfg(any(test, feature = "serde"))]
398mod serde_nibbles_map {
399 use crate::Nibbles;
400 use alloc::{
401 string::{String, ToString},
402 vec::Vec,
403 };
404 use alloy_primitives::{hex, map::HashMap};
405 use core::marker::PhantomData;
406 use serde::{
407 de::{Error, MapAccess, Visitor},
408 ser::SerializeMap,
409 Deserialize, Deserializer, Serialize, Serializer,
410 };
411
412 pub(super) fn serialize<S, T>(
413 map: &HashMap<Nibbles, T>,
414 serializer: S,
415 ) -> Result<S::Ok, S::Error>
416 where
417 S: Serializer,
418 T: Serialize,
419 {
420 let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
421 let mut storage_nodes = Vec::from_iter(map);
422 storage_nodes.sort_unstable_by_key(|node| node.0);
423 for (k, v) in storage_nodes {
424 let packed = alloy_primitives::hex::encode(k.pack());
426 map_serializer.serialize_entry(&packed, &v)?;
427 }
428 map_serializer.end()
429 }
430
431 pub(super) fn deserialize<'de, D, T>(deserializer: D) -> Result<HashMap<Nibbles, T>, D::Error>
432 where
433 D: Deserializer<'de>,
434 T: Deserialize<'de>,
435 {
436 struct NibblesMapVisitor<T> {
437 marker: PhantomData<T>,
438 }
439
440 impl<'de, T> Visitor<'de> for NibblesMapVisitor<T>
441 where
442 T: Deserialize<'de>,
443 {
444 type Value = HashMap<Nibbles, T>;
445
446 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
447 formatter.write_str("a map with hex-encoded Nibbles keys")
448 }
449
450 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
451 where
452 A: MapAccess<'de>,
453 {
454 let mut result = HashMap::with_capacity_and_hasher(
455 map.size_hint().unwrap_or(0),
456 Default::default(),
457 );
458
459 while let Some((key, value)) = map.next_entry::<String, T>()? {
460 let decoded_key =
461 hex::decode(&key).map_err(|err| Error::custom(err.to_string()))?;
462
463 let nibbles = Nibbles::unpack(&decoded_key);
464
465 result.insert(nibbles, value);
466 }
467
468 Ok(result)
469 }
470 }
471
472 deserializer.deserialize_map(NibblesMapVisitor { marker: PhantomData })
473 }
474}
475
476#[derive(PartialEq, Eq, Clone, Default, Debug)]
478#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
479pub struct TrieUpdatesSortedRef<'a> {
480 pub account_nodes: Vec<(&'a Nibbles, &'a BranchNodeCompact)>,
482 pub removed_nodes: BTreeSet<&'a Nibbles>,
484 pub storage_tries: BTreeMap<FixedBytes<32>, StorageTrieUpdatesSortedRef<'a>>,
486}
487
488#[derive(PartialEq, Eq, Clone, Default, Debug)]
490#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
491pub struct TrieUpdatesSorted {
492 account_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
495 storage_tries: B256Map<StorageTrieUpdatesSorted>,
497}
498
499impl TrieUpdatesSorted {
500 pub fn new(
507 account_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
508 storage_tries: B256Map<StorageTrieUpdatesSorted>,
509 ) -> Self {
510 debug_assert!(
511 account_nodes.is_sorted_by_key(|item| &item.0),
512 "account_nodes must be sorted by Nibbles key"
513 );
514 debug_assert!(
515 storage_tries.values().all(|storage_trie| {
516 storage_trie.storage_nodes.is_sorted_by_key(|item| &item.0)
517 }),
518 "all storage_nodes in storage_tries must be sorted by Nibbles key"
519 );
520 Self { account_nodes, storage_tries }
521 }
522
523 pub fn is_empty(&self) -> bool {
525 self.account_nodes.is_empty() && self.storage_tries.is_empty()
526 }
527
528 pub fn account_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
530 &self.account_nodes
531 }
532
533 pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdatesSorted> {
535 &self.storage_tries
536 }
537
538 pub fn total_len(&self) -> usize {
540 self.account_nodes.len() +
541 self.storage_tries.values().map(|storage| storage.len()).sum::<usize>()
542 }
543
544 pub fn extend_ref(&mut self, other: &Self) {
550 extend_sorted_vec(&mut self.account_nodes, &other.account_nodes);
552
553 for (hashed_address, storage_trie) in &other.storage_tries {
555 self.storage_tries
556 .entry(*hashed_address)
557 .and_modify(|existing| existing.extend_ref(storage_trie))
558 .or_insert_with(|| storage_trie.clone());
559 }
560 }
561
562 pub fn clear(&mut self) {
564 self.account_nodes.clear();
565 self.storage_tries.clear();
566 }
567}
568
569impl AsRef<Self> for TrieUpdatesSorted {
570 fn as_ref(&self) -> &Self {
571 self
572 }
573}
574
575impl From<TrieUpdatesSorted> for TrieUpdates {
576 fn from(sorted: TrieUpdatesSorted) -> Self {
577 let mut account_nodes = HashMap::default();
578 let mut removed_nodes = HashSet::default();
579
580 for (nibbles, node) in sorted.account_nodes {
581 if let Some(node) = node {
582 account_nodes.insert(nibbles, node);
583 } else {
584 removed_nodes.insert(nibbles);
585 }
586 }
587
588 let storage_tries = sorted
589 .storage_tries
590 .into_iter()
591 .map(|(address, storage)| (address, storage.into()))
592 .collect();
593
594 Self { account_nodes, removed_nodes, storage_tries }
595 }
596}
597
598#[derive(PartialEq, Eq, Clone, Default, Debug)]
600#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))]
601pub struct StorageTrieUpdatesSortedRef<'a> {
602 pub is_deleted: bool,
604 pub storage_nodes: BTreeMap<&'a Nibbles, &'a BranchNodeCompact>,
606 pub removed_nodes: BTreeSet<&'a Nibbles>,
608}
609
610#[derive(PartialEq, Eq, Clone, Default, Debug)]
612#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
613pub struct StorageTrieUpdatesSorted {
614 pub is_deleted: bool,
616 pub storage_nodes: Vec<(Nibbles, Option<BranchNodeCompact>)>,
619}
620
621impl StorageTrieUpdatesSorted {
622 pub const fn is_deleted(&self) -> bool {
624 self.is_deleted
625 }
626
627 pub fn storage_nodes_ref(&self) -> &[(Nibbles, Option<BranchNodeCompact>)] {
629 &self.storage_nodes
630 }
631
632 pub const fn len(&self) -> usize {
634 self.storage_nodes.len()
635 }
636
637 pub const fn is_empty(&self) -> bool {
639 self.storage_nodes.is_empty()
640 }
641
642 pub fn extend_ref(&mut self, other: &Self) {
647 if other.is_deleted {
648 self.is_deleted = true;
649 self.storage_nodes.clear();
650 self.storage_nodes.extend(other.storage_nodes.iter().cloned());
651 return;
652 }
653
654 extend_sorted_vec(&mut self.storage_nodes, &other.storage_nodes);
656 self.is_deleted = self.is_deleted || other.is_deleted;
657 }
658}
659
660fn exclude_empty(iter: impl IntoIterator<Item = Nibbles>) -> impl Iterator<Item = Nibbles> {
662 iter.into_iter().filter(|n| !n.is_empty())
663}
664
665fn exclude_empty_from_pair<V>(
667 iter: impl IntoIterator<Item = (Nibbles, V)>,
668) -> impl Iterator<Item = (Nibbles, V)> {
669 iter.into_iter().filter(|(n, _)| !n.is_empty())
670}
671
672impl From<StorageTrieUpdatesSorted> for StorageTrieUpdates {
673 fn from(sorted: StorageTrieUpdatesSorted) -> Self {
674 let mut storage_nodes = HashMap::default();
675 let mut removed_nodes = HashSet::default();
676
677 for (nibbles, node) in sorted.storage_nodes {
678 if let Some(node) = node {
679 storage_nodes.insert(nibbles, node);
680 } else {
681 removed_nodes.insert(nibbles);
682 }
683 }
684
685 Self { is_deleted: sorted.is_deleted, storage_nodes, removed_nodes }
686 }
687}
688
689#[cfg(test)]
690mod tests {
691 use super::*;
692 use alloy_primitives::B256;
693
694 #[test]
695 fn test_trie_updates_sorted_extend_ref() {
696 let mut updates1 = TrieUpdatesSorted::default();
698 let updates2 = TrieUpdatesSorted::default();
699 updates1.extend_ref(&updates2);
700 assert_eq!(updates1.account_nodes.len(), 0);
701 assert_eq!(updates1.storage_tries.len(), 0);
702
703 let mut updates1 = TrieUpdatesSorted {
705 account_nodes: vec![
706 (Nibbles::from_nibbles_unchecked([0x01]), Some(BranchNodeCompact::default())),
707 (Nibbles::from_nibbles_unchecked([0x03]), None),
708 ],
709 storage_tries: B256Map::default(),
710 };
711 let updates2 = TrieUpdatesSorted {
712 account_nodes: vec![
713 (Nibbles::from_nibbles_unchecked([0x02]), Some(BranchNodeCompact::default())),
714 (Nibbles::from_nibbles_unchecked([0x03]), Some(BranchNodeCompact::default())), ],
716 storage_tries: B256Map::default(),
717 };
718 updates1.extend_ref(&updates2);
719 assert_eq!(updates1.account_nodes.len(), 3);
720 assert_eq!(updates1.account_nodes[0].0, Nibbles::from_nibbles_unchecked([0x01]));
722 assert_eq!(updates1.account_nodes[1].0, Nibbles::from_nibbles_unchecked([0x02]));
723 assert_eq!(updates1.account_nodes[2].0, Nibbles::from_nibbles_unchecked([0x03]));
724 assert!(updates1.account_nodes[2].1.is_some());
726
727 let storage_trie1 = StorageTrieUpdatesSorted {
729 is_deleted: false,
730 storage_nodes: vec![(
731 Nibbles::from_nibbles_unchecked([0x0a]),
732 Some(BranchNodeCompact::default()),
733 )],
734 };
735 let storage_trie2 = StorageTrieUpdatesSorted {
736 is_deleted: false,
737 storage_nodes: vec![(Nibbles::from_nibbles_unchecked([0x0b]), None)],
738 };
739
740 let hashed_address1 = B256::from([1; 32]);
741 let hashed_address2 = B256::from([2; 32]);
742
743 let mut updates1 = TrieUpdatesSorted {
744 account_nodes: vec![],
745 storage_tries: B256Map::from_iter([(hashed_address1, storage_trie1.clone())]),
746 };
747 let updates2 = TrieUpdatesSorted {
748 account_nodes: vec![],
749 storage_tries: B256Map::from_iter([
750 (hashed_address1, storage_trie2),
751 (hashed_address2, storage_trie1),
752 ]),
753 };
754 updates1.extend_ref(&updates2);
755 assert_eq!(updates1.storage_tries.len(), 2);
756 assert!(updates1.storage_tries.contains_key(&hashed_address1));
757 assert!(updates1.storage_tries.contains_key(&hashed_address2));
758 let merged_storage = &updates1.storage_tries[&hashed_address1];
760 assert_eq!(merged_storage.storage_nodes.len(), 2);
761 }
762
763 #[test]
764 fn test_storage_trie_updates_sorted_extend_ref_deleted() {
765 let mut storage1 = StorageTrieUpdatesSorted {
767 is_deleted: false,
768 storage_nodes: vec![
769 (Nibbles::from_nibbles_unchecked([0x01]), Some(BranchNodeCompact::default())),
770 (Nibbles::from_nibbles_unchecked([0x02]), None),
771 ],
772 };
773
774 let storage2 = StorageTrieUpdatesSorted {
775 is_deleted: true,
776 storage_nodes: vec![
777 (Nibbles::from_nibbles_unchecked([0x03]), Some(BranchNodeCompact::default())),
778 (Nibbles::from_nibbles_unchecked([0x04]), None),
779 ],
780 };
781
782 storage1.extend_ref(&storage2);
783
784 assert!(storage1.is_deleted);
786 assert_eq!(storage1.storage_nodes.len(), 2);
788 assert_eq!(storage1.storage_nodes[0].0, Nibbles::from_nibbles_unchecked([0x03]));
789 assert_eq!(storage1.storage_nodes[1].0, Nibbles::from_nibbles_unchecked([0x04]));
790
791 let mut storage3 = StorageTrieUpdatesSorted {
793 is_deleted: true,
794 storage_nodes: vec![(
795 Nibbles::from_nibbles_unchecked([0x05]),
796 Some(BranchNodeCompact::default()),
797 )],
798 };
799
800 let storage4 = StorageTrieUpdatesSorted {
801 is_deleted: true,
802 storage_nodes: vec![
803 (Nibbles::from_nibbles_unchecked([0x06]), Some(BranchNodeCompact::default())),
804 (Nibbles::from_nibbles_unchecked([0x07]), None),
805 ],
806 };
807
808 storage3.extend_ref(&storage4);
809
810 assert!(storage3.is_deleted);
812 assert_eq!(storage3.storage_nodes.len(), 2);
814 assert_eq!(storage3.storage_nodes[0].0, Nibbles::from_nibbles_unchecked([0x06]));
815 assert_eq!(storage3.storage_nodes[1].0, Nibbles::from_nibbles_unchecked([0x07]));
816 }
817
818 #[test]
820 fn test_trie_updates_extend_from_sorted_with_storage_tries() {
821 let hashed_address = B256::from([1; 32]);
822
823 let mut updates = TrieUpdates::default();
824
825 let storage_trie = StorageTrieUpdatesSorted {
826 is_deleted: false,
827 storage_nodes: vec![
828 (Nibbles::from_nibbles_unchecked([0x0a]), Some(BranchNodeCompact::default())),
829 (Nibbles::from_nibbles_unchecked([0x0b]), None),
830 ],
831 };
832
833 let sorted = TrieUpdatesSorted {
834 account_nodes: vec![],
835 storage_tries: B256Map::from_iter([(hashed_address, storage_trie)]),
836 };
837
838 updates.extend_from_sorted(&sorted);
839
840 assert_eq!(updates.storage_tries.len(), 1);
841 let storage = updates.storage_tries.get(&hashed_address).unwrap();
842 assert!(!storage.is_deleted);
843 assert_eq!(storage.storage_nodes.len(), 1);
844 assert!(storage.removed_nodes.contains(&Nibbles::from_nibbles_unchecked([0x0b])));
845 }
846
847 #[test]
849 fn test_trie_updates_extend_from_sorted_with_deleted_storage() {
850 let hashed_address = B256::from([1; 32]);
851
852 let mut updates = TrieUpdates::default();
853 updates.storage_tries.insert(
854 hashed_address,
855 StorageTrieUpdates {
856 is_deleted: false,
857 storage_nodes: HashMap::from_iter([(
858 Nibbles::from_nibbles_unchecked([0x01]),
859 BranchNodeCompact::default(),
860 )]),
861 removed_nodes: Default::default(),
862 },
863 );
864
865 let storage_trie = StorageTrieUpdatesSorted {
866 is_deleted: true,
867 storage_nodes: vec![(
868 Nibbles::from_nibbles_unchecked([0x0a]),
869 Some(BranchNodeCompact::default()),
870 )],
871 };
872
873 let sorted = TrieUpdatesSorted {
874 account_nodes: vec![],
875 storage_tries: B256Map::from_iter([(hashed_address, storage_trie)]),
876 };
877
878 updates.extend_from_sorted(&sorted);
879
880 let storage = updates.storage_tries.get(&hashed_address).unwrap();
881 assert!(storage.is_deleted);
882 assert_eq!(storage.storage_nodes.len(), 1);
884 assert!(storage.storage_nodes.contains_key(&Nibbles::from_nibbles_unchecked([0x0a])));
885 }
886
887 #[test]
889 fn test_storage_trie_updates_extend_from_sorted_non_deleted() {
890 let mut storage = StorageTrieUpdates {
891 is_deleted: false,
892 storage_nodes: HashMap::from_iter([(
893 Nibbles::from_nibbles_unchecked([0x01]),
894 BranchNodeCompact::default(),
895 )]),
896 removed_nodes: Default::default(),
897 };
898
899 let sorted = StorageTrieUpdatesSorted {
900 is_deleted: false,
901 storage_nodes: vec![
902 (Nibbles::from_nibbles_unchecked([0x02]), Some(BranchNodeCompact::default())),
903 (Nibbles::from_nibbles_unchecked([0x03]), None),
904 ],
905 };
906
907 storage.extend_from_sorted(&sorted);
908
909 assert!(!storage.is_deleted);
910 assert_eq!(storage.storage_nodes.len(), 2);
911 assert!(storage.removed_nodes.contains(&Nibbles::from_nibbles_unchecked([0x03])));
912 }
913
914 #[test]
916 fn test_storage_trie_updates_extend_from_sorted_deleted() {
917 let mut storage = StorageTrieUpdates {
918 is_deleted: false,
919 storage_nodes: HashMap::from_iter([(
920 Nibbles::from_nibbles_unchecked([0x01]),
921 BranchNodeCompact::default(),
922 )]),
923 removed_nodes: Default::default(),
924 };
925
926 let sorted = StorageTrieUpdatesSorted {
927 is_deleted: true,
928 storage_nodes: vec![(
929 Nibbles::from_nibbles_unchecked([0x0a]),
930 Some(BranchNodeCompact::default()),
931 )],
932 };
933
934 storage.extend_from_sorted(&sorted);
935
936 assert!(storage.is_deleted);
937 assert_eq!(storage.storage_nodes.len(), 1);
939 assert!(storage.storage_nodes.contains_key(&Nibbles::from_nibbles_unchecked([0x0a])));
940 }
941
942 #[test]
944 fn test_trie_updates_extend_from_sorted_filters_empty_nibbles() {
945 let mut updates = TrieUpdates::default();
946
947 let sorted = TrieUpdatesSorted {
948 account_nodes: vec![
949 (Nibbles::default(), Some(BranchNodeCompact::default())), (Nibbles::from_nibbles_unchecked([0x01]), Some(BranchNodeCompact::default())),
951 ],
952 storage_tries: B256Map::default(),
953 };
954
955 updates.extend_from_sorted(&sorted);
956
957 assert_eq!(updates.account_nodes.len(), 1);
959 assert!(updates.account_nodes.contains_key(&Nibbles::from_nibbles_unchecked([0x01])));
960 assert!(!updates.account_nodes.contains_key(&Nibbles::default()));
961 }
962}
963
964#[cfg(feature = "serde-bincode-compat")]
966pub mod serde_bincode_compat {
967 use crate::{BranchNodeCompact, Nibbles};
968 use alloc::borrow::Cow;
969 use alloy_primitives::map::{B256Map, HashMap, HashSet};
970 use serde::{Deserialize, Deserializer, Serialize, Serializer};
971 use serde_with::{DeserializeAs, SerializeAs};
972
973 #[derive(Debug, Serialize, Deserialize)]
989 pub struct TrieUpdates<'a> {
990 account_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
991 removed_nodes: Cow<'a, HashSet<Nibbles>>,
992 storage_tries: B256Map<StorageTrieUpdates<'a>>,
993 }
994
995 impl<'a> From<&'a super::TrieUpdates> for TrieUpdates<'a> {
996 fn from(value: &'a super::TrieUpdates) -> Self {
997 Self {
998 account_nodes: Cow::Borrowed(&value.account_nodes),
999 removed_nodes: Cow::Borrowed(&value.removed_nodes),
1000 storage_tries: value.storage_tries.iter().map(|(k, v)| (*k, v.into())).collect(),
1001 }
1002 }
1003 }
1004
1005 impl<'a> From<TrieUpdates<'a>> for super::TrieUpdates {
1006 fn from(value: TrieUpdates<'a>) -> Self {
1007 Self {
1008 account_nodes: value.account_nodes.into_owned(),
1009 removed_nodes: value.removed_nodes.into_owned(),
1010 storage_tries: value
1011 .storage_tries
1012 .into_iter()
1013 .map(|(k, v)| (k, v.into()))
1014 .collect(),
1015 }
1016 }
1017 }
1018
1019 impl SerializeAs<super::TrieUpdates> for TrieUpdates<'_> {
1020 fn serialize_as<S>(source: &super::TrieUpdates, serializer: S) -> Result<S::Ok, S::Error>
1021 where
1022 S: Serializer,
1023 {
1024 TrieUpdates::from(source).serialize(serializer)
1025 }
1026 }
1027
1028 impl<'de> DeserializeAs<'de, super::TrieUpdates> for TrieUpdates<'de> {
1029 fn deserialize_as<D>(deserializer: D) -> Result<super::TrieUpdates, D::Error>
1030 where
1031 D: Deserializer<'de>,
1032 {
1033 TrieUpdates::deserialize(deserializer).map(Into::into)
1034 }
1035 }
1036
1037 #[derive(Debug, Serialize, Deserialize)]
1053 pub struct StorageTrieUpdates<'a> {
1054 is_deleted: bool,
1055 storage_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
1056 removed_nodes: Cow<'a, HashSet<Nibbles>>,
1057 }
1058
1059 impl<'a> From<&'a super::StorageTrieUpdates> for StorageTrieUpdates<'a> {
1060 fn from(value: &'a super::StorageTrieUpdates) -> Self {
1061 Self {
1062 is_deleted: value.is_deleted,
1063 storage_nodes: Cow::Borrowed(&value.storage_nodes),
1064 removed_nodes: Cow::Borrowed(&value.removed_nodes),
1065 }
1066 }
1067 }
1068
1069 impl<'a> From<StorageTrieUpdates<'a>> for super::StorageTrieUpdates {
1070 fn from(value: StorageTrieUpdates<'a>) -> Self {
1071 Self {
1072 is_deleted: value.is_deleted,
1073 storage_nodes: value.storage_nodes.into_owned(),
1074 removed_nodes: value.removed_nodes.into_owned(),
1075 }
1076 }
1077 }
1078
1079 impl SerializeAs<super::StorageTrieUpdates> for StorageTrieUpdates<'_> {
1080 fn serialize_as<S>(
1081 source: &super::StorageTrieUpdates,
1082 serializer: S,
1083 ) -> Result<S::Ok, S::Error>
1084 where
1085 S: Serializer,
1086 {
1087 StorageTrieUpdates::from(source).serialize(serializer)
1088 }
1089 }
1090
1091 impl<'de> DeserializeAs<'de, super::StorageTrieUpdates> for StorageTrieUpdates<'de> {
1092 fn deserialize_as<D>(deserializer: D) -> Result<super::StorageTrieUpdates, D::Error>
1093 where
1094 D: Deserializer<'de>,
1095 {
1096 StorageTrieUpdates::deserialize(deserializer).map(Into::into)
1097 }
1098 }
1099
1100 #[cfg(test)]
1101 mod tests {
1102 use crate::{
1103 serde_bincode_compat,
1104 updates::{StorageTrieUpdates, TrieUpdates},
1105 BranchNodeCompact, Nibbles,
1106 };
1107 use alloy_primitives::B256;
1108 use serde::{Deserialize, Serialize};
1109 use serde_with::serde_as;
1110
1111 #[test]
1112 fn test_trie_updates_bincode_roundtrip() {
1113 #[serde_as]
1114 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1115 struct Data {
1116 #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")]
1117 trie_updates: TrieUpdates,
1118 }
1119
1120 let mut data = Data { trie_updates: TrieUpdates::default() };
1121 let encoded = bincode::serialize(&data).unwrap();
1122 let decoded: Data = bincode::deserialize(&encoded).unwrap();
1123 assert_eq!(decoded, data);
1124
1125 data.trie_updates
1126 .removed_nodes
1127 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
1128 let encoded = bincode::serialize(&data).unwrap();
1129 let decoded: Data = bincode::deserialize(&encoded).unwrap();
1130 assert_eq!(decoded, data);
1131
1132 data.trie_updates.account_nodes.insert(
1133 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
1134 BranchNodeCompact::default(),
1135 );
1136 let encoded = bincode::serialize(&data).unwrap();
1137 let decoded: Data = bincode::deserialize(&encoded).unwrap();
1138 assert_eq!(decoded, data);
1139
1140 data.trie_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
1141 let encoded = bincode::serialize(&data).unwrap();
1142 let decoded: Data = bincode::deserialize(&encoded).unwrap();
1143 assert_eq!(decoded, data);
1144 }
1145
1146 #[test]
1147 fn test_storage_trie_updates_bincode_roundtrip() {
1148 #[serde_as]
1149 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1150 struct Data {
1151 #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")]
1152 trie_updates: StorageTrieUpdates,
1153 }
1154
1155 let mut data = Data { trie_updates: StorageTrieUpdates::default() };
1156 let encoded = bincode::serialize(&data).unwrap();
1157 let decoded: Data = bincode::deserialize(&encoded).unwrap();
1158 assert_eq!(decoded, data);
1159
1160 data.trie_updates
1161 .removed_nodes
1162 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
1163 let encoded = bincode::serialize(&data).unwrap();
1164 let decoded: Data = bincode::deserialize(&encoded).unwrap();
1165 assert_eq!(decoded, data);
1166
1167 data.trie_updates.storage_nodes.insert(
1168 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
1169 BranchNodeCompact::default(),
1170 );
1171 let encoded = bincode::serialize(&data).unwrap();
1172 let decoded: Data = bincode::deserialize(&encoded).unwrap();
1173 assert_eq!(decoded, data);
1174 }
1175 }
1176}
1177
1178#[cfg(all(test, feature = "serde"))]
1179mod serde_tests {
1180 use super::*;
1181
1182 #[test]
1183 fn test_trie_updates_serde_roundtrip() {
1184 let mut default_updates = TrieUpdates::default();
1185 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1186 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
1187 assert_eq!(updates_deserialized, default_updates);
1188
1189 default_updates
1190 .removed_nodes
1191 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
1192 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1193 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
1194 assert_eq!(updates_deserialized, default_updates);
1195
1196 default_updates.account_nodes.insert(
1197 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
1198 BranchNodeCompact::default(),
1199 );
1200 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1201 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
1202 assert_eq!(updates_deserialized, default_updates);
1203
1204 default_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
1205 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1206 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
1207 assert_eq!(updates_deserialized, default_updates);
1208 }
1209
1210 #[test]
1211 fn test_storage_trie_updates_serde_roundtrip() {
1212 let mut default_updates = StorageTrieUpdates::default();
1213 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1214 let updates_deserialized: StorageTrieUpdates =
1215 serde_json::from_str(&updates_serialized).unwrap();
1216 assert_eq!(updates_deserialized, default_updates);
1217
1218 default_updates
1219 .removed_nodes
1220 .insert(Nibbles::from_nibbles_unchecked([0x0b, 0x0e, 0x0e, 0x0f]));
1221 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1222 let updates_deserialized: StorageTrieUpdates =
1223 serde_json::from_str(&updates_serialized).unwrap();
1224 assert_eq!(updates_deserialized, default_updates);
1225
1226 default_updates.storage_nodes.insert(
1227 Nibbles::from_nibbles_unchecked([0x0d, 0x0e, 0x0a, 0x0d]),
1228 BranchNodeCompact::default(),
1229 );
1230 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
1231 let updates_deserialized: StorageTrieUpdates =
1232 serde_json::from_str(&updates_serialized).unwrap();
1233 assert_eq!(updates_deserialized, default_updates);
1234 }
1235}