1 // Copyright (c) 2009-2021 The Limenka 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 {
20 // Trivial helpers.
21 (void)desc.IsRange();
22 (void)desc.IsSingleType();
23 (void)desc.GetOutputType();
24 25 if (is_ranged.has_value()) {
26 assert(desc.IsRange() == *is_ranged);
27 } else {
28 is_ranged = desc.IsRange();
29 }
30 if (is_solvable.has_value()) {
31 assert(desc.IsSolvable() == *is_solvable);
32 } else {
33 is_solvable = desc.IsSolvable();
34 }
35 36 // Serialization to string representation.
37 (void)desc.ToString();
38 (void)desc.ToPrivateString(sig_provider, dummy);
39 (void)desc.ToNormalizedString(sig_provider, dummy);
40 41 // Serialization to Script.
42 DescriptorCache cache;
43 std::vector<CScript> out_scripts;
44 (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache);
45 (void)desc.ExpandPrivate(0, sig_provider, sig_provider);
46 (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 if (!out_scripts.empty()) {
50 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 const bool is_combo{!desc.IsSingleType()};
54 assert(is_combo || desc.ScriptSize() == out_scripts.back().size());
55 }
56 57 const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)};
58 const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)};
59 const auto max_elems{desc.MaxSatisfactionElems()};
60 // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
61 const bool is_nontop_or_nonsolvable{!*is_solvable || !desc.GetOutputType()};
62 const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
63 assert(is_input_size_info_set || is_nontop_or_nonsolvable);
64 }
65 66 void initialize_descriptor_parse()
67 {
68 static ECC_Context ecc_context{};
69 SelectParams(ChainType::MAIN);
70 }
71 72 void initialize_mocked_descriptor_parse()
73 {
74 initialize_descriptor_parse();
75 MOCKED_DESC_CONVERTER.Init();
76 }
77 78 FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
79 {
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 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 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 if (HasTooManyWrappers(buffer)) return;
92 93 const std::string mocked_descriptor{buffer.begin(), buffer.end()};
94 if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
95 FlatSigningProvider signing_provider;
96 std::string error;
97 const auto desc = Parse(*descriptor, signing_provider, error);
98 std::optional<bool> is_ranged;
99 std::optional<bool> is_solvable;
100 for (const auto& d : desc) {
101 assert(d);
102 TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
103 }
104 }
105 }
106 107 FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
108 {
109 // See comments above for rationales.
110 if (HasDeepDerivPath(buffer)) return;
111 if (HasTooManySubFrag(buffer)) return;
112 if (HasTooManyWrappers(buffer)) return;
113 114 const std::string descriptor(buffer.begin(), buffer.end());
115 FlatSigningProvider signing_provider;
116 std::string error;
117 for (const bool require_checksum : {true, false}) {
118 const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
119 std::optional<bool> is_ranged;
120 std::optional<bool> is_solvable;
121 for (const auto& d : desc) {
122 assert(d);
123 TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
124 }
125 }
126 }
127