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