Introduce WabiSabiProtocolException
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/InputRegistrationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Backend/InputRegistrationTests.cs
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
using WalletWasabi.Tests.Helpers;
using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Banning;
+using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Backend.PostRequests;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Models;
@@ -29,7 +30,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
WabiSabiFactory.CreateInputRoundSignaturePairs(1),
null!,
null!);
- await Assert.ThrowsAnyAsync<InvalidOperationException>(async () => await handler.RegisterInputAsync(req));
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.RoundNotFound, ex.ErrorCode);
}
[Fact]
@@ -50,7 +52,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
WabiSabiFactory.CreateInputRoundSignaturePairs(1),
null!,
null!);
- await Assert.ThrowsAnyAsync<InvalidOperationException>(async () => await handler.RegisterInputAsync(req));
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.WrongPhase, ex.ErrorCode);
}
}
}
@@ -70,7 +73,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
WabiSabiFactory.CreateInputRoundSignaturePairs(1),
null!,
null!);
- await Assert.ThrowsAnyAsync<InvalidOperationException>(async () => await handler.RegisterInputAsync(req));
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.InputSpent, ex.ErrorCode);
}
[Fact]
@@ -88,7 +92,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
WabiSabiFactory.CreateInputRoundSignaturePairs(1),
null!,
null!);
- await Assert.ThrowsAnyAsync<InvalidOperationException>(async () => await handler.RegisterInputAsync(req));
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.InputUnconfirmed, ex.ErrorCode);
}
[Fact]
@@ -109,7 +114,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
WabiSabiFactory.CreateInputRoundSignaturePairs(1),
null!,
null!);
- await Assert.ThrowsAnyAsync<InvalidOperationException>(async () => await handler.RegisterInputAsync(req));
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.InputImmature, ex.ErrorCode);
}
}
@@ -129,7 +135,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Backend
WabiSabiFactory.CreateInputRoundSignaturePairs(1),
null!,
null!);
- await Assert.ThrowsAnyAsync<InvalidOperationException>(async () => await handler.RegisterInputAsync(req));
+ var ex = await Assert.ThrowsAnyAsync<WabiSabiProtocolException>(async () => await handler.RegisterInputAsync(req));
+ Assert.Equal(WabiSabiProtocolErrorCode.InputNonSegwit, ex.ErrorCode);
}
}
}

MODIFY
WalletWasabi.Tests/UnitTests/Wabisabi/CredentialTests.cs
WalletWasabi.Tests/UnitTests/Wabisabi/CredentialTests.cs
@@ -199,8 +199,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
validCredentialRequest.Requested,
validCredentialRequest.Proofs);
- var ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
+ var ex = Assert.Throws<WabiSabiCryptoException>(() => issuer.HandleRequest(invalidCredentialRequest));
+ Assert.Equal(WabiSabiCryptoErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
Assert.Equal("3 credential presentations were expected but 1 were received.", ex.Message);
// Test incorrect number of presentations (0 instead of 3.)
@@ -211,8 +211,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
validCredentialRequest.Requested,
validCredentialRequest.Proofs);
- ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
+ ex = Assert.Throws<WabiSabiCryptoException>(() => issuer.HandleRequest(invalidCredentialRequest));
+ Assert.Equal(WabiSabiCryptoErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
Assert.Equal("3 credential presentations were expected but 0 were received.", ex.Message);
(validCredentialRequest, _) = client.CreateRequest(Array.Empty<long>(), client.Credentials.All);
@@ -224,8 +224,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
validCredentialRequest.Requested.Take(1),
validCredentialRequest.Proofs);
- ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode);
+ ex = Assert.Throws<WabiSabiCryptoException>(() => issuer.HandleRequest(invalidCredentialRequest));
+ Assert.Equal(WabiSabiCryptoErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode);
Assert.Equal("3 credential requests were expected but 1 were received.", ex.Message);
// Test incorrect number of credential requests.
@@ -235,8 +235,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
validCredentialRequest.Requested.Take(1),
validCredentialRequest.Proofs);
- ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode);
+ ex = Assert.Throws<WabiSabiCryptoException>(() => issuer.HandleRequest(invalidCredentialRequest));
+ Assert.Equal(WabiSabiCryptoErrorCode.InvalidNumberOfRequestedCredentials, ex.ErrorCode);
Assert.Equal("3 credential requests were expected but 1 were received.", ex.Message);
// Test invalid range proof.
@@ -248,8 +248,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
new[] { requested[0], requested[1], new IssuanceRequest(requested[2].Ma, new[] { GroupElement.Infinity }) },
validCredentialRequest.Proofs);
- ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.InvalidBitCommitment, ex.ErrorCode);
+ ex = Assert.Throws<WabiSabiCryptoException>(() => issuer.HandleRequest(invalidCredentialRequest));
+ Assert.Equal(WabiSabiCryptoErrorCode.InvalidBitCommitment, ex.ErrorCode);
}
{
@@ -263,8 +263,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
validCredentialRequest.Requested,
proofs);
- var ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.CoordinatorReceivedInvalidProofs, ex.ErrorCode);
+ var ex = Assert.Throws<WabiSabiCryptoException>(() => issuer.HandleRequest(invalidCredentialRequest));
+ Assert.Equal(WabiSabiCryptoErrorCode.CoordinatorReceivedInvalidProofs, ex.ErrorCode);
}
{
@@ -277,8 +277,8 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
(validCredentialRequest, validationData) = client.CreateRequest(Enumerable.Empty<long>(), client.Credentials.All);
issuer.HandleRequest(validCredentialRequest);
- var ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(validCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.SerialNumberAlreadyUsed, ex.ErrorCode);
+ var ex = Assert.Throws<WabiSabiCryptoException>(() => issuer.HandleRequest(validCredentialRequest));
+ Assert.Equal(WabiSabiCryptoErrorCode.SerialNumberAlreadyUsed, ex.ErrorCode);
}
}
}

