Bitcoin Core  24.99.0
P2P Digital Currency
miniscript.cpp
Go to the documentation of this file.
1 // Copyright (c) 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 <core_io.h>
6 #include <hash.h>
7 #include <key.h>
8 #include <script/miniscript.h>
9 #include <script/script.h>
11 #include <test/fuzz/fuzz.h>
12 #include <test/fuzz/util.h>
13 #include <util/strencodings.h>
14 
15 namespace {
16 
18 struct TestData {
19  typedef CPubKey Key;
20 
21  // Precomputed public keys.
22  std::vector<Key> dummy_keys;
23  std::map<Key, int> dummy_key_idx_map;
24  std::map<CKeyID, Key> dummy_keys_map;
25 
27  void Init() {
28  unsigned char keydata[32] = {1};
29  for (size_t i = 0; i < 256; i++) {
30  keydata[31] = i;
31  CKey privkey;
32  privkey.Set(keydata, keydata + 32, true);
33  const Key pubkey = privkey.GetPubKey();
34 
35  dummy_keys.push_back(pubkey);
36  dummy_key_idx_map.emplace(pubkey, i);
37  dummy_keys_map.insert({pubkey.GetID(), pubkey});
38  }
39  }
40 } TEST_DATA;
41 
47 struct ParserContext {
48  typedef CPubKey Key;
49 
50  bool KeyCompare(const Key& a, const Key& b) const {
51  return a < b;
52  }
53 
54  std::optional<std::string> ToString(const Key& key) const
55  {
56  auto it = TEST_DATA.dummy_key_idx_map.find(key);
57  if (it == TEST_DATA.dummy_key_idx_map.end()) return {};
58  uint8_t idx = it->second;
59  return HexStr(Span{&idx, 1});
60  }
61 
62  template<typename I>
63  std::optional<Key> FromString(I first, I last) const {
64  if (last - first != 2) return {};
65  auto idx = ParseHex(std::string(first, last));
66  if (idx.size() != 1) return {};
67  return TEST_DATA.dummy_keys[idx[0]];
68  }
69 
70  template<typename I>
71  std::optional<Key> FromPKBytes(I first, I last) const {
72  Key key;
73  key.Set(first, last);
74  if (!key.IsValid()) return {};
75  return key;
76  }
77 
78  template<typename I>
79  std::optional<Key> FromPKHBytes(I first, I last) const {
80  assert(last - first == 20);
81  CKeyID keyid;
82  std::copy(first, last, keyid.begin());
83  const auto it = TEST_DATA.dummy_keys_map.find(keyid);
84  if (it == TEST_DATA.dummy_keys_map.end()) return {};
85  return it->second;
86  }
87 } PARSER_CTX;
88 
90 struct ScriptParserContext {
92  struct Key {
93  bool is_hash;
94  std::vector<unsigned char> data;
95  };
96 
97  bool KeyCompare(const Key& a, const Key& b) const {
98  return a.data < b.data;
99  }
100 
101  const std::vector<unsigned char>& ToPKBytes(const Key& key) const
102  {
103  assert(!key.is_hash);
104  return key.data;
105  }
106 
107  const std::vector<unsigned char> ToPKHBytes(const Key& key) const
108  {
109  if (key.is_hash) return key.data;
110  const auto h = Hash160(key.data);
111  return {h.begin(), h.end()};
112  }
113 
114  template<typename I>
115  std::optional<Key> FromPKBytes(I first, I last) const
116  {
117  Key key;
118  key.data.assign(first, last);
119  key.is_hash = false;
120  return key;
121  }
122 
123  template<typename I>
124  std::optional<Key> FromPKHBytes(I first, I last) const
125  {
126  Key key;
127  key.data.assign(first, last);
128  key.is_hash = true;
129  return key;
130  }
131 } SCRIPT_PARSER_CONTEXT;
132 
133 } // namespace
134 
135 void FuzzInit()
136 {
137  ECC_Start();
138  TEST_DATA.Init();
139 }
140 
141 /* Fuzz tests that test parsing from a string, and roundtripping via string. */
142 FUZZ_TARGET_INIT(miniscript_string, FuzzInit)
143 {
144  FuzzedDataProvider provider(buffer.data(), buffer.size());
145  auto str = provider.ConsumeRemainingBytesAsString();
146  auto parsed = miniscript::FromString(str, PARSER_CTX);
147  if (!parsed) return;
148 
149  const auto str2 = parsed->ToString(PARSER_CTX);
150  assert(str2);
151  auto parsed2 = miniscript::FromString(*str2, PARSER_CTX);
152  assert(parsed2);
153  assert(*parsed == *parsed2);
154 }
155 
156 /* Fuzz tests that test parsing from a script, and roundtripping via script. */
157 FUZZ_TARGET(miniscript_script)
158 {
159  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
160  const std::optional<CScript> script = ConsumeDeserializable<CScript>(fuzzed_data_provider);
161  if (!script) return;
162 
163  const auto ms = miniscript::FromScript(*script, SCRIPT_PARSER_CONTEXT);
164  if (!ms) return;
165 
166  assert(ms->ToScript(SCRIPT_PARSER_CONTEXT) == *script);
167 }
ECC_Start()
Definition: key.cpp:392
An encapsulated private key.
Definition: key.h:27
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:187
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition: key.h:73
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:24
An encapsulated public key.
Definition: pubkey.h:34
std::string ConsumeRemainingBytesAsString()
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:97
unsigned char * begin()
Definition: uint256.h:61
uint160 Hash160(const T1 &in1)
Compute the 160-bit hash an object.
Definition: hash.h:91
NodeRef< typename Ctx::Key > FromScript(const CScript &script, const Ctx &ctx)
Definition: miniscript.h:1832
NodeRef< typename Ctx::Key > FromString(const std::string &str, const Ctx &ctx)
Definition: miniscript.h:1827
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:109
FUZZ_TARGET(miniscript_script)
Definition: miniscript.cpp:157
void FuzzInit()
Definition: miniscript.cpp:135
FUZZ_TARGET_INIT(miniscript_string, FuzzInit)
Definition: miniscript.cpp:142
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::vector< Byte > ParseHex(std::string_view str)
Parse the hex string into bytes (uint8_t or std::byte).
assert(!tx.IsCoinBase())