Bitcoin Core Fuzz Coverage Report

Coverage Report

Created: 2026-06-01 16:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/zip/work/bitcoin/src/wallet/receive.cpp
Line
Count
Source
1
// Copyright (c) 2021-present 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 <util/check.h>
8
#include <wallet/receive.h>
9
#include <wallet/transaction.h>
10
#include <wallet/wallet.h>
11
12
namespace wallet {
13
bool InputIsMine(const CWallet& wallet, const CTxIn& txin)
14
0
{
15
0
    AssertLockHeld(wallet.cs_wallet);
Line
Count
Source
144
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
16
0
    const CWalletTx* prev = wallet.GetWalletTx(txin.prevout.hash);
17
0
    if (prev && txin.prevout.n < prev->tx->vout.size()) {
18
0
        return wallet.IsMine(prev->tx->vout[txin.prevout.n]);
19
0
    }
20
0
    return false;
21
0
}
22
23
bool AllInputsMine(const CWallet& wallet, const CTransaction& tx)
24
0
{
25
0
    LOCK(wallet.cs_wallet);
Line
Count
Source
268
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
26
0
    for (const CTxIn& txin : tx.vin) {
27
0
        if (!InputIsMine(wallet, txin)) return false;
28
0
    }
29
0
    return true;
30
0
}
31
32
CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout)
33
0
{
34
0
    if (!MoneyRange(txout.nValue))
35
0
        throw std::runtime_error(std::string(__func__) + ": value out of range");
36
0
    LOCK(wallet.cs_wallet);
Line
Count
Source
268
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
37
0
    return (wallet.IsMine(txout) ? txout.nValue : 0);
38
0
}
39
40
CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx)
41
0
{
42
0
    CAmount nCredit = 0;
43
0
    for (const CTxOut& txout : tx.vout)
44
0
    {
45
0
        nCredit += OutputGetCredit(wallet, txout);
46
0
        if (!MoneyRange(nCredit))
47
0
            throw std::runtime_error(std::string(__func__) + ": value out of range");
48
0
    }
49
0
    return nCredit;
50
0
}
51
52
bool ScriptIsChange(const CWallet& wallet, const CScript& script)
53
0
{
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
0
    AssertLockHeld(wallet.cs_wallet);
Line
Count
Source
144
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
62
0
    if (wallet.IsMine(script))
63
0
    {
64
0
        CTxDestination address;
65
0
        if (!ExtractDestination(script, address))
66
0
            return true;
67
0
        if (!wallet.FindAddressBookEntry(address)) {
68
0
            return true;
69
0
        }
70
0
    }
71
0
    return false;
72
0
}
73
74
bool OutputIsChange(const CWallet& wallet, const CTxOut& txout)
75
0
{
76
0
    return ScriptIsChange(wallet, txout.scriptPubKey);
77
0
}
78
79
CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout)
80
0
{
81
0
    AssertLockHeld(wallet.cs_wallet);
Line
Count
Source
144
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
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
CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx)
88
0
{
89
0
    LOCK(wallet.cs_wallet);
Line
Count
Source
268
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
90
0
    CAmount nChange = 0;
91
0
    for (const CTxOut& txout : tx.vout)
92
0
    {
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
0
    }
97
0
    return nChange;
98
0
}
99
100
static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, bool avoid_reuse)
101
0
{
102
0
    auto& amount = wtx.m_amounts[type];
103
0
    if (!amount.IsCached(avoid_reuse)) {
104
0
        amount.Set(avoid_reuse, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx) : TxGetCredit(wallet, *wtx.tx));
105
0
        wtx.m_is_cache_empty = false;
106
0
    }
107
0
    return amount.Get(avoid_reuse);
108
0
}
109
110
CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)
111
0
{
112
0
    AssertLockHeld(wallet.cs_wallet);
Line
Count
Source
144
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
113
114
    // Must wait until coinbase is safely deep enough in the chain before valuing it
115
0
    if (wallet.IsTxImmatureCoinBase(wtx))
116
0
        return 0;
117
118
    // GetBalance can assume transactions in mapWallet won't change
119
0
    return GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, avoid_reuse);