ADD
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolErrorCode.cs
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WalletWasabi.WabiSabi.Backend.Models
+{
+ public enum WabiSabiProtocolErrorCode
+ {
+ RoundNotFound,
+ WrongPhase,
+ InputSpent,
+ InputUnconfirmed,
+ InputImmature,
+ InputNonSegwit
+ }
+}

ADD
WalletWasabi/WabiSabi/Backend/Models/WabiSabiProtocolException.cs
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using WalletWasabi.WabiSabi.Crypto;
+using WalletWasabi.WabiSabi.Models;
+
+namespace WalletWasabi.WabiSabi.Backend.Models
+{
+ public class WabiSabiProtocolException : Exception
+ {
+ public WabiSabiProtocolException(WabiSabiProtocolErrorCode errorCode, string? message = null, Exception? innerException = null)
+ : base(message, innerException)
+ {
+ ErrorCode = errorCode;
+ }
+
+ public WabiSabiProtocolErrorCode ErrorCode { get; }
+ }
+}

MODIFY
WalletWasabi/WabiSabi/Backend/PostRequests/PostRequestHandler.cs
WalletWasabi/WabiSabi/Backend/PostRequests/PostRequestHandler.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using WalletWasabi.BitcoinCore.Rpc;
using WalletWasabi.Nito.AsyncEx;
using WalletWasabi.WabiSabi.Backend.Banning;
+using WalletWasabi.WabiSabi.Backend.Models;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Models;
@@ -37,11 +38,11 @@ namespace WalletWasabi.WabiSabi.Backend.PostRequests
{
if (!Arena.TryGetRound(request.RoundId, out var round))
{
- throw new InvalidOperationException("Round not found.");
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.RoundNotFound);
}
if (round.Phase != Phase.InputRegistration)
{
- throw new InvalidOperationException("Wrong phase.");
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.WrongPhase);
}
foreach (var inputRoundSignaturePair in request.InputRoundSignaturePairs)
@@ -50,19 +51,19 @@ namespace WalletWasabi.WabiSabi.Backend.PostRequests
var txOutResponse = await Rpc.GetTxOutAsync(input.Hash, (int)input.N, includeMempool: true).ConfigureAwait(false);
if (txOutResponse is null)
{
- throw new InvalidOperationException("Input spent.");
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputSpent);
}
if (txOutResponse.Confirmations == 0)
{
- throw new InvalidOperationException("Input unconfirmed.");
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputUnconfirmed);
}
if (txOutResponse.IsCoinBase && txOutResponse.Confirmations <= 100)
{
- throw new InvalidOperationException("Input immature.");
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputImmature);
}
if (txOutResponse.ScriptPubKeyType != "witness_v0_keyhash")
{
- throw new InvalidOperationException("Input nonsegwit.");
+ throw new WabiSabiProtocolException(WabiSabiProtocolErrorCode.InputNonSegwit);
}
}

