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