/ src / test / fuzz / util / descriptor.cpp
descriptor.cpp
 1  // Copyright (c) 2023-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 <test/fuzz/util/descriptor.h>
 6  
 7  void MockedDescriptorConverter::Init() {
 8      // The data to use as a private key or a seed for an xprv.
 9      std::array<std::byte, 32> key_data{std::byte{1}};
10      // Generate keys of all kinds and store them in the keys array.
11      for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
12          key_data[31] = std::byte(i);
13  
14          // If this is a "raw" key, generate a normal privkey. Otherwise generate
15          // an extended one.
16          if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
17              CKey privkey;
18              privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
19              if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
20                  CPubKey pubkey{privkey.GetPubKey()};
21                  keys_str[i] = HexStr(pubkey);
22              } else if (IdIsXOnlyPubKey(i)) {
23                  const XOnlyPubKey pubkey{privkey.GetPubKey()};
24                  keys_str[i] = HexStr(pubkey);
25              } else {
26                  keys_str[i] = EncodeSecret(privkey);
27              }
28          } else {
29              CExtKey ext_privkey;
30              ext_privkey.SetSeed(key_data);
31              if (IdIsXprv(i)) {
32                  keys_str[i] = EncodeExtKey(ext_privkey);
33              } else {
34                  const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
35                  keys_str[i] = EncodeExtPubKey(ext_pubkey);
36              }
37          }
38      }
39  }
40  
41  std::optional<uint8_t> MockedDescriptorConverter::IdxFromHex(std::string_view hex_characters) const {
42      if (hex_characters.size() != 2) return {};
43      auto idx = ParseHex(hex_characters);
44      if (idx.size() != 1) return {};
45      return idx[0];
46  }
47  
48  std::optional<std::string> MockedDescriptorConverter::GetDescriptor(std::string_view mocked_desc) const {
49      // The smallest fragment would be "pk(%00)"
50      if (mocked_desc.size() < 7) return {};
51  
52      // The actual descriptor string to be returned.
53      std::string desc;
54      desc.reserve(mocked_desc.size());
55  
56      // Replace all occurrences of '%' followed by two hex characters with the corresponding key.
57      for (size_t i = 0; i < mocked_desc.size();) {
58          if (mocked_desc[i] == '%') {
59              if (i + 3 >= mocked_desc.size()) return {};
60              if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
61                  desc += keys_str[*idx];
62                  i += 3;
63              } else {
64                  return {};
65              }
66          } else {
67              desc += mocked_desc[i++];
68          }
69      }
70  
71      return desc;
72  }
73  
74  bool HasDeepDerivPath(const FuzzBufferType& buff, const int max_depth)
75  {
76      auto depth{0};
77      for (const auto& ch: buff) {
78          if (ch == ',') {
79              // A comma is always present between two key expressions, so we use that as a delimiter.
80              depth = 0;
81          } else if (ch == '/') {
82              if (++depth > max_depth) return true;
83          }
84      }
85      return false;
86  }