Introduce `StrategyParameters`
MODIFY
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/BranchAndBoundTests.cs
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/BranchAndBoundTests.cs
@@ -23,7 +23,8 @@ public class BranchAndBoundTests
long target = 27; // Target that we cannot get as a sum of input values.
BranchAndBound algorithm = new();
- MoreSelectionStrategy strategy = new(target, inputValues, inputCosts);
+ StrategyParameters parameters = new(target, inputValues, inputCosts);
+ MoreSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
Assert.Equal(new long[] { 11, 7, 5, 3, 2 }, strategy.GetBestSelectionFound());
@@ -40,7 +41,8 @@ public class BranchAndBoundTests
long target = 27;
BranchAndBound algorithm = new();
- MoreSelectionStrategy strategy = new(target, inputValues, inputCosts);
+ StrategyParameters parameters = new(target, inputValues, inputCosts);
+ MoreSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
long[] actualSelection = strategy.GetBestSelectionFound()!;
@@ -66,7 +68,8 @@ public class BranchAndBoundTests
long target = 27; // Target that we cannot get as a sum of input values.
BranchAndBound algorithm = new();
- MoreSelectionStrategy strategy = new(target, inputValues, inputCosts);
+ StrategyParameters parameters = new(target, inputValues, inputCosts);
+ MoreSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
// Selection (35) costs us 35 + 1 = 36.
@@ -104,7 +107,8 @@ public class BranchAndBoundTests
long target = 999_999; // Target that we cannot get as a sum of input values.
BranchAndBound algorithm = new();
- MoreSelectionStrategy strategy = new(target, inputValues.ToArray(), inputCosts);
+ StrategyParameters parameters = new(target, inputValues.ToArray(), inputCosts);
+ MoreSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
// Assert that we get expected best solution.
@@ -132,7 +136,8 @@ public class BranchAndBoundTests
long target = 1_000_000; // Target that we cannot get as a sum of input values.
BranchAndBound algorithm = new();
- LessSelectionStrategy strategy = new(target, inputValues.ToArray(), inputCosts);
+ StrategyParameters parameters = new(target, inputValues.ToArray(), inputCosts);
+ LessSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
// Assert that we get expected best solution.
@@ -151,7 +156,8 @@ public class BranchAndBoundTests
long target = 16;
BranchAndBound algorithm = new();
- LessSelectionStrategy strategy = new(target, inputValues, inputCosts);
+ StrategyParameters parameters = new(target, inputValues, inputCosts);
+ LessSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
long[] actualSelection = strategy.GetBestSelectionFound()!;
@@ -170,7 +176,8 @@ public class BranchAndBoundTests
long target = 26;
BranchAndBound algorithm = new();
- LessSelectionStrategy strategy = new(target, inputValues, inputCosts);
+ StrategyParameters parameters = new(target, inputValues, inputCosts);
+ LessSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
long[] actualSelection = strategy.GetBestSelectionFound()!;
@@ -193,7 +200,8 @@ public class BranchAndBoundTests
long target = 33;
BranchAndBound algorithm = new();
- LessSelectionStrategy strategy = new(target, inputValues, inputCosts);
+ StrategyParameters parameters = new(target, inputValues, inputCosts);
+ LessSelectionStrategy strategy = new(parameters);
_ = algorithm.TryGetMatch(strategy, out _, cts.Token);
long[] actualSelection = strategy.GetBestSelectionFound()!;

