3
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
4
5
5
#include < chainparams.h>
6
+ #include < key_io.h>
6
7
#include < pubkey.h>
7
8
#include < script/descriptor.h>
8
9
#include < test/fuzz/fuzz.h>
9
10
#include < util/chaintype.h>
10
11
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
+
11
110
/* * Test a successfully parsed descriptor. */
12
111
static void TestDescriptor (const Descriptor& desc)
13
112
{
@@ -22,6 +121,23 @@ void initialize_descriptor_parse()
22
121
SelectParams (ChainType::MAIN);
23
122
}
24
123
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
+
25
141
FUZZ_TARGET (descriptor_parse, .init = initialize_descriptor_parse)
26
142
{
27
143
const std::string descriptor (buffer.begin (), buffer.end ());
0 commit comments