MODIFY
WalletWasabi/WabiSabi/Crypto/CredentialIssuer.cs
WalletWasabi/WabiSabi/Crypto/CredentialIssuer.cs
@@ -82,7 +82,7 @@ namespace WalletWasabi.WabiSabi.Crypto
/// </summary>
/// <param name="registrationRequest">The request containing the credentials presentations, credential requests and the proofs.</param>
/// <returns>The <see cref="CredentialsResponse">registration response</see> containing the requested credentials and the proofs.</returns>
- /// <exception cref="WabiSabiException">Error code: <see cref="WabiSabiErrorCode">WabiSabiErrorCode</see></exception>
+ /// <exception cref="WabiSabiCryptoException">Error code: <see cref="WabiSabiCryptoErrorCode">WabiSabiErrorCode</see></exception>
public CredentialsResponse HandleRequest(CredentialsRequest registrationRequest)
{
Guard.NotNull(nameof(registrationRequest), registrationRequest);
@@ -93,8 +93,8 @@ namespace WalletWasabi.WabiSabi.Crypto
var requestedCount = requested.Count();
if (requestedCount != NumberOfCredentials)
{
- throw new WabiSabiException(
- WabiSabiErrorCode.InvalidNumberOfRequestedCredentials,
+ throw new WabiSabiCryptoException(
+ WabiSabiCryptoErrorCode.InvalidNumberOfRequestedCredentials,
$"{NumberOfCredentials} credential requests were expected but {requestedCount} were received.");
}
@@ -102,8 +102,8 @@ namespace WalletWasabi.WabiSabi.Crypto
var requiredNumberOfPresentations = registrationRequest.IsNullRequest ? 0 : NumberOfCredentials;
if (presentedCount != requiredNumberOfPresentations)
{
- throw new WabiSabiException(
- WabiSabiErrorCode.InvalidNumberOfPresentedCredentials,
+ throw new WabiSabiCryptoException(
+ WabiSabiCryptoErrorCode.InvalidNumberOfPresentedCredentials,
$"{requiredNumberOfPresentations} credential presentations were expected but {presentedCount} were received.");
}
@@ -111,7 +111,7 @@ namespace WalletWasabi.WabiSabi.Crypto
// then there is a problem somewhere because this should not be possible.
if (Balance + registrationRequest.Delta < 0)
{
- throw new WabiSabiException(WabiSabiErrorCode.NegativeBalance);
+ throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.NegativeBalance);
}
// Check that the range proofs are of the appropriate bitwidth
@@ -119,7 +119,7 @@ namespace WalletWasabi.WabiSabi.Crypto
var allRangeProofsAreCorrectSize = requested.All(x => x.BitCommitments.Count() == rangeProofWidth);
if (!allRangeProofsAreCorrectSize)
{
- throw new WabiSabiException(WabiSabiErrorCode.InvalidBitCommitment);
+ throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.InvalidBitCommitment);
}
// Check all the serial numbers are unique. Note that this is checked separately from
@@ -127,7 +127,7 @@ namespace WalletWasabi.WabiSabi.Crypto
// unused credential more than once in the same request is still a double spend.
if (registrationRequest.AreThereDuplicatedSerialNumbers)
{
- throw new WabiSabiException(WabiSabiErrorCode.SerialNumberDuplicated);
+ throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.SerialNumberDuplicated);
}
var statements = new List<Statement>();
@@ -145,7 +145,7 @@ namespace WalletWasabi.WabiSabi.Crypto
// rejected.
if (SerialNumbers.Contains(presentation.S))
{
- throw new WabiSabiException(WabiSabiErrorCode.SerialNumberAlreadyUsed, $"Serial number reused {presentation.S}");
+ throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.SerialNumberAlreadyUsed, $"Serial number reused {presentation.S}");
}
}
@@ -179,7 +179,7 @@ namespace WalletWasabi.WabiSabi.Crypto
var areProofsValid = ProofSystem.Verify(transcript, statements, registrationRequest.Proofs);
if (!areProofsValid)
{
- throw new WabiSabiException(WabiSabiErrorCode.CoordinatorReceivedInvalidProofs);
+ throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.CoordinatorReceivedInvalidProofs);
}
// Issue credentials.

