Make anonymity calculation irrelevant for coin selection in coinjoins
MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -445,8 +445,7 @@ public class CoinJoinClient
.Where(x => parameters.AllowedInputAmounts.Contains(x.Amount))
.Where(x => parameters.AllowedInputTypes.Any(t => x.ScriptPubKey.IsScriptType(t)))
.ToShuffled() // Preshuffle before ordering.
- .OrderBy(x => x.HdPubKey.AnonymitySet)
- .ThenByDescending(y => y.Amount)
+ .OrderByDescending(y => y.Amount)
.ToArray();
// How many inputs do we want to provide to the mix?
@@ -459,28 +458,23 @@ public class CoinJoinClient
// Select a group of coins those are close to each other by Anonimity Score.
Dictionary<int, IEnumerable<SmartCoin>> groups = new();
- // I can take more coins those are already reached the minimum privacy threshold.
- for (int i = 0; i < nonPrivateFilteredCoins.Length; i++)
- {
- // Make sure the group can at least register an output even after paying fees.
- var group = filteredCoins.Skip(i).Take(inputCount);
-
- if (group.Count() < Math.Min(filteredCoins.Length, inputCount))
- {
- break;
- }
-
- TryAddGroup(groups, group, parameters);
- }
-
// We can potentially add a lot more groups to improve results.
var sw1 = Stopwatch.StartNew();
foreach (var coin in nonPrivateFilteredCoins.OrderByDescending(x => x.Amount))
{
var sw2 = Stopwatch.StartNew();
- foreach (var group in filteredCoins.Except(new[] { coin }).CombinationsWithoutRepetition(inputCount - 1))
+ foreach (var group in filteredCoins
+ .Except(new[] { coin })
+ .CombinationsWithoutRepetition(inputCount - 1)
+ .Select(x => x.Concat(new[] { coin })))
{
- TryAddGroup(groups, group.Concat(new[] { coin }), parameters);
+ var inSum = group.Sum(x => x.EffectiveValue(parameters.MiningFeeRate, parameters.CoordinationFeeRate));
+ var outFee = parameters.MiningFeeRate.GetFee(Constants.P2wpkhOutputVirtualSize);
+ if (inSum >= outFee + parameters.AllowedOutputAmounts.Min)
+ {
+ var k = HashCode.Combine(group.OrderBy(x => x.TransactionId).ThenBy(x => x.Index));
+ groups.TryAdd(k, group);
+ }
if (sw2.Elapsed > TimeSpan.FromSeconds(1))
{
@@ -496,31 +490,14 @@ public class CoinJoinClient
}
}
- // Calculate the anonScore cost of input consolidation.
- List<(long Cost, IEnumerable<SmartCoin> Group)> anonScoreCosts = new();
- foreach (var group in groups)
- {
- var smallestAnon = group.Value.Min(x => x.HdPubKey.AnonymitySet);
- var cost = 0L;
- foreach (var coin in group.Value.Where(c => c.HdPubKey.AnonymitySet != smallestAnon))
- {
- cost += (coin.Amount.Satoshi * coin.HdPubKey.AnonymitySet) - (coin.Amount.Satoshi * smallestAnon);
- }
-
- anonScoreCosts.Add((cost, group.Value));
- }
-
- if (anonScoreCosts.Count == 0)
+ if (groups.Count == 0)
{
return ImmutableList<SmartCoin>.Empty;
}
- var bestCost = anonScoreCosts.Min(g => g.Cost);
- var bestCostGroups = anonScoreCosts.Where(x => x.Cost == bestCost).Select(x => x.Group);
-
// Select the group where the less coins coming from the same tx.
- var bestRep = bestCostGroups.Select(x => GetReps(x)).Min(x => x);
- var bestRepGroups = bestCostGroups.Where(x => GetReps(x) == bestRep);
+ var bestRep = groups.Values.Select(x => GetReps(x)).Min(x => x);
+ var bestRepGroups = groups.Values.Where(x => GetReps(x) == bestRep);
var bestgroup = bestRepGroups
.ToShuffled()
@@ -532,20 +509,6 @@ public class CoinJoinClient
private static int GetReps(IEnumerable<SmartCoin> group)
=> group.GroupBy(x => x.TransactionId).Sum(coinsInTxGroup => coinsInTxGroup.Count() - 1);
- private static bool TryAddGroup(IDictionary<int, IEnumerable<SmartCoin>> groups, IEnumerable<SmartCoin> group, RoundParameters parameters)
- {
- var inSum = group.Sum(x => x.EffectiveValue(parameters.MiningFeeRate, parameters.CoordinationFeeRate));
- var outFee = parameters.MiningFeeRate.GetFee(Constants.P2wpkhOutputVirtualSize);
- if (inSum >= outFee + parameters.AllowedOutputAmounts.Min)
- {
- var k = HashCode.Combine(group.OrderBy(x => x.TransactionId).ThenBy(x => x.Index));
-
- return CollectionExtensions.TryAdd(groups, k, group);
- }
-
- return false;
- }
-
public bool IsRoundEconomic(FeeRate roundFeeRate)
{
if (FeeRateMedianTimeFrame == default)

Make anonymity calculation irrelevant for coin selection in coinjoins