Stateless client
RENAME
WalletWasabi.Tests/UnitTests/Crypto/Api/CredentialTests.cs
WalletWasabi.Tests/UnitTests/Wabisabi/CredentialTests.cs
@@ -2,13 +2,13 @@ using System.Linq;
using NBitcoin;
using NBitcoin.Secp256k1;
using WalletWasabi.Crypto;
-using WalletWasabi.Crypto.Api;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Crypto.Randomness;
using WalletWasabi.Crypto.ZeroKnowledge;
+using WalletWasabi.Wabisabi;
using Xunit;
-namespace WalletWasabi.Tests.UnitTests.Crypto.Api
+namespace WalletWasabi.Tests.UnitTests.Wabisabi
{
public class CredentialTests
{
@@ -24,52 +24,32 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
{
// Null request. This requests `numberOfCredentials` zero-value credentials.
- var credentialRequest = client.AsCredentialRequestBuilder()
- .Build();
+ var requestFactory = client.GetCredentialRegistrationFactory();
+ var (credentialRequest, validationData) = requestFactory.CreateRequestForZeroAmount();
Assert.True(credentialRequest.IsNullRequest);
Assert.Equal(numberOfCredentials, credentialRequest.Requested.Count());
- var requested = credentialRequest.Requested.First();
- Assert.Empty(requested.BitCommitments);
- Assert.Equal(Money.Zero, credentialRequest.DeltaAmount);
-
- // Issuer part
- var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
-
- var credentialResponse = issuer.HandleRequest(credentialRequest);
- client.HandleResponse(credentialResponse);
- Assert.Equal(numberOfCredentials, client.Credentials.Count());
- var issuedCredential = client.Credentials.First();
- Assert.True(issuedCredential.Amount.IsZero);
- }
-
- {
- var credentialRequest = client.AsCredentialRequestBuilder()
- .RequestCredentialFor(Money.Zero)
- .RequestCredentialFor(Money.Zero)
- .Build();
-
- Assert.True(credentialRequest.IsNullRequest);
var requested = credentialRequest.Requested.ToArray();
- Assert.Equal(numberOfCredentials, requested.Count());
Assert.Empty(requested[0].BitCommitments);
Assert.Empty(requested[1].BitCommitments);
+ Assert.Empty(requested[2].BitCommitments);
Assert.Equal(Money.Zero, credentialRequest.DeltaAmount);
// Issuer part
var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
var credentialResponse = issuer.HandleRequest(credentialRequest);
- client.HandleResponse(credentialResponse);
- var credentials = client.Credentials.ToArray();
- Assert.NotEmpty(credentials);
- Assert.Equal(6, credentials.Count());
- Assert.All(credentials, x => Assert.True(x.Amount.IsZero));
+ client.HandleResponse(credentialResponse, validationData);
+ Assert.Equal(numberOfCredentials, client.Credentials.ZeroValue.Count());
+ Assert.Empty(client.Credentials.Valuable);
+ var issuedCredential = client.Credentials.ZeroValue.First();
+ Assert.True(issuedCredential.Amount.IsZero);
+ }
- credentialRequest = client.AsCredentialRequestBuilder()
- .RequestCredentialFor(Money.Coins(1))
- .RequestCredentialFor(Money.Zero)
- .Build();
+ {
+ var requestFactory = client.GetCredentialRegistrationFactory();
+ var present = client.Credentials.ZeroValue.Take(numberOfCredentials);
+ var (credentialRequest, validationData) = requestFactory.CreateRequest(new[] { Money.Coins(1)}, present);
Assert.False(credentialRequest.IsNullRequest);
var credentialRequested = credentialRequest.Requested.ToArray();
@@ -77,26 +57,24 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
Assert.NotEmpty(credentialRequested[0].BitCommitments);
Assert.NotEmpty(credentialRequested[1].BitCommitments);
- credentialResponse = issuer.HandleRequest(credentialRequest);
- client.HandleResponse(credentialResponse);
- credentials = client.Credentials.ToArray();
- Assert.NotEmpty(credentials);
- Assert.Equal(6, credentials.Count());
- Assert.True(credentials[0].Amount.IsZero);
- Assert.True(credentials[1].Amount.IsZero);
- Assert.True(credentials[2].Amount.IsZero);
- Assert.Equal(new Scalar(100_000_000), credentials[3].Amount);
- Assert.True(credentials[4].Amount.IsZero);
+ // Issuer part
+ var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
+
+ var credentialResponse = issuer.HandleRequest(credentialRequest);
+ client.HandleResponse(credentialResponse, validationData);
+ var issuedCredential = Assert.Single(client.Credentials.Valuable);
+ Assert.Equal(new Scalar(100_000_000), issuedCredential.Amount);
+
+ Assert.Equal(2, client.Credentials.ZeroValue.Count());
+ Assert.Equal(3, client.Credentials.All.Count());
}
{
- var availableCredentials = client.Credentials.ToArray();
- var credentialRequest = client.AsCredentialRequestBuilder()
- .RequestCredentialFor(Money.Coins(0.5m))
- .RequestCredentialFor(Money.Coins(0.5m))
- .PresentCredentials(availableCredentials.Single(x => !x.Amount.IsZero))
- .Build();
+ var requestFactory = client.GetCredentialRegistrationFactory();
+ var valueableCredential = client.Credentials.Valuable.Take(1);
+ var amounts = Enumerable.Repeat(Money.Coins(0.5m), 2);
+ var (credentialRequest, validationData) = requestFactory.CreateRequest(amounts, valueableCredential);
Assert.False(credentialRequest.IsNullRequest);
var requested = credentialRequest.Requested.ToArray();
@@ -109,47 +87,41 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
var credentialResponse = issuer.HandleRequest(credentialRequest);
- client.HandleResponse(credentialResponse);
- var credentials = client.Credentials.ToArray();
+ client.HandleResponse(credentialResponse, validationData);
+ var credentials = client.Credentials.All.ToArray();
Assert.NotEmpty(credentials);
- Assert.Equal(6, credentials.Count());
+ Assert.Equal(3, credentials.Count());
- var nonNullCredentials = client.Credentials.Where(x => !x.Amount.IsZero).ToArray();
- Assert.Equal(new Scalar(50_000_000), nonNullCredentials[0].Amount);
- Assert.Equal(new Scalar(50_000_000), nonNullCredentials[1].Amount);
+ var valuableCredentials = client.Credentials.Valuable.ToArray();
+ Assert.Equal(new Scalar(50_000_000), valuableCredentials[0].Amount);
+ Assert.Equal(new Scalar(50_000_000), valuableCredentials[1].Amount);
}
{
var client0 = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd);
- var credentialRequest = client0.AsCredentialRequestBuilder()
- .Build();
+ var requestFactory = client0.GetCredentialRegistrationFactory();
+
+ var (credentialRequest, validationData) = requestFactory.CreateRequestForZeroAmount();
var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
var credentialResponse = issuer.HandleRequest(credentialRequest);
- client0.HandleResponse(credentialResponse);
+ client0.HandleResponse(credentialResponse, validationData);
- credentialRequest = client0.AsCredentialRequestBuilder()
- .RequestCredentialFor(Money.Coins(1))
- .Build();
+ (credentialRequest, validationData) = requestFactory.CreateRequest(new[]{ Money.Coins(1m) }, Enumerable.Empty<Credential>());
credentialResponse = issuer.HandleRequest(credentialRequest);
- client0.HandleResponse(credentialResponse);
-
- var nonNullCredentials = client0.Credentials.Where(x => !x.Amount.IsZero).ToArray();
+ client0.HandleResponse(credentialResponse, validationData);
- credentialRequest = client0.AsCredentialRequestBuilder()
- .PresentCredentials(nonNullCredentials)
- .Build();
+ (credentialRequest, validationData) = requestFactory.CreateRequest(new Money[0], client0.Credentials.Valuable);
- // Issuer part
credentialResponse = issuer.HandleRequest(credentialRequest);
- client0.HandleResponse(credentialResponse);
- var credentials = client0.Credentials.ToArray();
- Assert.NotEmpty(credentials);
- Assert.Equal(numberOfCredentials, credentials.Count());
+ client0.HandleResponse(credentialResponse, validationData);
+ Assert.NotEmpty(client0.Credentials.All);
+ Assert.Equal(numberOfCredentials, client0.Credentials.All.Count());
}
}
+
[Fact]
[Trait("UnitTest", "UnitTest")]
public void InvalidCredentialRequests()
@@ -163,36 +135,23 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd);
// Null request. This requests `numberOfCredentials` zero-value credentials.
- var credentialRequest = client.AsCredentialRequestBuilder()
- .Build();
+ var factory = client.GetCredentialRegistrationFactory();
+ var (credentialRequest, validationData) = factory.CreateRequestForZeroAmount();
var credentialResponse = issuer.HandleRequest(credentialRequest);
- client.HandleResponse(credentialResponse);
-
- var validCredentialRequest = client.AsCredentialRequestBuilder()
- .PresentCredentials(client.Credentials.First())
- .Build();
-
- // Test duplicated serial numbers
- var presented = credentialRequest.Presented.ToArray();
- var invalidCredentialRequest = new RegistrationRequest(
- validCredentialRequest.DeltaAmount,
- new[] { presented[0], presented[0], presented[0] }, // dupliacted
- validCredentialRequest.Requested,
- validCredentialRequest.Proofs);
+ client.HandleResponse(credentialResponse, validationData);
- var ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
- Assert.Equal(WabiSabiErrorCode.SerialNumberDuplicated, ex.ErrorCode);
+ var (validCredentialRequest, _) = factory.CreateRequest(new Money[0], client.Credentials.ZeroValue.Take(1));
// Test incorrect number of presentations (one instead of 3)
- presented = credentialRequest.Presented.ToArray();
- invalidCredentialRequest = new RegistrationRequest(
+ var presented = validCredentialRequest.Presented.ToArray();
+ var invalidCredentialRequest = new RegistrationRequest(
validCredentialRequest.DeltaAmount,
new[] { presented[0] }, // Should present 3 credentials
validCredentialRequest.Requested,
validCredentialRequest.Proofs);
- ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
+ var ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
Assert.Equal("3 credential presentations were expected but 1 were received.", ex.Message);
@@ -208,9 +167,8 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
Assert.Equal(WabiSabiErrorCode.InvalidNumberOfPresentedCredentials, ex.ErrorCode);
Assert.Equal("3 credential presentations were expected but 0 were received.", ex.Message);
- validCredentialRequest = client.AsCredentialRequestBuilder()
- .PresentCredentials(client.Credentials.ToArray())
- .Build();
+
+ (validCredentialRequest, _) = factory.CreateRequest(new Money[0], client.Credentials.All);
// Test incorrect number of credential requests
invalidCredentialRequest = new RegistrationRequest(
@@ -240,7 +198,7 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
invalidCredentialRequest = new RegistrationRequest(
validCredentialRequest.DeltaAmount,
validCredentialRequest.Presented,
- new[] { requested[0], requested[1], new CredentialIssuanceRequest(requested[2].Ma, new[] { GroupElement.Infinity } )},
+ new[] { requested[0], requested[1], new IssuanceRequest(requested[2].Ma, new[] { GroupElement.Infinity } )},
validCredentialRequest.Proofs );
ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(invalidCredentialRequest));
@@ -249,9 +207,8 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
{
var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd);
-
- var validCredentialRequest = client.AsCredentialRequestBuilder()
- .Build();
+ var factory = client.GetCredentialRegistrationFactory();
+ var (validCredentialRequest, validationData) = factory.CreateRequestForZeroAmount();
// Test invalid proofs
var proofs = validCredentialRequest.Proofs.ToArray();
@@ -268,19 +225,16 @@ namespace WalletWasabi.Tests.UnitTests.Crypto.Api
{
var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd);
+ var factory = client.GetCredentialRegistrationFactory();
+ var (validCredentialRequest, validationData) = factory.CreateRequestForZeroAmount();
- var credentialRequest = client.AsCredentialRequestBuilder()
- .Build();
-
- var credentialResponse = issuer.HandleRequest(credentialRequest);
- client.HandleResponse(credentialResponse);
+ var credentialResponse = issuer.HandleRequest(validCredentialRequest);
+ client.HandleResponse(credentialResponse, validationData);
- credentialRequest = client.AsCredentialRequestBuilder()
- .PresentCredentials(client.Credentials.ToArray())
- .Build();
+ (validCredentialRequest, validationData) = factory.CreateRequest(Enumerable.Empty<Money>(), client.Credentials.All);
- issuer.HandleRequest(credentialRequest);
- var ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(credentialRequest));
+ issuer.HandleRequest(validCredentialRequest);
+ var ex = Assert.Throws<WabiSabiException>(() => issuer.HandleRequest(validCredentialRequest));
Assert.Equal(WabiSabiErrorCode.SerialNumberAlreadyUsed, ex.ErrorCode);
}
}

