Add Yuval's fix on his prev solution
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Client/GreedyDecomposerTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Client/GreedyDecomposerTests.cs
@@ -32,7 +32,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Client
public void Decompose1Test(long amount, long[] expected)
{
var decomposer = new GreedyDecomposer(new Money[] { new(1L), new(2L), new(5L), new(10L) });
- var amounts = decomposer.Decompose(Money.Satoshis(amount), new FeeRate(0L));
+ var amounts = decomposer.Decompose(Money.Satoshis(amount), Money.Zero);
Assert.Equal(Money.Satoshis(amount), amounts.Sum());
Assert.Equal(expected, amounts.Select(x => x.Satoshi));
}
@@ -62,9 +62,38 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Client
public void Decompose2Test(long amount, long[] expected)
{
var decomposer = new GreedyDecomposer(new Money[] { new(1L), new(3L), new(11L) });
- var amounts = decomposer.Decompose(Money.Satoshis(amount), new FeeRate(0L));
+ var amounts = decomposer.Decompose(Money.Satoshis(amount), Money.Zero);
Assert.Equal(Money.Satoshis(amount), amounts.Sum());
Assert.Equal(expected, amounts.Select(x => x.Satoshi));
}
+
+ [Theory]
+ [InlineData(0, new long[] { })]
+ [InlineData(1, new long[] { })]
+ [InlineData(2, new long[] { 1 })]
+ [InlineData(3, new long[] { 1 })]
+ [InlineData(4, new long[] { 3 })]
+ [InlineData(5, new long[] { 3 })]
+ [InlineData(6, new long[] { 3, 1 })]
+ [InlineData(7, new long[] { 3, 1 })]
+ [InlineData(8, new long[] { 3, 3 })]
+ [InlineData(9, new long[] { 3, 3 })]
+ [InlineData(10, new long[] { 3, 3, 1 })]
+ [InlineData(11, new long[] { 3, 3, 1 })]
+ [InlineData(12, new long[] { 11 })]
+ [InlineData(13, new long[] { 11 })]
+ [InlineData(14, new long[] { 11, 1 })]
+ [InlineData(15, new long[] { 11, 1 })]
+ [InlineData(16, new long[] { 11, 3 })]
+ [InlineData(17, new long[] { 11, 3 })]
+ [InlineData(18, new long[] { 11, 3, 1 })]
+ [InlineData(19, new long[] { 11, 3, 1 })]
+ [InlineData(38, new long[] { 11, 11, 11, 1 })]
+ public void DecomposeWithFee(long amount, long[] expected)
+ {
+ var decomposer = new GreedyDecomposer(new Money[] { new(1L), new(3L), new(11L) });
+ var amounts = decomposer.Decompose(Money.Satoshis(amount), new Money(1L));
+ Assert.Equal(expected, amounts.Select(x => x.Satoshi));
+ }
}
}

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -69,7 +69,7 @@ namespace WalletWasabi.WabiSabi.Client
aliceClients = await ConfirmConnectionsAsync(aliceClients, roundState.MaxVsizeAllocationPerAlice, roundState.ConnectionConfirmationTimeout, cancellationToken).ConfigureAwait(false);
// Re-issuances.
- DependencyGraph dependencyGraph = DependencyGraph.ResolveCredentialDependencies(aliceClients.Select(a => a.Coin), outputTxOuts, roundState.FeeRate);
+ DependencyGraph dependencyGraph = DependencyGraph.ResolveCredentialDependencies(aliceClients.Select(a => a.Coin), outputTxOuts, roundState.FeeRate, roundState.MaxVsizeAllocationPerAlice);
DependencyGraphResolver dgr = new(dependencyGraph);
var bobClient = CreateBobClient(roundState);
var outputCredentials = await dgr.ResolveAsync(aliceClients, bobClient, cancellationToken).ConfigureAwait(false);
@@ -161,7 +161,7 @@ namespace WalletWasabi.WabiSabi.Client
GreedyDecomposer greedyDecomposer = new(allDenominations);
var amounts = Coins.Select(c => c.Amount);
var sum = amounts.Sum();
- var denominations = greedyDecomposer.Decompose(sum, feeRate);
+ var denominations = greedyDecomposer.Decompose(sum, feeRate.GetFee(31));
if (sum != denominations.Sum())
{
throw new InvalidOperationException("Decomposed amounts and inputs sum not equal.");

MODIFY
WalletWasabi/WabiSabi/Client/CredentialDependencies/DependencyGraph.cs
WalletWasabi/WabiSabi/Client/CredentialDependencies/DependencyGraph.cs
@@ -46,7 +46,7 @@ namespace WalletWasabi.WabiSabi.Client.CredentialDependencies
/// and may contain additional nodes if reissuance requests are
/// required.</remarks>
///
- public static DependencyGraph ResolveCredentialDependencies(IEnumerable<Coin> inputs, IEnumerable<TxOut> outputs, FeeRate feerate)
+ public static DependencyGraph ResolveCredentialDependencies(IEnumerable<Coin> inputs, IEnumerable<TxOut> outputs, FeeRate feerate, long vsizeAllocationPerInput)
{
var inputSizes = inputs.Select(x => x.ScriptPubKey.EstimateInputVsize());
var effectiveValues = Enumerable.Zip(inputs, inputSizes, (coin, size) => coin.EffectiveValue(feerate));
@@ -60,7 +60,7 @@ namespace WalletWasabi.WabiSabi.Client.CredentialDependencies
var effectiveCosts = Enumerable.Zip(outputs, outputSizes, (txout, size) => txout.EffectiveCost(feerate));
return ResolveCredentialDependencies(
- Enumerable.Zip(effectiveValues.Select(a => (ulong)a.Satoshi), inputSizes.Select(i => (ulong)i), ImmutableArray.Create).Cast<IEnumerable<ulong>>(),
+ Enumerable.Zip(effectiveValues.Select(a => (ulong)a.Satoshi), inputSizes.Select(i => (ulong)(vsizeAllocationPerInput - i)), ImmutableArray.Create).Cast<IEnumerable<ulong>>(),
Enumerable.Zip(effectiveCosts.Select(a => (ulong)a.Satoshi), outputSizes.Select(i => (ulong)i), ImmutableArray.Create).Cast<IEnumerable<ulong>>()
);
}

MODIFY
WalletWasabi/WabiSabi/Client/GreedyDecomposer.cs
WalletWasabi/WabiSabi/Client/GreedyDecomposer.cs
@@ -17,16 +17,16 @@ namespace WalletWasabi.WabiSabi.Client
public ImmutableList<Money> Denominations { get; }
- public IEnumerable<Money> Decompose(Money amount, FeeRate feeRate)
+ public IEnumerable<Money> Decompose(Money amount, Money costPerOutput)
{
var i = 0;
var denomination = Denominations[i];
- while (amount > Money.Zero)
+ while (amount - costPerOutput > Money.Zero)
{
- if (denomination <= amount)
+ if (denomination <= amount - costPerOutput)
{
yield return denomination;
- amount -= denomination + feeRate.GetFee(31); // TODO how to determine output size?;
+ amount -= denomination + costPerOutput;
}
else if ((i + 1) < Denominations.Count)
{

Add Yuval's fix on his prev solution