From 47e5c9994c087d1826ccc0d539e916845b5648fb Mon Sep 17 00:00:00 2001 From: brunoerg Date: Tue, 25 Jul 2023 16:14:40 -0300 Subject: [PATCH] fuzz: add target for `DescriptorScriptPubKeyMan` --- src/Makefile.test.include | 3 +- src/wallet/test/fuzz/scriptpubkeyman.cpp | 165 +++++++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 src/wallet/test/fuzz/scriptpubkeyman.cpp diff --git a/src/Makefile.test.include b/src/Makefile.test.include index e9d3bff1dec..416a11b0c01 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -205,7 +205,8 @@ FUZZ_WALLET_SRC = \ if USE_SQLITE FUZZ_WALLET_SRC += \ - wallet/test/fuzz/notifications.cpp + wallet/test/fuzz/notifications.cpp \ + wallet/test/fuzz/scriptpubkeyman.cpp endif # USE_SQLITE BITCOIN_TEST_SUITE += \ diff --git a/src/wallet/test/fuzz/scriptpubkeyman.cpp b/src/wallet/test/fuzz/scriptpubkeyman.cpp new file mode 100644 index 00000000000..a8a9c7ce36f --- /dev/null +++ b/src/wallet/test/fuzz/scriptpubkeyman.cpp @@ -0,0 +1,165 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wallet { +namespace { +const TestingSetup* g_setup; + +//! The converter of mocked descriptors, needs to be initialized when the target is. +MockedDescriptorConverter MOCKED_DESC_CONVERTER; + +void initialize_spkm() +{ + static const auto testing_setup{MakeNoLogFileContext()}; + g_setup = testing_setup.get(); + SelectParams(ChainType::MAIN); + MOCKED_DESC_CONVERTER.Init(); +} + +static std::optional> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider) +{ + const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()}; + const auto desc_str{MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)}; + if (!desc_str.has_value()) return std::nullopt; + + FlatSigningProvider keys; + std::string error; + std::unique_ptr parsed_desc{Parse(desc_str.value(), keys, error, false)}; + if (!parsed_desc) return std::nullopt; + + WalletDescriptor w_desc{std::move(parsed_desc), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1}; + return std::make_pair(w_desc, keys); +} + +static DescriptorScriptPubKeyMan* CreateDescriptor(WalletDescriptor& wallet_desc, FlatSigningProvider& keys, CWallet& keystore) +{ + LOCK(keystore.cs_wallet); + keystore.AddWalletDescriptor(wallet_desc, keys, /*label=*/"", /*internal=*/false); + return keystore.GetDescriptorScriptPubKeyMan(wallet_desc); +}; + +FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + const auto& node{g_setup->m_node}; + Chainstate& chainstate{node.chainman->ActiveChainstate()}; + std::unique_ptr wallet_ptr{std::make_unique(node.chain.get(), "", CreateMockableWalletDatabase())}; + CWallet& wallet{*wallet_ptr}; + { + LOCK(wallet.cs_wallet); + wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS); + wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash()); + } + + auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)}; + if (!wallet_desc.has_value()) return; + auto spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)}; + if (spk_manager == nullptr) return; + + bool good_data{true}; + LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 300) { + CallOneOf( + fuzzed_data_provider, + [&] { + auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)}; + if (!wallet_desc.has_value()) { + good_data = false; + return; + } + std::string error; + if (spk_manager->CanUpdateToWalletDescriptor(wallet_desc->first, error)) { + auto new_spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)}; + if (new_spk_manager != nullptr) spk_manager = new_spk_manager; + } + }, + [&] { + const CScript script{ConsumeScript(fuzzed_data_provider)}; + auto is_mine{spk_manager->IsMine(script)}; + if (is_mine == isminetype::ISMINE_SPENDABLE) { + assert(spk_manager->GetScriptPubKeys().count(script)); + } + }, + [&] { + auto spks{spk_manager->GetScriptPubKeys()}; + for (const CScript& spk : spks) { + assert(spk_manager->IsMine(spk) == ISMINE_SPENDABLE); + CTxDestination dest; + bool extract_dest{ExtractDestination(spk, dest)}; + if (extract_dest) { + const std::string msg{fuzzed_data_provider.ConsumeRandomLengthString()}; + PKHash pk_hash{fuzzed_data_provider.ConsumeBool() ? PKHash{ConsumeUInt160(fuzzed_data_provider)} : *std::get_if(&dest)}; + std::string str_sig; + (void)spk_manager->SignMessage(msg, pk_hash, str_sig); + } + } + }, + [&] { + CKey key{ConsumePrivateKey(fuzzed_data_provider, /*compressed=*/fuzzed_data_provider.ConsumeBool())}; + if (!key.IsValid()) { + good_data = false; + return; + } + spk_manager->AddDescriptorKey(key, key.GetPubKey()); + spk_manager->TopUp(); + }, + [&] { + std::string descriptor; + (void)spk_manager->GetDescriptorString(descriptor, /*priv=*/fuzzed_data_provider.ConsumeBool()); + }, + [&] { + LOCK(spk_manager->cs_desc_man); + auto wallet_desc{spk_manager->GetWalletDescriptor()}; + if (wallet_desc.descriptor->IsSingleType()) { + auto output_type{wallet_desc.descriptor->GetOutputType()}; + if (output_type.has_value()) { + auto dest{spk_manager->GetNewDestination(*output_type)}; + if (dest) { + assert(IsValidDestination(*dest)); + assert(spk_manager->IsHDEnabled()); + } + } + } + }, + [&] { + CMutableTransaction tx_to; + const std::optional opt_tx_to{ConsumeDeserializable(fuzzed_data_provider, TX_WITH_WITNESS)}; + if (!opt_tx_to) { + good_data = false; + return; + } + tx_to = *opt_tx_to; + + std::map coins{ConsumeCoins(fuzzed_data_provider)}; + const int sighash{fuzzed_data_provider.ConsumeIntegral()}; + std::map input_errors; + (void)spk_manager->SignTransaction(tx_to, coins, sighash, input_errors); + }, + [&] { + std::optional opt_psbt{ConsumeDeserializable(fuzzed_data_provider)}; + if (!opt_psbt) { + good_data = false; + return; + } + auto psbt{*opt_psbt}; + const PrecomputedTransactionData txdata{PrecomputePSBTData(psbt)}; + const int sighash_type{fuzzed_data_provider.ConsumeIntegralInRange(0, 150)}; + (void)spk_manager->FillPSBT(psbt, txdata, sighash_type, fuzzed_data_provider.ConsumeBool(), fuzzed_data_provider.ConsumeBool(), nullptr, fuzzed_data_provider.ConsumeBool()); + } + ); + } +} + +} // namespace +} // namespace wallet