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

Coin selection improvement #8938

Closed

Conversation

onvej-sl
Copy link
Contributor

Coauthored with @nopara73 and @andrewkozlik.

int inputCount = Math.Min(
privateCoins.Length + allowedNonPrivateCoins.Count,
consolidationMode ? MaxInputsRegistrableByWallet : GetInputTarget(utxoCount, minUtxoCountTarget, rnd));
Copy link
Contributor

Choose a reason for hiding this comment

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

Note that these things got removed because the implementation down aims to address the same issue, just in a more intelligent way.

foreach (var coin in finalCandidate
.Except(new [] {selectedNonPrivateCoin})
Copy link
Contributor

Choose a reason for hiding this comment

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

Note this makes sure that selectedNonPrivateCoin participates, as that's what we want to mix the most.

Copy link
Contributor

@nopara73 nopara73 Aug 18, 2022

Choose a reason for hiding this comment

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

This is actually unrelated to this PR, just a minor bugfix.

@@ -815,6 +812,30 @@ private void LogCoinJoinSummary(ImmutableArray<AliceClient> registeredAliceClien
}
}

double winnerCost = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

@onvej-sl would you mind adding comments to what we're trying to do here?

winner = bestReducedWinner;
winnerCost = minCost;
}

if (winner.Count != finalCandidate.Count())
{
Logger.LogDebug($"Optimizing selection, removing coins coming from the same tx.");
Copy link
Contributor

Choose a reason for hiding this comment

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

The logging is now incorrect here. It was tailor made to the previous logic, but the added logic changes things.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd suggest to move these logs up before the new code was added, then duplicate the logs here as well so we can see how the new code changed the selection.

@@ -815,6 +812,30 @@ private void LogCoinJoinSummary(ImmutableArray<AliceClient> registeredAliceClien
}
}

double winnerCost = 0;
while ((winner.Sum(x => x.Amount) > liquidityClue) && (winnerCost/winner.Sum(x => x.Amount) < 3))
Copy link
Contributor

Choose a reason for hiding this comment

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

3 is a magic number here, this I'll test and figure out very well after we have the logs: https://github.com/zkSNACKs/WalletWasabi/pull/8938/files#r948849346

@stale
Copy link

stale bot commented Oct 18, 2022

This has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Oct 18, 2022
@nopara73 nopara73 removed the stale label Oct 19, 2022
@nopara73
Copy link
Contributor

FTR @turbolay and @Szpoti are creating unit tests, with which hopefully we can finetune this PR.

Copy link
Contributor

@Szpoti Szpoti left a comment

Choose a reason for hiding this comment

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

