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 with the same already-revealed parent and the bounded range traversed to
22/// calculate their direct children.
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 their already-revealed parent. Each group traverses the bounded span from its first
39/// targeted direct child through its last targeted direct child.
40pub(crate) fn iter_sub_trie_targets(
41    targets: &mut [ProofV2Target],
42) -> impl Iterator<Item = SubTrieTargets<'_>> {
43    // Sort by parent context first so equal parents are contiguous, then by target key so the
44    // first and last targets determine the traversal bounds for the group. `None` and a known root
45    // 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    targets
53        .chunk_by_mut(|current, next| known_parent_prefix(current) == known_parent_prefix(next))
54        .map(|targets| {
55            let parent_prefix = known_parent_prefix(&targets[0]);
56            let lower_bound = target_child_prefix(&targets[0]);
57            let upper_bound = target_child_prefix(targets.last().expect("chunk is non-empty"))
58                .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 span [20, 3).
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("3"),
116                        vec![
117                            "2020202020202020202020202020202020202020202020202020202020202020",
118                            "2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f",
119                        ],
120                    ),
121                    (
122                        Some("4"),
123                        "40",
124                        Some("41"),
125                        vec!["4040404040404040404040404040404040404040404040404040404040404040"],
126                    ),
127                ],
128            ),
129            // Nested parent paths remain separate groups.
130            (
131                vec![
132                    ProofV2Target::new(B256::repeat_byte(0x20))
133                        .with_parent(ProofV2TargetParent::new(1)),
134                    ProofV2Target::new(B256::repeat_byte(0x2f))
135                        .with_parent(ProofV2TargetParent::new(2)),
136                ],
137                vec![
138                    (
139                        Some("2"),
140                        "20",
141                        Some("21"),
142                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
143                    ),
144                    (
145                        Some("2f"),
146                        "2f2",
147                        Some("2f3"),
148                        vec!["2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f"],
149                    ),
150                ],
151            ),
152            // A known-root span ending in child f is unbounded.
153            (
154                vec![
155                    ProofV2Target::new(B256::repeat_byte(0x20))
156                        .with_parent(ProofV2TargetParent::new(0)),
157                    ProofV2Target::new(B256::repeat_byte(0xf0))
158                        .with_parent(ProofV2TargetParent::new(0)),
159                ],
160                vec![(
161                    Some(""),
162                    "2",
163                    None,
164                    vec![
165                        "2020202020202020202020202020202020202020202020202020202020202020",
166                        "f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0",
167                    ],
168                )],
169            ),
170            // Parent ordering can make traversal ranges move backwards (4 then 20).
171            (
172                vec![
173                    ProofV2Target::new(B256::repeat_byte(0x20))
174                        .with_parent(ProofV2TargetParent::new(1)),
175                    ProofV2Target::new(B256::repeat_byte(0x40))
176                        .with_parent(ProofV2TargetParent::new(0)),
177                ],
178                vec![
179                    (
180                        Some(""),
181                        "4",
182                        Some("5"),
183                        vec!["4040404040404040404040404040404040404040404040404040404040404040"],
184                    ),
185                    (
186                        Some("2"),
187                        "20",
188                        Some("21"),
189                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
190                    ),
191                ],
192            ),
193            // Root and root-parent targets are distinct despite sharing a prefix.
194            (
195                vec![
196                    ProofV2Target::new(B256::repeat_byte(0x20)),
197                    ProofV2Target::new(B256::repeat_byte(0x40))
198                        .with_parent(ProofV2TargetParent::new(0)),
199                ],
200                vec![
201                    (
202                        None,
203                        "",
204                        None,
205                        vec!["2020202020202020202020202020202020202020202020202020202020202020"],
206                    ),
207                    (
208                        Some(""),
209                        "4",
210                        Some("5"),
211                        vec!["4040404040404040404040404040404040404040404040404040404040404040"],
212                    ),
213                ],
214            ),
215        ];
216
217        for (i, (mut input_targets, expected)) in test_cases.into_iter().enumerate() {
218            let actual = iter_sub_trie_targets(&mut input_targets)
219                .map(|sub_trie| {
220                    (
221                        sub_trie.parent_prefix,
222                        sub_trie.lower_bound,
223                        sub_trie.upper_bound,
224                        sub_trie
225                            .targets
226                            .iter()
227                            .map(|target| target.key_nibbles)
228                            .collect::<Vec<_>>(),
229                    )
230                })
231                .collect::<Vec<_>>();
232            let expected = expected
233                .into_iter()
234                .map(|(parent_prefix, lower_bound, upper_bound, keys)| {
235                    (
236                        parent_prefix.map(nibbles),
237                        nibbles(lower_bound),
238                        upper_bound.map(nibbles),
239                        keys.into_iter().map(nibbles).collect::<Vec<_>>(),
240                    )
241                })
242                .collect::<Vec<_>>();
243
244            assert_eq!(actual, expected, "test case {}", i + 1);
245        }
246    }
247}