Split CredentialRequest types
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/SerializationTests.cs
WalletWasabi.Tests/UnitTests/WabiSabi/SerializationTests.cs
@@ -189,7 +189,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi
var issuer = new CredentialIssuer(sk, rnd, 4300000000000);
var client = new WabiSabiClient(sk.ComputeCredentialIssuerParameters(), rnd, 4300000000000);
- (CredentialsRequest credentialRequest, CredentialsResponseValidation validationData) = client.CreateRequestForZeroAmount();
+ (ICredentialsRequest credentialRequest, CredentialsResponseValidation validationData) = client.CreateRequestForZeroAmount();
var credentialResponse = issuer.HandleRequest(credentialRequest);
var present = client.HandleResponse(credentialResponse, validationData);
(credentialRequest, _) = client.CreateRequest(new[] { 1L }, present, CancellationToken.None);

MODIFY
WalletWasabi/Models/ImmutableValueSequence.cs
WalletWasabi/Models/ImmutableValueSequence.cs
@@ -20,6 +20,8 @@ namespace WalletWasabi.Models
_elements = sequence.ToImmutableArray();
}
+ public static ImmutableValueSequence<T> Empty { get; } = new (Enumerable.Empty<T>());
+
public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_elements).GetEnumerator();
public override int GetHashCode()

