Skip to main content

reth_trie_common/
prefix_set.rs

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