/root/bitcoin/src/test/fuzz/descriptor_parse.cpp
| Line | Count | Source | 
| 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 | } |