Changeset View
Changeset View
Standalone View
Standalone View
src/script/ismine.cpp
Show All 20 Lines | |||||
* distinguish between top-level scriptPubKey execution and P2SH redeemScript | * distinguish between top-level scriptPubKey execution and P2SH redeemScript | ||||
* execution (a distinction that has no impact on consensus rules). | * execution (a distinction that has no impact on consensus rules). | ||||
*/ | */ | ||||
enum class IsMineSigVersion { | enum class IsMineSigVersion { | ||||
TOP = 0, //! scriptPubKey execution | TOP = 0, //! scriptPubKey execution | ||||
P2SH = 1, //! P2SH redeemScript | P2SH = 1, //! P2SH redeemScript | ||||
}; | }; | ||||
/** | |||||
* This is an internal representation of isminetype + invalidity. | |||||
* Its order is significant, as we return the max of all explored | |||||
* possibilities. | |||||
*/ | |||||
enum class IsMineResult { | |||||
NO = 0, //! Not ours | |||||
WATCH_ONLY = 1, //! Included in watch-only balance | |||||
SPENDABLE = 2, //! Included in all balances | |||||
INVALID = 3, //! Not spendable by anyone | |||||
}; | |||||
bool HaveKeys(const std::vector<valtype> &pubkeys, const CKeyStore &keystore) { | bool HaveKeys(const std::vector<valtype> &pubkeys, const CKeyStore &keystore) { | ||||
for (const valtype &pubkey : pubkeys) { | for (const valtype &pubkey : pubkeys) { | ||||
CKeyID keyID = CPubKey(pubkey).GetID(); | CKeyID keyID = CPubKey(pubkey).GetID(); | ||||
if (!keystore.HaveKey(keyID)) { | if (!keystore.HaveKey(keyID)) { | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
isminetype IsMineInner(const CKeyStore &keystore, const CScript &scriptPubKey, | IsMineResult IsMineInner(const CKeyStore &keystore, const CScript &scriptPubKey, | ||||
bool &isInvalid, IsMineSigVersion sigversion) { | IsMineSigVersion sigversion) { | ||||
isInvalid = false; | IsMineResult ret = IsMineResult::NO; | ||||
std::vector<valtype> vSolutions; | std::vector<valtype> vSolutions; | ||||
txnouttype whichType; | txnouttype whichType; | ||||
if (!Solver(scriptPubKey, whichType, vSolutions)) { | Solver(scriptPubKey, whichType, vSolutions); | ||||
if (keystore.HaveWatchOnly(scriptPubKey)) | |||||
return ISMINE_WATCH_UNSOLVABLE; | |||||
return ISMINE_NO; | |||||
} | |||||
CKeyID keyID; | CKeyID keyID; | ||||
switch (whichType) { | switch (whichType) { | ||||
case TX_NONSTANDARD: | case TX_NONSTANDARD: | ||||
case TX_NULL_DATA: | case TX_NULL_DATA: | ||||
break; | break; | ||||
case TX_PUBKEY: | case TX_PUBKEY: | ||||
keyID = CPubKey(vSolutions[0]).GetID(); | keyID = CPubKey(vSolutions[0]).GetID(); | ||||
if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; | if (keystore.HaveKey(keyID)) { | ||||
ret = std::max(ret, IsMineResult::SPENDABLE); | |||||
} | |||||
break; | break; | ||||
case TX_PUBKEYHASH: | case TX_PUBKEYHASH: | ||||
keyID = CKeyID(uint160(vSolutions[0])); | keyID = CKeyID(uint160(vSolutions[0])); | ||||
if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; | if (keystore.HaveKey(keyID)) { | ||||
ret = std::max(ret, IsMineResult::SPENDABLE); | |||||
} | |||||
break; | break; | ||||
case TX_SCRIPTHASH: { | case TX_SCRIPTHASH: { | ||||
if (sigversion != IsMineSigVersion::TOP) { | |||||
// P2SH inside P2SH is invalid. | |||||
return IsMineResult::INVALID; | |||||
} | |||||
CScriptID scriptID = CScriptID(uint160(vSolutions[0])); | CScriptID scriptID = CScriptID(uint160(vSolutions[0])); | ||||
CScript subscript; | CScript subscript; | ||||
if (keystore.GetCScript(scriptID, subscript)) { | if (keystore.GetCScript(scriptID, subscript)) { | ||||
isminetype ret = IsMineInner(keystore, subscript, isInvalid, | ret = std::max(ret, IsMineInner(keystore, subscript, | ||||
IsMineSigVersion::P2SH); | IsMineSigVersion::P2SH)); | ||||
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || | |||||
(ret == ISMINE_NO && isInvalid)) | |||||
return ret; | |||||
} | } | ||||
break; | break; | ||||
} | } | ||||
case TX_MULTISIG: { | case TX_MULTISIG: { | ||||
// Never treat bare multisig outputs as ours (they can still be made | // Never treat bare multisig outputs as ours (they can still be made | ||||
// watchonly-though) | // watchonly-though) | ||||
if (sigversion == IsMineSigVersion::TOP) { | if (sigversion == IsMineSigVersion::TOP) { | ||||
break; | break; | ||||
} | } | ||||
// Only consider transactions "mine" if we own ALL the keys | // Only consider transactions "mine" if we own ALL the keys | ||||
// involved. Multi-signature transactions that are partially owned | // involved. Multi-signature transactions that are partially owned | ||||
// (somebody else has a key that can spend them) enable | // (somebody else has a key that can spend them) enable | ||||
// spend-out-from-under-you attacks, especially in shared-wallet | // spend-out-from-under-you attacks, especially in shared-wallet | ||||
// situations. | // situations. | ||||
std::vector<valtype> keys(vSolutions.begin() + 1, | std::vector<valtype> keys(vSolutions.begin() + 1, | ||||
vSolutions.begin() + vSolutions.size() - | vSolutions.begin() + vSolutions.size() - | ||||
1); | 1); | ||||
if (HaveKeys(keys, keystore)) { | if (HaveKeys(keys, keystore)) { | ||||
return ISMINE_SPENDABLE; | ret = std::max(ret, IsMineResult::SPENDABLE); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (keystore.HaveWatchOnly(scriptPubKey)) { | if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) { | ||||
// TODO: This could be optimized some by doing some work after the above | ret = std::max(ret, IsMineResult::WATCH_ONLY); | ||||
// solver | |||||
SignatureData sigs; | |||||
return ProduceSignature(keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, | |||||
sigs) | |||||
? ISMINE_WATCH_SOLVABLE | |||||
: ISMINE_WATCH_UNSOLVABLE; | |||||
} | } | ||||
return ISMINE_NO; | return ret; | ||||
} | } | ||||
} // namespace | } // namespace | ||||
isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey, | isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey, | ||||
bool &isInvalid) { | bool &isInvalid) { | ||||
return IsMineInner(keystore, scriptPubKey, isInvalid, | isInvalid = false; | ||||
IsMineSigVersion::TOP); | switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) { | ||||
case IsMineResult::INVALID: | |||||
isInvalid = true; | |||||
case IsMineResult::NO: | |||||
return ISMINE_NO; | |||||
case IsMineResult::WATCH_ONLY: | |||||
return ISMINE_WATCH_ONLY; | |||||
case IsMineResult::SPENDABLE: | |||||
return ISMINE_SPENDABLE; | |||||
} | |||||
assert(false); | |||||
} | } | ||||
isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey) { | isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey) { | ||||
bool isInvalid = false; | bool isInvalid = false; | ||||
return IsMine(keystore, scriptPubKey, isInvalid); | return IsMine(keystore, scriptPubKey, isInvalid); | ||||
} | } | ||||
isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest) { | isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest) { | ||||
CScript script = GetScriptForDestination(dest); | CScript script = GetScriptForDestination(dest); | ||||
return IsMine(keystore, script); | return IsMine(keystore, script); | ||||
} | } |