diff --git a/src/script/ismine.h b/src/script/ismine.h --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -16,14 +16,8 @@ /** IsMine() return codes */ enum isminetype { ISMINE_NO = 0, - //! Indicates that we don't know how to create a scriptSig that would solve - //! this if we were given the appropriate private keys - ISMINE_WATCH_UNSOLVABLE = 1, - //! Indicates that we know how to create a scriptSig that would solve this - //! if we were given the appropriate private keys - ISMINE_WATCH_SOLVABLE = 2, - ISMINE_WATCH_ONLY = ISMINE_WATCH_SOLVABLE | ISMINE_WATCH_UNSOLVABLE, - ISMINE_SPENDABLE = 4, + ISMINE_WATCH_ONLY = 1, + ISMINE_SPENDABLE = 2, ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE }; diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -26,6 +26,18 @@ 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 &pubkeys, const CKeyStore &keystore) { for (const valtype &pubkey : pubkeys) { CKeyID keyID = CPubKey(pubkey).GetID(); @@ -36,17 +48,13 @@ return true; } -isminetype IsMineInner(const CKeyStore &keystore, const CScript &scriptPubKey, - bool &isInvalid, IsMineSigVersion sigversion) { - isInvalid = false; +IsMineResult IsMineInner(const CKeyStore &keystore, const CScript &scriptPubKey, + IsMineSigVersion sigversion) { + IsMineResult ret = IsMineResult::NO; std::vector vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { - if (keystore.HaveWatchOnly(scriptPubKey)) - return ISMINE_WATCH_UNSOLVABLE; - return ISMINE_NO; - } + Solver(scriptPubKey, whichType, vSolutions); CKeyID keyID; switch (whichType) { @@ -55,21 +63,26 @@ break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; + if (keystore.HaveKey(keyID)) { + ret = std::max(ret, IsMineResult::SPENDABLE); + } break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; + if (keystore.HaveKey(keyID)) { + ret = std::max(ret, IsMineResult::SPENDABLE); + } break; case TX_SCRIPTHASH: { + if (sigversion != IsMineSigVersion::TOP) { + // P2SH inside P2SH is invalid. + return IsMineResult::INVALID; + } CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMineInner(keystore, subscript, isInvalid, - IsMineSigVersion::P2SH); - if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || - (ret == ISMINE_NO && isInvalid)) - return ret; + ret = std::max(ret, IsMineInner(keystore, subscript, + IsMineSigVersion::P2SH)); } break; } @@ -89,30 +102,34 @@ vSolutions.begin() + vSolutions.size() - 1); if (HaveKeys(keys, keystore)) { - return ISMINE_SPENDABLE; + ret = std::max(ret, IsMineResult::SPENDABLE); } break; } } - if (keystore.HaveWatchOnly(scriptPubKey)) { - // TODO: This could be optimized some by doing some work after the above - // solver - SignatureData sigs; - return ProduceSignature(keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, - sigs) - ? ISMINE_WATCH_SOLVABLE - : ISMINE_WATCH_UNSOLVABLE; + if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) { + ret = std::max(ret, IsMineResult::WATCH_ONLY); } - return ISMINE_NO; + return ret; } } // namespace isminetype IsMine(const CKeyStore &keystore, const CScript &scriptPubKey, bool &isInvalid) { - return IsMineInner(keystore, scriptPubKey, isInvalid, - IsMineSigVersion::TOP); + isInvalid = false; + 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) { diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -19,8 +19,7 @@ //! If false, allows unselected inputs, but requires all selected inputs be //! used bool fAllowOtherInputs; - //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE - //! criteria + //! Includes watch only addresses which are solvable bool fAllowWatchOnly; //! Override automatic min/max checks on fee, m_feerate must be set if true bool fOverrideFeeRate; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2492,15 +2492,14 @@ continue; } - bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || - (coinControl && coinControl->fAllowWatchOnly && - (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); - bool fSolvableIn = - (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != - ISMINE_NO; + bool solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey); + bool spendable = + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && + (coinControl && coinControl->fAllowWatchOnly && solvable)); vCoins.push_back( - COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx, + COutput(pcoin, i, nDepth, spendable, solvable, safeTx, (coinControl && coinControl->fAllowWatchOnly))); // Checks the sum amount of all UTXO's.