fix fee estimation
MODIFY
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
WalletWasabi.Backend/Controllers/ChaumianCoinJoinController.cs
@@ -258,8 +258,8 @@ namespace WalletWasabi.Backend.Controllers
if (!allInputsConfirmed)
{
// Check if mempool would accept a fake transaction created with the registered inputs.
- // Fake outputs: 200 + 400 because the size of a bech32 input is roughly the size of an output. To emulate total CoinJoin size this'd be a 200 inputs + 400 outputs tx and 0.9/600=0.0015, which is well above the dust, so should be good.
- var result = await RpcClient.TestMempoolAcceptAsync(inputs, fakeOutputsCount: 200 + 400);
+ // Fake outputs: 12 maximum, because there's 11 denom level + change.
+ var result = await RpcClient.TestMempoolAcceptAsync(inputs, fakeOutputCount: 12, round.FeePerInputs, round.FeePerOutputs);
if (!result.accept)
{
return BadRequest($"Provided input is from an unconfirmed coinjoin, but a limit is reached: {result.rejectReason}");

MODIFY
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRound.cs
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRound.cs
@@ -1248,12 +1248,12 @@ namespace WalletWasabi.CoinJoin.Coordinator.Rounds
{
// Check if mempool would accept a fake transaction created with all the registered inputs.
var coinsToTest = Alices.SelectMany(alice => alice.Inputs);
- // Add the outputs by denomination level.
- var outputsAll = Alices.Sum(alice => EstimateBestMixingLevel(alice));
+ // Add the outputs by denomination level. Add 1 as estimation could be sometimes off by 1.
+ var outputCount = Alices.Sum(alice => EstimateBestMixingLevel(alice) + 1);
// Add the change outputs.
- outputsAll += Alices.Count;
+ outputCount += Alices.Count;
- return await RpcClient.TestMempoolAcceptAsync(coinsToTest, fakeOutputsCount: outputsAll).ConfigureAwait(false);
+ return await RpcClient.TestMempoolAcceptAsync(coinsToTest, fakeOutputCount: outputCount, FeePerInputs, FeePerOutputs).ConfigureAwait(false);
}
public int RemoveAlicesBy(params Guid[] ids)

MODIFY
WalletWasabi/Extensions/RPCClientExtensions.cs
WalletWasabi/Extensions/RPCClientExtensions.cs
@@ -214,16 +214,16 @@ namespace NBitcoin.RPC
return newEstimations;
}
- public static async Task<(bool accept, string rejectReason)> TestMempoolAcceptAsync(this IRPCClient rpc, IEnumerable<Coin> coins, int fakeOutputsCount)
+ public static async Task<(bool accept, string rejectReason)> TestMempoolAcceptAsync(this IRPCClient rpc, IEnumerable<Coin> coins, int fakeOutputCount, Money feePerInputs, Money feePerOutputs)
{
// Check if mempool would accept a fake transaction created with the registered inputs.
// This will catch ascendant/descendant count and size limits for example.
var fakeTransaction = rpc.Network.CreateTransaction();
fakeTransaction.Inputs.AddRange(coins.Select(coin => new TxIn(coin.Outpoint)));
- Money totalFakeOutputsValue = NBitcoinHelpers.TakeAReasonableFee(coins.Sum(coin => coin.TxOut.Value));
- for (int i = 0; i < fakeOutputsCount; i++)
+ Money totalFakeOutputsValue = NBitcoinHelpers.TakeFee(coins, fakeOutputCount, feePerInputs, feePerOutputs);
+ for (int i = 0; i < fakeOutputCount; i++)
{
- var fakeOutputValue = totalFakeOutputsValue / fakeOutputsCount;
+ var fakeOutputValue = totalFakeOutputsValue / fakeOutputCount;
fakeTransaction.Outputs.Add(fakeOutputValue, new Key());
}
MempoolAcceptResult testMempoolAcceptResult = await rpc.TestMempoolAcceptAsync(fakeTransaction, allowHighFees: true);

MODIFY
WalletWasabi/Helpers/NBitcoinHelpers.cs
WalletWasabi/Helpers/NBitcoinHelpers.cs
@@ -12,18 +12,6 @@ namespace WalletWasabi.Helpers
{
public static class NBitcoinHelpers
{
- private static readonly Money[] ReasonableFees = new[]
- {
- Money.Coins(0.002m),
- Money.Coins(0.001m),
- Money.Coins(0.0005m),
- Money.Coins(0.0002m),
- Money.Coins(0.0001m),
- Money.Coins(0.00005m),
- Money.Coins(0.00002m),
- Money.Coins(0.00001m)
- };
-
public static string HashOutpoints(IEnumerable<OutPoint> outPoints)
{
var sb = new StringBuilder();
@@ -35,20 +23,16 @@ namespace WalletWasabi.Helpers
return HashHelpers.GenerateSha256Hash(sb.ToString());
}
- public static Money TakeAReasonableFee(Money inputValue)
- {
- Money half = inputValue / 2;
-
- foreach (Money fee in ReasonableFees)
+ public static Money TakeFee(IEnumerable<Coin> inputs, int outputCount, Money feePerInputs, Money feePerOutputs)
{
- Money diff = inputValue - fee;
- if (diff > half)
+ var inputValue = inputs.Sum(coin => coin.TxOut.Value);
+ var fee = inputs.Count() * feePerInputs + outputCount * feePerOutputs;
+ Money outputSum = inputValue - fee;
+ if (outputSum < Money.Zero)
{
- return diff;
+ throw new InvalidOperationException($"{nameof(outputSum)} cannot be negative.");
}
- }
-
- return half;
+ return outputSum;
}
public static int CalculateVsizeAssumeSegwit(int inNum, int outNum)

fix fee estimation