120
0
}
121
122
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)
123
0
{
124
0
    if (wtx.tx->vin.empty())
125
0
        return 0;
126
127
0
    return GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, avoid_reuse);
128
0
}
129
130
CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
131
0
{
132
0
    if (wtx.fChangeCached)
133
0
        return wtx.nChangeCached;
134
0
    wtx.nChangeCached = TxGetChange(wallet, *wtx.tx);
135
0
    wtx.fChangeCached = true;
136
0
    return wtx.nChangeCached;
137
0
}
138
139
void CachedTxGetAmounts(const CWallet& wallet, const CWalletTx& wtx,
140
                  std::list<COutputEntry>& listReceived,
141
                  std::list<COutputEntry>& listSent, CAmount& nFee,
142
                  bool include_change)
143
0
{
144
0
    nFee = 0;
145
0
    listReceived.clear();
146
0
    listSent.clear();
147
148
    // Compute fee:
149
0
    CAmount nDebit = CachedTxGetDebit(wallet, wtx, /*avoid_reuse=*/false);
150
0
    if (nDebit > 0) // debit>0 means we signed/sent this transaction
151
0
    {
152
0
        CAmount nValueOut = wtx.tx->GetValueOut();
153
0
        nFee = nDebit - nValueOut;
154
0
    }
155
156
0
    LOCK(wallet.cs_wallet);
Line
Count
Source
268
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
157
    // Sent/received.
158
0
    for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
159
0
    {
160
0
        const CTxOut& txout = wtx.tx->vout[i];
161
0
        bool ismine = wallet.IsMine(txout);
162
        // Only need to handle txouts if AT LEAST one of these is true:
163
        //   1) they debit from us (sent)
164
        //   2) the output is to us (received)
165
0
        if (nDebit > 0)
166
0
        {
167
0
            if (!include_change && OutputIsChange(wallet, txout))
168
0
                continue;
169
0
        }
170
0
        else if (!ismine)
171
0
            continue;
172
173
        // In either case, we need to get the destination address
174
0
        CTxDestination address;
175
176
0
        if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
177
0
        {
178
0
            wallet.WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
179
0
                                    wtx.GetHash().ToString());
180
0
            address = CNoDestination();
181
0
        }
182
183
0
        COutputEntry output = {address, txout.nValue, (int)i};
184
185
        // If we are debited by the transaction, add the output as a "sent" entry
186
0
        if (nDebit > 0)
187
0
            listSent.push_back(output);
188
189
        // If we are receiving the output, add it as a "received" entry
190
0
        if (ismine)
191
0
            listReceived.push_back(output);
192
0
    }
