LCOV - code coverage report
Current view: top level - src/wallet - receive.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 36 299 12.0 %
Date: 2023-11-10 23:46:46 Functions: 4 29 13.8 %
Branches: 17 400 4.2 %

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

Generated by: LCOV version 1.14