fuzz: move `MockedDescriptorConverter` to `fuzz/util`

This commit is contained in:
brunoerg 2023-10-04 16:28:06 -03:00
parent d752349029
commit 2e1833ca13
4 changed files with 123 additions and 95 deletions

View File

@ -11,6 +11,7 @@ TEST_FUZZ_H = \
test/fuzz/fuzz.h \
test/fuzz/FuzzedDataProvider.h \
test/fuzz/util.h \
test/fuzz/util/descriptor.h \
test/fuzz/util/mempool.h \
test/fuzz/util/net.h
@ -19,6 +20,7 @@ libtest_fuzz_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libtest_fuzz_a_SOURCES = \
test/fuzz/fuzz.cpp \
test/fuzz/util.cpp \
test/fuzz/util/descriptor.cpp \
test/fuzz/util/mempool.cpp \
test/fuzz/util/net.cpp \
$(TEST_FUZZ_H)

View File

@ -7,104 +7,10 @@
#include <pubkey.h>
#include <script/descriptor.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util/descriptor.h>
#include <util/chaintype.h>
#include <util/strencodings.h>
//! Types are raw (un)compressed pubkeys, raw xonly pubkeys, raw privkeys (WIF), xpubs, xprvs.
static constexpr uint8_t KEY_TYPES_COUNT{6};
//! How many keys we'll generate in total.
static constexpr size_t TOTAL_KEYS_GENERATED{std::numeric_limits<uint8_t>::max() + 1};
/**
* Converts a mocked descriptor string to a valid one. Every key in a mocked descriptor key is
* represented by 2 hex characters preceded by the '%' character. We parse the two hex characters
* as an index in a list of pre-generated keys. This list contains keys of the various types
* accepted in descriptor keys expressions.
*/
class MockedDescriptorConverter {
//! 256 keys of various types.
std::array<std::string, TOTAL_KEYS_GENERATED> keys_str;
public:
// We derive the type of key to generate from the 1-byte id parsed from hex.
bool IdIsCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0; }
bool IdIsUnCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1; }
bool IdIsXOnlyPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2; }
bool IdIsConstPrivKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 3; }
bool IdIsXpub(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 4; }
bool IdIsXprv(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5; }
//! When initializing the target, populate the list of keys.
void Init() {
// The data to use as a private key or a seed for an xprv.
std::array<std::byte, 32> key_data{std::byte{1}};
// Generate keys of all kinds and store them in the keys array.
for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
key_data[31] = std::byte(i);
// If this is a "raw" key, generate a normal privkey. Otherwise generate
// an extended one.
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
CKey privkey;
privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
CPubKey pubkey{privkey.GetPubKey()};
keys_str[i] = HexStr(pubkey);
} else if (IdIsXOnlyPubKey(i)) {
const XOnlyPubKey pubkey{privkey.GetPubKey()};
keys_str[i] = HexStr(pubkey);
} else {
keys_str[i] = EncodeSecret(privkey);
}
} else {
CExtKey ext_privkey;
ext_privkey.SetSeed(key_data);
if (IdIsXprv(i)) {
keys_str[i] = EncodeExtKey(ext_privkey);
} else {
const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
keys_str[i] = EncodeExtPubKey(ext_pubkey);
}
}
}
}
//! Parse an id in the keys vectors from a 2-characters hex string.
std::optional<uint8_t> IdxFromHex(std::string_view hex_characters) const {
if (hex_characters.size() != 2) return {};
auto idx = ParseHex(hex_characters);
if (idx.size() != 1) return {};
return idx[0];
}
//! Get an actual descriptor string from a descriptor string whose keys were mocked.
std::optional<std::string> GetDescriptor(std::string_view mocked_desc) const {
// The smallest fragment would be "pk(%00)"
if (mocked_desc.size() < 7) return {};
// The actual descriptor string to be returned.
std::string desc;
desc.reserve(mocked_desc.size());
// Replace all occurrences of '%' followed by two hex characters with the corresponding key.
for (size_t i = 0; i < mocked_desc.size();) {
if (mocked_desc[i] == '%') {
if (i + 3 >= mocked_desc.size()) return {};
if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
desc += keys_str[*idx];
i += 3;
} else {
return {};
}
} else {
desc += mocked_desc[i++];
}
}
return desc;
}
};
//! The converter of mocked descriptors, needs to be initialized when the target is.
MockedDescriptorConverter MOCKED_DESC_CONVERTER;

