diff options
author | fanquake <fanquake@gmail.com> | 2023-11-23 17:31:32 +0000 |
---|---|---|
committer | fanquake <fanquake@gmail.com> | 2023-11-23 17:34:03 +0000 |
commit | f4073c5395ecc1a1fc85147a4bd7f469327c53de (patch) | |
tree | 655b8448f02fdd63c783da3e9faf27e72d971716 /src/test | |
parent | ddc4b9850af363fbd446ca0540497c69cd91abe5 (diff) | |
parent | 47e5c9994c087d1826ccc0d539e916845b5648fb (diff) |
Merge bitcoin/bitcoin#28578: fuzz: add target for `DescriptorScriptPubKeyMan`
47e5c9994c087d1826ccc0d539e916845b5648fb fuzz: add target for `DescriptorScriptPubKeyMan` (brunoerg)
641dddf01812407d163520def83f5975413691e4 fuzz: create ConsumeCoins (brunoerg)
2e1833ca1341ab4dc92508a59181aa6c7c38db88 fuzz: move `MockedDescriptorConverter` to `fuzz/util` (brunoerg)
Pull request description:
This PR adds fuzz target for `DescriptorScriptPubKeyMan`. Also, moves `MockedDescriptorConverter` to `fuzz/util/descriptor` to be used here and in `descriptor` target.
ACKs for top commit:
maflcko:
lgtm ACK 47e5c9994c087d1826ccc0d539e916845b5648fb 🏓
dergoegge:
ACK 47e5c9994c087d1826ccc0d539e916845b5648fb
Tree-SHA512: 519acca6d7b7a3a0bfc031441b02d5980b12bfb97198bd1958a83cd815ceb9eb1499a48a3f0a7fe20e5d06d83b89335d987376fc0a014e2106b0bc0e9838dd02
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/fuzz/descriptor_parse.cpp | 96 | ||||
-rw-r--r-- | src/test/fuzz/script_sign.cpp | 13 | ||||
-rw-r--r-- | src/test/fuzz/util.cpp | 18 | ||||
-rw-r--r-- | src/test/fuzz/util.h | 2 | ||||
-rw-r--r-- | src/test/fuzz/util/descriptor.cpp | 72 | ||||
-rw-r--r-- | src/test/fuzz/util/descriptor.h | 48 |
6 files changed, 142 insertions, 107 deletions
diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index 57129a60b8..5474b38204 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -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; diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp index 0944c91c4a..9c2966e0cb 100644 --- a/src/test/fuzz/script_sign.cpp +++ b/src/test/fuzz/script_sign.cpp @@ -125,18 +125,7 @@ FUZZ_TARGET(script_sign, .init = initialize_script_sign) } (void)signature_creator.CreateSig(provider, vch_sig, address, ConsumeScript(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0})); } - std::map<COutPoint, Coin> coins; - LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { - const std::optional<COutPoint> outpoint = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); - if (!outpoint) { - break; - } - const std::optional<Coin> coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); - if (!coin) { - break; - } - coins[*outpoint] = *coin; - } + std::map<COutPoint, Coin> coins{ConsumeCoins(fuzzed_data_provider)}; std::map<int, bilingual_str> input_errors; (void)SignTransaction(sign_transaction_tx_to, &provider, coins, fuzzed_data_provider.ConsumeIntegral<int>(), input_errors); } diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp index 87ca2f6aed..4ce78d125a 100644 --- a/src/test/fuzz/util.cpp +++ b/src/test/fuzz/util.cpp @@ -164,6 +164,24 @@ uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept fuzzed_data_provider.ConsumeIntegral<uint32_t>(); } +std::map<COutPoint, Coin> ConsumeCoins(FuzzedDataProvider& fuzzed_data_provider) noexcept +{ + std::map<COutPoint, Coin> coins; + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { + const std::optional<COutPoint> outpoint{ConsumeDeserializable<COutPoint>(fuzzed_data_provider)}; + if (!outpoint) { + break; + } + const std::optional<Coin> coin{ConsumeDeserializable<Coin>(fuzzed_data_provider)}; + if (!coin) { + break; + } + coins[*outpoint] = *coin; + } + + return coins; +} + CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept { CTxDestination tx_destination; diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index 0ad2ed6128..9e360a4217 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -181,6 +181,8 @@ template <typename WeakEnumType, size_t size> return UintToArith256(ConsumeUInt256(fuzzed_data_provider)); } +[[nodiscard]] std::map<COutPoint, Coin> ConsumeCoins(FuzzedDataProvider& fuzzed_data_provider) noexcept; + [[nodiscard]] CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept; [[nodiscard]] CKey ConsumePrivateKey(FuzzedDataProvider& fuzzed_data_provider, std::optional<bool> compressed = std::nullopt) noexcept; diff --git a/src/test/fuzz/util/descriptor.cpp b/src/test/fuzz/util/descriptor.cpp new file mode 100644 index 0000000000..5bfd2721ce --- /dev/null +++ b/src/test/fuzz/util/descriptor.cpp @@ -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; +} diff --git a/src/test/fuzz/util/descriptor.h b/src/test/fuzz/util/descriptor.h new file mode 100644 index 0000000000..6289b91b07 --- /dev/null +++ b/src/test/fuzz/util/descriptor.h @@ -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 |