Skip to content

Commit bd2b816

Browse files
committed
Initial extraction
1 parent 949b802 commit bd2b816

9 files changed

Lines changed: 305 additions & 8 deletions

File tree

.gitignore

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
*.class
22

3-
# Mobile Tools for Java (J2ME)
4-
.mtj.tmp/
5-
6-
# Package Files #
7-
*.jar
8-
*.war
9-
*.ear
3+
build/**
4+
dist/**
5+
.idea/**
106

117
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
128
hs_err_pid*

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2016 Multiformats
3+
Copyright (c) 2015 Ian Preston
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
# java-multihash
22
A Java implementation of Multihash
3+
4+
## Usage
5+
Multihash m = Multihash.fromBase58("QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy");
6+
7+
## Compilation
8+
To compile just run ant.

build.xml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<project name="java-multihash" default="dist" basedir=".">
2+
<description>
3+
Java Multihash
4+
</description>
5+
6+
<property name="src" location="src"/>
7+
<property name="build" location="build"/>
8+
<property name="dist" location="dist"/>
9+
10+
<path id="dep.runtime">
11+
<fileset dir="./lib">
12+
<include name="**/*.jar" />
13+
</fileset>
14+
</path>
15+
16+
<target name="init">
17+
<mkdir dir="${build}"/>
18+
</target>
19+
20+
<target name="compile" depends="init" description="compile the source">
21+
<javac includeantruntime="false" srcdir="${src}" destdir="${build}" debug="true" debuglevel="lines,vars,source">
22+
<classpath>
23+
<fileset dir="lib">
24+
<include name="**/*.jar" />
25+
</fileset>
26+
</classpath>
27+
</javac>
28+
</target>
29+
30+
<target name="dist" depends="compile" description="generate the distribution">
31+
<mkdir dir="${dist}/lib"/>
32+
<copy todir="${dist}/lib">
33+
<fileset dir="lib"/>
34+
</copy>
35+
<manifestclasspath property="manifest_cp" jarfile="myjar.jar">
36+
<classpath refid="dep.runtime" />
37+
</manifestclasspath>
38+
<jar jarfile="${dist}/Multihash.jar" basedir="${build}">
39+
<manifest>
40+
<attribute name="Class-Path" value="${manifest_cp}"/>
41+
</manifest>
42+
<fileset dir=".">
43+
<include name="**/ui/**"/>
44+
<exclude name="out/**"/>
45+
<exclude name="ui/doppio/**"/>
46+
<exclude name="ui/PeergosServer.jar"/>
47+
</fileset>
48+
</jar>
49+
<copy todir=".">
50+
<fileset file="${dist}/Multihash.jar"/>
51+
</copy>
52+
</target>
53+
54+
55+
<target name="test" depends="compile,dist">
56+
<junit printsummary="yes" fork="true" haltonfailure="yes">
57+
<jvmarg value="-Xmx1g"/>
58+
<classpath>
59+
<pathelement location="lib/junit-4.11.jar" />
60+
<pathelement location="lib/hamcrest-core-1.3.jar" />
61+
<pathelement location="Multihash.jar" />
62+
</classpath>
63+
<test name="org.ipfs.api.MultihashTests" haltonfailure="yes">
64+
<formatter type="plain"/>
65+
<formatter type="xml"/>
66+
</test>
67+
</junit>
68+
</target>
69+
70+
<target name="clean" description="clean up">
71+
<delete dir="${build}"/>
72+
<delete dir="${dist}"/>
73+
</target>
74+
</project>

lib/hamcrest-core-1.3.jar

44 KB
Binary file not shown.

lib/junit-4.12.jar

