Ensure correct amount and weight credentials
MODIFY
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
WalletWasabi.Tests/Helpers/WabiSabiFactory.cs
@@ -62,6 +62,7 @@ namespace WalletWasabi.Tests.Helpers
cfg.ConnectionConfirmationTimeout,
cfg.OutputRegistrationTimeout,
cfg.TransactionSigningTimeout,
+ new FeeRate(100m),
new InsecureRandom());
public static Alice CreateAlice(InputRoundSignaturePair inputSigPairs) => CreateAlice(new[] { inputSigPairs });
@@ -155,7 +156,7 @@ namespace WalletWasabi.Tests.Helpers
var zeroPresentables = CreateZeroCredentials(round);
var alice = round?.Alices.FirstOrDefault();
var (realAmountCredentialRequest, _) = amClient.CreateRequest(
- new[] { amount?.Satoshi ?? (alice is null ? 1000L : alice.Coins.Select(x => x.Amount.Satoshi).Sum()) },
+ new[] { amount?.Satoshi ?? alice?.CalculateRemainingAmountCredentials(round!.FeeRate).Satoshi ?? 1000L },
zeroPresentables.amountCredentials);
var (realWeightCredentialRequest, _) = weClient.CreateRequest(
new[] { weight ?? alice?.CalculateRemainingWeightCredentials(round!.RegistrableWeightCredentials) ?? 1000L },
@@ -174,7 +175,7 @@ namespace WalletWasabi.Tests.Helpers
var zeroPresentables = CreateZeroCredentials(round);
var alice = round?.Alices.FirstOrDefault();
var (realAmountCredentialRequest, _) = amClient.CreateRequest(
- new[] { alice is null ? 1000L : alice.Coins.Select(x => x.Amount.Satoshi).Sum() },
+ new[] { alice?.CalculateRemainingAmountCredentials(round!.FeeRate).Satoshi ?? 1000L },
zeroPresentables.amountCredentials);
var (realWeightCredentialRequest, _) = weClient.CreateRequest(
new[] { alice?.CalculateRemainingWeightCredentials(round!.RegistrableWeightCredentials) ?? 1000L },

MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/ConfirmConnectionTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/ConfirmConnectionTests.cs
+using NBitcoin;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -137,5 +138,29 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.ConfirmConnection(req));
Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedWeightCredentials, ex.ErrorCode);
}
+
+ [Fact]
+ public async Task IncorrectRequestedAmountCredentialsAsync()
+ {
+ MockArena arena = new();
+ WabiSabiConfig cfg = new();
+ var round = WabiSabiFactory.CreateRound(cfg);
+ round.Phase = Phase.ConnectionConfirmation;
+ var alice = WabiSabiFactory.CreateAlice();
+ round.Alices.Add(alice);
+ arena.OnTryGetRound = _ => round;
+
+ var req = WabiSabiFactory.CreateConnectionConfirmationRequest(round);
+ req = new(
+ req.RoundId,
+ req.AliceId,
+ req.ZeroAmountCredentialRequests,
+ WabiSabiFactory.CreateRealCredentialRequests(round, Money.Coins(3), null).amountReq,
+ req.ZeroWeightCredentialRequests,
+ req.RealWeightCredentialRequests);
+ await using PostRequestHandler handler = new(cfg, new Prison(), arena, new MockRpcClient());
+ var ex = Assert.Throws<WabiSabiProtocolException>(() => handler.ConfirmConnection(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.IncorrectRequestedAmountCredentials, ex.ErrorCode);
+ }
}
}

