Skip to content

Commit

Permalink
[qt] Move some WalletModel functions into CWallet
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
ryanofsky committed Apr 28, 2017
1 parent fc8342f commit c59ffa1
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 49 deletions.
54 changes: 7 additions & 47 deletions src/qt/walletmodel.cpp
Expand Up @@ -63,14 +63,7 @@ CAmount WalletModel::getBalance(const CCoinControl *coinControl) const
{
if (coinControl)
{
CAmount nBalance = 0;
std::vector<COutput> vCoins;
wallet->AvailableCoins(vCoins, true, coinControl);
BOOST_FOREACH(const COutput& out, vCoins)
if(out.fSpendable)
nBalance += out.tx->tx->vout[out.i].nValue;

return nBalance;
return wallet->GetAvailableBalance(coinControl);
}

return wallet->GetBalance();
Expand Down Expand Up @@ -596,37 +589,11 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const
void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const
{
std::vector<COutput> vCoins;
wallet->AvailableCoins(vCoins);

LOCK2(cs_main, wallet->cs_wallet); // ListLockedCoins, mapWallet
std::vector<COutPoint> vLockedCoins;
wallet->ListLockedCoins(vLockedCoins);

// add locked coins
BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins)
{
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 /* 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);
}

BOOST_FOREACH(const COutput& out, vCoins)
{
COutput cout = out;

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 /* depth */, true /* spendable */, true /* solvable */, true /* safe */);
for (auto& group : wallet->ListCoins()) {
auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())];
for (auto& coin : group.second) {
resultGroup.emplace_back(std::move(coin));
}

CTxDestination address;
if(!out.fSpendable || !ExtractDestination(cout.tx->tx->vout[cout.i].scriptPubKey, address))
continue;
mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out);
}
}

Expand Down Expand Up @@ -657,10 +624,7 @@ void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests)
{
LOCK(wallet->cs_wallet);
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
BOOST_FOREACH(const PAIRTYPE(std::string, std::string)& item2, item.second.destdata)
if (item2.first.size() > 2 && item2.first.substr(0,2) == "rr") // receive request
vReceiveRequests.push_back(item2.second);
vReceiveRequests = wallet->GetDestValues("rr"); // receive request
}

bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
Expand All @@ -680,11 +644,7 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t

bool WalletModel::transactionCanBeAbandoned(uint256 hash) const
{
LOCK2(cs_main, wallet->cs_wallet);
const CWalletTx *wtx = wallet->GetWalletTx(hash);
if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->InMempool())
return false;
return true;
return wallet->TransactionCanBeAbandoned(hash);
}

bool WalletModel::abandonTransaction(uint256 hash) const
Expand Down
88 changes: 87 additions & 1 deletion src/wallet/wallet.cpp
Expand Up @@ -985,6 +985,13 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, const CBlockI
return false;
}

bool CWallet::TransactionCanBeAbandoned(const uint256& hashTx) const
{
LOCK2(cs_main, cs_wallet);
const CWalletTx* wtx = GetWalletTx(hashTx);
return wtx && !wtx->isAbandoned() && wtx->GetDepthInMainChain() <= 0 && !wtx->InMempool();
}

bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
Expand Down Expand Up @@ -1983,6 +1990,19 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons
return balance;
}

CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const
{
CAmount balance = 0;
std::vector<COutput> vCoins;
AvailableCoins(vCoins, true, coinControl);
for (const COutput& out : vCoins) {
if (out.fSpendable) {
balance += out.tx->tx->vout[out.i].nValue;
}
}
return balance;
}

void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const CCoinControl *coinControl, bool fIncludeZeroValue) const
{
vCoins.clear();
Expand Down Expand Up @@ -2060,6 +2080,59 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
}
}

