Implement equality rules.
MODIFY
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/Startup.cs
WalletWasabi.Tests/UnitTests/WabiSabi/Integration/Startup.cs
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using WalletWasabi.Backend.Controllers.WabiSabi;
using WalletWasabi.WabiSabi.Models.Serialization;
namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration
@@ -28,6 +29,7 @@ namespace WalletWasabi.Tests.UnitTests.WabiSabi.Integration
public void ConfigureServices(IServiceCollection services)
{
var backendAssembly = typeof(WalletWasabi.Backend.Controllers.WabiSabiController).Assembly;
+ services.AddSingleton<IdempotencyRequestCache>();
services.AddMvc()
.AddApplicationPart(backendAssembly)
.AddControllersAsServices()

MODIFY
WalletWasabi/Crypto/Bip322Signature.cs
WalletWasabi/Crypto/Bip322Signature.cs
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
namespace WalletWasabi.Crypto
{
- public class Bip322Signature : IBitcoinSerializable
+ public record Bip322Signature : IBitcoinSerializable
{
private Script _scriptSig = Script.Empty;

MODIFY
WalletWasabi/Crypto/GroupElementVector.cs
WalletWasabi/Crypto/GroupElementVector.cs
+using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
-using Newtonsoft.Json;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Helpers;
@@ -30,5 +30,34 @@ namespace WalletWasabi.Crypto
GetEnumerator();
public int Count => GroupElements.Count();
+
+ public static bool operator ==(GroupElementVector? x, GroupElementVector? y) => x?.Equals(y) ?? false;
+
+ public static bool operator !=(GroupElementVector? x, GroupElementVector? y) => !(x == y);
+
+ public override int GetHashCode()
+ {
+ int hc = 0;
+
+ foreach (var element in GroupElements)
+ {
+ hc ^= element.GetHashCode();
+ hc = (hc << 7) | (hc >> (32 - 7));
+ }
+
+ return hc;
+ }
+
+ public override bool Equals(object? other) => Equals(other as GroupElementVector);
+
+ public bool Equals(GroupElementVector? other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ return GroupElements.SequenceEqual(other.GroupElements);
+ }
}
}

MODIFY
WalletWasabi/Crypto/OwnershipIdentifier.cs
WalletWasabi/Crypto/OwnershipIdentifier.cs
-using System;
-using System.Linq;
using NBitcoin;
using NBitcoin.Crypto;
-
+using System;
+using System.Linq;
namespace WalletWasabi.Crypto
{
- public class OwnershipIdentifier : IBitcoinSerializable
+ public class OwnershipIdentifier : IBitcoinSerializable, IEquatable<OwnershipIdentifier>
{
public const int OwnershipIdLength = 32;
private byte[] _bytes;
@@ -30,9 +29,32 @@ namespace WalletWasabi.Crypto
{
}
+ public byte[] Bytes => _bytes;
+
public void ReadWrite(BitcoinStream bitcoinStream)
{
bitcoinStream.ReadWrite(ref _bytes);
}
+
+ public override int GetHashCode()
+ {
+ var hash = 0;
+ for (var i = 0; i < _bytes.Length; i++)
+ {
+ hash = (hash << 4) + _bytes[i];
+ }
+ return hash;
+ }
+
+ public static bool operator ==(OwnershipIdentifier? x, OwnershipIdentifier? y) => x?.Equals(y) ?? false;
+
+ public static bool operator !=(OwnershipIdentifier? x, OwnershipIdentifier? y) => !(x == y);
+
+ public override bool Equals(object? other) => Equals(other as OwnershipIdentifier);
+
+ public bool Equals(OwnershipIdentifier? other)
+ {
+ return other is null ? false : other.Bytes.SequenceEqual(Bytes);
+ }
}
}

MODIFY
WalletWasabi/Crypto/OwnershipProof.cs
WalletWasabi/Crypto/OwnershipProof.cs
@@ -7,7 +7,7 @@ using NBitcoin.Crypto;
namespace WalletWasabi.Crypto
{
- public class OwnershipProof : IBitcoinSerializable
+ public record OwnershipProof : IBitcoinSerializable
{
private ProofBody _proofBody;
private Bip322Signature _proofSignature;

MODIFY
WalletWasabi/Crypto/ProofBody.cs
WalletWasabi/Crypto/ProofBody.cs
+using NBitcoin;
+using NBitcoin.Crypto;
using System;
using System.Collections.Generic;
using System.Linq;
-using NBitcoin;
-using NBitcoin.Crypto;
namespace WalletWasabi.Crypto
{
- public class ProofBody : IBitcoinSerializable
+ public class ProofBody : IBitcoinSerializable, IEquatable<ProofBody>
{
private static readonly byte[] VersionMagic = { 0x53, 0x4c, 0x00, 0x19 };
private List<OwnershipIdentifier> _ownershipIdentifiers = new();
@@ -52,5 +52,34 @@ namespace WalletWasabi.Crypto
private static IEnumerable<byte> ProofFooter(Script scriptPubKey, byte[] commitmentData) =>
scriptPubKey.ToBytes().Concat(commitmentData);
+
+ public override int GetHashCode()
+ {
+ int hc = 0;
+
+ foreach (var element in _ownershipIdentifiers)
+ {
+ hc ^= element.GetHashCode();
+ hc = (hc << 7) | (hc >> (32 - 7));
+ }
+
+ return HashCode.Combine(_flags.GetHashCode(), hc);
+ }
+
+ public static bool operator ==(ProofBody? x, ProofBody? y) => x?.Equals(y) ?? false;
+
+ public static bool operator !=(ProofBody? x, ProofBody? y) => !(x == y);
+
+ public override bool Equals(object? other) => Equals(other as ProofBody);
+
+ public bool Equals(ProofBody? other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ return Flags == other.Flags && OwnershipIdentifiers.SequenceEqual(other.OwnershipIdentifiers);
+ }
}
}

MODIFY
WalletWasabi/Crypto/ScalarVector.cs
WalletWasabi/Crypto/ScalarVector.cs
+using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -8,7 +9,7 @@ using WalletWasabi.Helpers;
namespace WalletWasabi.Crypto
{
- public class ScalarVector : IEnumerable<Scalar>
+ public class ScalarVector : IEnumerable<Scalar>, IEquatable<ScalarVector>
{
[JsonConstructor]
internal ScalarVector(IEnumerable<Scalar> scalars)
@@ -55,5 +56,35 @@ namespace WalletWasabi.Crypto
IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
+
+ public static bool operator ==(ScalarVector? x, ScalarVector? y) => x?.Equals(y) ?? false;
+
+ public static bool operator !=(ScalarVector? x, ScalarVector? y) => !(x == y);
+
+ public override int GetHashCode()
+ {
+ int hc = 0;
+
+ foreach (var element in Scalars)
+ {
+ hc ^= element.GetHashCode();
+ hc = (hc << 7) | (hc >> (32 - 7));
+ }
+
+ return hc;
+ }
+
+ public override bool Equals(object? other) => Equals(other as ScalarVector);
+
+ // TODO: Define GetHashCode.
+ public bool Equals(ScalarVector? other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ return Scalars.SequenceEqual(other.Scalars);
+ }
}
}

MODIFY
WalletWasabi/Crypto/ZeroKnowledge/Proof.cs
WalletWasabi/Crypto/ZeroKnowledge/Proof.cs
using Newtonsoft.Json;
+using System;
using WalletWasabi.Helpers;
namespace WalletWasabi.Crypto.ZeroKnowledge
@@ -17,5 +18,23 @@ namespace WalletWasabi.Crypto.ZeroKnowledge
public GroupElementVector PublicNonces { get; }
public ScalarVector Responses { get; }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(PublicNonces, Responses);
+ }
+
+ public virtual bool Equals(Proof? other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ bool isEqual = PublicNonces == other.PublicNonces
+ && Responses == other.Responses;
+
+ return isEqual;
+ }
}
}

