33// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44
55#include < chainparams.h>
6+ #include < key_io.h>
67#include < pubkey.h>
78#include < script/descriptor.h>
89#include < test/fuzz/fuzz.h>
910#include < util/chaintype.h>
1011
12+ // ! Types are raw (un)compressed pubkeys, raw xonly pubkeys, raw privkeys (WIF), xpubs, xprvs.
13+ static constexpr uint8_t KEY_TYPES_COUNT{6 };
14+ // ! How many keys we'll generate in total.
15+ static constexpr size_t TOTAL_KEYS_GENERATED{std::numeric_limits<uint8_t >::max () + 1 };
16+
17+ /* *
18+ * Converts a mocked descriptor string to a valid one. Every key in a mocked descriptor key is
19+ * represented by 2 hex characters preceded by the '%' character. We parse the two hex characters
20+ * as an index in a list of pre-generated keys. This list contains keys of the various types
21+ * accepted in descriptor keys expressions.
22+ */
23+ class MockedDescriptorConverter {
24+ // ! 256 keys of various types.
25+ std::array<std::string, TOTAL_KEYS_GENERATED> keys_str;
26+
27+ public:
28+ // We derive the type of key to generate from the 1-byte id parsed from hex.
29+ bool IdIsCompPubKey (uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0 ; }
30+ bool IdIsUnCompPubKey (uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1 ; }
31+ bool IdIsXOnlyPubKey (uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2 ; }
32+ bool IdIsConstPrivKey (uint8_t idx) const { return idx % KEY_TYPES_COUNT == 3 ; }
33+ bool IdIsXpub (uint8_t idx) const { return idx % KEY_TYPES_COUNT == 4 ; }
34+ bool IdIsXprv (uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5 ; }
35+
36+ // ! When initializing the target, populate the list of keys.
37+ void Init () {
38+ // The data to use as a private key or a seed for an xprv.
39+ std::array<std::byte, 32 > key_data{std::byte{1 }};
40+ // Generate keys of all kinds and store them in the keys array.
41+ for (size_t i{0 }; i < TOTAL_KEYS_GENERATED; i++) {
42+ key_data[31 ] = std::byte (i);
43+
44+ // If this is a "raw" key, generate a normal privkey. Otherwise generate
45+ // an extended one.
46+ if (IdIsCompPubKey (i) || IdIsUnCompPubKey (i) || IdIsXOnlyPubKey (i) || IdIsConstPrivKey (i)) {
47+ CKey privkey;
48+ privkey.Set (UCharCast (key_data.begin ()), UCharCast (key_data.end ()), !IdIsUnCompPubKey (i));
49+ if (IdIsCompPubKey (i) || IdIsUnCompPubKey (i)) {
50+ CPubKey pubkey{privkey.GetPubKey ()};
51+ keys_str[i] = HexStr (pubkey);
52+ } else if (IdIsXOnlyPubKey (i)) {
53+ const XOnlyPubKey pubkey{privkey.GetPubKey ()};
54+ keys_str[i] = HexStr (pubkey);
55+ } else {
56+ keys_str[i] = EncodeSecret (privkey);
57+ }
58+ } else {
59+ CExtKey ext_privkey;
60+ ext_privkey.SetSeed (key_data);
61+ if (IdIsXprv (i)) {
62+ keys_str[i] = EncodeExtKey (ext_privkey);
63+ } else {
64+ const CExtPubKey ext_pubkey{ext_privkey.Neuter ()};
65+ keys_str[i] = EncodeExtPubKey (ext_pubkey);
66+ }
67+ }
68+ }
69+ }
70+
71+ // ! Parse an id in the keys vectors from a 2-characters hex string.
72+ std::optional<uint8_t > IdxFromHex (std::string_view hex_characters) const {
73+ if (hex_characters.size () != 2 ) return {};
74+ auto idx = ParseHex (hex_characters);
75+ if (idx.size () != 1 ) return {};
76+ return idx[0 ];
77+ }
78+
79+ // ! Get an actual descriptor string from a descriptor string whose keys were mocked.
80+ std::optional<std::string> GetDescriptor (std::string_view mocked_desc) const {
81+ // The smallest fragment would be "pk(%00)"
82+ if (mocked_desc.size () < 7 ) return {};
83+
84+ // The actual descriptor string to be returned.
85+ std::string desc;
86+ desc.reserve (mocked_desc.size ());
87+
88+ // Replace all occurences of '%' followed by two hex characters with the corresponding key.
89+ for (size_t i = 0 ; i < mocked_desc.size ();) {
90+ if (mocked_desc[i] == ' %' ) {
91+ if (i + 3 >= mocked_desc.size ()) return {};
92+ if (const auto idx = IdxFromHex (mocked_desc.substr (i + 1 , 2 ))) {
93+ desc += keys_str[*idx];
94+ i += 3 ;
95+ } else {
96+ return {};
97+ }
98+ } else {
99+ desc += mocked_desc[i++];
100+ }
101+ }
102+
103+ return desc;
104+ }
105+ };
106+
107+ // ! The converter of mocked descriptors, needs to be initialized when the target is.
108+ MockedDescriptorConverter MOCKED_DESC_CONVERTER;
109+
11110/* * Test a successfully parsed descriptor. */
12111static void TestDescriptor (const Descriptor& desc)
13112{
@@ -22,6 +121,23 @@ void initialize_descriptor_parse()
22121 SelectParams (ChainType::MAIN);
23122}
24123
124+ void initialize_mocked_descriptor_parse ()
125+ {
126+ initialize_descriptor_parse ();
127+ MOCKED_DESC_CONVERTER.Init ();
128+ }
129+
130+ FUZZ_TARGET (mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
131+ {
132+ const std::string mocked_descriptor{buffer.begin (), buffer.end ()};
133+ if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor (mocked_descriptor)) {
134+ FlatSigningProvider signing_provider;
135+ std::string error;
136+ const auto desc = Parse (*descriptor, signing_provider, error);
137+ if (desc) TestDescriptor (*desc);
138+ }
139+ }
140+
25141FUZZ_TARGET (descriptor_parse, .init = initialize_descriptor_parse)
26142{
27143 const std::string descriptor (buffer.begin (), buffer.end ());
0 commit comments