descriptor_parse.cpp raw
1 // Copyright (c) 2009-present 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 {
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(false)};
59 // Whether an estimate is available must not depend on the signature-size
60 // assumption, and assuming non-max-size signatures must never increase it.
61 assert(max_sat_maxsig.has_value() == max_sat_nonmaxsig.has_value());
62 assert(max_sat_nonmaxsig <= max_sat_maxsig);
63 const auto max_elems{desc.MaxSatisfactionElems()};
64 // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
65 const bool is_nontop_or_nonsolvable{!*is_solvable || !desc.GetOutputType()};
66 const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
67 assert(is_input_size_info_set || is_nontop_or_nonsolvable);
68
69 auto max_key_expr = desc.GetMaxKeyExpr();
70 auto key_count = desc.GetKeyCount();
71 assert((max_key_expr == 0 && key_count == 0) || max_key_expr + 1 == key_count);
72 }
73
74 void initialize_descriptor_parse()
75 {
76 static ECC_Context ecc_context{};
77 SelectParams(ChainType::MAIN);
78 }
79
80 void initialize_mocked_descriptor_parse()
81 {
82 initialize_descriptor_parse();
83 MOCKED_DESC_CONVERTER.Init();
84 }
85
86 FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
87 {
88 const std::string mocked_descriptor{buffer.begin(), buffer.end()};
89 if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
90 if (IsTooExpensive(MakeUCharSpan(*descriptor))) return;
91 FlatSigningProvider signing_provider;
92 std::string error;
93 const auto desc = Parse(*descriptor, signing_provider, error);
94 std::optional<bool> is_ranged;
95 std::optional<bool> is_solvable;
96 for (const auto& d : desc) {
97 assert(d);
98 TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
99 }
100 }
101 }
102
103 FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
104 {
105 if (IsTooExpensive(buffer)) return;
106
107 const std::string descriptor(buffer.begin(), buffer.end());
108 FlatSigningProvider signing_provider;
109 std::string error;
110 for (const bool require_checksum : {true, false}) {
111 const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
112 std::optional<bool> is_ranged;
113 std::optional<bool> is_solvable;
114 for (const auto& d : desc) {
115 assert(d);
116 TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
117 }
118 }
119 }
120