From 617581e5c907645d27e0d9878e04bfa91fd79c28 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sat, 15 Sep 2018 22:01:52 +0300 Subject: [PATCH 1/6] Alternative solution: pick rounds with the most inputs available to mix first --- src/privatesend-client.cpp | 62 ++++++++++++++++++-------------------- src/privatesend-client.h | 2 +- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index 3f2552e8e1c2..85e987ff77a3 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1167,17 +1167,6 @@ void CPrivateSendClientManager::ProcessPendingDsaRequest(CConnman& connman) bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) { - // This is just a local helper - auto GetStartRound = [](bool fMixLowest, bool fScanFromTheMiddle) -> int - { - if (fScanFromTheMiddle) { - return privateSendClient.nPrivateSendRounds / 2; - } else if (!fMixLowest) { - return privateSendClient.nPrivateSendRounds - 1; - } - return 0; - }; - LOCK2(cs_main, pwalletMain->cs_wallet); std::string strError; @@ -1188,28 +1177,36 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) return false; } - // lean towards "highest" branch but still mix via "lowest" one someties - bool fMixLowest = privateSendClient.nLiquidityProvider || (GetRandInt(4) == 0); - // Try to use only inputs with the same number of rounds, from low to high, or vice versa - int nLoopStep = fMixLowest ? 1 : -1; - // 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 = GetStartRound(fMixLowest, fScanFromTheMiddle); - int nRoundEdge = GetStartRound(fMixLowest, false); + std::vector< std::pair > vecInputsByRounds; + // Note: liqudity providers are fine whith whatever numner of inputs they've got + bool fDryRun = privateSendClient.nLiquidityProvider == 0; - // Submit transaction to the pool if we get here - while (true) { - for (int i = nRoundStart; i >= 0 && i < privateSendClient.nPrivateSendRounds; i += nLoopStep) { - if (PrepareDenominate(i, i, strError, vecPSInOutPairs, vecPSInOutPairsTmp)) { - LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); + for (int i = 0; i < privateSendClient.nPrivateSendRounds; i++) { + if (PrepareDenominate(i, i, strError, vecPSInOutPairs, vecPSInOutPairsTmp, fDryRun)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); + if (!fDryRun) { return SendDenominate(vecPSInOutPairsTmp, connman); } + vecInputsByRounds.emplace_back(i, vecPSInOutPairsTmp.size()); + } else { LogPrint("privatesend", "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); } - if (nRoundStart == nRoundEdge) break; - nRoundStart = nRoundEdge; + } + + // more inputs first, for equal input count prefer the one with less rounds + std::sort(vecInputsByRounds.begin(), vecInputsByRounds.end(), [](const auto& a, const auto& b) { + return a.second > b.second || (a.second == b.second && a.first < b.first); + }); + + LogPrint("privatesend", "vecInputsByRounds for denom %d\n", nSessionDenom); + for (const auto& pair : vecInputsByRounds) { + LogPrint("privatesend", "vecInputsByRounds: rounds: %d, inputs: %d\n", pair.first, pair.second); + } + + int nRounds = vecInputsByRounds.begin()->first; + if (PrepareDenominate(nRounds, nRounds, strError, vecPSInOutPairs, vecPSInOutPairsTmp)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", nRounds); + return SendDenominate(vecPSInOutPairsTmp, connman); } // We failed? That's strange but let's just make final attempt and try to mix everything @@ -1259,7 +1256,7 @@ bool CPrivateSendClientSession::SelectDenominate(std::string& strErrorRet, std:: return true; } -bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair >& vecPSInOutPairsIn, std::vector< std::pair >& vecPSInOutPairsRet) +bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair >& vecPSInOutPairsIn, std::vector< std::pair >& vecPSInOutPairsRet, bool fDryRun) { std::vector vecBits; if (!CPrivateSend::GetDenominationsBits(nSessionDenom, vecBits)) { @@ -1292,7 +1289,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds if (vecSteps[nBit] >= nStepsMax) break; CAmount nValueDenom = vecStandardDenoms[nBit]; if (pair.second.nValue == nValueDenom) { - CScript scriptDenom = keyHolderStorage.AddKey(pwalletMain); + CScript scriptDenom = fDryRun ? CScript() : keyHolderStorage.AddKey(pwalletMain); vecPSInOutPairsRet.emplace_back(pair.first, CTxOut(nValueDenom, scriptDenom)); fFound = true; nDenomResult |= 1 << nBit; @@ -1301,8 +1298,8 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds break; } } - if (!fFound) { - // unlock unused coins + if (!fFound || fDryRun) { + // unlock unused coins and if we are not going to mix right away pwalletMain->UnlockCoin(pair.first.prevout); } } @@ -1317,7 +1314,6 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds return false; } - // We also do not care about full amount as long as we have right denominations return true; } diff --git a/src/privatesend-client.h b/src/privatesend-client.h index 90f6942f5974..8e9b8341c5f4 100644 --- a/src/privatesend-client.h +++ b/src/privatesend-client.h @@ -109,7 +109,7 @@ class CPrivateSendClientSession : public CPrivateSendBaseSession /// step 0: select denominated inputs and txouts bool SelectDenominate(std::string& strErrorRet, std::vector< std::pair >& vecPSInOutPairsRet); /// step 1: prepare denominated inputs and outputs - bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair >& vecPSInOutPairsIn, std::vector< std::pair >& vecPSInOutPairsRet); + bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair >& vecPSInOutPairsIn, std::vector< std::pair >& vecPSInOutPairsRet, bool fDryRun = false); /// step 2: send denominated inputs and outputs prepared in step 1 bool SendDenominate(const std::vector< std::pair >& vecPSInOutPairsIn, CConnman& connman); From 33c12af49526ebd0cf0e318d6b84296eb882964c Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 20 Sep 2018 16:12:59 +0300 Subject: [PATCH 2/6] randomly skip some inputs when we have at least one of this denom already --- src/privatesend-client.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index 85e987ff77a3..abf5ee370329 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1289,7 +1289,14 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds if (vecSteps[nBit] >= nStepsMax) break; CAmount nValueDenom = vecStandardDenoms[nBit]; if (pair.second.nValue == nValueDenom) { - CScript scriptDenom = fDryRun ? CScript() : keyHolderStorage.AddKey(pwalletMain); + CScript scriptDenom; + if (fDryRun) { + scriptDenom = CScript(); + } else { + // randomly skip some inputs when we have at least one of the same denom already + if (vecSteps[nBit] > 1 && GetRandInt(2)) break; + scriptDenom = keyHolderStorage.AddKey(pwalletMain); + } vecPSInOutPairsRet.emplace_back(pair.first, CTxOut(nValueDenom, scriptDenom)); fFound = true; nDenomResult |= 1 << nBit; From ab86bd0c08506246e3621c44ea1ba2d9d8f6b604 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 26 Sep 2018 18:24:20 +0300 Subject: [PATCH 3/6] More randomization in PrepareDenominate --- src/privatesend-client.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index abf5ee370329..7c95fbfabb57 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1268,16 +1268,15 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds pwalletMain->LockCoin(pair.first.prevout); } - // Try to add every needed denomination, repeat up to 5-PRIVATESEND_ENTRY_MAX_SIZE times. // NOTE: No need to randomize order of inputs because they were // initially shuffled in CWallet::SelectPSInOutPairsByDenominations already. - int nStepsMax = 5 + GetRandInt(PRIVATESEND_ENTRY_MAX_SIZE - 5 + 1); int nDenomResult{0}; std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); std::vector vecSteps(vecStandardDenoms.size(), 0); vecPSInOutPairsRet.clear(); + // Try to add up to PRIVATESEND_ENTRY_MAX_SIZE of every needed denomination for (const auto& pair: vecPSInOutPairsIn) { if (pair.second.nRounds < nMinRounds || pair.second.nRounds > nMaxRounds) { // unlock unused coins @@ -1286,7 +1285,7 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds } bool fFound = false; for (const auto& nBit : vecBits) { - if (vecSteps[nBit] >= nStepsMax) break; + if (vecSteps[nBit] >= PRIVATESEND_ENTRY_MAX_SIZE) break; CAmount nValueDenom = vecStandardDenoms[nBit]; if (pair.second.nValue == nValueDenom) { CScript scriptDenom; @@ -1294,7 +1293,12 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds scriptDenom = CScript(); } else { // randomly skip some inputs when we have at least one of the same denom already - if (vecSteps[nBit] > 1 && GetRandInt(2)) break; + if (vecSteps[nBit] > 1 && GetRandInt(2)) { + // still count it as a step to randomize number of inputs + // if we have more than (or exactly) PRIVATESEND_ENTRY_MAX_SIZE of them + ++vecSteps[nBit]; + break; + } scriptDenom = keyHolderStorage.AddKey(pwalletMain); } vecPSInOutPairsRet.emplace_back(pair.first, CTxOut(nValueDenom, scriptDenom)); From 4f45bf68ed65fe85ddf298dac516c30c4a21fe1d Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 26 Sep 2018 19:37:38 +0300 Subject: [PATCH 4/6] fix `vecSteps[nBit] >= 1 ?` and adjust speed/privacy ratio for more speed i.e. `GetRandInt(5) == 0` --- src/privatesend-client.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index 7c95fbfabb57..79fe9829c8ab 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1293,7 +1293,8 @@ bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds scriptDenom = CScript(); } else { // randomly skip some inputs when we have at least one of the same denom already - if (vecSteps[nBit] > 1 && GetRandInt(2)) { + // TODO: make it adjustable via options/cmd-line params + if (vecSteps[nBit] >= 1 && GetRandInt(5) == 0) { // still count it as a step to randomize number of inputs // if we have more than (or exactly) PRIVATESEND_ENTRY_MAX_SIZE of them ++vecSteps[nBit]; From d0cf51a4c8a3be9c3dfc2502f2629b376761ca07 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Tue, 9 Oct 2018 23:16:54 +0300 Subject: [PATCH 5/6] fix typos --- src/privatesend-client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index 79fe9829c8ab..e3b53a2b1f95 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1178,7 +1178,7 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) } std::vector< std::pair > vecInputsByRounds; - // Note: liqudity providers are fine whith whatever numner of inputs they've got + // Note: liqudity providers are fine with whatever number of inputs they've got bool fDryRun = privateSendClient.nLiquidityProvider == 0; for (int i = 0; i < privateSendClient.nPrivateSendRounds; i++) { From 9f7f141fff2ead27d15f5be2ec846cefe9758efa Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Wed, 10 Oct 2018 17:54:34 +0300 Subject: [PATCH 6/6] no comments --- src/privatesend-client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index e3b53a2b1f95..275705a1194c 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1178,7 +1178,7 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) } std::vector< std::pair > vecInputsByRounds; - // Note: liqudity providers are fine with whatever number of inputs they've got + // Note: liquidity providers are fine with whatever number of inputs they've got bool fDryRun = privateSendClient.nLiquidityProvider == 0; for (int i = 0; i < privateSendClient.nPrivateSendRounds; i++) {