193
194
0
}
195
196
bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx)
197
0
{
198
0
    if (!wtx.m_cached_from_me.has_value()) {
199
0
        wtx.m_cached_from_me = wallet.IsFromMe(*wtx.tx);
200
0
    }
201
0
    return wtx.m_cached_from_me.value();
202
0
}
203
204
// NOLINTNEXTLINE(misc-no-recursion)
205
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<Txid>& trusted_parents)
206
0
{
207
0
    AssertLockHeld(wallet.cs_wallet);
Line
Count
Source
144
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
208
209
    // This wtx is already trusted
210
0
    if (trusted_parents.contains(wtx.GetHash())) return true;
211
212
0
    if (wtx.isConfirmed()) return true;
213
0
    if (wtx.isBlockConflicted()) return false;
214
    // using wtx's cached debit
215
0
    if (!wallet.m_spend_zero_conf_change || !CachedTxIsFromMe(wallet, wtx)) return false;
216
217
    // Don't trust unconfirmed transactions from us unless they are in the mempool.
218
0
    if (!wtx.InMempool()) return false;
219
220
    // Trusted if all inputs are from us and are in the mempool:
221
0
    for (const CTxIn& txin : wtx.tx->vin)
222
0
    {
223
        // Transactions not sent by us: not trusted
224
0
        const CWalletTx* parent = wallet.GetWalletTx(txin.prevout.hash);
225
0
        if (parent == nullptr) return false;
226
0
        const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
227
        // Check that this specific input being spent is trusted
228
0
        if (!wallet.IsMine(parentOut)) return false;
229
        // If we've already trusted this parent, continue
230
0
        if (trusted_parents.contains(parent->GetHash())) continue;
231
        // Recurse to check that the parent is also trusted
232
0
        if (!CachedTxIsTrusted(wallet, *parent, trusted_parents)) return false;
233
0
        trusted_parents.insert(parent->GetHash());
234
0
    }
235
0
    return true;
236
0
}
237
238
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx)
239
0
{
240
0
    std::set<Txid> trusted_parents;
241
0
    LOCK(wallet.cs_wallet);
Line
Count
Source
268
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
242
0
    return CachedTxIsTrusted(wallet, wtx, trusted_parents);
243
0
}
244
245
Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse, bool include_nonmempool)
246
0
{
247
0
    Balance ret;
248
0
    bool allow_used_addresses = !avoid_reuse || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
249
0
    {
250
0
        LOCK(wallet.cs_wallet);
Line
Count
Source
268
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
251
0
        std::set<Txid> trusted_parents;
252
0
        for (const auto& [outpoint, txo] : wallet.GetTXOs()) {
253
0
            const CWalletTx& wtx = txo.GetWalletTx();
254
255
0
            const bool is_trusted{CachedTxIsTrusted(wallet, wtx, trusted_parents)};
256
0
            const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
257
258
0
            bool nonmempool_spent = false;
259
0
            switch (wallet.HowSpent(outpoint)) {
260
0
            case CWallet::SpendType::CONFIRMED:
261
0
            case CWallet::SpendType::MEMPOOL:
262
                // treat as spent; ignore
263
0
                break;
264
0
            case CWallet::SpendType::NONMEMPOOL:
265
0
                if (!include_nonmempool) break;
266
0
                nonmempool_spent = true;
267
0
                [[fallthrough]];
268
0
            case CWallet::SpendType::UNSPENT:
269
0
                CAmount* bucket = nullptr;
270
271
                // Set the amounts in the return object
272
0
                if (wallet.IsTxImmatureCoinBase(wtx) && wtx.isConfirmed()) {
273
0
                    bucket = &ret.m_mine_immature;
274
0
                } else if (is_trusted && tx_depth >= min_depth) {
275
0
                    bucket = &ret.m_mine_trusted;
276
0
                } else if (!is_trusted && wtx.InMempool()) {
277
0
                    bucket = &ret.m_mine_untrusted_pending;
278
0
                }
279
0
                if (bucket) {
280
                    // Get the amounts for mine
281
0
                    CAmount credit_mine = txo.GetTxOut().nValue;
282
283
0
                    if (!allow_used_addresses && wallet.IsSpentKey(txo.GetTxOut().scriptPubKey)) {
284
0
                        bucket = &ret.m_mine_used;
285
0
                    }
286
0
                    *bucket += credit_mine;
287
0
                    if (nonmempool_spent) {
288
0
                        ret.m_mine_nonmempool -= credit_mine;
289
0
                    }
290
0
                }
291
0
            }
292
0
        }
293
0
    }
294
0
    return ret;
295
0
}
296
297
std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet)
298
0
{
299
0
    std::map<CTxDestination, CAmount> balances;
300
301
0
    {
302
0
        LOCK(wallet.cs_wallet);
Line
Count
Source
268
0
#define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
Line
Count
Source
11
0
#define UNIQUE_NAME(name) PASTE2(name, __COUNTER__)
Line
Count
Source
9
0
#define PASTE2(x, y) PASTE(x, y)
Line
Count
Source
8
0
#define PASTE(x, y) x ## y
303
0
        std::set<Txid> trusted_parents;
304
0
        for (const auto& [outpoint, txo] : wallet.GetTXOs()) {
305
0
            const CWalletTx& wtx = txo.GetWalletTx();
306
307
0
            if (!CachedTxIsTrusted(wallet, wtx, trusted_parents)) continue;
308
0
            if (wallet.IsTxImmatureCoinBase(wtx)) continue;
309
310
0
            int nDepth = wallet.GetTxDepthInMainChain(wtx);
311
0
            if (nDepth < (CachedTxIsFromMe(wallet, wtx) ? 0 : 1)) continue;
312
313
0
            CTxDestination addr;
314
0
            Assume(wallet.IsMine(txo.GetTxOut()));
Line
Count
Source
128
0
#define Assume(val) inline_assertion_check<false>(val, std::source_location::current(), #val)
315
0
            if(!ExtractDestination(txo.GetTxOut().scriptPubKey, addr)) continue;
316
317
0
            CAmount n = wallet.IsSpent(outpoint) ? 0 : txo.GetTxOut().nValue;
318
0
            balances[addr] += n;
319
0
        }
320
0
    }
321
322
0
    return balances;
323
0
}
324
325
std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet)
326
0
{
327
0
    AssertLockHeld(wallet.cs_wallet);
Line
Count
Source
144
0
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
328
0
    std::set< std::set<CTxDestination> > groupings;
329
0
    std::set<CTxDestination> grouping;
330
331
0
    for (const auto& walletEntry : wallet.mapWallet)
332
0
    {
333
0
        const CWalletTx& wtx = walletEntry.second;
334
335
0
        if (wtx.tx->vin.size() > 0)
336
0
        {
337
0
            bool any_mine = false;
338
            // group all input addresses with each other
339
0
            for (const CTxIn& txin : wtx.tx->vin)
340
0
            {
341
0
                CTxDestination address;
342
0
                if(!InputIsMine(wallet, txin)) /* If this input isn't mine, ignore it */
343
0
                    continue;
344
0
                if(!ExtractDestination(wallet.mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
345
0
                    continue;
346
0
                grouping.insert(address);
347
0
                any_mine = true;
348
0
            }
349
350
            // group change with input addresses
351
0
            if (any_mine)
352
0
            {
353
0
               for (const CTxOut& txout : wtx.tx->vout)
354
0
                   if (OutputIsChange(wallet, txout))
355
0
                   {
356
0
                       CTxDestination txoutAddr;
357
0
                       if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
358
0
                           continue;
359
0
                       grouping.insert(txoutAddr);
360
0
                   }
361
0
            }
362
0
            if (grouping.size() > 0)
363
0
            {
364
0
                groupings.insert(grouping);
365
0
                grouping.clear();
366
0
            }
367
0
        }
368
369
        // group lone addrs by themselves
370
0
        for (const auto& txout : wtx.tx->vout)
371
0
            if (wallet.IsMine(txout))
372
0
            {
373
0
                CTxDestination address;
374
0
                if(!ExtractDestination(txout.scriptPubKey, address))
375
0
                    continue;
376
0
                grouping.insert(address);
377
0
                groupings.insert(grouping);
378
0
                grouping.clear();
379
0
            }
380
0
    }
381
382
0
    std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
383
0
    std::map< CTxDestination, std::set<CTxDestination>* > setmap;  // map addresses to the unique group containing it
384
0
    for (const std::set<CTxDestination>& _grouping : groupings)
385
0
    {
386
        // make a set of all the groups hit by this new group
387
0
        std::set< std::set<CTxDestination>* > hits;
388
0
        std::map< CTxDestination, std::set<CTxDestination>* >::iterator it;
389
0
        for (const CTxDestination& address : _grouping)
390
0
            if ((it = setmap.find(address)) != setmap.end())
391
0
                hits.insert((*it).second);
392
393
        // merge all hit groups into a new single group and delete old groups
394
0
        std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping);
395
0
        for (std::set<CTxDestination>* hit : hits)
396
0
        {
397
0
            merged->insert(hit->begin(), hit->end());
398
0
            uniqueGroupings.erase(hit);
399
0
            delete hit;
400
0
        }
401
0
        uniqueGroupings.insert(merged);
402
403
        // update setmap
404
0
        for (const CTxDestination& element : *merged)
405
0
            setmap[element] = merged;
406
0
    }
407
408
0
    std::set< std::set<CTxDestination> > ret;
409
0
    for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings)
410
0
    {
411
0
        ret.insert(*uniqueGrouping);
412
0
        delete uniqueGrouping;
413
0
    }
414
415
0
    return ret;
416
0
}
417
} // namespace wallet