diff --git a/src/wallet/coinjoin.cpp b/src/wallet/coinjoin.cpp index f68adccf1f138..07afe5ca3bb61 100644 --- a/src/wallet/coinjoin.cpp +++ b/src/wallet/coinjoin.cpp @@ -6,13 +6,14 @@ #include #include -#include -#include -#include #include #include -#include #include +#include + +#include +#include +#include namespace wallet { void CWallet::InitCJSaltFromDb() @@ -78,8 +79,8 @@ bool CWallet::SelectTxDSInsByDenomination(int nDenom, CAmount nValueMax, std::ve nValueTotal += nValue; vecTxDSInRet.emplace_back(CTxDSIn(txin, scriptPubKey, nRounds)); setRecentTxIds.emplace(txHash); - WalletCJLogPrint(this, "CWallet::%s -- hash: %s, nValue: %d.%08d\n", - __func__, txHash.ToString(), nValue / COIN, nValue % COIN); + WalletCJLogPrint(this, "CWallet::%s -- hash: %s, nValue: %d.%08d\n", __func__, txHash.ToString(), nValue / COIN, + nValue % COIN); } WalletCJLogPrint(this, "CWallet::%s -- setRecentTxIds.size(): %d\n", __func__, setRecentTxIds.size()); @@ -87,12 +88,11 @@ bool CWallet::SelectTxDSInsByDenomination(int nDenom, CAmount nValueMax, std::ve return nValueTotal > 0; } -struct CompareByPriority -{ - bool operator()(const COutput& t1, - const COutput& t2) const +struct CompareByPriority { + bool operator()(const COutput& t1, const COutput& t2) const { - return CoinJoin::CalculateAmountPriority(t1.GetEffectiveValue()) > CoinJoin::CalculateAmountPriority(t2.GetEffectiveValue()); + return CoinJoin::CalculateAmountPriority(t1.GetEffectiveValue()) > + CoinJoin::CalculateAmountPriority(t2.GetEffectiveValue()); } }; @@ -121,7 +121,8 @@ bool CWallet::SelectDenominatedAmounts(CAmount nValueMax, std::set& set return nValueTotal >= CoinJoin::GetSmallestDenomination(); } -std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipDenominated, bool fAnonymizable, bool fSkipUnconfirmed, int nMaxOupointsPerAddress) const +std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipDenominated, bool fAnonymizable, + bool fSkipUnconfirmed, int nMaxOupointsPerAddress) const { LOCK(cs_wallet); @@ -129,13 +130,15 @@ std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipD // Try using the cache for already confirmed mixable inputs. // This should only be used if nMaxOupointsPerAddress was NOT specified. - if(nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) { - if(fSkipDenominated && fAnonymizableTallyCachedNonDenom) { - LogPrint(BCLog::SELECTCOINS, "SelectCoinsGroupedByAddresses - using cache for non-denom inputs %d\n", vecAnonymizableTallyCachedNonDenom.size()); + if (nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) { + if (fSkipDenominated && fAnonymizableTallyCachedNonDenom) { + LogPrint(BCLog::SELECTCOINS, "SelectCoinsGroupedByAddresses - using cache for non-denom inputs %d\n", + vecAnonymizableTallyCachedNonDenom.size()); return vecAnonymizableTallyCachedNonDenom; } - if(!fSkipDenominated && fAnonymizableTallyCached) { - LogPrint(BCLog::SELECTCOINS, "SelectCoinsGroupedByAddresses - using cache for all inputs %d\n", vecAnonymizableTallyCached.size()); + if (!fSkipDenominated && fAnonymizableTallyCached) { + LogPrint(BCLog::SELECTCOINS, "SelectCoinsGroupedByAddresses - using cache for all inputs %d\n", + vecAnonymizableTallyCached.size()); return vecAnonymizableTallyCached; } } @@ -146,13 +149,12 @@ std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipD std::map mapTally; std::set setWalletTxesCounted; for (const auto& outpoint : setWalletUTXO) { - if (!setWalletTxesCounted.emplace(outpoint.hash).second) continue; - const auto it = mapWallet.find(outpoint.hash); + const auto it{mapWallet.find(outpoint.hash)}; if (it == mapWallet.end()) continue; - const CWalletTx& wtx = (*it).second; + const CWalletTx& wtx{(*it).second}; if (wtx.IsCoinBase() && GetTxBlocksToMaturity(wtx) > 0) continue; if (fSkipUnconfirmed && !CachedTxIsTrusted(*this, wtx)) continue; @@ -163,23 +165,26 @@ std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipD if (!ExtractDestination(wtx.tx->vout[i].scriptPubKey, txdest)) continue; isminefilter mine = IsMine(txdest); - if(!(mine & filter)) continue; + if (!(mine & filter)) continue; auto itTallyItem = mapTally.find(txdest); - if (nMaxOupointsPerAddress != -1 && itTallyItem != mapTally.end() && int64_t(itTallyItem->second.outpoints.size()) >= nMaxOupointsPerAddress) continue; + if (nMaxOupointsPerAddress != -1 && itTallyItem != mapTally.end() && + int64_t(itTallyItem->second.outpoints.size()) >= nMaxOupointsPerAddress) { + continue; + } COutPoint target_outpoint(outpoint.hash, i); - if(IsSpent(target_outpoint) || IsLockedCoin(target_outpoint)) continue; + if (IsSpent(target_outpoint) || IsLockedCoin(target_outpoint)) continue; - if(fSkipDenominated && CoinJoin::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue; + if (fSkipDenominated && CoinJoin::IsDenominatedAmount(wtx.tx->vout[i].nValue)) continue; - if(fAnonymizable) { + if (fAnonymizable) { // ignore collaterals - if(CoinJoin::IsCollateralAmount(wtx.tx->vout[i].nValue)) continue; + if (CoinJoin::IsCollateralAmount(wtx.tx->vout[i].nValue)) continue; if (fMasternodeMode && dmn_types::IsCollateralAmount(wtx.tx->vout[i].nValue)) continue; // ignore outputs that are 10 times smaller then the smallest denomination // otherwise they will just lead to higher fee / lower priority - if(wtx.tx->vout[i].nValue <= nSmallestDenom/10) continue; + if (wtx.tx->vout[i].nValue <= nSmallestDenom / 10) continue; // ignore mixed if (IsFullyMixed(target_outpoint)) continue; } @@ -197,14 +202,14 @@ std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipD // NOTE: vecTallyRet is "sorted" by txdest (i.e. address), just like mapTally std::vector vecTallyRet; for (const auto& item : mapTally) { - if(fAnonymizable && item.second.nAmount < nSmallestDenom) continue; + if (fAnonymizable && item.second.nAmount < nSmallestDenom) continue; vecTallyRet.push_back(item.second); } // Cache already confirmed mixable entries for later use. // This should only be used if nMaxOupointsPerAddress was NOT specified. - if(nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) { - if(fSkipDenominated) { + if (nMaxOupointsPerAddress == -1 && fAnonymizable && fSkipUnconfirmed) { + if (fSkipDenominated) { vecAnonymizableTallyCachedNonDenom = vecTallyRet; fAnonymizableTallyCachedNonDenom = true; } else { @@ -216,8 +221,9 @@ std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipD // debug if (LogAcceptDebug(BCLog::SELECTCOINS)) { std::string strMessage = "SelectCoinsGroupedByAddresses - vecTallyRet:\n"; - for (const auto& item : vecTallyRet) - strMessage += strprintf(" %s %f\n", EncodeDestination(item.txdest), float(item.nAmount)/COIN); + for (const auto& item : vecTallyRet) { + strMessage += strprintf(" %s %f\n", EncodeDestination(item.txdest), float(item.nAmount) / COIN); + } LogPrint(BCLog::SELECTCOINS, "%s", strMessage); /* Continued */ } @@ -241,7 +247,7 @@ int CWallet::CountInputsWithAmount(CAmount nInputAmount) const LOCK(cs_wallet); for (const auto& outpoint : setWalletUTXO) { - const auto it = mapWallet.find(outpoint.hash); + const auto it{mapWallet.find(outpoint.hash)}; if (it == mapWallet.end()) continue; if (it->second.tx->vout[outpoint.n].nValue != nInputAmount) continue; if (GetTxDepthInMainChain(it->second) < 0) continue; @@ -257,7 +263,7 @@ int CWallet::GetRealOutpointCoinJoinRounds(const COutPoint& outpoint, int nRound { LOCK(cs_wallet); - const int nRoundsMax = MAX_COINJOIN_ROUNDS + CCoinJoinClientOptions::GetRandomRounds(); + const int nRoundsMax{MAX_COINJOIN_ROUNDS + CCoinJoinClientOptions::GetRandomRounds()}; if (nRounds >= nRoundsMax) { // there can only be nRoundsMax rounds max @@ -272,7 +278,7 @@ int CWallet::GetRealOutpointCoinJoinRounds(const COutPoint& outpoint, int nRound } // TODO wtx should refer to a CWalletTx object, not a pointer, based on surrounding code - const CWalletTx* wtx = GetWalletTx(outpoint.hash); + const CWalletTx* wtx{GetWalletTx(outpoint.hash)}; if (wtx == nullptr || wtx->tx == nullptr) { // no such tx in this wallet @@ -298,7 +304,7 @@ int CWallet::GetRealOutpointCoinJoinRounds(const COutPoint& outpoint, int nRound } // make sure the final output is non-denominate - if (!CoinJoin::IsDenominatedAmount(txOutRef->nValue)) { //NOT DENOM + if (!CoinJoin::IsDenominatedAmount(txOutRef->nValue)) { // NOT DENOM *nRoundsRef = -2; WalletCJLogPrint(this, "%s UPDATED %-70s %3d\n", __func__, outpoint.ToStringShort(), *nRoundsRef); return *nRoundsRef; @@ -327,15 +333,20 @@ int CWallet::GetRealOutpointCoinJoinRounds(const COutPoint& outpoint, int nRound if (InputIsMine(*this, txinNext)) { int n = GetRealOutpointCoinJoinRounds(txinNext.prevout, nRounds + 1); // denom found, find the shortest chain or initially assign nShortest with the first found value - if(n >= 0 && (n < nShortest || nShortest == -10)) { + if (n >= 0 && (n < nShortest || nShortest == -10)) { nShortest = n; fDenomFound = true; } } } - *nRoundsRef = fDenomFound - ? (nShortest >= nRoundsMax - 1 ? nRoundsMax : nShortest + 1) // good, we a +1 to the shortest one but only nRoundsMax rounds max allowed - : 0; // too bad, we are the fist one in that chain + *nRoundsRef = [&]() { + if (fDenomFound) { + // good, we a +1 to the shortest one but only nRoundsMax rounds max allowed + return nShortest >= nRoundsMax - 1 ? nRoundsMax : nShortest + 1; + } + // too bad, we are the first one in that chain + return 0; + }(); WalletCJLogPrint(this, "%s UPDATED %-70s %3d\n", __func__, outpoint.ToStringShort(), *nRoundsRef); return *nRoundsRef; } @@ -345,7 +356,8 @@ int CWallet::GetCappedOutpointCoinJoinRounds(const COutPoint& outpoint) const { LOCK(cs_wallet); int realCoinJoinRounds = GetRealOutpointCoinJoinRounds(outpoint); - return realCoinJoinRounds > CCoinJoinClientOptions::GetRounds() ? CCoinJoinClientOptions::GetRounds() : realCoinJoinRounds; + return realCoinJoinRounds > CCoinJoinClientOptions::GetRounds() ? CCoinJoinClientOptions::GetRounds() + : realCoinJoinRounds; } void CWallet::ClearCoinJoinRoundsCache() @@ -361,7 +373,7 @@ bool CWallet::IsDenominated(const COutPoint& outpoint) const { LOCK(cs_wallet); - const auto it = mapWallet.find(outpoint.hash); + const auto it{mapWallet.find(outpoint.hash)}; if (it == mapWallet.end()) { return false; } @@ -387,7 +399,7 @@ bool CWallet::IsFullyMixed(const COutPoint& outpoint) const CDataStream ss(SER_GETHASH, PROTOCOL_VERSION); ss << outpoint << nCoinJoinSalt; uint256 nHash; - CSHA256().Write((const unsigned char*)ss.data(), ss.size()).Finalize(nHash.begin()); + CSHA256().Write(reinterpret_cast(ss.data()), ss.size()).Finalize(nHash.begin()); if (ReadLE64(nHash.begin()) % 2 == 0) { return false; } @@ -428,14 +440,13 @@ CAmount CWallet::GetAnonymizableBalance(bool fSkipDenominated, bool fSkipUnconfi CAmount nTotal = 0; - const CAmount nSmallestDenom = CoinJoin::GetSmallestDenomination(); - const CAmount nMixingCollateral = CoinJoin::GetCollateralAmount(); + const CAmount nSmallestDenom{CoinJoin::GetSmallestDenomination()}; + const CAmount nMixingCollateral{CoinJoin::GetCollateralAmount()}; for (const auto& item : vecTally) { bool fIsDenominated = CoinJoin::IsDenominatedAmount(item.nAmount); - if(fSkipDenominated && fIsDenominated) continue; + if (fSkipDenominated && fIsDenominated) continue; // assume that the fee to create denoms should be mixing collateral at max - if(item.nAmount >= nSmallestDenom + (fIsDenominated ? 0 : nMixingCollateral)) - nTotal += item.nAmount; + if (item.nAmount >= nSmallestDenom + (fIsDenominated ? 0 : nMixingCollateral)) nTotal += item.nAmount; } return nTotal; @@ -452,15 +463,15 @@ float CWallet::GetAverageAnonymizedRounds() const LOCK(cs_wallet); for (const auto& outpoint : setWalletUTXO) { - if(!IsDenominated(outpoint)) continue; + if (!IsDenominated(outpoint)) continue; nTotal += GetCappedOutpointCoinJoinRounds(outpoint); nCount++; } - if(nCount == 0) return 0; + if (nCount == 0) return 0; - return (float)nTotal/nCount; + return (float)nTotal / nCount; } // Note: calculated including unconfirmed, @@ -473,7 +484,7 @@ CAmount CWallet::GetNormalizedAnonymizedBalance() const LOCK(cs_wallet); for (const auto& outpoint : setWalletUTXO) { - const auto it = mapWallet.find(outpoint.hash); + const auto it{mapWallet.find(outpoint.hash)}; if (it == mapWallet.end()) continue; CAmount nValue = it->second.tx->vout[outpoint.n].nValue; @@ -492,26 +503,29 @@ CAmount CachedTxGetAnonymizedCredit(const CWallet& wallet, const CWalletTx& wtx, AssertLockHeld(wallet.cs_wallet); // Exclude coinbase and conflicted txes - if (wtx.IsCoinBase() || wallet.GetTxDepthInMainChain(wtx) < 0) - return 0; + if (wtx.IsCoinBase() || wallet.GetTxDepthInMainChain(wtx) < 0) return 0; CAmount nCredit = 0; uint256 hashTx = wtx.GetHash(); - for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) - { - const CTxOut &txout = wtx.tx->vout[i]; - const COutPoint outpoint = COutPoint(hashTx, i); + for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { + const CTxOut& txout{wtx.tx->vout[i]}; + const COutPoint outpoint(hashTx, i); if (coinControl.HasSelected() && !coinControl.IsSelected(outpoint)) { continue; } - if (wallet.IsSpent(outpoint) || !CoinJoin::IsDenominatedAmount(txout.nValue)) continue; + if (wallet.IsSpent(outpoint) || !CoinJoin::IsDenominatedAmount(txout.nValue)) { + continue; + } - if (wallet.IsFullyMixed(outpoint)) { - nCredit += OutputGetCredit(wallet, txout, ISMINE_SPENDABLE); - if (!MoneyRange(nCredit)) - throw std::runtime_error(std::string(__func__) + ": value out of range"); + if (!wallet.IsFullyMixed(outpoint)) { + continue; + } + + nCredit += OutputGetCredit(wallet, txout, ISMINE_SPENDABLE); + if (!MoneyRange(nCredit)) { + throw std::runtime_error(std::string(__func__) + ": value out of range"); } } @@ -525,8 +539,7 @@ CoinJoinCredits CachedTxGetAvailableCoinJoinCredits(const CWallet& wallet, const AssertLockHeld(wallet.cs_wallet); // Must wait until coinbase is safely deep enough in the chain before valuing it - if (wtx.IsCoinBase() && wallet.GetTxBlocksToMaturity(wtx) > 0) - return ret; + if (wtx.IsCoinBase() && wallet.GetTxBlocksToMaturity(wtx) > 0) return ret; int nDepth = wallet.GetTxDepthInMainChain(wtx); if (nDepth < 0) return ret; @@ -535,29 +548,35 @@ CoinJoinCredits CachedTxGetAvailableCoinJoinCredits(const CWallet& wallet, const if (wtx.m_amounts[CWalletTx::ANON_CREDIT].m_cached[ISMINE_SPENDABLE]) { if (ret.is_unconfirmed && wtx.m_amounts[CWalletTx::DENOM_UCREDIT].m_cached[ISMINE_SPENDABLE]) { - return {wtx.m_amounts[CWalletTx::ANON_CREDIT].m_value[ISMINE_SPENDABLE], wtx.m_amounts[CWalletTx::DENOM_UCREDIT].m_value[ISMINE_SPENDABLE], ret.is_unconfirmed}; + return {wtx.m_amounts[CWalletTx::ANON_CREDIT].m_value[ISMINE_SPENDABLE], + wtx.m_amounts[CWalletTx::DENOM_UCREDIT].m_value[ISMINE_SPENDABLE], ret.is_unconfirmed}; } else if (!ret.is_unconfirmed && wtx.m_amounts[CWalletTx::DENOM_CREDIT].m_cached[ISMINE_SPENDABLE]) { - return {wtx.m_amounts[CWalletTx::ANON_CREDIT].m_value[ISMINE_SPENDABLE], wtx.m_amounts[CWalletTx::DENOM_CREDIT].m_value[ISMINE_SPENDABLE], ret.is_unconfirmed}; + return {wtx.m_amounts[CWalletTx::ANON_CREDIT].m_value[ISMINE_SPENDABLE], + wtx.m_amounts[CWalletTx::DENOM_CREDIT].m_value[ISMINE_SPENDABLE], ret.is_unconfirmed}; } } uint256 hashTx = wtx.GetHash(); for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) { - const CTxOut &txout = wtx.tx->vout[i]; - const COutPoint outpoint = COutPoint(hashTx, i); + const CTxOut& txout{wtx.tx->vout[i]}; + const COutPoint outpoint(hashTx, i); - if (wallet.IsSpent(outpoint) || !CoinJoin::IsDenominatedAmount(txout.nValue)) continue; - const CAmount credit = OutputGetCredit(wallet, txout, ISMINE_SPENDABLE); + if (wallet.IsSpent(outpoint) || !CoinJoin::IsDenominatedAmount(txout.nValue)) { + continue; + } + const CAmount credit{OutputGetCredit(wallet, txout, ISMINE_SPENDABLE)}; if (wallet.IsFullyMixed(outpoint)) { ret.m_anonymized += credit; - if (!MoneyRange(ret.m_anonymized)) + if (!MoneyRange(ret.m_anonymized)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); + } } ret.m_denominated += credit; - if (!MoneyRange(ret.m_denominated)) + if (!MoneyRange(ret.m_denominated)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); + } } wtx.m_amounts[CWalletTx::ANON_CREDIT].Set(ISMINE_SPENDABLE, ret.m_anonymized); diff --git a/src/wallet/coinjoin.h b/src/wallet/coinjoin.h index 44512b4ca57ea..3f5bda8a00b4d 100644 --- a/src/wallet/coinjoin.h +++ b/src/wallet/coinjoin.h @@ -12,11 +12,11 @@ // Use a macro instead of a function for conditional logging to prevent // evaluating arguments when logging for the category is not enabled. -#define WalletCJLogPrint(wallet, ...) \ - do { \ - if (LogAcceptDebug(BCLog::COINJOIN)) { \ - wallet->WalletLogPrintf(__VA_ARGS__); \ - } \ +#define WalletCJLogPrint(wallet, ...) \ + do { \ + if (LogAcceptDebug(BCLog::COINJOIN)) { \ + wallet->WalletLogPrintf(__VA_ARGS__); \ + } \ } while (0) namespace wallet { @@ -29,8 +29,7 @@ CAmount GetBalanceAnonymized(const CWallet& wallet, const CCoinControl& coinCont CAmount CachedTxGetAnonymizedCredit(const CWallet& wallet, const CWalletTx& wtx, const CCoinControl& coinControl) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); -struct CoinJoinCredits -{ +struct CoinJoinCredits { CAmount m_anonymized{0}; CAmount m_denominated{0}; bool is_unconfirmed{false}; diff --git a/test/util/data/non-backported.txt b/test/util/data/non-backported.txt index 05fc3f1cd774b..6270f65e029fc 100644 --- a/test/util/data/non-backported.txt +++ b/test/util/data/non-backported.txt @@ -50,5 +50,6 @@ src/util/ranges.h src/util/ranges_set.* src/util/wpipe.* src/wallet/bip39* +src/wallet/coinjoin.* src/wallet/hdchain.* src/hash_x11.h