Coverage Report

Created: 2024-10-29 12:15

/root/bitcoin/src/wallet/receive.cpp
Line
Count
Source (jump to first uncovered line)
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
isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin)
13
0
{
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
0
        return wallet.IsMine(prev->tx->vout[txin.prevout.n]);
18
0
    }
19
0
    return ISMINE_NO;
20
0
}
21
22
bool AllInputsMine(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
23
0
{
24
0
    LOCK(wallet.cs_wallet);
25
0
    for (const CTxIn& txin : tx.vin) {
26
0
        if (!(InputIsMine(wallet, txin) & filter)) return false;
27
0
    }
28
0
    return true;
29
0
}
30
31
CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout, const isminefilter& filter)
32
0
{
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
CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx, const isminefilter& filter)
40
0
{
41
0
    CAmount nCredit = 0;
42
0
    for (const CTxOut& txout : tx.vout)
43
0
    {
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
0
    }
48
0
    return nCredit;
49
0
}
50
51
bool ScriptIsChange(const CWallet& wallet, const CScript& script)
52
0
{
53
    // 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
0
    {
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
0
        }
69
0
    }
70
0
    return false;
71
0
}
72
73
bool OutputIsChange(const CWallet& wallet, const CTxOut& txout)
74
0
{
75
0
    return ScriptIsChange(wallet, txout.scriptPubKey);
76
0
}
77
78
CAmount OutputGetChange(const CWallet& wallet, const CTxOut& txout)
79
0
{
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
CAmount TxGetChange(const CWallet& wallet, const CTransaction& tx)
87
0
{
88
0
    LOCK(wallet.cs_wallet);
89
0
    CAmount nChange = 0;
90
0
    for (const CTxOut& txout : tx.vout)
91
0
    {
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
0
    }
96
0
    return nChange;
97
0
}
98
99
static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, const isminefilter& filter)
100
0
{
101
0
    auto& amount = wtx.m_amounts[type];
102
0
    if (!amount.m_cached[filter]) {
103
0
        amount.Set(filter, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx, filter) : TxGetCredit(wallet, *wtx.tx, filter));
104
0
        wtx.m_is_cache_empty = false;
105
0
    }
106
0
    return amount.m_value[filter];
107
0
}
108
109
CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
110
0
{
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
CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
127
0
{
128
0
    if (wtx.tx->vin.empty())
129
0
        return 0;
130
131
0
    CAmount debit = 0;
132
0
    const isminefilter get_amount_filter{filter & ISMINE_ALL};
133
0
    if (get_amount_filter) {
134
0
        debit += GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, get_amount_filter);
135
0
    }
136
0
    return debit;
137
0
}
138
139
CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
140
0
{
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
CAmount CachedTxGetImmatureCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
149
0
{
150
0
    AssertLockHeld(wallet.cs_wallet);
151
152
0
    if (wallet.IsTxImmatureCoinBase(wtx) && wtx.isConfirmed()) {
153
0
        return GetCachableAmount(wallet, wtx, CWalletTx::IMMATURE_CREDIT, filter);
154
0
    }
155
156
0
    return 0;
157
0
}
158
159
CAmount CachedTxGetAvailableCredit(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
160
0
{
161
0
    AssertLockHeld(wallet.cs_wallet);
162
163
    // Avoid caching ismine for NO or ALL cases (could remove this check and simplify in the future).
164
0
    bool allow_cache = (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
165
166
    // Must wait until coinbase is safely deep enough in the chain before valuing it
167
0
    if (wallet.IsTxImmatureCoinBase(wtx))
168
0
        return 0;
169
170
0
    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
0
    }
173
174
0
    bool allow_used_addresses = (filter & ISMINE_USED) || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
175
0
    CAmount nCredit = 0;
176
0
    Txid 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
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
0
{
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
0
    {
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
0
    {
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
0
        {
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
0
        {
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
bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminefilter& filter)
252
0
{
253
0
    return (CachedTxGetDebit(wallet, wtx, filter) > 0);
254
0
}
255
256
// NOLINTNEXTLINE(misc-no-recursion)
257
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents)
258
0
{
259
0
    AssertLockHeld(wallet.cs_wallet);
260
0
    if (wtx.isConfirmed()) return true;
261
0
    if (wtx.isBlockConflicted()) 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
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
0
    {
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
0
    }
283
0
    return true;
284
0
}
285
286
bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx)
287
0
{
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
Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
294
0
{
295
0
    Balance ret;
296
0
    isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
297
0
    {
298
0
        LOCK(wallet.cs_wallet);
299
0
        std::set<uint256> trusted_parents;
300
0
        for (const auto& entry : wallet.mapWallet)
301
0
        {
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
0
        }
318
0
    }
319
0
    return ret;
320
0
}
321
322
std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet)
323
0
{
324
0
    std::map<CTxDestination, CAmount> balances;
325
326
0
    {
327
0
        LOCK(wallet.cs_wallet);
328
0
        std::set<uint256> trusted_parents;
329
0
        for (const auto& walletEntry : wallet.mapWallet)
330
0
        {
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(Txid::FromUint256(walletEntry.first), i)) ? 0 : output.nValue;
352
0
                balances[addr] += n;
353
0
            }
354
0
        }
355
0
    }
356
357
0
    return balances;
358
0
}
359
360
std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet)
361
0
{
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
0
    {
368
0
        const CWalletTx& wtx = walletEntry.second;
369
370
0
        if (wtx.tx->vin.size() > 0)
371
0
        {
372
0
            bool any_mine = false;
373
            // group all input addresses with each other
374
0
            for (const CTxIn& txin : wtx.tx->vin)
375
0
            {
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
0
            {
388
0
               for (const CTxOut& txout : wtx.tx->vout)
389
0
                   if (OutputIsChange(wallet, txout))
390
0
                   {
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
0
            {
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
0
            {
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
0
    }
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
0
    {
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
0
        {
432
0
            merged->insert(hit->begin(), hit->end());
433
0
            uniqueGroupings.erase(hit);
434
0
            delete hit;
435
0
        }
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
0
    {
446
0
        ret.insert(*uniqueGrouping);
447
0
        delete uniqueGrouping;
448
0
    }
449
450
0
    return ret;
451
0
}
452
} // namespace wallet