DELETE
WalletWasabi/Crypto/Api/CredentialIssuanceRequest.cs
-using System.Collections.Generic;
-using WalletWasabi.Crypto.Groups;
-
-namespace WalletWasabi.Crypto.Api
-{
- public class CredentialIssuanceRequest
- {
- public CredentialIssuanceRequest(GroupElement ma, IEnumerable<GroupElement> bitCommitments)
- {
- Ma = ma;
- BitCommitments = bitCommitments;
- }
-
- public GroupElement Ma { get; }
- public IEnumerable<GroupElement> BitCommitments { get; }
- }
-}
\ No newline at end of file

DELETE
WalletWasabi/Crypto/Api/ICredentialRequestBuilder.cs
-using NBitcoin;
-using WalletWasabi.Crypto.ZeroKnowledge;
-
-namespace WalletWasabi.Crypto.Api
-{
- public interface ICredentialRequestBuilder
- {
- ICredentialRequestBuilder RequestCredentialFor(Money amount);
-
- ICredentialRequestBuilder PresentCredentials(params Credential[] credential);
-
- RegistrationRequest Build();
- }
-}
\ No newline at end of file

RENAME
WalletWasabi/Crypto/Api/CredentialIssuer.cs
WalletWasabi/Wabisabi/CredentialIssuer.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using NBitcoin;
using NBitcoin.Secp256k1;
+using WalletWasabi.Crypto;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Crypto.Randomness;
using WalletWasabi.Crypto.ZeroKnowledge;
@@ -11,7 +12,7 @@ using WalletWasabi.Crypto.ZeroKnowledge.LinearRelation;
using WalletWasabi.Crypto.ZeroKnowledge.NonInteractive;
using WalletWasabi.Helpers;
-namespace WalletWasabi.Crypto.Api
+namespace WalletWasabi.Wabisabi
{
public class CredentialIssuer
{
@@ -36,23 +37,25 @@ namespace WalletWasabi.Crypto.Api
private CoordinatorParameters CoordinatorParameters { get; }
+ // Gets the number of credential that has to be requested/presented
+ // This parameter is called `k` in the wabisabi paper.
public int NumberOfCredentials { get; }
public RegistrationResponse HandleRequest(RegistrationRequest registrationRequest)
{
Guard.NotNull(nameof(registrationRequest), registrationRequest);
- var requested = registrationRequest.Requested;
- var presented = registrationRequest.Presented;
+ var requested = registrationRequest.Requested ?? Enumerable.Empty<IssuanceRequest>();
+ var presented = registrationRequest.Presented ?? Enumerable.Empty<CredentialPresentation>();
- var requestedCount = requested is null ? 0 : requested.Count();
+ var requestedCount = requested.Count();
if (requestedCount != NumberOfCredentials)
{
throw new WabiSabiException(WabiSabiErrorCode.InvalidNumberOfRequestedCredentials,
$"{NumberOfCredentials} credential requests were expected but {requestedCount} were received.");
}
- var presentedCount = presented is null ? 0 : presented.Count();
+ var presentedCount = presented.Count();
var requiredNumberOfPresentations = registrationRequest.IsNullRequest ? 0 : NumberOfCredentials;
if (presentedCount != requiredNumberOfPresentations)
{
@@ -79,7 +82,7 @@ namespace WalletWasabi.Crypto.Api
}
// Check all the serial numbers are unique.
- if (registrationRequest.SerialNumbers.Distinct().Count() < registrationRequest.SerialNumbers.Count())
+ if (registrationRequest.AreThereDuplicatedSerialNumbers)
{
throw new WabiSabiException(WabiSabiErrorCode.SerialNumberDuplicated);
}

ADD
WalletWasabi/Wabisabi/CredentialPool.cs
+using System.Collections.Generic;
+using System.Linq;
+using WalletWasabi.Crypto.ZeroKnowledge;
+
+namespace WalletWasabi.Wabisabi
+{
+ public class CredentialPool
+ {
+ internal CredentialPool()
+ {
+ }
+
+ private HashSet<Credential> Credentials { get; } = new HashSet<Credential>();
+
+ public IEnumerable<Credential> ZeroValue => Credentials.Where(x => x.Amount.IsZero);
+
+ public IEnumerable<Credential> Valuable => Credentials.Where(x => !x.Amount.IsZero);
+
+ public IEnumerable<Credential> All => Credentials;
+
+ internal void UpdateCredentials(IEnumerable<Credential> newCredentials, IEnumerable<Credential> oldCredentials)
+ {
+ var hs = oldCredentials.ToHashSet();
+ Credentials.RemoveWhere(x => hs.Contains(x));
+
+ foreach (var credential in newCredentials)
+ {
+ Credentials.Add(credential);
+ }
+ }
+ }
+}
\ No newline at end of file

ADD
WalletWasabi/Wabisabi/CredentialRegistrationFactory.cs
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NBitcoin;
+using NBitcoin.Secp256k1;
+using WalletWasabi.Crypto;
+using WalletWasabi.Crypto.Groups;
+using WalletWasabi.Crypto.Randomness;
+using WalletWasabi.Crypto.ZeroKnowledge;
+using WalletWasabi.Crypto.ZeroKnowledge.LinearRelation;
+using WalletWasabi.Crypto.ZeroKnowledge.NonInteractive;
+using WalletWasabi.Helpers;
+
+namespace WalletWasabi.Wabisabi
+{
+ public class CredentialRegistrationFactory
+ {
+ internal CredentialRegistrationFactory(
+ int numberOfCredentials,
+ CredentialPool credentialPool,
+ CoordinatorParameters coordinatorParameters,
+ WasabiRandom random)
+ {
+ NumberOfCredentials = numberOfCredentials;
+ Random = random;
+ CoordinatorParameters = coordinatorParameters;
+ CredentialPool = credentialPool;
+ }
+
+ private int NumberOfCredentials { get; }
+
+ private CoordinatorParameters CoordinatorParameters { get; }
+
+ private CredentialPool CredentialPool { get; }
+
+ private WasabiRandom Random { get; }
+
+ public (RegistrationRequest, RegistrationValidationData) CreateRequestForZeroAmount()
+ {
+ var credentialsToRequest = new IssuanceRequest[NumberOfCredentials];
+ var knowledge = new Knowledge[NumberOfCredentials];
+ var validationData = new IssuanceValidationData[NumberOfCredentials];
+
+ for (var i = 0; i < NumberOfCredentials; i++)
+ {
+ var amount = Money.Zero;
+ var attribute = Attribute.FromMoney(amount, Random);
+
+ knowledge[i] = ProofSystem.ZeroProof(attribute.Ma, attribute.Randomness);
+ credentialsToRequest[i] = new IssuanceRequest(attribute.Ma, Enumerable.Empty<GroupElement>());
+ validationData[i] = new IssuanceValidationData(amount, attribute.Randomness, attribute.Ma);
+ }
+
+ // Generate Balance Proof
+ // var r = Random.GetScalar();
+ // var Ma = r * Generators.Gh;
+ // var balanceKnowledge = ProofSystem.ZeroProof(Ma, r);
+ var knowledgeToProve = knowledge; //.Append(balanceKnowledge);
+
+ var transcript = BuildTransnscript(isNullRequest: true);
+
+ return (
+ new RegistrationRequest(
+ Money.Zero,
+ Enumerable.Empty<CredentialPresentation>(),
+ credentialsToRequest,
+ Prover.Prove(transcript, knowledgeToProve, Random)),
+ new RegistrationValidationData(
+ transcript,
+ Enumerable.Empty<Credential>(),
+ validationData
+ ));
+ }
+
+ public (RegistrationRequest, RegistrationValidationData) CreateRequest(
+ IEnumerable<Money> amountsToRequest,
+ IEnumerable<Credential> credentialsToPresent)
+ {
+ // Make sure we request always the same number of credentials
+ var credentialAmountsToRequest = amountsToRequest.ToList();
+ var missingCredentialRequests = NumberOfCredentials - amountsToRequest.Count();
+ for (var i = 0; i < missingCredentialRequests; i++)
+ {
+ credentialAmountsToRequest.Add(Money.Zero);
+ }
+
+ // Make sure we present always the same number of credentials (except for Null requests)
+ var missingCredentialPresent = NumberOfCredentials - credentialsToPresent.Count();
+
+ var alreadyPresentedZeroCredentials = credentialsToPresent.Where(x => x.Amount.IsZero);
+ var availableZeroCredentials = CredentialPool.ZeroValue.Except(alreadyPresentedZeroCredentials);
+
+ // This should not be possible
+ var availableZeroCredentialCount = availableZeroCredentials.Count();
+ if (availableZeroCredentialCount < missingCredentialPresent)
+ {
+ throw new WabiSabiException(
+ WabiSabiErrorCode.NotEnoughZeroCredentialToFillTheRequest,
+ $"{missingCredentialPresent} credentials are missing but there are only {availableZeroCredentialCount} zero-value credentials available.");
+ }
+
+ credentialsToPresent = credentialsToPresent.Concat(availableZeroCredentials.Take(missingCredentialPresent)).ToList();
+ var macsToPresent = credentialsToPresent.Select(x => x.Mac);
+ if (macsToPresent.Distinct().Count() < macsToPresent.Count())
+ {
+ throw new WabiSabiException(WabiSabiErrorCode.CredentialToPresentDuplicated);
+ }
+
+ var zs = new List<Scalar>();
+ var knowledgeToProve = new List<Knowledge>();
+ var presentations = new List<CredentialPresentation>();
+ foreach (var credential in credentialsToPresent)
+ {
+ var z = Random.GetScalar();
+ var presentation = credential.Present(z);
+ presentations.Add(presentation);
+ knowledgeToProve.Add(ProofSystem.ShowCredential(presentation, z, credential, CoordinatorParameters));
+ zs.Add(z);
+ }
+
+ // Generate RangeProofs (or ZeroProof) for each requested credential
+ var credentialsToRequest = new IssuanceRequest[NumberOfCredentials];
+ var validationData = new IssuanceValidationData[NumberOfCredentials];
+ for (var i = 0; i < NumberOfCredentials; i++)
+ {
+ var amount = credentialAmountsToRequest[i];
+ var attribute = Attribute.FromMoney(amount, Random);
+ var scalarAmount = new Scalar((ulong)amount.Satoshi);
+
+ var (rangeKnowledge, bitCommitments) = ProofSystem.RangeProof(scalarAmount, attribute.Randomness, Constants.RangeProofWidth, Random);
+ knowledgeToProve.Add(rangeKnowledge);
+
+ var credentialRequest = new IssuanceRequest(attribute.Ma, bitCommitments);
+ credentialsToRequest[i] = credentialRequest;
+ validationData[i] = new IssuanceValidationData(amount, attribute.Randomness, credentialRequest.Ma);
+ }
+
+ // Generate Balance Proof
+ var sumOfZ = zs.Sum();
+ var cr = credentialsToPresent.Select(x => x.Randomness).Sum();
+ var r = validationData.Select(x => x.Randomness).Sum();
+ var deltaR = cr + r.Negate();
+
+ var balanceKnowledge = ProofSystem.BalanceProof(sumOfZ, deltaR);
+ knowledgeToProve.Add(balanceKnowledge);
+
+ var transcript = BuildTransnscript(isNullRequest: false);
+ return (
+ new RegistrationRequest(
+ amountsToRequest.Sum() - credentialsToPresent.Sum(x => x.Amount.ToMoney()),
+ presentations,
+ credentialsToRequest,
+ Prover.Prove(transcript, knowledgeToProve, Random)),
+ new RegistrationValidationData(
+ transcript,
+ credentialsToPresent,
+ validationData
+ ));
+ }
+
+ private Transcript BuildTransnscript(bool isNullRequest)
+ {
+ var label = $"UnifiedRegistration/{NumberOfCredentials}/{isNullRequest}";
+ var encodedLabel = Encoding.UTF8.GetBytes(label);
+ return new Transcript(encodedLabel);
+ }
+ }
+}

RENAME
WalletWasabi/Crypto/Api/RegistrationRequest.cs
WalletWasabi/Wabisabi/RegistrationRequest.cs
using System.Collections.Generic;
using System.Linq;
using NBitcoin;
+using NBitcoin.Secp256k1;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Crypto.ZeroKnowledge;
+using WalletWasabi.Crypto.ZeroKnowledge.LinearRelation;
using WalletWasabi.Crypto.ZeroKnowledge.NonInteractive;
-namespace WalletWasabi.Crypto.Api
+namespace WalletWasabi.Wabisabi
{
public class RegistrationRequest
{
internal RegistrationRequest(
Money balance,
IEnumerable<CredentialPresentation> presented,
- IEnumerable<CredentialIssuanceRequest> requested,
+ IEnumerable<IssuanceRequest> requested,
IEnumerable<Proof> proofs)
{
DeltaAmount = balance;
@@ -25,12 +27,15 @@ namespace WalletWasabi.Crypto.Api
public IEnumerable<CredentialPresentation> Presented { get; }
- public IEnumerable<CredentialIssuanceRequest> Requested { get; }
+ public IEnumerable<IssuanceRequest> Requested { get; }
public IEnumerable<Proof> Proofs { get; }
public bool IsNullRequest => DeltaAmount == Money.Zero && !Presented.Any();
public IEnumerable<GroupElement> SerialNumbers => Presented.Select(x => x.S);
+
+ public bool AreThereDuplicatedSerialNumbers => SerialNumbers.Distinct().Count() < SerialNumbers.Count();
+
}
}
\ No newline at end of file

RENAME
WalletWasabi/Crypto/Api/RegistrationResponse.cs
WalletWasabi/Wabisabi/RegistrationResponse.cs
using System;
using System.Collections.Generic;
+using WalletWasabi.Crypto;
using WalletWasabi.Crypto.ZeroKnowledge.NonInteractive;
-namespace WalletWasabi.Crypto.Api
+namespace WalletWasabi.Wabisabi
{
public class RegistrationResponse
{

ADD
WalletWasabi/Wabisabi/WabiSabiErrorCode.cs
+namespace WalletWasabi.Wabisabi
+{
+ public enum WabiSabiErrorCode
+ {
+ Unspecified = 0,
+ SerialNumberAlreadyUsed = 1,
+ CoordinatorReceivedInvalidProofs = 2,
+ NegativeBalance = 3,
+ InvalidBitCommitment = 4,
+ ClientReceivedInvalidProofs = 5,
+ IssuedCredentialNumberMismatch = 6,
+ SerialNumberDuplicated = 7,
+ NotEnoughZeroCredentialToFillTheRequest = 8,
+ InvalidNumberOfRequestedCredentials = 9,
+ InvalidNumberOfPresentedCredentials = 10,
+ CredentialToPresentDuplicated = 11,
+ }
+}
\ No newline at end of file

RENAME
WalletWasabi/Crypto/Api/WabiSabiException.cs
WalletWasabi/Wabisabi/WabiSabiException.cs
using System;
using System.Runtime.Serialization;
-namespace WalletWasabi.Crypto.Api
+namespace WalletWasabi.Wabisabi
{
- public enum WabiSabiErrorCode
- {
- Unspecified = 0,
- SerialNumberAlreadyUsed = 1,
- CoordinatorReceivedInvalidProofs = 2,
- NegativeBalance = 3,
- InvalidBitCommitment = 4,
- ClientReceivedInvalidProofs = 5,
- IssuedCredentialNumberMismatch = 6,
- SerialNumberDuplicated = 7,
- NotEnoughZeroCredentialToFillTheRequest = 8,
- InvalidNumberOfRequestedCredentials = 9,
- InvalidNumberOfPresentedCredentials = 10,
- }
-
[Serializable]
public class WabiSabiException : Exception
{

Stateless client