From 8c0f8bf7bc3750fad648af1a548517a272114bca Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Wed, 13 Apr 2022 14:30:26 +0200 Subject: [PATCH] fuzz: add a Miniscript target for string representation roundtripping Co-authored-by: Pieter Wuille --- src/test/fuzz/miniscript.cpp | 89 ++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 0788f37e55..65346a44a9 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -14,6 +14,74 @@ namespace { +//! Some pre-computed data for more efficient string roundtrips. +struct TestData { + typedef CPubKey Key; + + // Precomputed public keys. + std::vector dummy_keys; + std::map dummy_key_idx_map; + std::map dummy_keys_map; + + //! Set the precomputed data. + void Init() { + unsigned char keydata[32] = {1}; + for (size_t i = 0; i < 256; i++) { + keydata[31] = i; + CKey privkey; + privkey.Set(keydata, keydata + 32, true); + const Key pubkey = privkey.GetPubKey(); + + dummy_keys.push_back(pubkey); + dummy_key_idx_map.emplace(pubkey, i); + dummy_keys_map.insert({pubkey.GetID(), pubkey}); + } + } +} TEST_DATA; + +/** + * Context to parse a Miniscript node to and from Script or text representation. + * Uses an integer (an index in the dummy keys array from the test data) as keys in order + * to focus on fuzzing the Miniscript nodes' test representation, not the key representation. + */ +struct ParserContext { + typedef CPubKey Key; + + std::optional ToString(const Key& key) const + { + auto it = TEST_DATA.dummy_key_idx_map.find(key); + if (it == TEST_DATA.dummy_key_idx_map.end()) return {}; + uint8_t idx = it->second; + return HexStr(Span{&idx, 1}); + } + + template + std::optional FromString(I first, I last) const { + if (last - first != 2) return {}; + auto idx = ParseHex(std::string(first, last)); + if (idx.size() != 1) return {}; + return TEST_DATA.dummy_keys[idx[0]]; + } + + template + std::optional FromPKBytes(I first, I last) const { + Key key; + key.Set(first, last); + if (!key.IsValid()) return {}; + return key; + } + + template + std::optional FromPKHBytes(I first, I last) const { + assert(last - first == 20); + CKeyID keyid; + std::copy(first, last, keyid.begin()); + const auto it = TEST_DATA.dummy_keys_map.find(keyid); + if (it == TEST_DATA.dummy_keys_map.end()) return {}; + return it->second; + } +} PARSER_CTX; + //! Context that implements naive conversion from/to script only, for roundtrip testing. struct ScriptParserContext { //! For Script roundtrip we never need the key from a key hash. @@ -54,6 +122,27 @@ struct ScriptParserContext { } } SCRIPT_PARSER_CONTEXT; +} // namespace + +void FuzzInit() +{ + ECC_Start(); + TEST_DATA.Init(); +} + +/* Fuzz tests that test parsing from a string, and roundtripping via string. */ +FUZZ_TARGET_INIT(miniscript_string, FuzzInit) +{ + FuzzedDataProvider provider(buffer.data(), buffer.size()); + auto str = provider.ConsumeRemainingBytesAsString(); + auto parsed = miniscript::FromString(str, PARSER_CTX); + if (!parsed) return; + + const auto str2 = parsed->ToString(PARSER_CTX); + assert(str2); + auto parsed2 = miniscript::FromString(*str2, PARSER_CTX); + assert(parsed2); + assert(*parsed == *parsed2); } /* Fuzz tests that test parsing from a script, and roundtripping via script. */