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