View File

@ -0,0 +1,72 @@
// Copyright (c) 2023-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/fuzz/util/descriptor.h>
void MockedDescriptorConverter::Init() {
// The data to use as a private key or a seed for an xprv.
std::array<std::byte, 32> key_data{std::byte{1}};
// Generate keys of all kinds and store them in the keys array.
for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
key_data[31] = std::byte(i);
// If this is a "raw" key, generate a normal privkey. Otherwise generate
// an extended one.
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
CKey privkey;
privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
CPubKey pubkey{privkey.GetPubKey()};
keys_str[i] = HexStr(pubkey);
} else if (IdIsXOnlyPubKey(i)) {
const XOnlyPubKey pubkey{privkey.GetPubKey()};
keys_str[i] = HexStr(pubkey);
} else {
keys_str[i] = EncodeSecret(privkey);
}
} else {
CExtKey ext_privkey;
ext_privkey.SetSeed(key_data);
if (IdIsXprv(i)) {
keys_str[i] = EncodeExtKey(ext_privkey);
} else {
const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
keys_str[i] = EncodeExtPubKey(ext_pubkey);
}
}
}
}
std::optional<uint8_t> MockedDescriptorConverter::IdxFromHex(std::string_view hex_characters) const {
if (hex_characters.size() != 2) return {};
auto idx = ParseHex(hex_characters);
if (idx.size() != 1) return {};
return idx[0];
}
std::optional<std::string> MockedDescriptorConverter::GetDescriptor(std::string_view mocked_desc) const {
// The smallest fragment would be "pk(%00)"
if (mocked_desc.size() < 7) return {};
// The actual descriptor string to be returned.
std::string desc;
desc.reserve(mocked_desc.size());
// Replace all occurrences of '%' followed by two hex characters with the corresponding key.
for (size_t i = 0; i < mocked_desc.size();) {
if (mocked_desc[i] == '%') {
if (i + 3 >= mocked_desc.size()) return {};
if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
desc += keys_str[*idx];
i += 3;
} else {
return {};
}
} else {
desc += mocked_desc[i++];
}
}
return desc;
}

View File

@ -0,0 +1,48 @@
// Copyright (c) 2023-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_TEST_FUZZ_UTIL_DESCRIPTOR_H
#define BITCOIN_TEST_FUZZ_UTIL_DESCRIPTOR_H
#include <key_io.h>
#include <util/strencodings.h>
#include <script/descriptor.h>
#include <functional>
/**
* Converts a mocked descriptor string to a valid one. Every key in a mocked descriptor key is
* represented by 2 hex characters preceded by the '%' character. We parse the two hex characters
* as an index in a list of pre-generated keys. This list contains keys of the various types
* accepted in descriptor keys expressions.
*/
class MockedDescriptorConverter {
private:
//! Types are raw (un)compressed pubkeys, raw xonly pubkeys, raw privkeys (WIF), xpubs, xprvs.
static constexpr uint8_t KEY_TYPES_COUNT{6};
//! How many keys we'll generate in total.
static constexpr size_t TOTAL_KEYS_GENERATED{std::numeric_limits<uint8_t>::max() + 1};
//! 256 keys of various types.
std::array<std::string, TOTAL_KEYS_GENERATED> keys_str;
public:
// We derive the type of key to generate from the 1-byte id parsed from hex.
bool IdIsCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0; }
bool IdIsUnCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1; }
bool IdIsXOnlyPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2; }
bool IdIsConstPrivKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 3; }
bool IdIsXpub(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 4; }
bool IdIsXprv(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5; }
//! When initializing the target, populate the list of keys.
void Init();
//! Parse an id in the keys vectors from a 2-characters hex string.
std::optional<uint8_t> IdxFromHex(std::string_view hex_characters) const;
//! Get an actual descriptor string from a descriptor string whose keys were mocked.
std::optional<std::string> GetDescriptor(std::string_view mocked_desc) const;
};
#endif // BITCOIN_TEST_FUZZ_UTIL_DESCRIPTOR_H