WabiSabi Messages Serialization
ADD
WalletWasabi.Tests/UnitTests/Wabisabi/SerializationTests.cs
+using System;
+using System.Linq;
+using NBitcoin;
+using NBitcoin.Secp256k1;
+using Newtonsoft.Json;
+using WalletWasabi.Crypto;
+using WalletWasabi.Crypto.Groups;
+using WalletWasabi.Crypto.Randomness;
+using WalletWasabi.JsonConverters;
+using WalletWasabi.Wabisabi;
+using WalletWasabi.Wabisabi.Serialization;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests.Wabisabi
+{
+ public class SerializationTests
+ {
+ [Fact]
+ public void GroupElementSerialization()
+ {
+ var converters = new JsonConverter[]
+ {
+ new GroupElementJsonConverter()
+ };
+
+ // Deserialization invalid elements
+ var ex = Assert.Throws<ArgumentException>(() => JsonConvert.DeserializeObject<GroupElement>("21", converters));
+ Assert.StartsWith("No valid serialized GroupElement", ex.Message);
+
+ ex = Assert.Throws<ArgumentException>(() => JsonConvert.DeserializeObject<GroupElement>("\"\"", converters));
+ Assert.StartsWith("Parameter must be 33. Actual: 0.", ex.Message);
+
+ ex = Assert.Throws<ArgumentException>(() => JsonConvert.DeserializeObject<GroupElement>("\"0000000000000000000000000000000000000000000000000000000000000000\"", converters));
+ Assert.StartsWith("Parameter must be 33. Actual: 32.", ex.Message);
+
+ // Serialization Infinity test
+ var serializedInfinityGroupElement = JsonConvert.SerializeObject(GroupElement.Infinity, converters);
+ Assert.Equal("\"000000000000000000000000000000000000000000000000000000000000000000\"", serializedInfinityGroupElement);
+
+ var deserializedInfinityGroupElement = JsonConvert.DeserializeObject<GroupElement>("\"000000000000000000000000000000000000000000000000000000000000000000\"", converters);
+ Assert.Equal(GroupElement.Infinity, deserializedInfinityGroupElement);
+
+ // Serialization round test
+ var serializedGroupElement = JsonConvert.SerializeObject(Generators.G, converters);
+ Assert.Equal("\"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798\"", serializedGroupElement);
+
+ var deserializedGroupElement = JsonConvert.DeserializeObject<GroupElement>("\"0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798\"", converters);
+ Assert.Equal(Generators.G, deserializedGroupElement);
+ }
+
+
+ [Fact]
+ public void GroupElementCollectionSerialization()
+ {
+ var converters = new JsonConverter[]
+ {
+ new GroupElementJsonConverter()
+ };
+
+ // Serialization collection test
+ var serializedGroupElements = JsonConvert.SerializeObject(new[]{ Generators.Gx0, Generators.Gx1 }, converters);
+ Assert.Equal("[\"02E33C9F3CBE6388A2D3C3ECB12153DB73499928541905D86AAA4FFC01F2763B54\",\"0246253CC926AAB789BAA278AB9A54EDEF455CA2014038E9F84DE312C05A8121CC\"]", serializedGroupElements);
+
+ var deserializedGroupElements = JsonConvert.DeserializeObject<GroupElement[]>("[\"02E33C9F3CBE6388A2D3C3ECB12153DB73499928541905D86AAA4FFC01F2763B54\",\"0246253CC926AAB789BAA278AB9A54EDEF455CA2014038E9F84DE312C05A8121CC\"]", converters);
+ Assert.Equal(Generators.Gx0, deserializedGroupElements[0]);
+ Assert.Equal(Generators.Gx1, deserializedGroupElements[1]);
+
+ var deserializedGroupElementVector = JsonConvert.DeserializeObject<GroupElementVector>("[\"02E33C9F3CBE6388A2D3C3ECB12153DB73499928541905D86AAA4FFC01F2763B54\",\"0246253CC926AAB789BAA278AB9A54EDEF455CA2014038E9F84DE312C05A8121CC\"]", converters);
+ Assert.Equal(deserializedGroupElements, deserializedGroupElementVector);
+ }
+
+
+ [Fact]
+ public void IssuanceRequestSerialization()
+ {
+ var converters = new JsonConverter[]
+ {
+ new GroupElementJsonConverter()
+ };
+
+ // Serialization round test
+ var issuanceRequest = new IssuanceRequest(Generators.G, Enumerable.Range(0, 5).Select(i => Generators.FromText($"G{i}")));
+ var serializedIssuanceRequest = JsonConvert.SerializeObject(issuanceRequest, converters);
+
+ var deserializedIssuanceRequest = JsonConvert.DeserializeObject<IssuanceRequest>(serializedIssuanceRequest, converters);
+ Assert.Equal(issuanceRequest.Ma, deserializedIssuanceRequest.Ma);
+ Assert.Equal(issuanceRequest.BitCommitments, deserializedIssuanceRequest.BitCommitments);
+ }
+
+
+ [Fact]
+ public void ScalarSerialization()
+ {
+ var converters = new JsonConverter[]
+ {
+ new ScalarJsonConverter()
+ };
+
+ // Deserialization invalid scalar
+ var ex = Assert.Throws<ArgumentException>(() => JsonConvert.DeserializeObject<Scalar>("21", converters));
+ Assert.StartsWith("No valid serialized Scalar", ex.Message);
+
+ Assert.Throws<IndexOutOfRangeException>(() => JsonConvert.DeserializeObject<Scalar>("\"\"", converters));
+
+ // Serialization Zero test
+ var serializedZero = JsonConvert.SerializeObject(Scalar.Zero, converters);
+ Assert.Equal("\"0000000000000000000000000000000000000000000000000000000000000000\"", serializedZero);
+
+ var deserializedZero = JsonConvert.DeserializeObject<Scalar>("\"0000000000000000000000000000000000000000000000000000000000000000\"", converters);
+ Assert.Equal(Scalar.Zero, deserializedZero);
+
+ // Serialization round test
+ var scalar = new Scalar(ByteHelpers.FromHex("D9C17A80D299A51E1ED9CF94FCE5FD883ADACE4ECC167E1D1FB8E5C4A0ADC4D2"));
+
+ var serializedScalar = JsonConvert.SerializeObject(scalar, converters);
+ Assert.Equal("\"D9C17A80D299A51E1ED9CF94FCE5FD883ADACE4ECC167E1D1FB8E5C4A0ADC4D2\"", serializedScalar);
+
+ var deserializedScalar = JsonConvert.DeserializeObject<Scalar>("\"D9C17A80D299A51E1ED9CF94FCE5FD883ADACE4ECC167E1D1FB8E5C4A0ADC4D2\"", converters);
+ Assert.Equal(scalar, deserializedScalar);
+ }
+
+
+ [Fact]
+ public void ScalarCollectionSerialization()
+ {
+ var converters = new JsonConverter[]
+ {
+ new ScalarJsonConverter()
+ };
+
+ // Serialization collection test
+ var one = Scalar.One;
+ var two = one + one;
+ var three = two + one;
+ var other = two.Sqr(8) + three;
+
+ var serializedScalars = JsonConvert.SerializeObject(new[]{ one, two }, converters);
+ Assert.Equal("[\"0000000000000000000000000000000000000000000000000000000000000001\",\"0000000000000000000000000000000000000000000000000000000000000002\"]", serializedScalars);
+
+ var deserializedScalars = JsonConvert.DeserializeObject<Scalar[]>("[\"000000000000000000000000000000014551231950B75FC4402DA1732FC9BEC2\",\"0000000000000000000000000000000000000000000000000000000000000003\"]", converters);
+ Assert.Equal(other, deserializedScalars[0]);
+ Assert.Equal(three, deserializedScalars[1]);
+
+ var deserializedScalarVector = JsonConvert.DeserializeObject<ScalarVector>("[\"000000000000000000000000000000014551231950B75FC4402DA1732FC9BEC2\",\"0000000000000000000000000000000000000000000000000000000000000003\"]", converters);
+ Assert.Equal(deserializedScalars, deserializedScalarVector);
+ }
+
+ [Fact]
+ public void RegistrationMessageSerialization()
+ {
+ var converters = new JsonConverter[]
+ {
+ new ScalarJsonConverter(),
+ new GroupElementJsonConverter(),
+ new MoneySatoshiJsonConverter()
+ };
+
+ var numberOfCredentials = 2;
+ var rnd = new SecureRandom();
+ var sk = new CoordinatorSecretKey(rnd);
+
+ var issuer = new CredentialIssuer(sk, numberOfCredentials, rnd);
+ var client = new WabiSabiClient(sk.ComputeCoordinatorParameters(), numberOfCredentials, rnd);
+ var (credentialRequest, validationData) = client.CreateRequestForZeroAmount();
+ var credentialResponse = issuer.HandleRequest(credentialRequest);
+ client.HandleResponse(credentialResponse, validationData);
+ var present = client.Credentials.ZeroValue.Take(numberOfCredentials);
+ (credentialRequest, _) = client.CreateRequest(new[] { Money.Coins(1) }, present);
+
+ // Registration request message
+ var serializedRequestMessage = JsonConvert.SerializeObject(credentialRequest, converters);
+ var deserializedRequestMessage = JsonConvert.DeserializeObject<RegistrationRequestMessage>(serializedRequestMessage, converters);
+ var reserializedRequestMessage = JsonConvert.SerializeObject(deserializedRequestMessage, converters);
+ Assert.Equal(serializedRequestMessage, reserializedRequestMessage);
+
+ // Registration response message
+ var serializedResponseMessage = JsonConvert.SerializeObject(credentialResponse, converters);
+ var deserializedResponseMessage = JsonConvert.DeserializeObject<RegistrationResponseMessage>(serializedResponseMessage, converters);
+ var reserializedResponseMessage = JsonConvert.SerializeObject(deserializedResponseMessage, converters);
+ Assert.Equal(serializedResponseMessage, reserializedResponseMessage);
+ }
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi/Crypto/GroupElementVector.cs
WalletWasabi/Crypto/GroupElementVector.cs
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using Newtonsoft.Json;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Helpers;
@@ -8,6 +9,7 @@ namespace WalletWasabi.Crypto
{
public class GroupElementVector : IEnumerable<GroupElement>
{
+ [JsonConstructor]
internal GroupElementVector(IEnumerable<GroupElement> groupElements)
{
Guard.NotNullOrEmpty(nameof(groupElements), groupElements);

MODIFY
WalletWasabi/Crypto/Mac.cs
WalletWasabi/Crypto/Mac.cs
using System;
using NBitcoin.Secp256k1;
+using Newtonsoft.Json;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Helpers;
@@ -7,6 +8,7 @@ namespace WalletWasabi.Crypto
{
public class MAC : IEquatable<MAC>
{
+ [JsonConstructor]
private MAC(Scalar t, GroupElement v)
{
T = Guard.NotZero(nameof(t), t);

MODIFY
WalletWasabi/Crypto/ScalarVector.cs
WalletWasabi/Crypto/ScalarVector.cs
@@ -2,6 +2,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using NBitcoin.Secp256k1;
+using Newtonsoft.Json;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Helpers;
@@ -9,6 +10,7 @@ namespace WalletWasabi.Crypto
{
public class ScalarVector : IEnumerable<Scalar>
{
+ [JsonConstructor]
internal ScalarVector(IEnumerable<Scalar> scalars)
{
Guard.NotNullOrEmpty(nameof(scalars), scalars);

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/Proof.cs
WalletWasabi/Crypto/ZeroKnowledge/Proof.cs
+using Newtonsoft.Json;
using WalletWasabi.Helpers;
namespace WalletWasabi.Crypto.ZeroKnowledge
{
public class Proof
{
+ [JsonConstructor]
internal Proof(GroupElementVector publicNonces, ScalarVector responses)
{
Guard.NotNullOrInfinity(nameof(publicNonces), publicNonces);

MODIFY
WalletWasabi/Wabisabi/RegistrationRequestMessage.cs
WalletWasabi/Wabisabi/RegistrationRequestMessage.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using NBitcoin;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Crypto.ZeroKnowledge;
+using Newtonsoft.Json;
namespace WalletWasabi.Wabisabi
{
@@ -16,13 +17,14 @@ namespace WalletWasabi.Wabisabi
/// </remarks>
public class RegistrationRequestMessage
{
+ [JsonConstructor]
internal RegistrationRequestMessage(
- Money balance,
+ Money deltaAmount,
IEnumerable<CredentialPresentation> presented,
IEnumerable<IssuanceRequest> requested,
IEnumerable<Proof> proofs)
{
- DeltaAmount = balance;
+ DeltaAmount = deltaAmount;
Presented = presented;
Requested = requested;
Proofs = proofs;
@@ -56,12 +58,12 @@ namespace WalletWasabi.Wabisabi
/// <summary>
/// Is request for zero-value credentials only.
/// </summary>
- public bool IsNullRequest => DeltaAmount == Money.Zero && !Presented.Any();
+ internal bool IsNullRequest => DeltaAmount == Money.Zero && !Presented.Any();
/// <summary>
/// Serial numbers used in the credential presentations.
/// </summary>
- public IEnumerable<GroupElement> SerialNumbers => Presented.Select(x => x.S);
+ internal IEnumerable<GroupElement> SerialNumbers => Presented.Select(x => x.S);
/// <summary>
/// Indicates whether the message contains duplicated serial number or not.

WabiSabi Messages Serialization