Skip to content

Commit d944bd7

Browse files
committed
[qt] Move some WalletModel functions into CWallet
Motivation for moving these is to make supporting IPC simpler (#10102), so these lookups can be one-shot IPC requests, instead of back-and-forth interactions over the IPC channel. Also these functions are potentially useful outside of the bitcoin GUI (e.g. for RPCs).
1 parent ef8ca17 commit d944bd7

File tree

3 files changed

+122
-51
lines changed

3 files changed

+122
-51
lines changed

src/qt/walletmodel.cpp

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,7 @@ CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
6363
{
6464
if (coinControl)
6565
{
66-
CAmount nBalance = 0;
67-
std::vector<COutput> vCoins;
68-
wallet->AvailableCoins(vCoins, true, coinControl);
69-
BOOST_FOREACH(const COutput& out, vCoins)
70-
if(out.fSpendable)
71-
nBalance += out.tx->tx->vout[out.i].nValue;
72-
73-
return nBalance;
66+
return wallet->GetAvailableBalance(coinControl);
7467
}
7568

7669
return wallet->GetBalance();
@@ -595,38 +588,11 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const
595588
// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address)
596589
void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
597590
{
598-
std::vector<COutput> vCoins;
599-
wallet->AvailableCoins(vCoins);
600-
601-
LOCK2(cs_main, wallet->cs_wallet); // ListLockedCoins, mapWallet
602-
std::vector<COutPoint> vLockedCoins;
603-
wallet->ListLockedCoins(vLockedCoins);
604-
605-
// add locked coins
606-
BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins)
607-
{
608-
if (!wallet->mapWallet.count(outpoint.hash)) continue;
609-
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
610-
if (nDepth < 0) continue;
611-
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */);
612-
if (outpoint.n < out.tx->tx->vout.size() && wallet->IsMine(out.tx->tx->vout[outpoint.n]) == ISMINE_SPENDABLE)
613-
vCoins.push_back(out);
614-
}
615-
616-
BOOST_FOREACH(const COutput& out, vCoins)
617-
{
618-
COutput cout = out;
619-
620-
while (wallet->IsChange(cout.tx->tx->vout[cout.i]) && cout.tx->tx->vin.size() > 0 && wallet->IsMine(cout.tx->tx->vin[0]))
621-
{
622-
if (!wallet->mapWallet.count(cout.tx->tx->vin[0].prevout.hash)) break;
623-
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 */);
591+
for (auto& group : wallet->ListCoins()) {
592+
auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())];
593+
for (auto& coin : group.second) {
594+
resultGroup.emplace_back(std::move(coin));
624595
}
625-
626-
CTxDestination address;
627-
if(!out.fSpendable || !ExtractDestination(cout.tx->tx->vout[cout.i].scriptPubKey, address))
628-
continue;
629-
mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out);
630596
}
631597
}
632598

@@ -656,11 +622,7 @@ void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
656622

657623
void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
658624
{
659-
LOCK(wallet->cs_wallet);
660-
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
661-
BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item2, item.second.destdata)
662-
if (item2.first.size() > 2 && item2.first.substr(0,2) == "rr") // receive request
663-
vReceiveRequests.push_back(item2.second);
625+
vReceiveRequests = wallet->GetDestValues("rr"); // receive request
664626
}
665627

666628
bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
@@ -680,11 +642,7 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t
680642

681643
bool WalletModel::transactionCanBeAbandoned(uint256 hash) const
682644
{
683-
LOCK2(cs_main, wallet->cs_wallet);
684-
const CWalletTx *wtx = wallet->GetWalletTx(hash);
685-
if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->InMempool())
686-
return false;
687-
return true;
645+
return wallet->TransactionCanBeAbandoned(hash);
688646
}
689647

690648
bool WalletModel::abandonTransaction(uint256 hash) const

src/wallet/wallet.cpp

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,13 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
982982
return false;
983983
}
984984

985+
bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
986+
{
987+
LOCK2(cs_main, cs_wallet);
988+
const CWalletTx* wtx = GetWalletTx(hashTx);
989+
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() <= 0 && !wtx->InMempool();
990+
}
991+
985992
bool CWallet::AbandonTransaction(const uint256& hashTx)
986993
{
987994
LOCK2(cs_main, cs_wallet);
@@ -1977,6 +1984,19 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons
19771984
return balance;
19781985
}
19791986

1987+
CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
1988+
{
1989+
CAmount balance = 0;
1990+
std::vector<COutput> vCoins;
1991+
AvailableCoins(vCoins, true, coinControl);
1992+
for (const COutput& out : vCoins) {
1993+
if (out.fSpendable) {
1994+
balance += out.tx->tx->vout[out.i].nValue;
1995+
}
1996+
}
1997+
return balance;
1998+
}
1999+
19802000
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const
19812001
{
19822002
vCoins.clear();
@@ -2088,6 +2108,69 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const
20882108
}
20892109
}
20902110

