descriptor_parse.cpp raw

   1  // Copyright (c) 2009-2021 The Limenka 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  
  66  void initialize_descriptor_parse()
  67  {
  68      static ECC_Context ecc_context{};
  69      SelectParams(ChainType::MAIN);
  70  }
  71  
  72  void initialize_mocked_descriptor_parse()
  73  {
  74      initialize_descriptor_parse();
  75      MOCKED_DESC_CONVERTER.Init();
  76  }
  77  
  78  FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
  79  {
  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      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      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      if (HasTooManyWrappers(buffer)) return;
  92  
  93      const std::string mocked_descriptor{buffer.begin(), buffer.end()};
  94      if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
  95          FlatSigningProvider signing_provider;
  96          std::string error;
  97          const auto desc = Parse(*descriptor, signing_provider, error);
  98          std::optional<bool> is_ranged;
  99          std::optional<bool> is_solvable;
 100          for (const auto& d : desc) {
 101              assert(d);
 102              TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
 103          }
 104      }
 105  }
 106  
 107  FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
 108  {
 109      // See comments above for rationales.
 110      if (HasDeepDerivPath(buffer)) return;
 111      if (HasTooManySubFrag(buffer)) return;
 112      if (HasTooManyWrappers(buffer)) return;
 113  
 114      const std::string descriptor(buffer.begin(), buffer.end());
 115      FlatSigningProvider signing_provider;
 116      std::string error;
 117      for (const bool require_checksum : {true, false}) {
 118          const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
 119          std::optional<bool> is_ranged;
 120          std::optional<bool> is_solvable;
 121          for (const auto& d : desc) {
 122              assert(d);
 123              TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable);
 124          }
 125      }
 126  }
 127