descriptor_parse.cpp
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) 19 { 20 // Trivial helpers. 21 (void)desc.IsRange(); 22 const bool is_solvable{desc.IsSolvable()}; 23 (void)desc.IsSingleType(); 24 (void)desc.GetOutputType(); 25 26 // Serialization to string representation. 27 (void)desc.ToString(); 28 (void)desc.ToPrivateString(sig_provider, dummy); 29 (void)desc.ToNormalizedString(sig_provider, dummy); 30 31 // Serialization to Script. 32 DescriptorCache cache; 33 std::vector<CScript> out_scripts; 34 (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache); 35 (void)desc.ExpandPrivate(0, sig_provider, sig_provider); 36 (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider); 37 38 // If we could serialize to script we must be able to infer using the same provider. 39 if (!out_scripts.empty()) { 40 assert(InferDescriptor(out_scripts.back(), sig_provider)); 41 42 // The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.) 43 const bool is_combo{!desc.IsSingleType()}; 44 assert(is_combo || desc.ScriptSize() == out_scripts.back().size()); 45 } 46 47 const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)}; 48 const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)}; 49 const auto max_elems{desc.MaxSatisfactionElems()}; 50 // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo). 51 const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()}; 52 const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems}; 53 assert(is_input_size_info_set || is_nontop_or_nonsolvable); 54 } 55 56 void initialize_descriptor_parse() 57 { 58 ECC_Start(); 59 SelectParams(ChainType::MAIN); 60 } 61 62 void initialize_mocked_descriptor_parse() 63 { 64 initialize_descriptor_parse(); 65 MOCKED_DESC_CONVERTER.Init(); 66 } 67 68 FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse) 69 { 70 // Key derivation is expensive. Deriving deep derivation paths take a lot of compute and we'd 71 // rather spend time elsewhere in this target, like on the actual descriptor syntax. So rule 72 // out strings which could correspond to a descriptor containing a too large derivation path. 73 if (HasDeepDerivPath(buffer)) return; 74 75 const std::string mocked_descriptor{buffer.begin(), buffer.end()}; 76 if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) { 77 FlatSigningProvider signing_provider; 78 std::string error; 79 const auto desc = Parse(*descriptor, signing_provider, error); 80 if (desc) TestDescriptor(*desc, signing_provider, error); 81 } 82 } 83 84 FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) 85 { 86 // See comment above for rationale. 87 if (HasDeepDerivPath(buffer)) return; 88 89 const std::string descriptor(buffer.begin(), buffer.end()); 90 FlatSigningProvider signing_provider; 91 std::string error; 92 for (const bool require_checksum : {true, false}) { 93 const auto desc = Parse(descriptor, signing_provider, error, require_checksum); 94 if (desc) TestDescriptor(*desc, signing_provider, error); 95 } 96 }