Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pick rounds with the most inputs available to mix first #2278

Merged
merged 6 commits into from
Oct 15, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 44 additions & 36 deletions src/privatesend-client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<int, size_t> > vecInputsByRounds;
// Note: liqudity providers are fine whith whatever numner of inputs they've got
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/whith/with/

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/numner/number/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omg, 2 typos in one sentence 🙈 fixed :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually 3 typos 😆 "liquidity"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Expand Down Expand Up @@ -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<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet)
bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet, bool fDryRun)
{
std::vector<int> vecBits;
if (!CPrivateSend::GetDenominationsBits(nSessionDenom, vecBits)) {
Expand All @@ -1271,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<CAmount> vecStandardDenoms = CPrivateSend::GetStandardDenominations();
std::vector<int> 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
Expand All @@ -1289,10 +1285,23 @@ 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 = 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
// 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];
break;
}
scriptDenom = keyHolderStorage.AddKey(pwalletMain);
}
vecPSInOutPairsRet.emplace_back(pair.first, CTxOut(nValueDenom, scriptDenom));
fFound = true;
nDenomResult |= 1 << nBit;
Expand All @@ -1301,8 +1310,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);
}
}
Expand All @@ -1317,7 +1326,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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/privatesend-client.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class CPrivateSendClientSession : public CPrivateSendBaseSession
/// step 0: select denominated inputs and txouts
bool SelectDenominate(std::string& strErrorRet, std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet);
/// step 1: prepare denominated inputs and outputs
bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet);
bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsRet, bool fDryRun = false);
/// step 2: send denominated inputs and outputs prepared in step 1
bool SendDenominate(const std::vector< std::pair<CTxDSIn, CTxOut> >& vecPSInOutPairsIn, CConnman& connman);

Expand Down