diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index e123f8c6733f..01ce35f0cabf 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1036,7 +1036,7 @@ bool CPrivateSendClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymize CAmount nMaxAmount = nBalanceNeedsAnonymized; // Try to match their denominations if possible, select exact number of denominations - if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, nMinAmount, nMaxAmount, vecTxDSInTmp, vCoinsTmp, nValueInTmp, 0, privateSendClient.nPrivateSendRounds)) { + if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, nMinAmount, nMaxAmount, vecTxDSInTmp, vCoinsTmp, nValueInTmp, 0, privateSendClient.nPrivateSendRounds, true)) { LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- Couldn't match %d denominations %d (%s)\n", vecBits.front(), dsq.nDenom, CPrivateSend::GetDenominationsToString(dsq.nDenom)); continue; } @@ -1180,25 +1180,45 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) std::string strError; std::vector vecTxDSInRet; std::vector vecTxOutRet; + // lean towards "highest" branch but still mix via "lowest" one someties + bool fMixLowest = privateSendClient.nLiquidityProvider || (GetRandInt(4) == 0); + // lean towards edges but still mix starting from the middle someties + // Note: liqudity providers always start from 0 + bool fScanFromTheMiddle = (privateSendClient.nLiquidityProvider == 0) && (GetRandInt(4) == 0); + + int nRoundStart{0}; + if (fScanFromTheMiddle) { + nRoundStart = privateSendClient.nPrivateSendRounds / 2; + } else if (!fMixLowest) { + nRoundStart = privateSendClient.nPrivateSendRounds; + } // Submit transaction to the pool if we get here - if (privateSendClient.nLiquidityProvider) { - // Try to use only inputs with the same number of rounds starting from the lowest number of rounds possible - for(int i = 0; i< privateSendClient.nPrivateSendRounds; i++) { - if(PrepareDenominate(i, i + 1, strError, vecTxDSInRet, vecTxOutRet)) { - LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); - return SendDenominate(vecTxDSInRet, vecTxOutRet, connman); + if (fMixLowest) { + // Try to use only inputs with the same number of rounds, from low to high + while (true) { + for(int i = nRoundStart; i < privateSendClient.nPrivateSendRounds; i++) { + if(PrepareDenominate(i, i + 1, strError, vecTxDSInRet, vecTxOutRet)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); + return SendDenominate(vecTxDSInRet, vecTxOutRet, connman); + } + LogPrint("privatesend", "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); } - LogPrint("privatesend", "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); + if (nRoundStart == 0) break; + nRoundStart = 0; } } else { - // Try to use only inputs with the same number of rounds starting from the highest number of rounds possible - for(int i = privateSendClient.nPrivateSendRounds; i > 0; i--) { - if(PrepareDenominate(i - 1, i, strError, vecTxDSInRet, vecTxOutRet)) { - LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); - return SendDenominate(vecTxDSInRet, vecTxOutRet, connman); + // Try to use only inputs with the same number of rounds, from high to low + while (true) { + for(int i = nRoundStart; i > 0; i--) { + if(PrepareDenominate(i - 1, i, strError, vecTxDSInRet, vecTxOutRet)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); + return SendDenominate(vecTxDSInRet, vecTxOutRet, connman); + } + LogPrint("privatesend", "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); } - LogPrint("privatesend", "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); + if (nRoundStart == privateSendClient.nPrivateSendRounds) break; + nRoundStart = privateSendClient.nPrivateSendRounds; } } @@ -1251,7 +1271,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds return false; } std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); - bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], CPrivateSend::GetMaxPoolAmount(), vecTxDSIn, vCoins, nValueIn, nMinRounds, nMaxRounds); + bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], CPrivateSend::GetMaxPoolAmount(), vecTxDSIn, vCoins, nValueIn, nMinRounds, nMaxRounds, true); if (nMinRounds >= 0 && !fSelected) { strErrorRet = "Can't select current denominated inputs"; return false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e4bf343a6dab..5eac1b238dab 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3007,8 +3007,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov return true; } -bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxDSInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) +bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxDSInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax, bool fNoDuplicateTxIds) { + std::set setRecentTxIds; + vecTxDSInRet.clear(); vCoinsRet.clear(); nValueRet = 0; @@ -3037,6 +3039,7 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount { // masternode-like input should not be selected by AvailableCoins now anyway //if(out.tx->vout[out.i].nValue == 1000*COIN) continue; + if(fNoDuplicateTxIds && setRecentTxIds.find(out.tx->GetHash()) != setRecentTxIds.end()) continue; if(nValueRet + out.tx->tx->vout[out.i].nValue <= nValueMax){ CTxIn txin = CTxIn(out.tx->GetHash(), out.i); @@ -3051,11 +3054,15 @@ bool CWallet::SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount vecTxDSInRet.push_back(CTxDSIn(txin, out.tx->tx->vout[out.i].scriptPubKey)); vCoinsRet.push_back(out); nDenomResult |= 1 << nBit; + setRecentTxIds.emplace(out.tx->GetHash()); + LogPrint("privatesend", "CWallet::SelectCoinsByDenominations -- hash %s nValue %d\n", out.tx->GetHash().ToString(), out.tx->tx->vout[out.i].nValue); } } } } + LogPrintf("CWallet::SelectCoinsByDenominations -- setRecentTxIds.size() %d\n", setRecentTxIds.size()); + return nValueRet >= nValueMin && nDenom == nDenomResult; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 27dd22ef1859..8fff1dbf5020 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -806,7 +806,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet, bool fUseInstantSend = false) const; // Coin selection - bool SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxDSInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax); + bool SelectCoinsByDenominations(int nDenom, CAmount nValueMin, CAmount nValueMax, std::vector& vecTxDSInRet, std::vector& vCoinsRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax, bool fNoDuplicateTxIds); bool GetCollateralTxDSIn(CTxDSIn& txdsinRet, CAmount& nValueRet) const; bool SelectCoinsDark(CAmount nValueMin, CAmount nValueMax, std::vector& vecTxInRet, CAmount& nValueRet, int nPrivateSendRoundsMin, int nPrivateSendRoundsMax) const;