MODIFY
WalletWasabi/Extensions/NBitcoinExtensions.cs
WalletWasabi/Extensions/NBitcoinExtensions.cs
@@ -455,7 +455,7 @@ namespace NBitcoin
return sanityFee;
}
- public static uint EstimateSpendVsize(this Script script)
+ public static int EstimateSpendVsize(this Script script)
{
if (script.IsScriptType(ScriptType.P2WPKH))
{

MODIFY
WalletWasabi/WabiSabi/Backend/Models/Alice.cs
WalletWasabi/WabiSabi/Backend/Models/Alice.cs
@@ -20,10 +20,13 @@ namespace WalletWasabi.WabiSabi.Backend.Models
public IEnumerable<Coin> Coins { get; }
public IDictionary<Coin, byte[]> CoinRoundSignaturePairs { get; }
public Money TotalInputAmount => Coins.Sum(x => x.Amount);
- public long TotalInputWeight => Coins.Sum(x => x.ScriptPubKey.EstimateSpendVsize() * 4);
+ public long TotalInputWeight => TotalInputVsize * 4;
+ public int TotalInputVsize => Coins.Sum(x => x.ScriptPubKey.EstimateSpendVsize());
public long CalculateRemainingWeightCredentials(uint maxRegistrableWeight) => maxRegistrableWeight - TotalInputWeight;
+ public Money CalculateRemainingAmountCredentials(FeeRate feeRate) => TotalInputAmount - feeRate.GetFee(TotalInputVsize);
+
public void SetDeadlineRelativeTo(TimeSpan connectionConfirmationTimeout)
{
// Have alice timeouts a bit sooner than the timeout of connection confirmation phase.

MODIFY
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolErrorCode.cs
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolErrorCode.cs
@@ -23,6 +23,7 @@ namespace WalletWasabi.WabiSabi.Backend.Models
InputNotWhitelisted,
AliceNotFound,
IncorrectRequestedWeightCredentials,
- TooMuchWeight
+ TooMuchWeight,
+ IncorrectRequestedAmountCredentials
}
}

MODIFY
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
WalletWasabi/WabiSabi/Backend/Rounds/Round.cs
@@ -26,6 +26,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
TimeSpan connectionConfirmationTimeout,
TimeSpan outputRegistrationTimeout,
TimeSpan transactionSigningTimeout,
+ FeeRate feeRate,
WasabiRandom random)
{
Network = network;
@@ -37,7 +38,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
ConnectionConfirmationTimeout = connectionConfirmationTimeout;
OutputRegistrationTimeout = outputRegistrationTimeout;
TransactionSigningTimeout = transactionSigningTimeout;
-
+ FeeRate = feeRate;
Random = random;
AmountCredentialIssuer = new(new(Random), 2, random, MaxRegistrableAmountByAlice);
WeightCredentialIssuer = new(new(Random), 2, random, RegistrableWeightCredentials);
@@ -57,6 +58,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
blameOf.ConnectionConfirmationTimeout,
blameOf.OutputRegistrationTimeout,
blameOf.TransactionSigningTimeout,
+ blameOf.FeeRate,
blameOf.Random)
{
BlameOf = blameOf;
@@ -76,6 +78,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
public TimeSpan ConnectionConfirmationTimeout { get; }
public TimeSpan OutputRegistrationTimeout { get; }
public TimeSpan TransactionSigningTimeout { get; }
+ public FeeRate FeeRate { get; }
public WasabiRandom Random { get; }
public CredentialIssuer AmountCredentialIssuer { get; }
public CredentialIssuer WeightCredentialIssuer { get; }
@@ -111,7 +114,7 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
}
var inputValueSum = Money.Zero;
- var inputWeightSum = 0u;
+ var inputWeightSum = 0;
foreach (var coinRoundSignaturePair in alice.CoinRoundSignaturePairs)
{
var coin = coinRoundSignaturePair.Key;
@@ -170,6 +173,15 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.AliceNotFound);
}
+ if (realWeightCredentialRequests.Delta != alice.CalculateRemainingWeightCredentials(RegistrableWeightCredentials))
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.IncorrectRequestedWeightCredentials);
+ }
+ if (realAmountCredentialRequests.Delta != alice.CalculateRemainingAmountCredentials(FeeRate))
+ {
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.IncorrectRequestedAmountCredentials);
+ }
+
if (Phase == Phase.InputRegistration)
{
alice.SetDeadlineRelativeTo(ConnectionConfirmationTimeout);
@@ -182,11 +194,6 @@ namespace WalletWasabi.WabiSabi.Backend.Rounds
var amountRealCredentialResponse = AmountCredentialIssuer.HandleRequest(realAmountCredentialRequests);
var weightRealCredentialResponse = WeightCredentialIssuer.HandleRequest(realWeightCredentialRequests);
- if (realWeightCredentialRequests.Delta != alice.CalculateRemainingWeightCredentials(RegistrableWeightCredentials))
- {
- throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.IncorrectRequestedWeightCredentials);
- }
-
return new(
amountZeroCredentialResponse,
weightZeroCredentialResponse,

MODIFY
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
WalletWasabi/WabiSabi/Backend/WabiSabiConfig.cs
@@ -25,9 +25,9 @@ namespace WalletWasabi.WabiSabi.Backend
{
}
- [DefaultValue(Constants.OneDayConfirmationTarget)]
+ [DefaultValue(108)]
[JsonProperty(PropertyName = "ConfirmationTarget", DefaultValueHandling = DefaultValueHandling.Populate)]
- public uint ConfirmationTarget { get; set; } = Constants.OneDayConfirmationTarget;
+ public uint ConfirmationTarget { get; set; } = 108;
[DefaultValueTimeSpan("0d 3h 0m 0s")]
[JsonProperty(PropertyName = "ReleaseUtxoFromPrisonAfter", DefaultValueHandling = DefaultValueHandling.Populate)]

Ensure correct amount and weight credentials