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));
+ }
+
+ }
+}
+