reth_trie_common/prefix_set.rs
1use crate::Nibbles;
2use alloc::{sync::Arc, vec::Vec};
3use alloy_primitives::map::{B256Map, B256Set};
4
5/// Collection of mutable prefix sets.
6#[derive(Clone, Default, Debug)]
7pub struct TriePrefixSetsMut {
8 /// A set of account prefixes that have changed.
9 pub account_prefix_set: PrefixSetMut,
10 /// A map containing storage changes with the hashed address as key and a set of storage key
11 /// prefixes as the value.
12 pub storage_prefix_sets: B256Map<PrefixSetMut>,
13 /// A set of hashed addresses of destroyed accounts.
14 pub destroyed_accounts: B256Set,
15}
16
17impl TriePrefixSetsMut {
18 /// Extends prefix sets with contents of another prefix set.
19 pub fn extend(&mut self, other: Self) {
20 self.account_prefix_set.extend(other.account_prefix_set);
21 for (hashed_address, prefix_set) in other.storage_prefix_sets {
22 self.storage_prefix_sets.entry(hashed_address).or_default().extend(prefix_set);
23 }
24 self.destroyed_accounts.extend(other.destroyed_accounts);
25 }
26
27 /// Returns a `TriePrefixSets` with the same elements as these sets.
28 ///
29 /// If not yet sorted, the elements will be sorted and deduplicated.
30 pub fn freeze(self) -> TriePrefixSets {
31 TriePrefixSets {
32 account_prefix_set: self.account_prefix_set.freeze(),
33 storage_prefix_sets: self
34 .storage_prefix_sets
35 .into_iter()
36 .map(|(hashed_address, prefix_set)| (hashed_address, prefix_set.freeze()))
37 .collect(),
38 destroyed_accounts: self.destroyed_accounts,
39 }
40 }
41}
42
43/// Collection of trie prefix sets.
44#[derive(Default, Debug)]
45pub struct TriePrefixSets {
46 /// A set of account prefixes that have changed.
47 pub account_prefix_set: PrefixSet,
48 /// A map containing storage changes with the hashed address as key and a set of storage key
49 /// prefixes as the value.
50 pub storage_prefix_sets: B256Map<PrefixSet>,
51 /// A set of hashed addresses of destroyed accounts.
52 pub destroyed_accounts: B256Set,
53}
54
55/// A container for efficiently storing and checking for the presence of key prefixes.
56///
57/// This data structure stores a set of `Nibbles` and provides methods to insert
58/// new elements and check whether any existing element has a given prefix.
59///
60/// Internally, this implementation uses a `Vec` and aims to act like a `BTreeSet` in being both
61/// sorted and deduplicated. It does this by keeping a `sorted` flag. The `sorted` flag represents
62/// whether or not the `Vec` is definitely sorted. When a new element is added, it is set to
63/// `false.`. The `Vec` is sorted and deduplicated when `sorted` is `true` and:
64/// * An element is being checked for inclusion (`contains`), or
65/// * The set is being converted into an immutable `PrefixSet` (`freeze`)
66///
67/// This means that a `PrefixSet` will always be sorted and deduplicated when constructed from a
68/// `PrefixSetMut`.
69///
70/// # Examples
71///
72/// ```
73/// use reth_trie_common::{prefix_set::PrefixSetMut, Nibbles};
74///
75/// let mut prefix_set_mut = PrefixSetMut::default();
76/// prefix_set_mut.insert(Nibbles::from_nibbles_unchecked(&[0xa, 0xb]));
77/// prefix_set_mut.insert(Nibbles::from_nibbles_unchecked(&[0xa, 0xb, 0xc]));
78/// let mut prefix_set = prefix_set_mut.freeze();
79/// assert!(prefix_set.contains(&[0xa, 0xb]));
80/// assert!(prefix_set.contains(&[0xa, 0xb, 0xc]));
81/// ```
82#[derive(PartialEq, Eq, Clone, Default, Debug)]
83pub struct PrefixSetMut {
84 /// Flag indicating that any entry should be considered changed.
85 /// If set, the keys will be discarded.
86 all: bool,
87 keys: Vec<Nibbles>,
88}
89
90impl<I> From<I> for PrefixSetMut
91where
92 I: IntoIterator<Item = Nibbles>,
93{
94 fn from(value: I) -> Self {
95 Self { all: false, keys: value.into_iter().collect() }
96 }
97}
98
99impl PrefixSetMut {
100 /// Create [`PrefixSetMut`] with pre-allocated capacity.
101 pub fn with_capacity(capacity: usize) -> Self {
102 Self { all: false, keys: Vec::with_capacity(capacity) }
103 }
104
105 /// Create [`PrefixSetMut`] that considers all key changed.
106 pub const fn all() -> Self {
107 Self { all: true, keys: Vec::new() }
108 }
109
110 /// Inserts the given `nibbles` into the set.
111 pub fn insert(&mut self, nibbles: Nibbles) {
112 self.keys.push(nibbles);
113 }
114
115 /// Extend prefix set with contents of another prefix set.
116 pub fn extend(&mut self, other: Self) {
117 self.all |= other.all;
118 self.keys.extend(other.keys);
119 }
120
121 /// Extend prefix set keys with contents of provided iterator.
122 pub fn extend_keys<I>(&mut self, keys: I)
123 where
124 I: IntoIterator<Item = Nibbles>,
125 {
126 self.keys.extend(keys);
127 }
128
129 /// Returns the number of elements in the set.
130 pub fn len(&self) -> usize {
131 self.keys.len()
132 }
133
134 /// Returns `true` if the set is empty.
135 pub fn is_empty(&self) -> bool {
136 self.keys.is_empty()
137 }
138
139 /// Clears the inner vec for reuse, setting `all` to `false`.
140 pub fn clear(&mut self) {
141 self.all = false;
142 self.keys.clear();
143 }
144
145 /// Returns a `PrefixSet` with the same elements as this set.
146 ///
147 /// If not yet sorted, the elements will be sorted and deduplicated.
148 pub fn freeze(mut self) -> PrefixSet {
149 if self.all {
150 PrefixSet { index: 0, all: true, keys: Arc::new(Vec::new()) }
151 } else {
152 self.keys.sort_unstable();
153 self.keys.dedup();
154 // We need to shrink in both the sorted and non-sorted cases because deduping may have
155 // occurred either on `freeze`, or during `contains`.
156 self.keys.shrink_to_fit();
157 PrefixSet { index: 0, all: false, keys: Arc::new(self.keys) }
158 }
159 }
160}
161
162/// A sorted prefix set that has an immutable _sorted_ list of unique keys.
163///
164/// See also [`PrefixSetMut::freeze`].
165#[derive(Debug, Default, Clone)]
166pub struct PrefixSet {
167 /// Flag indicating that any entry should be considered changed.
168 all: bool,
169 index: usize,
170 keys: Arc<Vec<Nibbles>>,
171}
172
173impl PrefixSet {
174 /// Returns `true` if any of the keys in the set has the given prefix
175 ///
176 /// # Note on Mutability
177 ///
178 /// This method requires `&mut self` (unlike typical `contains` methods) because it maintains an
179 /// internal position tracker (`self.index`) between calls. This enables significant performance
180 /// optimization for sequential lookups in sorted order, which is common during trie traversal.
181 ///
182 /// The `index` field allows subsequent searches to start where previous ones left off,
183 /// avoiding repeated full scans of the prefix array when keys are accessed in nearby ranges.
184 ///
185 /// This optimization was inspired by Silkworm's implementation and significantly improves
186 /// incremental state root calculation performance
187 /// ([see PR #2417](https://github.com/paradigmxyz/reth/pull/2417)).
188 #[inline]
189 pub fn contains(&mut self, prefix: &[u8]) -> bool {
190 if self.all {
191 return true
192 }
193
194 while self.index > 0 && &self.keys[self.index] > prefix {
195 self.index -= 1;
196 }
197
198 for (idx, key) in self.keys[self.index..].iter().enumerate() {
199 if key.has_prefix(prefix) {
200 self.index += idx;
201 return true
202 }
203
204 if key > prefix {
205 self.index += idx;
206 return false
207 }
208 }
209
210 false
211 }
212
213 /// Returns an iterator over reference to _all_ nibbles regardless of cursor position.
214 pub fn iter(&self) -> core::slice::Iter<'_, Nibbles> {
215 self.keys.iter()
216 }
217
218 /// Returns the number of elements in the set.
219 pub fn len(&self) -> usize {
220 self.keys.len()
221 }
222
223 /// Returns `true` if the set is empty.
224 pub fn is_empty(&self) -> bool {
225 self.keys.is_empty()
226 }
227}
228
229impl<'a> IntoIterator for &'a PrefixSet {
230 type Item = &'a Nibbles;
231 type IntoIter = core::slice::Iter<'a, Nibbles>;
232 fn into_iter(self) -> Self::IntoIter {
233 self.iter()
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_contains_with_multiple_inserts_and_duplicates() {
243 let mut prefix_set_mut = PrefixSetMut::default();
244 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
245 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
246 prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
247 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
248
249 let mut prefix_set = prefix_set_mut.freeze();
250 assert!(prefix_set.contains(&[1, 2]));
251 assert!(prefix_set.contains(&[4, 5]));
252 assert!(!prefix_set.contains(&[7, 8]));
253 assert_eq!(prefix_set.len(), 3); // Length should be 3 (excluding duplicate)
254 }
255
256 #[test]
257 fn test_freeze_shrinks_capacity() {
258 let mut prefix_set_mut = PrefixSetMut::default();
259 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
260 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
261 prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
262 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
263
264 assert_eq!(prefix_set_mut.keys.len(), 4); // Length should be 3 (including duplicate)
265 assert_eq!(prefix_set_mut.keys.capacity(), 4); // Capacity should be 4 (including duplicate)
266
267 let mut prefix_set = prefix_set_mut.freeze();
268 assert!(prefix_set.contains(&[1, 2]));
269 assert!(prefix_set.contains(&[4, 5]));
270 assert!(!prefix_set.contains(&[7, 8]));
271 assert_eq!(prefix_set.keys.len(), 3); // Length should be 3 (excluding duplicate)
272 assert_eq!(prefix_set.keys.capacity(), 3); // Capacity should be 3 after shrinking
273 }
274
275 #[test]
276 fn test_freeze_shrinks_existing_capacity() {
277 // do the above test but with preallocated capacity
278 let mut prefix_set_mut = PrefixSetMut::with_capacity(101);
279 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
280 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
281 prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
282 prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
283
284 assert_eq!(prefix_set_mut.keys.len(), 4); // Length should be 3 (including duplicate)
285 assert_eq!(prefix_set_mut.keys.capacity(), 101); // Capacity should be 101 (including duplicate)
286
287 let mut prefix_set = prefix_set_mut.freeze();
288 assert!(prefix_set.contains(&[1, 2]));
289 assert!(prefix_set.contains(&[4, 5]));
290 assert!(!prefix_set.contains(&[7, 8]));
291 assert_eq!(prefix_set.keys.len(), 3); // Length should be 3 (excluding duplicate)
292 assert_eq!(prefix_set.keys.capacity(), 3); // Capacity should be 3 after shrinking
293 }
294
295 #[test]
296 fn test_prefix_set_all_extend() {
297 let mut prefix_set_mut = PrefixSetMut::default();
298 prefix_set_mut.extend(PrefixSetMut::all());
299 assert!(prefix_set_mut.all);
300 }
301}