/root/bitcoin/src/test/fuzz/descriptor_parse.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2009-2021 The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <chainparams.h> |
6 | | #include <key_io.h> |
7 | | #include <pubkey.h> |
8 | | #include <script/descriptor.h> |
9 | | #include <test/fuzz/fuzz.h> |
10 | | #include <test/fuzz/util/descriptor.h> |
11 | | #include <util/chaintype.h> |
12 | | #include <util/strencodings.h> |
13 | | |
14 | | //! The converter of mocked descriptors, needs to be initialized when the target is. |
15 | | MockedDescriptorConverter MOCKED_DESC_CONVERTER; |
16 | | |
17 | | /** Test a successfully parsed descriptor. */ |
18 | | static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy, std::optional<bool>& is_ranged, std::optional<bool>& is_solvable) |
19 | 0 | { |
20 | | // Trivial helpers. |
21 | 0 | (void)desc.IsRange(); |
22 | 0 | (void)desc.IsSingleType(); |
23 | 0 | (void)desc.GetOutputType(); |
24 | |
|
25 | 0 | if (is_ranged.has_value()) { |
26 | 0 | assert(desc.IsRange() == *is_ranged); |
27 | 0 | } else { |
28 | 0 | is_ranged = desc.IsRange(); |
29 | 0 | } |
30 | 0 | if (is_solvable.has_value()) { |
31 | 0 | assert(desc.IsSolvable() == *is_solvable); |
32 | 0 | } else { |
33 | 0 | is_solvable = desc.IsSolvable(); |
34 | 0 | } |
35 | | |
36 | | // Serialization to string representation. |
37 | 0 | (void)desc.ToString(); |
38 | 0 | (void)desc.ToPrivateString(sig_provider, dummy); |
39 | 0 | (void)desc.ToNormalizedString(sig_provider, dummy); |
40 | | |
41 | | // Serialization to Script. |
42 | 0 | DescriptorCache cache; |
43 | 0 | std::vector<CScript> out_scripts; |
44 | 0 | (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache); |
45 | 0 | (void)desc.ExpandPrivate(0, sig_provider, sig_provider); |
46 | 0 | (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider); |
47 | | |
48 | | // If we could serialize to script we must be able to infer using the same provider. |
49 | 0 | if (!out_scripts.empty()) { |
50 | 0 | assert(InferDescriptor(out_scripts.back(), sig_provider)); |
51 | | |
52 | | // The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.) |
53 | 0 | const bool is_combo{!desc.IsSingleType()}; |
54 | 0 | assert(is_combo || desc.ScriptSize() == out_scripts.back().size()); |
55 | 0 | } |
56 | | |
57 | 0 | const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)}; |
58 | 0 | const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)}; |
59 | 0 | const auto max_elems{desc.MaxSatisfactionElems()}; |
60 | | // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo). |
61 | 0 | const bool is_nontop_or_nonsolvable{!*is_solvable || !desc.GetOutputType()}; |
62 | 0 | const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems}; |
63 | 0 | assert(is_input_size_info_set || is_nontop_or_nonsolvable); |
64 | 0 | } |
65 | | |
66 | | void initialize_descriptor_parse() |
67 | 0 | { |
68 | 0 | static ECC_Context ecc_context{}; |
69 | 0 | SelectParams(ChainType::MAIN); |
70 | 0 | } |
71 | | |
72 | | void initialize_mocked_descriptor_parse() |
73 | 0 | { |
74 | 0 | initialize_descriptor_parse(); |
75 | 0 | MOCKED_DESC_CONVERTER.Init(); |
76 | 0 | } |
77 | | |
78 | | FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse) |
79 | 0 | { |
80 | | // Key derivation is expensive. Deriving deep derivation paths take a lot of compute and we'd |
81 | | // rather spend time elsewhere in this target, like on the actual descriptor syntax. So rule |
82 | | // out strings which could correspond to a descriptor containing a too large derivation path. |
83 | 0 | if (HasDeepDerivPath(buffer)) return; |
84 | | |
85 | | // Some fragments can take a virtually unlimited number of sub-fragments (thresh, multi_a) but |
86 | | // may perform quadratic operations on them. Limit the number of sub-fragments per fragment. |
87 | 0 | if (HasTooManySubFrag(buffer)) return; |
88 | | |
89 | | // The script building logic performs quadratic copies in the number of nested wrappers. Limit |
90 | | // the number of nested wrappers per fragment. |
91 | 0 | if (HasTooManyWrappers(buffer)) return; |
92 | | |
93 | 0 | const std::string mocked_descriptor{buffer.begin(), buffer.end()}; |
94 | 0 | if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) { |
95 | 0 | FlatSigningProvider signing_provider; |
96 | 0 | std::string error; |
97 | 0 | const auto desc = Parse(*descriptor, signing_provider, error); |
98 | 0 | std::optional<bool> is_ranged; |
99 | 0 | std::optional<bool> is_solvable; |
100 | 0 | for (const auto& d : desc) { |
101 | 0 | assert(d); |
102 | 0 | TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable); |
103 | 0 | } |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | | FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) |
108 | 0 | { |
109 | | // See comments above for rationales. |
110 | 0 | if (HasDeepDerivPath(buffer)) return; |
111 | 0 | if (HasTooManySubFrag(buffer)) return; |
112 | 0 | if (HasTooManyWrappers(buffer)) return; |
113 | | |
114 | 0 | const std::string descriptor(buffer.begin(), buffer.end()); |
115 | 0 | FlatSigningProvider signing_provider; |
116 | 0 | std::string error; |
117 | 0 | for (const bool require_checksum : {true, false}) { |
118 | 0 | const auto desc = Parse(descriptor, signing_provider, error, require_checksum); |
119 | 0 | std::optional<bool> is_ranged; |
120 | 0 | std::optional<bool> is_solvable; |
121 | 0 | for (const auto& d : desc) { |
122 | 0 | assert(d); |
123 | 0 | TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable); |
124 | 0 | } |
125 | 0 | } |
126 | 0 | } |