/ src / test / fuzz / descriptor_parse.cpp
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  }