std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const
{
std::map<CTxDestination, std::vector<COutput>> result;

std::vector<COutput> availableCoins;
AvailableCoins(availableCoins);

LOCK2(cs_main, cs_wallet);
for (auto& coin : availableCoins) {
CTxDestination address;
if (coin.fSpendable &&
ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) {
result[address].emplace_back(std::move(coin));
}
}

std::vector<COutPoint> lockedCoins;
ListLockedCoins(lockedCoins);
for (const auto& output : lockedCoins) {
auto it = mapWallet.find(output.hash);
if (it != mapWallet.end()) {
int depth = it->second.GetDepthInMainChain();
if (depth >= 0 && output.n < it->second.tx->vout.size() &&
IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
CTxDestination address;
if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) {
result[address].emplace_back(
&it->second, output.n, depth, true /* spendable */, true /* solvable */, false /* safe */);
}
}
}
}

return result;
}

const CTxOut& CWallet::FindNonChangeParentOutput(const CTransaction& tx, int output) const
{
const CTransaction* ptx = &tx;
int n = output;
while (IsChange(ptx->vout[n]) && ptx->vin.size() > 0) {
const COutPoint& prevout = ptx->vin[0].prevout;
auto it = mapWallet.find(prevout.hash);
if (it == mapWallet.end() || it->second.tx->vout.size() <= prevout.n ||
!IsMine(it->second.tx->vout[prevout.n])) {
break;
}
ptx = it->second.tx.get();
n = prevout.n;
}
return ptx->vout[n];
}

static void ApproximateBestSubset(const std::vector<CInputCoin>& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,
std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)
{
Expand Down Expand Up @@ -3379,7 +3452,7 @@ bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
return (setLockedCoins.count(outpt) > 0);
}

void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts)
void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) const
{
AssertLockHeld(cs_wallet); // setLockedCoins
for (std::set<COutPoint>::iterator it = setLockedCoins.begin();
Expand Down Expand Up @@ -3580,6 +3653,19 @@ bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, st
return false;
}

std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
{
std::vector<std::string> values;
for (const auto& address : mapAddressBook) {
for (const auto& data : address.second.destdata) {
if (!data.first.compare(0, prefix.size(), prefix)) {
values.emplace_back(data.second);
}
}
}
return values;
}

std::string CWallet::GetWalletHelpString(bool showDebug)
{
std::string strUsage = HelpMessageGroup(_("Wallet options:"));
Expand Down
18 changes: 17 additions & 1 deletion src/wallet/wallet.h
Expand Up @@ -819,6 +819,16 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
*/
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const;

/**
* Return list of available coins and locked coins grouped by non-change output address.
*/
std::map<CTxDestination, std::vector<COutput>> ListCoins() const;

/**
* Find non-change parent output.
*/
const CTxOut& FindNonChangeParentOutput(const CTransaction& tx, int output) const;

/**
* Shuffle and select coins until nTargetValue is reached while avoiding
* small change; This method is stochastic for some inputs and upon
Expand All @@ -833,7 +843,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
void LockCoin(const COutPoint& output);
void UnlockCoin(const COutPoint& output);
void UnlockAllCoins();
void ListLockedCoins(std::vector<COutPoint>& vOutpts);
void ListLockedCoins(std::vector<COutPoint>& vOutpts) const;

/*
* Rescan abort properties
Expand Down Expand Up @@ -872,6 +882,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
bool LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value);
//! Look up a destination data tuple in the store, return true if found false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
//! Get all destination values matching a prefix.
std::vector<std::string> GetDestValues(const std::string& prefix) const;

//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript& dest, int64_t nCreateTime);
Expand Down Expand Up @@ -916,6 +928,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
CAmount GetUnconfirmedWatchOnlyBalance() const;
CAmount GetImmatureWatchOnlyBalance() const;
CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;

/**
* Insert additional inputs into the transaction by
Expand Down Expand Up @@ -1065,6 +1078,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
/** Set whether this wallet broadcasts transactions. */
void SetBroadcastTransactions(bool broadcast) { fBroadcastTransactions = broadcast; }

/** Return whether transaction can be abandoned */
bool TransactionCanBeAbandoned(const uint256& hashTx) const;

/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
bool AbandonTransaction(const uint256& hashTx);

Expand Down

0 comments on commit c59ffa1

Please sign in to comment.