Skip to main content

reth_trie/proof_v2/
target.rs

1use reth_trie_common::{Nibbles, ProofV2Target};
2
3// Returns the path of the already-revealed parent branch for a target. `None` means the target
4// needs the actual trie root, while `Some(Nibbles::new())` means the root branch is already
5// revealed.
6#[inline]
7pub(crate) fn known_parent_prefix(target: &ProofV2Target) -> Option<Nibbles> {
8    target.parent.path(target.key_nibbles)
9}
10
11// Returns the direct child of the known parent which contains the target. If there is no known
12// parent then the target requires the full trie.
13#[inline]
14fn target_child_prefix(target: &ProofV2Target) -> Nibbles {
15    target
16        .parent
17        .path_len()
18        .map_or_else(Nibbles::new, |parent_len| target.key_nibbles.slice(0..parent_len + 1))
19}
20
21/// Describes targets below one direct child of an already-revealed parent and the bounded range
22/// traversed to calculate their proofs.
23pub(crate) struct SubTrieTargets<'a> {
24    /// The first path traversed for these targets.
25    pub(crate) lower_bound: Nibbles,
26    /// The first path after the traversal range, or `None` if it extends through the end of the
27    /// trie.
28    pub(crate) upper_bound: Option<Nibbles>,
29    /// The path of the already-revealed parent branch. `None` means the actual trie root is
30    /// requested, while `Some(Nibbles::new())` means the root branch is already revealed.
31    pub(crate) parent_prefix: Option<Nibbles>,
32    /// The targets belonging to this sub-trie. These will be sorted by their `key` field,
33    /// lexicographically.
34    pub(crate) targets: &'a [ProofV2Target],
35}
36
37/// Given a set of [`ProofV2Target`]s, returns an iterator over those same [`ProofV2Target`]s
38/// grouped by the direct child of their already-revealed parent. Targets without a known parent
39/// remain grouped together to calculate the root. A group never traverses another child of a
40/// revealed parent.
41pub(crate) fn iter_sub_trie_targets(
42    targets: &mut [ProofV2Target],
43) -> impl Iterator<Item = SubTrieTargets<'_>> {
44    // Sort by parent context first so equal parents are contiguous, then by target key so targets
45    // below the same direct child are contiguous. `None` and a known root parent remain distinct.
46    targets.sort_unstable_by(|a, b| {
47        known_parent_prefix(a)
48            .cmp(&known_parent_prefix(b))
49            .then_with(|| a.key_nibbles.cmp(&b.key_nibbles))
50    });
51
52    // A revealed sibling can contain updates that are not present in the database view.
53    targets
54        .chunk_by_mut(|current, next| target_child_prefix(current) == target_child_prefix(next))
55        .map(|targets| {
56            let parent_prefix = known_parent_prefix(&targets[0]);
57            let lower_bound = target_child_prefix(&targets[0]);
58            let upper_bound = lower_bound.next_without_prefix();
59            SubTrieTargets { lower_bound, upper_bound, parent_prefix, targets }
60        })
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use alloy_primitives::B256;
67    use reth_trie_common::ProofV2TargetParent;
68
69    #[test]
70    fn test_iter_sub_trie_targets() {
71        // Helper to create nibbles from hex string (each character is a nibble)
72        let nibbles = |hex: &str| -> Nibbles {
73            if hex.is_empty() {
74                return Nibbles::new();
75            }
76            format!("0x{}", hex).parse().expect("valid nibbles hex string")
77        };
78
79        // Test cases: (input_targets, expected_output)
80        // Expected output format:
81        // Vec<(known_parent_prefix_hex, lower_bound_hex, upper_bound_hex, Vec<key_hex>)>
82        let test_cases = vec![
83            // Empty targets.
84            (vec![], vec![]),
85            // A root traversal stays unbounded and sorts its targets.
86            (
87                vec![
88                    ProofV2Target::new(B256::repeat_byte(0x21)),
89                    ProofV2Target::new(B256::repeat_byte(0x20)),
90                ],
91                vec![(
92                    None,
93                    "",
94                    None,
95                    vec![
96                        "2020202020202020202020202020202020202020202020202020202020202020",
97                        "2121212121212121212121212121212121212121212121212121212121212121",
98                    ],
99                )],
100            ),
101            // Targets below children 0 and f of parent 2 use separate ranges.
102            (
103                vec![
104                    ProofV2Target::new(B256::repeat_byte(0x20))
105                        .with_parent(ProofV2TargetParent::new(1)),
106                    ProofV2Target::new(B256::repeat_byte(0x2f))
107                        .with_parent(ProofV2TargetParent::new(1)),
108                    ProofV2Target::new(B256::repeat_byte(0x40))
109                        .with_parent(ProofV2TargetParent::new(1)),
110                ],
111                vec![
112                    (
113                        Some("2"),
114                        "20",
115                        Some("21"),
116                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
117                    ),
118                    (
119                        Some("2"),
120                        "2f",
121                        Some("3"),
122                        vec!["2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f"],
123                    ),
124                    (
125                        Some("4"),
126                        "40",
127                        Some("41"),
128                        vec!["4040404040404040404040404040404040404040404040404040404040404040"],
129                    ),
130                ],
131            ),
132            // Nested parent paths remain separate groups.
133            (
134                vec![
135                    ProofV2Target::new(B256::repeat_byte(0x20))
136                        .with_parent(ProofV2TargetParent::new(1)),
137                    ProofV2Target::new(B256::repeat_byte(0x2f))
138                        .with_parent(ProofV2TargetParent::new(2)),
139                ],
140                vec![
141                    (
142                        Some("2"),
143                        "20",
144                        Some("21"),
145                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
146                    ),
147                    (
148                        Some("2f"),
149                        "2f2",
150                        Some("2f3"),
151                        vec!["2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f"],
152                    ),
153                ],
154            ),
155            // A known-root child f range is unbounded.
156            (
157                vec![
158                    ProofV2Target::new(B256::repeat_byte(0x20))
159                        .with_parent(ProofV2TargetParent::new(0)),
160                    ProofV2Target::new(B256::repeat_byte(0xf0))
161                        .with_parent(ProofV2TargetParent::new(0)),
162                ],
163                vec![
164                    (
165                        Some(""),
166                        "2",
167                        Some("3"),
168                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
169                    ),
170                    (
171                        Some(""),
172                        "f",
173                        None,
174                        vec!["f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0"],
175                    ),
176                ],
177            ),
178            // Parent ordering can make traversal ranges move backwards (4 then 20).
179            (
180                vec![
181                    ProofV2Target::new(B256::repeat_byte(0x20))
182                        .with_parent(ProofV2TargetParent::new(1)),
183                    ProofV2Target::new(B256::repeat_byte(0x40))
184                        .with_parent(ProofV2TargetParent::new(0)),
185                ],
186                vec![
187                    (
188                        Some(""),
189                        "4",
190                        Some("5"),
191                        vec!["4040404040404040404040404040404040404040404040404040404040404040"],
192                    ),
193                    (
194                        Some("2"),
195                        "20",
196                        Some("21"),
197                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
198                    ),
199                ],
200            ),
201            // Root and root-parent targets are distinct despite sharing a prefix.
202            (
203                vec![
204                    ProofV2Target::new(B256::repeat_byte(0x20)),
205                    ProofV2Target::new(B256::repeat_byte(0x40))
206                        .with_parent(ProofV2TargetParent::new(0)),
207                ],
208                vec![
209                    (
210                        None,
211                        "",
212                        None,
213                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
214                    ),
215                    (
216                        Some(""),
217                        "4",
218                        Some("5"),
219                        vec!["4040404040404040404040404040404040404040404040404040404040404040"],
220                    ),
221                ],
222            ),
223        ];
224
225        for (i, (mut input_targets, expected)) in test_cases.into_iter().enumerate() {
226            let actual = iter_sub_trie_targets(&mut input_targets)
227                .map(|sub_trie| {
228                    (
229                        sub_trie.parent_prefix,
230                        sub_trie.lower_bound,
231                        sub_trie.upper_bound,
232                        sub_trie
233                            .targets
234                            .iter()
235                            .map(|target| target.key_nibbles)
236                            .collect::<Vec<_>>(),
237                    )
238                })
239                .collect::<Vec<_>>();
240            let expected = expected
241                .into_iter()
242                .map(|(parent_prefix, lower_bound, upper_bound, keys)| {
243                    (
244                        parent_prefix.map(nibbles),
245                        nibbles(lower_bound),
246                        upper_bound.map(nibbles),
247                        keys.into_iter().map(nibbles).collect::<Vec<_>>(),
248                    )
249                })
250                .collect::<Vec<_>>();
251
252            assert_eq!(actual, expected, "test case {}", i + 1);
253        }
254    }
255}