Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2023-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <addresstype.h>6#include <chainparams.h>7#include <coins.h>8#include <key.h>9#include <primitives/transaction.h>10#include <psbt.h>11#include <script/descriptor.h>12#include <script/interpreter.h>13#include <script/script.h>14#include <script/signingprovider.h>15#include <sync.h>16#include <test/fuzz/FuzzedDataProvider.h>17#include <test/fuzz/fuzz.h>18#include <test/fuzz/util.h>19#include <test/fuzz/util/descriptor.h>20#include <test/util/setup_common.h>21#include <test/util/time.h>22#include <util/check.h>23#include <util/time.h>24#include <util/translation.h>25#include <util/string.h>26#include <validation.h>27#include <wallet/context.h>28#include <wallet/scriptpubkeyman.h>29#include <wallet/test/util.h>30#include <wallet/types.h>31#include <wallet/wallet.h>32#include <wallet/walletutil.h>34#include <map>35#include <memory>36#include <optional>37#include <string>38#include <utility>39#include <variant>41namespace wallet {42namespace {43const TestingSetup* g_setup;45//! The converter of mocked descriptors, needs to be initialized when the target is.46MockedDescriptorConverter MOCKED_DESC_CONVERTER;48void initialize_spkm()49{50static const auto testing_setup{MakeNoLogFileContext<const TestingSetup>()};51g_setup = testing_setup.get();52MOCKED_DESC_CONVERTER.Init();53}55void initialize_spkm_migration()56{57static const auto testing_setup{MakeNoLogFileContext<const TestingSetup>()};58g_setup = testing_setup.get();59}61static std::optional<std::pair<WalletDescriptor, FlatSigningProvider>> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider)62{63const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()};64const auto desc_str{MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)};65if (!desc_str.has_value()) return std::nullopt;66if (IsTooExpensive(MakeUCharSpan(*desc_str))) return {};68FlatSigningProvider keys;69std::string error;70std::vector<std::unique_ptr<Descriptor>> parsed_descs = Parse(desc_str.value(), keys, error, false);71if (parsed_descs.empty()) return std::nullopt;73// Verify expand succeeds before making WalletDescriptor74// Expansion results are not needed75FlatSigningProvider out_keys;76std::vector<CScript> scripts_temp;77DescriptorCache temp_cache;78if (!parsed_descs.at(0)->Expand(0, keys, scripts_temp, out_keys, &temp_cache)) return std::nullopt;80WalletDescriptor w_desc{std::move(parsed_descs.at(0)), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1};81return std::make_pair(w_desc, keys);82}84static DescriptorScriptPubKeyMan* CreateDescriptor(WalletDescriptor& wallet_desc, FlatSigningProvider& keys, CWallet& keystore)85{86LOCK(keystore.cs_wallet);87auto spk_manager_res = keystore.AddWalletDescriptor(wallet_desc, keys, /*label=*/"", /*internal=*/false);88if (!spk_manager_res) return nullptr;89return &spk_manager_res.value().get();90};92FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm)93{94SeedRandomStateForTest(SeedRand::ZEROS);95FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};96FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};97const auto& node{g_setup->m_node};98Chainstate& chainstate{node.chainman->ActiveChainstate()};99std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())};100CWallet& wallet{*wallet_ptr};101{102LOCK(wallet.cs_wallet);103wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);104wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash());105wallet.m_keypool_size = 1;106}108auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)};109if (!wallet_desc.has_value()) return;110auto spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)};111if (spk_manager == nullptr) return;113if (fuzzed_data_provider.ConsumeBool()) {114auto wallet_desc{CreateWalletDescriptor(fuzzed_data_provider)};115if (!wallet_desc.has_value()) {116return;117}118std::string error;119if (spk_manager->CanUpdateToWalletDescriptor(wallet_desc->first, error)) {120auto new_spk_manager{CreateDescriptor(wallet_desc->first, wallet_desc->second, wallet)};121if (new_spk_manager != nullptr) spk_manager = new_spk_manager;122}123}125bool good_data{true};126LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 20) {127CallOneOf(128fuzzed_data_provider,129[&] {130const CScript script{ConsumeScript(fuzzed_data_provider)};131if (spk_manager->IsMine(script)) {132assert(spk_manager->GetScriptPubKeys().contains(script));133}134},135[&] {136auto spks{spk_manager->GetScriptPubKeys()};137for (const CScript& spk : spks) {138assert(spk_manager->IsMine(spk));139CTxDestination dest;140bool extract_dest{ExtractDestination(spk, dest)};141if (extract_dest) {142const std::string msg{fuzzed_data_provider.ConsumeRandomLengthString()};143PKHash pk_hash{std::get_if<PKHash>(&dest) && fuzzed_data_provider.ConsumeBool() ?144*std::get_if<PKHash>(&dest) :145PKHash{ConsumeUInt160(fuzzed_data_provider)}};146std::string str_sig;147(void)spk_manager->SignMessage(msg, pk_hash, str_sig);148(void)spk_manager->GetMetadata(dest);149}150}151},152[&] {153auto spks{spk_manager->GetScriptPubKeys()};154if (!spks.empty()) {155auto& spk{PickValue(fuzzed_data_provider, spks)};156(void)spk_manager->MarkUnusedAddresses(spk);157}158},159[&] {160LOCK(spk_manager->cs_desc_man);161auto wallet_desc{spk_manager->GetWalletDescriptor()};162if (wallet_desc.descriptor->IsSingleType()) {163auto output_type{wallet_desc.descriptor->GetOutputType()};164if (output_type.has_value()) {165auto dest{spk_manager->GetNewDestination(*output_type)};166if (dest) {167assert(IsValidDestination(*dest));168assert(spk_manager->IsHDEnabled());169}170}171}172},173[&] {174CMutableTransaction tx_to;175const std::optional<CMutableTransaction> opt_tx_to{ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS)};176if (!opt_tx_to) {177good_data = false;178return;179}180tx_to = *opt_tx_to;182std::map<COutPoint, Coin> coins{ConsumeCoins(fuzzed_data_provider)};183const int sighash{fuzzed_data_provider.ConsumeIntegral<int>()};184std::map<int, bilingual_str> input_errors;185(void)spk_manager->SignTransaction(tx_to, coins, sighash, input_errors);186},187[&] {188std::optional<PartiallySignedTransaction> opt_psbt{ConsumeDeserializableConstructor<PartiallySignedTransaction>(fuzzed_data_provider)};189if (!opt_psbt) {190good_data = false;191return;192}193auto psbt{*opt_psbt};194std::optional<PrecomputedTransactionData> txdata_res = PrecomputePSBTData(psbt);195if (!txdata_res) {196return;197}198const PrecomputedTransactionData& txdata = *txdata_res;199common::PSBTFillOptions options{200.sign = fuzzed_data_provider.ConsumeBool(),201.sighash_type = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 151),202.finalize = fuzzed_data_provider.ConsumeBool(),203.bip32_derivs = fuzzed_data_provider.ConsumeBool()204};205if (options.sighash_type == 151) options.sighash_type = std::nullopt;206(void)spk_manager->FillPSBT(psbt, txdata, options);207}208);209}211std::string descriptor;212(void)spk_manager->GetDescriptorString(descriptor, /*priv=*/fuzzed_data_provider.ConsumeBool());213(void)spk_manager->GetEndRange();214(void)spk_manager->GetKeyPoolSize();215}217FUZZ_TARGET(spkm_migration, .init = initialize_spkm_migration)218{219SeedRandomStateForTest(SeedRand::ZEROS);220FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};221FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};222const auto& node{g_setup->m_node};223Chainstate& chainstate{node.chainman->ActiveChainstate()};225std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())};226CWallet& wallet{*wallet_ptr};227wallet.m_keypool_size = 1;228{229LOCK(wallet.cs_wallet);230wallet.UnsetWalletFlag(WALLET_FLAG_DESCRIPTORS);231wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash());232}234auto& legacy_data{*wallet.GetOrCreateLegacyDataSPKM()};236std::vector<CKey> keys;237LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 30) {238const auto key{ConsumePrivateKey(fuzzed_data_provider)};239if (!key.IsValid()) return;240auto pub_key{key.GetPubKey()};241if (!pub_key.IsFullyValid()) return;242if (legacy_data.LoadKey(key, pub_key) && std::find(keys.begin(), keys.end(), key) == keys.end()) keys.push_back(key);243}245size_t added_chains = 0;246bool add_hd_chain{fuzzed_data_provider.ConsumeBool() && !keys.empty()};247CHDChain hd_chain;248auto version{fuzzed_data_provider.ConsumeBool() ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE};249CKey hd_key;250if (add_hd_chain) {
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.