Skip to content

Commit

Permalink
feat: define an IPFS Peer, 314
Browse files Browse the repository at this point in the history
  • Loading branch information
richardschneider committed Jan 3, 2018
1 parent 457e092 commit 5c69d8b
Show file tree
Hide file tree
Showing 2 changed files with 287 additions and 0 deletions.
160 changes: 160 additions & 0 deletions src/Peer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Ipfs
{
/// <summary>
/// A node on the IPFS network.
/// </summary>
public class Peer : IEquatable<Peer>
{
static MultiAddress[] noAddress = new MultiAddress[0];
const string unknown = "unknown/0.0";

/// <summary>
/// Universally unique identifier.
/// </summary>
/// <value>
/// This is the <see cref="MultiHash"/> of the peer's <see cref="PublicKey"/>.
/// </value>
public MultiHash Id { get; set; }

/// <summary>
/// The public key of the node.
/// </summary>
/// <value>
/// The base 64 encoding of the node's public key. The default is <b>null</b>
/// </value>
public string PublicKey { get; set; }

/// <summary>
/// The multiple addresses of the node.
/// </summary>
/// <value>
/// Where the peer can be found. The default is an empty sequence.
/// </value>
public IEnumerable<MultiAddress> Addresses { get; set; } = noAddress;

/// <summary>
/// The name and version of the IPFS software.
/// </summary>
/// <value>
/// For example "go-ipfs/0.4.17/".
/// </value>
/// <remarks>
/// There is no specification that describes the agent version string. The default
/// is "unknown/0.0".
/// </remarks>
public string AgentVersion { get; set; } = unknown;

/// <summary>
/// The name and version of the supported IPFS protocol.
/// </summary>
/// <value>
/// For example "ipfs/0.1.0".
/// </value>
/// <remarks>
/// There is no specification that describes the protocol version string. The default
/// is "unknown/0.0".
/// </remarks>
public string ProtocolVersion { get; set; } = unknown;

/// <summary>
/// Determines if the information on the peer is valid.
/// </summary>
/// <returns>
/// <b>true</b> if all validation rules pass; otherwise <b>false</b>.
/// </returns>
/// <remarks>
/// Verifies that
/// <list type="bullet">
/// <item><description>The <see cref="Id"/> is defined</description></item>
/// <item><description>The <see cref="Id"/> is a hash of the <see cref="PublicKey"/></description></item>
/// </list>
/// </remarks>
public bool IsValid()
{
if (Id == null)
return false;
if (PublicKey != null && !Id.Matches(Convert.FromBase64String(PublicKey)))
return false;

return true;
}

/// <inheritdoc />
public override int GetHashCode()
{
return ToString().GetHashCode();
}

/// <inheritdoc />
public override bool Equals(object obj)
{
var that = obj as Peer;
return (that == null)
? false
: this.ToString() == that.ToString();
}

/// <inheritdoc />
public bool Equals(Peer that)
{
return this.ToString() == that.ToString();
}

/// <summary>
/// Value equality.
/// </summary>
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);
}

/// <summary>
/// Value inequality.
/// </summary>
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);
}

/// <summary>
/// Returns the <see cref="Base58"/> encoding of the <see cref="Id"/>.
/// </summary>
/// <returns>
/// A Base58 representaton of the peer.
/// </returns>
public override string ToString()
{
return Id == null ? string.Empty : Id.ToBase58();
}

/// <summary>
/// Implicit casting of a <see cref="string"/> to a <see cref="Peer"/>.
/// </summary>
/// <param name="s">
/// A <see cref="Base58"/> encoded <see cref="Id"/>.
/// </param>
/// <returns>
/// A new <see cref="Peer"/>.
/// </returns>
/// <remarks>
/// Equivalent to <code>new Peer { Id = s }</code>
/// </remarks>
static public implicit operator Peer(string s)
{
return new Peer { Id = s };
}
}
}
127 changes: 127 additions & 0 deletions test/PeerTest.cs
Original file line number Diff line number Diff line change
@@ -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<Peer>(a0, a0);
Assert.AreEqual<Peer>(a0, a1);
Assert.AreNotEqual<Peer>(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));
}

}
}

1 comment on commit 5c69d8b

@richardschneider
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closes #24

Please sign in to comment.