Skip to content

Commit b19fd58

Browse files
committed
Imported base58 and started to use Multihash
1 parent 692224c commit b19fd58

File tree

5 files changed

+131
-10
lines changed

5 files changed

+131
-10
lines changed

src/org/ipfs/Base58.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.ipfs;
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+
}

src/org/ipfs/IPFS.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public List<MerkleNode> put(List<byte[]> data) throws IOException {
145145

146146
public MerkleNode get(MerkleNode merkleObject) throws IOException {
147147
Map json = (Map)retrieveAndParse("object/get?stream-channels=true&arg=" + merkleObject.hash);
148-
json.put("Hash", merkleObject.hash);
148+
json.put("Hash", merkleObject.hash.toBase58());
149149
return MerkleNode.fromJSON(json);
150150
}
151151

src/org/ipfs/MerkleNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
public class MerkleNode {
77
public final Optional<String> name;
8-
public final String hash;
8+
public final Multihash hash;
99
public final Optional<Integer> size;
1010
public final Optional<Integer> type;
1111
public final List<MerkleNode> links;
@@ -21,7 +21,7 @@ public MerkleNode(String hash, Optional<String> name) {
2121

2222
public MerkleNode(String hash, Optional<String> name, Optional<Integer> size, Optional<Integer> type, List<MerkleNode> links, Optional<byte[]> data) {
2323
this.name = name;
24-
this.hash = hash;
24+
this.hash = Multihash.fromBase58(hash);
2525
this.size = size;
2626
this.type = type;
2727
this.links = links;

src/org/ipfs/Multihash.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,19 @@ public static Type lookup(int t) {
3333
}
3434

3535
public final Type type;
36-
public final byte size;
3736
public final byte[] hash;
3837

39-
public Multihash(Type type, byte size, byte[] hash) {
40-
if ((size & 0xff) != hash.length)
41-
throw new IllegalStateException("Incorrect size: " + (size&0xff) + " != "+hash.length);
38+
public Multihash(Type type, byte[] hash) {
39+
if (hash.length > 127)
40+
throw new IllegalStateException("Unsupported hash size: "+hash.length);
4241
if (hash.length != type.length)
4342
throw new IllegalStateException("Incorrect hash length: " + hash.length + " != "+type.length);
4443
this.type = type;
45-
this.size = size;
4644
this.hash = hash;
4745
}
4846

4947
public Multihash(byte[] multihash) {
50-
this(Type.lookup(multihash[0] & 0xff), multihash[1], Arrays.copyOfRange(multihash, 2, multihash.length));
48+
this(Type.lookup(multihash[0] & 0xff), Arrays.copyOfRange(multihash, 2, multihash.length));
5149
}
5250

5351
public byte[] toBytes() {
@@ -58,13 +56,34 @@ public byte[] toBytes() {
5856
return res;
5957
}
6058

59+
@Override
60+
public String toString() {
61+
return toBase58();
62+
}
63+
64+
@Override
65+
public boolean equals(Object o) {
66+
if (!(o instanceof Multihash))
67+
return false;
68+
return type == ((Multihash) o).type && Arrays.equals(hash, ((Multihash) o).hash);
69+
}
70+
71+
@Override
72+
public int hashCode() {
73+
return Arrays.hashCode(hash) ^ type.hashCode();
74+
}
75+
6176
public String toHex() {
6277
StringBuilder res = new StringBuilder();
6378
for (byte b: toBytes())
6479
res.append(String.format("%x", b&0xff));
6580
return res.toString();
6681
}
6782

83+
public String toBase58() {
84+
return Base58.encode(toBytes());
85+
}
86+
6887
public static Multihash fromHex(String hex) {
6988
if (hex.length() % 2 != 0)
7089
throw new IllegalStateException("Uneven number of hex digits!");
@@ -73,4 +92,8 @@ public static Multihash fromHex(String hex) {
7392
bout.write(Integer.valueOf(hex.substring(i, i+2), 16));
7493
return new Multihash(bout.toByteArray());
7594
}
95+
96+
public static Multihash fromBase58(String base58) {
97+
return new Multihash(Base58.decode(base58));
98+
}
7699
}

test/org/ipfs/Test.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66
public class Test {
77

88
IPFS ipfs = new IPFS("127.0.0.1", 5001);
9+
@org.junit.Test
10+
public void base58Test() {
11+
String input = "QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB";
12+
byte[] output = Base58.decode(input);
13+
String encoded = Base58.encode(output);
14+
if (!encoded.equals(input))
15+
throw new IllegalStateException("Incorrect base58! "+ input + " => "+encoded);
16+
}
17+
918

1019
@org.junit.Test
1120
public void singleFileTest() {
@@ -139,7 +148,7 @@ public void dhtTest() {
139148
Map findprovs = ipfs.dht.findprovs(pointer);
140149
List<NodeAddress> peers = ipfs.swarm.peers();
141150
Map query = ipfs.dht.query(peers.get(0));
142-
Map find = ipfs.dht.findpeer(peers.get(0));
151+
// Map find = ipfs.dht.findpeer(peers.get(0));
143152
} catch (IOException e) {
144153
throw new RuntimeException(e);
145154
}

0 commit comments

Comments
 (0)