Line data Source code
1 : // Copyright (c) 2009-2021 The Bitcoin Core developers
2 : // Copyright (c) 2014-2025 The Dash Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include <wallet/coinjoin.h>
7 :
8 : #include <key_io.h>
9 : #include <wallet/receive.h>
10 : #include <wallet/spend.h>
11 : #include <wallet/transaction.h>
12 : #include <wallet/wallet.h>
13 :
14 : #include <coinjoin/common.h>
15 : #include <coinjoin/options.h>
16 :
17 : namespace wallet {
18 2334 : void CWallet::InitCJSaltFromDb()
19 : {
20 2334 : assert(nCoinJoinSalt.IsNull());
21 :
22 2334 : WalletBatch batch(GetDatabase());
23 2334 : if (!batch.ReadCoinJoinSalt(nCoinJoinSalt) && batch.ReadCoinJoinSalt(nCoinJoinSalt, true)) {
24 : // Migrate salt stored with legacy key
25 0 : batch.WriteCoinJoinSalt(nCoinJoinSalt);
26 0 : }
27 2334 : }
28 :
29 2348 : const uint256& CWallet::GetCoinJoinSalt()
30 : {
31 2348 : if (nCoinJoinSalt.IsNull()) {
32 2334 : InitCJSaltFromDb();
33 2334 : }
34 2348 : return nCoinJoinSalt;
35 : }
36 :
37 1255 : bool CWallet::SetCoinJoinSalt(const uint256& cj_salt)
38 : {
39 1255 : WalletBatch batch(GetDatabase());
40 : // Only store new salt in CWallet if database write is successful
41 1255 : if (batch.WriteCoinJoinSalt(cj_salt)) {
42 1255 : nCoinJoinSalt = cj_salt;
43 1255 : return true;
44 : }
45 0 : return false;
46 1255 : }
47 :
48 0 : bool CWallet::SelectTxDSInsByDenomination(int nDenom, CAmount nValueMax, std::vector<CTxDSIn>& vecTxDSInRet)
49 : {
50 0 : LOCK(cs_wallet);
51 :
52 0 : vecTxDSInRet.clear();
53 0 : if (!CoinJoin::IsValidDenomination(nDenom)) {
54 0 : return false;
55 : }
56 :
57 0 : CAmount nDenomAmount{CoinJoin::DenominationToAmount(nDenom)};
58 0 : CAmount nValueTotal{0};
59 0 : CCoinControl coin_control(CoinType::ONLY_READY_TO_MIX);
60 0 : std::set<uint256> setRecentTxIds;
61 0 : std::vector<COutput> vCoins{AvailableCoinsListUnspent(*this, &coin_control).all()};
62 :
63 0 : WalletCJLogPrint(this, "CWallet::%s -- vCoins.size(): %d\n", __func__, vCoins.size());
64 :
65 0 : Shuffle(vCoins.rbegin(), vCoins.rend(), FastRandomContext());
66 :
67 0 : for (const auto& out : vCoins) {
68 0 : uint256 txHash = out.outpoint.hash;
69 0 : CAmount nValue = out.txout.nValue;
70 0 : if (setRecentTxIds.find(txHash) != setRecentTxIds.end()) continue; // no duplicate txids
71 0 : if (nValueTotal + nValue > nValueMax) continue;
72 0 : if (nValue != nDenomAmount) continue;
73 :
74 0 : CTxIn txin = CTxIn(txHash, out.outpoint.n);
75 0 : CScript scriptPubKey = out.txout.scriptPubKey;
76 0 : int nRounds = GetRealOutpointCoinJoinRounds(txin.prevout);
77 :
78 0 : nValueTotal += nValue;
79 0 : vecTxDSInRet.emplace_back(CTxDSIn(txin, scriptPubKey, nRounds));
80 0 : setRecentTxIds.emplace(txHash);
81 0 : WalletCJLogPrint(this, "CWallet::%s -- hash: %s, nValue: %d.%08d\n", __func__, txHash.ToString(), nValue / COIN,
82 : nValue % COIN);
83 0 : }
84 :
85 0 : WalletCJLogPrint(this, "CWallet::%s -- setRecentTxIds.size(): %d\n", __func__, setRecentTxIds.size());
86 :
87 0 : return nValueTotal > 0;
88 0 : }
89 :
90 : struct CompareByPriority {
91 0 : bool operator()(const COutput& t1, const COutput& t2) const
92 : {
93 0 : return CoinJoin::CalculateAmountPriority(t1.GetEffectiveValue()) >
94 0 : CoinJoin::CalculateAmountPriority(t2.GetEffectiveValue());
95 : }
96 : };
97 :
98 0 : bool CWallet::SelectDenominatedAmounts(CAmount nValueMax, std::set<CAmount>& setAmountsRet) const
99 : {
100 0 : LOCK(cs_wallet);
101 :
102 0 : setAmountsRet.clear();
103 :
104 0 : CAmount nValueTotal{0};
105 0 : CCoinControl coin_control(CoinType::ONLY_READY_TO_MIX);
106 : // CompareByPriority() cares about effective value, which is only calculable when supplied a feerate
107 0 : std::vector<COutput> vCoins{AvailableCoins(*this, &coin_control, /*feerate=*/CFeeRate(0)).all()};
108 :
109 : // larger denoms first
110 0 : std::sort(vCoins.rbegin(), vCoins.rend(), CompareByPriority());
111 :
112 0 : for (const auto& out : vCoins) {
113 0 : CAmount nValue = out.txout.nValue;
114 0 : if (nValueTotal + nValue <= nValueMax) {
115 0 : nValueTotal += nValue;
116 0 : setAmountsRet.emplace(nValue);
117 0 : }
118 : }
119 :
120 0 : return nValueTotal >= CoinJoin::GetSmallestDenomination();
121 0 : }
122 :
123 2 : std::vector<CompactTallyItem> CWallet::SelectCoinsGroupedByAddresses(bool fSkipDenominated, bool fAnonymizable,
124 : bool fSkipUnconfirmed, int nMaxOupointsPerAddress) const
125 : {
126 2 : LOCK(cs_wallet);
127 :
128 2 : isminefilter filter = ISMINE_SPENDABLE;
129 :
130 : // Try using the cache for already confirmed mixable inputs.
131 : // This should only be used if nMaxOupointsPerAddress was NOT specified.
132 2 : if (nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) {
133 0 : if (fSkipDenominated && fAnonymizableTallyCachedNonDenom) {
134 0 : LogPrint(BCLog::SELECTCOINS, "SelectCoinsGroupedByAddresses - using cache for non-denom inputs %d\n",
135 : vecAnonymizableTallyCachedNonDenom.size());
136 0 : return vecAnonymizableTallyCachedNonDenom;
137 : }
138 0 : if (!fSkipDenominated && fAnonymizableTallyCached) {
139 0 : LogPrint(BCLog::SELECTCOINS, "SelectCoinsGroupedByAddresses - using cache for all inputs %d\n",
140 : vecAnonymizableTallyCached.size());
141 0 : return vecAnonymizableTallyCached;
142 : }
143 0 : }
144 :
145 2 : CAmount nSmallestDenom = CoinJoin::GetSmallestDenomination();
146 :
147 : // Tally
148 2 : std::map<CTxDestination, CompactTallyItem> mapTally;
149 2 : std::set<uint256> setWalletTxesCounted;
150 205 : for (const auto& outpoint : setWalletUTXO) {
151 203 : if (!setWalletTxesCounted.emplace(outpoint.hash).second) continue;
152 :
153 203 : const auto it{mapWallet.find(outpoint.hash)};
154 203 : if (it == mapWallet.end()) continue;
155 :
156 203 : const CWalletTx& wtx{(*it).second};
157 :
158 203 : if (wtx.IsCoinBase() && GetTxBlocksToMaturity(wtx) > 0) continue;
159 4 : if (fSkipUnconfirmed && !CachedTxIsTrusted(*this, wtx)) continue;
160 4 : if (GetTxDepthInMainChain(wtx) < 0) continue;
161 :
162 7 : for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
163 4 : CTxDestination txdest;
164 4 : if (!ExtractDestination(wtx.tx->vout[i].scriptPubKey, txdest)) continue;
165 :
166 3 : isminefilter mine = IsMine(txdest);
167 3 : if (!(mine & filter)) continue;
168 :
169 3 : auto itTallyItem = mapTally.find(txdest);
170 3 : if (nMaxOupointsPerAddress != -1 && itTallyItem != mapTally.end() &&
171 0 : int64_t(itTallyItem->second.outpoints.size()) >= nMaxOupointsPerAddress) {
172 0 : continue;
173 : }
174 :
175 3 : COutPoint target_outpoint(outpoint.hash, i);
176 3 : if (IsSpent(target_outpoint) || IsLockedCoin(target_outpoint)) continue;
177 :
178 3 : if (fSkipDenominated && CoinJoin::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue;
179 :
180 3 : if (fAnonymizable) {
181 : // ignore collaterals
182 0 : if (CoinJoin::IsCollateralAmount(wtx.tx->vout[i].nValue)) continue;
183 : // ignore outputs that are 10 times smaller then the smallest denomination
184 : // otherwise they will just lead to higher fee / lower priority
185 0 : if (wtx.tx->vout[i].nValue <= nSmallestDenom / 10) continue;
186 : // ignore mixed
187 0 : if (IsFullyMixed(target_outpoint)) continue;
188 0 : }
189 :
190 3 : if (itTallyItem == mapTally.end()) {
191 3 : itTallyItem = mapTally.emplace(txdest, CompactTallyItem()).first;
192 3 : itTallyItem->second.txdest = txdest;
193 3 : }
194 3 : itTallyItem->second.nAmount += wtx.tx->vout[i].nValue;
195 3 : itTallyItem->second.outpoints.emplace_back(COutPoint{outpoint.hash, i});
196 3 : }
197 : }
198 :
199 : // construct resulting vector
200 : // NOTE: vecTallyRet is "sorted" by txdest (i.e. address), just like mapTally
201 2 : std::vector<CompactTallyItem> vecTallyRet;
202 5 : for (const auto& item : mapTally) {
203 3 : if (fAnonymizable && item.second.nAmount < nSmallestDenom) continue;
204 3 : vecTallyRet.push_back(item.second);
205 : }
206 :
207 : // Cache already confirmed mixable entries for later use.
208 : // This should only be used if nMaxOupointsPerAddress was NOT specified.
209 2 : if (nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) {
210 0 : if (fSkipDenominated) {
211 0 : vecAnonymizableTallyCachedNonDenom = vecTallyRet;
212 0 : fAnonymizableTallyCachedNonDenom = true;
213 0 : } else {
214 0 : vecAnonymizableTallyCached = vecTallyRet;
215 0 : fAnonymizableTallyCached = true;
216 : }
217 0 : }
218 :
219 : // debug
220 2 : if (LogAcceptDebug(BCLog::SELECTCOINS)) {
221 2 : std::string strMessage = "SelectCoinsGroupedByAddresses - vecTallyRet:\n";
222 5 : for (const auto& item : vecTallyRet) {
223 3 : strMessage += strprintf(" %s %f\n", EncodeDestination(item.txdest), float(item.nAmount) / COIN);
224 : }
225 2 : LogPrint(BCLog::SELECTCOINS, "%s", strMessage); /* Continued */
226 2 : }
227 :
228 2 : return vecTallyRet;
229 2 : }
230 :
231 0 : bool CWallet::HasCollateralInputs(bool fOnlyConfirmed) const
232 : {
233 0 : LOCK(cs_wallet);
234 :
235 0 : CCoinControl coin_control(CoinType::ONLY_COINJOIN_COLLATERAL);
236 0 : coin_control.m_include_unsafe_inputs = !fOnlyConfirmed;
237 :
238 0 : return AvailableCoinsListUnspent(*this, &coin_control).size() > 0;
239 0 : }
240 :
241 0 : int CWallet::CountInputsWithAmount(CAmount nInputAmount) const
242 : {
243 0 : CAmount nTotal = 0;
244 :
245 0 : LOCK(cs_wallet);
246 :
247 0 : for (const auto& outpoint : setWalletUTXO) {
248 0 : const auto it{mapWallet.find(outpoint.hash)};
249 0 : if (it == mapWallet.end()) continue;
250 0 : if (it->second.tx->vout[outpoint.n].nValue != nInputAmount) continue;
251 0 : if (GetTxDepthInMainChain(it->second) < 0) continue;
252 :
253 0 : nTotal++;
254 : }
255 :
256 0 : return nTotal;
257 0 : }
258 :
259 : // Recursively determine the rounds of a given input (How deep is the CoinJoin chain for a given input)
260 11457 : int CWallet::GetRealOutpointCoinJoinRounds(const COutPoint& outpoint, int nRounds) const
261 : {
262 11457 : LOCK(cs_wallet);
263 :
264 11457 : const int nRoundsMax{MAX_COINJOIN_ROUNDS + CCoinJoinClientOptions::GetRandomRounds()};
265 :
266 11457 : if (nRounds >= nRoundsMax) {
267 : // there can only be nRoundsMax rounds max
268 0 : return nRoundsMax - 1;
269 : }
270 :
271 11457 : auto pair = mapOutpointRoundsCache.emplace(outpoint, -10);
272 11457 : auto nRoundsRef = &pair.first->second;
273 11457 : if (!pair.second) {
274 : // we already processed it, just return what we have
275 5854 : return *nRoundsRef;
276 : }
277 :
278 : // TODO wtx should refer to a CWalletTx object, not a pointer, based on surrounding code
279 5603 : const CWalletTx* wtx{GetWalletTx(outpoint.hash)};
280 :
281 5603 : if (wtx == nullptr || wtx->tx == nullptr) {
282 : // no such tx in this wallet
283 0 : *nRoundsRef = -1;
284 0 : WalletCJLogPrint(this, "%s FAILED %-70s %3d\n", __func__, outpoint.ToStringShort(), -1);
285 0 : return *nRoundsRef;
286 : }
287 :
288 : // bounds check
289 5603 : if (outpoint.n >= wtx->tx->vout.size()) {
290 : // should never actually hit this
291 0 : *nRoundsRef = -4;
292 0 : WalletCJLogPrint(this, "%s FAILED %-70s %3d\n", __func__, outpoint.ToStringShort(), -4);
293 0 : return *nRoundsRef;
294 : }
295 :
296 5603 : auto txOutRef = &wtx->tx->vout[outpoint.n];
297 :
298 5603 : if (CoinJoin::IsCollateralAmount(txOutRef->nValue)) {
299 16 : *nRoundsRef = -3;
300 16 : WalletCJLogPrint(this, "%s UPDATED %-70s %3d\n", __func__, outpoint.ToStringShort(), *nRoundsRef);
301 16 : return *nRoundsRef;
302 : }
303 :
304 : // make sure the final output is non-denominate
305 5587 : if (!CoinJoin::IsDenominatedAmount(txOutRef->nValue)) { // NOT DENOM
306 5545 : *nRoundsRef = -2;
307 5545 : WalletCJLogPrint(this, "%s UPDATED %-70s %3d\n", __func__, outpoint.ToStringShort(), *nRoundsRef);
308 5545 : return *nRoundsRef;
309 : }
310 :
311 84 : for (const auto& out : wtx->tx->vout) {
312 84 : if (!CoinJoin::IsDenominatedAmount(out.nValue)) {
313 : // this one is denominated but there is another non-denominated output found in the same tx
314 42 : *nRoundsRef = 0;
315 42 : WalletCJLogPrint(this, "%s UPDATED %-70s %3d\n", __func__, outpoint.ToStringShort(), *nRoundsRef);
316 42 : return *nRoundsRef;
317 : }
318 : }
319 :
320 : // make sure we spent all of it with 0 fee, reset to 0 rounds otherwise
321 0 : if (CachedTxGetDebit(*this, *wtx, ISMINE_SPENDABLE) != CachedTxGetCredit(*this, *wtx, ISMINE_SPENDABLE)) {
322 0 : *nRoundsRef = 0;
323 0 : WalletCJLogPrint(this, "%s UPDATED %-70s %3d\n", __func__, outpoint.ToStringShort(), *nRoundsRef);
324 0 : return *nRoundsRef;
325 : }
326 :
327 0 : int nShortest = -10; // an initial value, should be no way to get this by calculations
328 0 : bool fDenomFound = false;
329 : // only denoms here so let's look up
330 0 : for (const auto& txinNext : wtx->tx->vin) {
331 0 : if (InputIsMine(*this, txinNext)) {
332 0 : int n = GetRealOutpointCoinJoinRounds(txinNext.prevout, nRounds + 1);
333 : // denom found, find the shortest chain or initially assign nShortest with the first found value
334 0 : if (n >= 0 && (n < nShortest || nShortest == -10)) {
335 0 : nShortest = n;
336 0 : fDenomFound = true;
337 0 : }
338 0 : }
339 : }
340 0 : *nRoundsRef = [&]() {
341 0 : if (fDenomFound) {
342 : // good, we a +1 to the shortest one but only nRoundsMax rounds max allowed
343 0 : return nShortest >= nRoundsMax - 1 ? nRoundsMax : nShortest + 1;
344 : }
345 : // too bad, we are the first one in that chain
346 0 : return 0;
347 0 : }();
348 0 : WalletCJLogPrint(this, "%s UPDATED %-70s %3d\n", __func__, outpoint.ToStringShort(), *nRoundsRef);
349 0 : return *nRoundsRef;
350 11457 : }
351 :
352 : // respect current settings
353 0 : int CWallet::GetCappedOutpointCoinJoinRounds(const COutPoint& outpoint) const
354 : {
355 0 : LOCK(cs_wallet);
356 0 : int realCoinJoinRounds = GetRealOutpointCoinJoinRounds(outpoint);
357 0 : return realCoinJoinRounds > CCoinJoinClientOptions::GetRounds() ? CCoinJoinClientOptions::GetRounds()
358 0 : : realCoinJoinRounds;
359 0 : }
360 :
361 8 : void CWallet::ClearCoinJoinRoundsCache()
362 : {
363 8 : LOCK(cs_wallet);
364 8 : mapOutpointRoundsCache.clear();
365 8 : MarkDirty();
366 : // Notify UI
367 8 : NotifyTransactionChanged(uint256::ONE, CT_UPDATED);
368 8 : }
369 :
370 0 : bool CWallet::IsDenominated(const COutPoint& outpoint) const
371 : {
372 0 : LOCK(cs_wallet);
373 :
374 0 : const auto it{mapWallet.find(outpoint.hash)};
375 0 : if (it == mapWallet.end()) {
376 0 : return false;
377 : }
378 :
379 0 : if (outpoint.n >= it->second.tx->vout.size()) {
380 0 : return false;
381 : }
382 :
383 0 : return CoinJoin::IsDenominatedAmount(it->second.tx->vout[outpoint.n].nValue);
384 0 : }
385 :
386 54 : bool CWallet::IsFullyMixed(const COutPoint& outpoint) const
387 : {
388 54 : int nRounds = GetRealOutpointCoinJoinRounds(outpoint);
389 : // Mix again if we don't have N rounds yet
390 54 : if (nRounds < CCoinJoinClientOptions::GetRounds()) return false;
391 :
392 : // Try to mix a "random" number of rounds more than minimum.
393 : // If we have already mixed N + MaxOffset rounds, don't mix again.
394 : // Otherwise, we should mix again 50% of the time, this results in an exponential decay
395 : // N rounds 50% N+1 25% N+2 12.5%... until we reach N + GetRandomRounds() rounds where we stop.
396 0 : if (nRounds < CCoinJoinClientOptions::GetRounds() + CCoinJoinClientOptions::GetRandomRounds()) {
397 0 : CDataStream ss(SER_GETHASH, PROTOCOL_VERSION);
398 0 : ss << outpoint << nCoinJoinSalt;
399 0 : uint256 nHash;
400 0 : CSHA256().Write(reinterpret_cast<const uint8_t*>(ss.data()), ss.size()).Finalize(nHash.begin());
401 0 : if (ReadLE64(nHash.begin()) % 2 == 0) {
402 0 : return false;
403 : }
404 0 : }
405 :
406 0 : return true;
407 54 : }
408 :
409 30240 : void CWallet::RecalculateMixedCredit(const uint256 hash)
410 : {
411 30240 : AssertLockHeld(cs_wallet);
412 30240 : if (auto it = mapWallet.find(hash); it != mapWallet.end()) {
413 : // Recalculate all credits for this tx
414 24763 : it->second.MarkDirty();
415 24763 : }
416 30240 : fAnonymizableTallyCached = false;
417 30240 : fAnonymizableTallyCachedNonDenom = false;
418 30240 : }
419 :
420 0 : CAmount GetBalanceAnonymized(const CWallet& wallet, const CCoinControl& coinControl)
421 : {
422 0 : if (!CCoinJoinClientOptions::IsEnabled()) return 0;
423 :
424 0 : CAmount anonymized_amount{0};
425 0 : LOCK(wallet.cs_wallet);
426 0 : for (auto pcoin : wallet.GetSpendableTXs()) {
427 0 : anonymized_amount += CachedTxGetAnonymizedCredit(wallet, *pcoin, coinControl);
428 : }
429 0 : return anonymized_amount;
430 0 : }
431 :
432 0 : CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated, bool fSkipUnconfirmed) const
433 : {
434 0 : if (!CCoinJoinClientOptions::IsEnabled()) return 0;
435 :
436 0 : std::vector<CompactTallyItem> vecTally = SelectCoinsGroupedByAddresses(fSkipDenominated, true, fSkipUnconfirmed);
437 0 : if (vecTally.empty()) return 0;
438 :
439 0 : CAmount nTotal = 0;
440 :
441 0 : const CAmount nSmallestDenom{CoinJoin::GetSmallestDenomination()};
442 0 : const CAmount nMixingCollateral{CoinJoin::GetCollateralAmount()};
443 0 : for (const auto& item : vecTally) {
444 0 : bool fIsDenominated = CoinJoin::IsDenominatedAmount(item.nAmount);
445 0 : if (fSkipDenominated && fIsDenominated) continue;
446 : // assume that the fee to create denoms should be mixing collateral at max
447 0 : if (item.nAmount >= nSmallestDenom + (fIsDenominated ? 0 : nMixingCollateral)) nTotal += item.nAmount;
448 : }
449 :
450 0 : return nTotal;
451 0 : }
452 :
453 : // Note: calculated including unconfirmed,
454 : // that's ok as long as we use it for informational purposes only
455 0 : float CWallet::GetAverageAnonymizedRounds() const
456 : {
457 0 : if (!CCoinJoinClientOptions::IsEnabled()) return 0;
458 :
459 0 : int nTotal = 0;
460 0 : int nCount = 0;
461 :
462 0 : LOCK(cs_wallet);
463 0 : for (const auto& outpoint : setWalletUTXO) {
464 0 : if (!IsDenominated(outpoint)) continue;
465 :
466 0 : nTotal += GetCappedOutpointCoinJoinRounds(outpoint);
467 0 : nCount++;
468 : }
469 :
470 0 : if (nCount == 0) return 0;
471 :
472 0 : return (float)nTotal / nCount;
473 0 : }
474 :
475 : // Note: calculated including unconfirmed,
476 : // that's ok as long as we use it for informational purposes only
477 0 : CAmount CWallet::GetNormalizedAnonymizedBalance() const
478 : {
479 0 : if (!CCoinJoinClientOptions::IsEnabled()) return 0;
480 :
481 0 : CAmount nTotal = 0;
482 :
483 0 : LOCK(cs_wallet);
484 0 : for (const auto& outpoint : setWalletUTXO) {
485 0 : const auto it{mapWallet.find(outpoint.hash)};
486 0 : if (it == mapWallet.end()) continue;
487 :
488 0 : CAmount nValue = it->second.tx->vout[outpoint.n].nValue;
489 0 : if (!CoinJoin::IsDenominatedAmount(nValue)) continue;
490 0 : if (GetTxDepthInMainChain(it->second) < 0) continue;
491 :
492 0 : int nRounds = GetCappedOutpointCoinJoinRounds(outpoint);
493 0 : nTotal += nValue * nRounds / CCoinJoinClientOptions::GetRounds();
494 : }
495 :
496 0 : return nTotal;
497 0 : }
498 :
499 0 : CAmount CachedTxGetAnonymizedCredit(const CWallet& wallet, const CWalletTx& wtx, const CCoinControl& coinControl)
500 : {
501 0 : AssertLockHeld(wallet.cs_wallet);
502 :
503 : // Exclude coinbase and conflicted txes
504 0 : if (wtx.IsCoinBase() || wallet.GetTxDepthInMainChain(wtx) < 0) return 0;
505 :
506 0 : CAmount nCredit = 0;
507 0 : uint256 hashTx = wtx.GetHash();
508 0 : for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
509 0 : const CTxOut& txout{wtx.tx->vout[i]};
510 0 : const COutPoint outpoint(hashTx, i);
511 :
512 0 : if (coinControl.HasSelected() && !coinControl.IsSelected(outpoint)) {
513 0 : continue;
514 : }
515 :
516 0 : if (wallet.IsSpent(outpoint) || !CoinJoin::IsDenominatedAmount(txout.nValue)) {
517 0 : continue;
518 : }
519 :
520 0 : if (!wallet.IsFullyMixed(outpoint)) {
521 0 : continue;
522 : }
523 :
524 0 : nCredit += OutputGetCredit(wallet, txout, ISMINE_SPENDABLE);
525 0 : if (!MoneyRange(nCredit)) {
526 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
527 : }
528 0 : }
529 :
530 0 : return nCredit;
531 0 : }
532 :
533 170843 : CoinJoinCredits CachedTxGetAvailableCoinJoinCredits(const CWallet& wallet, const CWalletTx& wtx)
534 : {
535 170843 : CoinJoinCredits ret;
536 :
537 170843 : AssertLockHeld(wallet.cs_wallet);
538 :
539 : // Must wait until coinbase is safely deep enough in the chain before valuing it
540 170843 : if (wtx.IsCoinBase() && wallet.GetTxBlocksToMaturity(wtx) > 0) return ret;
541 :
542 51435 : int nDepth = wallet.GetTxDepthInMainChain(wtx);
543 51435 : if (nDepth < 0) return ret;
544 :
545 51395 : ret.is_unconfirmed = CachedTxIsTrusted(wallet, wtx) && nDepth == 0;
546 :
547 51395 : if (wtx.m_amounts[CWalletTx::ANON_CREDIT].m_cached[ISMINE_SPENDABLE]) {
548 35747 : if (ret.is_unconfirmed && wtx.m_amounts[CWalletTx::DENOM_UCREDIT].m_cached[ISMINE_SPENDABLE]) {
549 10023 : return {wtx.m_amounts[CWalletTx::ANON_CREDIT].m_value[ISMINE_SPENDABLE],
550 6682 : wtx.m_amounts[CWalletTx::DENOM_UCREDIT].m_value[ISMINE_SPENDABLE], ret.is_unconfirmed};
551 32406 : } else if (!ret.is_unconfirmed && wtx.m_amounts[CWalletTx::DENOM_CREDIT].m_cached[ISMINE_SPENDABLE]) {
552 97218 : return {wtx.m_amounts[CWalletTx::ANON_CREDIT].m_value[ISMINE_SPENDABLE],
553 64812 : wtx.m_amounts[CWalletTx::DENOM_CREDIT].m_value[ISMINE_SPENDABLE], ret.is_unconfirmed};
554 : }
555 0 : }
556 :
557 15648 : uint256 hashTx = wtx.GetHash();
558 34650 : for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
559 19002 : const CTxOut& txout{wtx.tx->vout[i]};
560 19002 : const COutPoint outpoint(hashTx, i);
561 :
562 19002 : if (wallet.IsSpent(outpoint) || !CoinJoin::IsDenominatedAmount(txout.nValue)) {
563 18960 : continue;
564 : }
565 :
566 42 : const CAmount credit{OutputGetCredit(wallet, txout, ISMINE_SPENDABLE)};
567 42 : if (wallet.IsFullyMixed(outpoint)) {
568 0 : ret.m_anonymized += credit;
569 0 : if (!MoneyRange(ret.m_anonymized)) {
570 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
571 : }
572 0 : }
573 :
574 42 : ret.m_denominated += credit;
575 42 : if (!MoneyRange(ret.m_denominated)) {
576 0 : throw std::runtime_error(std::string(__func__) + ": value out of range");
577 : }
578 42 : }
579 :
580 15648 : wtx.m_amounts[CWalletTx::ANON_CREDIT].Set(ISMINE_SPENDABLE, ret.m_anonymized);
581 15648 : if (ret.is_unconfirmed) {
582 177 : wtx.m_amounts[CWalletTx::DENOM_UCREDIT].Set(ISMINE_SPENDABLE, ret.m_denominated);
583 177 : } else {
584 15471 : wtx.m_amounts[CWalletTx::DENOM_CREDIT].Set(ISMINE_SPENDABLE, ret.m_denominated);
585 : }
586 15648 : return ret;
587 170843 : }
588 : } // namespace wallet
|