From 5c69d8b645b3c127a96c07ec26f8325910f1ed76 Mon Sep 17 00:00:00 2001 From: Richard Schneider Date: Wed, 3 Jan 2018 21:52:02 +1300 Subject: [PATCH] feat: define an IPFS Peer, 314 --- src/Peer.cs | 160 +++++++++++++++++++++++++++++++++++++++++++++++ test/PeerTest.cs | 127 +++++++++++++++++++++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 src/Peer.cs create mode 100644 test/PeerTest.cs diff --git a/src/Peer.cs b/src/Peer.cs new file mode 100644 index 0000000..391e375 --- /dev/null +++ b/src/Peer.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Ipfs +{ + /// + /// A node on the IPFS network. + /// + public class Peer : IEquatable + { + static MultiAddress[] noAddress = new MultiAddress[0]; + const string unknown = "unknown/0.0"; + + /// + /// Universally unique identifier. + /// + /// + /// This is the of the peer's . + /// + public MultiHash Id { get; set; } + + /// + /// The public key of the node. + /// + /// + /// The base 64 encoding of the node's public key. The default is null + /// + public string PublicKey { get; set; } + + /// + /// The multiple addresses of the node. + /// + /// + /// Where the peer can be found. The default is an empty sequence. + /// + public IEnumerable Addresses { get; set; } = noAddress; + + /// + /// The name and version of the IPFS software. + /// + /// + /// For example "go-ipfs/0.4.17/". + /// + /// + /// There is no specification that describes the agent version string. The default + /// is "unknown/0.0". + /// + public string AgentVersion { get; set; } = unknown; + + /// + /// The name and version of the supported IPFS protocol. + /// + /// + /// For example "ipfs/0.1.0". + /// + /// + /// There is no specification that describes the protocol version string. The default + /// is "unknown/0.0". + /// + public string ProtocolVersion { get; set; } = unknown; + + /// + /// Determines if the information on the peer is valid. + /// + /// + /// true if all validation rules pass; otherwise false. + /// + /// + /// Verifies that + /// + /// The is defined + /// The is a hash of the + /// + /// + public bool IsValid() + { + if (Id == null) + return false; + if (PublicKey != null && !Id.Matches(Convert.FromBase64String(PublicKey))) + return false; + + return true; + } + + /// + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + /// + public override bool Equals(object obj) + { + var that = obj as Peer; + return (that == null) + ? false + : this.ToString() == that.ToString(); + } + + /// + public bool Equals(Peer that) + { + return this.ToString() == that.ToString(); + } + + /// + /// Value equality. + /// + public static bool operator ==(Peer a, Peer b) + { + if (object.ReferenceEquals(a, b)) return true; + if (object.ReferenceEquals(a, null)) return false; + if (object.ReferenceEquals(b, null)) return false; + + return a.Equals(b); + } + + /// + /// Value inequality. + /// + public static bool operator !=(Peer a, Peer b) + { + if (object.ReferenceEquals(a, b)) return false; + if (object.ReferenceEquals(a, null)) return true; + if (object.ReferenceEquals(b, null)) return true; + + return !a.Equals(b); + } + + /// + /// Returns the encoding of the . + /// + /// + /// A Base58 representaton of the peer. + /// + public override string ToString() + { + return Id == null ? string.Empty : Id.ToBase58(); + } + + /// + /// Implicit casting of a to a . + /// + /// + /// A encoded . + /// + /// + /// A new . + /// + /// + /// Equivalent to new Peer { Id = s } + /// + static public implicit operator Peer(string s) + { + return new Peer { Id = s }; + } + } +} diff --git a/test/PeerTest.cs b/test/PeerTest.cs new file mode 100644 index 0000000..4144e7c --- /dev/null +++ b/test/PeerTest.cs @@ -0,0 +1,127 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.IO; + +namespace Ipfs +{ + + [TestClass] + public class PeerTest + { + const string marsId = "QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + const string plutoId = "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM"; + const string marsPublicKey = "CAASogEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKGUtbRQf+a9SBHFEruNAUatS/tsGUnHuCtifGrlbYPELD3UyyhWf/FYczBCavx3i8hIPEW2jQv4ehxQxi/cg9SHswZCQblSi0ucwTBFr8d40JEiyB9CcapiMdFQxdMgGvXEOQdLz1pz+UPUDojkdKZq8qkkeiBn7KlAoGEocnmpAgMBAAE="; + + [TestMethod] + public new void ToString() + { + Assert.AreEqual("", new Peer().ToString()); + Assert.AreEqual(marsId, new Peer { Id = marsId }.ToString()); + } + + [TestMethod] + public void DefaultValues() + { + var peer = new Peer(); + Assert.AreEqual(null, peer.Id); + Assert.AreEqual(0, peer.Addresses.Count()); + Assert.AreEqual("unknown/0.0", peer.ProtocolVersion); + Assert.AreEqual("unknown/0.0", peer.AgentVersion); + Assert.AreEqual(null, peer.PublicKey); + Assert.AreEqual(false, peer.IsValid()); // missing peer ID + } + + [TestMethod] + public void Validation_No_Id() + { + var peer = new Peer(); + Assert.AreEqual(false, peer.IsValid()); + } + + [TestMethod] + public void Validation_With_Id() + { + Peer peer = marsId; + Assert.AreEqual(true, peer.IsValid()); + } + + [TestMethod] + public void Validation_With_Id_Pubkey() + { + var peer = new Peer + { + Id = marsId, + PublicKey = marsPublicKey + }; + Assert.AreEqual(true, peer.IsValid()); + } + + [TestMethod] + public void Validation_With_Id_Invalid_Pubkey() + { + var peer = new Peer + { + Id = plutoId, + PublicKey = marsPublicKey + }; + Assert.AreEqual(false, peer.IsValid()); + } + + [TestMethod] + public void Value_Equality() + { + var a0 = new Peer { Id = marsId }; + var a1 = new Peer { Id = marsId }; + var b = new Peer { Id = plutoId }; + Peer c = null; + Peer d = null; + + Assert.IsTrue(c == d); + Assert.IsFalse(c == b); + Assert.IsFalse(b == c); + + Assert.IsFalse(c != d); + Assert.IsTrue(c != b); + Assert.IsTrue(b != c); + +#pragma warning disable 1718 + Assert.IsTrue(a0 == a0); + Assert.IsTrue(a0 == a1); + Assert.IsFalse(a0 == b); + +#pragma warning disable 1718 + Assert.IsFalse(a0 != a0); + Assert.IsFalse(a0 != a1); + Assert.IsTrue(a0 != b); + + Assert.IsTrue(a0.Equals(a0)); + Assert.IsTrue(a0.Equals(a1)); + Assert.IsFalse(a0.Equals(b)); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); + Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); + Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); + } + + + [TestMethod] + public void Implicit_Conversion_From_String() + { + Peer a = marsId; + Assert.IsInstanceOfType(a, typeof(Peer)); + } + + } +} +