Coverage Report

Created: 2024-05-16 12:16

/__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
}