diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -20,7 +20,8 @@ CWalletTx *wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx))); int nAge = 6 * 24; - COutput output(wtx, nInput, nAge, true, true); + COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, + true /* safe */); vCoins.push_back(output); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -547,8 +547,8 @@ if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, - true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, + true /* spendable */, true /* solvable */, true /* safe */); vOutputs.push_back(out); } } @@ -575,11 +575,12 @@ if (!wallet->mapWallet.count(outpoint.hash)) continue; int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, - true); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, + true /* spendable */, true /* solvable */, true /* safe */); if (outpoint.n < out.tx->tx->vout.size() && - wallet->IsMine(out.tx->tx->vout[outpoint.n]) == ISMINE_SPENDABLE) + wallet->IsMine(out.tx->tx->vout[outpoint.n]) == ISMINE_SPENDABLE) { vCoins.push_back(out); + } } for (const COutput &out : vCoins) { @@ -591,7 +592,9 @@ if (!wallet->mapWallet.count(cout.tx->tx->vin[0].prevout.hash)) break; cout = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], - cout.tx->tx->vin[0].prevout.n, 0, true, true); + cout.tx->tx->vin[0].prevout.n, 0 /* depth */, + true /* spendable */, true /* solvable */, + true /* safe */); } CTxDestination address; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3108,21 +3108,27 @@ " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction output " "amount in " + - CURRENCY_UNIT + "\n" - " \"confirmations\" : n, (numeric) The " - "number of confirmations\n" - " \"redeemScript\" : n (string) The " - "redeemScript if scriptPubKey is P2SH\n" - " \"spendable\" : xxx, (bool) Whether we " - "have the private keys to spend this output\n" - " \"solvable\" : xxx (bool) Whether we " - "know how to spend this output, ignoring the lack " - "of keys\n" - " }\n" - " ,...\n" - "]\n" + CURRENCY_UNIT + + "\n" + " \"confirmations\" : n, (numeric) The number of " + "confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if " + "scriptPubKey is P2SH\n" + " \"spendable\" : xxx, (bool) Whether we have the " + "private keys to spend this output\n" + " \"solvable\" : xxx, (bool) Whether we know how to " + "spend this output, ignoring the lack of keys\n" + " \"safe\" : xxx (bool) Whether this output is " + "considered safe to spend. Unconfirmed transactions\n" + " from outside keys are considered " + "unsafe and are not eligible for spending by\n" + " fundrawtransaction and " + "sendtoaddress.\n" + " }\n" + " ,...\n" + "]\n" - "\nExamples\n" + + "\nExamples\n" + HelpExampleCli("listunspent", "") + HelpExampleCli("listunspent", "6 9999999 " @@ -3222,6 +3228,7 @@ entry.push_back(Pair("confirmations", out.nDepth)); entry.push_back(Pair("spendable", out.fSpendable)); entry.push_back(Pair("solvable", out.fSolvable)); + entry.push_back(Pair("safe", out.fSafe)); results.push_back(entry); } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -58,7 +58,8 @@ wtx->fDebitCached = true; wtx->nDebitCached = Amount(1); } - COutput output(wtx.get(), nInput, nAge, true, true); + COutput output(wtx.get(), nInput, nAge, true /* spendable */, + true /* solvable */, true /* safe */); vCoins.push_back(output); wtxn.emplace_back(std::move(wtx)); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -464,16 +464,28 @@ const CWalletTx *tx; int i; int nDepth; + + /** Whether we have the private keys to spend this output */ bool fSpendable; + + /** Whether we know how to spend this output, ignoring the lack of keys */ bool fSolvable; + /** + * Whether this output is considered safe to spend. Unconfirmed transactions + * from outside keys are considered unsafe and will not be used to fund new + * spending transactions. + */ + bool fSafe; + COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, - bool fSolvableIn) { + bool fSolvableIn, bool fSafeIn) { tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; + fSafe = fSafeIn; } std::string ToString() const; @@ -755,8 +767,7 @@ /** * populate vCoins with vector of available COutputs. */ - void AvailableCoins(std::vector &vCoins, - bool fOnlyConfirmed = true, + void AvailableCoins(std::vector &vCoins, bool fOnlySafe = true, const CCoinControl *coinControl = nullptr, bool fIncludeZeroValue = false) const; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2251,7 +2251,7 @@ return nTotal; } -void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlyConfirmed, +void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const { vCoins.clear(); @@ -2266,10 +2266,6 @@ continue; } - if (fOnlyConfirmed && !pcoin->IsTrusted()) { - continue; - } - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) { continue; } @@ -2286,6 +2282,8 @@ continue; } + bool safeTx = pcoin->IsTrusted(); + // Bitcoin-ABC: Removed check that prevents consideration of coins from // transactions that are replacing other transactions. This check based // on pcoin->mapValue.count("replaces_txid") which was not being set @@ -2302,8 +2300,11 @@ // Bitcoin-ABC: retained this check as 'replaced_by_txid' is still set // in the wallet code. - if (nDepth == 0 && fOnlyConfirmed && - pcoin->mapValue.count("replaced_by_txid")) { + if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { + safeTx = false; + } + + if (fOnlySafe && !safeTx) { continue; } @@ -2321,7 +2322,8 @@ (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO), (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != - ISMINE_NO)); + ISMINE_NO, + safeTx)); } } }