2111+
std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
2112+
{
2113+
// TODO: Add AssertLockHeld(cs_wallet) here.
2114+
//
2115+
// Because the return value from this function contains pointers to
2116+
// CWalletTx objects, callers to this function really should acquire the
2117+
// cs_wallet lock before calling it. However, the current caller doesn't
2118+
// acquire this lock yet. There was an attempt to add the missing lock in
2119+
// https://github.com/bitcoin/bitcoin/pull/10340, but that change has been
2120+
// postponed until after https://github.com/bitcoin/bitcoin/pull/10244 to
2121+
// avoid adding some extra complexity to the Qt code.
2122+
2123+
std::map<CTxDestination, std::vector<COutput>> result;
2124+
2125+
std::vector<COutput> availableCoins;
2126+
AvailableCoins(availableCoins);
2127+
2128+
LOCK2(cs_main, cs_wallet);
2129+
for (auto& coin : availableCoins) {
2130+
CTxDestination address;
2131+
if (coin.fSpendable &&
2132+
ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) {
2133+
result[address].emplace_back(std::move(coin));
2134+
}
2135+
}
2136+
2137+
std::vector<COutPoint> lockedCoins;
2138+
ListLockedCoins(lockedCoins);
2139+
for (const auto& output : lockedCoins) {
2140+
auto it = mapWallet.find(output.hash);
2141+
if (it != mapWallet.end()) {
2142+
int depth = it->second.GetDepthInMainChain();
2143+
if (depth >= 0 && output.n < it->second.tx->vout.size() &&
2144+
IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
2145+
CTxDestination address;
2146+
if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) {
2147+
result[address].emplace_back(
2148+
&it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
2149+
}
2150+
}
2151+
}
2152+
}
2153+
2154+
return result;
2155+
}
2156+
2157+
const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const
2158+
{
2159+
const CTransaction* ptx = &tx;
2160+
int n = output;
2161+
while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
2162+
const COutPoint& prevout = ptx->vin[0].prevout;
2163+
auto it = mapWallet.find(prevout.hash);
2164+
if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n ||
2165+
!IsMine(it->second.tx->vout[prevout.n])) {
2166+
break;
2167+
}
2168+
ptx = it->second.tx.get();
2169+
n = prevout.n;
2170+
}
2171+
return ptx->vout[n];
2172+
}
2173+
20912174
static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
20922175
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
20932176
{
@@ -3408,7 +3491,7 @@ bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
34083491
return (setLockedCoins.count(outpt) > 0);
34093492
}
34103493

3411-
void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts)
3494+
void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
34123495
{
34133496
AssertLockHeld(cs_wallet); // setLockedCoins
34143497
for (std::set<COutPoint>::iterator it = setLockedCoins.begin();
@@ -3609,6 +3692,20 @@ bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, st
36093692
return false;
36103693
}
36113694

3695+
std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
3696+
{
3697+
LOCK(cs_wallet);
3698+
std::vector<std::string> values;
3699+
for (const auto& address : mapAddressBook) {
3700+
for (const auto& data : address.second.destdata) {
3701+
if (!data.first.compare(0, prefix.size(), prefix)) {
3702+
values.emplace_back(data.second);
3703+
}
3704+
}
3705+
}
3706+
return values;
3707+
}
3708+
36123709
std::string CWallet::GetWalletHelpString(bool showDebug)
36133710
{
36143711
std::string strUsage = HelpMessageGroup(_("Wallet options:"));

src/wallet/wallet.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,16 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
820820
*/
821821
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 9999999) const;
822822

823+
/**
824+
* Return list of available coins and locked coins grouped by non-change output address.
825+
*/
826+
std::map<CTxDestination, std::vector<COutput>> ListCoins() const;
827+
828+
/**
829+
* Find non-change parent output.
830+
*/
831+
const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const;
832+
823833
/**
824834
* Shuffle and select coins until nTargetValue is reached while avoiding
825835
* small change; This method is stochastic for some inputs and upon
@@ -834,7 +844,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
834844
void LockCoin(const COutPoint& output);
835845
void UnlockCoin(const COutPoint& output);
836846
void UnlockAllCoins();
837-
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
847+
void ListLockedCoins(std::vector<COutPoint>& vOutpts) const;
838848

839849
/*
840850
* Rescan abort properties
@@ -873,6 +883,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
873883
bool LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
874884
//! Look up a destination data tuple in the store, return true if found false otherwise
875885
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
886+
//! Get all destination values matching a prefix.
887+
std::vector<std::string> GetDestValues(const std::string& prefix) const;
876888

877889
//! Adds a watch-only address to the store, and saves it to disk.
878890
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime);
@@ -917,6 +929,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
917929
CAmount GetUnconfirmedWatchOnlyBalance() const;
918930
CAmount GetImmatureWatchOnlyBalance() const;
919931
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
932+
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
920933

921934
/**
922935
* Insert additional inputs into the transaction by
@@ -1066,6 +1079,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
10661079
/** Set whether this wallet broadcasts transactions. */
10671080
void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; }
10681081

1082+
/** Return whether transaction can be abandoned */
1083+
bool TransactionCanBeAbandoned(const uint256& hashTx) const;
1084+
10691085
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
10701086
bool AbandonTransaction(const uint256& hashTx);
10711087

0 commit comments

Comments
 (0)