LCOV - code coverage report
Current view: top level - src/wallet - receive.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 254 298 85.2 %
Date: 2026-06-25 07:23:43 Functions: 19 23 82.6 %

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

Generated by: LCOV version 1.16