diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp index f9fa46b9112..758be582ac4 100644 --- a/src/wallet/external_signer_scriptpubkeyman.cpp +++ b/src/wallet/external_signer_scriptpubkeyman.cpp @@ -21,28 +21,35 @@ using common::PSBTError; namespace wallet { -bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::unique_ptr desc) +std::unique_ptr ExternalSignerScriptPubKeyMan::LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys) { - LOCK(cs_desc_man); - assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); - assert(m_storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)); + return std::unique_ptr(new ExternalSignerScriptPubKeyMan(storage, descriptor, keypool_size, keys, ckeys)); +} + +std::unique_ptr ExternalSignerScriptPubKeyMan::CreateNew(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr desc) +{ + auto spkm = std::unique_ptr(new ExternalSignerScriptPubKeyMan(storage, keypool_size)); + + LOCK(spkm->cs_desc_man); + assert(storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); + assert(storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)); int64_t creation_time = GetTime(); // Make the descriptor WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); - m_wallet_descriptor = w_desc; + spkm->m_wallet_descriptor = w_desc; // Store the descriptor - if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) { + if (!batch.WriteDescriptor(spkm->GetID(), spkm->m_wallet_descriptor)) { throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); } // TopUp - TopUpWithDB(batch); + spkm->TopUpWithDB(batch); - m_storage.UnsetBlankWalletFlag(batch); - return true; + storage.UnsetBlankWalletFlag(batch); + return spkm; } util::Result ExternalSignerScriptPubKeyMan::GetExternalSigner() { diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h index 634b3bdbaa1..8a3ae3df7a2 100644 --- a/src/wallet/external_signer_scriptpubkeyman.h +++ b/src/wallet/external_signer_scriptpubkeyman.h @@ -15,18 +15,19 @@ struct bilingual_str; namespace wallet { class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan { - public: - ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size) - : DescriptorScriptPubKeyMan(storage, descriptor, keypool_size) - {} - ExternalSignerScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) - : DescriptorScriptPubKeyMan(storage, keypool_size) - {} +private: + //! Create an ExternalSPKM from existing wallet data + ExternalSignerScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys) + : DescriptorScriptPubKeyMan(storage, descriptor, keypool_size, keys, ckeys) + {} - /** Provide a descriptor at setup time - * Returns false if already setup or setup fails, true if setup is successful - */ - bool SetupDescriptor(WalletBatch& batch, std::unique_ptrdesc); + ExternalSignerScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) + : DescriptorScriptPubKeyMan(storage, keypool_size) + {} + +public: + static std::unique_ptr LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys); + static std::unique_ptr CreateNew(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr desc); static util::Result GetExternalSigner(); diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 5c6c01e1616..06ceed40d9b 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -596,16 +596,15 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() // Construct the combo descriptor std::string desc_str = "combo(" + origin_str + HexStr(key.GetPubKey()) + ")"; - FlatSigningProvider keys; + FlatSigningProvider provider; std::string error; - std::vector> descs = Parse(desc_str, keys, error, false); + std::vector> descs = Parse(desc_str, provider, error, false); CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor WalletDescriptor w_desc(std::move(descs.at(0)), creation_time, 0, 0, 0); // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys - auto desc_spk_man = std::make_unique(m_storage, w_desc, /*keypool_size=*/0); - WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey())); - desc_spk_man->TopUpWithDB(batch); + provider.keys.emplace(key.GetPubKey().GetID(), key); + auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider); auto desc_spks = desc_spk_man->GetScriptPubKeys(); // Remove the scriptPubKeys from our current set @@ -619,10 +618,10 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() } // Handle HD keys by using the CHDChains - std::vector chains; - chains.push_back(m_hd_chain); + std::set chains; + chains.insert(m_hd_chain); for (const auto& chain_pair : m_inactive_hd_chains) { - chains.push_back(chain_pair.second); + chains.insert(chain_pair.second); } bool can_support_hd_split_feature = m_hd_chain.nVersion >= CHDChain::VERSION_HD_CHAIN_SPLIT; @@ -644,17 +643,16 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() // Make the combo descriptor std::string xpub = EncodeExtPubKey(master_key.Neuter()); std::string desc_str = "combo(" + xpub + "/0h/" + ToString(i) + "h/*h)"; - FlatSigningProvider keys; + FlatSigningProvider provider; std::string error; - std::vector> descs = Parse(desc_str, keys, error, false); + std::vector> descs = Parse(desc_str, provider, error, false); CHECK_NONFATAL(descs.size() == 1); // It shouldn't be possible to have an invalid or multipath descriptor uint32_t chain_counter = std::max((i == 1 ? chain.nInternalChainCounter : chain.nExternalChainCounter), (uint32_t)0); WalletDescriptor w_desc(std::move(descs.at(0)), 0, 0, chain_counter, 0); // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys - auto desc_spk_man = std::make_unique(m_storage, w_desc, /*keypool_size=*/0); - WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey())); - desc_spk_man->TopUpWithDB(batch); + provider.keys.emplace(master_key.key.GetPubKey().GetID(), master_key.key); + auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, provider); auto desc_spks = desc_spk_man->GetScriptPubKeys(); // Remove the scriptPubKeys from our current set @@ -727,16 +725,15 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() desc->Expand(0, provider, desc_spks, provider); } else { // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys - WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); - auto desc_spk_man = std::make_unique(m_storage, w_desc, /*keypool_size=*/0); for (const auto& keyid : privkeyids) { CKey key; if (!GetKey(keyid, key)) { continue; } - WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey())); + keys.keys.emplace(key.GetPubKey().GetID(), key); } - desc_spk_man->TopUpWithDB(batch); + WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); + auto desc_spk_man = DescriptorScriptPubKeyMan::CreateFromMigration(m_storage, batch, w_desc, /*keypool_size=*/0, keys); auto desc_spks_set = desc_spk_man->GetScriptPubKeys(); desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end()); @@ -821,6 +818,48 @@ bool LegacyDataSPKM::DeleteRecordsWithDB(WalletBatch& batch) return batch.EraseRecords(DBKeys::LEGACY_TYPES); } +std::unique_ptr DescriptorScriptPubKeyMan::CreateFromImport(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider) +{ + auto spkm = std::unique_ptr(new DescriptorScriptPubKeyMan(storage, descriptor, keypool_size)); + LOCK(spkm->cs_desc_man); + WalletBatch batch(storage.GetDatabase()); + spkm->UpdateWithSigningProvider(batch, provider); + return spkm; +} + +std::unique_ptr DescriptorScriptPubKeyMan::CreateFromMigration(WalletStorage& storage, WalletBatch& batch, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider) +{ + auto spkm = std::unique_ptr(new DescriptorScriptPubKeyMan(storage, descriptor, keypool_size)); + LOCK(spkm->cs_desc_man); + spkm->UpdateWithSigningProvider(batch, provider); + return spkm; +} + +DescriptorScriptPubKeyMan::DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys) + : ScriptPubKeyMan(storage), + m_map_keys(keys), + m_map_crypted_keys(ckeys), + m_keypool_size(keypool_size), + m_wallet_descriptor(descriptor) +{ + if (!keys.empty() && !ckeys.empty()) { + throw std::runtime_error("Wallet contains both unencrypted and encrypted keys"); + } + Load(); +} + +std::unique_ptr DescriptorScriptPubKeyMan::LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys) +{ + return std::unique_ptr(new DescriptorScriptPubKeyMan(storage, descriptor, keypool_size, keys, ckeys)); +} + +std::unique_ptr DescriptorScriptPubKeyMan::GenerateNewSingleSig(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, const CExtKey& master_key, OutputType addr_type, bool internal) +{ + auto spkm = std::unique_ptr(new DescriptorScriptPubKeyMan(storage, keypool_size)); + spkm->SetupDescriptorGeneration(batch, master_key, addr_type, internal); + return spkm; +} + util::Result DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type) { // Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later @@ -1132,15 +1171,11 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const } } -bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(WalletBatch& batch, const CExtKey& master_key, OutputType addr_type, bool internal) +void DescriptorScriptPubKeyMan::SetupDescriptorGeneration(WalletBatch& batch, const CExtKey& master_key, OutputType addr_type, bool internal) { LOCK(cs_desc_man); - assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); - - // Ignore when there is already a descriptor - if (m_wallet_descriptor.descriptor) { - return false; - } + Assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); + Assert(!m_wallet_descriptor.descriptor); m_wallet_descriptor = GenerateWalletDescriptor(master_key.Neuter(), addr_type, internal); @@ -1152,11 +1187,15 @@ bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(WalletBatch& batch, co throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); } + // Set m_decryption_thoroughly_checked for encrypted wallets + if (m_storage.HasEncryptionKeys()) { + m_decryption_thoroughly_checked = true; + } + // TopUp TopUpWithDB(batch); m_storage.UnsetBlankWalletFlag(batch); - return true; } bool DescriptorScriptPubKeyMan::IsHDEnabled() const @@ -1425,11 +1464,10 @@ uint256 DescriptorScriptPubKeyMan::GetID() const return m_wallet_descriptor.id; } -void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) +void DescriptorScriptPubKeyMan::Load() { LOCK(cs_desc_man); std::set new_spks; - m_wallet_descriptor.cache = cache; for (int32_t i = m_wallet_descriptor.range_start; i < m_wallet_descriptor.range_end; ++i) { FlatSigningProvider out_keys; std::vector scripts_temp; @@ -1459,24 +1497,6 @@ void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) m_storage.TopUpCallback(new_spks, this); } -bool DescriptorScriptPubKeyMan::AddKey(const CKeyID& key_id, const CKey& key) -{ - LOCK(cs_desc_man); - m_map_keys[key_id] = key; - return true; -} - -bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector& crypted_key) -{ - LOCK(cs_desc_man); - if (!m_map_keys.empty()) { - return false; - } - - m_map_crypted_keys[key_id] = make_pair(pubkey, crypted_key); - return true; -} - bool DescriptorScriptPubKeyMan::HasWalletDescriptor(const WalletDescriptor& desc) const { LOCK(cs_desc_man); @@ -1565,7 +1585,7 @@ void DescriptorScriptPubKeyMan::UpgradeDescriptorCache() } } -util::Result DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor) +util::Result DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider) { LOCK(cs_desc_man); std::string error; @@ -1578,10 +1598,29 @@ util::Result DescriptorScriptPubKeyMan::UpdateWalletDescriptor(WalletDescr m_max_cached_index = -1; m_wallet_descriptor = descriptor; + WalletBatch batch(m_storage.GetDatabase()); + UpdateWithSigningProvider(batch, provider); NotifyFirstKeyTimeChanged(this, m_wallet_descriptor.creation_time); return {}; } +void DescriptorScriptPubKeyMan::UpdateWithSigningProvider(WalletBatch& batch, const FlatSigningProvider& signing_provider) +{ + AssertLockHeld(cs_desc_man); + // Add the private keys to the descriptor + for (const auto& entry : signing_provider.keys) { + const CKey& key = entry.second; + if (!AddDescriptorKeyWithDB(batch, key, key.GetPubKey())) { + throw std::runtime_error(std::string(__func__) + ": writing descriptor private key failed"); + } + } + + // Top up key pool, to generate scriptPubKeys + if (!TopUpWithDB(batch)) { + throw std::runtime_error("Could not top up scriptPubKeys"); + } +} + bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error) { LOCK(cs_desc_man); diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 49731d16a7d..1acaa93bf8f 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -165,6 +165,9 @@ static const std::unordered_set LEGACY_OUTPUT_TYPES { OutputType::BECH32, }; +using KeyMap = std::map; +using CryptedKeyMap = std::map>>; + // Manages the data for a LegacyScriptPubKeyMan. // This is the minimum necessary to load a legacy wallet so that it can be migrated. class LegacyDataSPKM : public ScriptPubKeyMan, public FillableSigningProvider @@ -172,7 +175,6 @@ class LegacyDataSPKM : public ScriptPubKeyMan, public FillableSigningProvider private: using WatchOnlySet = std::set; using WatchKeyMap = std::map; - using CryptedKeyMap = std::map>>; CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore); @@ -274,8 +276,6 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan private: using ScriptPubKeyMap = std::map; // Map of scripts to descriptor range index using PubKeyMap = std::map; // Map of pubkeys involved in scripts to descriptor range index - using CryptedKeyMap = std::map>>; - using KeyMap = std::map; ScriptPubKeyMap m_map_script_pub_keys GUARDED_BY(cs_desc_man); PubKeyMap m_map_pubkeys GUARDED_BY(cs_desc_man); @@ -303,6 +303,13 @@ private: */ mutable std::map m_musig2_secnonces; + //! Create a new DescriptorScriptPubKeyMan from an existing descriptor (i.e. from an import) + DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size) + : ScriptPubKeyMan(storage), + m_keypool_size(keypool_size), + m_wallet_descriptor(descriptor) + {} + bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); @@ -314,22 +321,33 @@ private: // Fetch the SigningProvider for a given index and optionally include private keys. Called by the above functions. std::unique_ptr GetSigningProvider(int32_t index, bool include_private = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); + void Load(); + + void AddDescriptorKey(const CKey& key, const CPubKey &pubkey); + void UpdateWithSigningProvider(WalletBatch& batch, const FlatSigningProvider& signing_provider) EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); + + //! Setup descriptors based on the given CExtKey + void SetupDescriptorGeneration(WalletBatch& batch, const CExtKey& master_key, OutputType addr_type, bool internal); + protected: + //! Create a DescriptorScriptPubKeyMan from existing data (i.e. during loading) + DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys); + + DescriptorScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) + : ScriptPubKeyMan(storage), + m_keypool_size(keypool_size) + {} + WalletDescriptor m_wallet_descriptor GUARDED_BY(cs_desc_man); //! Same as 'TopUp' but designed for use within a batch transaction context bool TopUpWithDB(WalletBatch& batch, unsigned int size = 0); public: - DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size) - : ScriptPubKeyMan(storage), - m_keypool_size(keypool_size), - m_wallet_descriptor(descriptor) - {} - DescriptorScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) - : ScriptPubKeyMan(storage), - m_keypool_size(keypool_size) - {} + static std::unique_ptr LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys); + static std::unique_ptr CreateFromImport(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider); + static std::unique_ptr CreateFromMigration(WalletStorage& storage, WalletBatch& batch, WalletDescriptor& descriptor, int64_t keypool_size, const FlatSigningProvider& provider); + static std::unique_ptr GenerateNewSingleSig(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, const CExtKey& master_key, OutputType addr_type, bool internal); mutable RecursiveMutex cs_desc_man; @@ -352,9 +370,6 @@ public: bool IsHDEnabled() const override; - //! Setup descriptors based on the given CExtkey - bool SetupDescriptorGeneration(WalletBatch& batch, const CExtKey& master_key, OutputType addr_type, bool internal); - bool HavePrivateKeys() const override; bool HasPrivKey(const CKeyID& keyid) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); //! Retrieve the particular key if it is available. Returns nullopt if the key is not in the wallet, or if the wallet is locked. @@ -382,15 +397,9 @@ public: uint256 GetID() const override; - void SetCache(const DescriptorCache& cache); - - bool AddKey(const CKeyID& key_id, const CKey& key); - bool AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector& crypted_key); - bool HasWalletDescriptor(const WalletDescriptor& desc) const; - util::Result UpdateWalletDescriptor(WalletDescriptor& descriptor); + util::Result UpdateWalletDescriptor(WalletDescriptor& descriptor, const FlatSigningProvider& provider); bool CanUpdateToWalletDescriptor(const WalletDescriptor& descriptor, std::string& error); - void AddDescriptorKey(const CKey& key, const CPubKey &pubkey); void WriteDescriptor(); WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man); diff --git a/src/wallet/test/fuzz/scriptpubkeyman.cpp b/src/wallet/test/fuzz/scriptpubkeyman.cpp index 620c1be6ef3..b6b3c5d5597 100644 --- a/src/wallet/test/fuzz/scriptpubkeyman.cpp +++ b/src/wallet/test/fuzz/scriptpubkeyman.cpp @@ -70,6 +70,13 @@ static std::optional> CreateWal std::vector> parsed_descs = Parse(desc_str.value(), keys, error, false); if (parsed_descs.empty()) return std::nullopt; + // Verify expand succeeds before making WalletDescriptor + // Expansion results are not needed + FlatSigningProvider out_keys; + std::vector scripts_temp; + DescriptorCache temp_cache; + if (!parsed_descs.at(0)->Expand(0, keys, scripts_temp, out_keys, &temp_cache)) return std::nullopt; + WalletDescriptor w_desc{std::move(parsed_descs.at(0)), /*creation_time=*/0, /*range_start=*/0, /*range_end=*/1, /*next_index=*/1}; return std::make_pair(w_desc, keys); } @@ -235,6 +242,7 @@ FUZZ_TARGET(spkm_migration, .init = initialize_spkm_migration) if (legacy_data.LoadKey(key, pub_key) && std::find(keys.begin(), keys.end(), key) == keys.end()) keys.push_back(key); } + size_t added_chains = 0; bool add_hd_chain{fuzzed_data_provider.ConsumeBool() && !keys.empty()}; CHDChain hd_chain; auto version{fuzzed_data_provider.ConsumeBool() ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE}; @@ -244,14 +252,17 @@ FUZZ_TARGET(spkm_migration, .init = initialize_spkm_migration) hd_chain.nVersion = version; hd_chain.seed_id = hd_key.GetPubKey().GetID(); legacy_data.LoadHDChain(hd_chain); + added_chains++; } bool add_inactive_hd_chain{fuzzed_data_provider.ConsumeBool() && !keys.empty()}; if (add_inactive_hd_chain) { hd_key = PickValue(fuzzed_data_provider, keys); hd_chain.nVersion = fuzzed_data_provider.ConsumeBool() ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; + bool dup_chain = hd_chain.seed_id == hd_key.GetPubKey().GetID(); hd_chain.seed_id = hd_key.GetPubKey().GetID(); legacy_data.AddInactiveHDChain(hd_chain); + if (!dup_chain) added_chains++; } bool watch_only = false; @@ -329,7 +340,6 @@ FUZZ_TARGET(spkm_migration, .init = initialize_spkm_migration) auto result{legacy_data.MigrateToDescriptor()}; assert(result); - size_t added_chains{static_cast(add_hd_chain) + static_cast(add_inactive_hd_chain)}; if ((add_hd_chain && version >= CHDChain::VERSION_HD_CHAIN_SPLIT) || (!add_hd_chain && add_inactive_hd_chain)) { added_chains *= 2; } diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp index 9c62436d6c0..74389a54967 100644 --- a/src/wallet/test/scriptpubkeyman_tests.cpp +++ b/src/wallet/test/scriptpubkeyman_tests.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include