Skip to content

Commit 8c0f8bf

Browse files
darosiorsipa
andcommitted
fuzz: add a Miniscript target for string representation roundtripping
Co-authored-by: Pieter Wuille <[email protected]>
1 parent be34d50 commit 8c0f8bf

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed

src/test/fuzz/miniscript.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,74 @@
1414

1515
namespace {
1616

17+
//! Some pre-computed data for more efficient string roundtrips.
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+
26+
//! Set the precomputed data.
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+
42+
/**
43+
* Context to parse a Miniscript node to and from Script or text representation.
44+
* Uses an integer (an index in the dummy keys array from the test data) as keys in order
45+
* to focus on fuzzing the Miniscript nodes' test representation, not the key representation.
46+
*/
47+
struct ParserContext {
48+
typedef CPubKey Key;
49+
50+
std::optional<std::string> ToString(const Key& key) const
51+
{
52+
auto it = TEST_DATA.dummy_key_idx_map.find(key);
53+
if (it == TEST_DATA.dummy_key_idx_map.end()) return {};
54+
uint8_t idx = it->second;
55+
return HexStr(Span{&idx, 1});
56+
}
57+
58+
template<typename I>
59+
std::optional<Key> FromString(I first, I last) const {
60+
if (last - first != 2) return {};
61+
auto idx = ParseHex(std::string(first, last));
62+
if (idx.size() != 1) return {};
63+
return TEST_DATA.dummy_keys[idx[0]];
64+
}
65+
66+
template<typename I>
67+
std::optional<Key> FromPKBytes(I first, I last) const {
68+
Key key;
69+
key.Set(first, last);
70+
if (!key.IsValid()) return {};
71+
return key;
72+
}
73+
74+
template<typename I>
75+
std::optional<Key> FromPKHBytes(I first, I last) const {
76+
assert(last - first == 20);
77+
CKeyID keyid;
78+
std::copy(first, last, keyid.begin());
79+
const auto it = TEST_DATA.dummy_keys_map.find(keyid);
80+
if (it == TEST_DATA.dummy_keys_map.end()) return {};
81+
return it->second;
82+
}
83+
} PARSER_CTX;
84+
1785
//! Context that implements naive conversion from/to script only, for roundtrip testing.
1886
struct ScriptParserContext {
1987
//! For Script roundtrip we never need the key from a key hash.
@@ -54,6 +122,27 @@ struct ScriptParserContext {
54122
}
55123
} SCRIPT_PARSER_CONTEXT;
56124

125+
} // namespace
126+
127+
void FuzzInit()
128+
{
129+
ECC_Start();
130+
TEST_DATA.Init();
131+
}
132+
133+
/* Fuzz tests that test parsing from a string, and roundtripping via string. */
134+
FUZZ_TARGET_INIT(miniscript_string, FuzzInit)
135+
{
136+
FuzzedDataProvider provider(buffer.data(), buffer.size());
137+
auto str = provider.ConsumeRemainingBytesAsString();
138+
auto parsed = miniscript::FromString(str, PARSER_CTX);
139+
if (!parsed) return;
140+
141+
const auto str2 = parsed->ToString(PARSER_CTX);
142+
assert(str2);
143+
auto parsed2 = miniscript::FromString(*str2, PARSER_CTX);
144+
assert(parsed2);
145+
assert(*parsed == *parsed2);
57146
}
58147

59148
/* Fuzz tests that test parsing from a script, and roundtripping via script. */

0 commit comments

Comments
 (0)