Skip to main content

reth_trie_common/
prefix_set.rs

1use crate::Nibbles;
2use alloc::{sync::Arc, vec::Vec};
3use alloy_primitives::{
4    map::{B256Map, B256Set},
5    B256,
6};
7use core::ops::Range;
8
9/// Collection of mutable prefix sets.
10#[derive(Clone, Default, Debug, PartialEq, Eq)]
11pub struct TriePrefixSetsMut {
12    /// A set of account prefixes that have changed.
13    pub account_prefix_set: PrefixSetMut,
14    /// A map containing storage changes with the hashed address as key and a set of storage key
15    /// prefixes as the value.
16    pub storage_prefix_sets: B256Map<PrefixSetMut>,
17    /// A set of hashed addresses of destroyed accounts.
18    pub destroyed_accounts: B256Set,
19}
20
21impl TriePrefixSetsMut {
22    /// Returns `true` if all prefix sets are empty.
23    pub fn is_empty(&self) -> bool {
24        self.account_prefix_set.is_empty() &&
25            self.storage_prefix_sets.is_empty() &&
26            self.destroyed_accounts.is_empty()
27    }
28
29    /// Extends prefix sets with contents of another prefix set.
30    pub fn extend(&mut self, other: Self) {
31        self.account_prefix_set.extend(other.account_prefix_set);
32        for (hashed_address, prefix_set) in other.storage_prefix_sets {
33            self.storage_prefix_sets.entry(hashed_address).or_default().extend(prefix_set);
34        }
35        self.destroyed_accounts.extend(other.destroyed_accounts);
36    }
37
38    /// Extends prefix sets with contents of another prefix set by reference.
39    pub fn extend_ref(&mut self, other: &Self) {
40        self.account_prefix_set.extend_ref(&other.account_prefix_set);
41        for (hashed_address, prefix_set) in &other.storage_prefix_sets {
42            self.storage_prefix_sets.entry(*hashed_address).or_default().extend_ref(prefix_set);
43        }
44        self.destroyed_accounts.extend(other.destroyed_accounts.iter().copied());
45    }
46
47    /// Returns a `TriePrefixSets` with the same elements as these sets.
48    ///
49    /// If not yet sorted, the elements will be sorted and deduplicated.
50    pub fn freeze(self) -> TriePrefixSets {
51        TriePrefixSets {
52            account_prefix_set: self.account_prefix_set.freeze(),
53            storage_prefix_sets: self
54                .storage_prefix_sets
55                .into_iter()
56                .map(|(hashed_address, prefix_set)| (hashed_address, prefix_set.freeze()))
57                .collect(),
58            destroyed_accounts: self.destroyed_accounts,
59        }
60    }
61
62    /// Clears the prefix sets and destroyed accounts map.
63    pub fn clear(&mut self) {
64        self.destroyed_accounts.clear();
65        self.storage_prefix_sets.clear();
66        self.account_prefix_set.clear();
67    }
68}
69
70/// Collection of trie prefix sets.
71#[derive(Default, Debug, Clone)]
72pub struct TriePrefixSets {
73    /// A set of account prefixes that have changed.
74    pub account_prefix_set: PrefixSet,
75    /// A map containing storage changes with the hashed address as key and a set of storage key
76    /// prefixes as the value.
77    pub storage_prefix_sets: B256Map<PrefixSet>,
78    /// A set of hashed addresses of destroyed accounts.
79    pub destroyed_accounts: B256Set,
80}
81
82/// A container for efficiently storing and checking for the presence of key prefixes.
83///
84/// This data structure stores a set of `Nibbles` and provides methods to insert
85/// new elements and check whether any existing element has a given prefix.
86///
87/// Internally, this implementation stores keys in an unsorted `Vec<Nibbles>` together with an
88/// `all` flag. The `all` flag indicates that every entry should be considered changed and that
89/// individual keys can be ignored.
90///
91/// Sorting and deduplication do not happen during insertion or membership checks on this mutable
92/// structure. Instead, keys are sorted and deduplicated when converting into the immutable
93/// `PrefixSet` via `freeze()`. The immutable `PrefixSet` provides `contains` and relies on the
94/// sorted and unique keys produced by `freeze()`; it does not perform additional sorting or
95/// deduplication.
96///
97/// This guarantees that a `PrefixSet` constructed from a `PrefixSetMut` is always sorted and
98/// deduplicated.
99/// # Examples
100///
101/// ```
102/// use reth_trie_common::{prefix_set::PrefixSetMut, Nibbles};
103///
104/// let mut prefix_set_mut = PrefixSetMut::default();
105/// prefix_set_mut.insert(Nibbles::from_nibbles_unchecked(&[0xa, 0xb]));
106/// prefix_set_mut.insert(Nibbles::from_nibbles_unchecked(&[0xa, 0xb, 0xc]));
107/// let mut prefix_set = prefix_set_mut.freeze();
108/// assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([0xa, 0xb])));
109/// assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([0xa, 0xb, 0xc])));
110/// ```
111#[derive(PartialEq, Eq, Clone, Default, Debug)]
112pub struct PrefixSetMut {
113    /// Flag indicating that any entry should be considered changed.
114    /// If set, the keys will be discarded.
115    all: bool,
116    keys: Vec<Nibbles>,
117}
118
119impl<I> From<I> for PrefixSetMut
120where
121    I: IntoIterator<Item = Nibbles>,
122{
123    fn from(value: I) -> Self {
124        Self { all: false, keys: value.into_iter().collect() }
125    }
126}
127
128impl PrefixSetMut {
129    /// Create [`PrefixSetMut`] with pre-allocated capacity.
130    pub fn with_capacity(capacity: usize) -> Self {
131        Self { all: false, keys: Vec::with_capacity(capacity) }
132    }
133
134    /// Create [`PrefixSetMut`] that considers all key changed.
135    pub const fn all() -> Self {
136        Self { all: true, keys: Vec::new() }
137    }
138
139    /// Inserts the given `nibbles` into the set.
140    pub fn insert(&mut self, nibbles: Nibbles) {
141        self.keys.push(nibbles);
142    }
143
144    /// Extend prefix set with contents of another prefix set.
145    pub fn extend(&mut self, other: Self) {
146        self.all |= other.all;
147        self.keys.extend(other.keys);
148    }
149
150    /// Extend prefix set with contents of another prefix set by reference.
151    pub fn extend_ref(&mut self, other: &Self) {
152        self.all |= other.all;
153        self.keys.extend(other.keys.iter().copied());
154    }
155
156    /// Appends prefix set keys from another mutable prefix set, leaving it empty.
157    pub fn append(&mut self, other: &mut Self) {
158        self.all |= other.all;
159        other.all = false;
160        self.keys.append(&mut other.keys);
161    }
162
163    /// Extend prefix set keys with contents of provided iterator.
164    pub fn extend_keys<I>(&mut self, keys: I)
165    where
166        I: IntoIterator<Item = Nibbles>,
167    {
168        self.keys.extend(keys);
169    }
170
171    /// Returns an iterator over all currently retained keys.
172    pub fn iter(&self) -> core::slice::Iter<'_, Nibbles> {
173        self.keys.iter()
174    }
175
176    /// Returns the number of elements in the set.
177    pub const fn len(&self) -> usize {
178        self.keys.len()
179    }
180
181    /// Returns `true` if the set is empty and `all` flag is not set.
182    pub const fn is_empty(&self) -> bool {
183        !self.all && self.keys.is_empty()
184    }
185
186    /// Clears the inner vec for reuse, setting `all` to `false`.
187    pub fn clear(&mut self) {
188        self.all = false;
189        self.keys.clear();
190    }
191
192    /// Returns a `PrefixSet` with the same elements as this set.
193    ///
194    /// If not yet sorted, the elements will be sorted and deduplicated.
195    pub fn freeze(mut self) -> PrefixSet {
196        if !self.all {
197            self.keys.sort_unstable();
198            self.keys.dedup();
199            // Shrink after deduplication to release unused capacity.
200            self.keys.shrink_to_fit();
201        }
202        PrefixSet::new(self.all, self.keys)
203    }
204}
205
206impl<'a> IntoIterator for &'a PrefixSetMut {
207    type Item = &'a Nibbles;
208    type IntoIter = core::slice::Iter<'a, Nibbles>;
209    fn into_iter(self) -> Self::IntoIter {
210        self.iter()
211    }
212}
213
214/// A sorted prefix set that has an immutable _sorted_ list of unique keys.
215///
216/// See also [`PrefixSetMut::freeze`].
217#[derive(Debug, Clone)]
218pub struct PrefixSet {
219    /// Flag indicating that any entry should be considered changed.
220    all: bool,
221    index: usize,
222    /// `None` for an empty set to avoid allocating an empty vector.
223    keys: Option<Arc<Vec<Nibbles>>>,
224}
225
226impl Default for PrefixSet {
227    fn default() -> Self {
228        Self::new(false, Vec::new())
229    }
230}
231
232impl<I> From<I> for PrefixSet
233where
234    I: Iterator<Item = B256>,
235{
236    fn from(keys: I) -> Self {
237        let (lower_bound, upper_bound) = keys.size_hint();
238        let mut unpacked = Vec::with_capacity(upper_bound.unwrap_or(lower_bound));
239        let mut previous = None;
240
241        for key in keys {
242            debug_assert!(
243                previous.is_none_or(|previous| previous <= key),
244                "prefix set keys must be sorted"
245            );
246            if previous != Some(key) {
247                unpacked.push(Nibbles::unpack(key));
248            }
249            previous = Some(key);
250        }
251
252        Self::new(false, unpacked)
253    }
254}
255
256impl PrefixSet {
257    fn new(all: bool, keys: Vec<Nibbles>) -> Self {
258        Self { index: 0, all, keys: (!all && !keys.is_empty()).then(|| Arc::new(keys)) }
259    }
260
261    /// Creates a prefix set that considers every path changed.
262    pub fn all_paths() -> Self {
263        Self::new(true, Vec::new())
264    }
265
266    /// Returns `true` if any of the keys in the set has the given prefix
267    ///
268    /// # Note on Mutability
269    ///
270    /// This method requires `&mut self` (unlike typical `contains` methods) because it maintains an
271    /// internal position tracker (`self.index`) between calls. This enables significant performance
272    /// optimization for sequential lookups in sorted order, which is common during trie traversal.
273    ///
274    /// The `index` field allows subsequent searches to start where previous ones left off,
275    /// avoiding repeated full scans of the prefix array when keys are accessed in nearby ranges.
276    ///
277    /// This optimization was inspired by Silkworm's implementation and significantly improves
278    /// incremental state root calculation performance
279    /// ([see PR #2417](https://github.com/paradigmxyz/reth/pull/2417)).
280    #[inline]
281    pub fn contains(&mut self, prefix: &Nibbles) -> bool {
282        if self.all {
283            return true
284        }
285
286        let keys = self.keys.as_deref().map(Vec::as_slice).unwrap_or_default();
287        while self.index > 0 && &keys[self.index] > prefix {
288            self.index -= 1;
289        }
290
291        for (idx, key) in keys[self.index..].iter().enumerate() {
292            if key.starts_with(prefix) {
293                self.index += idx;
294                return true
295            }
296
297            if key > prefix {
298                self.index += idx;
299                return false
300            }
301        }
302
303        false
304    }
305
306    /// Returns `true` if any key in the set falls within the given half-open range
307    /// `[start, end)`.
308    ///
309    /// Like [`Self::contains`], this method maintains the internal index for sequential access
310    /// optimization.
311    #[inline]
312    pub fn contains_range(&mut self, range: Range<&Nibbles>) -> bool {
313        if self.all {
314            return true
315        }
316
317        let keys = self.keys.as_deref().map(Vec::as_slice).unwrap_or_default();
318        while self.index > 0 && &keys[self.index] >= range.end {
319            self.index -= 1;
320        }
321
322        for (idx, key) in keys[self.index..].iter().enumerate() {
323            if key >= range.start && key < range.end {
324                self.index += idx;
325                return true
326            }
327
328            if key >= range.end {
329                self.index += idx;
330                return false
331            }
332        }
333
334        false
335    }
336
337    /// Returns `true` if any key in the set is at or after `start`.
338    #[inline]
339    pub fn contains_from(&mut self, start: &Nibbles) -> bool {
340        if self.all {
341            return true
342        }
343
344        let keys = self.keys.as_deref().map(Vec::as_slice).unwrap_or_default();
345        while self.index > 0 && &keys[self.index] > start {
346            self.index -= 1;
347        }
348
349        for (idx, key) in keys[self.index..].iter().enumerate() {
350            if key >= start {
351                self.index += idx;
352                return true
353            }
354        }
355
356        false
357    }
358
359    /// Returns an iterator over reference to _all_ nibbles regardless of cursor position.
360    pub fn iter(&self) -> core::slice::Iter<'_, Nibbles> {
361        self.slice().iter()
362    }
363
364    /// Returns the underlying sorted prefix slice.
365    pub fn slice(&self) -> &[Nibbles] {
366        self.keys.as_deref().map(Vec::as_slice).unwrap_or_default()
367    }
368
369    /// Returns true if every entry should be considered changed.
370    pub const fn all(&self) -> bool {
371        self.all
372    }
373
374    /// Returns the number of elements in the set.
375    pub fn len(&self) -> usize {
376        self.slice().len()
377    }
378
379    /// Returns `true` if the set is empty and `all` flag is not set.
380    pub const fn is_empty(&self) -> bool {
381        !self.all && self.keys.is_none()
382    }
383}
384
385impl<'a> IntoIterator for &'a PrefixSet {
386    type Item = &'a Nibbles;
387    type IntoIter = core::slice::Iter<'a, Nibbles>;
388    fn into_iter(self) -> Self::IntoIter {
389        self.iter()
390    }
391}
392
393#[cfg(test)]
394mod tests {
395    use super::*;
396    use alloy_primitives::B256;
397
398    #[test]
399    fn test_contains_with_multiple_inserts_and_duplicates() {
400        let mut prefix_set_mut = PrefixSetMut::default();
401        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
402        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
403        prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
404        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
405
406        let mut prefix_set = prefix_set_mut.freeze();
407        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2])));
408        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([4, 5])));
409        assert!(!prefix_set.contains(&Nibbles::from_nibbles_unchecked([7, 8])));
410        assert_eq!(prefix_set.len(), 3); // Length should be 3 (excluding duplicate)
411    }
412
413    #[test]
414    fn test_contains_from() {
415        let first = Nibbles::from_nibbles([1, 2, 3]);
416        let middle = Nibbles::from_nibbles([1, 2, 4]);
417        let last = Nibbles::from_nibbles([4, 5, 6]);
418        let mut prefix_set = PrefixSetMut::from([first, middle, last]).freeze();
419
420        assert!(prefix_set.contains_range(&first..&middle));
421        assert!(prefix_set.contains_from(&middle));
422        assert!(prefix_set.contains_from(&last));
423        assert!(!prefix_set.contains_from(&Nibbles::from_nibbles([5])));
424    }
425
426    #[test]
427    fn test_from_sorted_b256_iterator() {
428        let first = B256::with_last_byte(1);
429        let second = B256::with_last_byte(2);
430        let prefix_set = PrefixSet::from([first, second].into_iter());
431
432        assert_eq!(prefix_set.slice(), &[Nibbles::unpack(first), Nibbles::unpack(second)]);
433    }
434
435    #[test]
436    fn test_from_sorted_b256_iterator_with_duplicates() {
437        let first = B256::with_last_byte(1);
438        let second = B256::with_last_byte(2);
439        let prefix_set = PrefixSet::from([first, first, second].into_iter());
440
441        assert_eq!(prefix_set.slice(), &[Nibbles::unpack(first), Nibbles::unpack(second)]);
442    }
443
444    #[cfg(debug_assertions)]
445    #[test]
446    #[should_panic(expected = "prefix set keys must be sorted")]
447    fn test_from_unsorted_b256_iterator() {
448        let _: PrefixSet = [B256::with_last_byte(2), B256::with_last_byte(1)].into_iter().into();
449    }
450
451    #[test]
452    fn test_freeze_shrinks_capacity() {
453        let mut prefix_set_mut = PrefixSetMut::default();
454        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
455        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
456        prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
457        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
458
459        assert_eq!(prefix_set_mut.keys.len(), 4); // Length is 4 (before deduplication)
460        assert_eq!(prefix_set_mut.keys.capacity(), 4); // Capacity is 4 (before deduplication)
461
462        let mut prefix_set = prefix_set_mut.freeze();
463        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2])));
464        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([4, 5])));
465        assert!(!prefix_set.contains(&Nibbles::from_nibbles_unchecked([7, 8])));
466        assert_eq!(prefix_set.slice().len(), 3); // Length should be 3 (excluding duplicate)
467        assert_eq!(prefix_set.keys.as_ref().unwrap().capacity(), 3); // Capacity after shrinking
468    }
469
470    #[test]
471    fn test_freeze_shrinks_existing_capacity() {
472        // do the above test but with preallocated capacity
473        let mut prefix_set_mut = PrefixSetMut::with_capacity(101);
474        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
475        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
476        prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
477        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
478
479        assert_eq!(prefix_set_mut.keys.len(), 4); // Length is 4 (before deduplication)
480        assert_eq!(prefix_set_mut.keys.capacity(), 101); // Capacity is 101 (before deduplication)
481
482        let mut prefix_set = prefix_set_mut.freeze();
483        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2])));
484        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([4, 5])));
485        assert!(!prefix_set.contains(&Nibbles::from_nibbles_unchecked([7, 8])));
486        assert_eq!(prefix_set.slice().len(), 3); // Length should be 3 (excluding duplicate)
487        assert_eq!(prefix_set.keys.as_ref().unwrap().capacity(), 3); // Capacity after shrinking
488    }
489
490    #[test]
491    fn test_empty_prefix_sets_do_not_allocate() {
492        assert!(PrefixSet::default().keys.is_none());
493        assert!(PrefixSetMut::default().freeze().keys.is_none());
494        assert!(PrefixSet::from(core::iter::empty()).keys.is_none());
495        assert!(PrefixSet::all_paths().keys.is_none());
496    }
497
498    #[test]
499    fn test_prefix_set_all_extend() {
500        let mut prefix_set_mut = PrefixSetMut::default();
501        prefix_set_mut.extend(PrefixSetMut::all());
502        assert!(prefix_set_mut.all);
503    }
504
505    #[test]
506    fn test_prefix_set_slice_returns_frozen_keys() {
507        let path_a = Nibbles::from_nibbles([1, 2, 3]);
508        let path_b = Nibbles::from_nibbles([4, 5, 6]);
509        let mut prefix_set_mut = PrefixSetMut::default();
510        prefix_set_mut.insert(path_b);
511        prefix_set_mut.insert(path_a);
512        prefix_set_mut.insert(path_b);
513
514        let prefix_set = prefix_set_mut.freeze();
515        assert_eq!(prefix_set.slice(), &[path_a, path_b]);
516    }
517
518    #[test]
519    fn test_trie_prefix_sets_mut_extend_ref() {
520        let account_path = Nibbles::from_nibbles([1, 2]);
521        let storage_path = Nibbles::from_nibbles([3, 4]);
522        let storage_account = B256::with_last_byte(1);
523        let destroyed_account = B256::with_last_byte(2);
524        let other = TriePrefixSetsMut {
525            account_prefix_set: PrefixSetMut::from([account_path]),
526            storage_prefix_sets: B256Map::from_iter([(
527                storage_account,
528                PrefixSetMut::from([storage_path]),
529            )]),
530            destroyed_accounts: B256Set::from_iter([destroyed_account]),
531        };
532
533        let mut prefix_sets = TriePrefixSetsMut::default();
534        prefix_sets.extend_ref(&other);
535
536        let frozen = prefix_sets.freeze();
537        assert_eq!(frozen.account_prefix_set.slice(), &[account_path]);
538        assert_eq!(frozen.storage_prefix_sets[&storage_account].slice(), &[storage_path]);
539        assert!(frozen.destroyed_accounts.contains(&destroyed_account));
540        assert_eq!(other.account_prefix_set.len(), 1);
541    }
542}