Elevate out variables for better debugging.
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -256,17 +256,20 @@ namespace WalletWasabi.Backend.Controllers
var acceptedBlindedOutputScripts = new List<uint256>();
- // Check if inputs have enough coins.
+ // Calculate expected networkfee to pay after base denomination.
int inputCount = inputs.Count();
+ Money networkFeeToPayAfterBaseDenomination = (inputCount * round.FeePerInputs) + (2 * round.FeePerOutputs);
+
+ // Check if inputs have enough coins.
Money inputSum = inputs.Sum(x => x.Amount);
- Money networkFeeToPay = (inputCount * round.FeePerInputs) + (2 * round.FeePerOutputs);
- Money changeAmount = inputSum - (round.MixingLevels.GetBaseDenomination() + networkFeeToPay);
+ Money changeAmount = (inputSum - (round.MixingLevels.GetBaseDenomination() + networkFeeToPayAfterBaseDenomination));
if (changeAmount < Money.Zero)
{
- return BadRequest($"Not enough inputs are provided. Fee to pay: {networkFeeToPay.ToString(false, true)} BTC. Round denomination: {round.MixingLevels.GetBaseDenomination().ToString(false, true)} BTC. Only provided: {inputSum.ToString(false, true)} BTC.");
+ return BadRequest($"Not enough inputs are provided. Fee to pay: {networkFeeToPayAfterBaseDenomination.ToString(false, true)} BTC. Round denomination: {round.MixingLevels.GetBaseDenomination().ToString(false, true)} BTC. Only provided: {inputSum.ToString(false, true)} BTC.");
}
acceptedBlindedOutputScripts.Add(blindedOutputs.First());
+ Money networkFeeToPay = networkFeeToPayAfterBaseDenomination;
// Make sure we sign the proper number of additional blinded outputs.
var moneySoFar = Money.Zero;
for (int i = 1; i < blindedOutputCount; i++)

MODIFY
WalletWasabi/Models/ChaumianCoinJoin/CcjRound.cs
WalletWasabi/Models/ChaumianCoinJoin/CcjRound.cs
@@ -321,7 +321,10 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
spentCoins.Add(input);
}
- Money changeAmount = alice.InputSum - alice.NetworkFeeToPay - newDenomination - coordinatorBaseFeePerAlice;
+ Money changeAmount = alice.InputSum;
+ changeAmount -= alice.NetworkFeeToPay;
+ changeAmount -= newDenomination;
+ changeAmount -= coordinatorBaseFeePerAlice;
if (tinkerWithAdditionalMixingLevels)
{
@@ -331,15 +334,17 @@ namespace WalletWasabi.Models.ChaumianCoinJoin
var denominationOutputCount = transaction.Outputs.Count(x => x.Value == level.Denomination);
if (denominationOutputCount <= 1) break;
- changeAmount -= (level.Denomination + FeePerOutputs + (level.Denomination.Percentage(CoordinatorFeePercent * denominationOutputCount)));
+ changeAmount -= FeePerOutputs;
+ changeAmount -= level.Denomination;
+ changeAmount -= level.Denomination.Percentage(CoordinatorFeePercent * denominationOutputCount);
}
}
if (changeAmount > Money.Zero) // If the coordinator fee would make change amount to be negative or zero then no need to pay it.
{
Money minimumOutputAmount = Money.Coins(0.0001m); // If the change would be less than about $1 then add it to the coordinator.
- Money onePercentOfDenomination = newDenomination.Percentage(1m); // If the change is less than about 1% of the newDenomination then add it to the coordinator fee.
- Money minimumChangeAmount = Math.Max(minimumOutputAmount, onePercentOfDenomination);
+ Money somePercentOfDenomination = newDenomination.Percentage(0.7m); // If the change is less than about 0.7% of the newDenomination then add it to the coordinator fee.
+ Money minimumChangeAmount = Math.Max(minimumOutputAmount, somePercentOfDenomination);
if (changeAmount < minimumChangeAmount)
{
coordinatorFee += changeAmount;

MODIFY
WalletWasabi/Services/CcjClient.cs
WalletWasabi/Services/CcjClient.cs
@@ -337,7 +337,7 @@ namespace WalletWasabi.Services
// Make sure change is counted.
Money minAmountBack = ongoingRound.CoinsRegistered.Sum(x => x.Amount); // Start with input sum.
// Do outputs.lenght + 1 in case the server estimated the network fees wrongly due to insufficient data in an edge case.
- Money networkFeesAfterOutputs = ongoingRound.State.FeePerOutputs * (myOutputs.Length + 1);
+ Money networkFeesAfterOutputs = ongoingRound.State.FeePerOutputs * (ongoingRound.Registration.AliceClient.RegisteredAddresses.Length + 1); // Use registered addresses here, because network fees are decided at inputregistration.
Money networkFeesAfterInputs = ongoingRound.State.FeePerInputs * ongoingRound.Registration.CoinsRegistered.Count();
Money networkFees = networkFeesAfterOutputs + networkFeesAfterInputs;
minAmountBack -= networkFees; // Minus miner fee.
@@ -356,7 +356,8 @@ namespace WalletWasabi.Services
}
// If there's no change output then coordinator protection may happened:
- if (!myOutputs.Select(x => x.ScriptPubKey).Contains(ongoingRound.Registration.ChangeAddress.ScriptPubKey))
+ bool gotChange = myOutputs.Select(x => x.ScriptPubKey).Contains(ongoingRound.Registration.ChangeAddress.ScriptPubKey);
+ if (!gotChange)
{
Money minimumOutputAmount = Money.Coins(0.0001m); // If the change would be less than about $1 then add it to the coordinator.
Money baseDenomination = indistinguishableOutputs.First().value;
@@ -368,7 +369,8 @@ namespace WalletWasabi.Services
if (amountBack < minAmountBack && !amountBack.Almost(minAmountBack, Money.Satoshis(10000))) // Just in case.
{
- throw new NotSupportedException($"Coordinator did not add enough value to our outputs in the coinjoin. Missing: {(minAmountBack - amountBack).Satoshi} satoshis.");
+ Money diff = minAmountBack - amountBack;
+ throw new NotSupportedException($"Coordinator did not add enough value to our outputs in the coinjoin. Missing: {diff.Satoshi} satoshis.");
}
var signedCoinJoin = unsignedCoinJoin.Clone();

Elevate out variables for better debugging.