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            PrefixSet { index: 0, all: true, keys: Arc::new(Vec::new()) }
198        } else {
199            self.keys.sort_unstable();
200            self.keys.dedup();
201            // Shrink after deduplication to release unused capacity.
202            self.keys.shrink_to_fit();
203            PrefixSet { index: 0, all: false, keys: Arc::new(self.keys) }
204        }
205    }
206}
207
208impl<'a> IntoIterator for &'a PrefixSetMut {
209    type Item = &'a Nibbles;
210    type IntoIter = core::slice::Iter<'a, Nibbles>;
211    fn into_iter(self) -> Self::IntoIter {
212        self.iter()
213    }
214}
215
216/// A sorted prefix set that has an immutable _sorted_ list of unique keys.
217///
218/// See also [`PrefixSetMut::freeze`].
219#[derive(Debug, Default, Clone)]
220pub struct PrefixSet {
221    /// Flag indicating that any entry should be considered changed.
222    all: bool,
223    index: usize,
224    keys: Arc<Vec<Nibbles>>,
225}
226
227impl<I> From<I> for PrefixSet
228where
229    I: Iterator<Item = B256>,
230{
231    fn from(keys: I) -> Self {
232        let (lower_bound, upper_bound) = keys.size_hint();
233        let mut unpacked = Vec::with_capacity(upper_bound.unwrap_or(lower_bound));
234        let mut previous = None;
235
236        for key in keys {
237            debug_assert!(
238                previous.is_none_or(|previous| previous <= key),
239                "prefix set keys must be sorted"
240            );
241            if previous != Some(key) {
242                unpacked.push(Nibbles::unpack(key));
243            }
244            previous = Some(key);
245        }
246
247        Self { index: 0, all: false, keys: Arc::new(unpacked) }
248    }
249}
250
251impl PrefixSet {
252    /// Creates a prefix set that considers every path changed.
253    pub fn all_paths() -> Self {
254        Self { index: 0, all: true, keys: Arc::new(Vec::new()) }
255    }
256
257    /// Returns `true` if any of the keys in the set has the given prefix
258    ///
259    /// # Note on Mutability
260    ///
261    /// This method requires `&mut self` (unlike typical `contains` methods) because it maintains an
262    /// internal position tracker (`self.index`) between calls. This enables significant performance
263    /// optimization for sequential lookups in sorted order, which is common during trie traversal.
264    ///
265    /// The `index` field allows subsequent searches to start where previous ones left off,
266    /// avoiding repeated full scans of the prefix array when keys are accessed in nearby ranges.
267    ///
268    /// This optimization was inspired by Silkworm's implementation and significantly improves
269    /// incremental state root calculation performance
270    /// ([see PR #2417](https://github.com/paradigmxyz/reth/pull/2417)).
271    #[inline]
272    pub fn contains(&mut self, prefix: &Nibbles) -> bool {
273        if self.all {
274            return true
275        }
276
277        while self.index > 0 && &self.keys[self.index] > prefix {
278            self.index -= 1;
279        }
280
281        for (idx, key) in self.keys[self.index..].iter().enumerate() {
282            if key.starts_with(prefix) {
283                self.index += idx;
284                return true
285            }
286
287            if key > prefix {
288                self.index += idx;
289                return false
290            }
291        }
292
293        false
294    }
295
296    /// Returns `true` if any key in the set falls within the given half-open range
297    /// `[start, end)`.
298    ///
299    /// Like [`Self::contains`], this method maintains the internal index for sequential access
300    /// optimization.
301    #[inline]
302    pub fn contains_range(&mut self, range: Range<&Nibbles>) -> bool {
303        if self.all {
304            return true
305        }
306
307        while self.index > 0 && &self.keys[self.index] >= range.end {
308            self.index -= 1;
309        }
310
311        for (idx, key) in self.keys[self.index..].iter().enumerate() {
312            if key >= range.start && key < range.end {
313                self.index += idx;
314                return true
315            }
316
317            if key >= range.end {
318                self.index += idx;
319                return false
320            }
321        }
322
323        false
324    }
325
326    /// Returns an iterator over reference to _all_ nibbles regardless of cursor position.
327    pub fn iter(&self) -> core::slice::Iter<'_, Nibbles> {
328        self.keys.iter()
329    }
330
331    /// Returns the underlying sorted prefix slice.
332    pub fn slice(&self) -> &[Nibbles] {
333        self.keys.as_slice()
334    }
335
336    /// Returns true if every entry should be considered changed.
337    pub const fn all(&self) -> bool {
338        self.all
339    }
340
341    /// Returns the number of elements in the set.
342    pub fn len(&self) -> usize {
343        self.keys.len()
344    }
345
346    /// Returns `true` if the set is empty and `all` flag is not set.
347    pub fn is_empty(&self) -> bool {
348        !self.all && self.keys.is_empty()
349    }
350}
351
352impl<'a> IntoIterator for &'a PrefixSet {
353    type Item = &'a Nibbles;
354    type IntoIter = core::slice::Iter<'a, Nibbles>;
355    fn into_iter(self) -> Self::IntoIter {
356        self.iter()
357    }
358}
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363    use alloy_primitives::B256;
364
365    #[test]
366    fn test_contains_with_multiple_inserts_and_duplicates() {
367        let mut prefix_set_mut = PrefixSetMut::default();
368        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
369        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
370        prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
371        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
372
373        let mut prefix_set = prefix_set_mut.freeze();
374        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2])));
375        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([4, 5])));
376        assert!(!prefix_set.contains(&Nibbles::from_nibbles_unchecked([7, 8])));
377        assert_eq!(prefix_set.len(), 3); // Length should be 3 (excluding duplicate)
378    }
379
380    #[test]
381    fn test_from_sorted_b256_iterator() {
382        let first = B256::with_last_byte(1);
383        let second = B256::with_last_byte(2);
384        let prefix_set = PrefixSet::from([first, second].into_iter());
385
386        assert_eq!(prefix_set.slice(), &[Nibbles::unpack(first), Nibbles::unpack(second)]);
387    }
388
389    #[test]
390    fn test_from_sorted_b256_iterator_with_duplicates() {
391        let first = B256::with_last_byte(1);
392        let second = B256::with_last_byte(2);
393        let prefix_set = PrefixSet::from([first, first, second].into_iter());
394
395        assert_eq!(prefix_set.slice(), &[Nibbles::unpack(first), Nibbles::unpack(second)]);
396    }
397
398    #[cfg(debug_assertions)]
399    #[test]
400    #[should_panic(expected = "prefix set keys must be sorted")]
401    fn test_from_unsorted_b256_iterator() {
402        let _: PrefixSet = [B256::with_last_byte(2), B256::with_last_byte(1)].into_iter().into();
403    }
404
405    #[test]
406    fn test_freeze_shrinks_capacity() {
407        let mut prefix_set_mut = PrefixSetMut::default();
408        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
409        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
410        prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
411        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
412
413        assert_eq!(prefix_set_mut.keys.len(), 4); // Length is 4 (before deduplication)
414        assert_eq!(prefix_set_mut.keys.capacity(), 4); // Capacity is 4 (before deduplication)
415
416        let mut prefix_set = prefix_set_mut.freeze();
417        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2])));
418        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([4, 5])));
419        assert!(!prefix_set.contains(&Nibbles::from_nibbles_unchecked([7, 8])));
420        assert_eq!(prefix_set.keys.len(), 3); // Length should be 3 (excluding duplicate)
421        assert_eq!(prefix_set.keys.capacity(), 3); // Capacity should be 3 after shrinking
422    }
423
424    #[test]
425    fn test_freeze_shrinks_existing_capacity() {
426        // do the above test but with preallocated capacity
427        let mut prefix_set_mut = PrefixSetMut::with_capacity(101);
428        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3]));
429        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 4]));
430        prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6]));
431        prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate
432
433        assert_eq!(prefix_set_mut.keys.len(), 4); // Length is 4 (before deduplication)
434        assert_eq!(prefix_set_mut.keys.capacity(), 101); // Capacity is 101 (before deduplication)
435
436        let mut prefix_set = prefix_set_mut.freeze();
437        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2])));
438        assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([4, 5])));
439        assert!(!prefix_set.contains(&Nibbles::from_nibbles_unchecked([7, 8])));
440        assert_eq!(prefix_set.keys.len(), 3); // Length should be 3 (excluding duplicate)
441        assert_eq!(prefix_set.keys.capacity(), 3); // Capacity should be 3 after shrinking
442    }
443
444    #[test]
445    fn test_prefix_set_all_extend() {
446        let mut prefix_set_mut = PrefixSetMut::default();
447        prefix_set_mut.extend(PrefixSetMut::all());
448        assert!(prefix_set_mut.all);
449    }
450
451    #[test]
452    fn test_prefix_set_slice_returns_frozen_keys() {
453        let path_a = Nibbles::from_nibbles([1, 2, 3]);
454        let path_b = Nibbles::from_nibbles([4, 5, 6]);
455        let mut prefix_set_mut = PrefixSetMut::default();
456        prefix_set_mut.insert(path_b);
457        prefix_set_mut.insert(path_a);
458        prefix_set_mut.insert(path_b);
459
460        let prefix_set = prefix_set_mut.freeze();
461        assert_eq!(prefix_set.slice(), &[path_a, path_b]);
462    }
463
464    #[test]
465    fn test_trie_prefix_sets_mut_extend_ref() {
466        let account_path = Nibbles::from_nibbles([1, 2]);
467        let storage_path = Nibbles::from_nibbles([3, 4]);
468        let storage_account = B256::with_last_byte(1);
469        let destroyed_account = B256::with_last_byte(2);
470        let other = TriePrefixSetsMut {
471            account_prefix_set: PrefixSetMut::from([account_path]),
472            storage_prefix_sets: B256Map::from_iter([(
473                storage_account,
474                PrefixSetMut::from([storage_path]),
475            )]),
476            destroyed_accounts: B256Set::from_iter([destroyed_account]),
477        };
478
479        let mut prefix_sets = TriePrefixSetsMut::default();
480        prefix_sets.extend_ref(&other);
481
482        let frozen = prefix_sets.freeze();
483        assert_eq!(frozen.account_prefix_set.slice(), &[account_path]);
484        assert_eq!(frozen.storage_prefix_sets[&storage_account].slice(), &[storage_path]);
485        assert!(frozen.destroyed_accounts.contains(&destroyed_account));
486        assert_eq!(other.account_prefix_set.len(), 1);
487    }
488}