aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRussell Yanofsky <russ@yanofsky.org>2017-02-23 11:20:16 -0500
committerNicolasDorier <nicolas.dorier@gmail.com>2017-03-10 05:11:10 +0000
commitaf61d9f78bec62ff3688d88409a53df9ff5bc591 (patch)
tree4d808ec76cd86bbeaeab7473a6527b9d8aeacfaf /src
parent8910b4717e5bb946ee6988f7fe9fd461f53a5935 (diff)
Add COutput::fSafe member for safe handling of unconfirmed outputs
This exposes a value computed in CWallet::AvailableCoins so it can used for other things, like inclusion in listunspent output.
Diffstat (limited to 'src')
-rw-r--r--src/bench/coin_selection.cpp2
-rw-r--r--src/qt/walletmodel.cpp6
-rw-r--r--src/wallet/test/wallet_tests.cpp2
-rw-r--r--src/wallet/wallet.cpp19
-rw-r--r--src/wallet/wallet.h17
5 files changed, 30 insertions, 16 deletions
diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp
index 29fbd34631..06882f1514 100644
--- a/src/bench/coin_selection.cpp
+++ b/src/bench/coin_selection.cpp
@@ -20,7 +20,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<CO
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
index 0a5a7c3e9f..878b7d58ae 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -580,7 +580,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect
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);
}
}
@@ -607,7 +607,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
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)
vCoins.push_back(out);
}
@@ -619,7 +619,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
while (wallet->IsChange(cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0]))
{
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 = COutput(&wallet->mapWallet[cout.tx->tx->vin[0].prevout.hash], cout.tx->tx->vin[0].prevout.n, 0 /* depth */, true /* spendable */, true /* solvable */, true /* safe */);
}
CTxDestination address;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index c94491ca21..67e5e90224 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -54,7 +54,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
wtx->fDebitCached = true;
wtx->nDebitCached = 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.cpp b/src/wallet/wallet.cpp
index 965fba67fc..9e3c8be3f2 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1946,7 +1946,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
return nTotal;
}
-void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const
+void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const
{
vCoins.clear();
@@ -1960,9 +1960,6 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed,
if (!CheckFinalTx(*pcoin))
continue;
- if (fOnlyConfirmed && !pcoin->IsTrusted())
- continue;
-
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;
@@ -1975,6 +1972,8 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed,
if (nDepth == 0 && !pcoin->InMempool())
continue;
+ bool safeTx = pcoin->IsTrusted();
+
// We should not consider coins from transactions that are replacing
// other transactions.
//
@@ -1990,8 +1989,8 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed,
// be a 1-block reorg away from the chain where transactions A and C
// were accepted to another chain where B, B', and C were all
// accepted.
- if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) {
- continue;
+ if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) {
+ safeTx = false;
}
// Similarly, we should not consider coins from transactions that
@@ -2002,7 +2001,11 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed,
// intending to replace A', but potentially resulting in a scenario
// where A, A', and D could all be accepted (instead of just B and
// D, or just A and A' like the user would want).
- 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;
}
@@ -2014,7 +2017,7 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed,
vCoins.push_back(COutput(pcoin, i, nDepth,
((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
(coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO),
- (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO));
+ (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO, safeTx));
}
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index cae92a0b09..80201e8ce0 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -466,12 +466,23 @@ public:
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;
- COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn)
+ /**
+ * Whether this output is considered safe to spend. Unconfirmed transactions
+ * from outside keys and unconfirmed replacement transactions 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 fSafeIn)
{
- tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn;
+ tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn;
}
std::string ToString() const;
@@ -740,7 +751,7 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const;
+ void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const;
/**
* Shuffle and select coins until nTargetValue is reached while avoiding