Implement GroupElement encapsulation.
ADD
WalletWasabi.Tests/UnitTests/Crypto/GroupElementTests.cs
+using NBitcoin.Secp256k1;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WalletWasabi.Crypto;
+using Xunit;
+
+namespace WalletWasabi.Tests.UnitTests.Crypto
+{
+ public class GroupElementTests
+ {
+ [Fact]
+ public void IsIEquitable()
+ {
+ Assert.True(GroupElement.Infinity is IEquatable<GroupElement>);
+ }
+
+ [Fact]
+ public void ConstructorThrows()
+ {
+ var one = new FE(1);
+ var two = new FE(2);
+ var large = new FE(uint.MaxValue);
+ var largest = new FE(uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue, uint.MaxValue);
+
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(FE.Zero, FE.Zero)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(one, FE.Zero)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(one, one)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(one, two)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(two, two)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(one, large)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(large, large)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(one, largest)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GE(largest, largest)));
+
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(FE.Zero, FE.Zero, FE.Zero)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(one, FE.Zero, FE.Zero)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(one, one, one)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(one, one, two)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(two, two, two)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(one, one, large)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(large, large, large)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(one, one, largest)));
+ Assert.Throws<ArgumentOutOfRangeException>(() => new GroupElement(new GEJ(largest, largest, largest)));
+ }
+
+ [Fact]
+ public void ConstructorDoesntThrow()
+ {
+ new GroupElement(GE.Infinity);
+ new GroupElement(GEJ.Infinity);
+ new GroupElement(EC.G);
+ new GroupElement(EC.G * new Scalar(1));
+ new GroupElement(EC.G * new Scalar(uint.MaxValue));
+ new GroupElement(EC.G * new Scalar(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue));
+ }
+
+ [Fact]
+ public void InfinityWorks()
+ {
+ var a = GroupElement.Infinity;
+ var b = new GroupElement(GE.Infinity);
+ var c = new GroupElement(new GE(FE.Zero, FE.Zero, infinity: true));
+ var d = new GroupElement(new GE(new FE(1), new FE(1), infinity: true));
+ var e = new GroupElement(GEJ.Infinity);
+ var f = new GroupElement(new GEJ(FE.Zero, FE.Zero, FE.Zero, infinity: true));
+ var g = new GroupElement(new GEJ(new FE(1), new FE(1), new FE(1), infinity: true));
+
+ Assert.True(a.IsInfinity);
+ Assert.True(b.IsInfinity);
+ Assert.True(c.IsInfinity);
+ Assert.True(d.IsInfinity);
+ Assert.True(e.IsInfinity);
+ Assert.True(f.IsInfinity);
+ Assert.True(g.IsInfinity);
+
+ Assert.Equal(a, b);
+ Assert.Equal(a, c);
+ Assert.Equal(a, d);
+ Assert.Equal(a, e);
+ Assert.Equal(a, f);
+ Assert.Equal(a, g);
+
+ Assert.Equal(a.GetHashCode(), b.GetHashCode());
+ Assert.Equal(a.GetHashCode(), c.GetHashCode());
+ Assert.Equal(a.GetHashCode(), d.GetHashCode());
+ Assert.Equal(a.GetHashCode(), e.GetHashCode());
+ Assert.Equal(a.GetHashCode(), f.GetHashCode());
+ Assert.Equal(a.GetHashCode(), g.GetHashCode());
+
+ var singleSet = new HashSet<GroupElement> { a, b, c, d, e, f, g };
+ Assert.Single(singleSet);
+ }
+ }
+}

ADD
WalletWasabi/Crypto/GroupElement.cs
+using NBitcoin.Secp256k1;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using WalletWasabi.Helpers;
+
+namespace WalletWasabi.Crypto
+{
+ public class GroupElement : IEquatable<GroupElement>
+ {
+ public GroupElement(GE groupElement)
+ {
+ if (groupElement.IsInfinity)
+ {
+ Ge = GE.Infinity;
+ }
+ else
+ {
+ Guard.True($"{nameof(groupElement)}.{nameof(groupElement.IsValidVariable)}", groupElement.IsValidVariable);
+ Ge = groupElement;
+ }
+ }
+
+ public GroupElement(GEJ groupElement)
+ : this(groupElement.ToGroupElement())
+ {
+ }
+
+ public static GroupElement Infinity { get; } = new GroupElement(GE.Infinity);
+
+ private GE Ge { get; }
+
+ public bool IsInfinity => Ge.IsInfinity;
+
+ public override bool Equals(object obj) => Equals(obj as GroupElement);
+
+ public bool Equals(GroupElement other) => this == other;
+
+ public override int GetHashCode() => Ge.GetHashCode();
+
+ public static bool operator ==(GroupElement x, GroupElement y)
+ {
+ if (x.IsInfinity && y.IsInfinity)
+ {
+ return true;
+ }
+ else
+ {
+ return x.IsInfinity == y.IsInfinity && x.Ge.x == y.Ge.x && x.Ge.y == y.Ge.y;
+ }
+ }
+
+ public static bool operator !=(GroupElement x, GroupElement y) => !(x == y);
+ }
+}

Implement GroupElement encapsulation.