MODIFY
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs
@@ -27,14 +27,15 @@ public class ChangelessTransactionCoinSelectorTests
using Key key = new();
List<SmartCoin> coins = GenerateDummySmartCoins(key, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288);
- var target = Money.Satoshis(150_000);
- var feeRate = new FeeRate(Money.Satoshis(2));
- var txOut = new TxOut(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
+ Money target = Money.Satoshis(150_000);
+ FeeRate feeRate = new(Money.Satoshis(2));
+ TxOut txOut = new(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
long[] inputCosts = coins.Select(x => feeRate.GetFee(x.ScriptPubKey.EstimateInputVsize()).Satoshi).ToArray();
Dictionary<SmartCoin, long> inputEffectiveValues = new(coins.ToDictionary(x => x, x => x.EffectiveValue(feeRate).Satoshi));
- var strategy = new MoreSelectionStrategy(target, inputEffectiveValues.Values.ToArray(), inputCosts);
+ StrategyParameters parameters = new(target, inputEffectiveValues.Values.ToArray(), inputCosts);
+ MoreSelectionStrategy strategy = new(parameters);
bool found = ChangelessTransactionCoinSelector.TryGetCoins(strategy, target, inputEffectiveValues, out var selectedCoins);
Assert.True(found);
@@ -50,16 +51,15 @@ public class ChangelessTransactionCoinSelectorTests
using Key key = new();
List<SmartCoin> coins = GenerateDummySmartCoins(key, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288);
- var target = Money.Satoshis(65_000);
- var feeRate = new FeeRate(Money.Satoshis(2));
-
- var txOut = new TxOut(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
+ Money target = Money.Satoshis(65_000);
+ FeeRate feeRate = new(Money.Satoshis(2));
+ TxOut txOut = new(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
long[] inputCosts = coins.Select(x => feeRate.GetFee(x.ScriptPubKey.EstimateInputVsize()).Satoshi).ToArray();
Dictionary<SmartCoin, long> inputEffectiveValues = new(coins.ToDictionary(x => x, x => x.EffectiveValue(feeRate).Satoshi));
-
- var strategy = new LessSelectionStrategy(target, inputEffectiveValues.Values.ToArray(), inputCosts);
+ StrategyParameters parameters = new(target, inputEffectiveValues.Values.ToArray(), inputCosts);
+ LessSelectionStrategy strategy = new(parameters);
bool found = ChangelessTransactionCoinSelector.TryGetCoins(strategy, target, inputEffectiveValues, out var selectedCoins);
Assert.True(found);
@@ -77,15 +77,15 @@ public class ChangelessTransactionCoinSelectorTests
using Key key = new();
List<SmartCoin> coins = GenerateDummySmartCoins(key, 150_000);
- var target = Money.Satoshis(100_000);
- var feeRate = new FeeRate(Money.Satoshis(2));
- var txOut = new TxOut(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
+ Money target = Money.Satoshis(100_000);
+ FeeRate feeRate = new(Money.Satoshis(2));
+ TxOut txOut = new(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
long[] inputCosts = coins.Select(x => feeRate.GetFee(x.ScriptPubKey.EstimateInputVsize()).Satoshi).ToArray();
Dictionary<SmartCoin, long> inputEffectiveValues = new(coins.ToDictionary(x => x, x => x.EffectiveValue(feeRate).Satoshi));
-
- var strategy = new LessSelectionStrategy(target, coins.Select(coin => coin.Amount.Satoshi).ToArray(), inputCosts);
+ StrategyParameters parameters = new(target, coins.Select(coin => coin.Amount.Satoshi).ToArray(), inputCosts);
+ LessSelectionStrategy strategy = new(parameters);
bool found = ChangelessTransactionCoinSelector.TryGetCoins(strategy, target, inputEffectiveValues, out var selectedCoins);
Assert.False(found);

MODIFY
WalletWasabi/Blockchain/TransactionBuilding/BnB/LessSelectionStrategy.cs
WalletWasabi/Blockchain/TransactionBuilding/BnB/LessSelectionStrategy.cs
@@ -11,10 +11,10 @@ public class LessSelectionStrategy : SelectionStrategy
public const double MinPaymentThreshold = 0.75;
/// <inheritdoc/>
- public LessSelectionStrategy(long target, long[] inputValues, long[] inputCosts)
- : base(target, inputValues, inputCosts, new CoinSelection(long.MinValue, long.MinValue))
+ public LessSelectionStrategy(StrategyParameters parameters)
+ : base(parameters, new CoinSelection(long.MinValue, long.MinValue))
{
- MinimumTarget = (long)(target * MinPaymentThreshold);
+ MinimumTarget = (long)(parameters.Target * MinPaymentThreshold);
}
/// <summary>Minimum acceptable target (inclusive).</summary>

MODIFY
WalletWasabi/Blockchain/TransactionBuilding/BnB/MoreSelectionStrategy.cs
WalletWasabi/Blockchain/TransactionBuilding/BnB/MoreSelectionStrategy.cs
@@ -11,10 +11,10 @@ public class MoreSelectionStrategy : SelectionStrategy
public const double MaxExtraPayment = 1.25;
/// <inheritdoc/>
- public MoreSelectionStrategy(long target, long[] inputValues, long[] inputCosts)
- : base(target, inputValues, inputCosts, new CoinSelection(long.MaxValue, long.MaxValue))
+ public MoreSelectionStrategy(StrategyParameters parameters)
+ : base(parameters, new CoinSelection(long.MaxValue, long.MaxValue))
{
- MaximumTarget = (long)(target * MaxExtraPayment);
+ MaximumTarget = (long)(parameters.Target * MaxExtraPayment);
}
/// <summary>Maximum acceptable target (inclusive).</summary>

MODIFY
WalletWasabi/Blockchain/TransactionBuilding/BnB/SelectionStrategy.cs
WalletWasabi/Blockchain/TransactionBuilding/BnB/SelectionStrategy.cs
-using System.Diagnostics;
using System.Linq;
namespace WalletWasabi.Blockchain.TransactionBuilding.BnB;
public abstract class SelectionStrategy
{
- /// <param name="target">Value in satoshis.</param>
- /// <param name="inputValues">Values in satoshis of the coins the user has (in descending order).</param>
- /// <param name="inputCosts">Costs of spending coins in satoshis.</param>
+ /// <param name="parameters">Parameters of the strategy specifying input coins, the target and final selection restrictions.</param>
/// <param name="bestSelection">Best selection so far.</param>
- public SelectionStrategy(long target, long[] inputValues, long[] inputCosts, CoinSelection bestSelection)
+ public SelectionStrategy(StrategyParameters parameters, CoinSelection bestSelection)
{
- InputCosts = inputCosts;
- InputValues = inputValues;
- Target = target;
+ Parameters = parameters;
BestSelection = bestSelection;
- RemainingAmounts = new long[inputValues.Length];
+ RemainingAmounts = new long[InputValues.Length];
long accumulator = InputValues.Sum();
- for (int i = 0; i < inputValues.Length; i++)
+ for (int i = 0; i < InputValues.Length; i++)
{
- accumulator -= inputValues[i];
+ accumulator -= InputValues[i];
RemainingAmounts[i] = accumulator;
}
}
+ public StrategyParameters Parameters { get; }
- /// <summary>Costs corresponding to <see cref="InputValues"/> values.</summary>
- public long[] InputCosts { get; }
+ /// <inheritdoc cref="StrategyParameters.InputCosts"/>
+ public long[] InputCosts => Parameters.InputCosts;
- /// <summary>Target value we want to, ideally, sum up from the input values.</summary>
- public long Target { get; }
+ /// <inheritdoc cref="StrategyParameters.Target"/>
+ public long Target => Parameters.Target;
- /// <summary>Input values sorted in descending orders.</summary>
- public long[] InputValues { get; }
+ /// <inheritdoc cref="StrategyParameters.InputValues"/>
+ public long[] InputValues => Parameters.InputValues;
/// <summary>Number of coins included in current selection.</summary>
/// <remarks>Range of values is <c>0</c> to <see cref="InputValues"/> size.</remarks>

ADD
WalletWasabi/Blockchain/TransactionBuilding/BnB/StrategyParameters.cs
+namespace WalletWasabi.Blockchain.TransactionBuilding.BnB;
+
+/// <summary>
+/// Parameters for BnB strategies.
+/// </summary>
+public record StrategyParameters
+{
+ /// <summary>Target value (in satoshis) we want to, ideally, sum up from the input values.</summary>
+ public long Target { get; }
+
+ /// <summary>Values in satoshis of the coins the user has (in descending order).</summary>
+ public long[] InputValues { get; }
+
+ /// <summary>Costs of spending coins in satoshis.</summary>
+ /// <remarks>Costs corresponding to <see cref="InputValues"/> values. So the arrays has to have the same lengths.</remarks>
+ public long[] InputCosts { get; }
+
+ /// <param name="target">Target value (in satoshis) we want to, ideally, sum up from the input values.</param>
+ /// <param name="inputValues">Values in satoshis of the coins the user has (in descending order).</param>
+ /// <param name="inputCosts">Costs of spending coins in satoshis.</param>
+ public StrategyParameters(long target, long[] inputValues, long[] inputCosts)
+ {
+ if (inputValues.Length != inputCosts.Length)
+ {
+ throw new ArgumentException("Arrays' lengths are not the same.");
+ }
+
+ Target = target;
+ InputValues = inputValues;
+ InputCosts = inputCosts;
+ }
+}

MODIFY
WalletWasabi/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelector.cs
WalletWasabi/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelector.cs
@@ -33,10 +33,12 @@ public static class ChangelessTransactionCoinSelector
// Pass smart coins' effective values in descending order.
long[] inputValues = inputEffectiveValues.Values.ToArray();
+ StrategyParameters parameters = new(target, inputValues, inputCosts);
+
var strategies = new SelectionStrategy[]
{
- new MoreSelectionStrategy(target, inputValues, inputCosts),
- new LessSelectionStrategy(target, inputValues, inputCosts)
+ new MoreSelectionStrategy(parameters),
+ new LessSelectionStrategy(parameters)
};
var tasks = strategies.Select(strategy => Task.Run(() =>

Introduce `StrategyParameters`