diff --git a/.gitignore b/.gitignore
index 32858aa..bdb6657 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,8 @@
*.class
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
-# Package Files #
-*.jar
-*.war
-*.ear
+build/**
+dist/**
+.idea/**
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
diff --git a/LICENSE b/LICENSE
index e502a90..0d363b0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2016 Multiformats
+Copyright (c) 2015 Ian Preston
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 20df057..2243a9d 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,8 @@
# java-multihash
A Java implementation of Multihash
+
+## Usage
+Multihash m = Multihash.fromBase58("QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy");
+
+## Compilation
+To compile just run ant.
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..217f0ac
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,74 @@
+
+
+ Java Multihash
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/hamcrest-core-1.3.jar b/lib/hamcrest-core-1.3.jar
new file mode 100644
index 0000000..9d5fe16
Binary files /dev/null and b/lib/hamcrest-core-1.3.jar differ
diff --git a/lib/junit-4.12.jar b/lib/junit-4.12.jar
new file mode 100644
index 0000000..3a7fc26
Binary files /dev/null and b/lib/junit-4.12.jar differ
diff --git a/src/main/java/org/ipfs/api/Base58.java b/src/main/java/org/ipfs/api/Base58.java
new file mode 100644
index 0000000..1f7ad39
--- /dev/null
+++ b/src/main/java/org/ipfs/api/Base58.java
@@ -0,0 +1,89 @@
+package org.ipfs.api;
+
+/**
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.math.BigInteger;
+
+/**
+ * A custom form of base58 is used to encode BitCoin addresses. Note that this is not the same base58 as used by
+ * Flickr, which you may see reference to around the internet.
+ *
+ * Satoshi says: why base-58 instead of standard base-64 encoding?
+ *
+ *
+ * - Don't want 0OIl characters that look the same in some fonts and
+ * could be used to create visually identical looking account numbers.
+ * - A string with non-alphanumeric characters is not as easily accepted as an account number.
+ * - E-mail usually won't line-break if there's no punctuation to break at.
+ * - Doubleclicking selects the whole number as one word if it's all alphanumeric.
+ *
+ */
+public class Base58 {
+ private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+ private static final BigInteger BASE = BigInteger.valueOf(58);
+
+ public static String encode(byte[] input) {
+ // TODO: This could be a lot more efficient.
+ BigInteger bi = new BigInteger(1, input);
+ StringBuffer s = new StringBuffer();
+ while (bi.compareTo(BASE) >= 0) {
+ BigInteger mod = bi.mod(BASE);
+ s.insert(0, ALPHABET.charAt(mod.intValue()));
+ bi = bi.subtract(mod).divide(BASE);
+ }
+ s.insert(0, ALPHABET.charAt(bi.intValue()));
+ // Convert leading zeros too.
+ for (byte anInput : input) {
+ if (anInput == 0)
+ s.insert(0, ALPHABET.charAt(0));
+ else
+ break;
+ }
+ return s.toString();
+ }
+
+ public static byte[] decode(String input) {
+ byte[] bytes = decodeToBigInteger(input).toByteArray();
+ // We may have got one more byte than we wanted, if the high bit of the next-to-last byte was not zero. This
+ // is because BigIntegers are represented with twos-compliment notation, thus if the high bit of the last
+ // byte happens to be 1 another 8 zero bits will be added to ensure the number parses as positive. Detect
+ // that case here and chop it off.
+ boolean stripSignByte = bytes.length > 1 && bytes[0] == 0 && bytes[1] < 0;
+ // Count the leading zeros, if any.
+ int leadingZeros = 0;
+ for (int i = 0; input.charAt(i) == ALPHABET.charAt(0); i++) {
+ leadingZeros++;
+ }
+ // Now cut/pad correctly. Java 6 has a convenience for this, but Android can't use it.
+ byte[] tmp = new byte[bytes.length - (stripSignByte ? 1 : 0) + leadingZeros];
+ System.arraycopy(bytes, stripSignByte ? 1 : 0, tmp, leadingZeros, tmp.length - leadingZeros);
+ return tmp;
+ }
+
+ public static BigInteger decodeToBigInteger(String input) {
+ BigInteger bi = BigInteger.valueOf(0);
+ // Work backwards through the string.
+ for (int i = input.length() - 1; i >= 0; i--) {
+ int alphaIndex = ALPHABET.indexOf(input.charAt(i));
+ if (alphaIndex == -1) {
+ throw new IllegalStateException("Illegal character " + input.charAt(i) + " at " + i);
+ }
+ bi = bi.add(BigInteger.valueOf(alphaIndex).multiply(BASE.pow(input.length() - 1 - i)));
+ }
+ return bi;
+ }
+}
diff --git a/src/main/java/org/ipfs/api/Multihash.java b/src/main/java/org/ipfs/api/Multihash.java
new file mode 100644
index 0000000..bab19a4
--- /dev/null
+++ b/src/main/java/org/ipfs/api/Multihash.java
@@ -0,0 +1,112 @@
+package org.ipfs.api;
+
+import java.io.*;
+import java.util.*;
+
+public class Multihash {
+ public enum Type {
+ sha1(0x11, 20),
+ sha2_256(0x12, 32),
+ sha2_512(0x13, 64),
+ sha3(0x14, 64),
+ blake2b(0x40, 64),
+ blake2s(0x41, 32);
+
+ public int index, length;
+
+ Type(int index, int length) {
+ this.index = index;
+ this.length = length;
+ }
+
+ private static Map lookup = new TreeMap<>();
+ static {
+ for (Type t: Type.values())
+ lookup.put(t.index, t);
+ }
+
+ public static Type lookup(int t) {
+ if (!lookup.containsKey(t))
+ throw new IllegalStateException("Unknown Multihash type: "+t);
+ return lookup.get(t);
+ }
+ }
+
+ public final Type type;
+ private final byte[] hash;
+
+ public Multihash(Type type, byte[] hash) {
+ if (hash.length > 127)
+ throw new IllegalStateException("Unsupported hash size: "+hash.length);
+ if (hash.length != type.length)
+ throw new IllegalStateException("Incorrect hash length: " + hash.length + " != "+type.length);
+ this.type = type;
+ this.hash = hash;
+ }
+
+ public Multihash(byte[] multihash) {
+ this(Type.lookup(multihash[0] & 0xff), Arrays.copyOfRange(multihash, 2, multihash.length));
+ }
+
+ public byte[] toBytes() {
+ byte[] res = new byte[hash.length+2];
+ res[0] = (byte)type.index;
+ res[1] = (byte)hash.length;
+ System.arraycopy(hash, 0, res, 2, hash.length);
+ return res;
+ }
+
+ public void serialize(DataOutput dout) throws IOException {
+ dout.write(toBytes());
+ }
+
+ public static Multihash deserialize(DataInput din) throws IOException {
+ int type = din.readUnsignedByte();
+ int len = din.readUnsignedByte();
+ Type t = Type.lookup(type);
+ byte[] hash = new byte[len];
+ din.readFully(hash);
+ return new Multihash(t, hash);
+ }
+
+ @Override
+ public String toString() {
+ return toBase58();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Multihash))
+ return false;
+ return type == ((Multihash) o).type && Arrays.equals(hash, ((Multihash) o).hash);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(hash) ^ type.hashCode();
+ }
+
+ public String toHex() {
+ StringBuilder res = new StringBuilder();
+ for (byte b: toBytes())
+ res.append(String.format("%x", b&0xff));
+ return res.toString();
+ }
+
+ public String toBase58() {
+ return Base58.encode(toBytes());
+ }
+
+ public static Multihash fromHex(String hex) {
+ if (hex.length() % 2 != 0)
+ throw new IllegalStateException("Uneven number of hex digits!");
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ for (int i=0; i < hex.length()-1; i+= 2)
+ bout.write(Integer.valueOf(hex.substring(i, i+2), 16));
+ return new Multihash(bout.toByteArray());
+ }
+
+ public static Multihash fromBase58(String base58) {
+ return new Multihash(Base58.decode(base58));
+ }
+}
diff --git a/src/test/java/org/ipfs/api/MultihashTests.java b/src/test/java/org/ipfs/api/MultihashTests.java
new file mode 100755
index 0000000..0e9ea5c
--- /dev/null
+++ b/src/test/java/org/ipfs/api/MultihashTests.java
@@ -0,0 +1,20 @@
+package org.ipfs.api;
+
+import org.junit.*;
+
+import java.util.*;
+
+public class MultihashTests {
+
+ @Test
+ public void base58Test() {
+ List examples = Arrays.asList("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB",
+ "QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy");
+ for (String example: examples) {
+ byte[] output = Base58.decode(example);
+ String encoded = Base58.encode(output);
+ if (!encoded.equals(encoded))
+ throw new IllegalStateException("Incorrect base58! " + example + " => " + encoded);
+ }
+ }
+}