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 <util/check.h>
8 : : #include <wallet/receive.h>
9 : : #include <wallet/transaction.h>
10 : : #include <wallet/wallet.h>
11 : :
12 : : namespace wallet {
13 : 479 : bool InputIsMine(const CWallet& wallet, const CTxIn& txin)
14 : : {
15 : 479 : AssertLockHeld(wallet.cs_wallet);
16 : 479 : const CWalletTx* prev = wallet.GetWalletTx(txin.prevout.hash);
17 [ + + - + : 479 : if (prev && txin.prevout.n < prev->tx->vout.size()) {
+ - ]
18 : 275 : return wallet.IsMine(prev->tx->vout[txin.prevout.n]);
19 : : }
20 : : return false;
21 : : }
22 : :
23 : 115 : bool AllInputsMine(const CWallet& wallet, const CTransaction& tx)
24 : : {
25 : 115 : LOCK(wallet.cs_wallet);
26 [ + + ]: 387 : for (const CTxIn& txin : tx.vin) {
27 [ + - + + ]: 273 : if (!InputIsMine(wallet, txin)) return false;
28 : : }
29 : : return true;
30 : 115 : }
31 : :
32 : 822 : CAmount OutputGetCredit(const CWallet& wallet, const CTxOut& txout)
33 : : {
34 [ - + ]: 822 : if (!MoneyRange(txout.nValue))
35 [ # # # # ]: 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
36 : 822 : LOCK(wallet.cs_wallet);
37 [ + - + + ]: 822 : return (wallet.IsMine(txout) ? txout.nValue : 0);
38 : 822 : }
39 : :
40 : 431 : CAmount TxGetCredit(const CWallet& wallet, const CTransaction& tx)
41 : : {
42 : 431 : CAmount nCredit = 0;
43 [ + + ]: 1253 : for (const CTxOut& txout : tx.vout)
44 : : {
45 : 822 : nCredit += OutputGetCredit(wallet, txout);
46 [ - + ]: 822 : if (!MoneyRange(nCredit))
47 [ # # # # ]: 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
48 : : }
49 : 431 : return nCredit;
50 : : }
51 : :
52 : 2127 : 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 : 2127 : AssertLockHeld(wallet.cs_wallet);
62 [ + + ]: 2127 : if (wallet.IsMine(script))
63 : : {
64 : 1449 : CTxDestination address;
65 [ + - + + ]: 1449 : if (!ExtractDestination(script, address))
66 : : return true;
67 [ + - + + ]: 1444 : if (!wallet.FindAddressBookEntry(address)) {
68 : : return true;
69 : : }
70 : 1449 : }
71 : : return false;
72 : : }
73 : :
74 : 1385 : bool OutputIsChange(const CWallet& wallet, const CTxOut& txout)
75 : : {
76 : 1385 : 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 : : }
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 : 2841 : static CAmount GetCachableAmount(const CWallet& wallet, const CWalletTx& wtx, CWalletTx::AmountType type, bool avoid_reuse)
101 : : {
102 : 2841 : auto& amount = wtx.m_amounts[type];
103 [ - + + + ]: 5682 : if (!amount.IsCached(avoid_reuse)) {
104 [ + + - + ]: 1690 : amount.Set(avoid_reuse, type == CWalletTx::DEBIT ? wallet.GetDebit(*wtx.tx) : TxGetCredit(wallet, *wtx.tx));
105 : 1690 : wtx.m_is_cache_empty = false;
106 : : }
107 : 2841 : return amount.Get(avoid_reuse);
108 : : }
109 : :
110 : 461 : CAmount CachedTxGetCredit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)
111 : : {
112 : 461 : AssertLockHeld(wallet.cs_wallet);
113 : :
114 : : // Must wait until coinbase is safely deep enough in the chain before valuing it
115 [ + + ]: 461 : if (wallet.IsTxImmatureCoinBase(wtx))
116 : : return 0;
117 : :
118 : : // GetBalance can assume transactions in mapWallet won't change
119 : 451 : return GetCachableAmount(wallet, wtx, CWalletTx::CREDIT, avoid_reuse);
120 : : }
121 : :
122 : 2390 : CAmount CachedTxGetDebit(const CWallet& wallet, const CWalletTx& wtx, bool avoid_reuse)
123 : : {
124 [ + - ]: 2390 : if (wtx.tx->vin.empty())
125 : : return 0;
126 : :
127 : 2390 : return GetCachableAmount(wallet, wtx, CWalletTx::DEBIT, avoid_reuse);
128 : : }
129 : :
130 : 0 : CAmount CachedTxGetChange(const CWallet& wallet, const CWalletTx& wtx)
131 : : {
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 : : }
138 : :
139 : 1929 : 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 : : {
144 : 1929 : nFee = 0;
145 : 1929 : listReceived.clear();
146 : 1929 : listSent.clear();
147 : :
148 : : // Compute fee:
149 : 1929 : CAmount nDebit = CachedTxGetDebit(wallet, wtx, /*avoid_reuse=*/false);
150 [ + + ]: 1929 : if (nDebit > 0) // debit>0 means we signed/sent this transaction
151 : : {
152 : 590 : CAmount nValueOut = wtx.tx->GetValueOut();
153 : 590 : nFee = nDebit - nValueOut;
154 : : }
155 : :
156 : 1929 : LOCK(wallet.cs_wallet);
157 : : // Sent/received.
158 [ - + + + ]: 5760 : for (unsigned int i = 0; i < wtx.tx->vout.size(); ++i)
159 : : {
160 [ + - ]: 3831 : const CTxOut& txout = wtx.tx->vout[i];
161 [ + - ]: 3831 : 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 [ + + ]: 3831 : if (nDebit > 0)
166 : : {
167 [ + + + - : 1155 : if (!include_change && OutputIsChange(wallet, txout))
+ + ]
168 : 453 : continue;
169 : : }
170 [ + + ]: 2676 : else if (!ismine)
171 : 1328 : continue;
172 : :
173 : : // In either case, we need to get the destination address
174 : 2050 : CTxDestination address;
175 : :
176 [ + - + + : 2050 : if (!ExtractDestination(txout.scriptPubKey, address) && !txout.scriptPubKey.IsUnspendable())
- + ]
177 : : {
178 [ # # ]: 0 : wallet.WalletLogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",
179 [ # # # # ]: 0 : wtx.GetHash().ToString());
180 : 0 : address = CNoDestination();
181 : : }
182 : :
183 [ + - ]: 2050 : COutputEntry output = {address, txout.nValue, (int)i};
184 : :
185 : : // If we are debited by the transaction, add the output as a "sent" entry
186 [ + + ]: 2050 : if (nDebit > 0)
187 [ + - ]: 702 : listSent.push_back(output);
188 : :
189 : : // If we are receiving the output, add it as a "received" entry
190 [ + + ]: 2050 : if (ismine)
191 [ + - ]: 1565 : listReceived.push_back(output);
192 : 2050 : }
193 : :
194 : 1929 : }
195 : :
196 : 253231 : bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx)
197 : : {
198 [ + + ]: 253231 : if (!wtx.m_cached_from_me.has_value()) {
199 : 10384 : wtx.m_cached_from_me = wallet.IsFromMe(*wtx.tx);
200 : : }
201 [ + - ]: 253231 : return wtx.m_cached_from_me.value();
202 : : }
203 : :
204 : : // NOLINTNEXTLINE(misc-no-recursion)
205 : 586020 : bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<Txid>& trusted_parents)
206 : : {
207 : 586020 : AssertLockHeld(wallet.cs_wallet);
208 : :
209 : : // This wtx is already trusted
210 [ + + ]: 586020 : if (trusted_parents.contains(wtx.GetHash())) return true;
211 : :
212 [ + + ]: 538298 : if (wtx.isConfirmed()) return true;
213 [ + + ]: 75967 : if (wtx.isBlockConflicted()) return false;
214 : : // using wtx's cached debit
215 [ + + + + ]: 75876 : 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 [ + + ]: 73027 : if (!wtx.InMempool()) return false;
219 : :
220 : : // Trusted if all inputs are from us and are in the mempool:
221 [ + + ]: 151049 : for (const CTxIn& txin : wtx.tx->vin)
222 : : {
223 : : // Transactions not sent by us: not trusted
224 : 78478 : const CWalletTx* parent = wallet.GetWalletTx(txin.prevout.hash);
225 [ + + ]: 78478 : if (parent == nullptr) return false;
226 : 78466 : const CTxOut& parentOut = parent->tx->vout[txin.prevout.n];
227 : : // Check that this specific input being spent is trusted
228 [ + + ]: 78466 : if (!wallet.IsMine(parentOut)) return false;
229 : : // If we've already trusted this parent, continue
230 [ + + ]: 78464 : if (trusted_parents.count(parent->GetHash())) continue;
231 : : // Recurse to check that the parent is also trusted
232 [ + + ]: 72419 : if (!CachedTxIsTrusted(wallet, *parent, trusted_parents)) return false;
233 : 72089 : trusted_parents.insert(parent->GetHash());
234 : : }
235 : : return true;
236 : : }
237 : :
238 : 666 : bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx)
239 : : {
240 [ + - ]: 666 : std::set<Txid> trusted_parents;
241 [ + - ]: 666 : LOCK(wallet.cs_wallet);
242 [ + - + - ]: 666 : return CachedTxIsTrusted(wallet, wtx, trusted_parents);
243 : 666 : }
244 : :
245 : 1156 : Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
246 : : {
247 : 1156 : Balance ret;
248 [ + + + + ]: 1156 : bool allow_used_addresses = !avoid_reuse || !wallet.IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
249 : 1156 : {
250 : 1156 : LOCK(wallet.cs_wallet);
251 : 1156 : std::set<Txid> trusted_parents;
252 [ + + + - ]: 84162 : for (const auto& [outpoint, txo] : wallet.GetTXOs()) {
253 [ + - ]: 83006 : const CWalletTx& wtx = txo.GetWalletTx();
254 : :
255 [ + - ]: 83006 : const bool is_trusted{CachedTxIsTrusted(wallet, wtx, trusted_parents)};
256 [ + - ]: 83006 : const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
257 : :
258 [ + - + + : 83006 : if (!wallet.IsSpent(outpoint) && (allow_used_addresses || !wallet.IsSpentKey(txo.GetTxOut().scriptPubKey))) {
+ + + - +
+ ]
259 : : // Get the amounts for mine
260 [ + - ]: 32515 : CAmount credit_mine = txo.GetTxOut().nValue;
261 : :
262 : : // Set the amounts in the return object
263 [ + - + + ]: 32515 : if (wallet.IsTxImmatureCoinBase(wtx) && wtx.isConfirmed()) {
264 : 20811 : ret.m_mine_immature += credit_mine;
265 [ + + ]: 11704 : } else if (is_trusted && tx_depth >= min_depth) {
266 : 11105 : ret.m_mine_trusted += credit_mine;
267 [ + + + - : 599 : } else if (!is_trusted && wtx.InMempool()) {
+ + ]
268 : 394 : ret.m_mine_untrusted_pending += credit_mine;
269 : : }
270 : : }
271 : : }
272 [ + - ]: 1156 : }
273 : 1156 : return ret;
274 : : }
275 : :
276 : 2 : std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet)
277 : : {
278 [ + - ]: 2 : std::map<CTxDestination, CAmount> balances;
279 : :
280 : 2 : {
281 [ + - ]: 2 : LOCK(wallet.cs_wallet);
282 : 2 : std::set<Txid> trusted_parents;
283 [ + + + - ]: 206 : for (const auto& [outpoint, txo] : wallet.GetTXOs()) {
284 [ + - ]: 204 : const CWalletTx& wtx = txo.GetWalletTx();
285 : :
286 [ - + + - ]: 204 : if (!CachedTxIsTrusted(wallet, wtx, trusted_parents)) continue;
287 [ + - + + ]: 204 : if (wallet.IsTxImmatureCoinBase(wtx)) continue;
288 : :
289 [ + - ]: 4 : int nDepth = wallet.GetTxDepthInMainChain(wtx);
290 [ + - + - : 8 : if (nDepth < (CachedTxIsFromMe(wallet, wtx) ? 0 : 1)) continue;
- + ]
291 : :
292 : 4 : CTxDestination addr;
293 [ + - ]: 4 : Assume(wallet.IsMine(txo.GetTxOut()));
294 [ + - - + ]: 4 : if(!ExtractDestination(txo.GetTxOut().scriptPubKey, addr)) continue;
295 : :
296 [ + - + + ]: 4 : CAmount n = wallet.IsSpent(outpoint) ? 0 : txo.GetTxOut().nValue;
297 [ + - ]: 4 : balances[addr] += n;
298 : 4 : }
299 [ + - ]: 2 : }
300 : :
301 : 2 : return balances;
302 : 0 : }
303 : :
304 : 2 : std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet)
305 : : {
306 : 2 : AssertLockHeld(wallet.cs_wallet);
307 : 2 : std::set< std::set<CTxDestination> > groupings;
308 : 2 : std::set<CTxDestination> grouping;
309 : :
310 [ + + - + ]: 207 : for (const auto& walletEntry : wallet.mapWallet)
311 : : {
312 : 205 : const CWalletTx& wtx = walletEntry.second;
313 : :
314 [ + - - + ]: 205 : if (wtx.tx->vin.size() > 0)
315 : : {
316 : 205 : bool any_mine = false;
317 : : // group all input addresses with each other
318 [ + + ]: 411 : for (const CTxIn& txin : wtx.tx->vin)
319 : : {
320 : 206 : CTxDestination address;
321 [ + - + + ]: 206 : if(!InputIsMine(wallet, txin)) /* If this input isn't mine, ignore it */
322 : 204 : continue;
323 [ + - + - : 2 : if(!ExtractDestination(wallet.mapWallet.at(txin.prevout.hash).tx->vout[txin.prevout.n].scriptPubKey, address))
- + ]
324 : 0 : continue;
325 [ + - ]: 2 : grouping.insert(address);
326 : 2 : any_mine = true;
327 : 206 : }
328 : :
329 : : // group change with input addresses
330 [ + + ]: 205 : if (any_mine)
331 : : {
332 [ + + ]: 2 : for (const CTxOut& txout : wtx.tx->vout)
333 [ + - - + ]: 1 : if (OutputIsChange(wallet, txout))
334 : : {
335 : 0 : CTxDestination txoutAddr;
336 [ # # # # ]: 0 : if(!ExtractDestination(txout.scriptPubKey, txoutAddr))
337 : 0 : continue;
338 [ # # ]: 0 : grouping.insert(txoutAddr);
339 : 0 : }
340 : : }
341 [ + + ]: 205 : if (grouping.size() > 0)
342 : : {
343 [ + - ]: 1 : groupings.insert(grouping);
344 : 1 : grouping.clear();
345 : : }
346 : : }
347 : :
348 : : // group lone addrs by themselves
349 [ + + ]: 614 : for (const auto& txout : wtx.tx->vout)
350 [ + - + + ]: 409 : if (wallet.IsMine(txout))
351 : : {
352 : 204 : CTxDestination address;
353 [ + - - + ]: 204 : if(!ExtractDestination(txout.scriptPubKey, address))
354 : 0 : continue;
355 [ + - ]: 204 : grouping.insert(address);
356 [ + - ]: 204 : groupings.insert(grouping);
357 : 204 : grouping.clear();
358 : 204 : }
359 : : }
360 : :
361 : 2 : std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses
362 : 2 : std::map< CTxDestination, std::set<CTxDestination>* > setmap; // map addresses to the unique group containing it
363 [ + + ]: 7 : for (const std::set<CTxDestination>& _grouping : groupings)
364 : : {
365 : : // make a set of all the groups hit by this new group
366 : 5 : std::set< std::set<CTxDestination>* > hits;
367 : 5 : std::map< CTxDestination, std::set<CTxDestination>* >::iterator it;
368 [ + + ]: 11 : for (const CTxDestination& address : _grouping)
369 [ + + ]: 6 : if ((it = setmap.find(address)) != setmap.end())
370 [ + - ]: 2 : hits.insert((*it).second);
371 : :
372 : : // merge all hit groups into a new single group and delete old groups
373 [ + - + - ]: 5 : std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping);
374 [ + + ]: 7 : for (std::set<CTxDestination>* hit : hits)
375 : : {
376 [ + - ]: 2 : merged->insert(hit->begin(), hit->end());
377 : 2 : uniqueGroupings.erase(hit);
378 [ + - ]: 4 : delete hit;
379 : : }
380 [ + - ]: 5 : uniqueGroupings.insert(merged);
381 : :
382 : : // update setmap
383 [ + + ]: 12 : for (const CTxDestination& element : *merged)
384 [ + - ]: 7 : setmap[element] = merged;
385 : 5 : }
386 : :
387 : 2 : std::set< std::set<CTxDestination> > ret;
388 [ + + ]: 5 : for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings)
389 : : {
390 [ + - ]: 3 : ret.insert(*uniqueGrouping);
391 [ + - ]: 6 : delete uniqueGrouping;
392 : : }
393 : :
394 : 2 : return ret;
395 : 2 : }
396 : : } // namespace wallet
|