Branch data Line data Source code
# 1 : : // Copyright (c) 2021 The Bitcoin Core developers
# 2 : : // Distributed under the MIT software license, see the accompanying
# 3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
# 4 : :
# 5 : : #include <consensus/amount.h>
# 6 : : #include <consensus/consensus.h>
# 7 : : #include <wallet/receive.h>
# 8 : : #include <wallet/transaction.h>
# 9 : : #include <wallet/wallet.h>
# 10 : :
# 11 : : namespace wallet {
# 12 : : isminetype InputIsMine(const CWallet& wallet, const CTxIn &txin)
# 13 : 520 : {
# 14 : 520 : AssertLockHeld(wallet.cs_wallet);
# 15 : 520 : std::map<uint256, CWalletTx>::const_iterator mi = wallet.mapWallet.find(txin.prevout.hash);
# 16 [ + + ]: 520 : if (mi != wallet.mapWallet.end())
# 17 : 7 : {
# 18 : 7 : const CWalletTx& prev = (*mi).second;
# 19 [ + - ]: 7 : if (txin.prevout.n < prev.tx->vout.size())
# 20 : 7 : return wallet.IsMine(prev.tx->vout[txin.prevout.n]);
# 21 : 7 : }
# 22 : 513 : return ISMINE_NO;
# 23 : 520 : }
# 24 : :
# 25 : : bool AllInputsMine(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
# 26 : 313 : {
# 27 : 313 : LOCK(wallet.cs_wallet);
# 28 : :
# 29 [ + + ]: 313 : for (const CTxIn& txin : tx.vin)
# 30 : 603 : {
# 31 : 603 : auto mi = wallet.mapWallet.find(txin.prevout.hash);
# 32 [ + + ]: 603 : if (mi == wallet.mapWallet.end())
# 33 : 2 : return false; // any unknown inputs can't be from us
# 34 : :
# 35 : 601 : const CWalletTx& prev = (*mi).second;
# 36 : :
# 37 [ - + ]: 601 : if (txin.prevout.n >= prev.tx->vout.size())
# 38 : 0 : return false; // invalid input!
# 39 : :
# 40 [ - + ]: 601 : if (!(wallet.IsMine(prev.tx->vout[txin.prevout.n]) & filter))
# 41 : 0 : return false;
# 42 : 601 : }
# 43 : 311 : return true;
# 44 : 313 : }
# 45 : :
# 46 : : CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter)
# 47 : 82546 : {
# 48 [ - + ]: 82546 : if (!MoneyRange(txout.nValue))
# 49 : 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
# 50 : 82546 : LOCK(wallet.cs_wallet);
# 51 [ + + ]: 82546 : return ((wallet.IsMine(txout) & filter) ? txout.nValue : 0);
# 52 : 82546 : }
# 53 : :
# 54 : : CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
# 55 : 16066 : {
# 56 : 16066 : CAmount nCredit = 0;
# 57 [ + + ]: 16066 : for (const CTxOut& txout : tx.vout)
# 58 : 32064 : {
# 59 : 32064 : nCredit += OutputGetCredit(wallet, txout, filter);
# 60 [ - + ]: 32064 : if (!MoneyRange(nCredit))
# 61 : 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
# 62 : 32064 : }
# 63 : 16066 : return nCredit;
# 64 : 16066 : }
# 65 : :
# 66 : : bool ScriptIsChange(const CWallet& wallet, const CScript& script)
# 67 : 3245 : {
# 68 : : // TODO: fix handling of 'change' outputs. The assumption is that any
# 69 : : // payment to a script that is ours, but is not in the address book
# 70 : : // is change. That assumption is likely to break when we implement multisignature
# 71 : : // wallets that return change back into a multi-signature-protected address;
# 72 : : // a better way of identifying which outputs are 'the send' and which are
# 73 : : // 'the change' will need to be implemented (maybe extend CWalletTx to remember
# 74 : : // which output, if any, was change).
# 75 : 3245 : AssertLockHeld(wallet.cs_wallet);
# 76 [ + + ]: 3245 : if (wallet.IsMine(script))
# 77 : 2235 : {
# 78 : 2235 : CTxDestination address;
# 79 [ - + ]: 2235 : if (!ExtractDestination(script, address))
# 80 : 0 : return true;
# 81 [ + + ]: 2235 : if (!wallet.FindAddressBookEntry(address)) {
# 82 : 975 : return true;
# 83 : 975 : }
# 84 : 2235 : }
# 85 : 2270 : return false;
# 86 : 3245 : }
# 87 : :
# 88 : : bool OutputIsChange(const CWallet& wallet, const CTxOut& txout)
# 89 : 1925 : {
# 90 : 1925 : return ScriptIsChange(wallet, txout.scriptPubKey);
# 91 : 1925 : }
# 92 : :
# 93 : : CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout)
# 94 : 216 : {
# 95 : 216 : AssertLockHeld(wallet.cs_wallet);
# 96 [ - + ]: 216 : if (!MoneyRange(txout.nValue))
# 97 : 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
# 98 [ + + ]: 216 : return (OutputIsChange(wallet, txout) ? txout.nValue : 0);
# 99 : 216 : }
# 100 : :
# 101 : : CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx)
# 102 : 108 : {
# 103 : 108 : LOCK(wallet.cs_wallet);
# 104 : 108 : CAmount nChange = 0;
# 105 [ + + ]: 108 : for (const CTxOut& txout : tx.vout)
# 106 : 216 : {
# 107 : 216 : nChange += OutputGetChange(wallet, txout);
# 108 [ - + ]: 216 : if (!MoneyRange(nChange))
# 109 : 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
# 110 : 216 : }
# 111 : 108 : return nChange;
# 112 : 108 : }
# 113 : :
# 114 : : static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, const isminefilter& filter, bool recalculate = false)
# 115 : 2259519 : {
# 116 : 2259519 : auto& amount = wtx.m_amounts[type];
# 117 [ + + ][ - + ]: 2259519 : if (recalculate || !amount.m_cached[filter]) {
# [ + + ]
# 118 [ + + ]: 77158 : amount.Set(filter, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx, filter) : TxGetCredit(wallet, *wtx.tx, filter));
# 119 : 77158 : wtx.m_is_cache_empty = false;
# 120 : 77158 : }
# 121 : 2259519 : return amount.m_value[filter];
# 122 : 2259519 : }
# 123 : :
# 124 : : CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
# 125 : 582 : {
# 126 : : // Must wait until coinbase is safely deep enough in the chain before valuing it
# 127 [ + + ]: 582 : if (wallet.IsTxImmatureCoinBase(wtx))
# 128 : 116 : return 0;
# 129 : :
# 130 : 466 : CAmount credit = 0;
# 131 [ + - ]: 466 : if (filter & ISMINE_SPENDABLE) {
# 132 : : // GetBalance can assume transactions in mapWallet won't change
# 133 : 466 : credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, ISMINE_SPENDABLE);
# 134 : 466 : }
# 135 [ + + ]: 466 : if (filter & ISMINE_WATCH_ONLY) {
# 136 : 83 : credit += GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, ISMINE_WATCH_ONLY);
# 137 : 83 : }
# 138 : 466 : return credit;
# 139 : 582 : }
# 140 : :
# 141 : : CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
# 142 : 1093681 : {
# 143 [ - + ]: 1093681 : if (wtx.tx->vin.empty())
# 144 : 0 : return 0;
# 145 : :
# 146 : 1093681 : CAmount debit = 0;
# 147 [ + + ]: 1093681 : if (filter & ISMINE_SPENDABLE) {
# 148 : 1075174 : debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, ISMINE_SPENDABLE);
# 149 : 1075174 : }
# 150 [ + + ]: 1093681 : if (filter & ISMINE_WATCH_ONLY) {
# 151 : 1088966 : debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, ISMINE_WATCH_ONLY);
# 152 : 1088966 : }
# 153 : 1093681 : return debit;
# 154 : 1093681 : }
# 155 : :
# 156 : : CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
# 157 : 108 : {
# 158 [ - + ]: 108 : if (wtx.fChangeCached)
# 159 : 0 : return wtx.nChangeCached;
# 160 : 108 : wtx.nChangeCached = TxGetChange(wallet, *wtx.tx);
# 161 : 108 : wtx.fChangeCached = true;
# 162 : 108 : return wtx.nChangeCached;
# 163 : 108 : }
# 164 : :
# 165 : : CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache)
# 166 : 151500 : {
# 167 [ + + ][ + + ]: 151500 : if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
# 168 : 47416 : return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_SPENDABLE, !fUseCache);
# 169 : 47416 : }
# 170 : :
# 171 : 104084 : return 0;
# 172 : 151500 : }
# 173 : :
# 174 : : CAmount CachedTxGetImmatureWatchOnlyCredit(const CWallet& wallet, const CWalletTx& wtx, const bool fUseCache)
# 175 : 151498 : {
# 176 [ + + ][ + + ]: 151498 : if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
# 177 : 47414 : return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, ISMINE_WATCH_ONLY, !fUseCache);
# 178 : 47414 : }
# 179 : :
# 180 : 104084 : return 0;
# 181 : 151498 : }
# 182 : :
# 183 : : CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, bool fUseCache, const isminefilter& filter)
# 184 : 302996 : {
# 185 : : // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
# 186 [ + - ][ + - ]: 302996 : bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
# 187 : :
# 188 : : // Must wait until coinbase is safely deep enough in the chain before valuing it
# 189 [ + + ]: 302996 : if (wallet.IsTxImmatureCoinBase(wtx))
# 190 : 94866 : return 0;
# 191 : :
# 192 [ + - ][ + + ]: 208130 : if (fUseCache && allow_cache && wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].m_cached[filter]) {
# [ + - ][ + + ]
# 193 : 174526 : return wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].m_value[filter];
# 194 : 174526 : }
# 195 : :
# 196 [ + + ][ + + ]: 33604 : bool allow_used_addresses = (filter & ISMINE_USED) || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
# 197 : 33604 : CAmount nCredit = 0;
# 198 : 33604 : uint256 hashTx = wtx.GetHash();
# 199 [ + + ]: 107448 : for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
# 200 : 73844 : {
# 201 [ + + ][ + + ]: 73844 : if (!wallet.IsSpent(hashTx, i) && (allow_used_addresses || !wallet.IsSpentKey(hashTx, i))) {
# [ + + ]
# 202 : 50482 : const CTxOut &txout = wtx.tx->vout[i];
# 203 : 50482 : nCredit += OutputGetCredit(wallet, txout, filter);
# 204 [ - + ]: 50482 : if (!MoneyRange(nCredit))
# 205 : 0 : throw std::runtime_error(std::string(__func__) + " : value out of range");
# 206 : 50482 : }
# 207 : 73844 : }
# 208 : :
# 209 [ + - ]: 33604 : if (allow_cache) {
# 210 : 33604 : wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].Set(filter, nCredit);
# 211 : 33604 : wtx.m_is_cache_empty = false;
# 212 : 33604 : }
# 213 : :
# 214 : 33604 : return nCredit;
# 215 : 33604 : }
# 216 : :
# 217 : : void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
# 218 : : std::list<COutputEntry>& listReceived,
# 219 : : std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter)
# 220 : 18505 : {
# 221 : 18505 : nFee = 0;
# 222 : 18505 : listReceived.clear();
# 223 : 18505 : listSent.clear();
# 224 : :
# 225 : : // Compute fee:
# 226 : 18505 : CAmount nDebit = CachedTxGetDebit(wallet, wtx, filter);
# 227 [ + + ]: 18505 : if (nDebit > 0) // debit>0 means we signed/sent this transaction
# 228 : 730 : {
# 229 : 730 : CAmount nValueOut = wtx.tx->GetValueOut();
# 230 : 730 : nFee = nDebit - nValueOut;
# 231 : 730 : }
# 232 : :
# 233 : 18505 : LOCK(wallet.cs_wallet);
# 234 : : // Sent/received.
# 235 [ + + ]: 55429 : for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
# 236 : 36924 : {
# 237 : 36924 : const CTxOut& txout = wtx.tx->vout[i];
# 238 : 36924 : isminetype fIsMine = wallet.IsMine(txout);
# 239 : : // Only need to handle txouts if AT LEAST one of these is true:
# 240 : : // 1) they debit from us (sent)
# 241 : : // 2) the output is to us (received)
# 242 [ + + ]: 36924 : if (nDebit > 0)
# 243 : 1368 : {
# 244 : : // Don't report 'change' txouts
# 245 [ + + ]: 1368 : if (OutputIsChange(wallet, txout))
# 246 : 535 : continue;
# 247 : 1368 : }
# 248 [ + + ]: 35556 : else if (!(fIsMine & filter))
# 249 : 17774 : continue;
# 250 : :
# 251 : : // In either case, we need to get the destination address
# 252 : 18615 : CTxDestination address;
# 253 : :
# 254 [ + + ][ - + ]: 18615 : if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
# 255 : 0 : {
# 256 : 0 : wallet.WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
# 257 : 0 : wtx.GetHash().ToString());
# 258 : 0 : address = CNoDestination();
# 259 : 0 : }
# 260 : :
# 261 : 18615 : COutputEntry output = {address, txout.nValue, (int)i};
# 262 : :
# 263 : : // If we are debited by the transaction, add the output as a "sent" entry
# 264 [ + + ]: 18615 : if (nDebit > 0)
# 265 : 833 : listSent.push_back(output);
# 266 : :
# 267 : : // If we are receiving the output, add it as a "received" entry
# 268 [ + + ]: 18615 : if (fIsMine & filter)
# 269 : 18029 : listReceived.push_back(output);
# 270 : 18615 : }
# 271 : :
# 272 : 18505 : }
# 273 : :
# 274 : : bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
# 275 : 1074394 : {
# 276 : 1074394 : return (CachedTxGetDebit(wallet, wtx, filter) > 0);
# 277 : 1074394 : }
# 278 : :
# 279 : : bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents)
# 280 : 1188978 : {
# 281 : 1188978 : AssertLockHeld(wallet.cs_wallet);
# 282 : 1188978 : int nDepth = wallet.GetTxDepthInMainChain(wtx);
# 283 [ + + ]: 1188978 : if (nDepth >= 1) return true;
# 284 [ + + ]: 171923 : if (nDepth < 0) return false;
# 285 : : // using wtx's cached debit
# 286 [ + + ][ - + ]: 171692 : if (!wallet.m_spend_zero_conf_change || !CachedTxIsFromMe(wallet, wtx, ISMINE_ALL)) return false;
# [ + + ]
# 287 : :
# 288 : : // Don't trust unconfirmed transactions from us unless they are in the mempool.
# 289 [ + + ]: 166794 : if (!wtx.InMempool()) return false;
# 290 : :
# 291 : : // Trusted if all inputs are from us and are in the mempool:
# 292 [ + + ]: 166664 : for (const CTxIn& txin : wtx.tx->vin)
# 293 : 179380 : {
# 294 : : // Transactions not sent by us: not trusted
# 295 : 179380 : const CWalletTx* parent = wallet.GetWalletTx(txin.prevout.hash);
# 296 [ - + ]: 179380 : if (parent == nullptr) return false;
# 297 : 179380 : const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
# 298 : : // Check that this specific input being spent is trusted
# 299 [ + + ]: 179380 : if (wallet.IsMine(parentOut) != ISMINE_SPENDABLE) return false;
# 300 : : // If we've already trusted this parent, continue
# 301 [ + + ]: 179373 : if (trusted_parents.count(parent->GetHash())) continue;
# 302 : : // Recurse to check that the parent is also trusted
# 303 [ + + ]: 143801 : if (!CachedTxIsTrusted(wallet, *parent, trusted_parents)) return false;
# 304 : 143743 : trusted_parents.insert(parent->GetHash());
# 305 : 143743 : }
# 306 : 166599 : return true;
# 307 : 166664 : }
# 308 : :
# 309 : : bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx)
# 310 : 1143 : {
# 311 : 1143 : std::set<uint256> trusted_parents;
# 312 : 1143 : LOCK(wallet.cs_wallet);
# 313 : 1143 : return CachedTxIsTrusted(wallet, wtx, trusted_parents);
# 314 : 1143 : }
# 315 : :
# 316 : : Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
# 317 : 2737 : {
# 318 : 2737 : Balance ret;
# 319 [ + + ]: 2737 : isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
# 320 : 2737 : {
# 321 : 2737 : LOCK(wallet.cs_wallet);
# 322 : 2737 : std::set<uint256> trusted_parents;
# 323 [ + + ]: 2737 : for (const auto& entry : wallet.mapWallet)
# 324 : 151498 : {
# 325 : 151498 : const CWalletTx& wtx = entry.second;
# 326 : 151498 : const bool is_trusted{CachedTxIsTrusted(wallet, wtx, trusted_parents)};
# 327 : 151498 : const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
# 328 : 151498 : const CAmount tx_credit_mine{CachedTxGetAvailableCredit(wallet, wtx, /*fUseCache=*/true, ISMINE_SPENDABLE | reuse_filter)};
# 329 : 151498 : const CAmount tx_credit_watchonly{CachedTxGetAvailableCredit(wallet, wtx, /*fUseCache=*/true, ISMINE_WATCH_ONLY | reuse_filter)};
# 330 [ + + ][ + + ]: 151498 : if (is_trusted && tx_depth >= min_depth) {
# 331 : 150322 : ret.m_mine_trusted += tx_credit_mine;
# 332 : 150322 : ret.m_watchonly_trusted += tx_credit_watchonly;
# 333 : 150322 : }
# 334 [ + + ][ + + ]: 151498 : if (!is_trusted && tx_depth == 0 && wtx.InMempool()) {
# [ + + ]
# 335 : 863 : ret.m_mine_untrusted_pending += tx_credit_mine;
# 336 : 863 : ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
# 337 : 863 : }
# 338 : 151498 : ret.m_mine_immature += CachedTxGetImmatureCredit(wallet, wtx);
# 339 : 151498 : ret.m_watchonly_immature += CachedTxGetImmatureWatchOnlyCredit(wallet, wtx);
# 340 : 151498 : }
# 341 : 2737 : }
# 342 : 2737 : return ret;
# 343 : 2737 : }
# 344 : :
# 345 : : std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet)
# 346 : 4 : {
# 347 : 4 : std::map<CTxDestination, CAmount> balances;
# 348 : :
# 349 : 4 : {
# 350 : 4 : LOCK(wallet.cs_wallet);
# 351 : 4 : std::set<uint256> trusted_parents;
# 352 [ + + ]: 4 : for (const auto& walletEntry : wallet.mapWallet)
# 353 : 410 : {
# 354 : 410 : const CWalletTx& wtx = walletEntry.second;
# 355 : :
# 356 [ - + ]: 410 : if (!CachedTxIsTrusted(wallet, wtx, trusted_parents))
# 357 : 0 : continue;
# 358 : :
# 359 [ + + ]: 410 : if (wallet.IsTxImmatureCoinBase(wtx))
# 360 : 400 : continue;
# 361 : :
# 362 : 10 : int nDepth = wallet.GetTxDepthInMainChain(wtx);
# 363 [ - + ][ + + ]: 10 : if (nDepth < (CachedTxIsFromMe(wallet, wtx, ISMINE_ALL) ? 0 : 1))
# 364 : 0 : continue;
# 365 : :
# 366 [ + + ]: 28 : for (unsigned int i = 0; i < wtx.tx->vout.size(); i++)
# 367 : 18 : {
# 368 : 18 : CTxDestination addr;
# 369 [ + + ]: 18 : if (!wallet.IsMine(wtx.tx->vout[i]))
# 370 : 10 : continue;
# 371 [ - + ]: 8 : if(!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr))
# 372 : 0 : continue;
# 373 : :
# 374 [ + + ]: 8 : CAmount n = wallet.IsSpent(walletEntry.first, i) ? 0 : wtx.tx->vout[i].nValue;
# 375 : 8 : balances[addr] += n;
# 376 : 8 : }
# 377 : 10 : }
# 378 : 4 : }
# 379 : :
# 380 : 4 : return balances;
# 381 : 4 : }
# 382 : :
# 383 : : std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet)
# 384 : 4 : {
# 385 : 4 : AssertLockHeld(wallet.cs_wallet);
# 386 : 4 : std::set< std::set<CTxDestination> > groupings;
# 387 : 4 : std::set<CTxDestination> grouping;
# 388 : :
# 389 [ + + ]: 4 : for (const auto& walletEntry : wallet.mapWallet)
# 390 : 410 : {
# 391 : 410 : const CWalletTx& wtx = walletEntry.second;
# 392 : :
# 393 [ + - ]: 410 : if (wtx.tx->vin.size() > 0)
# 394 : 410 : {
# 395 : 410 : bool any_mine = false;
# 396 : : // group all input addresses with each other
# 397 [ + + ]: 410 : for (const CTxIn& txin : wtx.tx->vin)
# 398 : 412 : {
# 399 : 412 : CTxDestination address;
# 400 [ + + ]: 412 : if(!InputIsMine(wallet, txin)) /* If this input isn't mine, ignore it */
# 401 : 408 : continue;
# 402 [ - + ]: 4 : if(!ExtractDestination(wallet.mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
# 403 : 0 : continue;
# 404 : 4 : grouping.insert(address);
# 405 : 4 : any_mine = true;
# 406 : 4 : }
# 407 : :
# 408 : : // group change with input addresses
# 409 [ + + ]: 410 : if (any_mine)
# 410 : 2 : {
# 411 [ + + ]: 2 : for (const CTxOut& txout : wtx.tx->vout)
# 412 [ - + ]: 2 : if (OutputIsChange(wallet, txout))
# 413 : 0 : {
# 414 : 0 : CTxDestination txoutAddr;
# 415 [ # # ]: 0 : if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
# 416 : 0 : continue;
# 417 : 0 : grouping.insert(txoutAddr);
# 418 : 0 : }
# 419 : 2 : }
# 420 [ + + ]: 410 : if (grouping.size() > 0)
# 421 : 2 : {
# 422 : 2 : groupings.insert(grouping);
# 423 : 2 : grouping.clear();
# 424 : 2 : }
# 425 : 410 : }
# 426 : :
# 427 : : // group lone addrs by themselves
# 428 [ + + ]: 410 : for (const auto& txout : wtx.tx->vout)
# 429 [ + + ]: 818 : if (wallet.IsMine(txout))
# 430 : 408 : {
# 431 : 408 : CTxDestination address;
# 432 [ - + ]: 408 : if(!ExtractDestination(txout.scriptPubKey, address))
# 433 : 0 : continue;
# 434 : 408 : grouping.insert(address);
# 435 : 408 : groupings.insert(grouping);
# 436 : 408 : grouping.clear();
# 437 : 408 : }
# 438 : 410 : }
# 439 : :
# 440 : 4 : std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
# 441 : 4 : std::map< CTxDestination, std::set<CTxDestination>* > setmap; // map addresses to the unique group containing it
# 442 [ + + ]: 4 : for (std::set<CTxDestination> _grouping : groupings)
# 443 : 10 : {
# 444 : : // make a set of all the groups hit by this new group
# 445 : 10 : std::set< std::set<CTxDestination>* > hits;
# 446 : 10 : std::map< CTxDestination, std::set<CTxDestination>* >::iterator it;
# 447 [ + + ]: 10 : for (const CTxDestination& address : _grouping)
# 448 [ + + ]: 12 : if ((it = setmap.find(address)) != setmap.end())
# 449 : 4 : hits.insert((*it).second);
# 450 : :
# 451 : : // merge all hit groups into a new single group and delete old groups
# 452 : 10 : std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping);
# 453 [ + + ]: 10 : for (std::set<CTxDestination>* hit : hits)
# 454 : 4 : {
# 455 : 4 : merged->insert(hit->begin(), hit->end());
# 456 : 4 : uniqueGroupings.erase(hit);
# 457 : 4 : delete hit;
# 458 : 4 : }
# 459 : 10 : uniqueGroupings.insert(merged);
# 460 : :
# 461 : : // update setmap
# 462 [ + + ]: 10 : for (const CTxDestination& element : *merged)
# 463 : 14 : setmap[element] = merged;
# 464 : 10 : }
# 465 : :
# 466 : 4 : std::set< std::set<CTxDestination> > ret;
# 467 [ + + ]: 4 : for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings)
# 468 : 6 : {
# 469 : 6 : ret.insert(*uniqueGrouping);
# 470 : 6 : delete uniqueGrouping;
# 471 : 6 : }
# 472 : :
# 473 : 4 : return ret;
# 474 : 4 : }
# 475 : : } // namespace wallet
|