MODIFY
WalletWasabi/WabiSabi/Crypto/WabiSabiClient.cs
WalletWasabi/WabiSabi/Crypto/WabiSabiClient.cs
@@ -110,8 +110,8 @@ namespace WalletWasabi.WabiSabi.Crypto
var availableZeroCredentialCount = availableZeroCredentials.Count();
if (availableZeroCredentialCount < missingCredentialPresent)
{
- throw new WabiSabiException(
- WabiSabiErrorCode.NotEnoughZeroCredentialToFillTheRequest,
+ throw new WabiSabiCryptoException(
+ WabiSabiCryptoErrorCode.NotEnoughZeroCredentialToFillTheRequest,
$"{missingCredentialPresent} credentials are missing but there are only {availableZeroCredentialCount} zero-value credentials available.");
}
@@ -119,7 +119,7 @@ namespace WalletWasabi.WabiSabi.Crypto
var macsToPresent = credentialsToPresent.Select(x => x.Mac);
if (macsToPresent.Distinct().Count() < macsToPresent.Count())
{
- throw new WabiSabiException(WabiSabiErrorCode.CredentialToPresentDuplicated);
+ throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.CredentialToPresentDuplicated);
}
var zs = new List<Scalar>();
@@ -194,8 +194,8 @@ namespace WalletWasabi.WabiSabi.Crypto
var requestedCredentialCount = registrationValidationData.Requested.Count();
if (issuedCredentialCount != NumberOfCredentials)
{
- throw new WabiSabiException(
- WabiSabiErrorCode.IssuedCredentialNumberMismatch,
+ throw new WabiSabiCryptoException(
+ WabiSabiCryptoErrorCode.IssuedCredentialNumberMismatch,
$"{issuedCredentialCount} issued but {requestedCredentialCount} were requested.");
}
@@ -209,7 +209,7 @@ namespace WalletWasabi.WabiSabi.Crypto
var areCorrectlyIssued = ProofSystem.Verify(registrationValidationData.Transcript, statements, registrationResponse.Proofs);
if (!areCorrectlyIssued)
{
- throw new WabiSabiException(WabiSabiErrorCode.ClientReceivedInvalidProofs);
+ throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.ClientReceivedInvalidProofs);
}
var credentialReceived = credentials.Select(x =>

RENAME
WalletWasabi/WabiSabi/Crypto/WabiSabiErrorCode.cs
WalletWasabi/WabiSabi/Crypto/WabiSabiCryptoErrorCode.cs
namespace WalletWasabi.WabiSabi.Crypto
{
- public enum WabiSabiErrorCode
+ public enum WabiSabiCryptoErrorCode
{
Unspecified = 0,
SerialNumberAlreadyUsed = 1,
@@ -13,6 +13,6 @@ namespace WalletWasabi.WabiSabi.Crypto
NotEnoughZeroCredentialToFillTheRequest = 8,
InvalidNumberOfRequestedCredentials = 9,
InvalidNumberOfPresentedCredentials = 10,
- CredentialToPresentDuplicated = 11,
+ CredentialToPresentDuplicated = 11
}
}

ADD
WalletWasabi/WabiSabi/Crypto/WabiSabiCryptoException.cs
+using System;
+using System.Runtime.Serialization;
+
+namespace WalletWasabi.WabiSabi.Crypto
+{
+ [Serializable]
+ public class WabiSabiCryptoException : Exception
+ {
+ public WabiSabiCryptoException(WabiSabiCryptoErrorCode errorCode, string? message = null, Exception? innerException = null)
+ : base(message, innerException)
+ {
+ ErrorCode = errorCode;
+ }
+
+ protected WabiSabiCryptoException(SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ {
+ }
+
+ public WabiSabiCryptoErrorCode ErrorCode { get; }
+ }
+}

DELETE
WalletWasabi/WabiSabi/Crypto/WabiSabiException.cs
-using System;
-using System.Runtime.Serialization;
-
-namespace WalletWasabi.WabiSabi.Crypto
-{
- [Serializable]
- public class WabiSabiException : Exception
- {
- public WabiSabiException()
- {
- }
-
- public WabiSabiException(string? message) : base(message)
- {
- }
-
- public WabiSabiException(WabiSabiErrorCode errorCode, string? message = null)
- : base(message)
- {
- ErrorCode = errorCode;
- }
-
- public WabiSabiException(string? message, Exception? innerException)
- : base(message, innerException)
- {
- }
-
- protected WabiSabiException(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- }
-
- public WabiSabiErrorCode ErrorCode { get; }
- }
-}

Introduce WabiSabiProtocolException