Some work on addresses and private keys that still doesn't work. As a side effect, sending objects now works basically.
This commit is contained in:
parent
8d1466f6f4
commit
a65907f13b
@ -16,18 +16,17 @@
|
||||
|
||||
package ch.dissem.bitmessage.demo;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.inventory.JdbcAddressRepository;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||
import ch.dissem.bitmessage.inventory.JdbcInventory;
|
||||
import ch.dissem.bitmessage.inventory.JdbcNodeRegistry;
|
||||
import ch.dissem.bitmessage.networking.NetworkNode;
|
||||
import ch.dissem.bitmessage.ports.NetworkHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Scanner;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by chris on 06.04.15.
|
||||
@ -36,26 +35,60 @@ public class Main {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Main.class);
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
BitmessageContext ctx = new BitmessageContext.Builder()
|
||||
.addressRepo(new JdbcAddressRepository())
|
||||
.inventory(new JdbcInventory())
|
||||
.nodeRegistry(new JdbcNodeRegistry())
|
||||
.networkHandler(new NetworkNode())
|
||||
.port(48444)
|
||||
.streams(1)
|
||||
.build();
|
||||
ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() {
|
||||
@Override
|
||||
public void receive(ObjectPayload payload) {
|
||||
// LOG.info("message received: " + payload);
|
||||
// System.out.print('.');
|
||||
}
|
||||
});
|
||||
final BitmessageAddress address = new BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ");
|
||||
|
||||
System.out.print("Press Enter to exit\n");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
scanner.nextLine();
|
||||
LOG.info("Shutting down client");
|
||||
ctx.getNetworkHandler().stop();
|
||||
// BitmessageContext ctx = new BitmessageContext.Builder()
|
||||
// .addressRepo(new JdbcAddressRepository())
|
||||
// .inventory(new JdbcInventory())
|
||||
// .nodeRegistry(new JdbcNodeRegistry())
|
||||
// .networkHandler(new NetworkNode())
|
||||
// .port(48444)
|
||||
// .streams(1)
|
||||
// .build();
|
||||
//
|
||||
// ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() {
|
||||
// @Override
|
||||
// public void receive(ObjectPayload payload) {
|
||||
//// LOG.info("message received: " + payload);
|
||||
//// System.out.print('.');
|
||||
// if (payload instanceof V3Pubkey) {
|
||||
// V3Pubkey pubkey = (V3Pubkey) payload;
|
||||
// try {
|
||||
// address.setPubkey(pubkey);
|
||||
// System.out.println(address);
|
||||
// } catch (Exception ignore) {
|
||||
// System.err.println("Received pubkey we didn't request.");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// Scanner scanner = new Scanner(System.in);
|
||||
// System.out.println("Press Enter to request pubkey for address " + address);
|
||||
// scanner.nextLine();
|
||||
// ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000);
|
||||
//
|
||||
// System.out.println("Press Enter to exit");
|
||||
// scanner.nextLine();
|
||||
// LOG.info("Shutting down client");
|
||||
// ctx.getNetworkHandler().stop();
|
||||
|
||||
List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY);
|
||||
System.out.println("Address version: " + address.getVersion());
|
||||
System.out.println("Address stream: " + address.getStream());
|
||||
for (ObjectMessage o : objects) {
|
||||
Pubkey pubkey = (Pubkey) o.getPayload();
|
||||
if (Arrays.equals(address.getRipe(), pubkey.getRipe()))
|
||||
System.out.println("Pubkey found!");
|
||||
try {
|
||||
address.setPubkey(pubkey);
|
||||
System.out.println(address);
|
||||
} catch (Exception ignore) {
|
||||
System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length);
|
||||
if (Arrays.equals(address.getRipe(), pubkey.getRipe())) {
|
||||
ignore.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,15 @@
|
||||
|
||||
package ch.dissem.bitmessage;
|
||||
|
||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||
import ch.dissem.bitmessage.ports.Inventory;
|
||||
import ch.dissem.bitmessage.ports.NetworkHandler;
|
||||
import ch.dissem.bitmessage.ports.NodeRegistry;
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.ports.*;
|
||||
import ch.dissem.bitmessage.utils.Security;
|
||||
import ch.dissem.bitmessage.utils.UnixTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@ -29,15 +33,16 @@ import java.util.TreeSet;
|
||||
*/
|
||||
public class BitmessageContext {
|
||||
public static final int CURRENT_VERSION = 3;
|
||||
private final static Logger LOG = LoggerFactory.getLogger(BitmessageContext.class);
|
||||
private final Inventory inventory;
|
||||
private final NodeRegistry nodeRegistry;
|
||||
private final NetworkHandler networkHandler;
|
||||
private final AddressRepository addressRepo;
|
||||
private final ProofOfWorkEngine proofOfWorkEngine;
|
||||
|
||||
private Inventory inventory;
|
||||
private NodeRegistry nodeRegistry;
|
||||
private NetworkHandler networkHandler;
|
||||
private AddressRepository addressRepo;
|
||||
private final TreeSet<Long> streams;
|
||||
|
||||
private Collection<Long> streams = new TreeSet<>();
|
||||
|
||||
private int port;
|
||||
private final int port;
|
||||
|
||||
private long networkNonceTrialsPerByte = 1000;
|
||||
private long networkExtraBytes = 1000;
|
||||
@ -48,9 +53,10 @@ public class BitmessageContext {
|
||||
nodeRegistry = builder.nodeRegistry;
|
||||
networkHandler = builder.networkHandler;
|
||||
addressRepo = builder.addressRepo;
|
||||
proofOfWorkEngine = builder.proofOfWorkEngine;
|
||||
streams = builder.streams;
|
||||
|
||||
init(inventory, nodeRegistry, networkHandler, addressRepo);
|
||||
init(inventory, nodeRegistry, networkHandler, addressRepo, proofOfWorkEngine);
|
||||
}
|
||||
|
||||
private void init(Object... objects) {
|
||||
@ -61,6 +67,20 @@ public class BitmessageContext {
|
||||
}
|
||||
}
|
||||
|
||||
public void send(long stream, long version, ObjectPayload payload, long timeToLive, long nonceTrialsPerByte, long extraBytes) throws IOException {
|
||||
long expires = UnixTime.now(+timeToLive);
|
||||
LOG.info("Expires at " + expires);
|
||||
ObjectMessage object = new ObjectMessage.Builder()
|
||||
.stream(stream)
|
||||
.version(version)
|
||||
.expiresTime(expires)
|
||||
.payload(payload)
|
||||
.build();
|
||||
Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes);
|
||||
inventory.storeObject(object);
|
||||
networkHandler.offer(object.getInventoryVector());
|
||||
}
|
||||
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
@ -113,7 +133,8 @@ public class BitmessageContext {
|
||||
private NodeRegistry nodeRegistry;
|
||||
private NetworkHandler networkHandler;
|
||||
private AddressRepository addressRepo;
|
||||
private Collection<Long> streams;
|
||||
private ProofOfWorkEngine proofOfWorkEngine;
|
||||
private TreeSet<Long> streams;
|
||||
|
||||
public Builder() {
|
||||
}
|
||||
@ -143,8 +164,13 @@ public class BitmessageContext {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) {
|
||||
this.proofOfWorkEngine = proofOfWorkEngine;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder streams(Collection<Long> streams) {
|
||||
this.streams = streams;
|
||||
this.streams = new TreeSet<>(streams);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -164,6 +190,9 @@ public class BitmessageContext {
|
||||
if (streams == null) {
|
||||
streams(1);
|
||||
}
|
||||
if (proofOfWorkEngine == null) {
|
||||
proofOfWorkEngine = new MultiThreadedPOWEngine();
|
||||
}
|
||||
return new BitmessageContext(this);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,9 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static ch.dissem.bitmessage.utils.Decode.bytes;
|
||||
import static ch.dissem.bitmessage.utils.Decode.varInt;
|
||||
|
||||
/**
|
||||
* A Bitmessage address. Can be a user's private address, an address string without public keys or a recipient's address
|
||||
* holding private keys.
|
||||
@ -53,10 +56,10 @@ public class BitmessageAddress {
|
||||
byte[] bytes = Base58.decode(address.substring(3));
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
|
||||
AccessCounter counter = new AccessCounter();
|
||||
this.version = Decode.varInt(in, counter);
|
||||
this.stream = Decode.varInt(in, counter);
|
||||
this.ripe = Decode.bytes(in, bytes.length - counter.length() - 4);
|
||||
testChecksum(Decode.bytes(in, 4), bytes);
|
||||
this.version = varInt(in, counter);
|
||||
this.stream = varInt(in, counter);
|
||||
this.ripe = Bytes.expand(bytes(in, bytes.length - counter.length() - 4), 20);
|
||||
testChecksum(bytes(in, 4), bytes);
|
||||
this.address = generateAddress();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -108,10 +111,6 @@ public class BitmessageAddress {
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
@ -120,6 +119,10 @@ public class BitmessageAddress {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return alias != null ? alias : address;
|
||||
|
@ -89,20 +89,20 @@ public class ObjectMessage implements MessagePayload {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
stream.write(nonce);
|
||||
stream.write(getPayloadBytesWithoutNonce());
|
||||
public void write(OutputStream out) throws IOException {
|
||||
out.write(nonce);
|
||||
out.write(getPayloadBytesWithoutNonce());
|
||||
}
|
||||
|
||||
public byte[] getPayloadBytesWithoutNonce() throws IOException {
|
||||
if (payloadBytes == null) {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
Encode.int64(expiresTime, stream);
|
||||
Encode.int32(objectType, stream);
|
||||
Encode.varInt(version, stream);
|
||||
Encode.varInt(this.stream, stream);
|
||||
payload.write(stream);
|
||||
payloadBytes = stream.toByteArray();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Encode.int64(expiresTime, out);
|
||||
Encode.int32(objectType, out);
|
||||
Encode.varInt(version, out);
|
||||
Encode.varInt(stream, out);
|
||||
payload.write(out);
|
||||
payloadBytes = out.toByteArray();
|
||||
}
|
||||
return payloadBytes;
|
||||
}
|
||||
@ -110,8 +110,8 @@ public class ObjectMessage implements MessagePayload {
|
||||
public static final class Builder {
|
||||
private byte[] nonce;
|
||||
private long expiresTime;
|
||||
private long objectType;
|
||||
private long version;
|
||||
private long objectType = -1;
|
||||
private long version = -1;
|
||||
private long streamNumber;
|
||||
private ObjectPayload payload;
|
||||
|
||||
@ -138,13 +138,15 @@ public class ObjectMessage implements MessagePayload {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder streamNumber(long streamNumber) {
|
||||
public Builder stream(long streamNumber) {
|
||||
this.streamNumber = streamNumber;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder payload(ObjectPayload payload) {
|
||||
this.payload = payload;
|
||||
if (this.objectType == -1)
|
||||
this.objectType = payload.getType().getNumber();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,11 @@ public class GenericPayload implements ObjectPayload {
|
||||
return new GenericPayload(stream, Decode.bytes(is, length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectType getType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStream() {
|
||||
return stream;
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package ch.dissem.bitmessage.entity.payload;
|
||||
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.utils.Bytes;
|
||||
import ch.dissem.bitmessage.utils.Decode;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -30,22 +32,30 @@ public class GetPubkey implements ObjectPayload {
|
||||
private byte[] ripe;
|
||||
private byte[] tag;
|
||||
|
||||
private GetPubkey(long stream, byte[] ripeOrTag) {
|
||||
public GetPubkey(BitmessageAddress address) {
|
||||
this.stream = address.getStream();
|
||||
if (address.getVersion() < 4)
|
||||
this.ripe = address.getRipe();
|
||||
else
|
||||
this.tag = ((V4Pubkey) address.getPubkey()).getTag();
|
||||
}
|
||||
|
||||
private GetPubkey(long stream, long version, byte[] ripeOrTag) {
|
||||
this.stream = stream;
|
||||
switch (ripeOrTag.length) {
|
||||
case 20:
|
||||
if (version < 4) {
|
||||
ripe = ripeOrTag;
|
||||
break;
|
||||
case 32:
|
||||
} else {
|
||||
tag = ripeOrTag;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("ripe (20 bytes) or tag (32 bytes) expected, but pubkey was " + ripeOrTag.length + " bytes long.");
|
||||
}
|
||||
}
|
||||
|
||||
public static GetPubkey read(InputStream is, long stream, int length) throws IOException {
|
||||
return new GetPubkey(stream, Decode.bytes(is, length));
|
||||
public static GetPubkey read(InputStream is, long stream, int length, long version) throws IOException {
|
||||
return new GetPubkey(stream, version, Decode.bytes(is, length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectType getType() {
|
||||
return ObjectType.GET_PUBKEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,6 +44,11 @@ public class Msg implements ObjectPayload {
|
||||
return new Msg(stream, Decode.bytes(is, length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectType getType() {
|
||||
return ObjectType.MSG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStream() {
|
||||
return stream;
|
||||
|
@ -22,5 +22,7 @@ import ch.dissem.bitmessage.entity.Streamable;
|
||||
* The payload of an 'object' command. This is shared by the network.
|
||||
*/
|
||||
public interface ObjectPayload extends Streamable {
|
||||
ObjectType getType();
|
||||
|
||||
long getStream();
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Known types for 'object' messages. Must not be used where an unknown type must be resent.
|
||||
*/
|
||||
public enum ObjectType {
|
||||
GET_PUBKEY(0),
|
||||
PUBKEY(1),
|
||||
MSG(2),
|
||||
BROADCAST(3);
|
||||
|
||||
int number;
|
||||
|
||||
ObjectType(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public static ObjectType fromNumber(long number) {
|
||||
for (ObjectType type : values()) {
|
||||
if (type.number == number) return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public long getNumber() {
|
||||
return number;
|
||||
}
|
||||
}
|
@ -56,6 +56,11 @@ public class V2Pubkey extends Pubkey {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectType getType() {
|
||||
return ObjectType.PUBKEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStream() {
|
||||
return stream;
|
||||
|
@ -40,6 +40,11 @@ public class V4Broadcast implements Broadcast {
|
||||
return new V4Broadcast(stream, Decode.bytes(is, length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectType getType() {
|
||||
return ObjectType.BROADCAST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStream() {
|
||||
return stream;
|
||||
|
@ -62,6 +62,11 @@ public class V4Pubkey extends Pubkey {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectType getType() {
|
||||
return ObjectType.PUBKEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStream() {
|
||||
return stream;
|
||||
|
@ -103,7 +103,7 @@ public class NetworkAddress implements Streamable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toInetAddress() + ":" + port;
|
||||
return "[" + toInetAddress() + "]:" + port;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -101,27 +101,28 @@ public class Factory {
|
||||
}
|
||||
|
||||
static ObjectPayload getObjectPayload(long objectType, long version, long streamNumber, InputStream stream, int length) throws IOException {
|
||||
if (objectType < 4) {
|
||||
switch ((int) objectType) {
|
||||
case 0:
|
||||
ObjectType type = ObjectType.fromNumber(objectType);
|
||||
if (type != null) {
|
||||
switch (type) {
|
||||
case GET_PUBKEY:
|
||||
return parseGetPubkey(version, streamNumber, stream, length);
|
||||
case 1:
|
||||
case PUBKEY:
|
||||
return parsePubkey(version, streamNumber, stream, length);
|
||||
case 2:
|
||||
case MSG:
|
||||
return parseMsg(version, streamNumber, stream, length);
|
||||
case 3:
|
||||
case BROADCAST:
|
||||
return parseBroadcast(version, streamNumber, stream, length);
|
||||
default:
|
||||
LOG.error("This should not happen, someone broke something in the code!");
|
||||
}
|
||||
}
|
||||
// fallback: just store the message - we don't really care what it is
|
||||
LOG.warn("Unexpected object type: " + objectType);
|
||||
// LOG.info("Unexpected object type: " + objectType);
|
||||
return GenericPayload.read(stream, streamNumber, length);
|
||||
}
|
||||
|
||||
private static ObjectPayload parseGetPubkey(long version, long streamNumber, InputStream stream, int length) throws IOException {
|
||||
return GetPubkey.read(stream, streamNumber, length);
|
||||
return GetPubkey.read(stream, streamNumber, length, version);
|
||||
}
|
||||
|
||||
public static Pubkey readPubkey(long version, long stream, InputStream is, int length) throws IOException {
|
||||
|
@ -46,7 +46,10 @@ class V3MessageFactory {
|
||||
|
||||
if (testChecksum(checksum, payloadBytes)) {
|
||||
MessagePayload payload = getPayload(command, new ByteArrayInputStream(payloadBytes), length);
|
||||
if (payload != null)
|
||||
return new NetworkMessage(payload);
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
throw new IOException("Checksum failed for message '" + command + "'");
|
||||
}
|
||||
@ -91,7 +94,7 @@ class V3MessageFactory {
|
||||
.expiresTime(expiresTime)
|
||||
.objectType(objectType)
|
||||
.version(version)
|
||||
.streamNumber(streamNumber)
|
||||
.stream(streamNumber)
|
||||
.payload(payload)
|
||||
.build();
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package ch.dissem.bitmessage.ports;
|
||||
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
|
||||
import java.util.List;
|
||||
@ -31,6 +32,8 @@ public interface Inventory {
|
||||
|
||||
ObjectMessage getObject(InventoryVector vector);
|
||||
|
||||
List<ObjectMessage> getObjects(long stream, long version, ObjectType type);
|
||||
|
||||
void storeObject(ObjectMessage object);
|
||||
|
||||
void cleanup();
|
||||
|
@ -18,6 +18,7 @@ package ch.dissem.bitmessage.ports;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
|
||||
/**
|
||||
* Handles incoming messages
|
||||
@ -27,7 +28,7 @@ public interface NetworkHandler {
|
||||
|
||||
void stop();
|
||||
|
||||
void send(ObjectPayload payload);
|
||||
void offer(InventoryVector iv);
|
||||
|
||||
interface MessageListener {
|
||||
void receive(ObjectPayload payload);
|
||||
|
@ -80,6 +80,9 @@ public class Security {
|
||||
}
|
||||
|
||||
public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, long extraBytes) throws IOException {
|
||||
if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000;
|
||||
if (extraBytes < 1000) extraBytes = 1000;
|
||||
|
||||
byte[] initialHash = getInitialHash(object);
|
||||
|
||||
byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes);
|
||||
|
@ -20,7 +20,7 @@ package ch.dissem.bitmessage.utils;
|
||||
* Created by chris on 13.04.15.
|
||||
*/
|
||||
public class Strings {
|
||||
public static CharSequence join(byte[]... objects) {
|
||||
public static StringBuilder join(byte[]... objects) {
|
||||
StringBuilder streamList = new StringBuilder();
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
if (i > 0) streamList.append(", ");
|
||||
@ -29,7 +29,7 @@ public class Strings {
|
||||
return streamList;
|
||||
}
|
||||
|
||||
public static CharSequence join(long... objects) {
|
||||
public static StringBuilder join(long... objects) {
|
||||
StringBuilder streamList = new StringBuilder();
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
if (i > 0) streamList.append(", ");
|
||||
@ -38,7 +38,7 @@ public class Strings {
|
||||
return streamList;
|
||||
}
|
||||
|
||||
public static CharSequence join(Object... objects) {
|
||||
public static StringBuilder join(Object... objects) {
|
||||
StringBuilder streamList = new StringBuilder();
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
if (i > 0) streamList.append(", ");
|
||||
@ -47,9 +47,8 @@ public class Strings {
|
||||
return streamList;
|
||||
}
|
||||
|
||||
public static CharSequence hex(byte[] bytes) {
|
||||
public static StringBuilder hex(byte[] bytes) {
|
||||
StringBuilder hex = new StringBuilder(bytes.length + 2);
|
||||
hex.append("0x");
|
||||
for (byte b : bytes) {
|
||||
hex.append(String.format("%02x", b));
|
||||
}
|
||||
|
@ -26,4 +26,8 @@ public class UnixTime {
|
||||
public static long now() {
|
||||
return System.currentTimeMillis() / 1000;
|
||||
}
|
||||
|
||||
public static long now(long shiftSeconds) {
|
||||
return (System.currentTimeMillis() / 1000) + shiftSeconds;
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,14 @@
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||
import ch.dissem.bitmessage.utils.Base58;
|
||||
import ch.dissem.bitmessage.utils.Bytes;
|
||||
import ch.dissem.bitmessage.utils.Security;
|
||||
import ch.dissem.bitmessage.utils.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class BitmessageAddressTest {
|
||||
@Test
|
||||
@ -49,24 +49,59 @@ public class BitmessageAddressTest {
|
||||
assertNotNull(address.getPubkey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testV3() {
|
||||
// ripe 007402be6e76c3cb87caa946d0c003a3d4d8e1d5
|
||||
// publicSigningKey in hex: 0435e3f10f4884ec42f11f1a815ace8c7c4575cad455ca98db19a245c4c57baebdce990919b647f2657596b75aa939b858bd70c55a03492dd95119bef009cf9eea
|
||||
// publicEncryptionKey in hex: 04bf30a7ee7854f9381332a6285659215a6a4b2ab3479fa87fe996f7cd11710367748371d8d2545f8466964dd3140ab80508b2b18e45616ef6cc4d8e54db923761
|
||||
BitmessageAddress address = new BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ");
|
||||
V3Pubkey pubkey = new V3Pubkey.Builder()
|
||||
.stream(1)
|
||||
.publicSigningKey(Bytes.fromHex("0435e3f10f4884ec42f11f1a815ace8c7c4575cad455ca98db19a245c4c57baebdce990919b647f2657596b75aa939b858bd70c55a03492dd95119bef009cf9eea"))
|
||||
.publicEncryptionKey(Bytes.fromHex("04bf30a7ee7854f9381332a6285659215a6a4b2ab3479fa87fe996f7cd11710367748371d8d2545f8466964dd3140ab80508b2b18e45616ef6cc4d8e54db923761"))
|
||||
.build();
|
||||
address.setPubkey(pubkey);
|
||||
assertArrayEquals(Bytes.fromHex("007402be6e76c3cb87caa946d0c003a3d4d8e1d5"), address.getRipe());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testV3PubkeyImport() throws IOException {
|
||||
ObjectMessage object = TestUtils.loadObjectMessage(3, "V3Pubkey.payload");
|
||||
V3Pubkey pubkey = (V3Pubkey) object.getPayload();
|
||||
BitmessageAddress address = new BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn");
|
||||
address.setPubkey(pubkey);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testV3Import() {
|
||||
assertEquals(3, new BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn").getVersion());
|
||||
assertEquals(1, new BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn").getStream());
|
||||
String address_string = "BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn";
|
||||
assertEquals(3, new BitmessageAddress(address_string).getVersion());
|
||||
assertEquals(1, new BitmessageAddress(address_string).getStream());
|
||||
|
||||
byte[] privsigningkey = Base58.decode("5KU2gbe9u4rKJ8PHYb1rvwMnZnAJj4gtV5GLwoYckeYzygWUzB9");
|
||||
byte[] privencryptionkey = Base58.decode("5KHd4c6cavd8xv4kzo3PwnVaYuBgEfg7voPQ5V97aZKgpYBXGck");
|
||||
assertEquals((byte) 0x80, privsigningkey[0]);
|
||||
assertEquals((byte) 0x80, privencryptionkey[0]);
|
||||
privsigningkey = Bytes.subArray(privsigningkey, 1, privsigningkey.length - 5);
|
||||
privencryptionkey = Bytes.subArray(privencryptionkey, 1, privencryptionkey.length - 5);
|
||||
byte[] privsigningkey = getSecret("5KU2gbe9u4rKJ8PHYb1rvwMnZnAJj4gtV5GLwoYckeYzygWUzB9");
|
||||
byte[] privencryptionkey = getSecret("5KHd4c6cavd8xv4kzo3PwnVaYuBgEfg7voPQ5V97aZKgpYBXGck");
|
||||
|
||||
privsigningkey = Bytes.expand(privsigningkey, 32);
|
||||
privencryptionkey = Bytes.expand(privencryptionkey, 32);
|
||||
System.out.println("\n\n" + Strings.hex(privsigningkey) + "\n\n");
|
||||
|
||||
// privsigningkey = Bytes.expand(privsigningkey, 32);
|
||||
// privencryptionkey = Bytes.expand(privencryptionkey, 32);
|
||||
|
||||
BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey,
|
||||
Security.createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000)));
|
||||
assertEquals("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn", address.getAddress());
|
||||
assertEquals(address_string, address.getAddress());
|
||||
}
|
||||
|
||||
private byte[] getSecret(String walletImportFormat) {
|
||||
byte[] bytes = Base58.decode("5KU2gbe9u4rKJ8PHYb1rvwMnZnAJj4gtV5GLwoYckeYzygWUzB9");
|
||||
assertEquals(37, bytes.length);
|
||||
assertEquals((byte) 0x80, bytes[0]);
|
||||
byte[] checksum = Bytes.subArray(bytes, bytes.length - 4, 4);
|
||||
byte[] secret = Bytes.subArray(bytes, 1, 32);
|
||||
// assertArrayEquals("Checksum failed", checksum, Bytes.subArray(Security.doubleSha512(new byte[]{(byte) 0x80}, secret, new byte[]{0x01}), 0, 4));
|
||||
byte[] result = new byte[33];
|
||||
result[0] = 0x04;
|
||||
System.arraycopy(secret, 0, result, 1, secret.length);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.payload.*;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
import ch.dissem.bitmessage.utils.TestUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -69,7 +70,7 @@ public class SerializationTest {
|
||||
}
|
||||
|
||||
private void doTest(String resourceName, int version, Class<?> expectedPayloadType) throws IOException {
|
||||
byte[] data = getBytes(resourceName);
|
||||
byte[] data = TestUtils.getBytes(resourceName);
|
||||
InputStream in = new ByteArrayInputStream(data);
|
||||
ObjectMessage object = Factory.getObjectMessage(version, in, data.length);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
@ -77,16 +78,4 @@ public class SerializationTest {
|
||||
assertArrayEquals(data, out.toByteArray());
|
||||
assertEquals(expectedPayloadType.getCanonicalName(), object.getPayload().getClass().getCanonicalName());
|
||||
}
|
||||
|
||||
private byte[] getBytes(String resourceName) throws IOException {
|
||||
InputStream in = getClass().getClassLoader().getResourceAsStream(resourceName);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len = in.read(buffer);
|
||||
while (len != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
len = in.read(buffer);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,14 @@ public class BytesTest {
|
||||
public static final Random rnd = new Random();
|
||||
|
||||
@Test
|
||||
public void testIncrement() throws IOException {
|
||||
public void ensureExpandsCorrectly() {
|
||||
byte[] source = {1};
|
||||
byte[] expected = {0,1};
|
||||
assertArrayEquals(expected, Bytes.expand(source, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureIncrementCarryWorks() throws IOException {
|
||||
byte[] bytes = {0, -1};
|
||||
Bytes.inc(bytes);
|
||||
assertArrayEquals(TestUtils.int16(256), bytes);
|
||||
|
@ -29,6 +29,7 @@ public class StringsTest {
|
||||
|
||||
@Test
|
||||
public void testHexString() {
|
||||
assertEquals("0x48656c6c6f21", Strings.hex("Hello!".getBytes()));
|
||||
assertEquals("48656c6c6f21", Strings.hex("Hello!".getBytes()).toString());
|
||||
assertEquals("0001", Strings.hex(new byte[]{0, 1}).toString());
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,13 @@
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* If there's ever a need for this in production code, it should be rewritten to be more efficient.
|
||||
@ -28,4 +33,22 @@ public class TestUtils {
|
||||
Encode.int16(number, out);
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static ObjectMessage loadObjectMessage(int version, String resourceName) throws IOException {
|
||||
byte[] data = getBytes(resourceName);
|
||||
InputStream in = new ByteArrayInputStream(data);
|
||||
return Factory.getObjectMessage(version, in, data.length);
|
||||
}
|
||||
|
||||
public static byte[] getBytes(String resourceName) throws IOException {
|
||||
InputStream in = TestUtils.class.getClassLoader().getResourceAsStream(resourceName);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int len = in.read(buffer);
|
||||
while (len != -1) {
|
||||
out.write(buffer, 0, len);
|
||||
len = in.read(buffer);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package ch.dissem.bitmessage.inventory;
|
||||
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
import ch.dissem.bitmessage.ports.Inventory;
|
||||
@ -62,9 +63,41 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
|
||||
public ObjectMessage getObject(InventoryVector vector) {
|
||||
try {
|
||||
Statement stmt = getConnection().createStatement();
|
||||
ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = " + vector);
|
||||
ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = X'" + vector + "'");
|
||||
if (rs.next()) {
|
||||
Blob data = rs.getBlob("data");
|
||||
return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length());
|
||||
} else {
|
||||
LOG.info("Object requested that we don't have. IV: " + vector);
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ObjectMessage> getObjects(long stream, long version, ObjectType type) {
|
||||
try {
|
||||
StringBuilder query = new StringBuilder("SELECT data, version FROM Inventory WHERE 1=1");
|
||||
if (stream >= 0) {
|
||||
query.append(" AND stream = ").append(stream);
|
||||
}
|
||||
if (version >= 0) {
|
||||
query.append(" AND version = ").append(version);
|
||||
}
|
||||
if (type != null) {
|
||||
query.append(" AND type = ").append(type.getNumber());
|
||||
}
|
||||
Statement stmt = getConnection().createStatement();
|
||||
ResultSet rs = stmt.executeQuery(query.toString());
|
||||
List<ObjectMessage> result = new LinkedList<>();
|
||||
while (rs.next()) {
|
||||
Blob data = rs.getBlob("data");
|
||||
result.add(Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()));
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new RuntimeException(e);
|
||||
|
@ -17,6 +17,7 @@
|
||||
package ch.dissem.bitmessage.inventory;
|
||||
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.ports.Inventory;
|
||||
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
|
||||
@ -43,6 +44,11 @@ public class SimpleInventory implements Inventory {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ObjectMessage> getObjects(long stream, long version, ObjectType type) {
|
||||
return new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeObject(ObjectMessage object) {
|
||||
throw new NotImplementedException();
|
||||
|
@ -91,6 +91,7 @@ public class Connection implements Runnable {
|
||||
switch (state) {
|
||||
case ACTIVE:
|
||||
receiveMessage(msg.getPayload());
|
||||
sendQueue();
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -121,13 +122,18 @@ public class Connection implements Runnable {
|
||||
}
|
||||
} catch (SocketTimeoutException e) {
|
||||
if (state == ACTIVE) {
|
||||
sendQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendQueue() {
|
||||
LOG.debug("Sending " + sendingQueue.size() + " messages to node " + node);
|
||||
for (MessagePayload msg = sendingQueue.poll(); msg != null; msg = sendingQueue.poll()) {
|
||||
send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveMessage(MessagePayload messagePayload) {
|
||||
switch (messagePayload.getCommand()) {
|
||||
@ -140,11 +146,10 @@ public class Connection implements Runnable {
|
||||
break;
|
||||
case GETDATA:
|
||||
GetData getData = (GetData) messagePayload;
|
||||
// for (InventoryVector iv : getData.getInventory()) {
|
||||
// ObjectMessage om = ctx.getInventory().getObject(iv);
|
||||
// sendingQueue.offer(om);
|
||||
// }
|
||||
LOG.error("Node requests data!!!! This shouldn't happen, the hash is done wrong!!!");
|
||||
for (InventoryVector iv : getData.getInventory()) {
|
||||
ObjectMessage om = ctx.getInventory().getObject(iv);
|
||||
if (om != null) sendingQueue.offer(om);
|
||||
}
|
||||
break;
|
||||
case OBJECT:
|
||||
ObjectMessage objectMessage = (ObjectMessage) messagePayload;
|
||||
@ -201,5 +206,12 @@ public class Connection implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public void offer(InventoryVector iv) {
|
||||
LOG.debug("Offering " + iv + " to node " + node.toString());
|
||||
sendingQueue.offer(new Inv.Builder()
|
||||
.addInventoryVector(iv)
|
||||
.build());
|
||||
}
|
||||
|
||||
public enum State {SERVER, CLIENT, ACTIVE, DISCONNECTED}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package ch.dissem.bitmessage.networking;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.BitmessageContext.ContextHolder;
|
||||
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.ports.NetworkHandler;
|
||||
import org.slf4j.Logger;
|
||||
@ -40,9 +40,9 @@ import static ch.dissem.bitmessage.networking.Connection.State.*;
|
||||
*/
|
||||
public class NetworkNode implements NetworkHandler, ContextHolder {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class);
|
||||
private BitmessageContext ctx;
|
||||
private final ExecutorService pool;
|
||||
private final List<Connection> connections = new LinkedList<>();
|
||||
private BitmessageContext ctx;
|
||||
private ServerSocket serverSocket;
|
||||
private Thread connectionManager;
|
||||
|
||||
@ -137,7 +137,15 @@ public class NetworkNode implements NetworkHandler, ContextHolder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(final ObjectPayload payload) {
|
||||
// TODO: sendingQueue.add(message);
|
||||
public void offer(final InventoryVector iv) {
|
||||
// TODO:
|
||||
// - should offer to (random) 8 nodes during 8 seconds (if possible)
|
||||
// - should probably offer later if no connection available at the moment?
|
||||
synchronized (connections) {
|
||||
LOG.debug(connections.size() + " connections available to offer " + iv);
|
||||
for (Connection connection : connections) {
|
||||
connection.offer(iv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user