/__w/smoldot/smoldot/repo/lib/src/trie/branch_search/tests.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2023 Pierre Krieger |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | #![cfg(test)] |
19 | | |
20 | | use super::{start_branch_search, BranchSearch, Config}; |
21 | | use crate::trie; |
22 | | |
23 | | use rand::distributions::{Distribution as _, Uniform}; |
24 | | |
25 | | #[test] |
26 | 1 | fn fuzzing() { |
27 | 2.11M | fn uniform_sample(min: u8, max: u8) -> u8 { |
28 | 2.11M | Uniform::new_inclusive(min, max).sample(&mut rand::thread_rng()) |
29 | 2.11M | } |
30 | | |
31 | | // We run the test multiple times because of randomness. |
32 | 2.04k | for _ in 0..2048 { |
33 | | // Generate a random trie structure. |
34 | 2.04k | let mut trie = trie::trie_structure::TrieStructure::<()>::new(); |
35 | 2.04k | let mut list = vec![Vec::new()]; |
36 | 2.04k | for elem in list.clone().into_iter() { |
37 | 2.04k | for _ in 0..uniform_sample(0, 4) { |
38 | 4.15k | let mut elem = elem.clone(); |
39 | 6.34k | for _ in 0..uniform_sample(0, 3) { |
40 | 6.34k | elem.push(uniform_sample(0, 255)); |
41 | 6.34k | } |
42 | 4.15k | list.push(elem); |
43 | | } |
44 | | } |
45 | 8.25k | for elem6.20k in list { |
46 | 6.20k | match trie.node(trie::bytes_to_nibbles(elem.iter().copied())) { |
47 | 5.23k | trie::trie_structure::Entry::Vacant(e) => { |
48 | 5.23k | e.insert_storage_value().insert((), ()); |
49 | 5.23k | } |
50 | | trie::trie_structure::Entry::Occupied( |
51 | 0 | trie::trie_structure::NodeAccess::Branch(e), |
52 | 0 | ) => { |
53 | 0 | e.insert_storage_value(); |
54 | 0 | } |
55 | | trie::trie_structure::Entry::Occupied( |
56 | | trie::trie_structure::NodeAccess::Storage(_), |
57 | 972 | ) => {} |
58 | | } |
59 | | } |
60 | | |
61 | | // Now perform random key search requests. |
62 | 526k | for _ in 0..256 { |
63 | | // Generate random search parameters. |
64 | 524k | let search_params = { |
65 | 524k | let mut prefix = Vec::new(); |
66 | 524k | for _ in 0..uniform_sample(0, 2) { |
67 | 524k | prefix.push(trie::Nibble::try_from(uniform_sample(0, 15)).unwrap()); |
68 | 524k | } |
69 | | |
70 | | Config { |
71 | | key_before: { |
72 | 524k | let mut key = prefix.clone(); |
73 | 524k | for _ in 0..uniform_sample(0, 2) { |
74 | 524k | key.push(trie::Nibble::try_from(uniform_sample(0, 15)).unwrap()); |
75 | 524k | } |
76 | 524k | key |
77 | 524k | }, |
78 | 524k | no_branch_search: rand::random(), |
79 | 524k | or_equal: rand::random(), |
80 | 524k | prefix, |
81 | 524k | } |
82 | 524k | }; |
83 | 524k | |
84 | 524k | // Find the expected value for these parameters. |
85 | 524k | let expected = { |
86 | 1.25M | trie.iter_ordered().find_map(|node_index| { |
87 | 1.25M | if !trie.is_storage(node_index) && search_params.no_branch_search32.7k { |
88 | 16.3k | return None; |
89 | 1.24M | } |
90 | 1.24M | let full_key = trie |
91 | 1.24M | .node_full_key_by_index(node_index) |
92 | 1.24M | .unwrap() |
93 | 1.24M | .collect::<Vec<_>>(); |
94 | 1.24M | if full_key < search_params.key_before { |
95 | 830k | return None; |
96 | 410k | } |
97 | 410k | if full_key == search_params.key_before && !search_params.or_equal58.7k { |
98 | 29.2k | return None; |
99 | 380k | } |
100 | 380k | if !full_key.starts_with(&search_params.prefix) { |
101 | 260k | return None; |
102 | 119k | } |
103 | 119k | Some(full_key) |
104 | 1.25M | })524k |
105 | | }; |
106 | | |
107 | | // Now find the value using the branch searcher. |
108 | 524k | let obtained = { |
109 | 524k | let mut search = BranchSearch::NextKey(start_branch_search(Config { |
110 | 524k | key_before: search_params.key_before.iter().copied(), |
111 | 524k | or_equal: search_params.or_equal, |
112 | 524k | no_branch_search: search_params.no_branch_search, |
113 | 524k | prefix: search_params.prefix.iter().copied(), |
114 | 524k | })); |
115 | | |
116 | 1.09M | loop { |
117 | 1.09M | match search { |
118 | | BranchSearch::Found { |
119 | 524k | branch_trie_node_key, |
120 | 524k | } => break branch_trie_node_key.map(|k| k.collect::<Vec<_>>()119k ), |
121 | 571k | BranchSearch::NextKey(req) => { |
122 | 1.36M | let key = trie.iter_ordered().find_map(|node_index| { |
123 | 1.36M | if !trie.is_storage(node_index) { |
124 | 37.1k | return None; |
125 | 1.32M | } |
126 | 1.32M | |
127 | 1.32M | let full_key = trie |
128 | 1.32M | .node_full_key_by_index(node_index) |
129 | 1.32M | .unwrap() |
130 | 1.32M | .collect::<Vec<_>>(); |
131 | 1.32M | if full_key |
132 | 1.32M | < trie::bytes_to_nibbles(req.key_before()).collect::<Vec<_>>() |
133 | | { |
134 | 940k | return None; |
135 | 383k | } |
136 | 383k | if full_key |
137 | 383k | == trie::bytes_to_nibbles(req.key_before()).collect::<Vec<_>>() |
138 | 58.8k | && !req.or_equal() |
139 | | { |
140 | 29.1k | return None; |
141 | 354k | } |
142 | 354k | if !full_key.starts_with( |
143 | 354k | &trie::bytes_to_nibbles(req.prefix()).collect::<Vec<_>>(), |
144 | 354k | ) { |
145 | 135k | return None; |
146 | 218k | } |
147 | 218k | |
148 | 218k | assert!(full_key.len() % 2 == 0); |
149 | 218k | Some(full_key) |
150 | 1.36M | }); |
151 | 571k | |
152 | 571k | search = req.inject( |
153 | 571k | key.map(|k| trie::nibbles_to_bytes_suffix_extend(k.into_iter())218k ), |
154 | 571k | ); |
155 | 571k | } |
156 | | } |
157 | | } |
158 | | }; |
159 | | |
160 | | // Compare them! |
161 | 524k | if expected != obtained { |
162 | 0 | panic!( |
163 | 0 | "\nexpected: {:?}\nobtained: {:?}\nsearch_params: {:?}\ntrie: {:?}", |
164 | 0 | expected, obtained, search_params, trie |
165 | 0 | ); |
166 | 524k | } |
167 | | } |
168 | | } |
169 | 1 | } |