Improve decomposition
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Client/AmountDecomposerTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Client/AmountDecomposerTests.cs
@@ -36,7 +36,7 @@ public class AmountDecomposerTests
var theirCoinEffectiveValues = GenerateRandomCoins().Take(30).Select(c => c.EffectiveValue(feeRate, CoordinationFeeRate.Zero)).ToList();
var allowedOutputAmountRange = new MoneyRange(Money.Satoshis(minOutputAmount), Money.Satoshis(ProtocolConstants.MaxAmountPerAlice));
- var amountDecomposer = new AmountDecomposer(feeRate, allowedOutputAmountRange, Constants.P2wpkhOutputSizeInBytes, availableVsize);
+ var amountDecomposer = new AmountDecomposer(feeRate, allowedOutputAmountRange, Constants.P2wpkhOutputSizeInBytes, Constants.P2wpkhInputVirtualSize, availableVsize);
var outputValues = amountDecomposer.Decompose(registeredCoinEffectiveValues, theirCoinEffectiveValues);
var totalEffectiveValue = registeredCoinEffectiveValues.Sum(x => x);

MODIFY
WalletWasabi/WabiSabi/Client/AmountDecomposer.cs
WalletWasabi/WabiSabi/Client/AmountDecomposer.cs
@@ -13,11 +13,13 @@ public class AmountDecomposer
/// <param name="feeRate">Bitcoin network fee rate the coinjoin is targeting.</param>
/// <param name="allowedOutputAmount">Range of output amount that's allowed to be registered.</param>
/// <param name="outputSize">Size of an output.</param>
+ /// <param name="outputSize">Size of an input.</param>
/// <param name="availableVsize">Available virtual size for outputs.</param>
- public AmountDecomposer(FeeRate feeRate, MoneyRange allowedOutputAmount, int outputSize, int availableVsize)
+ public AmountDecomposer(FeeRate feeRate, MoneyRange allowedOutputAmount, int outputSize, int inputSize, int availableVsize)
{
FeeRate = feeRate;
OutputSize = outputSize;
+ InputSize = inputSize;
AvailableVsize = availableVsize;
MinAllowedOutputAmountPlusFee = allowedOutputAmount.Min + OutputFee;
@@ -33,7 +35,9 @@ public class AmountDecomposer
public Money MaxAllowedOutputAmount { get; }
public Money OutputFee => FeeRate.GetFee(OutputSize);
+ public Money InputFee => FeeRate.GetFee(InputSize);
public int OutputSize { get; }
+ public int InputSize { get; }
public IOrderedEnumerable<ulong> DenominationsPlusFees { get; }
private IOrderedEnumerable<ulong> CreateDenominationsPlusFees()
@@ -250,7 +254,7 @@ public class AmountDecomposer
setCandidates.Add(
hash.ToHashCode(), // Create hash to ensure uniqueness.
- (naiveSet, loss + (ulong)naiveSet.Count * OutputFee)); // The cost is the remaining + output cost.
+ (naiveSet, loss + (ulong)naiveSet.Count * OutputFee + (ulong)naiveSet.Count * InputFee)); // The cost is the remaining + output cost + input cost.
// Create many decompositions for optimization.
var stdDenoms = denoms.Where(x => x <= myInputSum).Select(x => (long)x).ToArray();
@@ -273,7 +277,7 @@ public class AmountDecomposer
{
hash.Add(item);
}
- setCandidates.TryAdd(hash.ToHashCode(), (currentSet, myInputSum - (ulong)currentSet.Sum() + (ulong)count * OutputFee)); // The cost is the remaining + output cost.
+ setCandidates.TryAdd(hash.ToHashCode(), (currentSet, myInputSum - (ulong)currentSet.Sum() + (ulong)count * OutputFee + (ulong)count * InputFee)); // The cost is the remaining + output cost + input cost.
}
}

MODIFY
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
WalletWasabi/WabiSabi/Client/CoinJoinClient.cs
@@ -509,7 +509,7 @@ public class CoinJoinClient
// Calculate outputs values
var constructionState = roundState.Assert<ConstructionState>();
- AmountDecomposer amountDecomposer = new(roundState.FeeRate, roundState.CoinjoinState.Parameters.AllowedOutputAmounts, Constants.P2wpkhOutputSizeInBytes, (int)availableVsize);
+ AmountDecomposer amountDecomposer = new(roundState.FeeRate, roundState.CoinjoinState.Parameters.AllowedOutputAmounts, Constants.P2wpkhOutputSizeInBytes, Constants.P2wpkhInputVirtualSize, (int)availableVsize);
var theirCoins = constructionState.Inputs.Where(x => !registeredCoins.Any(y => x.Outpoint == y.Outpoint));
var registeredCoinEffectiveValues = registeredAliceClients.Select(x => x.EffectiveValue);
var theirCoinEffectiveValues = theirCoins.Select(x => x.EffectiveValue(roundState.FeeRate, roundState.CoordinationFeeRate));

Improve decomposition