/ src / test / fuzz / descriptor_parse.cpp
descriptor_parse.cpp
  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(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      auto max_key_expr = desc.GetMaxKeyExpr();
 66      auto key_count = desc.GetKeyCount();
 67      assert((max_key_expr == 0 && key_count == 0) || max_key_expr + 1 == key_count);
 68  }
 69  
 70  void initialize_descriptor_parse()
 71  {
 72      static ECC_Context ecc_context{};
 73      SelectParams(ChainType::MAIN);
 74  }
 75  
 76  void initialize_mocked_descriptor_parse()
 77  {
 78      initialize_descriptor_parse();
 79      MOCKED_DESC_CONVERTER.Init();
 80  }
 81  
 82  FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
 83  {
 84      const std::string mocked_descriptor{buffer.begin(), buffer.end()};
 85      if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
 86          if (IsTooExpensive(MakeUCharSpan(*descriptor))) return;
 87          FlatSigningProvider signing_provider;
 88          std::string error;
 89          const auto desc = Parse(*descriptor, signing_provider, error);
 90          std::optional<bool> is_ranged;
 91          std::optional<bool> is_solvable;
 92          for (const auto& d : desc) {
 93              assert(d);
 94              TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
 95          }
 96      }
 97  }
 98  
 99  FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
100  {
101      if (IsTooExpensive(buffer)) return;
102  
103      const std::string descriptor(buffer.begin(), buffer.end());
104      FlatSigningProvider signing_provider;
105      std::string error;
106      for (const bool require_checksum : {true, false}) {
107          const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
108          std::optional<bool> is_ranged;
109          std::optional<bool> is_solvable;
110          for (const auto& d : desc) {
111              assert(d);
112              TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
113          }
114      }
115  }