Skip to content

Commit

Permalink
Add trusted flag to listunspent result
Browse files Browse the repository at this point in the history
Based on commit fe6cbed Add eligible flag to listunspent result (NicolasDorier)
from bitcoin#9830.
  • Loading branch information
ryanofsky committed Feb 23, 2017
1 parent bed5b30 commit f4cace4
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 20 deletions.
4 changes: 4 additions & 0 deletions qa/rpc-tests/listtransactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ def get_unconfirmed_utxo_entry(node, txid_to_match):
assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"})

# Tx2 will build off txid_1, still not opting in to RBF.
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_1)
assert_equal(utxo_to_use["safe"], True)
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_1)
utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_1)
assert_equal(utxo_to_use["safe"], False)

# Create tx2 using createrawtransaction
inputs = [{"txid":utxo_to_use["txid"], "vout":utxo_to_use["vout"]}]
Expand Down
2 changes: 1 addition & 1 deletion src/bench/coin_selection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand All @@ -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);
}
Expand All @@ -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;
Expand Down
10 changes: 6 additions & 4 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2405,9 +2405,7 @@ UniValue listunspent(const JSONRPCRequest& request)
" ,...\n"
" ]\n"
"4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
" because they come from unconfirmed untrusted transactions or unconfirmed\n"
" replacement transactions (cases where we are less sure that a conflicting\n"
" transaction won't be mined).\n"
" See description of \"safe\" attribute below.\n"
"\nResult\n"
"[ (array of json object)\n"
" {\n"
Expand All @@ -2420,7 +2418,10 @@ UniValue listunspent(const JSONRPCRequest& request)
" \"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"
" \"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 and unconfirmed replacement transactions are considered unsafe\n"
" and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
" }\n"
" ,...\n"
"]\n"
Expand Down Expand Up @@ -2503,6 +2504,7 @@ UniValue listunspent(const JSONRPCRequest& request)
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);
}

Expand Down
2 changes: 1 addition & 1 deletion src/wallet/test/wallet_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,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));
}
Expand Down
19 changes: 11 additions & 8 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2019,7 +2019,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
return nTotal;
}

void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const
void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const
{
vCoins.clear();

Expand All @@ -2033,9 +2033,6 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (!CheckFinalTx(*pcoin))
continue;

if (fOnlyConfirmed && !pcoin->IsTrusted())
continue;

if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue;

Expand All @@ -2048,6 +2045,8 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (nDepth == 0 && !pcoin->InMempool())
continue;

bool safeTx = pcoin->IsTrusted();

// We should not consider coins from transactions that are replacing
// other transactions.
//
Expand All @@ -2063,8 +2062,8 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
// 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
Expand All @@ -2075,7 +2074,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
// 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;
}

Expand All @@ -2087,7 +2090,7 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
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));
}
}
}
Expand Down
17 changes: 14 additions & 3 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,23 @@ class COutput
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;
Expand Down Expand Up @@ -712,7 +723,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
/**
* 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
Expand Down

0 comments on commit f4cace4

Please sign in to comment.