wallet/migration: use HavePrivateKeys in place of ToPrivateString
What changed, and why it matters
This is a small internal refactoring change in Bitcoin Core's wallet migration code. It swaps one method for another when deciding whether a wallet descriptor should be treated as watch-only during migration. The commit message says this is to keep the current pull request focused on RPC behavior and that a follow-up change may refine the logic later. There is no direct evidence in the commit of a security bug being fixed.
Treat as a routine refactor. Review the follow-up PR mentioned in the commit message to ensure descriptors with only some private keys are handled correctly in migration. No immediate security action is indicated by this commit alone.
Security signals we found
Refactor of wallet descriptor private-key handling
Potential for changed watch-only classification during wallet migration
Author notes follow-up work needed for descriptors with partial private keys
Evidence from the diff
In src/wallet/scriptpubkeyman.cpp, LegacyDataSPKM::MigrateToDescriptor() no longer calls Descriptor::ToPrivateString() to both build a private descriptor string and infer watchonly status. Instead it calls Descriptor::HavePrivateKeys() to decide watchonly status. The descriptor string is now only produced later via ToString() for watch-only descriptors. The change is described by the author as a scope-limiting refactor ahead of future behavior changes to ToPrivateString().
Changed components
src/wallet/scriptpubkeyman.cppLegacyDataSPKM::MigrateToDescriptor()Wallet migration to descriptor walletsInspect captured patch +3 / −4
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 27681714..708f5989 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -717,10 +717,9 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
std::vector<CScript> desc_spks;
- // Make the descriptor string with private keys
- std::string desc_str;
- bool watchonly = !desc->ToPrivateString(*this, desc_str);
- if (watchonly && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
+ // If we can't provide all private keys for this inferred descriptor,
+ // but this wallet is not watch-only, migrate it to the watch-only wallet.
+ if (!desc->HavePrivateKeys(*this) && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
out.watch_descs.emplace_back(desc->ToString(), creation_time);
// Get the scriptPubKeys without writing this to the wallet
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.