It's now possible to send a 'version' message that will be accepted by the other node.
This commit is contained in:
@ -25,9 +25,9 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by chris on 13.03.15.
|
||||
* The 'addr' command holds a list of known active Bitmessage nodes.
|
||||
*/
|
||||
public class Addr implements MessagePayload {
|
||||
public class Addr implements Command {
|
||||
private final List<NetworkAddress> addresses;
|
||||
|
||||
private Addr(Builder builder) {
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||
import ch.dissem.bitmessage.utils.Base58;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
import ch.dissem.bitmessage.utils.Security;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import static ch.dissem.bitmessage.utils.Security.ripemd160;
|
||||
import static ch.dissem.bitmessage.utils.Security.sha512;
|
||||
|
||||
/**
|
||||
* A Bitmessage address. Can be a user's private address, an address string without public keys or a recipient's address
|
||||
* holding private keys.
|
||||
*/
|
||||
public abstract class BitmessageAddress {
|
||||
private long version;
|
||||
private long streamNumber;
|
||||
|
||||
private Pubkey pubkey;
|
||||
|
||||
public BitmessageAddress(Pubkey pubkey) {
|
||||
this.pubkey = pubkey;
|
||||
}
|
||||
|
||||
public BitmessageAddress(String address) {
|
||||
Base58.decode(address.substring(3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
byte[] combinedKeys = new byte[pubkey.getSigningKey().length + pubkey.getEncryptionKey().length];
|
||||
System.arraycopy(pubkey.getSigningKey(), 0, combinedKeys, 0, pubkey.getSigningKey().length);
|
||||
System.arraycopy(pubkey.getEncryptionKey(), 0, combinedKeys, pubkey.getSigningKey().length, pubkey.getEncryptionKey().length);
|
||||
|
||||
byte[] hash = ripemd160(sha512(combinedKeys));
|
||||
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
Encode.varInt(version, stream);
|
||||
Encode.varInt(streamNumber, stream);
|
||||
stream.write(hash);
|
||||
|
||||
byte[] checksum = Security.doubleSha512(stream.toByteArray());
|
||||
for (int i = 0; i < 4; i++) {
|
||||
stream.write(checksum[i]);
|
||||
}
|
||||
return "BM-" + Base58.encode(stream.toByteArray());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,8 +17,8 @@
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
/**
|
||||
* Created by chris on 10.03.15.
|
||||
* A command can hold a network message payload
|
||||
*/
|
||||
public interface MessagePayload extends Streamable {
|
||||
public interface Command extends Streamable {
|
||||
String getCommand();
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The 'getdata' command is used to request objects from a node.
|
||||
*/
|
||||
public class GetData implements Command {
|
||||
List<InventoryVector> inventory;
|
||||
|
||||
private GetData(Builder builder) {
|
||||
inventory = builder.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "getdata";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
Encode.varInt(inventory.size(), stream);
|
||||
for (InventoryVector iv : inventory) {
|
||||
iv.write(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private List<InventoryVector> inventory = new LinkedList<>();
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder addInventoryVector(InventoryVector inventoryVector) {
|
||||
this.inventory.add(inventoryVector);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder inventory(List<InventoryVector> inventory) {
|
||||
this.inventory = inventory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GetData build() {
|
||||
return new GetData(this);
|
||||
}
|
||||
}
|
||||
}
|
70
domain/src/main/java/ch/dissem/bitmessage/entity/Inv.java
Normal file
70
domain/src/main/java/ch/dissem/bitmessage/entity/Inv.java
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The 'inv' command holds up to 50000 inventory vectors, i.e. hashes of inventory items.
|
||||
*/
|
||||
public class Inv implements Command {
|
||||
private List<InventoryVector> inventory;
|
||||
|
||||
private Inv(Builder builder) {
|
||||
inventory = builder.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "inv";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
Encode.varInt(inventory.size(), stream);
|
||||
for (InventoryVector iv : inventory) {
|
||||
iv.write(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private List<InventoryVector> inventory = new LinkedList<>();
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder addInventoryVector(InventoryVector inventoryVector) {
|
||||
this.inventory.add(inventoryVector);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder inventory(List<InventoryVector> inventory) {
|
||||
this.inventory = inventory;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Inv build() {
|
||||
return new Inv(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,28 +16,35 @@
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import static ch.dissem.bitmessage.utils.Security.sha512;
|
||||
|
||||
/**
|
||||
* Created by chris on 10.03.15.
|
||||
* A network message is exchanged between two nodes.
|
||||
*/
|
||||
public class NetworkMessage implements Streamable {
|
||||
/**
|
||||
* Magic value indicating message origin network, and used to seek to next message when stream state is unknown
|
||||
*/
|
||||
private final static int MAGIC = 0xE9BEB4D9;
|
||||
public final static int MAGIC = 0xE9BEB4D9;
|
||||
public final static byte[] MAGIC_BYTES = ByteBuffer.allocate(4).putInt(MAGIC).array();
|
||||
|
||||
private MessagePayload payload;
|
||||
private final NetworkAddress targetNode;
|
||||
|
||||
public NetworkMessage(MessagePayload payload) {
|
||||
private final Command payload;
|
||||
|
||||
public NetworkMessage(NetworkAddress target, Command payload) {
|
||||
this.targetNode = target;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
@ -45,18 +52,21 @@ public class NetworkMessage implements Streamable {
|
||||
* First 4 bytes of sha512(payload)
|
||||
*/
|
||||
private byte[] getChecksum(byte[] bytes) throws NoSuchProviderException, NoSuchAlgorithmException {
|
||||
MessageDigest mda = MessageDigest.getInstance("SHA-512");
|
||||
byte[] d = mda.digest(bytes);
|
||||
byte[] d = sha512(bytes);
|
||||
return new byte[]{d[0], d[1], d[2], d[3]};
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual data, a message or an object. Not to be confused with objectPayload.
|
||||
*/
|
||||
public MessagePayload getPayload() {
|
||||
public Command getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public NetworkAddress getTargetNode() {
|
||||
return targetNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
// magic
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* The 'object' command sends an object that is shared throughout the network.
|
||||
*/
|
||||
public class ObjectMessage implements Command {
|
||||
private long nonce;
|
||||
private long expiresTime;
|
||||
private long objectType;
|
||||
/**
|
||||
* The object's version
|
||||
*/
|
||||
private long version;
|
||||
private long streamNumber;
|
||||
|
||||
private ObjectPayload payload;
|
||||
|
||||
private ObjectMessage(Builder builder) {
|
||||
nonce = builder.nonce;
|
||||
expiresTime = builder.expiresTime;
|
||||
objectType = builder.objectType;
|
||||
version = builder.version;
|
||||
streamNumber = builder.streamNumber;
|
||||
payload = builder.payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "object";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
Encode.int64(nonce, stream);
|
||||
Encode.int64(expiresTime, stream);
|
||||
Encode.int32(objectType, stream);
|
||||
Encode.varInt(version, stream);
|
||||
Encode.varInt(streamNumber, stream);
|
||||
payload.write(stream);
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private long nonce;
|
||||
private long expiresTime;
|
||||
private long objectType;
|
||||
private long version;
|
||||
private long streamNumber;
|
||||
private ObjectPayload payload;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder nonce(long nonce) {
|
||||
this.nonce = nonce;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder expiresTime(long expiresTime) {
|
||||
this.expiresTime = expiresTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder objectType(long objectType) {
|
||||
this.objectType = objectType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder version(long version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder streamNumber(long streamNumber) {
|
||||
this.streamNumber = streamNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder payload(ObjectPayload payload) {
|
||||
this.payload = payload;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ObjectMessage build() {
|
||||
return new ObjectMessage(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by chris on 10.03.15.
|
||||
* An object that can be written to an {@link OutputStream}
|
||||
*/
|
||||
public interface Streamable {
|
||||
void write(OutputStream stream) throws IOException;
|
||||
|
@ -20,9 +20,9 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by chris on 10.03.15.
|
||||
* The 'verack' command answers a 'version' command, accepting the other node's version.
|
||||
*/
|
||||
public class VerAck implements MessagePayload {
|
||||
public class VerAck implements Command {
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "verack";
|
||||
@ -30,6 +30,6 @@ public class VerAck implements MessagePayload {
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
// NO OP
|
||||
// 'verack' doesn't have any payload, so there is nothing to write
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,10 @@ import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Created by chris on 10.03.15.
|
||||
* The 'version' command advertises this node's latest supported protocol version upon initiation.
|
||||
*/
|
||||
public class Version implements MessagePayload {
|
||||
public class Version implements Command {
|
||||
public static final int CURRENT = 3;
|
||||
/**
|
||||
* Identifies protocol version being used by the node. Should equal 3. Nodes should disconnect if the remote node's
|
||||
* version is lower but continue with the connection if it is higher.
|
||||
@ -115,7 +116,7 @@ public class Version implements MessagePayload {
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "ver";
|
||||
return "version";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,8 +124,8 @@ public class Version implements MessagePayload {
|
||||
Encode.int32(version, stream);
|
||||
Encode.int64(services, stream);
|
||||
Encode.int64(timestamp, stream);
|
||||
addrRecv.write(stream);
|
||||
addrFrom.write(stream);
|
||||
addrRecv.write(stream, true);
|
||||
addrFrom.write(stream, true);
|
||||
Encode.int64(nonce, stream);
|
||||
Encode.varString(userAgent, stream);
|
||||
Encode.varIntList(streamNumbers, stream);
|
||||
@ -132,18 +133,28 @@ public class Version implements MessagePayload {
|
||||
|
||||
|
||||
public static final class Builder {
|
||||
private int version = 3;
|
||||
private long services = 1; // This is a normal network node
|
||||
private long timestamp = System.currentTimeMillis() / 1000;
|
||||
private int version;
|
||||
private long services;
|
||||
private long timestamp;
|
||||
private NetworkAddress addrRecv;
|
||||
private NetworkAddress addrFrom;
|
||||
private long nonce = new Random().nextInt();
|
||||
private String userAgent = "/Jabit:0.0.1/";
|
||||
private long[] streamNumbers = {1};
|
||||
private long nonce;
|
||||
private String userAgent;
|
||||
private long[] streamNumbers;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder defaults() {
|
||||
version = CURRENT;
|
||||
services = 1;
|
||||
timestamp = System.currentTimeMillis() / 1000;
|
||||
nonce = new Random().nextInt();
|
||||
userAgent = "/Jabit:0.0.1/";
|
||||
streamNumbers = new long[]{1};
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder version(int version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
@ -179,7 +190,7 @@ public class Version implements MessagePayload {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder streamNumbers(long... streamNumbers) {
|
||||
public Builder streams(long... streamNumbers) {
|
||||
this.streamNumbers = streamNumbers;
|
||||
return this;
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by chris on 24.03.15.
|
||||
*/
|
||||
public class GenericPayload implements ObjectPayload {
|
||||
private byte[] data;
|
||||
|
||||
public GenericPayload(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
stream.write(data);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by chris on 24.03.15.
|
||||
*/
|
||||
public class GetPubkey implements ObjectPayload {
|
||||
private byte[] ripe;
|
||||
private byte[] tag;
|
||||
|
||||
public GetPubkey(byte[] ripeOrTag) {
|
||||
switch (ripeOrTag.length) {
|
||||
case 20:
|
||||
ripe = ripeOrTag;
|
||||
break;
|
||||
case 32:
|
||||
tag = ripeOrTag;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("ripe (20 bytes) or tag (32 bytes) expected, but pubkey was " + ripeOrTag.length + " bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
if (tag != null) {
|
||||
stream.write(tag);
|
||||
} else {
|
||||
stream.write(ripe);
|
||||
}
|
||||
}
|
||||
}
|
@ -14,10 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import ch.dissem.bitmessage.entity.Streamable;
|
||||
|
||||
/**
|
||||
* Created by chris on 16.03.15.
|
||||
* The payload of an 'object' command. This is shared by the network.
|
||||
*/
|
||||
public interface ObjectPayload {
|
||||
public interface ObjectPayload extends Streamable {
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
/**
|
||||
* Created by chris on 24.03.15.
|
||||
*/
|
||||
public interface Pubkey extends ObjectPayload {
|
||||
long getVersion();
|
||||
|
||||
long getStream();
|
||||
|
||||
byte[] getSigningKey();
|
||||
|
||||
byte[] getEncryptionKey();
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by chris on 24.03.15.
|
||||
*/
|
||||
public class V2Pubkey implements Pubkey {
|
||||
protected long streamNumber;
|
||||
protected long behaviorBitfield;
|
||||
protected byte[] publicSigningKey;
|
||||
protected byte[] publicEncryptionKey;
|
||||
|
||||
@Override
|
||||
public long getVersion() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStream() {
|
||||
return streamNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSigningKey() {
|
||||
return publicSigningKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncryptionKey() {
|
||||
return publicEncryptionKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
Encode.int32(behaviorBitfield, stream);
|
||||
stream.write(publicSigningKey);
|
||||
stream.write(publicEncryptionKey);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by chris on 27.03.15.
|
||||
*/
|
||||
public class V3Pubkey extends V2Pubkey {
|
||||
long nonceTrialsPerByte;
|
||||
long extraBytes;
|
||||
byte[] signature;
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
super.write(stream);
|
||||
Encode.varInt(nonceTrialsPerByte, stream);
|
||||
Encode.varInt(extraBytes, stream);
|
||||
Encode.varInt(signature.length, stream);
|
||||
stream.write(signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVersion() {
|
||||
return 3;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Created by chris on 27.03.15.
|
||||
*/
|
||||
public class V4Pubkey implements Pubkey {
|
||||
private long streamNumber;
|
||||
private byte[] tag;
|
||||
private byte[] encrypted;
|
||||
private V3Pubkey decrypted;
|
||||
|
||||
public V4Pubkey(V3Pubkey decrypted) {
|
||||
this.decrypted = decrypted;
|
||||
|
||||
// TODO: this.tag = new BitmessageAddress(this).doubleHash
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
stream.write(tag);
|
||||
stream.write(encrypted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVersion() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStream() {
|
||||
return streamNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSigningKey() {
|
||||
return decrypted.getSigningKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncryptionKey() {
|
||||
return decrypted.getEncryptionKey();
|
||||
}
|
||||
}
|
@ -21,13 +21,15 @@ import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Created by chris on 10.03.15.
|
||||
* A node's address. It's written in IPv6 format.
|
||||
*/
|
||||
public class NetworkAddress implements Streamable {
|
||||
private long time;
|
||||
@ -35,7 +37,7 @@ public class NetworkAddress implements Streamable {
|
||||
/**
|
||||
* Stream number for this node
|
||||
*/
|
||||
private int stream;
|
||||
private long stream;
|
||||
|
||||
/**
|
||||
* same service(s) listed in version
|
||||
@ -88,8 +90,14 @@ public class NetworkAddress implements Streamable {
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
Encode.int64(time, stream);
|
||||
Encode.int32(this.stream, stream);
|
||||
write(stream, false);
|
||||
}
|
||||
|
||||
public void write(OutputStream stream, boolean light) throws IOException {
|
||||
if (!light) {
|
||||
Encode.int64(time, stream);
|
||||
Encode.int32(this.stream, stream);
|
||||
}
|
||||
Encode.int64(services, stream);
|
||||
stream.write(ipv6);
|
||||
Encode.int16(port, stream);
|
||||
@ -97,7 +105,7 @@ public class NetworkAddress implements Streamable {
|
||||
|
||||
public static final class Builder {
|
||||
private long time;
|
||||
private int stream;
|
||||
private long stream;
|
||||
private long services = 1;
|
||||
private byte[] ipv6;
|
||||
private int port;
|
||||
@ -110,7 +118,7 @@ public class NetworkAddress implements Streamable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder stream(final int stream) {
|
||||
public Builder stream(final long stream) {
|
||||
this.stream = stream;
|
||||
return this;
|
||||
}
|
||||
@ -120,6 +128,24 @@ public class NetworkAddress implements Streamable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ip(InetAddress inetAddress) {
|
||||
byte[] addr = inetAddress.getAddress();
|
||||
if (addr.length == 16) {
|
||||
this.ipv6 = addr;
|
||||
} else if (addr.length == 4) {
|
||||
this.ipv6 = new byte[16];
|
||||
System.arraycopy(addr, 0, this.ipv6, 12, 4);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Weird address " + inetAddress);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ipv6(byte[] ipv6) {
|
||||
this.ipv6 = ipv6;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ipv6(int p00, int p01, int p02, int p03,
|
||||
int p04, int p05, int p06, int p07,
|
||||
int p08, int p09, int p10, int p11,
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.factory;
|
||||
|
||||
import ch.dissem.bitmessage.entity.NetworkMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.GenericPayload;
|
||||
import ch.dissem.bitmessage.entity.payload.GetPubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.utils.Decode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Creates {@link NetworkMessage} objects from {@link InputStream InputStreams}
|
||||
*/
|
||||
public class Factory {
|
||||
public static NetworkMessage getNetworkMessage(int version, InputStream stream) throws IOException {
|
||||
return new V3MessageFactory().read(stream);
|
||||
}
|
||||
|
||||
static ObjectPayload getObjectPayload(long objectType, long version, InputStream stream, int length) throws IOException {
|
||||
if (objectType < 4) {
|
||||
switch ((int) objectType) {
|
||||
case 0: // getpubkey
|
||||
return new GetPubkey(Decode.bytes(stream, length));
|
||||
case 1: // pubkey
|
||||
break;
|
||||
case 2: // msg
|
||||
break;
|
||||
case 3: // broadcast
|
||||
break;
|
||||
}
|
||||
throw new RuntimeException("This must not happen, someone broke something in the code!");
|
||||
} else {
|
||||
// passthrough message
|
||||
return new GenericPayload(Decode.bytes(stream, length));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.factory;
|
||||
|
||||
import ch.dissem.bitmessage.entity.*;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
import ch.dissem.bitmessage.utils.Decode;
|
||||
import ch.dissem.bitmessage.utils.Security;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Creates protocol v3 network messages from {@link InputStream InputStreams}
|
||||
*/
|
||||
class V3MessageFactory {
|
||||
public NetworkMessage read(InputStream stream) throws IOException {
|
||||
if (testMagic(stream)) {
|
||||
String command = getCommand(stream);
|
||||
int length = (int) Decode.uint32(stream);
|
||||
byte[] checksum = Decode.bytes(stream, 4);
|
||||
|
||||
byte[] payloadBytes = Decode.bytes(stream, length);
|
||||
|
||||
if (testChecksum(checksum, payloadBytes)) {
|
||||
Command payload = getPayload(command, new ByteArrayInputStream(payloadBytes), length);
|
||||
return new NetworkMessage(payload);
|
||||
} else {
|
||||
throw new IOException("Checksum failed for message '" + command + "'");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Command getPayload(String command, InputStream stream, int length) throws IOException {
|
||||
switch (command) {
|
||||
case "version":
|
||||
return parseVersion(stream);
|
||||
case "verack":
|
||||
return new VerAck();
|
||||
case "addr":
|
||||
return parseAddr(stream);
|
||||
case "inv":
|
||||
return parseInv(stream);
|
||||
case "getdata":
|
||||
return parseGetData(stream);
|
||||
case "object":
|
||||
return parseObject(stream, length);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectMessage parseObject(InputStream stream, int length) throws IOException {
|
||||
long nonce = Decode.int64(stream);
|
||||
long expiresTime = Decode.int64(stream);
|
||||
long objectType = Decode.uint32(stream);
|
||||
long version = Decode.varInt(stream);
|
||||
long streamNumber = Decode.varInt(stream);
|
||||
|
||||
ObjectPayload payload = Factory.getObjectPayload(objectType, version, stream, length);
|
||||
|
||||
return new ObjectMessage.Builder()
|
||||
.nonce(nonce)
|
||||
.expiresTime(expiresTime)
|
||||
.objectType(objectType)
|
||||
.version(version)
|
||||
.streamNumber(streamNumber)
|
||||
.payload(payload)
|
||||
.build();
|
||||
}
|
||||
|
||||
private GetData parseGetData(InputStream stream) throws IOException {
|
||||
long count = Decode.varInt(stream);
|
||||
GetData.Builder builder = new GetData.Builder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.addInventoryVector(parseInventoryVector(stream));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Inv parseInv(InputStream stream) throws IOException {
|
||||
long count = Decode.varInt(stream);
|
||||
Inv.Builder builder = new Inv.Builder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.addInventoryVector(parseInventoryVector(stream));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Addr parseAddr(InputStream stream) throws IOException {
|
||||
long count = Decode.varInt(stream);
|
||||
Addr.Builder builder = new Addr.Builder();
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.addAddress(parseAddress(stream));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Version parseVersion(InputStream stream) throws IOException {
|
||||
int version = Decode.int32(stream);
|
||||
long services = Decode.int64(stream);
|
||||
long timestamp = Decode.int64(stream);
|
||||
NetworkAddress addrRecv = parseAddress(stream);
|
||||
NetworkAddress addrFrom = parseAddress(stream);
|
||||
long nonce = Decode.int64(stream);
|
||||
String userAgent = Decode.varString(stream);
|
||||
long[] streamNumbers = Decode.varIntList(stream);
|
||||
|
||||
return new Version.Builder()
|
||||
.version(version)
|
||||
.services(services)
|
||||
.timestamp(timestamp)
|
||||
.addrRecv(addrRecv).addrFrom(addrFrom)
|
||||
.nonce(nonce)
|
||||
.userAgent(userAgent)
|
||||
.streams(streamNumbers).build();
|
||||
}
|
||||
|
||||
private InventoryVector parseInventoryVector(InputStream stream) throws IOException {
|
||||
return new InventoryVector(Decode.bytes(stream, 32));
|
||||
}
|
||||
|
||||
private NetworkAddress parseAddress(InputStream stream) throws IOException {
|
||||
long time = Decode.int64(stream);
|
||||
long streamNumber = Decode.uint32(stream); // This isn't consistent, not sure if this is correct
|
||||
long services = Decode.int64(stream);
|
||||
byte[] ipv6 = Decode.bytes(stream, 16);
|
||||
int port = Decode.uint16(stream);
|
||||
return new NetworkAddress.Builder().time(time).stream(streamNumber).services(services).ipv6(ipv6).port(port).build();
|
||||
}
|
||||
|
||||
private boolean testChecksum(byte[] checksum, byte[] payload) {
|
||||
byte[] payloadChecksum = Security.sha512(payload);
|
||||
for (int i = 0; i < checksum.length; i++) {
|
||||
if (checksum[i] != payloadChecksum[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getCommand(InputStream stream) throws IOException {
|
||||
byte[] bytes = new byte[12];
|
||||
stream.read(bytes);
|
||||
return new String(bytes, "ASCII");
|
||||
}
|
||||
|
||||
private boolean testMagic(InputStream stream) throws IOException {
|
||||
for (byte b : NetworkMessage.MAGIC_BYTES) {
|
||||
if (b != stream.read()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -16,13 +16,13 @@
|
||||
|
||||
package ch.dissem.bitmessage.ports;
|
||||
|
||||
import ch.dissem.bitmessage.entity.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by chris on 16.03.15.
|
||||
* The Inventory stores and retrieves objects, cleans up outdated objects and can tell which objects are still missing.
|
||||
*/
|
||||
public interface Inventory {
|
||||
public List<InventoryVector> getInventory();
|
||||
|
@ -19,13 +19,15 @@ package ch.dissem.bitmessage.ports;
|
||||
import ch.dissem.bitmessage.entity.NetworkMessage;
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by chris on 16.03.15.
|
||||
* Handles incoming messages
|
||||
*/
|
||||
public interface NetworkMessageReceiver {
|
||||
public void registerListener(int port);
|
||||
public void registerListener(int port) throws IOException;
|
||||
|
||||
public void registerListener(NetworkAddress node, MessageListener listener);
|
||||
public void registerListener(NetworkAddress node, MessageListener listener) throws IOException;
|
||||
|
||||
public static interface MessageListener {
|
||||
public void receive(NetworkMessage message);
|
||||
|
@ -20,7 +20,7 @@ import ch.dissem.bitmessage.entity.NetworkMessage;
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
|
||||
/**
|
||||
* Created by chris on 16.03.15.
|
||||
* Sends messages
|
||||
*/
|
||||
public interface NetworkMessageSender {
|
||||
public void send(NetworkAddress node, NetworkMessage message);
|
||||
|
32
domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java
Normal file
32
domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
/**
|
||||
* Base58 encoder and decoder
|
||||
*/
|
||||
public class Base58 {
|
||||
private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
|
||||
|
||||
public static String encode(byte[] input) {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
public static byte[] decode(String input) {
|
||||
return null; // TODO
|
||||
}
|
||||
}
|
86
domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java
Normal file
86
domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* This class handles decoding simple types from byte stream, according to
|
||||
* https://bitmessage.org/wiki/Protocol_specification#Common_structures
|
||||
*/
|
||||
public class Decode {
|
||||
public static byte[] bytes(InputStream stream, int count) throws IOException {
|
||||
byte[] result = new byte[count];
|
||||
stream.read(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long[] varIntList(InputStream stream) throws IOException {
|
||||
int length = (int) varInt(stream);
|
||||
long[] result = new long[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
result[i] = varInt(stream);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static long varInt(InputStream stream) throws IOException {
|
||||
int first = stream.read();
|
||||
switch (first) {
|
||||
case 0xfd:
|
||||
return uint16(stream);
|
||||
case 0xfe:
|
||||
return uint32(stream);
|
||||
case 0xff:
|
||||
return int64(stream);
|
||||
default:
|
||||
return first;
|
||||
}
|
||||
}
|
||||
|
||||
public static int uint8(InputStream stream) throws IOException {
|
||||
return stream.read();
|
||||
}
|
||||
|
||||
public static int uint16(InputStream stream) throws IOException {
|
||||
return stream.read() * 256 + stream.read();
|
||||
}
|
||||
|
||||
public static long uint32(InputStream stream) throws IOException {
|
||||
return stream.read() * 16777216L + stream.read() * 65536L + stream.read() * 256L + stream.read();
|
||||
}
|
||||
|
||||
public static int int32(InputStream stream) throws IOException {
|
||||
return ByteBuffer.wrap(bytes(stream, 4)).getInt();
|
||||
}
|
||||
|
||||
public static long int64(InputStream stream) throws IOException {
|
||||
return ByteBuffer.wrap(bytes(stream, 8)).getLong();
|
||||
}
|
||||
|
||||
public static String varString(InputStream stream) throws IOException {
|
||||
int length = (int) varInt(stream);
|
||||
// FIXME: technically, it says the length in characters, but I think this one might be correct
|
||||
byte[] bytes = new byte[length];
|
||||
// FIXME: I'm also not quite sure if this works, maybe the read return value needs to be handled properly
|
||||
stream.read(bytes);
|
||||
return new String(bytes, "utf-8");
|
||||
}
|
||||
}
|
@ -21,7 +21,8 @@ import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Created by chris on 13.03.15.
|
||||
* This class handles encoding simple types from byte stream, according to
|
||||
* https://bitmessage.org/wiki/Protocol_specification#Common_structures
|
||||
*/
|
||||
public class Encode {
|
||||
public static void varIntList(long[] values, OutputStream stream) throws IOException {
|
||||
@ -71,6 +72,7 @@ public class Encode {
|
||||
|
||||
public static void varString(String value, OutputStream stream) throws IOException {
|
||||
byte[] bytes = value.getBytes("utf-8");
|
||||
// FIXME: technically, it says the length in characters, but I think this one might be correct
|
||||
varInt(bytes.length, stream);
|
||||
stream.write(bytes);
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Provides some methods to help with hashing and encryption.
|
||||
*/
|
||||
public class Security {
|
||||
public static byte[] sha512(byte[] data) {
|
||||
try {
|
||||
MessageDigest mda = MessageDigest.getInstance("SHA-512");
|
||||
return mda.digest(data);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] doubleSha512(byte[] data) {
|
||||
try {
|
||||
MessageDigest mda = MessageDigest.getInstance("SHA-512");
|
||||
return mda.digest(mda.digest(data));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ripemd160(byte[] data) {
|
||||
try {
|
||||
MessageDigest mda = MessageDigest.getInstance("RIPEMD-160");
|
||||
return mda.digest(data);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Created by chris on 20.03.15.
|
||||
*/
|
||||
public class DecodeTest {
|
||||
@Test
|
||||
public void ensureDecodingWorks() throws Exception {
|
||||
// This should test all relevant cases for var_int and therefore also uint_16, uint_32 and int_64
|
||||
testCodec(0);
|
||||
for (long i = 1; i > 0; i = 3 * i + 7) {
|
||||
testCodec(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void testCodec(long number) throws IOException {
|
||||
ByteArrayOutputStream is = new ByteArrayOutputStream();
|
||||
Encode.varInt(number, is);
|
||||
assertEquals(number, Decode.varInt(new ByteArrayInputStream(is.toByteArray())));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user