1use crate::{BranchNodeCompact, HashBuilder, Nibbles};
2use alloc::vec::Vec;
3use alloy_primitives::{
4 map::{B256Map, B256Set, HashMap, HashSet},
5 B256,
6};
7
8#[derive(PartialEq, Eq, Clone, Default, Debug)]
10#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
11pub struct TrieUpdates {
12 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
14 pub account_nodes: HashMap<Nibbles, BranchNodeCompact>,
15 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_set"))]
17 pub removed_nodes: HashSet<Nibbles>,
18 pub storage_tries: B256Map<StorageTrieUpdates>,
20}
21
22impl TrieUpdates {
23 pub fn is_empty(&self) -> bool {
25 self.account_nodes.is_empty() &&
26 self.removed_nodes.is_empty() &&
27 self.storage_tries.is_empty()
28 }
29
30 pub const fn account_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
32 &self.account_nodes
33 }
34
35 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
37 &self.removed_nodes
38 }
39
40 pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdates> {
42 &self.storage_tries
43 }
44
45 pub fn extend(&mut self, other: Self) {
47 self.extend_common(&other);
48 self.account_nodes.extend(exclude_empty_from_pair(other.account_nodes));
49 self.removed_nodes.extend(exclude_empty(other.removed_nodes));
50 for (hashed_address, storage_trie) in other.storage_tries {
51 self.storage_tries.entry(hashed_address).or_default().extend(storage_trie);
52 }
53 }
54
55 pub fn extend_ref(&mut self, other: &Self) {
59 self.extend_common(other);
60 self.account_nodes.extend(exclude_empty_from_pair(
61 other.account_nodes.iter().map(|(k, v)| (k.clone(), v.clone())),
62 ));
63 self.removed_nodes.extend(exclude_empty(other.removed_nodes.iter().cloned()));
64 for (hashed_address, storage_trie) in &other.storage_tries {
65 self.storage_tries.entry(*hashed_address).or_default().extend_ref(storage_trie);
66 }
67 }
68
69 fn extend_common(&mut self, other: &Self) {
70 self.account_nodes.retain(|nibbles, _| !other.removed_nodes.contains(nibbles));
71 }
72
73 pub fn insert_storage_updates(
75 &mut self,
76 hashed_address: B256,
77 storage_updates: StorageTrieUpdates,
78 ) {
79 if storage_updates.is_empty() {
80 return;
81 }
82 let existing = self.storage_tries.insert(hashed_address, storage_updates);
83 debug_assert!(existing.is_none());
84 }
85
86 pub fn finalize(
88 &mut self,
89 hash_builder: HashBuilder,
90 removed_keys: HashSet<Nibbles>,
91 destroyed_accounts: B256Set,
92 ) {
93 let (_, updated_nodes) = hash_builder.split();
95 self.account_nodes.extend(exclude_empty_from_pair(updated_nodes));
96
97 self.removed_nodes.extend(exclude_empty(removed_keys));
99
100 for destroyed in destroyed_accounts {
102 self.storage_tries.entry(destroyed).or_default().set_deleted(true);
103 }
104 }
105
106 pub fn into_sorted(self) -> TrieUpdatesSorted {
108 let mut account_nodes = Vec::from_iter(self.account_nodes);
109 account_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
110 let storage_tries = self
111 .storage_tries
112 .into_iter()
113 .map(|(hashed_address, updates)| (hashed_address, updates.into_sorted()))
114 .collect();
115 TrieUpdatesSorted { removed_nodes: self.removed_nodes, account_nodes, storage_tries }
116 }
117}
118
119#[derive(PartialEq, Eq, Clone, Default, Debug)]
121#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))]
122pub struct StorageTrieUpdates {
123 pub is_deleted: bool,
125 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_map"))]
127 pub storage_nodes: HashMap<Nibbles, BranchNodeCompact>,
128 #[cfg_attr(any(test, feature = "serde"), serde(with = "serde_nibbles_set"))]
130 pub removed_nodes: HashSet<Nibbles>,
131}
132
133#[cfg(feature = "test-utils")]
134impl StorageTrieUpdates {
135 pub fn new(updates: impl IntoIterator<Item = (Nibbles, BranchNodeCompact)>) -> Self {
137 Self { storage_nodes: exclude_empty_from_pair(updates).collect(), ..Default::default() }
138 }
139}
140
141impl StorageTrieUpdates {
142 pub fn deleted() -> Self {
144 Self {
145 is_deleted: true,
146 storage_nodes: HashMap::default(),
147 removed_nodes: HashSet::default(),
148 }
149 }
150
151 pub fn len(&self) -> usize {
153 (self.is_deleted as usize) + self.storage_nodes.len() + self.removed_nodes.len()
154 }
155
156 pub const fn is_deleted(&self) -> bool {
158 self.is_deleted
159 }
160
161 pub const fn storage_nodes_ref(&self) -> &HashMap<Nibbles, BranchNodeCompact> {
163 &self.storage_nodes
164 }
165
166 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
168 &self.removed_nodes
169 }
170
171 pub fn is_empty(&self) -> bool {
173 !self.is_deleted && self.storage_nodes.is_empty() && self.removed_nodes.is_empty()
174 }
175
176 pub const fn set_deleted(&mut self, deleted: bool) {
178 self.is_deleted = deleted;
179 }
180
181 pub fn extend(&mut self, other: Self) {
183 self.extend_common(&other);
184 self.storage_nodes.extend(exclude_empty_from_pair(other.storage_nodes));
185 self.removed_nodes.extend(exclude_empty(other.removed_nodes));
186 }
187
188 pub fn extend_ref(&mut self, other: &Self) {
192 self.extend_common(other);
193 self.storage_nodes.extend(exclude_empty_from_pair(
194 other.storage_nodes.iter().map(|(k, v)| (k.clone(), v.clone())),
195 ));
196 self.removed_nodes.extend(exclude_empty(other.removed_nodes.iter().cloned()));
197 }
198
199 fn extend_common(&mut self, other: &Self) {
200 if other.is_deleted {
201 self.storage_nodes.clear();
202 self.removed_nodes.clear();
203 }
204 self.is_deleted |= other.is_deleted;
205 self.storage_nodes.retain(|nibbles, _| !other.removed_nodes.contains(nibbles));
206 }
207
208 pub fn finalize(&mut self, hash_builder: HashBuilder, removed_keys: HashSet<Nibbles>) {
210 let (_, updated_nodes) = hash_builder.split();
212 self.storage_nodes.extend(exclude_empty_from_pair(updated_nodes));
213
214 self.removed_nodes.extend(exclude_empty(removed_keys));
216 }
217
218 pub fn into_sorted(self) -> StorageTrieUpdatesSorted {
220 let mut storage_nodes = Vec::from_iter(self.storage_nodes);
221 storage_nodes.sort_unstable_by(|a, b| a.0.cmp(&b.0));
222 StorageTrieUpdatesSorted {
223 is_deleted: self.is_deleted,
224 removed_nodes: self.removed_nodes,
225 storage_nodes,
226 }
227 }
228}
229
230#[cfg(any(test, feature = "serde"))]
235mod serde_nibbles_set {
236 use crate::Nibbles;
237 use alloc::{
238 string::{String, ToString},
239 vec::Vec,
240 };
241 use alloy_primitives::map::HashSet;
242 use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
243
244 pub(super) fn serialize<S>(map: &HashSet<Nibbles>, serializer: S) -> Result<S::Ok, S::Error>
245 where
246 S: Serializer,
247 {
248 let mut storage_nodes =
249 map.iter().map(|elem| alloy_primitives::hex::encode(elem.pack())).collect::<Vec<_>>();
250 storage_nodes.sort_unstable();
251 storage_nodes.serialize(serializer)
252 }
253
254 pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<HashSet<Nibbles>, D::Error>
255 where
256 D: Deserializer<'de>,
257 {
258 Vec::<String>::deserialize(deserializer)?
259 .into_iter()
260 .map(|node| {
261 Ok(Nibbles::unpack(
262 alloy_primitives::hex::decode(node)
263 .map_err(|err| D::Error::custom(err.to_string()))?,
264 ))
265 })
266 .collect::<Result<HashSet<_>, _>>()
267 }
268}
269
270#[cfg(any(test, feature = "serde"))]
275mod serde_nibbles_map {
276 use crate::Nibbles;
277 use alloc::{
278 string::{String, ToString},
279 vec::Vec,
280 };
281 use alloy_primitives::{hex, map::HashMap};
282 use core::marker::PhantomData;
283 use serde::{
284 de::{Error, MapAccess, Visitor},
285 ser::SerializeMap,
286 Deserialize, Deserializer, Serialize, Serializer,
287 };
288
289 pub(super) fn serialize<S, T>(
290 map: &HashMap<Nibbles, T>,
291 serializer: S,
292 ) -> Result<S::Ok, S::Error>
293 where
294 S: Serializer,
295 T: Serialize,
296 {
297 let mut map_serializer = serializer.serialize_map(Some(map.len()))?;
298 let mut storage_nodes = Vec::from_iter(map);
299 storage_nodes.sort_unstable_by_key(|node| node.0);
300 for (k, v) in storage_nodes {
301 let packed = alloy_primitives::hex::encode(k.pack());
303 map_serializer.serialize_entry(&packed, &v)?;
304 }
305 map_serializer.end()
306 }
307
308 pub(super) fn deserialize<'de, D, T>(deserializer: D) -> Result<HashMap<Nibbles, T>, D::Error>
309 where
310 D: Deserializer<'de>,
311 T: Deserialize<'de>,
312 {
313 struct NibblesMapVisitor<T> {
314 marker: PhantomData<T>,
315 }
316
317 impl<'de, T> Visitor<'de> for NibblesMapVisitor<T>
318 where
319 T: Deserialize<'de>,
320 {
321 type Value = HashMap<Nibbles, T>;
322
323 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
324 formatter.write_str("a map with hex-encoded Nibbles keys")
325 }
326
327 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
328 where
329 A: MapAccess<'de>,
330 {
331 let mut result = HashMap::with_capacity_and_hasher(
332 map.size_hint().unwrap_or(0),
333 Default::default(),
334 );
335
336 while let Some((key, value)) = map.next_entry::<String, T>()? {
337 let decoded_key =
338 hex::decode(&key).map_err(|err| Error::custom(err.to_string()))?;
339
340 let nibbles = Nibbles::unpack(&decoded_key);
341
342 result.insert(nibbles, value);
343 }
344
345 Ok(result)
346 }
347 }
348
349 deserializer.deserialize_map(NibblesMapVisitor { marker: PhantomData })
350 }
351}
352
353#[derive(PartialEq, Eq, Clone, Default, Debug)]
355pub struct TrieUpdatesSorted {
356 pub account_nodes: Vec<(Nibbles, BranchNodeCompact)>,
358 pub removed_nodes: HashSet<Nibbles>,
360 pub storage_tries: B256Map<StorageTrieUpdatesSorted>,
362}
363
364impl TrieUpdatesSorted {
365 pub fn account_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] {
367 &self.account_nodes
368 }
369
370 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
372 &self.removed_nodes
373 }
374
375 pub const fn storage_tries_ref(&self) -> &B256Map<StorageTrieUpdatesSorted> {
377 &self.storage_tries
378 }
379}
380
381#[derive(PartialEq, Eq, Clone, Default, Debug)]
383pub struct StorageTrieUpdatesSorted {
384 pub is_deleted: bool,
386 pub storage_nodes: Vec<(Nibbles, BranchNodeCompact)>,
388 pub removed_nodes: HashSet<Nibbles>,
390}
391
392impl StorageTrieUpdatesSorted {
393 pub const fn is_deleted(&self) -> bool {
395 self.is_deleted
396 }
397
398 pub fn storage_nodes_ref(&self) -> &[(Nibbles, BranchNodeCompact)] {
400 &self.storage_nodes
401 }
402
403 pub const fn removed_nodes_ref(&self) -> &HashSet<Nibbles> {
405 &self.removed_nodes
406 }
407}
408
409fn exclude_empty(iter: impl IntoIterator<Item = Nibbles>) -> impl Iterator<Item = Nibbles> {
411 iter.into_iter().filter(|n| !n.is_empty())
412}
413
414fn exclude_empty_from_pair<V>(
416 iter: impl IntoIterator<Item = (Nibbles, V)>,
417) -> impl Iterator<Item = (Nibbles, V)> {
418 iter.into_iter().filter(|(n, _)| !n.is_empty())
419}
420
421#[cfg(feature = "serde-bincode-compat")]
423pub mod serde_bincode_compat {
424 use crate::{BranchNodeCompact, Nibbles};
425 use alloc::borrow::Cow;
426 use alloy_primitives::map::{B256Map, HashMap, HashSet};
427 use serde::{Deserialize, Deserializer, Serialize, Serializer};
428 use serde_with::{DeserializeAs, SerializeAs};
429
430 #[derive(Debug, Serialize, Deserialize)]
446 pub struct TrieUpdates<'a> {
447 account_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
448 removed_nodes: Cow<'a, HashSet<Nibbles>>,
449 storage_tries: B256Map<StorageTrieUpdates<'a>>,
450 }
451
452 impl<'a> From<&'a super::TrieUpdates> for TrieUpdates<'a> {
453 fn from(value: &'a super::TrieUpdates) -> Self {
454 Self {
455 account_nodes: Cow::Borrowed(&value.account_nodes),
456 removed_nodes: Cow::Borrowed(&value.removed_nodes),
457 storage_tries: value.storage_tries.iter().map(|(k, v)| (*k, v.into())).collect(),
458 }
459 }
460 }
461
462 impl<'a> From<TrieUpdates<'a>> for super::TrieUpdates {
463 fn from(value: TrieUpdates<'a>) -> Self {
464 Self {
465 account_nodes: value.account_nodes.into_owned(),
466 removed_nodes: value.removed_nodes.into_owned(),
467 storage_tries: value
468 .storage_tries
469 .into_iter()
470 .map(|(k, v)| (k, v.into()))
471 .collect(),
472 }
473 }
474 }
475
476 impl SerializeAs<super::TrieUpdates> for TrieUpdates<'_> {
477 fn serialize_as<S>(source: &super::TrieUpdates, serializer: S) -> Result<S::Ok, S::Error>
478 where
479 S: Serializer,
480 {
481 TrieUpdates::from(source).serialize(serializer)
482 }
483 }
484
485 impl<'de> DeserializeAs<'de, super::TrieUpdates> for TrieUpdates<'de> {
486 fn deserialize_as<D>(deserializer: D) -> Result<super::TrieUpdates, D::Error>
487 where
488 D: Deserializer<'de>,
489 {
490 TrieUpdates::deserialize(deserializer).map(Into::into)
491 }
492 }
493
494 #[derive(Debug, Serialize, Deserialize)]
510 pub struct StorageTrieUpdates<'a> {
511 is_deleted: bool,
512 storage_nodes: Cow<'a, HashMap<Nibbles, BranchNodeCompact>>,
513 removed_nodes: Cow<'a, HashSet<Nibbles>>,
514 }
515
516 impl<'a> From<&'a super::StorageTrieUpdates> for StorageTrieUpdates<'a> {
517 fn from(value: &'a super::StorageTrieUpdates) -> Self {
518 Self {
519 is_deleted: value.is_deleted,
520 storage_nodes: Cow::Borrowed(&value.storage_nodes),
521 removed_nodes: Cow::Borrowed(&value.removed_nodes),
522 }
523 }
524 }
525
526 impl<'a> From<StorageTrieUpdates<'a>> for super::StorageTrieUpdates {
527 fn from(value: StorageTrieUpdates<'a>) -> Self {
528 Self {
529 is_deleted: value.is_deleted,
530 storage_nodes: value.storage_nodes.into_owned(),
531 removed_nodes: value.removed_nodes.into_owned(),
532 }
533 }
534 }
535
536 impl SerializeAs<super::StorageTrieUpdates> for StorageTrieUpdates<'_> {
537 fn serialize_as<S>(
538 source: &super::StorageTrieUpdates,
539 serializer: S,
540 ) -> Result<S::Ok, S::Error>
541 where
542 S: Serializer,
543 {
544 StorageTrieUpdates::from(source).serialize(serializer)
545 }
546 }
547
548 impl<'de> DeserializeAs<'de, super::StorageTrieUpdates> for StorageTrieUpdates<'de> {
549 fn deserialize_as<D>(deserializer: D) -> Result<super::StorageTrieUpdates, D::Error>
550 where
551 D: Deserializer<'de>,
552 {
553 StorageTrieUpdates::deserialize(deserializer).map(Into::into)
554 }
555 }
556
557 #[cfg(test)]
558 mod tests {
559 use crate::{
560 serde_bincode_compat,
561 updates::{StorageTrieUpdates, TrieUpdates},
562 BranchNodeCompact, Nibbles,
563 };
564 use alloy_primitives::B256;
565 use serde::{Deserialize, Serialize};
566 use serde_with::serde_as;
567
568 #[test]
569 fn test_trie_updates_bincode_roundtrip() {
570 #[serde_as]
571 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
572 struct Data {
573 #[serde_as(as = "serde_bincode_compat::updates::TrieUpdates")]
574 trie_updates: TrieUpdates,
575 }
576
577 let mut data = Data { trie_updates: TrieUpdates::default() };
578 let encoded = bincode::serialize(&data).unwrap();
579 let decoded: Data = bincode::deserialize(&encoded).unwrap();
580 assert_eq!(decoded, data);
581
582 data.trie_updates.removed_nodes.insert(Nibbles::from_vec(vec![0x0b, 0x0e, 0x0e, 0x0f]));
583 let encoded = bincode::serialize(&data).unwrap();
584 let decoded: Data = bincode::deserialize(&encoded).unwrap();
585 assert_eq!(decoded, data);
586
587 data.trie_updates.account_nodes.insert(
588 Nibbles::from_vec(vec![0x0d, 0x0e, 0x0a, 0x0d]),
589 BranchNodeCompact::default(),
590 );
591 let encoded = bincode::serialize(&data).unwrap();
592 let decoded: Data = bincode::deserialize(&encoded).unwrap();
593 assert_eq!(decoded, data);
594
595 data.trie_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
596 let encoded = bincode::serialize(&data).unwrap();
597 let decoded: Data = bincode::deserialize(&encoded).unwrap();
598 assert_eq!(decoded, data);
599 }
600
601 #[test]
602 fn test_storage_trie_updates_bincode_roundtrip() {
603 #[serde_as]
604 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
605 struct Data {
606 #[serde_as(as = "serde_bincode_compat::updates::StorageTrieUpdates")]
607 trie_updates: StorageTrieUpdates,
608 }
609
610 let mut data = Data { trie_updates: StorageTrieUpdates::default() };
611 let encoded = bincode::serialize(&data).unwrap();
612 let decoded: Data = bincode::deserialize(&encoded).unwrap();
613 assert_eq!(decoded, data);
614
615 data.trie_updates.removed_nodes.insert(Nibbles::from_vec(vec![0x0b, 0x0e, 0x0e, 0x0f]));
616 let encoded = bincode::serialize(&data).unwrap();
617 let decoded: Data = bincode::deserialize(&encoded).unwrap();
618 assert_eq!(decoded, data);
619
620 data.trie_updates.storage_nodes.insert(
621 Nibbles::from_vec(vec![0x0d, 0x0e, 0x0a, 0x0d]),
622 BranchNodeCompact::default(),
623 );
624 let encoded = bincode::serialize(&data).unwrap();
625 let decoded: Data = bincode::deserialize(&encoded).unwrap();
626 assert_eq!(decoded, data);
627 }
628 }
629}
630
631#[cfg(all(test, feature = "serde"))]
632mod tests {
633 use super::*;
634
635 #[test]
636 fn test_trie_updates_serde_roundtrip() {
637 let mut default_updates = TrieUpdates::default();
638 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
639 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
640 assert_eq!(updates_deserialized, default_updates);
641
642 default_updates.removed_nodes.insert(Nibbles::from_vec(vec![0x0b, 0x0e, 0x0e, 0x0f]));
643 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
644 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
645 assert_eq!(updates_deserialized, default_updates);
646
647 default_updates
648 .account_nodes
649 .insert(Nibbles::from_vec(vec![0x0d, 0x0e, 0x0a, 0x0d]), BranchNodeCompact::default());
650 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
651 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
652 assert_eq!(updates_deserialized, default_updates);
653
654 default_updates.storage_tries.insert(B256::default(), StorageTrieUpdates::default());
655 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
656 let updates_deserialized: TrieUpdates = serde_json::from_str(&updates_serialized).unwrap();
657 assert_eq!(updates_deserialized, default_updates);
658 }
659
660 #[test]
661 fn test_storage_trie_updates_serde_roundtrip() {
662 let mut default_updates = StorageTrieUpdates::default();
663 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
664 let updates_deserialized: StorageTrieUpdates =
665 serde_json::from_str(&updates_serialized).unwrap();
666 assert_eq!(updates_deserialized, default_updates);
667
668 default_updates.removed_nodes.insert(Nibbles::from_vec(vec![0x0b, 0x0e, 0x0e, 0x0f]));
669 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
670 let updates_deserialized: StorageTrieUpdates =
671 serde_json::from_str(&updates_serialized).unwrap();
672 assert_eq!(updates_deserialized, default_updates);
673
674 default_updates
675 .storage_nodes
676 .insert(Nibbles::from_vec(vec![0x0d, 0x0e, 0x0a, 0x0d]), BranchNodeCompact::default());
677 let updates_serialized = serde_json::to_string(&default_updates).unwrap();
678 let updates_deserialized: StorageTrieUpdates =
679 serde_json::from_str(&updates_serialized).unwrap();
680 assert_eq!(updates_deserialized, default_updates);
681 }
682}