-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add more variance to coin selection in PS mixing #2261
Conversation
concept ACK, I think the selection could be a little bit better than this in such a way that any input could be selected (rather than highest, middlest(¯\(ツ)/¯), lowest). But with this in conjunction with 8fe3811, this should be a major improvement, while not decreasing speed much. The only situation I am worried about here is the mixing of a single input, but even that should be fine with 8fe3811 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See inline: Spacing after if
and for
Looks like these files as a whole need a redo, I might do that after this gets merged
src/privatesend-client.cpp
Outdated
// 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)) { | ||
LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- Couldn't match %d denominations %d %d (%s)\n", dsq.nInputCount, vecBits.front(), dsq.nDenom, CPrivateSend::GetDenominationsToString(dsq.nDenom)); | ||
if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, nMinAmount, nMaxAmount, vecTxDSInTmp, vCoinsTmp, nValueInTmp, 0, privateSendClient.nPrivateSendRounds, true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(
-> if (
;)
src/privatesend-client.cpp
Outdated
if (privateSendClient.nLiquidityProvider || 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++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for(
-> for (
src/privatesend-client.cpp
Outdated
// 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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(
-> if (
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--) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for(
-> for (
// 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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(
-> if (
src/wallet/wallet.cpp
Outdated
@@ -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 && mapRecentTxIds.find(out.tx->GetHash()) != mapRecentTxIds.end()) continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if(
-> if (
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm following the surrounding code format here intentionally (to improve readability).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree on that, if a general code cleanup is needed for the touched code, that's better in a separate PR. If a small code cleanup is enough, e.g. in just the line that is being touched, then in the same commit is ok IMHO.
… high" one someties
If failed when started from the middle try mixing from the edges right away. Note: liqudity providers always start from 0
Rebased and force-pushed. Re all the formatting comments: new line has such for readability reasons and I didn't fix code style in others (old ones) because of the "do not mix functionality and refactoring in one PR" principle https://github.com/dashpay/dash/blob/master/CONTRIBUTING.md#pull-request-philosophy |
src/privatesend-client.cpp
Outdated
// Note: liqudity providers always start from 0 | ||
int nRoundStart = GetRandInt(4) == 0 | ||
? (privateSendClient.nLiquidityProvider ? 0 : (privateSendClient.nPrivateSendRounds / 2)) | ||
: (fMixLowest ? 0 : privateSendClient.nPrivateSendRounds); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you convert this into if/else statements? Hard to read and using if/else would make this easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not exactly if/else but should be simpler to read now imo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about this?
diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp
index bf1d60f2f..ac0f74fe1 100644
--- a/src/privatesend-client.cpp
+++ b/src/privatesend-client.cpp
@@ -1185,9 +1185,13 @@ bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman)
// 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 = fScanFromTheMiddle
- ? privateSendClient.nPrivateSendRounds / 2
- : (fMixLowest ? 0 : privateSendClient.nPrivateSendRounds);
+
+ int nRoundStart = privateSendClient.nPrivateSendRounds;
+ if (fScanFromTheMiddle) {
+ nRoundStart = privateSendClient.nPrivateSendRounds / 2;
+ } else if (fMixLowest) {
+ nRoundStart = 0;
+ }
// Submit transaction to the pool if we get here
if (privateSendClient.nLiquidityProvider || fMixLowest) {
I find this much more clear, as it's immediately visible that there are 3 possible cases. With the nested ternary operators, it's very hard to see this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, makes sense. replaced with e0b0f4e
src/wallet/wallet.cpp
Outdated
{ | ||
std::map<uint256, uint8_t> mapRecentTxIds; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why std::map
and not std::set
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooops, initial idea was to count them and then maybe sort etc. but then I figured out that I just need unique ones and... forgot about the map thing completely 🙈 Good catch 👍
src/wallet/wallet.cpp
Outdated
@@ -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 && mapRecentTxIds.find(out.tx->GetHash()) != mapRecentTxIds.end()) continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree on that, if a general code cleanup is needed for the touched code, that's better in a separate PR. If a small code cleanup is enough, e.g. in just the line that is being touched, then in the same commit is ok IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK
@@ -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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But will not this lead to the situation that most mixing transactions will contain only 1 input from each participant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it shouldn't. Even if it does that's fine for privacy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's very fine for privacy. But mixing time will increase significantly. And mixing will cost much more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sounds like a valid concern. I'll have a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fees would go up, but I see no situation where a participant would only be able to submit one denom, maybe the first mix when they are all fresh out of create denoms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PastaPastaPasta not only after create denoms.
What we have: in most cases (nRoundStart = privateSendClient.nPrivateSendRounds
) mixing function will select inputs with highest round originating only from different Txs.
After "create denoms" the participant will be able to provide from 1 to several inputs (depending on amount he is trying to mix - can be several "create denoms" Txs).
And after every mixing round where participant will be able to provide more than 1 input, on the next round in most cases he will not be able to provide more than 1 input because all inputs with highest round will be from the same Tx.
The number of inputs in this situation will always tends to one.
I like the concept but what is the motivation behind randomly choosing to scan from
Wouldn't this approach make certain I think it might be better and more simple if we sometimes choose a random input instead of scanning. SelectCoinsByDenominations used by PrepareDenominate seems to choose a random input between nPrivateSendRoundsMin and nPrivateSendRoundsMax. My idea would look something like this: (totally untested)
|
@Antti-Kaikkonen lowest to highest, vice versa or from the middle to whatever edge - all of them are not about denominations but about number of rounds, so no, there should be no preferences for specific denoms I think. Motivation for slightly randomising it for rounds is basically to make it harder to guess what's going on, otherwise you know it's highest to lowest all the time, so maybe someone could use this as a heuristic or smth. Also, pls see an extension of this idea in #2275 (avoiding single input mix) and an alternative solution in #2278 (mixing rounds with most inputs first). |
@UdjinM6 yeah rounds is what I mean and not denominations. I think it might be better to sometimes simply pick a random input rather than changing the scan direction because it will randomly choose from a larger set of inputs. |
@Antti-Kaikkonen Probably. That was one of the ideas I had before #2278 tbh ;) but then I realized that picking random rounds have to be balanced with the speed of mixing somehow and picking rounds with the most inputs improves mixing speed while also achieving some kind of shuffling/randomization as a free bonus, see PR description there. |
This should make it harder to analyse mixing sessions. See individual commits, should be self-explanatory. Client side only, requires no protobump.
Note: this includes #2259 , will rebase after #2259 is merged.@PastaPastaPasta @Antti-Kaikkonen