@@ -1700,59 +1700,62 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
17001700 return set_address;
17011701}
17021702
1703- std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys () const
1703+ std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetCandidateScriptPubKeys () const
17041704{
17051705 LOCK (cs_KeyStore);
1706- std::unordered_set<CScript, SaltedSipHasher> spks ;
1706+ std::unordered_set<CScript, SaltedSipHasher> candidate_spks ;
17071707
1708- // All keys have at least P2PK and P2PKH
1709- for (const auto & key_pair : mapKeys) {
1710- const CPubKey& pub = key_pair.second .GetPubKey ();
1711- spks.insert (GetScriptForRawPubKey (pub));
1712- spks.insert (GetScriptForDestination (PKHash (pub)));
1708+ // For every private key in the wallet, there should be a P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH
1709+ const auto & add_pubkey = [&candidate_spks](const CPubKey& pub) -> void {
1710+ candidate_spks.insert (GetScriptForRawPubKey (pub));
1711+ candidate_spks.insert (GetScriptForDestination (PKHash (pub)));
1712+
1713+ CScript wpkh = GetScriptForDestination (WitnessV0KeyHash (pub));
1714+ candidate_spks.insert (wpkh);
1715+ candidate_spks.insert (GetScriptForDestination (ScriptHash (wpkh)));
1716+ };
1717+ for (const auto & [_, key] : mapKeys) {
1718+ add_pubkey (key.GetPubKey ());
17131719 }
1714- for (const auto & key_pair : mapCryptedKeys) {
1715- const CPubKey& pub = key_pair.second .first ;
1716- spks.insert (GetScriptForRawPubKey (pub));
1717- spks.insert (GetScriptForDestination (PKHash (pub)));
1720+ for (const auto & [_, ckeypair] : mapCryptedKeys) {
1721+ add_pubkey (ckeypair.first );
17181722 }
17191723
1720- // For every script in mapScript, only the ISMINE_SPENDABLE ones are being tracked.
1721- // The watchonly ones will be in setWatchOnly which we deal with later
1722- // For all keys, if they have segwit scripts, those scripts will end up in mapScripts
1723- for (const auto & script_pair : mapScripts) {
1724- const CScript& script = script_pair.second ;
1725- if (IsMine (script) == ISMINE_SPENDABLE) {
1726- // Add ScriptHash for scripts that are not already P2SH
1727- if (!script.IsPayToScriptHash ()) {
1728- spks.insert (GetScriptForDestination (ScriptHash (script)));
1729- }
1730- // For segwit scripts, we only consider them spendable if we have the segwit spk
1731- int wit_ver = -1 ;
1732- std::vector<unsigned char > witprog;
1733- if (script.IsWitnessProgram (wit_ver, witprog) && wit_ver == 0 ) {
1734- spks.insert (script);
1735- }
1736- } else {
1737- // Multisigs are special. They don't show up as ISMINE_SPENDABLE unless they are in a P2SH
1738- // So check the P2SH of a multisig to see if we should insert it
1739- std::vector<std::vector<unsigned char >> sols;
1740- TxoutType type = Solver (script, sols);
1741- if (type == TxoutType::MULTISIG) {
1742- CScript ms_spk = GetScriptForDestination (ScriptHash (script));
1743- if (IsMine (ms_spk) != ISMINE_NO) {
1744- spks.insert (ms_spk);
1745- }
1746- }
1747- }
1724+ // mapScripts contains all redeemScripts and witnessScripts. Therefore each script in it has
1725+ // itself, P2SH, P2WSH, and P2SH-P2WSH as a candidate.
1726+ // Invalid scripts such as P2SH-P2SH and P2WSH-P2SH, among others, will be added as candidates.
1727+ // Callers of this function will need to remove such scripts.
1728+ const auto & add_script = [&candidate_spks](const CScript& script) -> void {
1729+ candidate_spks.insert (script);
1730+ candidate_spks.insert (GetScriptForDestination (ScriptHash (script)));
1731+
1732+ CScript wsh = GetScriptForDestination (WitnessV0ScriptHash (script));
1733+ candidate_spks.insert (wsh);
1734+ candidate_spks.insert (GetScriptForDestination (ScriptHash (wsh)));
1735+ };
1736+ for (const auto & [_, script] : mapScripts) {
1737+ add_script (script);
17481738 }
17491739
1750- // All watchonly scripts are raw
1751- for (const CScript& script : setWatchOnly) {
1752- // As the legacy wallet allowed to import any script, we need to verify the validity here.
1753- // LegacyScriptPubKeyMan::IsMine() return 'ISMINE_NO' for invalid or not watched scripts (IsMineResult::INVALID or IsMineResult::NO).
1754- // e.g. a "sh(sh(pkh()))" which legacy wallets allowed to import!.
1755- if (IsMine (script) != ISMINE_NO) spks.insert (script);
1740+ // Although setWatchOnly should only contain output scripts, we will also include each script's
1741+ // P2SH, P2WSH, and P2SH-P2WSH as a precaution.
1742+ for (const auto & script : setWatchOnly) {
1743+ add_script (script);
1744+ }
1745+
1746+ return candidate_spks;
1747+ }
1748+
1749+ std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys () const
1750+ {
1751+ // Run IsMine() on each candidate output script. Any script that is not ISMINE_NO is an output
1752+ // script to return.
1753+ // This both filters out things that are not watched by the wallet, and things that are invalid.
1754+ std::unordered_set<CScript, SaltedSipHasher> spks;
1755+ for (const CScript& script : GetCandidateScriptPubKeys ()) {
1756+ if (IsMine (script) != ISMINE_NO) {
1757+ spks.insert (script);
1758+ }
17561759 }
17571760
17581761 return spks;
0 commit comments