Bitcoin Core  26.1.0
P2P Digital Currency
descriptor_parse.cpp
Go to the documentation of this file.
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 <util/chaintype.h>
11 #include <util/strencodings.h>
12 
14 static constexpr uint8_t KEY_TYPES_COUNT{6};
16 static constexpr size_t TOTAL_KEYS_GENERATED{std::numeric_limits<uint8_t>::max() + 1};
17 
26  std::array<std::string, TOTAL_KEYS_GENERATED> keys_str;
27 
28 public:
29  // We derive the type of key to generate from the 1-byte id parsed from hex.
30  bool IdIsCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0; }
31  bool IdIsUnCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1; }
32  bool IdIsXOnlyPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2; }
33  bool IdIsConstPrivKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 3; }
34  bool IdIsXpub(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 4; }
35  bool IdIsXprv(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5; }
36 
38  void Init() {
39  // The data to use as a private key or a seed for an xprv.
40  std::array<std::byte, 32> key_data{std::byte{1}};
41  // Generate keys of all kinds and store them in the keys array.
42  for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
43  key_data[31] = std::byte(i);
44 
45  // If this is a "raw" key, generate a normal privkey. Otherwise generate
46  // an extended one.
48  CKey privkey;
49  privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
50  if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
51  CPubKey pubkey{privkey.GetPubKey()};
52  keys_str[i] = HexStr(pubkey);
53  } else if (IdIsXOnlyPubKey(i)) {
54  const XOnlyPubKey pubkey{privkey.GetPubKey()};
55  keys_str[i] = HexStr(pubkey);
56  } else {
57  keys_str[i] = EncodeSecret(privkey);
58  }
59  } else {
60  CExtKey ext_privkey;
61  ext_privkey.SetSeed(key_data);
62  if (IdIsXprv(i)) {
63  keys_str[i] = EncodeExtKey(ext_privkey);
64  } else {
65  const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
66  keys_str[i] = EncodeExtPubKey(ext_pubkey);
67  }
68  }
69  }
70  }
71 
73  std::optional<uint8_t> IdxFromHex(std::string_view hex_characters) const {
74  if (hex_characters.size() != 2) return {};
75  auto idx = ParseHex(hex_characters);
76  if (idx.size() != 1) return {};
77  return idx[0];
78  }
79 
81  std::optional<std::string> GetDescriptor(std::string_view mocked_desc) const {
82  // The smallest fragment would be "pk(%00)"
83  if (mocked_desc.size() < 7) return {};
84 
85  // The actual descriptor string to be returned.
86  std::string desc;
87  desc.reserve(mocked_desc.size());
88 
89  // Replace all occurrences of '%' followed by two hex characters with the corresponding key.
90  for (size_t i = 0; i < mocked_desc.size();) {
91  if (mocked_desc[i] == '%') {
92  if (i + 3 >= mocked_desc.size()) return {};
93  if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
94  desc += keys_str[*idx];
95  i += 3;
96  } else {
97  return {};
98  }
99  } else {
100  desc += mocked_desc[i++];
101  }
102  }
103 
104  return desc;
105  }
106 };
107 
110 
112 static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy)
113 {
114  // Trivial helpers.
115  (void)desc.IsRange();
116  const bool is_solvable{desc.IsSolvable()};
117  (void)desc.IsSingleType();
118  (void)desc.GetOutputType();
119 
120  // Serialization to string representation.
121  (void)desc.ToString();
122  (void)desc.ToPrivateString(sig_provider, dummy);
123  (void)desc.ToNormalizedString(sig_provider, dummy);
124 
125  // Serialization to Script.
126  DescriptorCache cache;
127  std::vector<CScript> out_scripts;
128  (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache);
129  (void)desc.ExpandPrivate(0, sig_provider, sig_provider);
130  (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider);
131 
132  // If we could serialize to script we must be able to infer using the same provider.
133  if (!out_scripts.empty()) {
134  assert(InferDescriptor(out_scripts.back(), sig_provider));
135 
136  // The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.)
137  const bool is_combo{!desc.IsSingleType()};
138  assert(is_combo || desc.ScriptSize() == out_scripts.back().size());
139  }
140 
141  const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)};
142  const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)};
143  const auto max_elems{desc.MaxSatisfactionElems()};
144  // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
145  const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()};
146  const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
147  assert(is_input_size_info_set || is_nontop_or_nonsolvable);
148 }
149 
151 {
152  ECC_Start();
154 }
155 
157 {
160 }
161 
163 {
164  const std::string mocked_descriptor{buffer.begin(), buffer.end()};
165  if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
166  FlatSigningProvider signing_provider;
167  std::string error;
168  const auto desc = Parse(*descriptor, signing_provider, error);
169  if (desc) TestDescriptor(*desc, signing_provider, error);
170  }
171 }
172 
174 {
175  const std::string descriptor(buffer.begin(), buffer.end());
176  FlatSigningProvider signing_provider;
177  std::string error;
178  for (const bool require_checksum : {true, false}) {
179  const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
180  if (desc) TestDescriptor(*desc, signing_provider, error);
181  }
182 }
static constexpr size_t TOTAL_KEYS_GENERATED
How many keys we&#39;ll generate in total.
static UniValue Parse(std::string_view raw)
Parse string to UniValue or throw runtime_error if string contains invalid JSON.
Definition: client.cpp:309
virtual std::optional< int64_t > MaxSatisfactionWeight(bool use_max_sig) const =0
Get the maximum size of a satisfaction for this descriptor, in weight units.
virtual bool IsSingleType() const =0
Whether this descriptor will return one scriptPubKey or multiple (aka is or is not combo) ...
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:65
assert(!tx.IsCoinBase())
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:188
std::array< std::string, TOTAL_KEYS_GENERATED > keys_str
256 keys of various types.
static constexpr uint8_t KEY_TYPES_COUNT
Types are raw (un)compressed pubkeys, raw xonly pubkeys, raw privkeys (WIF), xpubs, xprvs.
bool IdIsXpub(uint8_t idx) const
Definition: key.h:208
bool IdIsXprv(uint8_t idx) const
std::optional< uint8_t > IdxFromHex(std::string_view hex_characters) const
Parse an id in the keys vectors from a 2-characters hex string.
bool IdIsCompPubKey(uint8_t idx) const
virtual bool IsSolvable() const =0
Whether this descriptor has all information about signing ignoring lack of private keys...
bool IdIsXOnlyPubKey(uint8_t idx) const
virtual bool ExpandFromCache(int pos, const DescriptorCache &read_cache, std::vector< CScript > &output_scripts, FlatSigningProvider &out) const =0
Expand a descriptor at a specified position using cached expansion data.
std::optional< std::string > GetDescriptor(std::string_view mocked_desc) const
Get an actual descriptor string from a descriptor string whose keys were mocked.
FUZZ_TARGET(mocked_descriptor_parse,.init=initialize_mocked_descriptor_parse)
virtual void ExpandPrivate(int pos, const SigningProvider &provider, FlatSigningProvider &out) const =0
Expand the private key for a descriptor at a specified position, if possible.
static void TestDescriptor(const Descriptor &desc, FlatSigningProvider &sig_provider, std::string &dummy)
Test a successfully parsed descriptor.
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
ECC_Start()
Definition: key.cpp:429
An encapsulated public key.
Definition: pubkey.h:33
virtual bool ToNormalizedString(const SigningProvider &provider, std::string &out, const DescriptorCache *cache=nullptr) const =0
Convert the descriptor to a normalized string.
void Init()
When initializing the target, populate the list of keys.
void initialize_descriptor_parse()
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition: key.h:99
virtual std::optional< int64_t > MaxSatisfactionElems() const =0
Get the maximum size number of stack elements for satisfying this descriptor.
void initialize_mocked_descriptor_parse()
virtual std::optional< int64_t > ScriptSize() const =0
Get the size of the scriptPubKey for this descriptor.
virtual bool Expand(int pos, const SigningProvider &provider, std::vector< CScript > &output_scripts, FlatSigningProvider &out, DescriptorCache *write_cache=nullptr) const =0
Expand a descriptor at a specified position.
std::string EncodeExtPubKey(const CExtPubKey &key)
Definition: key_io.cpp:253
void SetSeed(Span< const std::byte > seed)
Definition: key.cpp:381
std::unique_ptr< Descriptor > InferDescriptor(const CScript &script, const SigningProvider &provider)
Find a descriptor for the specified script, using information from provider where possible...
Converts a mocked descriptor string to a valid one.
CExtPubKey Neuter() const
Definition: key.cpp:393
bool error(const char *fmt, const Args &... args)
Definition: logging.h:262
Cache for single descriptor&#39;s derived extended pubkeys.
Definition: descriptor.h:19
unsigned char * UCharCast(char *c)
Definition: span.h:270
MockedDescriptorConverter MOCKED_DESC_CONVERTER
The converter of mocked descriptors, needs to be initialized when the target is.
An encapsulated private key.
Definition: key.h:32
virtual bool IsRange() const =0
Whether the expansion of this descriptor depends on the position.
virtual std::optional< OutputType > GetOutputType() const =0
bool IdIsConstPrivKey(uint8_t idx) const
virtual std::string ToString(bool compat_format=false) const =0
Convert the descriptor back to a string, undoing parsing.
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:227
virtual bool ToPrivateString(const SigningProvider &provider, std::string &out) const =0
Convert the descriptor to a private string.
std::string EncodeExtKey(const CExtKey &key)
Definition: key_io.cpp:276
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given chain type.
Interface for parsed descriptor objects.
Definition: descriptor.h:98
bool IdIsUnCompPubKey(uint8_t idx) const