Messages Json Serialization
ADD
WalletWasabi.Tests/UnitTests/WabiSabi/Models/SerializationTests.cs
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NBitcoin;
+using NBitcoin.Secp256k1;
+using Newtonsoft.Json;
+using WalletWasabi.Crypto;
+using WalletWasabi.Crypto.Groups;
+using WalletWasabi.Crypto.Randomness;
+using WalletWasabi.Crypto.ZeroKnowledge;
+using WalletWasabi.JsonConverters;
+using WalletWasabi.Tests.Helpers;
+using WalletWasabi.WabiSabi.Backend;
+using WalletWasabi.WabiSabi.Crypto;
+using WalletWasabi.WabiSabi.Crypto.CredentialRequesting;
+using WalletWasabi.WabiSabi.Crypto.Serialization;
+using WalletWasabi.WabiSabi.Models;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests.WabiSabi.Models
+{
+ public class SerializationTests
+ {
+
+ private static JsonConverter[] Converters =
+ {
+ new ScalarJsonConverter(),
+ new GroupElementJsonConverter(),
+ new OutPointJsonConverter(),
+ new WitScriptJsonConverter()
+ };
+
+ private static IEnumerable<GroupElement> Points = Enumerable.Range(0, int.MaxValue).Select(i => Generators.FromText($"T{i}"));
+ private static IEnumerable<Scalar> Scalars = Enumerable.Range(1, int.MaxValue).Select(i => new Scalar((uint)i));
+ private static CredentialIssuerSecretKey IssuerKey = new (new InsecureRandom());
+
+ [Fact]
+ public void InputRegistrationRequestMessageSerialization()
+ {
+ var message = new InputsRegistrationRequest(
+ Guid.NewGuid(),
+ new[] { new InputRoundSignaturePair(BitcoinFactory.CreateOutPoint(), new byte[] { 1, 2, 3, 4, 5 }) },
+ CreateZeroCredentialsRequest(),
+ CreateZeroCredentialsRequest());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void InputRegistrationResponseMessageSerialization()
+ {
+ var message = new InputsRegistrationResponse(
+ Guid.NewGuid(),
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void ConnectionConfirmationRequestMessageSerialization()
+ {
+ var message = new ConnectionConfirmationRequest(
+ Guid.NewGuid(),
+ Guid.NewGuid(),
+ CreateZeroCredentialsRequest(),
+ CreateRealCredentialsRequest(),
+ CreateZeroCredentialsRequest(),
+ CreateRealCredentialsRequest());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void ConnectionConfirmationResponseMessageSerialization()
+ {
+ var message = new ConnectionConfirmationResponse(
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void OutputRegistrationRequestMessageSerialization()
+ {
+ var message = new OutputRegistrationRequest(
+ Guid.NewGuid(),
+ BitcoinFactory.CreateScript(),
+ CreateRealCredentialsRequest(),
+ CreateRealCredentialsRequest());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void OutputRegistrationResponseMessageSerialization()
+ {
+ var message = new OutputRegistrationResponse(
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void ReissueCredentialRequestMessageSerialization()
+ {
+ var message = new ReissueCredentialRequest(
+ Guid.NewGuid(),
+ CreateRealCredentialsRequest(),
+ CreateRealCredentialsRequest(),
+ CreateZeroCredentialsRequest(),
+ CreateZeroCredentialsRequest());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void ReissueCredentialResponseMessageSerialization()
+ {
+ var message = new ReissueCredentialResponse(
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse(),
+ CreateCredentialsResponse());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void InpuRemovalRequestMessageSerialization()
+ {
+ var message = new InputsRemovalRequest(
+ Guid.NewGuid(),
+ Guid.NewGuid());
+
+ AssertSerialization(message);
+ }
+
+ [Fact]
+ public void TransactionSignatureRequestMessageSerialization()
+ {
+ using var key1 = new Key();
+ using var key2 = new Key();
+ var message = new TransactionSignaturesRequest(
+ Guid.NewGuid(),
+ new [] {
+ new InputWitnessPair(1, new WitScript(Op.GetPushOp(key1.PubKey.ToBytes())) ),
+ new InputWitnessPair(17, new WitScript(Op.GetPushOp(key2.PubKey.ToBytes())) )
+ } );
+
+ AssertSerialization(message);
+ }
+
+
+ private static void AssertSerialization<T>(T message)
+ {
+ var serializedMessage = JsonConvert.SerializeObject(message, Converters);
+ var deserializedMessage = JsonConvert.DeserializeObject<T>(serializedMessage, Converters);
+ var reserializedMessage = JsonConvert.SerializeObject(deserializedMessage, Converters);
+ Assert.Equal(reserializedMessage, serializedMessage);
+ }
+
+ private static ZeroCredentialsRequest CreateZeroCredentialsRequest() =>
+ new(
+ new[] { new IssuanceRequest(Points.First(), Points.Take(2)) },
+ new[] { new Proof(new GroupElementVector(Points.Take(2)), new ScalarVector(Scalars.Take(2))) });
+
+ private static RealCredentialsRequest CreateRealCredentialsRequest() =>
+ new(
+ 123456L,
+ new[] { new CredentialPresentation(Points.First(), Points.First(), Points.First(), Points.First(), Points.First()) },
+ new[] { new IssuanceRequest(Points.First(), Points.Take(2)) },
+ new[] { new Proof(new GroupElementVector(Points.Take(2)), new ScalarVector(Scalars.Take(2))) });
+
+ private static CredentialsResponse CreateCredentialsResponse() =>
+ new(
+ new[] { MAC.ComputeMAC(IssuerKey, Points.First(), Scalars.First()) },
+ new[] { new Proof(new GroupElementVector(Points.Take(2)), new ScalarVector(Scalars.Take(2))) });
+
+ }
+}

ADD
WalletWasabi/JsonConverters/WitScriptJsonConverter.cs
+using NBitcoin;
+using Newtonsoft.Json;
+using System;
+
+namespace WalletWasabi.JsonConverters
+{
+ public class WitScriptJsonConverter : JsonConverter
+ {
+ /// <inheritdoc />
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(WitScript);
+ }
+
+ /// <inheritdoc />
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var value = (string)reader.Value;
+ return new WitScript(ByteHelpers.FromHex(value));
+ }
+
+ /// <inheritdoc />
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var bytes = ((WitScript)value).ToBytes();
+ writer.WriteValue(ByteHelpers.ToHex(bytes));
+ }
+ }
+}

MODIFY
WalletWasabi/WabiSabi/Client/HttpRequestHandlerProxy.cs
WalletWasabi/WabiSabi/Client/HttpRequestHandlerProxy.cs
@@ -20,28 +20,17 @@ namespace WalletWasabi.WabiSabi.Client
_client = client;
}
- public async Task<InputsRegistrationResponse> RegisterInputAsync(InputsRegistrationRequest request)
- {
- using var content = Serialize(request);
- using var response = await _client.SendAsync(HttpMethod.Post, "register-input", content).ConfigureAwait(false);
+ public Task<InputsRegistrationResponse> RegisterInputAsync(InputsRegistrationRequest request) =>
+ SendAndReceiveAsync<InputsRegistrationRequest, InputsRegistrationResponse>("register-input", request);
- if (response.StatusCode != HttpStatusCode.OK)
- {
- await response.ThrowRequestExceptionFromContentAsync().ConfigureAwait(false);
- }
+ public Task<ConnectionConfirmationResponse> ConfirmConnectionAsync(ConnectionConfirmationRequest request) =>
+ SendAndReceiveAsync<ConnectionConfirmationRequest, ConnectionConfirmationResponse>("confirm-connection", request);
- return await response.Content.ReadAsJsonAsync<InputsRegistrationResponse>().ConfigureAwait(false);
- }
+ public Task<OutputRegistrationResponse> RegisterOutputAsync(OutputRegistrationRequest request) =>
+ SendAndReceiveAsync<OutputRegistrationRequest, OutputRegistrationResponse>("register-output", request);
- public Task<ConnectionConfirmationResponse> ConfirmConnectionAsync(ConnectionConfirmationRequest request)
- {
- throw new NotImplementedException();
- }
-
- public Task<OutputRegistrationResponse> RegisterOutputAsync(OutputRegistrationRequest request)
- {
- throw new NotImplementedException();
- }
+ public Task<ReissueCredentialResponse> ReissueCredentialAsync(ReissueCredentialRequest request) =>
+ SendAndReceiveAsync<ReissueCredentialRequest, ReissueCredentialResponse>("reissue-credential", request);
public Task RemoveInputAsync(InputsRemovalRequest request)
{
@@ -59,12 +48,27 @@ namespace WalletWasabi.WabiSabi.Client
return new StringContent(jsonString, Encoding.UTF8, "application/json");
}
+ private async Task<TResponse> SendAndReceiveAsync<TRequest, TResponse>(string actionName, TRequest request) where TRequest : class
+ {
+ using var content = Serialize(request);
+ using var response = await _client.SendAsync(HttpMethod.Post, GetUriEndPoint(actionName), content).ConfigureAwait(false);
+
+ if (response.StatusCode != HttpStatusCode.OK)
+ {
+ await response.ThrowRequestExceptionFromContentAsync().ConfigureAwait(false);
+ }
+
+ return await response.Content.ReadAsJsonAsync<TResponse>().ConfigureAwait(false);
+ }
+
private static string GetUriEndPoint(string action) =>
action switch
{
"register-input" => $"/api/v2/btc/wabisabicoinjoin/inputs/",
- _ => $"/api/v2/btc/wabisabicoinjoin/who-knows/"
+ "register-output" => $"/api/v2/btc/wabisabicoinjoin/outputs/",
+ "reissue-credential" => $"/api/v2/btc/wabisabicoinjoin/credentials/",
+ "confirm-connection" => $"/api/v2/btc/wabisabicoinjoin/connections/",
+ _ => throw new NotSupportedException($"Action '{action}' is unknown and has no endpoint associated.")
};
-
}
}
\ No newline at end of file

MODIFY
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/CredentialsRequest.cs
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/CredentialsRequest.cs
@@ -59,6 +59,7 @@ namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
/// <summary>
/// Is request for zero-value credentials only.
/// </summary>
+ [JsonIgnore]
internal bool IsNullRequest => Delta == 0 && !Presented.Any();
/// <summary>

Messages Json Serialization