Add min and max registrable limits
MODIFY
WalletWasabi.Gui/Config.cs
WalletWasabi.Gui/Config.cs
@@ -8,6 +8,7 @@ using WalletWasabi.Bases;
using WalletWasabi.Exceptions;
using WalletWasabi.Helpers;
using WalletWasabi.JsonConverters;
+using WalletWasabi.JsonConverters.Bitcoin;
using WalletWasabi.Logging;
using WalletWasabi.Models;
using WalletWasabi.Tor;

MODIFY
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
@@ -10,19 +10,37 @@ namespace WalletWasabi.Tests.Helpers
{
public static class WabiSabiFactory
{
- public static InputRoundSignaturePair CreateInputRoundSignaturePair()
+ public static InputRoundSignaturePair CreateInputRoundSignaturePair(Key? key = null, uint256? roundHash = null)
+ {
+ var rh = roundHash ?? BitcoinFactory.CreateUint256();
+ if (key is null)
+ {
+ using Key k = new();
+ return new InputRoundSignaturePair(
+ BitcoinFactory.CreateOutPoint(),
+ k.SignCompact(rh));
+ }
+ else
{
- using Key key = new();
return new InputRoundSignaturePair(
BitcoinFactory.CreateOutPoint(),
- key.SignCompact(BitcoinFactory.CreateUint256()));
+ key.SignCompact(rh));
+ }
}
- public static IEnumerable<InputRoundSignaturePair> CreateInputRoundSignaturePairs(int count)
+ public static IEnumerable<InputRoundSignaturePair> CreateInputRoundSignaturePairs(int count, uint256? roundHash = null)
{
for (int i = 0; i < count; i++)
{
- yield return CreateInputRoundSignaturePair();
+ yield return CreateInputRoundSignaturePair(null, roundHash);
+ }
+ }
+
+ public static IEnumerable<InputRoundSignaturePair> CreateInputRoundSignaturePairs(IEnumerable<Key> keys, uint256? roundHash = null)
+ {
+ foreach (var key in keys)
+ {
+ yield return CreateInputRoundSignaturePair(key, roundHash);
}
}
}

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/InputRegistrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/InputRegistrationTests.cs
@@ -188,5 +188,57 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
Assert.Equal(WabiSabiProtocolErrorCode.WrongRoundSignature, ex.ErrorCode);
}
+
+ [Fact]
+ public async Task NotEnoughFundsAsync()
+ {
+ MockArena arena = new();
+ Round round = new();
+ arena.OnTryGetRound = _ => round;
+ MockRpcClient rpc = new();
+ using Key key = new();
+
+ rpc.OnGetTxOutAsync = (_, _, _) => new GetTxOutResponse
+ {
+ Confirmations = 1,
+ ScriptPubKeyType = "witness_v0_keyhash",
+ TxOut = new TxOut(Money.Coins(1), key.PubKey.GetSegwitAddress(Network.Main))
+ };
+
+ await using PostRequestHandler handler = new(new WabiSabiConfig() { MinRegistrableAmount = Money.Coins(2) }, new Prison(), arena, rpc);
+ var req = new InputsRegistrationRequest(
+ round.Id,
+ WabiSabiFactory.CreateInputRoundSignaturePairs(new[] { key }, round.Hash),
+ null!,
+ null!);
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.NotEnoughFunds, ex.ErrorCode);
+ }
+
+ [Fact]
+ public async Task TooMuchFundsAsync()
+ {
+ MockArena arena = new();
+ Round round = new();
+ arena.OnTryGetRound = _ => round;
+ MockRpcClient rpc = new();
+ using Key key = new();
+
+ rpc.OnGetTxOutAsync = (_, _, _) => new GetTxOutResponse
+ {
+ Confirmations = 1,
+ ScriptPubKeyType = "witness_v0_keyhash",
+ TxOut = new TxOut(Money.Coins(1), key.PubKey.GetSegwitAddress(Network.Main))
+ };
+
+ await using PostRequestHandler handler = new(new WabiSabiConfig() { MaxRegistrableAmount = Money.Coins(0.9m) }, new Prison(), arena, rpc);
+ var req = new InputsRegistrationRequest(
+ round.Id,
+ WabiSabiFactory.CreateInputRoundSignaturePairs(new[] { key }, round.Hash),
+ null!,
+ null!);
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.TooMuchFunds, ex.ErrorCode);
+ }
}
}

MODIFY
WalletWasabi.Tests/UnitTests/Wabisabi/SerializationTests.cs
WalletWasabi.Tests/UnitTests/Wabisabi/SerializationTests.cs
@@ -7,6 +7,7 @@ using WalletWasabi.Crypto;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Crypto.Randomness;
using WalletWasabi.JsonConverters;
+using WalletWasabi.JsonConverters.Bitcoin;
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Crypto;
using WalletWasabi.WabiSabi.Crypto.CredentialRequesting;

