Add MaxSuggestedAmountBase to WabiSabiConfig
MODIFY
WalletWasabi.Tests/Helpers/ArenaBuilder.cs
WalletWasabi.Tests/Helpers/ArenaBuilder.cs
@@ -35,12 +35,7 @@ public class ArenaBuilder
Network network = Network ?? Network.Main;
InMemoryCoinJoinIdStore coinJoinIdStore = CoinJoinIdStore ?? new();
- Mock<Arena> mockProcess = new(period, network, config, rpc, prison, coinJoinIdStore, null, null);
- mockProcess.Setup(p => p.GetMaxSuggestedAmount())
- .Returns(() => Money.Satoshis(ProtocolConstants.MaxAmountPerAlice));
- mockProcess.CallBase = true;
-
- Arena arena = mockProcess.Object;
+ Arena arena = new(period, network, config, rpc, prison, coinJoinIdStore);
foreach (var round in rounds)
{

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/PhaseStepping/StepTransactionSigningTests.cs
@@ -27,7 +27,8 @@ public class StepTransactionSigningTests
WabiSabiConfig cfg = new()
{
MaxInputCountByRound = 2,
- MinInputCountByRoundMultiplier = 0.5
+ MinInputCountByRoundMultiplier = 0.5,
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
};
var (keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();
@@ -58,7 +59,8 @@ public class StepTransactionSigningTests
WabiSabiConfig cfg = new()
{
MaxInputCountByRound = 2,
- MinInputCountByRoundMultiplier = 0.5
+ MinInputCountByRoundMultiplier = 0.5,
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
};
var (keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();
@@ -96,7 +98,8 @@ public class StepTransactionSigningTests
WabiSabiConfig cfg = new()
{
MaxInputCountByRound = 2,
- MinInputCountByRoundMultiplier = 0.5
+ MinInputCountByRoundMultiplier = 0.5,
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
};
var (keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();
@@ -140,7 +143,8 @@ public class StepTransactionSigningTests
{
MaxInputCountByRound = 2,
MinInputCountByRoundMultiplier = 1,
- TransactionSigningTimeout = TimeSpan.Zero
+ TransactionSigningTimeout = TimeSpan.Zero,
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
};
var (keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();
@@ -179,7 +183,8 @@ public class StepTransactionSigningTests
MaxInputCountByRound = 2,
MinInputCountByRoundMultiplier = 1,
TransactionSigningTimeout = TimeSpan.Zero,
- OutputRegistrationTimeout = TimeSpan.Zero
+ OutputRegistrationTimeout = TimeSpan.Zero,
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
};
var (keyChain, coin1, coin2) = WabiSabiFactory.CreateCoinKeyPairs();

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/MultipartyTransactionStateTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/Rounds/MultipartyTransactionStateTests.cs
@@ -88,7 +88,9 @@ public class MultipartyTransactionStateTests
[InlineData(0, "0.1")]
public void GetSuggestedAmountsTest(int roundCounter, string amount)
{
+ WabiSabiConfig config = new();
+ MaxSuggestedAmountProvider maxSuggestedAmountProvider = new(config);
var expected = Money.Coins(decimal.Parse(amount));
- Assert.Equal(expected, RoundHelpers.GetMaxSuggestedAmount(roundCounter));
+ Assert.Equal(expected, maxSuggestedAmountProvider.GetMaxSuggestedAmount(roundCounter));
}
}

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/WabiSabiHttpApiIntegrationTests.cs
@@ -12,6 +12,7 @@ using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Tests.Helpers;
using WalletWasabi.Tor.Http;
using WalletWasabi.Tor.Socks5.Pool.Circuits;
+using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Backend.Rounds;
@@ -126,7 +127,8 @@ public class WabiSabiHttpApiIntegrationTests : IClassFixture<WabiSabiApiApplicat
StandardInputRegistrationTimeout = TimeSpan.FromSeconds(60),
ConnectionConfirmationTimeout = TimeSpan.FromSeconds(60),
OutputRegistrationTimeout = TimeSpan.FromSeconds(60),
- TransactionSigningTimeout = TimeSpan.FromSeconds(60)
+ TransactionSigningTimeout = TimeSpan.FromSeconds(60),
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
}
);
@@ -237,7 +239,8 @@ public class WabiSabiHttpApiIntegrationTests : IClassFixture<WabiSabiApiApplicat
BlameInputRegistrationTimeout = TimeSpan.FromSeconds(60),
ConnectionConfirmationTimeout = TimeSpan.FromSeconds(60),
OutputRegistrationTimeout = TimeSpan.FromSeconds(60),
- TransactionSigningTimeout = TimeSpan.FromSeconds(5 * inputCount)
+ TransactionSigningTimeout = TimeSpan.FromSeconds(5 * inputCount),
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
});
})).CreateClient();
@@ -334,7 +337,8 @@ public class WabiSabiHttpApiIntegrationTests : IClassFixture<WabiSabiApiApplicat
StandardInputRegistrationTimeout = TimeSpan.FromSeconds(10 * ExpectedInputNumber),
ConnectionConfirmationTimeout = TimeSpan.FromSeconds(20 * ExpectedInputNumber),
OutputRegistrationTimeout = TimeSpan.FromSeconds(20 * ExpectedInputNumber),
- TransactionSigningTimeout = TimeSpan.FromSeconds(20 * ExpectedInputNumber)
+ TransactionSigningTimeout = TimeSpan.FromSeconds(20 * ExpectedInputNumber),
+ MaxSuggestedAmountBase = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice)
});
}));

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
WalletWasabi/WabiSabi/Backend/Rounds/Arena.cs
@@ -37,6 +37,7 @@ public partial class Arena : PeriodicRunner
Random = new SecureRandom();
InMemoryCoinJoinIdStore = inMemoryCoinJoinIdStore;
CoinJoinScriptStore = coinJoinScriptStore;
+ MaxSuggestedAmountProvider = new(Config);
}
public HashSet<Round> Rounds { get; } = new();
@@ -49,6 +50,7 @@ public partial class Arena : PeriodicRunner
private CoinJoinTransactionArchiver? TransactionArchiver { get; }
public CoinJoinScriptStore? CoinJoinScriptStore { get; }
private InMemoryCoinJoinIdStore InMemoryCoinJoinIdStore { get; }
+ private MaxSuggestedAmountProvider MaxSuggestedAmountProvider { get; }
public event EventHandler<Transaction>? CoinJoinBroadcast;
@@ -354,18 +356,13 @@ public partial class Arena : PeriodicRunner
Random,
feeRate,
Config.CoordinationFeeRate,
- GetMaxSuggestedAmount());
+ MaxSuggestedAmountProvider.GetMaxSuggestedAmount(ConnectionConfirmationStartedCounter));
Round r = new(roundParams);
Rounds.Add(r);
r.LogInfo($"Created round with params: {nameof(RoundParameters.MaxRegistrableAmount)}:'{roundParams.MaxRegistrableAmount}'.");
}
}
- internal virtual Money GetMaxSuggestedAmount()
- {
- return RoundHelpers.GetMaxSuggestedAmount(Config.MaxRegistrableAmount, ConnectionConfirmationStartedCounter);
- }
-
private void TimeoutRounds()
{
foreach (var expiredRound in Rounds.Where(

ADD
WalletWasabi/WabiSabi/Backend/Rounds/MaxSuggestedAmountProvider.cs
+using NBitcoin;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WalletWasabi.WabiSabi.Backend.Rounds;
+
+internal class MaxSuggestedAmountProvider
+{
+ public MaxSuggestedAmountProvider(WabiSabiConfig config)
+ {
+ Config = config;
+ }
+
+ private List<DividerMaxValue> RoundCounterDividerAndMaxAmounts { get; set; } = new List<DividerMaxValue>();
+ private Money LastGeneratedMaxSuggestedAmountBase { get; set; } = Money.Zero;
+ private WabiSabiConfig Config { get; init; }
+
+ private void CheckOrGenerateRoundCounterDividerAndMaxAmounts()
+ {
+ Money maxSuggestedAmountBase = Config.MaxSuggestedAmountBase;
+
+ if (maxSuggestedAmountBase == LastGeneratedMaxSuggestedAmountBase)
+ {
+ // It was already generated for this base.
+ return;
+ }
+
+ Money absoluteMaximumInput = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice);
+
+ int level = 0;
+ List<DividerMaxValue> roundCounterDividerAndMaxAmounts = new();
+ bool end = false;
+ do
+ {
+ var roundDivider = (int)Math.Pow(2, level);
+ var maxValue = maxSuggestedAmountBase * (long)Math.Pow(10, level);
+ if (maxValue >= absoluteMaximumInput)
+ {
+ maxValue = absoluteMaximumInput;
+ end = true;
+ }
+ roundCounterDividerAndMaxAmounts.Insert(0, new(roundDivider, maxValue));
+ level++;
+ }
+ while (!end);
+
+ RoundCounterDividerAndMaxAmounts = roundCounterDividerAndMaxAmounts;
+ LastGeneratedMaxSuggestedAmountBase = maxSuggestedAmountBase;
+ }
+
+ public Money GetMaxSuggestedAmount(int roundCounter)
+ {
+ CheckOrGenerateRoundCounterDividerAndMaxAmounts();
+ if (roundCounter != 0)
+ {
+ foreach (var (divider, maxValue) in RoundCounterDividerAndMaxAmounts.Where(v => v.MaxValue <= Config.MaxRegistrableAmount))
+ {
+ if (roundCounter % divider == 0)
+ {
+ return maxValue;
+ }
+ }
+ }
+
+ return RoundCounterDividerAndMaxAmounts.Last().MaxValue;
+ }
+
+ private record DividerMaxValue(int Divider, Money MaxValue);
+}

DELETE
WalletWasabi/WabiSabi/Backend/Rounds/RoundHelpers.cs
-using NBitcoin;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace WalletWasabi.WabiSabi.Backend.Rounds;
-
-internal class RoundHelpers
-{
- private static List<(int Divider, Money MaxValue)>? RoundCounterDividerAndMaxAmountsCache;
-
- private static List<(int Divider, Money MaxValue)> RoundCounterDividerAndMaxAmounts
- {
- get
- {
- if (RoundCounterDividerAndMaxAmountsCache == null)
- {
- RoundCounterDividerAndMaxAmountsCache = GenerateRoundCounterDividerAndMaxAmounts();
- }
- return RoundCounterDividerAndMaxAmountsCache;
- }
- }
-
- private static List<(int Divider, Money MaxValue)> GenerateRoundCounterDividerAndMaxAmounts()
- {
- Money smallestMaximum = Money.Coins(0.1m);
- Money absoluteMaximumInput = Money.Satoshis(ProtocolConstants.MaxAmountPerAlice);
-
- int level = 0;
- List<(int Divider, Money MaxValue)> roundCounterDividerAndMaxAmounts = new();
- bool end = false;
- do
- {
- var roundDivider = (int)Math.Pow(2, level);
- var maxValue = smallestMaximum * (long)Math.Pow(10, level);
- if (maxValue >= absoluteMaximumInput)
- {
- maxValue = absoluteMaximumInput;
- end = true;
- }
- roundCounterDividerAndMaxAmounts.Insert(0, (roundDivider, maxValue));
- level++;
- }
- while (!end);
-
- return roundCounterDividerAndMaxAmounts;
- }
-
- public static Money GetMaxSuggestedAmount(Money maxRegistrableAmount, int roundCounter)
- {
- if (roundCounter != 0)
- {
- foreach (var (divider, maxValue) in RoundCounterDividerAndMaxAmounts.Where(v => v.MaxValue <= maxRegistrableAmount))
- {
- if (roundCounter % divider == 0)
- {
- return maxValue;
- }
- }
- }
-
- return RoundCounterDividerAndMaxAmounts.Last().MaxValue;
- }
-}

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/RoundParameters.cs
WalletWasabi/WabiSabi/Backend/Rounds/RoundParameters.cs
@@ -18,6 +18,7 @@ public class RoundParameters
Random = random;
FeeRate = feeRate;
CoordinationFeeRate = coordinationFeeRate;
+ MaxSuggestedAmount = maxSuggestedAmount;
MaxInputCountByRound = wabiSabiConfig.MaxInputCountByRound;
MinInputCountByRound = wabiSabiConfig.MinInputCountByRound;
@@ -30,7 +31,6 @@ public class RoundParameters
OutputRegistrationTimeout = wabiSabiConfig.OutputRegistrationTimeout;
TransactionSigningTimeout = wabiSabiConfig.TransactionSigningTimeout;
BlameInputRegistrationTimeout = wabiSabiConfig.BlameInputRegistrationTimeout;
- MaxSuggestedAmount = maxSuggestedAmount;
}
public WasabiRandom Random { get; }
@@ -41,10 +41,10 @@ public class RoundParameters
public int MaxInputCountByRound { get; }
public Money MinRegistrableAmount { get; }
public Money MaxRegistrableAmount { get; }
+ public Money MaxSuggestedAmount { get; }
public TimeSpan StandardInputRegistrationTimeout { get; }
public TimeSpan ConnectionConfirmationTimeout { get; }
public TimeSpan OutputRegistrationTimeout { get; }
public TimeSpan TransactionSigningTimeout { get; }
public TimeSpan BlameInputRegistrationTimeout { get; }
- public Money MaxSuggestedAmount { get; }
}

MODIFY
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
@@ -95,6 +95,11 @@ public class WabiSabiConfig : ConfigBase
[JsonProperty(PropertyName = "CoordinatorExtPubKeyCurrentDepth", DefaultValueHandling = DefaultValueHandling.Populate)]
public int CoordinatorExtPubKeyCurrentDepth { get; private set; } = 1;
+ [DefaultValueMoneyBtc("0.1")]
+ [JsonProperty(PropertyName = "MaxSuggestedAmountBase", DefaultValueHandling = DefaultValueHandling.Populate)]
+ [JsonConverter(typeof(MoneyBtcJsonConverter))]
+ public Money MaxSuggestedAmountBase { get; set; } = Money.Coins(0.1m);
+
public Script GetNextCleanCoordinatorScript() => DeriveCoordinatorScript(CoordinatorExtPubKeyCurrentDepth);
public Script DeriveCoordinatorScript(int index) => CoordinatorExtPubKey.Derive(0, false).Derive(index, false).PubKey.WitHash.ScriptPubKey;

Add MaxSuggestedAmountBase to WabiSabiConfig