diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index ef96f1cb436..735ee7c7861 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1992,10 +1992,29 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() } } - // Multisigs are special. They don't show up as ISMINE_SPENDABLE unless they are in a P2SH - // So we have to check if any of our scripts are a multisig and if so, add the P2SH - for (const auto& script_pair : mapScripts) { - const CScript script = script_pair.second; + // Make sure that we have accounted for all scriptPubKeys + if (!Assume(spks.empty())) { + LogPrintf("%s\n", STR_INTERNAL_BUG("Error: Some output scripts were not migrated.\n")); + return std::nullopt; + } + + // Legacy wallets can also contain scripts whose P2SH, P2WSH, or P2SH-P2WSH it is not watching for + // but can provide script data to a PSBT spending them. These "solvable" output scripts will need to + // be put into the separate "solvables" wallet. + // These can be detected by going through the entire candidate output scripts, finding the ISMINE_NO scripts, + // and checking CanProvide() which will dummy sign. + for (const CScript& script : GetCandidateScriptPubKeys()) { + // Since we only care about P2SH, P2WSH, and P2SH-P2WSH, filter out any scripts that are not those + if (!script.IsPayToScriptHash() && !script.IsPayToWitnessScriptHash()) { + continue; + } + if (IsMine(script) != ISMINE_NO) { + continue; + } + SignatureData dummy_sigdata; + if (!CanProvide(script, dummy_sigdata)) { + continue; + } // Get birthdate from script meta uint64_t creation_time = 0; @@ -2004,45 +2023,28 @@ std::optional LegacyDataSPKM::MigrateToDescriptor() creation_time = it->second.nCreateTime; } - std::vector> sols; - TxoutType type = Solver(script, sols); - if (type == TxoutType::MULTISIG) { - CScript sh_spk = GetScriptForDestination(ScriptHash(script)); - CTxDestination witdest = WitnessV0ScriptHash(script); - CScript witprog = GetScriptForDestination(witdest); - CScript sh_wsh_spk = GetScriptForDestination(ScriptHash(witprog)); + // InferDescriptor as that will get us all the solving info if it is there + std::unique_ptr desc = InferDescriptor(script, *GetSolvingProvider(script)); + if (!desc->IsSolvable()) { + // The wallet was able to provide some information, but not enough to make a descriptor that actually + // contains anything useful. This is probably because the script itself is actually unsignable (e.g. P2WSH-P2WSH). + continue; + } - // We only want the multisigs that we have not already seen, i.e. they are not watchonly and not spendable - // For P2SH, a multisig is not ISMINE_NO when: - // * All keys are in the wallet - // * The multisig itself is watch only - // * The P2SH is watch only - // For P2SH-P2WSH, if the script is in the wallet, then it will have the same conditions as P2SH. - // For P2WSH, a multisig is not ISMINE_NO when, other than the P2SH conditions: - // * The P2WSH script is in the wallet and it is being watched - std::vector> keys(sols.begin() + 1, sols.begin() + sols.size() - 1); - if (HaveWatchOnly(sh_spk) || HaveWatchOnly(script) || HaveKeys(keys, *this) || (HaveCScript(CScriptID(witprog)) && HaveWatchOnly(witprog))) { - // The above emulates IsMine for these 3 scriptPubKeys, so double check that by running IsMine - assert(IsMine(sh_spk) != ISMINE_NO || IsMine(witprog) != ISMINE_NO || IsMine(sh_wsh_spk) != ISMINE_NO); + // Past bugs in InferDescriptor have caused it to create descriptors which cannot be re-parsed + // Re-parse the descriptors to detect that, and skip any that do not parse. + { + std::string desc_str = desc->ToString(); + FlatSigningProvider parsed_keys; + std::string parse_error; + std::vector> parsed_descs = Parse(desc_str, parsed_keys, parse_error, false); + if (parsed_descs.empty()) { continue; } - assert(IsMine(sh_spk) == ISMINE_NO && IsMine(witprog) == ISMINE_NO && IsMine(sh_wsh_spk) == ISMINE_NO); - - std::unique_ptr sh_desc = InferDescriptor(sh_spk, *GetSolvingProvider(sh_spk)); - out.solvable_descs.emplace_back(sh_desc->ToString(), creation_time); - - const auto desc = InferDescriptor(witprog, *this); - if (desc->IsSolvable()) { - std::unique_ptr wsh_desc = InferDescriptor(witprog, *GetSolvingProvider(witprog)); - out.solvable_descs.emplace_back(wsh_desc->ToString(), creation_time); - std::unique_ptr sh_wsh_desc = InferDescriptor(sh_wsh_spk, *GetSolvingProvider(sh_wsh_spk)); - out.solvable_descs.emplace_back(sh_wsh_desc->ToString(), creation_time); - } } - } - // Make sure that we have accounted for all scriptPubKeys - assert(spks.size() == 0); + out.solvable_descs.emplace_back(desc->ToString(), creation_time); + } // Finalize transaction if (!batch.TxnCommit()) {