Skip to content

Commit d017fa8

Browse files
committed
Implemented MultiAddress fully.
1 parent c475ba9 commit d017fa8

File tree

5 files changed

+286
-23
lines changed

5 files changed

+286
-23
lines changed

src/org/ipfs/IPFS.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,12 @@ public Map findprovs(MerkleNode node) throws IOException {
196196
return retrieveMap("dht/findprovs?arg=" + node.hash);
197197
}
198198

199-
public Map query(NodeAddress addr) throws IOException {
200-
return retrieveMap("dht/query?arg=" + addr.address);
199+
public Map query(MultiAddress addr) throws IOException {
200+
return retrieveMap("dht/query?arg=" + addr.toString());
201201
}
202202

203-
public Map findpeer(NodeAddress addr) throws IOException {
204-
return retrieveMap("dht/findpeer?arg=" + addr.address);
203+
public Map findpeer(MultiAddress addr) throws IOException {
204+
return retrieveMap("dht/findpeer?arg=" + addr.toString());
205205
}
206206

207207
public Map get(MerkleNode node) throws IOException {
@@ -230,9 +230,9 @@ public Map bootstrap() throws IOException {
230230
ipfs peers in the internet.
231231
*/
232232
class Swarm {
233-
public List<NodeAddress> peers() throws IOException {
233+
public List<MultiAddress> peers() throws IOException {
234234
Map m = retrieveMap("swarm/peers?stream-channels=true");
235-
return ((List<Object>)m.get("Strings")).stream().map(x -> new NodeAddress((String)x)).collect(Collectors.toList());
235+
return ((List<Object>)m.get("Strings")).stream().map(x -> new MultiAddress((String)x)).collect(Collectors.toList());
236236
}
237237

238238
public Map addrs() throws IOException {

src/org/ipfs/MultiAddress.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.ipfs;
2+
3+
import java.io.*;
4+
import java.util.*;
5+
6+
public class MultiAddress
7+
{
8+
private final byte[] raw;
9+
10+
public MultiAddress(String address) {
11+
this(decodeFromString(address));
12+
}
13+
14+
public MultiAddress(byte[] raw) {
15+
encodeToString(raw); // check validity
16+
this.raw = raw;
17+
}
18+
19+
public byte[] getBytes() {
20+
return Arrays.copyOfRange(raw, 0, raw.length);
21+
}
22+
23+
private static byte[] decodeFromString(String addr) {
24+
while (addr.endsWith("/"))
25+
addr = addr.substring(0, addr.length()-1);
26+
String[] parts = addr.split("/");
27+
if (parts[0].length() != 0)
28+
throw new IllegalStateException("MultiAddress must start with a /");
29+
30+
ByteArrayOutputStream bout = new ByteArrayOutputStream();
31+
try {
32+
for (int i = 1; i < parts.length;) {
33+
String part = parts[i++];
34+
Protocol p = Protocol.get(part);
35+
p.appendCode(bout);
36+
if (p.size() == 0)
37+
continue;
38+
39+
String component = parts[i++];
40+
if (component.length() == 0)
41+
throw new IllegalStateException("Protocol requires address, but non provided!");
42+
43+
bout.write(p.addressToBytes(component));
44+
}
45+
return bout.toByteArray();
46+
} catch (IOException e) {
47+
throw new IllegalStateException("Error decoding multiaddress: "+addr);
48+
}
49+
}
50+
51+
private static String encodeToString(byte[] raw) {
52+
StringBuilder b = new StringBuilder();
53+
InputStream in = new ByteArrayInputStream(raw);
54+
try {
55+
while (true) {
56+
int code = (int)Protocol.readVarint(in);
57+
Protocol p = Protocol.get(code);
58+
b.append("/" + p.name());
59+
if (p.size() == 0)
60+
continue;
61+
62+
String addr = p.readAddress(in);
63+
if (addr.length() > 0)
64+
b.append("/" +addr);
65+
}
66+
}
67+
catch (EOFException eof) {}
68+
catch (IOException e) {
69+
throw new RuntimeException(e);
70+
}
71+
72+
return b.toString();
73+
}
74+
75+
@Override
76+
public String toString() {
77+
return encodeToString(raw);
78+
}
79+
80+
@Override
81+
public boolean equals(Object other) {
82+
if (!(other instanceof MultiAddress))
83+
return false;
84+
return Arrays.equals(raw, ((MultiAddress) other).raw);
85+
}
86+
87+
@Override
88+
public int hashCode() {
89+
return Arrays.hashCode(raw);
90+
}
91+
}

src/org/ipfs/NodeAddress.java

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/org/ipfs/Protocol.java

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package org.ipfs;
2+
3+
import java.io.*;
4+
import java.net.*;
5+
import java.util.*;
6+
7+
public class Protocol {
8+
public static int LENGTH_PREFIXED_VAR_SIZE = -1;
9+
10+
enum Type {
11+
IP4(4, 32, "ip4"),
12+
TCP(6, 16, "tcp"),
13+
UDP(17, 16, "udp"),
14+
DCCP(33, 16, "dccp"),
15+
IP6(41, 128, "ip6"),
16+
SCTP(132, 16, "sctp"),
17+
UTP(301, 0, "utp"),
18+
UDT(302, 0, "udt"),
19+
IPFS(421, LENGTH_PREFIXED_VAR_SIZE, "ipfs"),
20+
HTTPS(443, 0, "https"),
21+
HTTP(480, 0, "http");
22+
23+
public final int code, size;
24+
public final String name;
25+
private final byte[] encoded;
26+
27+
Type(int code, int size, String name) {
28+
this.code = code;
29+
this.size = size;
30+
this.name = name;
31+
this.encoded = encode(code);
32+
}
33+
34+
static byte[] encode(int code) {
35+
byte[] varint = new byte[(32 - Integer.numberOfLeadingZeros(code)+6)/7];
36+
putUvarint(varint, code);
37+
return varint;
38+
}
39+
}
40+
41+
public final Type type;
42+
43+
public Protocol(Type type) {
44+
this.type = type;
45+
}
46+
47+
public void appendCode(OutputStream out) throws IOException {
48+
out.write(type.encoded);
49+
}
50+
51+
public int size() {
52+
return type.size;
53+
}
54+
55+
public String name() {
56+
return type.name;
57+
}
58+
59+
public int code() {
60+
return type.code;
61+
}
62+
63+
@Override
64+
public String toString() {
65+
return name();
66+
}
67+
68+
public byte[] addressToBytes(String addr) {
69+
try {
70+
switch (type) {
71+
case IP4:
72+
return Inet4Address.getByName(addr).getAddress();
73+
case IP6:
74+
return Inet6Address.getByName(addr).getAddress();
75+
case TCP:
76+
case UDP:
77+
case DCCP:
78+
case SCTP:
79+
int x = Integer.parseInt(addr);
80+
if (x > 65536)
81+
throw new IllegalStateException("Failed to parse "+type.name+" address "+addr + " (> 65536");
82+
return new byte[]{(byte)(x >>8), (byte)x};
83+
case IPFS:
84+
Multihash hash = Multihash.fromBase58(addr);
85+
ByteArrayOutputStream bout = new ByteArrayOutputStream();
86+
byte[] hashBytes = hash.toBytes();
87+
byte[] varint = new byte[(32 - Integer.numberOfLeadingZeros(hashBytes.length)+6)/7];
88+
putUvarint(varint, hashBytes.length);
89+
bout.write(varint);
90+
bout.write(hashBytes);
91+
return bout.toByteArray();
92+
}
93+
} catch (IOException e) {
94+
throw new RuntimeException(e);
95+
}
96+
throw new IllegalStateException("Failed to parse address: "+addr);
97+
}
98+
99+
public String readAddress(InputStream in) throws IOException {
100+
int sizeForAddress = sizeForAddress(in);
101+
byte[] buf;
102+
switch (type) {
103+
case IP4:
104+
buf = new byte[sizeForAddress];
105+
in.read(buf);
106+
return Inet4Address.getByAddress(buf).toString().substring(1);
107+
case IP6:
108+
buf = new byte[sizeForAddress];
109+
in.read(buf);
110+
return Inet6Address.getByAddress(buf).toString().substring(1);
111+
case TCP:
112+
case UDP:
113+
case DCCP:
114+
case SCTP:
115+
return Integer.toString((in.read() << 8) | (in.read()));
116+
case IPFS:
117+
buf = new byte[sizeForAddress];
118+
in.read(buf);
119+
return new Multihash(buf).toBase58();
120+
}
121+
throw new IllegalStateException("Unimplemented protocl type: "+type.name);
122+
}
123+
124+
public int sizeForAddress(InputStream in) throws IOException {
125+
if (type.size > 0)
126+
return type.size/8;
127+
if (type.size == 0)
128+
return 0;
129+
return (int)readVarint(in);
130+
}
131+
132+
static int putUvarint(byte[] buf, long x) {
133+
int i = 0;
134+
while (x >= 0x80) {
135+
buf[i] = (byte)(x | 0x80);
136+
x >>= 7;
137+
i++;
138+
}
139+
buf[i] = (byte)x;
140+
return i + 1;
141+
}
142+
143+
static long readVarint(InputStream in) throws IOException {
144+
long x = 0;
145+
int s=0;
146+
for (int i=0; i < 10; i++) {
147+
int b = in.read();
148+
if (b == -1)
149+
throw new EOFException();
150+
if (b < 0x80) {
151+
if (i > 9 || i == 9 && b > 1) {
152+
throw new IllegalStateException("Overflow reading varint" +(-(i + 1)));
153+
}
154+
return x | (((long)b) << s);
155+
}
156+
x |= ((long)b & 0x7f) << s;
157+
s += 7;
158+
}
159+
throw new IllegalStateException("Varint too long!");
160+
}
161+
162+
private static Map<String, Protocol> byName = new HashMap<>();
163+
private static Map<Integer, Protocol> byCode = new HashMap<>();
164+
165+
static {
166+
for (Protocol.Type t: Protocol.Type.values()) {
167+
Protocol p = new Protocol(t);
168+
byName.put(p.name(), p);
169+
byCode.put(p.code(), p);
170+
}
171+
172+
}
173+
174+
public static Protocol get(String name) {
175+
if (byName.containsKey(name))
176+
return byName.get(name);
177+
throw new IllegalStateException("No protocol with name: "+name);
178+
}
179+
180+
public static Protocol get(int code) {
181+
if (byCode.containsKey(code))
182+
return byCode.get(code);
183+
throw new IllegalStateException("No protocol with code: "+code);
184+
}
185+
}

test/org/ipfs/Test.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public void blockTest() {
9595
@org.junit.Test
9696
public void fileTest() {
9797
try {
98+
ipfs.repo.gc();
9899
List<String> local = ipfs.refs.local();
99100
for (String hash: local) {
100101
Map ls = ipfs.file.ls(hash);
@@ -140,7 +141,7 @@ public void dhtTest() {
140141
Map get = ipfs.dht.get(pointer);
141142
Map put = ipfs.dht.put("somekey", "somevalue");
142143
Map findprovs = ipfs.dht.findprovs(pointer);
143-
List<NodeAddress> peers = ipfs.swarm.peers();
144+
List<MultiAddress> peers = ipfs.swarm.peers();
144145
Map query = ipfs.dht.query(peers.get(0));
145146
// Map find = ipfs.dht.findpeer(peers.get(0));
146147
} catch (IOException e) {
@@ -178,7 +179,8 @@ public void swarmTest() {
178179
Map id = ipfs.id(addrs.keySet().stream().findAny().get());
179180
Map ping = ipfs.ping(addrs.keySet().stream().findAny().get());
180181
}
181-
List<NodeAddress> peers = ipfs.swarm.peers();
182+
List<MultiAddress> peers = ipfs.swarm.peers();
183+
System.out.println(peers);
182184
} catch (IOException e) {
183185
throw new RuntimeException(e);
184186
}

0 commit comments

Comments
 (0)