MODIFY
WalletWasabi/WabiSabi/Crypto/CredentialIssuer.cs
WalletWasabi/WabiSabi/Crypto/CredentialIssuer.cs
@@ -23,7 +23,7 @@ namespace WalletWasabi.WabiSabi.Crypto
/// <remarks>
/// CredentialIssuer is the coordinator's component used to issue anonymous credentials
/// requested by a WabiSabi client. This means this component abstracts receives
- /// <see cref="CredentialsRequest">RegistrationRequests</see>, validates the requested
+ /// <see cref="ICredentialsRequest">RegistrationRequests</see>, validates the requested
/// amounts are in the valid range, serial numbers are not duplicated nor reused,
/// and finally issues the credentials using the coordinator's secret key (and also
/// proving to the WabiSabi client that the credentials were issued with the right
@@ -37,7 +37,7 @@ namespace WalletWasabi.WabiSabi.Crypto
/// this means that the same instance has to be used for a given round (the
/// coordinator needs to maintain only one instance of this class per round)
///
- /// About replay requests: a replay request is a <see cref="CredentialsRequest">request</see>
+ /// About replay requests: a replay request is a <see cref="ICredentialsRequest">request</see>
/// that has already been seen before. These kind of requests can be the result of misbehaving
/// clients or simply clients using a retry communication mechanism.
/// Reply requests are not handled by this component and they have to be handled by a different
@@ -93,31 +93,32 @@ namespace WalletWasabi.WabiSabi.Crypto
public int NumberOfCredentials => ProtocolConstants.CredentialNumber;
/// <summary>
- /// Process the <see cref="CredentialsRequest">credentials registration requests</see> and
+ /// Process the <see cref="ICredentialsRequest">credentials registration requests</see> and
/// issues the credentials.
/// </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 issued credentials and the proofs.</returns>
/// <exception cref="WabiSabiCryptoException">Error code: <see cref="WabiSabiCryptoErrorCode">WabiSabiErrorCode</see></exception>
- public Task<CredentialsResponse> HandleRequestAsync(CredentialsRequest registrationRequest, CancellationToken cancel)
+ public Task<CredentialsResponse> HandleRequestAsync(ICredentialsRequest registrationRequest, CancellationToken cancel)
=> Task.Run(() => HandleRequest(registrationRequest), cancel);
/// <summary>
- /// Process the <see cref="CredentialsRequest">credentials registration requests</see> and
+ /// Process the <see cref="ICredentialsRequest">credentials registration requests</see> and
/// issues the credentials.
/// </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 issued credentials and the proofs.</returns>
/// <exception cref="WabiSabiCryptoException">Error code: <see cref="WabiSabiCryptoErrorCode">WabiSabiErrorCode</see></exception>
- public CredentialsResponse HandleRequest(CredentialsRequest registrationRequest)
+ public CredentialsResponse HandleRequest(ICredentialsRequest registrationRequest)
{
Guard.NotNull(nameof(registrationRequest), registrationRequest);
+ var isNullRequest = registrationRequest.IsNullRequest();
var requested = registrationRequest.Requested ?? Enumerable.Empty<IssuanceRequest>();
var presented = registrationRequest.Presented ?? Enumerable.Empty<CredentialPresentation>();
var requestedCount = requested.Count();
- var requiredNumberOfRequested = registrationRequest.IsPresentationOnlyRequest ? 0 : NumberOfCredentials;
+ var requiredNumberOfRequested = registrationRequest.IsPresentationOnlyRequest() ? 0 : NumberOfCredentials;
if (requestedCount != requiredNumberOfRequested)
{
throw new WabiSabiCryptoException(
@@ -126,7 +127,7 @@ namespace WalletWasabi.WabiSabi.Crypto
}
var presentedCount = presented.Count();
- var requiredNumberOfPresentations = registrationRequest.IsNullRequest ? 0 : NumberOfCredentials;
+ var requiredNumberOfPresentations = isNullRequest ? 0 : NumberOfCredentials;
if (presentedCount != requiredNumberOfPresentations)
{
throw new WabiSabiCryptoException(
@@ -142,7 +143,7 @@ namespace WalletWasabi.WabiSabi.Crypto
}
// Check that the range proofs are of the appropriate bitwidth
- var rangeProofWidth = registrationRequest.IsNullRequest ? 0 : RangeProofWidth;
+ var rangeProofWidth = isNullRequest ? 0 : RangeProofWidth;
var allRangeProofsAreCorrectSize = requested.All(x => x.BitCommitments.Count() == rangeProofWidth);
if (!allRangeProofsAreCorrectSize)
{
@@ -152,7 +153,7 @@ namespace WalletWasabi.WabiSabi.Crypto
// Check all the serial numbers are unique. Note that this is checked separately from
// ensuring that they haven't been used before, because even presenting a previously
// unused credential more than once in the same request is still a double spend.
- if (registrationRequest.AreThereDuplicatedSerialNumbers)
+ if (registrationRequest.AreThereDuplicatedSerialNumbers())
{
throw new WabiSabiCryptoException(WabiSabiCryptoErrorCode.SerialNumberDuplicated);
}
@@ -191,13 +192,13 @@ namespace WalletWasabi.WabiSabi.Crypto
foreach (var credentialRequest in requested)
{
- statements.Add(registrationRequest.IsNullRequest
+ statements.Add(isNullRequest
? ProofSystem.ZeroProofStatement(credentialRequest.Ma)
: ProofSystem.RangeProofStatement(credentialRequest.Ma, credentialRequest.BitCommitments, rangeProofWidth));
}
// Balance proof
- if (!registrationRequest.IsNullRequest)
+ if (!isNullRequest)
{
var sumCa = presented.Select(x => x.Ca).Sum();
var sumMa = requested.Select(x => x.Ma).Sum();
@@ -213,7 +214,7 @@ namespace WalletWasabi.WabiSabi.Crypto
statements.Add(ProofSystem.BalanceProofStatement(balanceTweak + sumCa - sumMa));
}
- var transcript = BuildTranscript(registrationRequest.IsNullRequest);
+ var transcript = BuildTranscript(isNullRequest);
bool areProofsValid = false;

DELETE
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/CredentialsRequest.cs
-using Newtonsoft.Json;
-using System.Collections.Generic;
-using System.Linq;
-using WalletWasabi.Crypto.Groups;
-using WalletWasabi.Crypto.ZeroKnowledge;
-using WalletWasabi.Extensions;
-using WalletWasabi.Models;
-
-namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
-{
- /// <summary>
- /// Represents a request message for the WabiSabi unified registration protocol.
- /// </summary>
- /// <remarks>
- /// RegistrationRequestMessage message is the unified protocol message used in both phases,
- /// inputs registration and outputs registration and it is designed to support
- /// credentials reissuance.
- /// </remarks>
- public abstract record CredentialsRequest
- {
- [JsonConstructor]
- internal CredentialsRequest(
- long delta,
- IEnumerable<CredentialPresentation> presented,
- IEnumerable<IssuanceRequest> requested,
- IEnumerable<Proof> proofs)
- {
- Delta = delta;
- Presented = presented.ToImmutableValueSequence();
- Requested = requested.ToImmutableValueSequence();
- Proofs = proofs.ToImmutableValueSequence();
- }
-
- /// <summary>
- /// The difference between the sum of the requested credentials and the presented credentials.
- /// </summary>
- /// <remarks>
- /// A positive value of this property indicates that the request is an inputs registration request,
- /// a negative value indicates it is an outputs registration request, while finally a zero value
- /// indicates it is a reissuance request or a request for zero-value credentials.
- /// </remarks>
- public long Delta { get; }
-
- /// <summary>
- /// Randomized credentials presented for output registration or reissuance.
- /// </summary>
- public ImmutableValueSequence<CredentialPresentation> Presented { get; }
-
- /// <summary>
- /// Credential isssuance requests.
- /// </summary>
- public ImmutableValueSequence<IssuanceRequest> Requested { get; }
-
- /// <summary>
- /// Accompanying range and sum proofs to the coordinator.
- /// </summary>
- public ImmutableValueSequence<Proof> Proofs { get; }
-
- /// <summary>
- /// Is request for zero-value credentials only.
- /// </summary>
- [JsonIgnore]
- internal bool IsNullRequest => Delta == 0 && !Presented.Any();
-
- /// <summary>
- /// Is request for credential presentation only.
- /// </summary>
- [JsonIgnore]
- internal bool IsPresentationOnlyRequest => Delta < 0 && !Requested.Any();
-
- /// <summary>
- /// Serial numbers used in the credential presentations.
- /// </summary>
- [JsonIgnore]
- internal IEnumerable<GroupElement> SerialNumbers => Presented.Select(x => x.S);
-
- /// <summary>
- /// Indicates whether the message contains duplicated serial numbers or not.
- /// </summary>
- [JsonIgnore]
- internal bool AreThereDuplicatedSerialNumbers => SerialNumbers.Distinct().Count() < SerialNumbers.Count();
- }
-}

ADD
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/CredentialsRequestExtensions.cs
+using System.Linq;
+using System.Collections.Generic;
+using WalletWasabi.Crypto.Groups;
+
+namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
+{
+ public static class CredentialsRequestExtensions
+ {
+ /// <summary>
+ /// Is request for zero-value credentials only.
+ /// </summary>
+ public static bool IsNullRequest(this ICredentialsRequest request) => request.Delta == 0 && !request.Presented.Any();
+
+ /// <summary>
+ /// Is request for credential presentation only.
+ /// </summary>
+ public static bool IsPresentationOnlyRequest(this ICredentialsRequest request) => request.Delta < 0 && !request.Requested.Any();
+
+ /// <summary>
+ /// Serial numbers used in the credential presentations.
+ /// </summary>
+ public static IEnumerable<GroupElement> SerialNumbers(this ICredentialsRequest request) => request.Presented.Select(x => x.S);
+
+ /// <summary>
+ /// Indicates whether the message contains duplicated serial numbers or not.
+ /// </summary>
+ public static bool AreThereDuplicatedSerialNumbers(this ICredentialsRequest request) => request.SerialNumbers().Distinct().Count() < request.SerialNumbers().Count();
+ }
+}

ADD
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/ICredentialsRequest.cs
+using WalletWasabi.Models;
+using WalletWasabi.Crypto.ZeroKnowledge;
+
+namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
+{
+ /// <summary>
+ /// Represents a request message for the WabiSabi unified registration protocol.
+ /// </summary>
+ /// <remarks>
+ /// RegistrationRequestMessage message is the unified protocol message used in both phases,
+ /// inputs registration and outputs registration and it is designed to support
+ /// credentials reissuance.
+ /// </remarks>
+ public interface ICredentialsRequest
+ {
+ /// <summary>
+ /// The difference between the sum of the requested credentials and the presented credentials.
+ /// </summary>
+ /// <remarks>
+ /// A positive value of this property indicates that the request is an inputs registration request,
+ /// a negative value indicates it is an outputs registration request, while finally a zero value
+ /// indicates it is a reissuance request or a request for zero-value credentials.
+ /// </remarks>
+ long Delta { get; }
+
+ /// <summary>
+ /// Randomized credentials presented for output registration or reissuance.
+ /// </summary>
+ ImmutableValueSequence<CredentialPresentation> Presented { get; }
+
+ /// <summary>
+ /// Credential isssuance requests.
+ /// </summary>
+ ImmutableValueSequence<IssuanceRequest> Requested { get; }
+
+ /// <summary>
+ /// Accompanying range and sum proofs to the coordinator.
+ /// </summary>
+ ImmutableValueSequence<Proof> Proofs { get; }
+ }
+}

MODIFY
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/RealCredentialsRequest.cs
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/RealCredentialsRequest.cs
using System;
using System.Collections.Generic;
+using Newtonsoft.Json;
using WalletWasabi.Crypto.ZeroKnowledge;
+using WalletWasabi.Extensions;
+using WalletWasabi.Models;
namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
{
- public record RealCredentialsRequest : CredentialsRequest
+ public record RealCredentialsRequest : ICredentialsRequest
{
+ [JsonConstructor]
public RealCredentialsRequest(
long delta,
IEnumerable<CredentialPresentation> presented,
IEnumerable<IssuanceRequest> requested,
IEnumerable<Proof> proofs)
- : base(delta, presented, requested, proofs)
{
- if (IsNullRequest)
- {
- throw new InvalidOperationException($"{nameof(RealCredentialsRequest)} must not be null request.");
- }
+ Delta = delta;
+ Presented = presented.ToImmutableValueSequence();
+ Requested = requested.ToImmutableValueSequence();
+ Proofs = proofs.ToImmutableValueSequence();
}
+
+ public long Delta { get; }
+
+ public ImmutableValueSequence<CredentialPresentation> Presented { get; }
+
+ public ImmutableValueSequence<IssuanceRequest> Requested { get; }
+
+ public ImmutableValueSequence<Proof> Proofs { get; }
}
}

MODIFY
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/ZeroCredentialsRequest.cs
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/ZeroCredentialsRequest.cs
using System;
using System.Collections.Generic;
+using Newtonsoft.Json;
using WalletWasabi.Crypto.ZeroKnowledge;
+using WalletWasabi.Extensions;
+using WalletWasabi.Models;
namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
{
- public record ZeroCredentialsRequest : CredentialsRequest
+ public record ZeroCredentialsRequest : ICredentialsRequest
{
+ [JsonConstructor]
public ZeroCredentialsRequest(
IEnumerable<IssuanceRequest> requested,
IEnumerable<Proof> proofs)
- : base(0, Array.Empty<CredentialPresentation>(), requested, proofs)
{
- if (!IsNullRequest)
- {
- throw new InvalidOperationException($"{nameof(ZeroCredentialsRequest)} must be null request.");
- }
+ Requested = requested.ToImmutableValueSequence();
+ Proofs = proofs.ToImmutableValueSequence();
}
+
+ public long Delta => 0;
+
+ public ImmutableValueSequence<CredentialPresentation> Presented => ImmutableValueSequence<CredentialPresentation>.Empty;
+
+ public ImmutableValueSequence<IssuanceRequest> Requested { get; }
+
+ public ImmutableValueSequence<Proof> Proofs { get; }
}
}

Split CredentialRequest types