Add 1.25 limit.
MODIFY
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs
@@ -21,7 +21,7 @@ public class ChangelessTransactionCoinSelectorTests
/// Tests that we select coins so that user can agree with paying more money for having a payment transaction with no change output.
/// </summary>
[Fact]
- public void TryGetCoins()
+ public void GoodSuggestion()
{
using Key key = new();
@@ -36,6 +36,21 @@ public class ChangelessTransactionCoinSelectorTests
Assert.Equal(6025, solution.Sum() - target); // ~4% more for the privacy.
}
+ /// <summary>
+ /// Tests that solutions respect <see cref="ChangelessTransactionCoinSelector.MaxExtraFee"/> restriction.
+ /// </summary>
+ [Fact]
+ public void TooExpensiveSolution()
+ {
+ using Key key = new();
+
+ List<SmartCoin> coins = GenerateDummySmartCoins(key, 150_000);
+ long target = 100_000;
+
+ bool found = ChangelessTransactionCoinSelector.TryGetCoins(coins, new FeeRate(satoshiPerByte: 4), target, out List<SmartCoin>? selectedCoins);
+ Assert.False(found);
+ }
+
/// <remarks>These smart coins are from an invalid transaction but we are interested only in smart coins' amounts.</remarks>
private List<SmartCoin> GenerateDummySmartCoins(Key key, params long[] values)
{

MODIFY
WalletWasabi/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelector.cs
WalletWasabi/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelector.cs
@@ -10,6 +10,9 @@ namespace WalletWasabi.Blockchain.TransactionBuilding;
public static class ChangelessTransactionCoinSelector
{
+ /// <summary>Payments are capped to be at most 25% more expensive than the original target.</summary>
+ public const double MaxExtraFee = 1.25;
+
/// <summary>
/// Select coins in a way that user can pay without a change input (to increase privacy)
/// and try to find a solution that requires to pay as little extra amount as possible.
@@ -18,10 +21,7 @@ public static class ChangelessTransactionCoinSelector
/// <param name="feeRate">Current fee rate to take into account effective values of available coins.</param>
/// <param name="target">Amount the user wants to pay in satoshis.</param>
/// <returns><c>true</c> if a solution was found, <c>false</c> otherwise.</returns>
- /// <remarks>
- /// The implementation does not give any guarantee how much extra amount we can pay. It's a responsibility of the caller
- /// to judge what is "too much" (e.g. paying $15 for a $5 dollar coffee seems excessive for majority of people but some may still want to do it.)
- /// </remarks>
+ /// <remarks>The implementation gives only the guarantee that user can pay at most 25% more than <paramref name="target"/>.</remarks>
public static bool TryGetCoins(List<SmartCoin> availableCoins, FeeRate feeRate, long target, [NotNullWhen(true)] out List<SmartCoin>? selectedCoins, CancellationToken cancellationToken = default)
{
selectedCoins = null;
@@ -46,6 +46,11 @@ public static class ChangelessTransactionCoinSelector
if (solution is not null)
{
+ if (solution.Sum() > target * MaxExtraFee)
+ {
+ return false;
+ }
+
selectedCoins = new();
int i = 0;

Add 1.25 limit.