MODIFY
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/CredentialsRequest.cs
WalletWasabi/WabiSabi/Crypto/CredentialRequesting/CredentialsRequest.cs
-using System.Linq;
+using Newtonsoft.Json;
+using System;
using System.Collections.Generic;
+using System.Linq;
+using WalletWasabi.Crypto;
using WalletWasabi.Crypto.Groups;
using WalletWasabi.Crypto.ZeroKnowledge;
-using Newtonsoft.Json;
-using System.ComponentModel.DataAnnotations;
namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
{
@@ -15,7 +16,7 @@ namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
/// inputs registration and outputs registration and it is designed to support
/// credentials reissuance.
/// </remarks>
- public abstract class CredentialsRequest
+ public abstract class CredentialsRequest : IEquatable<CredentialsRequest>
{
[JsonConstructor]
internal CredentialsRequest(
@@ -78,5 +79,51 @@ namespace WalletWasabi.WabiSabi.Crypto.CredentialRequesting
/// </summary>
[JsonIgnore]
internal bool AreThereDuplicatedSerialNumbers => SerialNumbers.Distinct().Count() < SerialNumbers.Count();
+
+ public override int GetHashCode()
+ {
+ int hc = 0;
+
+ foreach (var element in Presented)
+ {
+ hc ^= element.GetHashCode();
+ hc = (hc << 7) | (hc >> (32 - 7));
+ }
+
+ foreach (var element in Requested)
+ {
+ hc ^= element.GetHashCode();
+ hc = (hc << 7) | (hc >> (32 - 7));
+ }
+
+ foreach (var element in Proofs)
+ {
+ hc ^= element.GetHashCode();
+ hc = (hc << 7) | (hc >> (32 - 7));
+ }
+
+ return HashCode.Combine(Delta.GetHashCode(), hc);
+ }
+
+ public static bool operator ==(CredentialsRequest? x, CredentialsRequest? y) => x?.Equals(y) ?? false;
+
+ public static bool operator !=(CredentialsRequest? x, CredentialsRequest? y) => !(x == y);
+
+ public override bool Equals(object? other) => Equals(other as CredentialsRequest);
+
+ public bool Equals(CredentialsRequest? other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ bool isEqual = Delta == other.Delta
+ && Presented.SequenceEqual(other.Presented)
+ && Requested.SequenceEqual(other.Requested)
+ && Proofs.SequenceEqual(other.Proofs);
+
+ return isEqual;
+ }
}
}

MODIFY
WalletWasabi/WabiSabi/Crypto/IssuanceRequest.cs
WalletWasabi/WabiSabi/Crypto/IssuanceRequest.cs
-using System.Collections.Generic;
using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using WalletWasabi.Crypto.Groups;
-
namespace WalletWasabi.WabiSabi.Crypto
{
/// <summary>
/// Represents a request for issuing a new credential.
/// </summary>
- public record IssuanceRequest
+ public record IssuanceRequest : IEquatable<IssuanceRequest>
{
[JsonConstructor]
internal IssuanceRequest(GroupElement ma, IEnumerable<GroupElement> bitCommitments)
@@ -25,5 +26,31 @@ namespace WalletWasabi.WabiSabi.Crypto
/// Pedersen commitments to the credential amount's binary decomposition.
/// </summary>
public IEnumerable<GroupElement> BitCommitments { get; }
+
+ public override int GetHashCode()
+ {
+ int hc = 0;
+
+ foreach (var element in BitCommitments)
+ {
+ hc ^= element.GetHashCode();
+ hc = (hc << 7) | (hc >> (32 - 7));
+ }
+
+ return HashCode.Combine(Ma.GetHashCode(), hc);
+ }
+
+ public virtual bool Equals(IssuanceRequest? other)
+ {
+ if (other is null)
+ {
+ return false;
+ }
+
+ bool isEqual = Ma == other.Ma
+ && BitCommitments.SequenceEqual(other.BitCommitments);
+
+ return isEqual;
+ }
}
}

Implement equality rules.