308 KB
Binary file not shown.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.ipfs.api;
2+
3+
/**
4+
* Copyright 2011 Google Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import java.math.BigInteger;
20+
21+
/**
22+
* A custom form of base58 is used to encode BitCoin addresses. Note that this is not the same base58 as used by
23+
* Flickr, which you may see reference to around the internet.<p>
24+
*
25+
* Satoshi says: why base-58 instead of standard base-64 encoding?<p>
26+
*
27+
* <ul>
28+
* <li>Don't want 0OIl characters that look the same in some fonts and
29+
* could be used to create visually identical looking account numbers.</li>
30+
* <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li>
31+
* <li>E-mail usually won't line-break if there's no punctuation to break at.</li>
32+
* <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li>
33+
* </ul>
34+
*/
35+
public class Base58 {
36+
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
37+
private static final BigInteger BASE = BigInteger.valueOf(58);
38+
39+
public static String encode(byte[] input) {
40+
// TODO: This could be a lot more efficient.
41+
BigInteger bi = new BigInteger(1, input);
42+
StringBuffer s = new StringBuffer();
43+
while (bi.compareTo(BASE) >= 0) {
44+
BigInteger mod = bi.mod(BASE);
45+
s.insert(0, ALPHABET.charAt(mod.intValue()));
46+
bi = bi.subtract(mod).divide(BASE);
47+
}
48+
s.insert(0, ALPHABET.charAt(bi.intValue()));
49+
// Convert leading zeros too.
50+
for (byte anInput : input) {
51+
if (anInput == 0)
52+
s.insert(0, ALPHABET.charAt(0));
53+
else
54+
break;
55+
}
56+
return s.toString();
57+
}
58+
59+
public static byte[] decode(String input) {
60+
byte[] bytes = decodeToBigInteger(input).toByteArray();
61+
// We may have got one more byte than we wanted, if the high bit of the next-to-last byte was not zero. This
62+
// is because BigIntegers are represented with twos-compliment notation, thus if the high bit of the last
63+
// byte happens to be 1 another 8 zero bits will be added to ensure the number parses as positive. Detect
64+
// that case here and chop it off.
65+
boolean stripSignByte = bytes.length > 1 && bytes[0] == 0 && bytes[1] < 0;
66+
// Count the leading zeros, if any.
67+
int leadingZeros = 0;
68+
for (int i = 0; input.charAt(i) == ALPHABET.charAt(0); i++) {
69+
leadingZeros++;
70+
}
71+
// Now cut/pad correctly. Java 6 has a convenience for this, but Android can't use it.
72+
byte[] tmp = new byte[bytes.length - (stripSignByte ? 1 : 0) + leadingZeros];
73+
System.arraycopy(bytes, stripSignByte ? 1 : 0, tmp, leadingZeros, tmp.length - leadingZeros);
74+
return tmp;
75+
}
76+
77+
public static BigInteger decodeToBigInteger(String input) {
78+
BigInteger bi = BigInteger.valueOf(0);
79+
// Work backwards through the string.
80+
for (int i = input.length() - 1; i >= 0; i--) {
81+
int alphaIndex = ALPHABET.indexOf(input.charAt(i));
82+
if (alphaIndex == -1) {
83+
throw new IllegalStateException("Illegal character " + input.charAt(i) + " at " + i);
84+
}
85+
bi = bi.add(BigInteger.valueOf(alphaIndex).multiply(BASE.pow(input.length() - 1 - i)));
86+
}
87+
return bi;
88+
}
89+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.ipfs.api;
2+
3+
import java.io.*;
4+
import java.util.*;
5+
6+
public class Multihash {
7+
public enum Type {
8+
sha1(0x11, 20),
9+
sha2_256(0x12, 32),
10+
sha2_512(0x13, 64),
11+
sha3(0x14, 64),
12+
blake2b(0x40, 64),
13+
blake2s(0x41, 32);
14+
15+
public int index, length;
16+
17+
Type(int index, int length) {
18+
this.index = index;
19+
this.length = length;
20+
}
21+
22+
private static Map<Integer, Type> lookup = new TreeMap<>();
23+
static {
24+
for (Type t: Type.values())
25+
lookup.put(t.index, t);
26+
}
27+
28+
public static Type lookup(int t) {
29+
if (!lookup.containsKey(t))
30+
throw new IllegalStateException("Unknown Multihash type: "+t);
31+
return lookup.get(t);
32+
}
33+
}
34+
35+
public final Type type;
36+
private final byte[] hash;
37+
38+
public Multihash(Type type, byte[] hash) {
39+
if (hash.length > 127)
40+
throw new IllegalStateException("Unsupported hash size: "+hash.length);
41+
if (hash.length != type.length)
42+
throw new IllegalStateException("Incorrect hash length: " + hash.length + " != "+type.length);
43+
this.type = type;
44+
this.hash = hash;
45+
}
46+
47+
public Multihash(byte[] multihash) {
48+
this(Type.lookup(multihash[0] & 0xff), Arrays.copyOfRange(multihash, 2, multihash.length));
49+
}
50+
51+
public byte[] toBytes() {
52+
byte[] res = new byte[hash.length+2];
53+
res[0] = (byte)type.index;
54+
res[1] = (byte)hash.length;
55+
System.arraycopy(hash, 0, res, 2, hash.length);
56+
return res;
57+
}
58+
59+
public void serialize(DataOutput dout) throws IOException {
60+
dout.write(toBytes());
61+
}
62+
63+
public static Multihash deserialize(DataInput din) throws IOException {
64+
int type = din.readUnsignedByte();
65+
int len = din.readUnsignedByte();
66+
Type t = Type.lookup(type);
67+
byte[] hash = new byte[len];
68+
din.readFully(hash);
69+
return new Multihash(t, hash);
70+
}
71+
72+
@Override
73+
public String toString() {
74+
return toBase58();
75+
}
76+
77+
@Override
78+
public boolean equals(Object o) {
79+
if (!(o instanceof Multihash))
80+
return false;
81+
return type == ((Multihash) o).type && Arrays.equals(hash, ((Multihash) o).hash);
82+
}
83+
84+
@Override
85+
public int hashCode() {
86+
return Arrays.hashCode(hash) ^ type.hashCode();
87+
}
88+
89+
public String toHex() {
90+
StringBuilder res = new StringBuilder();
91+
for (byte b: toBytes())
92+
res.append(String.format("%x", b&0xff));
93+
return res.toString();
94+
}
95+
96+
public String toBase58() {
97+
return Base58.encode(toBytes());
98+
}
99+
100+
public static Multihash fromHex(String hex) {
101+
if (hex.length() % 2 != 0)
102+
throw new IllegalStateException("Uneven number of hex digits!");
103+
ByteArrayOutputStream bout = new ByteArrayOutputStream();
104+
for (int i=0; i < hex.length()-1; i+= 2)
105+
bout.write(Integer.valueOf(hex.substring(i, i+2), 16));
106+
return new Multihash(bout.toByteArray());
107+
}
108+
109+
public static Multihash fromBase58(String base58) {
110+
return new Multihash(Base58.decode(base58));
111+
}
112+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.ipfs.api;
2+
3+
import org.junit.*;
4+
5+
import java.util.*;
6+
7+
public class MultihashTests {
8+
9+
@Test
10+
public void base58Test() {
11+
List<String> examples = Arrays.asList("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB",
12+
"QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy");
13+
for (String example: examples) {
14+
byte[] output = Base58.decode(example);
15+
String encoded = Base58.encode(output);
16+
if (!encoded.equals(encoded))
17+
throw new IllegalStateException("Incorrect base58! " + example + " => " + encoded);
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)