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:
Christian Basler 2015-05-01 17:14:43 +02:00
parent 8d1466f6f4
commit a65907f13b
29 changed files with 414 additions and 138 deletions

View File

@ -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();
}
}
}
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -56,6 +56,11 @@ public class V2Pubkey extends Pubkey {
return 2;
}
@Override
public ObjectType getType() {
return ObjectType.PUBKEY;
}
@Override
public long getStream() {
return stream;

View File

@ -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;

View File

@ -62,6 +62,11 @@ public class V4Pubkey extends Pubkey {
return 4;
}
@Override
public ObjectType getType() {
return ObjectType.PUBKEY;
}
@Override
public long getStream() {
return stream;

View File

@ -103,7 +103,7 @@ public class NetworkAddress implements Streamable {
@Override
public String toString() {
return toInetAddress() + ":" + port;
return "[" + toInetAddress() + "]:" + port;
}
@Override

View File

@ -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 {

View File

@ -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();
}

View File

@ -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();

View File

@ -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);

View File

@ -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);

View File

@ -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));
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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}
}

View File

@ -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);
}
}
}
}