Skip to content

Commit

Permalink
wallet: Remove IsMine from migration
Browse files Browse the repository at this point in the history
As IsMine will be removed, the relevant components of IsMine are inlined
into the migration functions.
  • Loading branch information
achow101 committed Jun 6, 2024
1 parent c653f4f commit 8950371
Showing 1 changed file with 137 additions and 19 deletions.
156 changes: 137 additions & 19 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1720,37 +1720,157 @@ std::unordered_set<CScript, SaltedSipHasher> LegacyDataSPKM::GetScriptPubKeys()
// For all keys, if they have segwit scripts, those scripts will end up in mapScripts
for (const auto& script_pair : mapScripts) {
const CScript& script = script_pair.second;
if (IsMine(script) == ISMINE_SPENDABLE) {
// Add ScriptHash for scripts that are not already P2SH
if (!script.IsPayToScriptHash()) {
std::vector<std::vector<unsigned char>> sols;
TxoutType type = Solver(script, sols);
switch (type) {
// We don't care aboue these types because they are not spendable
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT:
case TxoutType::SCRIPTHASH:
// These are only spendable if the witness scripts is also spendable as a scriptPubKey
// We will check these later after spks has been updated with spks from scripts.
case TxoutType::WITNESS_V0_SCRIPTHASH:
break;
// These scriptPubKeys have already been handled by dealing with the keys
// However if these scripts are here, then the P2SH nested spk will be spendable if these are also scriptPubKeys
case TxoutType::PUBKEY:
case TxoutType::PUBKEYHASH:
if (spks.count(script) > 0) {
spks.insert(GetScriptForDestination(ScriptHash(script)));
}
// For segwit scripts, we only consider them spendable if we have the segwit spk
int wit_ver = -1;
std::vector<unsigned char> witprog;
if (script.IsWitnessProgram(wit_ver, witprog) && wit_ver == 0) {
break;
case TxoutType::WITNESS_V0_KEYHASH:
{
CKeyID key_id{uint160(sols[0])};
CPubKey pubkey;
if (GetPubKey(key_id, pubkey) && pubkey.IsCompressed() && HaveKey(key_id)) {
spks.insert(script);
spks.insert(GetScriptForDestination(ScriptHash(script)));
}
} else {
// Multisigs are special. They don't show up as ISMINE_SPENDABLE unless they are in a P2SH
// So check the P2SH of a multisig to see if we should insert it
std::vector<std::vector<unsigned char>> sols;
TxoutType type = Solver(script, sols);
if (type == TxoutType::MULTISIG) {
CScript ms_spk = GetScriptForDestination(ScriptHash(script));
if (IsMine(ms_spk) != ISMINE_NO) {
spks.insert(ms_spk);
break;
}
case TxoutType::MULTISIG:
{
// Multisigs are only spendable if we have all of their keys
std::vector<std::vector<unsigned char>> keys(sols.begin() + 1, sols.begin() + sols.size() - 1);
if (!HaveKeys(keys, *this)) {
break;
}
// Multisigs are always spendable inside of P2SH scripts
spks.insert(GetScriptForDestination(ScriptHash(script)));
// We need to have the P2WSH script for the P2WSH to be spendable.
// But the keys also must be compressed
CScript ms_wsh = GetScriptForDestination(WitnessV0ScriptHash(script));
if (HaveCScript(CScriptID(ms_wsh))) {
bool allowed = true;
for (const auto& key : keys) {
if (key.size() != 33) {
allowed = false;
break;
}
}
if (allowed) {
spks.insert(ms_wsh);
spks.insert(GetScriptForDestination(ScriptHash(ms_wsh)));
}
}
break;
}
}
}
// Iterate again for all the P2WSH scripts
for (const auto& script_pair : mapScripts) {
const CScript& script = script_pair.second;
std::vector<std::vector<unsigned char>> sols;
TxoutType type = Solver(script, sols);
if (type == TxoutType::WITNESS_V0_SCRIPTHASH) {
uint160 hash;
CRIPEMD160().Write(sols[0].data(), sols[0].size()).Finalize(hash.begin());
CScript witness_script;
int wit_ver = -1;
std::vector<unsigned char> wit_prog;
if (GetCScript(CScriptID(hash), witness_script) &&
!witness_script.IsPayToScriptHash() &&
!witness_script.IsWitnessProgram(wit_ver, wit_prog) &&
spks.count(witness_script) > 0) {
spks.insert(script);
spks.insert(GetScriptForDestination(ScriptHash(script)));
}
}
}

enum class ScriptContext {
TOP,
P2SH,
P2WSH,
};
std::function<bool(const CScript&, const ScriptContext)> is_valid_script = [&](const CScript& script, const ScriptContext ctx) -> bool {
std::vector<valtype> sols;
TxoutType spk_type = Solver(script, sols);

CKeyID keyID;
switch (spk_type) {
case TxoutType::NONSTANDARD:
case TxoutType::NULL_DATA:
case TxoutType::WITNESS_UNKNOWN:
case TxoutType::WITNESS_V1_TAPROOT:
return ctx == ScriptContext::TOP;
case TxoutType::PUBKEY:
if (ctx == ScriptContext::P2WSH && sols[0].size() != 33) return false;
return true;
case TxoutType::WITNESS_V0_KEYHASH:
return ctx != ScriptContext::P2WSH;
case TxoutType::PUBKEYHASH:
if (ctx == ScriptContext::P2WSH) {
CPubKey pubkey;
if (GetPubKey(CKeyID(uint160(sols[0])), pubkey) && !pubkey.IsCompressed()) {
return false;
}
}
return true;
case TxoutType::SCRIPTHASH:
{
if (ctx != ScriptContext::TOP) return false;
CScriptID script_id = CScriptID(uint160(sols[0]));
CScript subscript;
if (GetCScript(script_id, subscript)) {
return is_valid_script(subscript, ScriptContext::P2SH);
}
return true;
}
case TxoutType::WITNESS_V0_SCRIPTHASH:
{
if (ctx == ScriptContext::P2WSH) return false;
CScriptID script_id{RIPEMD160(sols[0])};
CScript subscript;
if (GetCScript(script_id, subscript)) {
return is_valid_script(subscript, ScriptContext::P2WSH);
}
return true;
}
case TxoutType::MULTISIG:
{
if (ctx == ScriptContext::P2WSH) {
std::vector<valtype> keys(sols.begin() + 1, sols.begin() + sols.size() - 1);
for (size_t i = 0; i < keys.size(); i++) {
if (keys[i].size() != 33) {
return false;
}
}
}
return true;
}
}
assert(false);
};
// All watchonly scripts are raw
for (const CScript& script : setWatchOnly) {
// As the legacy wallet allowed to import any script, we need to verify the validity here.
// LegacyScriptPubKeyMan::IsMine() return 'ISMINE_NO' for invalid or not watched scripts (IsMineResult::INVALID or IsMineResult::NO).
// e.g. a "sh(sh(pkh()))" which legacy wallets allowed to import!.
if (IsMine(script) != ISMINE_NO) spks.insert(script);
if (is_valid_script(script, ScriptContext::TOP)) spks.insert(script);
}

return spks;
Expand Down Expand Up @@ -1991,8 +2111,6 @@ std::optional<MigrationData> LegacyDataSPKM::MigrateToDescriptor()
// * The P2WSH script is in the wallet and it is being watched
std::vector<std::vector<unsigned char>> 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);
continue;
}
assert(IsMine(sh_spk) == ISMINE_NO && IsMine(witprog) == ISMINE_NO && IsMine(sh_wsh_spk) == ISMINE_NO);
Expand Down

0 comments on commit 8950371

Please sign in to comment.