MODIFY
WalletWasabi/CoinJoin/Common/Models/RoundStateResponseBase.cs
WalletWasabi/CoinJoin/Common/Models/RoundStateResponseBase.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using WalletWasabi.JsonConverters;
+using WalletWasabi.JsonConverters.Bitcoin;
namespace WalletWasabi.CoinJoin.Common.Models
{

MODIFY
WalletWasabi/CoinJoin/Coordinator/MixingLevels/MixingLevel.cs
WalletWasabi/CoinJoin/Coordinator/MixingLevels/MixingLevel.cs
@@ -2,7 +2,7 @@ using NBitcoin;
using Newtonsoft.Json;
using System;
using WalletWasabi.Helpers;
-using WalletWasabi.JsonConverters;
+using WalletWasabi.JsonConverters.Bitcoin;
using static WalletWasabi.Crypto.SchnorrBlinding;
namespace WalletWasabi.CoinJoin.Coordinator.MixingLevels

MODIFY
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRoundConfig.cs
WalletWasabi/CoinJoin/Coordinator/Rounds/CoordinatorRoundConfig.cs
@@ -4,6 +4,7 @@ using System.ComponentModel;
using WalletWasabi.Bases;
using WalletWasabi.Helpers;
using WalletWasabi.JsonConverters;
+using WalletWasabi.JsonConverters.Bitcoin;
namespace WalletWasabi.CoinJoin.Coordinator.Rounds
{

ADD
WalletWasabi/JsonConverters/Bitcoin/DefaultValueMoneyBtcAttribute.cs
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WalletWasabi.JsonConverters.Bitcoin
+{
+ public class DefaultValueMoneyBtcAttribute : DefaultValueAttribute
+ {
+ public DefaultValueMoneyBtcAttribute(string json) : base(MoneyBtcJsonConverter.Parse(json))
+ {
+ }
+ }
+}

RENAME
WalletWasabi/JsonConverters/MoneyBtcJsonConverter.cs
WalletWasabi/JsonConverters/Bitcoin/MoneyBtcJsonConverter.cs
@@ -2,7 +2,7 @@ using NBitcoin;
using Newtonsoft.Json;
using System;
-namespace WalletWasabi.JsonConverters
+namespace WalletWasabi.JsonConverters.Bitcoin
{
public class MoneyBtcJsonConverter : JsonConverter
{
@@ -13,11 +13,22 @@ namespace WalletWasabi.JsonConverters
}
/// <inheritdoc />
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ public override object? ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
- var serialized = (string)reader.Value;
+ var stringValue = reader.Value as string;
+ return Parse(stringValue);
+ }
- return Money.Parse(serialized);
+ public static Money? Parse(string? stringValue)
+ {
+ if (string.IsNullOrWhiteSpace(stringValue))
+ {
+ return null;
+ }
+ else
+ {
+ return Money.Parse(stringValue);
+ }
}
/// <inheritdoc />

RENAME
WalletWasabi/JsonConverters/MoneySatoshiJsonConverter.cs
WalletWasabi/JsonConverters/Bitcoin/MoneySatoshiJsonConverter.cs
@@ -2,7 +2,7 @@ using NBitcoin;
using Newtonsoft.Json;
using System;
-namespace WalletWasabi.JsonConverters
+namespace WalletWasabi.JsonConverters.Bitcoin
{
public class MoneySatoshiJsonConverter : JsonConverter
{

MODIFY
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolErrorCode.cs
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolErrorCode.cs
@@ -15,6 +15,8 @@ namespace WalletWasabi.WabiSabi.Backend.Models
InputImmature,
InputScriptNotAllowed,
WrongRoundSignature,
- TooManyInputs
+ TooManyInputs,
+ NotEnoughFunds,
+ TooMuchFunds
}
}

MODIFY
WalletWasabi/WabiSabi/Backend/PostRequests/PostRequestHandler.cs
WalletWasabi/WabiSabi/Backend/PostRequests/PostRequestHandler.cs
@@ -51,6 +51,7 @@ namespace WalletWasabi.WabiSabi.Backend.PostRequests
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooManyInputs);
}
+ var inputSum = Money.Zero;
foreach (var inputRoundSignaturePair in request.InputRoundSignaturePairs)
{
OutPoint input = inputRoundSignaturePair.Input;
@@ -76,6 +77,16 @@ namespace WalletWasabi.WabiSabi.Backend.PostRequests
{
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongRoundSignature);
}
+ inputSum += txOutResponse.TxOut.Value;
+ }
+
+ if (inputSum < Config.MinRegistrableAmount)
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.NotEnoughFunds);
+ }
+ if (inputSum > Config.MaxRegistrableAmount)
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.TooMuchFunds);
}
throw new NotImplementedException();

MODIFY
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
+using NBitcoin;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -7,6 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using WalletWasabi.Bases;
using WalletWasabi.Helpers;
+using WalletWasabi.JsonConverters.Bitcoin;
using WalletWasabi.JsonConverters.Collections;
using WalletWasabi.JsonConverters.Timing;
@@ -39,5 +41,15 @@ namespace WalletWasabi.WabiSabi.Backend
[DefaultValue(2)]
[JsonProperty(PropertyName = "MaxInputCountByAlice", DefaultValueHandling = DefaultValueHandling.Populate)]
public uint MaxInputCountByAlice { get; set; } = 2;
+
+ [DefaultValueMoneyBtc("0.00005")]
+ [JsonProperty(PropertyName = "MinRegistrableAmount", DefaultValueHandling = DefaultValueHandling.Populate)]
+ [JsonConverter(typeof(MoneyBtcJsonConverter))]
+ public Money MinRegistrableAmount { get; set; } = Money.Coins(0.00005m);
+
+ [DefaultValueMoneyBtc("1000")]
+ [JsonProperty(PropertyName = "MaxRegistrableAmount", DefaultValueHandling = DefaultValueHandling.Populate)]
+ [JsonConverter(typeof(MoneyBtcJsonConverter))]
+ public Money MaxRegistrableAmount { get; set; } = Money.Coins(1000m);
}
}

Add min and max registrable limits