From 0301c758ea0d0b95090d7492f1e5d30e6b447b9c Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Wed, 29 Apr 2026 14:13:28 -0700 Subject: [PATCH] wallet migration, fuzz: Migrate hd seed once If a wallet has multiple HD chains that have the same seed, we should only migrate that seed a single time. This fixes a fuzz crash that occurs once the return value of AddDescriptorKeyWithDB is checked during descriptor construction. --- src/wallet/scriptpubkeyman.cpp | 6 +++--- src/wallet/test/fuzz/scriptpubkeyman.cpp | 5 ++++- src/wallet/walletdb.h | 4 ++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 8a1c0a45a56..a65dee60ac0 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -619,10 +619,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; diff --git a/src/wallet/test/fuzz/scriptpubkeyman.cpp b/src/wallet/test/fuzz/scriptpubkeyman.cpp index a627c770541..341543ff412 100644 --- a/src/wallet/test/fuzz/scriptpubkeyman.cpp +++ b/src/wallet/test/fuzz/scriptpubkeyman.cpp @@ -229,6 +229,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}; @@ -238,14 +239,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; @@ -323,7 +327,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/walletdb.h b/src/wallet/walletdb.h index 455fc745571..454435cfcb4 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -126,6 +126,10 @@ public: { return seed_id == chain.seed_id; } + bool operator<(const CHDChain& chain) const + { + return seed_id < chain.seed_id; + } }; class CKeyMetadata