Bitcoin ABC 0.30.7
P2P Digital Currency
receive.cpp
Go to the documentation of this file.
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 <wallet/receive.h>
6
9#include <wallet/wallet.h>
10
11isminetype InputIsMine(const CWallet &wallet, const CTxIn &txin) {
12 AssertLockHeld(wallet.cs_wallet);
13 std::map<TxId, CWalletTx>::const_iterator mi =
14 wallet.mapWallet.find(txin.prevout.GetTxId());
15 if (mi != wallet.mapWallet.end()) {
16 const CWalletTx &prev = (*mi).second;
17 if (txin.prevout.GetN() < prev.tx->vout.size()) {
18 return wallet.IsMine(prev.tx->vout[txin.prevout.GetN()]);
19 }
20 }
21
22 return ISMINE_NO;
23}
24
25bool AllInputsMine(const CWallet &wallet, const CTransaction &tx,
26 const isminefilter &filter) {
27 LOCK(wallet.cs_wallet);
28
29 for (const CTxIn &txin : tx.vin) {
30 auto mi = wallet.mapWallet.find(txin.prevout.GetTxId());
31 if (mi == wallet.mapWallet.end()) {
32 // Any unknown inputs can't be from us.
33 return false;
34 }
35
36 const CWalletTx &prev = (*mi).second;
37
38 if (txin.prevout.GetN() >= prev.tx->vout.size()) {
39 // Invalid input!
40 return false;
41 }
42
43 if (!(wallet.IsMine(prev.tx->vout[txin.prevout.GetN()]) & filter)) {
44 return false;
45 }
46 }
47
48 return true;
49}
50
52 const isminefilter &filter) {
53 if (!MoneyRange(txout.nValue)) {
54 throw std::runtime_error(std::string(__func__) +
55 ": value out of range");
56 }
57 LOCK(wallet.cs_wallet);
58 return ((wallet.IsMine(txout) & filter) ? txout.nValue : Amount::zero());
59}
60
61Amount TxGetCredit(const CWallet &wallet, const CTransaction &tx,
62 const isminefilter &filter) {
63 Amount nCredit = Amount::zero();
64 for (const CTxOut &txout : tx.vout) {
65 nCredit += OutputGetCredit(wallet, txout, filter);
66 if (!MoneyRange(nCredit)) {
67 throw std::runtime_error(std::string(__func__) +
68 ": value out of range");
69 }
70 }
71
72 return nCredit;
73}
74
75bool ScriptIsChange(const CWallet &wallet, const CScript &script) {
76 // TODO: fix handling of 'change' outputs. The assumption is that any
77 // payment to a script that is ours, but is not in the address book is
78 // change. That assumption is likely to break when we implement
79 // multisignature wallets that return change back into a
80 // multi-signature-protected address; a better way of identifying which
81 // outputs are 'the send' and which are 'the change' will need to be
82 // implemented (maybe extend CWalletTx to remember which output, if any, was
83 // change).
84 AssertLockHeld(wallet.cs_wallet);
85 if (wallet.IsMine(script)) {
86 CTxDestination address;
87 if (!ExtractDestination(script, address)) {
88 return true;
89 }
90 if (!wallet.FindAddressBookEntry(address)) {
91 return true;
92 }
93 }
94
95 return false;
96}
97
98bool OutputIsChange(const CWallet &wallet, const CTxOut &txout) {
99 return ScriptIsChange(wallet, txout.scriptPubKey);
100}
101
103 AssertLockHeld(wallet.cs_wallet);
104 if (!MoneyRange(txout.nValue)) {
105 throw std::runtime_error(std::string(__func__) +
106 ": value out of range");
107 }
108 return (OutputIsChange(wallet, txout) ? txout.nValue : Amount::zero());
109}
110
111Amount TxGetChange(const CWallet &wallet, const CTransaction &tx) {
112 LOCK(wallet.cs_wallet);
113 Amount nChange = Amount::zero();
114 for (const CTxOut &txout : tx.vout) {
115 nChange += OutputGetChange(wallet, txout);
116 if (!MoneyRange(nChange)) {
117 throw std::runtime_error(std::string(__func__) +
118 ": value out of range");
119 }
120 }
121
122 return nChange;
123}
124
127 const isminefilter &filter,
128 bool recalculate = false) {
129 auto &amount = wtx.m_amounts[type];
130 if (recalculate || !amount.m_cached[filter]) {
131 amount.Set(filter, type == CWalletTx::DEBIT
132 ? wallet.GetDebit(*wtx.tx, filter)
133 : TxGetCredit(wallet, *wtx.tx, filter));
134 wtx.m_is_cache_empty = false;
135 }
136 return amount.m_value[filter];
137}
138
140 const isminefilter &filter) {
141 AssertLockHeld(wallet.cs_wallet);
142
143 // Must wait until coinbase is safely deep enough in the chain before
144 // valuing it.
145 if (wallet.IsTxImmatureCoinBase(wtx)) {
146 return Amount::zero();
147 }
148
149 Amount credit = Amount::zero();
150 if (filter & ISMINE_SPENDABLE) {
151 // GetBalance can assume transactions in mapWallet won't change.
152 credit +=
154 }
155
156 if (filter & ISMINE_WATCH_ONLY) {
159 }
160
161 return credit;
162}
163
165 const isminefilter &filter) {
166 if (wtx.tx->vin.empty()) {
167 return Amount::zero();
168 }
169
170 Amount debit = Amount::zero();
171 if (filter & ISMINE_SPENDABLE) {
172 debit +=
174 }
175 if (filter & ISMINE_WATCH_ONLY) {
176 debit +=
178 }
179
180 return debit;
181}
182
184 if (wtx.fChangeCached) {
185 return wtx.nChangeCached;
186 }
187 wtx.nChangeCached = TxGetChange(wallet, *wtx.tx);
188 wtx.fChangeCached = true;
189 return wtx.nChangeCached;
190}
191
193 bool fUseCache) {
194 AssertLockHeld(wallet.cs_wallet);
195
196 if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
198 ISMINE_SPENDABLE, !fUseCache);
199 }
200
201 return Amount::zero();
202}
203
205 const CWalletTx &wtx,
206 const bool fUseCache) {
207 AssertLockHeld(wallet.cs_wallet);
208
209 if (wallet.IsTxImmatureCoinBase(wtx) && wallet.IsTxInMainChain(wtx)) {
211 ISMINE_WATCH_ONLY, !fUseCache);
212 }
213
214 return Amount::zero();
215}
216
218 bool fUseCache, const isminefilter &filter) {
219 AssertLockHeld(wallet.cs_wallet);
220
221 // Avoid caching ismine for NO or ALL cases (could remove this check and
222 // simplify in the future).
223 bool allow_cache =
224 (filter & ISMINE_ALL) && (filter & ISMINE_ALL) != ISMINE_ALL;
225
226 // Must wait until coinbase is safely deep enough in the chain before
227 // valuing it.
228 if (wallet.IsTxImmatureCoinBase(wtx)) {
229 return Amount::zero();
230 }
231
232 if (fUseCache && allow_cache &&
235 }
236
237 bool allow_used_addresses =
238 (filter & ISMINE_USED) ||
239 !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
240 Amount nCredit = Amount::zero();
241 const TxId txid = wtx.GetId();
242 for (uint32_t i = 0; i < wtx.tx->vout.size(); i++) {
243 if (!wallet.IsSpent(COutPoint(txid, i)) &&
244 (allow_used_addresses || !wallet.IsSpentKey(txid, i))) {
245 const CTxOut &txout = wtx.tx->vout[i];
246 nCredit += OutputGetCredit(wallet, txout, filter);
247 if (!MoneyRange(nCredit)) {
248 throw std::runtime_error(std::string(__func__) +
249 " : value out of range");
250 }
251 }
252 }
253
254 if (allow_cache) {
255 wtx.m_amounts[CWalletTx::AVAILABLE_CREDIT].Set(filter, nCredit);
256 wtx.m_is_cache_empty = false;
257 }
258
259 return nCredit;
260}
261
263 std::list<COutputEntry> &listReceived,
264 std::list<COutputEntry> &listSent, Amount &nFee,
265 const isminefilter &filter) {
266 nFee = Amount::zero();
267 listReceived.clear();
268 listSent.clear();
269
270 // Compute fee:
271 Amount nDebit = CachedTxGetDebit(wallet, wtx, filter);
272 // debit>0 means we signed/sent this transaction
273 if (nDebit > Amount::zero()) {
274 Amount nValueOut = wtx.tx->GetValueOut();
275 nFee = nDebit - nValueOut;
276 }
277
278 LOCK(wallet.cs_wallet);
279 // Sent/received.
280 for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {
281 const CTxOut &txout = wtx.tx->vout[i];
282 isminetype fIsMine = wallet.IsMine(txout);
283 // Only need to handle txouts if AT LEAST one of these is true:
284 // 1) they debit from us (sent)
285 // 2) the output is to us (received)
286 if (nDebit > Amount::zero()) {
287 // Don't report 'change' txouts
288 if (OutputIsChange(wallet, txout)) {
289 continue;
290 }
291 } else if (!(fIsMine & filter)) {
292 continue;
293 }
294
295 // In either case, we need to get the destination address.
296 CTxDestination address;
297
298 if (!ExtractDestination(txout.scriptPubKey, address) &&
299 !txout.scriptPubKey.IsUnspendable()) {
300 wallet.WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction "
301 "type found, txid %s\n",
302 wtx.GetId().ToString());
303 address = CNoDestination();
304 }
305
306 COutputEntry output = {address, txout.nValue, (int)i};
307
308 // If we are debited by the transaction, add the output as a "sent"
309 // entry.
310 if (nDebit > Amount::zero()) {
311 listSent.push_back(output);
312 }
313
314 // If we are receiving the output, add it as a "received" entry.
315 if (fIsMine & filter) {
316 listReceived.push_back(output);
317 }
318 }
319}
320
321bool CachedTxIsFromMe(const CWallet &wallet, const CWalletTx &wtx,
322 const isminefilter &filter) {
323 return CachedTxGetDebit(wallet, wtx, filter) > Amount::zero();
324}
325
327 std::set<TxId> &trusted_parents) {
328 AssertLockHeld(wallet.cs_wallet);
329
330 int nDepth = wallet.GetTxDepthInMainChain(wtx);
331 if (nDepth >= 1) {
332 return true;
333 }
334
335 if (nDepth < 0) {
336 return false;
337 }
338
339 // using wtx's cached debit
340 if (!wallet.m_spend_zero_conf_change ||
342 return false;
343 }
344
345 // Don't trust unconfirmed transactions from us unless they are in the
346 // mempool.
347 if (!wtx.InMempool()) {
348 return false;
349 }
350
351 // Trusted if all inputs are from us and are in the mempool:
352 for (const CTxIn &txin : wtx.tx->vin) {
353 // Transactions not sent by us: not trusted
354 const CWalletTx *parent = wallet.GetWalletTx(txin.prevout.GetTxId());
355 if (parent == nullptr) {
356 return false;
357 }
358
359 const CTxOut &parentOut = parent->tx->vout[txin.prevout.GetN()];
360 // Check that this specific input being spent is trusted
361 if (wallet.IsMine(parentOut) != ISMINE_SPENDABLE) {
362 return false;
363 }
364 // If we've already trusted this parent, continue
365 if (trusted_parents.count(parent->GetId())) {
366 continue;
367 }
368 // Recurse to check that the parent is also trusted
369 if (!CachedTxIsTrusted(wallet, *parent, trusted_parents)) {
370 return false;
371 }
372 trusted_parents.insert(parent->GetId());
373 }
374
375 return true;
376}
377
378bool CachedTxIsTrusted(const CWallet &wallet, const CWalletTx &wtx) {
379 std::set<TxId> trusted_parents;
380 LOCK(wallet.cs_wallet);
381 return CachedTxIsTrusted(wallet, wtx, trusted_parents);
382}
383
384Balance GetBalance(const CWallet &wallet, const int min_depth,
385 bool avoid_reuse) {
386 Balance ret;
387 isminefilter reuse_filter = avoid_reuse ? ISMINE_NO : ISMINE_USED;
388 LOCK(wallet.cs_wallet);
389 std::set<TxId> trusted_parents;
390 for (const auto &entry : wallet.mapWallet) {
391 const CWalletTx &wtx = entry.second;
392 const bool is_trusted{CachedTxIsTrusted(wallet, wtx, trusted_parents)};
393 const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
394 const Amount tx_credit_mine{CachedTxGetAvailableCredit(
395 wallet, wtx, /*fUseCache=*/true, ISMINE_SPENDABLE | reuse_filter)};
396 const Amount tx_credit_watchonly{CachedTxGetAvailableCredit(
397 wallet, wtx, /*fUseCache=*/true, ISMINE_WATCH_ONLY | reuse_filter)};
398 if (is_trusted && tx_depth >= min_depth) {
399 ret.m_mine_trusted += tx_credit_mine;
400 ret.m_watchonly_trusted += tx_credit_watchonly;
401 }
402 if (!is_trusted && tx_depth == 0 && wtx.InMempool()) {
403 ret.m_mine_untrusted_pending += tx_credit_mine;
404 ret.m_watchonly_untrusted_pending += tx_credit_watchonly;
405 }
409 }
410 return ret;
411}
412
413std::map<CTxDestination, Amount> GetAddressBalances(const CWallet &wallet) {
414 std::map<CTxDestination, Amount> balances;
415
416 LOCK(wallet.cs_wallet);
417 std::set<TxId> trusted_parents;
418 for (const auto &walletEntry : wallet.mapWallet) {
419 const CWalletTx &wtx = walletEntry.second;
420
421 if (!CachedTxIsTrusted(wallet, wtx, trusted_parents)) {
422 continue;
423 }
424
425 if (wallet.IsTxImmatureCoinBase(wtx)) {
426 continue;
427 }
428
429 int nDepth = wallet.GetTxDepthInMainChain(wtx);
430 if (nDepth < (CachedTxIsFromMe(wallet, wtx, ISMINE_ALL) ? 0 : 1)) {
431 continue;
432 }
433
434 for (uint32_t i = 0; i < wtx.tx->vout.size(); i++) {
435 CTxDestination addr;
436 if (!wallet.IsMine(wtx.tx->vout[i])) {
437 continue;
438 }
439 if (!ExtractDestination(wtx.tx->vout[i].scriptPubKey, addr)) {
440 continue;
441 }
442
443 Amount n = wallet.IsSpent(COutPoint(walletEntry.first, i))
444 ? Amount::zero()
445 : wtx.tx->vout[i].nValue;
446 balances[addr] += n;
447 }
448 }
449
450 return balances;
451}
452
453std::set<std::set<CTxDestination>> GetAddressGroupings(const CWallet &wallet) {
454 AssertLockHeld(wallet.cs_wallet);
455 std::set<std::set<CTxDestination>> groupings;
456 std::set<CTxDestination> grouping;
457
458 for (const auto &walletEntry : wallet.mapWallet) {
459 const CWalletTx &wtx = walletEntry.second;
460
461 if (wtx.tx->vin.size() > 0) {
462 bool any_mine = false;
463 // Group all input addresses with each other.
464 for (const auto &txin : wtx.tx->vin) {
465 CTxDestination address;
466 // If this input isn't mine, ignore it.
467 if (!InputIsMine(wallet, txin)) {
468 continue;
469 }
470
472 wallet.mapWallet.at(txin.prevout.GetTxId())
473 .tx->vout[txin.prevout.GetN()]
474 .scriptPubKey,
475 address)) {
476 continue;
477 }
478
479 grouping.insert(address);
480 any_mine = true;
481 }
482
483 // Group change with input addresses.
484 if (any_mine) {
485 for (const auto &txout : wtx.tx->vout) {
486 if (OutputIsChange(wallet, txout)) {
487 CTxDestination txoutAddr;
488 if (!ExtractDestination(txout.scriptPubKey,
489 txoutAddr)) {
490 continue;
491 }
492
493 grouping.insert(txoutAddr);
494 }
495 }
496 }
497
498 if (grouping.size() > 0) {
499 groupings.insert(grouping);
500 grouping.clear();
501 }
502 }
503
504 // Group lone addrs by themselves.
505 for (const auto &txout : wtx.tx->vout) {
506 if (wallet.IsMine(txout)) {
507 CTxDestination address;
508 if (!ExtractDestination(txout.scriptPubKey, address)) {
509 continue;
510 }
511
512 grouping.insert(address);
513 groupings.insert(grouping);
514 grouping.clear();
515 }
516 }
517 }
518
519 // A set of pointers to groups of addresses.
520 std::set<std::set<CTxDestination> *> uniqueGroupings;
521 // Map addresses to the unique group containing it.
522 std::map<CTxDestination, std::set<CTxDestination> *> setmap;
523 for (std::set<CTxDestination> _grouping : groupings) {
524 // Make a set of all the groups hit by this new group.
525 std::set<std::set<CTxDestination> *> hits;
526 std::map<CTxDestination, std::set<CTxDestination> *>::iterator it;
527 for (const CTxDestination &address : _grouping) {
528 if ((it = setmap.find(address)) != setmap.end()) {
529 hits.insert((*it).second);
530 }
531 }
532
533 // Merge all hit groups into a new single group and delete old groups.
534 std::set<CTxDestination> *merged =
535 new std::set<CTxDestination>(_grouping);
536 for (std::set<CTxDestination> *hit : hits) {
537 merged->insert(hit->begin(), hit->end());
538 uniqueGroupings.erase(hit);
539 delete hit;
540 }
541 uniqueGroupings.insert(merged);
542
543 // Update setmap.
544 for (const CTxDestination &element : *merged) {
545 setmap[element] = merged;
546 }
547 }
548
549 std::set<std::set<CTxDestination>> ret;
550 for (const std::set<CTxDestination> *uniqueGrouping : uniqueGroupings) {
551 ret.insert(*uniqueGrouping);
552 delete uniqueGrouping;
553 }
554
555 return ret;
556}
bool MoneyRange(const Amount nValue)
Definition: amount.h:166
An output of a transaction.
Definition: transaction.h:128
CScript scriptPubKey
Definition: transaction.h:131
Amount nValue
Definition: transaction.h:130
A CWallet maintains a set of transactions and balances, and provides the ability to create new transa...
Definition: wallet.h:254
A transaction with a bunch of additional info that only the owner cares about.
Definition: transaction.h:65
CTransactionRef tx
Definition: transaction.h:160
TxId GetId() const
Definition: transaction.h:300
@ AVAILABLE_CREDIT
Definition: transaction.h:129
@ IMMATURE_CREDIT
Definition: transaction.h:128
Amount nChangeCached
Definition: transaction.h:142
bool fChangeCached
Definition: transaction.h:140
CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS]
Definition: transaction.h:132
bool m_is_cache_empty
This flag is true if all m_amounts caches are empty.
Definition: transaction.h:139
bool InMempool() const
Definition: transaction.cpp:21
std::string ToString() const
Definition: uint256.h:80
uint8_t isminefilter
Definition: wallet.h:42
isminetype
IsMine() return codes.
Definition: ismine.h:18
@ ISMINE_ALL
Definition: ismine.h:23
@ ISMINE_SPENDABLE
Definition: ismine.h:21
@ ISMINE_NO
Definition: ismine.h:19
@ ISMINE_WATCH_ONLY
Definition: ismine.h:20
@ ISMINE_USED
Definition: ismine.h:22
Amount CachedTxGetImmatureCredit(const CWallet &wallet, const CWalletTx &wtx, bool fUseCache)
Definition: receive.cpp:192
bool CachedTxIsFromMe(const CWallet &wallet, const CWalletTx &wtx, const isminefilter &filter)
Definition: receive.cpp:321
Amount OutputGetChange(const CWallet &wallet, const CTxOut &txout)
Definition: receive.cpp:102
std::set< std::set< CTxDestination > > GetAddressGroupings(const CWallet &wallet)
Definition: receive.cpp:453
Amount CachedTxGetAvailableCredit(const CWallet &wallet, const CWalletTx &wtx, bool fUseCache, const isminefilter &filter)
Definition: receive.cpp:217
Amount CachedTxGetChange(const CWallet &wallet, const CWalletTx &wtx)
Definition: receive.cpp:183
Amount CachedTxGetDebit(const CWallet &wallet, const CWalletTx &wtx, const isminefilter &filter)
filter decides which addresses will count towards the debit
Definition: receive.cpp:164
bool ScriptIsChange(const CWallet &wallet, const CScript &script)
Definition: receive.cpp:75
Amount TxGetCredit(const CWallet &wallet, const CTransaction &tx, const isminefilter &filter)
Definition: receive.cpp:61
Amount CachedTxGetImmatureWatchOnlyCredit(const CWallet &wallet, const CWalletTx &wtx, const bool fUseCache)
Definition: receive.cpp:204
void CachedTxGetAmounts(const CWallet &wallet, const CWalletTx &wtx, std::list< COutputEntry > &listReceived, std::list< COutputEntry > &listSent, Amount &nFee, const isminefilter &filter)
Definition: receive.cpp:262
Amount OutputGetCredit(const CWallet &wallet, const CTxOut &txout, const isminefilter &filter)
Definition: receive.cpp:51
Amount TxGetChange(const CWallet &wallet, const CTransaction &tx)
Definition: receive.cpp:111
std::map< CTxDestination, Amount > GetAddressBalances(const CWallet &wallet)
Definition: receive.cpp:413
bool AllInputsMine(const CWallet &wallet, const CTransaction &tx, const isminefilter &filter)
Returns whether all of the inputs match the filter.
Definition: receive.cpp:25
Amount CachedTxGetCredit(const CWallet &wallet, const CWalletTx &wtx, const isminefilter &filter)
Definition: receive.cpp:139
bool OutputIsChange(const CWallet &wallet, const CTxOut &txout)
Definition: receive.cpp:98
bool CachedTxIsTrusted(const CWallet &wallet, const CWalletTx &wtx, std::set< TxId > &trusted_parents)
Definition: receive.cpp:326
Balance GetBalance(const CWallet &wallet, const int min_depth, bool avoid_reuse)
Definition: receive.cpp:384
static Amount GetCachableAmount(const CWallet &wallet, const CWalletTx &wtx, CWalletTx::AmountType type, const isminefilter &filter, bool recalculate=false)
Definition: receive.cpp:125
isminetype InputIsMine(const CWallet &wallet, const CTxIn &txin)
Definition: receive.cpp:11
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:158
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:85
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32
Amount m_mine_immature
Immature coinbases in the main chain.
Definition: receive.h:75
Amount m_watchonly_trusted
Definition: receive.h:76
Amount m_mine_untrusted_pending
Untrusted, but in mempool (pending)
Definition: receive.h:73
Amount m_watchonly_immature
Definition: receive.h:78
Amount m_watchonly_untrusted_pending
Definition: receive.h:77
Amount m_mine_trusted
Trusted, at depth=GetBalance.min_depth or more.
Definition: receive.h:71
Definition: receive.h:53
Amount m_value[ISMINE_ENUM_ELEMENTS]
Definition: ismine.h:37
void Set(isminefilter filter, Amount value)
Definition: ismine.h:39
std::bitset< ISMINE_ENUM_ELEMENTS > m_cached
Definition: ismine.h:36
A TxId is the identifier of a transaction.
Definition: txid.h:14
#define LOCK(cs)
Definition: sync.h:306
AssertLockHeld(pool.cs)
@ WALLET_FLAG_AVOID_REUSE
Definition: walletutil.h:47