Good changes, actually works better, but in cases when the MaxWeightedAnonLoss is low (as it's expected for some to be low) this can cause an infinite loop, as removing coins from the winner doesn't decrease the anonLoss below the threshold.

Comment on lines +827 to +831
if (anonLoss <= bestAnonLoss)
{
bestAnonLoss = anonLoss;
bestReducedWinner = reducedWinner.ToList();
}
Copy link
Contributor

@Szpoti Szpoti Nov 4, 2022

Choose a reason for hiding this comment

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

Suggested change
if (anonLoss <= bestAnonLoss)
{
bestAnonLoss = anonLoss;
bestReducedWinner = reducedWinner.ToList();
}
if (anonLoss <= bestAnonLoss)
{
bestAnonLoss = anonLoss;
bestReducedWinner = reducedWinner.ToList();
}
if (winner == bestReducedWinner)
{
break;
}

My thinking: Prevent the infinite loop, by checking if we couldn't reduce our anonLoss no further below our expected threshold before. If so, use our remaining coins in the round.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as removing coins from the winner doesn't decrease the anonLoss below the threshold

I don't think this is the problem. You can always remove the coin with the highest anonymity, which decreases the anonymity loss.

The problem arises when winner contains exactly the same coins as selectedNonPrivateCoin, which means there is no coin that can be removed.

Anyway, the change you suggested does solve the problem.

Do you agree, @andrewkozlik?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good changes, actually works better, but in cases when the MaxWeightedAnonLoss is low (as it's expected for some to be low) this can cause an infinite loop, as removing coins from the winner doesn't decrease the anonLoss below the threshold.

By the way, did you actually experienced the algorithm ends up in an endless loop? @Szpoti

Choose a reason for hiding this comment

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

The reason why Ondřej is asking is because I think this should not enter an infinite loop.

Firstly selectedNonPrivateCoin is a single coin. There are two possible cases:

  1. winner contains one coin. Then this coin is selectedNonPrivateCoin and winnerAnonLoss is 0, therefore the while-loop will end, because the second condition isn't satisfied (assuming that MaxWeightedAnonLoss is non-negative, which it certainly shouldn't be).
  2. winner contains more than one coin. Then the foreach-loop will select one of the coins in winner which is not selectedNonPrivateCoin and it will remove it.

So either the while-cycle ends or a coin is removed. So the change proposed by @Szpoti is OK, but it seems to me that it's unreachable.

Copy link
Contributor

@turbolay turbolay Nov 8, 2022

Choose a reason for hiding this comment

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

Yes, we are running into an infinite loop in the case that all GetAnonLoss(reducedWinner); are higher than winnerAnonLoss. I get that intuitively it should not happen, but it seems that it can.

During testing, here's a case where it happened:

winner =
(Index, Amount, AnonymitySet)
(0, 0.04782969, 29),
(1, 0.11665525, 9),
(2, 0.04782969, 9)

winnerAnonLoss = 4.51

selectedNonPrivateCoin = coin with index 0

anonLoss without 1 = 10
anonLoss without 2 = 5.82

-> infinite loop

Choose a reason for hiding this comment

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

Good job guys! We didn't realize that after the change in 346ed7c the removal of coins is no longer monotonic in the anon loss...

Copy link
Contributor

Choose a reason for hiding this comment

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

@andrewkozlik do you ACK @Szpoti 's suggested change or is there further work to do to fix this problem?
This PR makes a great difference it would be nice to merge it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I discussed it with Andrew a bit before he went on vacation and we concluded that the suggested change is OK.

Copy link
Contributor

Choose a reason for hiding this comment

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

Failed to push to the remote repository. See the Output window for more details.
Pushing pr/8938
Pushing refs/heads/pr/8938
Remote: Permission to trezor/WalletWasabi.git denied to molnard.
Error encountered while pushing to the remote repository: Git failed with a fatal error.
Git failed with a fatal error.
unable to access 'https://github.com/trezor/WalletWasabi.git/': The requested URL returned error: 403

Created another PR https://github.com/zkSNACKs/WalletWasabi/pull/9555/files?diff=split&w=1

@MaxHillebrand
Copy link
Contributor

It seems that this greatly reduces the client side randomness in regards to input and output registration. Even though this increases reliability and speed, I think it does decrease privacy. Maybe it's worth to consider to keep the randomization for the privacy profile only?

(this might be a nonsense comment, I didn't look at the code)

@turbolay
Copy link
Contributor

It seems that this greatly reduces the client side randomness in regards to input and output registration. Even though this increases reliability and speed, I think it does decrease privacy. Maybe it's worth to consider to keep the randomization for the privacy profile only?

On master, we could select more or less any coin randomly, now we will remove some of these coins if they harm AnonScore.
With figures from the test, we are going from a 150% spread in term of number of rounds necessary to reach a certain value of AnonScore to a 10% spread, with a great optimization in any case.

Also, it only really applies for big values of required AnonScore, so only “Anonymous” profile, but this profile is by itself random (from 50 to 100) so the spread is still huge.

I can't think of any heuristics we could use to recognize WW inputs now that the AnonScore gain will be more consistent than before. Lot of things are still random (we select a random order of coins, number of inputs is also more or less random, we alternate coin selection etc…).

@molnard
Copy link
Contributor

molnard commented Nov 16, 2022

#8938 (comment)

@molnard molnard closed this Nov 16, 2022
@molnard
Copy link
Contributor

molnard commented Nov 16, 2022

Continuation #8938

@molnard
Copy link
Contributor

molnard commented Nov 16, 2022

#9555 got merged.

@nopara73 @andrewkozlik @onvej-sl @turbolay @Szpoti @MaxHillebrand awesome work 🥇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants