A simple command line application (WIP), and a few tests. Unfotrunately, receiving messages doesn't seem to work yet.

This commit is contained in:
Christian Basler 2015-05-22 20:51:57 +02:00
parent 648afbbc75
commit 6b3b361aa3
36 changed files with 757 additions and 298 deletions

View File

@ -0,0 +1,291 @@
package ch.dissem.bitmessage.demo;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.networking.NetworkNode;
import ch.dissem.bitmessage.repository.JdbcAddressRepository;
import ch.dissem.bitmessage.repository.JdbcInventory;
import ch.dissem.bitmessage.repository.JdbcMessageRepository;
import ch.dissem.bitmessage.repository.JdbcNodeRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Scanner;
/**
* A simple command line Bitmessage application
*/
public class Application {
private final static Logger LOG = LoggerFactory.getLogger(Application.class);
private final Scanner scanner;
private BitmessageContext ctx;
public Application() {
ctx = new BitmessageContext.Builder()
.addressRepo(new JdbcAddressRepository())
.inventory(new JdbcInventory())
.nodeRegistry(new JdbcNodeRegistry())
.networkHandler(new NetworkNode())
.messageRepo(new JdbcMessageRepository())
.port(48444)
.streams(1)
.build();
ctx.startup(new BitmessageContext.Listener() {
@Override
public void receive(Plaintext plaintext) {
try {
System.out.println(new String(plaintext.getMessage(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
LOG.error(e.getMessage(), e);
}
}
});
scanner = new Scanner(System.in);
String command;
do {
System.out.println();
System.out.println("available commands:");
System.out.println("i) identities");
System.out.println("c) contacts");
System.out.println("m) messages");
System.out.println("e) Exit");
command = nextCommand();
try {
switch (command) {
case "i": {
identities();
break;
}
case "c":
contacts();
break;
case "m":
messages();
break;
case "e":
break;
default:
System.out.println("Unknown command. Please try again.");
}
} catch (Exception e) {
LOG.debug(e.getMessage());
}
} while (!"e".equals(command));
LOG.info("Shutting down client");
ctx.shutdown();
}
private String nextCommand() {
return scanner.nextLine().trim().toLowerCase();
}
private void identities() {
String command;
List<BitmessageAddress> identities = ctx.addresses().getIdentities();
do {
System.out.println();
int i = 0;
for (BitmessageAddress identity : identities) {
i++;
System.out.print(i + ") ");
if (identity.getAlias() != null) {
System.out.println(identity.getAlias() + " (" + identity.getAddress() + ")");
} else {
System.out.println(identity.getAddress());
}
}
if (i == 0) {
System.out.println("You have no identities yet.");
}
System.out.println("a) create identity");
System.out.println("b) back");
command = nextCommand();
switch (command) {
case "a":
addIdentity();
break;
case "b":
return;
default:
try {
int index = Integer.parseInt(command) - 1;
address(identities.get(index));
} catch (NumberFormatException e) {
System.out.println("Unknown command. Please try again.");
}
}
} while (!"b".equals(command));
}
private void addIdentity() {
System.out.println();
BitmessageAddress identity = ctx.createIdentity(yesNo("would you like a shorter address? This will take some time to calculate."), Pubkey.Feature.DOES_ACK);
System.out.println("Please enter an alias for this identity, or an empty string for none");
String alias = nextCommand();
if (alias.length() > 0) {
identity.setAlias(alias);
}
ctx.addresses().save(identity);
}
private void contacts() {
String command;
List<BitmessageAddress> contacts = ctx.addresses().getContacts();
do {
System.out.println();
int i = 0;
for (BitmessageAddress contact : contacts) {
i++;
System.out.print(i + ") ");
if (contact.getAlias() != null) {
System.out.println(contact.getAlias() + " (" + contact.getAddress() + ")");
} else {
System.out.println(contact.getAddress());
}
}
if (i == 0) {
System.out.println("You have no contacts yet.");
}
System.out.println();
System.out.println("a) add contact");
System.out.println("b) back");
command = nextCommand();
switch (command) {
case "a":
addContact();
break;
case "b":
return;
default:
try {
int index = Integer.parseInt(command) - 1;
address(contacts.get(index));
} catch (NumberFormatException e) {
System.out.println("Unknown command. Please try again.");
}
}
} while (!"b".equals(command));
}
private void addContact() {
System.out.println();
System.out.println("Please enter the Bitmessage address you want to add");
try {
BitmessageAddress address = new BitmessageAddress(scanner.nextLine().trim());
System.out.println("Please enter an alias for this address, or an empty string for none");
String alias = scanner.nextLine().trim();
if (alias.length() > 0) {
address.setAlias(alias);
}
ctx.addContact(address);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
private void address(BitmessageAddress address) {
System.out.println();
if (address.getAlias() != null)
System.out.println(address.getAlias());
System.out.println(address.getAddress());
System.out.println("Stream: " + address.getStream());
System.out.println("Version: " + address.getVersion());
}
private void messages() {
String command;
List<Plaintext> messages = ctx.messages().findMessages(Plaintext.Status.RECEIVED);
do {
System.out.println();
int i = 0;
for (Plaintext message : messages) {
i++;
System.out.print(i + ") From: " + message.getFrom() + "; Subject: " + message.getSubject());
}
if (i == 0) {
System.out.println("You have no messages.");
}
System.out.println();
System.out.println("c) compose message");
System.out.println("b) back");
command = scanner.nextLine().trim();
switch (command) {
case "c":
compose();
break;
case "b":
return;
default:
try {
int index = Integer.parseInt(command);
show(messages.get(index));
} catch (NumberFormatException | IndexOutOfBoundsException e) {
System.out.println("Unknown command. Please try again.");
}
}
} while (!"b".equalsIgnoreCase(command));
}
private void show(Plaintext message) {
System.out.println();
System.out.println("From: " + message.getFrom());
System.out.println("To: " + message.getTo());
System.out.println("Subject: " + message.getSubject());
System.out.println();
System.out.println(message.getText());
System.out.println();
String command;
do {
System.out.printf("r) reply");
System.out.println("d) delete");
System.out.printf("b) back");
command = nextCommand();
switch (command) {
case "r":
compose(message.getTo(), message.getFrom(), "RE: " + message.getSubject());
break;
case "d":
ctx.messages().remove(message);
case "b":
return;
default:
System.out.println("Unknown command. Please try again.");
}
} while (!"b".equalsIgnoreCase(command));
}
private void compose() {
System.out.println();
System.out.println("TODO");
// TODO
}
private void compose(BitmessageAddress from, BitmessageAddress to, String subject) {
System.out.println();
System.out.println("TODO");
// TODO
}
private boolean yesNo(String question) {
String answer;
do {
System.out.println(question + " (y/n)");
answer = scanner.nextLine();
if ("y".equalsIgnoreCase(answer)) return true;
if ("n".equalsIgnoreCase(answer)) return false;
} while (true);
}
}

View File

@ -18,11 +18,12 @@ package ch.dissem.bitmessage.demo;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.networking.NetworkNode;
import ch.dissem.bitmessage.repository.JdbcAddressRepository; import ch.dissem.bitmessage.repository.JdbcAddressRepository;
import ch.dissem.bitmessage.repository.JdbcInventory; import ch.dissem.bitmessage.repository.JdbcInventory;
import ch.dissem.bitmessage.repository.JdbcMessageRepository; import ch.dissem.bitmessage.repository.JdbcMessageRepository;
import ch.dissem.bitmessage.repository.JdbcNodeRegistry; import ch.dissem.bitmessage.repository.JdbcNodeRegistry;
import ch.dissem.bitmessage.networking.NetworkNode;
import ch.dissem.bitmessage.utils.Base58; import ch.dissem.bitmessage.utils.Base58;
import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Encode;
import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.Security;
@ -31,48 +32,18 @@ import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
/** /**
* Created by chris on 06.04.15. * Created by chris on 06.04.15.
*/ */
public class Main { public class Main {
private final static Logger LOG = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
final BitmessageAddress address = new BitmessageAddress("BM-87hJ99tPAXxtetvnje7Z491YSvbEtBJVc5e"); final BitmessageAddress address = new BitmessageAddress("BM-87hJ99tPAXxtetvnje7Z491YSvbEtBJVc5e");
BitmessageContext ctx = new BitmessageContext.Builder() new Application();
.addressRepo(new JdbcAddressRepository())
.inventory(new JdbcInventory())
.nodeRegistry(new JdbcNodeRegistry())
.networkHandler(new NetworkNode())
.messageRepo(new JdbcMessageRepository())
.port(48444)
.streams(1)
.build();
// ctx.startup(new BitmessageContext.Listener() {
// @Override
// public void receive(Plaintext plaintext) {
// // TODO
// try {
// System.out.println(new String(plaintext.getMessage(), "UTF-8"));
// } catch (UnsupportedEncodingException e) {
// LOG.error(e.getMessage(), e);
// }
// }
// });
// 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.shutdown();
// //
// //
// List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY); // List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY);

View File

@ -17,16 +17,17 @@
package ch.dissem.bitmessage; package ch.dissem.bitmessage;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Encrypted;
import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.Plaintext.Encoding; import ch.dissem.bitmessage.entity.Plaintext.Encoding;
import ch.dissem.bitmessage.entity.payload.GetPubkey; import ch.dissem.bitmessage.entity.payload.GetPubkey;
import ch.dissem.bitmessage.entity.payload.Msg; import ch.dissem.bitmessage.entity.payload.Msg;
import ch.dissem.bitmessage.entity.payload.ObjectPayload; import ch.dissem.bitmessage.entity.payload.ObjectPayload;
import ch.dissem.bitmessage.entity.payload.ObjectType;
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature; import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.ports.*;
import ch.dissem.bitmessage.ports.NetworkHandler.MessageListener;
import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.Security;
import ch.dissem.bitmessage.utils.UnixTime; import ch.dissem.bitmessage.utils.UnixTime;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -34,10 +35,12 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.TreeSet; import java.util.TreeSet;
import static ch.dissem.bitmessage.entity.Plaintext.Status.*; import static ch.dissem.bitmessage.entity.Plaintext.Status.*;
import static ch.dissem.bitmessage.entity.payload.ObjectType.GET_PUBKEY;
import static ch.dissem.bitmessage.entity.payload.ObjectType.MSG;
import static ch.dissem.bitmessage.entity.payload.ObjectType.PUBKEY;
import static ch.dissem.bitmessage.utils.UnixTime.DAY; import static ch.dissem.bitmessage.utils.UnixTime.DAY;
/** /**
@ -53,8 +56,12 @@ public class BitmessageContext {
ctx = new InternalContext(builder); ctx = new InternalContext(builder);
} }
public List<BitmessageAddress> getIdentities() { public AddressRepository addresses() {
return ctx.getAddressRepo().getIdentities(); return ctx.getAddressRepo();
}
public MessageRepository messages() {
return ctx.getMessageRepository();
} }
public BitmessageAddress createIdentity(boolean shorter, Feature... features) { public BitmessageAddress createIdentity(boolean shorter, Feature... features) {
@ -66,6 +73,7 @@ public class BitmessageContext {
features features
)); ));
ctx.getAddressRepo().save(identity); ctx.getAddressRepo().save(identity);
ctx.sendPubkey(identity, identity.getStream());
return identity; return identity;
} }
@ -134,13 +142,23 @@ public class BitmessageContext {
} }
public void startup(Listener listener) { public void startup(Listener listener) {
ctx.getNetworkHandler().start(new DefaultMessageListener(ctx, listener)); MessageListener messageListener = new DefaultMessageListener(ctx, listener);
for (ObjectMessage object : ctx.getInventory().getObjects(0, 0, PUBKEY, MSG)) {
messageListener.receive(object);
}
ctx.getNetworkHandler().start(messageListener);
} }
public void shutdown() { public void shutdown() {
ctx.getNetworkHandler().stop(); ctx.getNetworkHandler().stop();
} }
public void addContact(BitmessageAddress contact) {
ctx.getAddressRepo().save(contact);
// TODO: search pubkey in inventory
ctx.requestPubkey(contact);
}
public interface Listener { public interface Listener {
void receive(Plaintext plaintext); void receive(Plaintext plaintext);
} }

View File

@ -17,13 +17,10 @@
package ch.dissem.bitmessage; package ch.dissem.bitmessage;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Encrypted;
import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.ports.NetworkHandler; import ch.dissem.bitmessage.ports.NetworkHandler;
import ch.dissem.bitmessage.utils.Security;
import ch.dissem.bitmessage.utils.UnixTime;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -47,6 +44,8 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
@Override @Override
public void receive(ObjectMessage object) { public void receive(ObjectMessage object) {
ObjectPayload payload = object.getPayload(); ObjectPayload payload = object.getPayload();
if (payload.getType() == null) return;
switch (payload.getType()) { switch (payload.getType()) {
case GET_PUBKEY: { case GET_PUBKEY: {
receive(object, (GetPubkey) payload); receive(object, (GetPubkey) payload);
@ -70,28 +69,8 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
protected void receive(ObjectMessage object, GetPubkey getPubkey) { protected void receive(ObjectMessage object, GetPubkey getPubkey) {
BitmessageAddress identity = ctx.getAddressRepo().findIdentity(getPubkey.getRipeTag()); BitmessageAddress identity = ctx.getAddressRepo().findIdentity(getPubkey.getRipeTag());
if (identity != null && identity.getPrivateKey() != null) { if (identity != null && identity.getPrivateKey() != null) {
try { LOG.debug("Got pubkey request for identity " + identity);
long expires = UnixTime.now(+28 * DAY); ctx.sendPubkey(identity, object.getStream());
LOG.info("Expires at " + expires);
ObjectMessage response = new ObjectMessage.Builder()
.stream(object.getStream())
.version(identity.getVersion())
.expiresTime(expires)
.payload(identity.getPubkey())
.build();
Security.doProofOfWork(response, ctx.getProofOfWorkEngine(),
ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes());
if (response.isSigned()) {
response.sign(identity.getPrivateKey());
}
if (response instanceof Encrypted) {
response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false));
}
ctx.getInventory().storeObject(response);
ctx.getNetworkHandler().offer(response.getInventoryVector());
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
} }
@ -109,6 +88,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
} }
if (address != null) { if (address != null) {
address.setPubkey(pubkey); address.setPubkey(pubkey);
LOG.debug("Got pubkey for contact " + address);
List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address); List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address);
for (Plaintext msg : messages) { for (Plaintext msg : messages) {
// TODO: send messages enqueued for this address // TODO: send messages enqueued for this address
@ -140,7 +120,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
ctx.getMessageRepository().save(msg.getPlaintext()); ctx.getMessageRepository().save(msg.getPlaintext());
listener.receive(msg.getPlaintext()); listener.receive(msg.getPlaintext());
break; break;
} catch (IOException ignore) { } catch (IOException | RuntimeException ignore) {
} }
} }
} }

View File

@ -19,6 +19,7 @@ package ch.dissem.bitmessage;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Encrypted; import ch.dissem.bitmessage.entity.Encrypted;
import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.payload.GetPubkey;
import ch.dissem.bitmessage.entity.payload.ObjectPayload; import ch.dissem.bitmessage.entity.payload.ObjectPayload;
import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.ports.*;
import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.Security;
@ -29,6 +30,8 @@ import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.TreeSet; import java.util.TreeSet;
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
/** /**
* The internal context should normally only be used for port implementations. If you need it in your client * The internal context should normally only be used for port implementations. If you need it in your client
* implementation, you're either doing something wrong, something very weird, or the BitmessageContext should * implementation, you're either doing something wrong, something very weird, or the BitmessageContext should
@ -160,6 +163,51 @@ public class InternalContext {
} }
} }
public void sendPubkey(BitmessageAddress identity, long targetStream) {
try {
long expires = UnixTime.now(+28 * DAY);
LOG.info("Expires at " + expires);
ObjectMessage response = new ObjectMessage.Builder()
.stream(targetStream)
.version(identity.getVersion())
.expiresTime(expires)
.payload(identity.getPubkey())
.build();
response.sign(identity.getPrivateKey());
response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false));
Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes);
if (response.isSigned()) {
response.sign(identity.getPrivateKey());
}
if (response instanceof Encrypted) {
response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false));
}
inventory.storeObject(response);
networkHandler.offer(response.getInventoryVector());
// TODO: save that the pubkey was just sent, and on which stream!
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void requestPubkey(BitmessageAddress contact) {
try {
long expires = UnixTime.now(+2 * DAY);
LOG.info("Expires at " + expires);
ObjectMessage response = new ObjectMessage.Builder()
.stream(contact.getStream())
.version(contact.getVersion())
.expiresTime(expires)
.payload(new GetPubkey(contact))
.build();
Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes);
inventory.storeObject(response);
networkHandler.offer(response.getInventoryVector());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public interface ContextHolder { public interface ContextHolder {
void setContext(InternalContext context); void setContext(InternalContext context);
} }

View File

@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects;
import static ch.dissem.bitmessage.utils.Decode.bytes; import static ch.dissem.bitmessage.utils.Decode.bytes;
import static ch.dissem.bitmessage.utils.Decode.varInt; import static ch.dissem.bitmessage.utils.Decode.varInt;
@ -180,4 +181,19 @@ public class BitmessageAddress {
public byte[] getTag() { public byte[] getTag() {
return tag; return tag;
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BitmessageAddress address = (BitmessageAddress) o;
return Objects.equals(version, address.version) &&
Objects.equals(stream, address.stream) &&
Arrays.equals(ripe, address.ripe);
}
@Override
public int hashCode() {
return Arrays.hashCode(ripe);
}
} }

View File

@ -60,15 +60,15 @@ public class NetworkMessage implements Streamable {
} }
@Override @Override
public void write(OutputStream stream) throws IOException { public void write(OutputStream out) throws IOException {
// magic // magic
Encode.int32(MAGIC, stream); Encode.int32(MAGIC, out);
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected) // ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
String command = payload.getCommand().name().toLowerCase(); String command = payload.getCommand().name().toLowerCase();
stream.write(command.getBytes("ASCII")); out.write(command.getBytes("ASCII"));
for (int i = command.length(); i < 12; i++) { for (int i = command.length(); i < 12; i++) {
stream.write('\0'); out.write('\0');
} }
ByteArrayOutputStream payloadStream = new ByteArrayOutputStream(); ByteArrayOutputStream payloadStream = new ByteArrayOutputStream();
@ -78,16 +78,16 @@ public class NetworkMessage implements Streamable {
// Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would // Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would
// ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are // ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are
// larger than this. // larger than this.
Encode.int32(payloadBytes.length, stream); Encode.int32(payloadBytes.length, out);
// checksum // checksum
try { try {
stream.write(getChecksum(payloadBytes)); out.write(getChecksum(payloadBytes));
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
// message payload // message payload
stream.write(payloadBytes); out.write(payloadBytes);
} }
} }

View File

@ -22,10 +22,7 @@ import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Decode;
import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Encode;
import java.io.IOException; import java.io.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.*; import java.util.*;
/** /**
@ -121,8 +118,8 @@ public class Plaintext implements Streamable {
Encode.varInt(from.getVersion(), out); Encode.varInt(from.getVersion(), out);
Encode.varInt(from.getStream(), out); Encode.varInt(from.getStream(), out);
Encode.int32(from.getPubkey().getBehaviorBitfield(), out); Encode.int32(from.getPubkey().getBehaviorBitfield(), out);
out.write(from.getPubkey().getSigningKey()); out.write(from.getPubkey().getSigningKey(), 1, 64);
out.write(from.getPubkey().getEncryptionKey()); out.write(from.getPubkey().getEncryptionKey(), 1, 64);
Encode.varInt(from.getPubkey().getNonceTrialsPerByte(), out); Encode.varInt(from.getPubkey().getNonceTrialsPerByte(), out);
Encode.varInt(from.getPubkey().getExtraBytes(), out); Encode.varInt(from.getPubkey().getExtraBytes(), out);
out.write(to.getRipe()); out.write(to.getRipe());
@ -167,6 +164,30 @@ public class Plaintext implements Streamable {
this.status = status; this.status = status;
} }
public String getSubject() {
Scanner s = new Scanner(new ByteArrayInputStream(message), "UTF-8");
String firstLine = s.nextLine();
if (encoding == 2) {
return firstLine.substring("Subject:".length()).trim();
} else if (firstLine.length() > 50) {
return firstLine.substring(0, 50).trim() + "...";
} else {
return firstLine;
}
}
public String getText() {
try {
String text = new String(message, "UTF-8");
if (encoding == 2) {
return text.substring(text.indexOf("\nBody:") + 6);
}
return text;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public enum Encoding { public enum Encoding {
IGNORE(0), TRIVIAL(1), SIMPLE(2); IGNORE(0), TRIVIAL(1), SIMPLE(2);
@ -176,23 +197,21 @@ public class Plaintext implements Streamable {
this.code = code; this.code = code;
} }
public static Encoding fromCode(long code) {
for (Encoding e : values()) {
if (e.getCode() == code) return e;
}
return null;
}
public long getCode() { public long getCode() {
return code; return code;
} }
} }
public enum Status { public enum Status {
// For sent messages
PUBKEY_REQUESTED, PUBKEY_REQUESTED,
DOING_PROOF_OF_WORK, DOING_PROOF_OF_WORK,
SENT, SENT,
ACKNOWLEDGED SENT_ACKNOWLEDGED,
// For received messages
RECEIVED,
READ
} }
public static final class Builder { public static final class Builder {
@ -340,7 +359,7 @@ public class Plaintext implements Streamable {
publicEncryptionKey, publicEncryptionKey,
nonceTrialsPerByte, nonceTrialsPerByte,
extraBytes, extraBytes,
Pubkey.Feature.features(behaviorBitfield) behaviorBitfield
)); ));
} }
if (to == null) { if (to == null) {
@ -349,4 +368,26 @@ public class Plaintext implements Streamable {
return new Plaintext(this); return new Plaintext(this);
} }
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Plaintext plaintext = (Plaintext) o;
return Objects.equals(encoding, plaintext.encoding) &&
Objects.equals(from, plaintext.from) &&
Arrays.equals(message, plaintext.message) &&
Arrays.equals(ack, plaintext.ack) &&
Arrays.equals(to.getRipe(), plaintext.to.getRipe()) &&
Arrays.equals(signature, plaintext.signature) &&
Objects.equals(status, plaintext.status) &&
Objects.equals(sent, plaintext.sent) &&
Objects.equals(received, plaintext.received) &&
Objects.equals(labels, plaintext.labels);
}
@Override
public int hashCode() {
return Objects.hash(from, encoding, message, ack, to, signature, status, sent, received, labels);
}
} }

View File

@ -28,16 +28,17 @@ import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.ECPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*; import java.io.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
/**
* Created by chris on 09.04.15.
*/
public class CryptoBox implements Streamable { public class CryptoBox implements Streamable {
private static final Logger LOG = LoggerFactory.getLogger(CryptoBox.class);
private final byte[] initializationVector; private final byte[] initializationVector;
private final int curveType; private final int curveType;
private final ECPoint R; private final ECPoint R;
@ -182,7 +183,7 @@ public class CryptoBox implements Streamable {
} }
public Builder curveType(int curveType) { public Builder curveType(int curveType) {
if (curveType != 0x2CA) System.out.println("Unexpected curve type " + curveType); if (curveType != 0x2CA) LOG.debug("Unexpected curve type " + curveType);
this.curveType = curveType; this.curveType = curveType;
return this; return this;
} }

View File

@ -16,6 +16,8 @@
package ch.dissem.bitmessage.entity.payload; package ch.dissem.bitmessage.entity.payload;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import static ch.dissem.bitmessage.utils.Security.ripemd160; import static ch.dissem.bitmessage.utils.Security.ripemd160;
@ -51,6 +53,10 @@ public abstract class Pubkey extends ObjectPayload {
return 0; return 0;
} }
public void writeUnencrypted(OutputStream out) throws IOException {
write(out);
}
protected byte[] add0x04(byte[] key) { protected byte[] add0x04(byte[] key) {
if (key.length == 65) return key; if (key.length == 65) return key;
byte[] result = new byte[65]; byte[] result = new byte[65];

View File

@ -22,6 +22,8 @@ import ch.dissem.bitmessage.utils.Encode;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays;
import java.util.Objects;
/** /**
* A version 3 public key. * A version 3 public key.
@ -100,7 +102,7 @@ public class V3Pubkey extends V2Pubkey {
private byte[] publicEncryptionKey; private byte[] publicEncryptionKey;
private long nonceTrialsPerByte; private long nonceTrialsPerByte;
private long extraBytes; private long extraBytes;
private byte[] signature; private byte[] signature = new byte[0];
public Builder() { public Builder() {
} }
@ -144,4 +146,22 @@ public class V3Pubkey extends V2Pubkey {
return new V3Pubkey(this); return new V3Pubkey(this);
} }
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
V3Pubkey pubkey = (V3Pubkey) o;
return Objects.equals(nonceTrialsPerByte, pubkey.nonceTrialsPerByte) &&
Objects.equals(extraBytes, pubkey.extraBytes) &&
stream == pubkey.stream &&
behaviorBitfield == pubkey.behaviorBitfield &&
Arrays.equals(publicSigningKey, pubkey.publicSigningKey) &&
Arrays.equals(publicEncryptionKey, pubkey.publicEncryptionKey);
}
@Override
public int hashCode() {
return Objects.hash(nonceTrialsPerByte, extraBytes);
}
} }

View File

@ -23,6 +23,7 @@ import ch.dissem.bitmessage.utils.Decode;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Arrays;
/** /**
* A version 4 public key. When version 4 pubkeys are created, most of the data in the pubkey is encrypted. This is * A version 4 public key. When version 4 pubkeys are created, most of the data in the pubkey is encrypted. This is
@ -43,15 +44,18 @@ public class V4Pubkey extends Pubkey implements Encrypted {
} }
public V4Pubkey(V3Pubkey decrypted) { public V4Pubkey(V3Pubkey decrypted) {
this.decrypted = decrypted;
this.stream = decrypted.stream; this.stream = decrypted.stream;
this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe()); this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe());
this.decrypted = decrypted;
} }
public static V4Pubkey read(InputStream in, long stream, int length) throws IOException { public static V4Pubkey read(InputStream in, long stream, int length, boolean encrypted) throws IOException {
if (encrypted)
return new V4Pubkey(stream, return new V4Pubkey(stream,
Decode.bytes(in, 32), Decode.bytes(in, 32),
CryptoBox.read(in, length - 32)); CryptoBox.read(in, length - 32));
else
return new V4Pubkey(V3Pubkey.read(in, stream));
} }
@Override @Override
@ -76,6 +80,11 @@ public class V4Pubkey extends Pubkey implements Encrypted {
encrypted.write(stream); encrypted.write(stream);
} }
@Override
public void writeUnencrypted(OutputStream out) throws IOException {
decrypted.write(out);
}
@Override @Override
public void writeBytesToSign(OutputStream out) throws IOException { public void writeBytesToSign(OutputStream out) throws IOException {
out.write(tag); out.write(tag);
@ -141,4 +150,25 @@ public class V4Pubkey extends Pubkey implements Encrypted {
public long getExtraBytes() { public long getExtraBytes() {
return decrypted.getExtraBytes(); return decrypted.getExtraBytes();
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
V4Pubkey v4Pubkey = (V4Pubkey) o;
if (stream != v4Pubkey.stream) return false;
if (!Arrays.equals(tag, v4Pubkey.tag)) return false;
return !(decrypted != null ? !decrypted.equals(v4Pubkey.decrypted) : v4Pubkey.decrypted != null);
}
@Override
public int hashCode() {
int result = (int) (stream ^ (stream >>> 32));
result = 31 * result + Arrays.hashCode(tag);
result = 31 * result + (decrypted != null ? decrypted.hashCode() : 0);
return result;
}
} }

View File

@ -18,6 +18,8 @@ package ch.dissem.bitmessage.entity.valueobject;
import ch.dissem.bitmessage.entity.Streamable; import ch.dissem.bitmessage.entity.Streamable;
import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.utils.Bytes; import ch.dissem.bitmessage.utils.Bytes;
import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Decode;
@ -76,7 +78,7 @@ public class PrivateKey implements Streamable {
int version = (int) Decode.varInt(is); int version = (int) Decode.varInt(is);
long stream = Decode.varInt(is); long stream = Decode.varInt(is);
int len = (int) Decode.varInt(is); int len = (int) Decode.varInt(is);
Pubkey pubkey = Factory.readPubkey(version, stream, is, len); Pubkey pubkey = Factory.readPubkey(version, stream, is, len, false);
len = (int) Decode.varInt(is); len = (int) Decode.varInt(is);
byte[] signingKey = Decode.bytes(is, len); byte[] signingKey = Decode.bytes(is, len);
len = (int) Decode.varInt(is); len = (int) Decode.varInt(is);
@ -97,16 +99,16 @@ public class PrivateKey implements Streamable {
} }
@Override @Override
public void write(OutputStream os) throws IOException { public void write(OutputStream out) throws IOException {
Encode.varInt(pubkey.getVersion(), os); Encode.varInt(pubkey.getVersion(), out);
Encode.varInt(pubkey.getStream(), os); Encode.varInt(pubkey.getStream(), out);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
pubkey.write(baos); pubkey.writeUnencrypted(baos);
Encode.varInt(baos.size(), os); Encode.varInt(baos.size(), out);
os.write(baos.toByteArray()); out.write(baos.toByteArray());
Encode.varInt(privateSigningKey.length, os); Encode.varInt(privateSigningKey.length, out);
os.write(privateSigningKey); out.write(privateSigningKey);
Encode.varInt(privateEncryptionKey.length, os); Encode.varInt(privateEncryptionKey.length, out);
os.write(privateEncryptionKey); out.write(privateEncryptionKey);
} }
} }

View File

@ -56,6 +56,12 @@ public class Factory {
public static Pubkey createPubkey(long version, long stream, byte[] publicSigningKey, byte[] publicEncryptionKey, public static Pubkey createPubkey(long version, long stream, byte[] publicSigningKey, byte[] publicEncryptionKey,
long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) {
return createPubkey(version, stream, publicSigningKey, publicEncryptionKey, nonceTrialsPerByte, extraBytes,
Pubkey.Feature.bitfield(features));
}
public static Pubkey createPubkey(long version, long stream, byte[] publicSigningKey, byte[] publicEncryptionKey,
long nonceTrialsPerByte, long extraBytes, int behaviourBitfield) {
if (publicSigningKey.length != 64 && publicSigningKey.length != 65) if (publicSigningKey.length != 64 && publicSigningKey.length != 65)
throw new IllegalArgumentException("64 bytes signing key expected, but it was " throw new IllegalArgumentException("64 bytes signing key expected, but it was "
+ publicSigningKey.length + " bytes long."); + publicSigningKey.length + " bytes long.");
@ -69,14 +75,14 @@ public class Factory {
.stream(stream) .stream(stream)
.publicSigningKey(publicSigningKey) .publicSigningKey(publicSigningKey)
.publicEncryptionKey(publicEncryptionKey) .publicEncryptionKey(publicEncryptionKey)
.behaviorBitfield(Pubkey.Feature.bitfield(features)) .behaviorBitfield(behaviourBitfield)
.build(); .build();
case 3: case 3:
return new V3Pubkey.Builder() return new V3Pubkey.Builder()
.stream(stream) .stream(stream)
.publicSigningKey(publicSigningKey) .publicSigningKey(publicSigningKey)
.publicEncryptionKey(publicEncryptionKey) .publicEncryptionKey(publicEncryptionKey)
.behaviorBitfield(Pubkey.Feature.bitfield(features)) .behaviorBitfield(behaviourBitfield)
.nonceTrialsPerByte(nonceTrialsPerByte) .nonceTrialsPerByte(nonceTrialsPerByte)
.extraBytes(extraBytes) .extraBytes(extraBytes)
.build(); .build();
@ -86,7 +92,7 @@ public class Factory {
.stream(stream) .stream(stream)
.publicSigningKey(publicSigningKey) .publicSigningKey(publicSigningKey)
.publicEncryptionKey(publicEncryptionKey) .publicEncryptionKey(publicEncryptionKey)
.behaviorBitfield(Pubkey.Feature.bitfield(features)) .behaviorBitfield(behaviourBitfield)
.nonceTrialsPerByte(nonceTrialsPerByte) .nonceTrialsPerByte(nonceTrialsPerByte)
.extraBytes(extraBytes) .extraBytes(extraBytes)
.build() .build()
@ -125,21 +131,21 @@ public class Factory {
return GetPubkey.read(stream, streamNumber, length, version); return GetPubkey.read(stream, streamNumber, length, version);
} }
public static Pubkey readPubkey(long version, long stream, InputStream is, int length) throws IOException { public static Pubkey readPubkey(long version, long stream, InputStream is, int length, boolean encrypted) throws IOException {
switch ((int) version) { switch ((int) version) {
case 2: case 2:
return V2Pubkey.read(is, stream); return V2Pubkey.read(is, stream);
case 3: case 3:
return V3Pubkey.read(is, stream); return V3Pubkey.read(is, stream);
case 4: case 4:
return V4Pubkey.read(is, stream, length); return V4Pubkey.read(is, stream, length, encrypted);
} }
LOG.debug("Unexpected pubkey version " + version + ", handling as generic payload object"); LOG.debug("Unexpected pubkey version " + version + ", handling as generic payload object");
return null; return null;
} }
private static ObjectPayload parsePubkey(long version, long streamNumber, InputStream stream, int length) throws IOException { private static ObjectPayload parsePubkey(long version, long streamNumber, InputStream stream, int length) throws IOException {
Pubkey pubkey = readPubkey(version, streamNumber, stream, length); Pubkey pubkey = readPubkey(version, streamNumber, stream, length, true);
return pubkey != null ? pubkey : GenericPayload.read(stream, streamNumber, length); return pubkey != null ? pubkey : GenericPayload.read(stream, streamNumber, length);
} }

View File

@ -32,7 +32,7 @@ public interface Inventory {
ObjectMessage getObject(InventoryVector vector); ObjectMessage getObject(InventoryVector vector);
List<ObjectMessage> getObjects(long stream, long version, ObjectType type); List<ObjectMessage> getObjects(long stream, long version, ObjectType... types);
void storeObject(ObjectMessage object); void storeObject(ObjectMessage object);

View File

@ -17,7 +17,7 @@
package ch.dissem.bitmessage.utils; package ch.dissem.bitmessage.utils;
import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ECParameters;
@ -35,6 +35,7 @@ import javax.crypto.spec.SecretKeySpec;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.*; import java.security.*;
import java.security.PrivateKey;
import java.security.spec.KeySpec; import java.security.spec.KeySpec;
import java.util.Arrays; import java.util.Arrays;

View File

@ -16,6 +16,8 @@
package ch.dissem.bitmessage.utils; package ch.dissem.bitmessage.utils;
import ch.dissem.bitmessage.entity.payload.ObjectType;
/** /**
* Created by chris on 13.04.15. * Created by chris on 13.04.15.
*/ */
@ -38,6 +40,15 @@ public class Strings {
return streamList; return streamList;
} }
public static StringBuilder join(ObjectType... types) {
StringBuilder streamList = new StringBuilder();
for (int i = 0; i < types.length; i++) {
if (i > 0) streamList.append(", ");
streamList.append(types[i].getNumber());
}
return streamList;
}
public static StringBuilder join(Object... objects) { public static StringBuilder join(Object... objects) {
StringBuilder streamList = new StringBuilder(); StringBuilder streamList = new StringBuilder();
for (int i = 0; i < objects.length; i++) { for (int i = 0; i < objects.length; i++) {

View File

@ -16,21 +16,23 @@
package ch.dissem.bitmessage; package ch.dissem.bitmessage;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.CryptoBox; import ch.dissem.bitmessage.entity.payload.CryptoBox;
import ch.dissem.bitmessage.entity.payload.GenericPayload; import ch.dissem.bitmessage.entity.payload.GenericPayload;
import ch.dissem.bitmessage.entity.payload.Msg;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.Security;
import org.junit.Ignore; import ch.dissem.bitmessage.utils.TestUtils;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.security.KeyPair;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Created by chris on 10.05.15.
*/
public class EncryptionTest { public class EncryptionTest {
@Test @Test
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException { public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException {
@ -43,4 +45,19 @@ public class EncryptionTest {
assertEquals(before, after); assertEquals(before, after);
} }
@Test
public void ensureMessageCanBeDecrypted() throws IOException {
PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey"));
BitmessageAddress identity = new BitmessageAddress(privateKey);
assertEquals("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8", identity.getAddress());
ObjectMessage object = TestUtils.loadObjectMessage(3, "V1Msg.payload");
Msg msg = (Msg) object.getPayload();
msg.decrypt(privateKey.getPrivateEncryptionKey());
Plaintext plaintext = msg.getPlaintext();
assertNotNull(plaintext);
assertEquals("Test", plaintext.getSubject());
assertEquals("Hallo, das ist ein Test von der v4-Adresse", plaintext.getText());
}
} }

View File

@ -18,15 +18,19 @@ package ch.dissem.bitmessage;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.Msg;
import ch.dissem.bitmessage.entity.payload.ObjectType; import ch.dissem.bitmessage.entity.payload.ObjectType;
import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.utils.TestUtils; import ch.dissem.bitmessage.utils.TestUtils;
import org.junit.Test; import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import java.util.Date;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.*;
public class SignatureTest { public class SignatureTest {
@Test @Test
@ -39,7 +43,6 @@ public class SignatureTest {
@Test @Test
public void ensureSigningWorks() throws IOException { public void ensureSigningWorks() throws IOException {
PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000); PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000);
BitmessageAddress address = new BitmessageAddress(privateKey);
ObjectMessage objectMessage = new ObjectMessage.Builder() ObjectMessage objectMessage = new ObjectMessage.Builder()
.objectType(ObjectType.PUBKEY) .objectType(ObjectType.PUBKEY)
@ -51,4 +54,25 @@ public class SignatureTest {
assertTrue(objectMessage.isSignatureValid(privateKey.getPubkey())); assertTrue(objectMessage.isSignatureValid(privateKey.getPubkey()));
} }
@Test
public void ensureMessageIsProperlySigned() throws IOException {
BitmessageAddress identity = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8");
ObjectMessage object = TestUtils.loadObjectMessage(3, "V1Msg.payload");
Msg msg = (Msg) object.getPayload();
msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey());
Plaintext plaintext = msg.getPlaintext();
assertEquals(0, object.getExpiresTime());
assertEquals(loadPubkey(), plaintext.getFrom().getPubkey());
assertNotNull(plaintext);
assertTrue(object.isSignatureValid(plaintext.getFrom().getPubkey()));
}
private V4Pubkey loadPubkey() throws IOException {
BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h");
ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload");
object.decrypt(address.getPubkeyDecryptionKey());
return (V4Pubkey) object.getPayload();
}
} }

View File

@ -17,7 +17,6 @@
package ch.dissem.bitmessage.entity; package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.utils.*; import ch.dissem.bitmessage.utils.*;

View File

@ -75,6 +75,23 @@ public class SerializationTest {
doTest("V1MsgStrangeData.payload", 1, GenericPayload.class); doTest("V1MsgStrangeData.payload", 1, GenericPayload.class);
} }
@Test
public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws IOException {
Plaintext p1 = new Plaintext.Builder()
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
.to(TestUtils.loadContact())
.encoding(Plaintext.Encoding.SIMPLE)
.message("Subject", "Message")
.ack("ack".getBytes())
.signature(new byte[0])
.build();
ByteArrayOutputStream out = new ByteArrayOutputStream();
p1.write(out);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
Plaintext p2 = Plaintext.read(in);
assertEquals(p1, p2);
}
private void doTest(String resourceName, int version, Class<?> expectedPayloadType) throws IOException { private void doTest(String resourceName, int version, Class<?> expectedPayloadType) throws IOException {
byte[] data = TestUtils.getBytes(resourceName); byte[] data = TestUtils.getBytes(resourceName);
InputStream in = new ByteArrayInputStream(data); InputStream in = new ByteArrayInputStream(data);

View File

@ -25,9 +25,8 @@ import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.util.Calendar;
import java.util.GregorianCalendar;
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
/** /**
@ -72,7 +71,8 @@ public class SecurityTest {
public void testProofOfWorkFails() throws IOException { public void testProofOfWorkFails() throws IOException {
ObjectMessage objectMessage = new ObjectMessage.Builder() ObjectMessage objectMessage = new ObjectMessage.Builder()
.nonce(new byte[8]) .nonce(new byte[8])
.expiresTime(UnixTime.now() + 300) // 5 minutes .expiresTime(UnixTime.now(+2 * DAY)) // 5 minutes
.objectType(0)
.payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0)) .payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0))
.build(); .build();
Security.checkProofOfWork(objectMessage, 1000, 1000); Security.checkProofOfWork(objectMessage, 1000, 1000);
@ -80,11 +80,10 @@ public class SecurityTest {
@Test @Test
public void testDoProofOfWork() throws IOException { public void testDoProofOfWork() throws IOException {
Calendar expires = new GregorianCalendar();
expires.add(1, Calendar.HOUR);
ObjectMessage objectMessage = new ObjectMessage.Builder() ObjectMessage objectMessage = new ObjectMessage.Builder()
.nonce(new byte[8]) .nonce(new byte[8])
.expiresTime(expires.getTimeInMillis() / 1000) .expiresTime(UnixTime.now(+2 * DAY))
.objectType(0)
.payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0)) .payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0))
.build(); .build();
Security.doProofOfWork(objectMessage, new MultiThreadedPOWEngine(), 1000, 1000); Security.doProofOfWork(objectMessage, new MultiThreadedPOWEngine(), 1000, 1000);

View File

@ -16,7 +16,10 @@
package ch.dissem.bitmessage.utils; package ch.dissem.bitmessage.utils;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -24,6 +27,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import static org.junit.Assert.assertEquals;
/** /**
* If there's ever a need for this in production code, it should be rewritten to be more efficient. * If there's ever a need for this in production code, it should be rewritten to be more efficient.
*/ */
@ -51,4 +56,23 @@ public class TestUtils {
} }
return out.toByteArray(); return out.toByteArray();
} }
public static InputStream getResource(String resourceName) {
return TestUtils.class.getClassLoader().getResourceAsStream(resourceName);
}
public static BitmessageAddress loadIdentity(String address) throws IOException {
PrivateKey privateKey = PrivateKey.read(TestUtils.getResource(address + ".privkey"));
BitmessageAddress identity = new BitmessageAddress(privateKey);
assertEquals(address, identity.getAddress());
return identity;
}
public static BitmessageAddress loadContact() throws IOException {
BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h");
ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload");
object.decrypt(address.getPubkeyDecryptionKey());
address.setPubkey((V4Pubkey) object.getPayload());
return address;
}
} }

View File

@ -18,12 +18,16 @@ package ch.dissem.bitmessage.repository;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.AddressRepository;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.sql.*; import java.sql.*;
import java.util.Arrays; import java.util.Arrays;
@ -62,7 +66,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
@Override @Override
public List<BitmessageAddress> getIdentities() { public List<BitmessageAddress> getIdentities() {
return find("private_signing_key IS NOT NULL"); return find("private_key IS NOT NULL");
} }
@Override @Override
@ -72,7 +76,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
@Override @Override
public List<BitmessageAddress> getContacts() { public List<BitmessageAddress> getContacts() {
return find("private_signing_key IS NULL"); return find("private_key IS NULL");
} }
private List<BitmessageAddress> find(String where) { private List<BitmessageAddress> find(String where) {
@ -91,7 +95,10 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
Blob publicKeyBlob = rs.getBlob("public_key"); Blob publicKeyBlob = rs.getBlob("public_key");
if (publicKeyBlob != null) { if (publicKeyBlob != null) {
Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(), Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(),
publicKeyBlob.getBinaryStream(), (int) publicKeyBlob.length()); publicKeyBlob.getBinaryStream(), (int) publicKeyBlob.length(), false);
if (address.getVersion() == 4) {
pubkey = new V4Pubkey((V3Pubkey) pubkey);
}
address.setPubkey(pubkey); address.setPubkey(pubkey);
} }
} }
@ -111,7 +118,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
Statement stmt = getConnection().createStatement(); Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM Address WHERE address='" + address.getAddress() + "'"); ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM Address WHERE address='" + address.getAddress() + "'");
rs.next(); rs.next();
return rs.getInt(0) > 0; return rs.getInt(1) > 0;
} catch (SQLException e) { } catch (SQLException e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
@ -136,21 +143,32 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
"UPDATE Address SET address=?, alias=?, public_key=?, private_key=?"); "UPDATE Address SET address=?, alias=?, public_key=?, private_key=?");
ps.setString(1, address.getAddress()); ps.setString(1, address.getAddress());
ps.setString(2, address.getAlias()); ps.setString(2, address.getAlias());
writeBlob(ps, 3, address.getPubkey()); writePubkey(ps, 3, address.getPubkey());
writeBlob(ps, 4, address.getPrivateKey()); writeBlob(ps, 4, address.getPrivateKey());
ps.executeUpdate(); ps.executeUpdate();
} }
private void insert(BitmessageAddress address) throws IOException, SQLException { private void insert(BitmessageAddress address) throws IOException, SQLException {
PreparedStatement ps = getConnection().prepareStatement( PreparedStatement ps = getConnection().prepareStatement(
"INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?, ?)"); "INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?)");
ps.setString(1, address.getAddress()); ps.setString(1, address.getAddress());
ps.setString(2, address.getAlias()); ps.setString(2, address.getAlias());
writeBlob(ps, 3, address.getPubkey()); writePubkey(ps, 3, address.getPubkey());
writeBlob(ps, 4, address.getPrivateKey()); writeBlob(ps, 4, address.getPrivateKey());
ps.executeUpdate(); ps.executeUpdate();
} }
protected void writePubkey(PreparedStatement ps, int parameterIndex, Pubkey data) throws SQLException, IOException {
if (data != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
data.writeUnencrypted(out);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
ps.setBlob(parameterIndex, in);
} else {
ps.setBlob(parameterIndex, (Blob) null);
}
}
@Override @Override
public void remove(BitmessageAddress address) { public void remove(BitmessageAddress address) {
try { try {

View File

@ -24,10 +24,7 @@ import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection; import java.sql.*;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/** /**
* Helper class that does Flyway migration, provides JDBC connections and some helper methods. * Helper class that does Flyway migration, provides JDBC connections and some helper methods.
@ -47,10 +44,14 @@ abstract class JdbcHelper {
} }
protected void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException { protected void writeBlob(PreparedStatement ps, int parameterIndex, Streamable data) throws SQLException, IOException {
if (data != null) {
ByteArrayOutputStream os = new ByteArrayOutputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream();
data.write(os); data.write(os);
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
ps.setBlob(parameterIndex, is); ps.setBlob(parameterIndex, is);
} else {
ps.setBlob(parameterIndex, (Blob) null);
}
} }
protected Connection getConnection() { protected Connection getConnection() {

View File

@ -52,10 +52,23 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
} }
return result; return result;
} }
private List<InventoryVector> getFullInventory(long... streams) {
List<InventoryVector> result = new LinkedList<>();
try {
Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE stream IN (" + join(streams) + ")");
while (rs.next()) {
result.add(new InventoryVector(rs.getBytes("hash")));
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);
}
return result;
}
@Override @Override
public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) {
offer.removeAll(getInventory(streams)); offer.removeAll(getFullInventory(streams));
return offer; return offer;
} }
@ -78,17 +91,17 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
} }
@Override @Override
public List<ObjectMessage> getObjects(long stream, long version, ObjectType type) { public List<ObjectMessage> getObjects(long stream, long version, ObjectType... types) {
try { try {
StringBuilder query = new StringBuilder("SELECT data, version FROM Inventory WHERE 1=1"); StringBuilder query = new StringBuilder("SELECT data, version FROM Inventory WHERE 1=1");
if (stream >= 0) { if (stream > 0) {
query.append(" AND stream = ").append(stream); query.append(" AND stream = ").append(stream);
} }
if (version >= 0) { if (version > 0) {
query.append(" AND version = ").append(version); query.append(" AND version = ").append(version);
} }
if (type != null) { if (types.length > 0) {
query.append(" AND type = ").append(type.getNumber()); query.append(" AND type IN (").append(join(types)).append(")");
} }
Statement stmt = getConnection().createStatement(); Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery(query.toString()); ResultSet rs = stmt.executeQuery(query.toString());

View File

@ -41,7 +41,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
List<String> result = new LinkedList<>(); List<String> result = new LinkedList<>();
try { try {
Statement stmt = getConnection().createStatement(); Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT label FROM Label ORDER BY order"); ResultSet rs = stmt.executeQuery("SELECT label FROM Label ORDER BY ord");
while (rs.next()) { while (rs.next()) {
result.add(rs.getString("label")); result.add(rs.getString("label"));
} }
@ -58,7 +58,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
@Override @Override
public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) { public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) {
return find("status='" + status.name() + "' AND to='" + recipient.getAddress() + "'"); return find("status='" + status.name() + "' AND recipient='" + recipient.getAddress() + "'");
} }
@Override @Override
@ -70,14 +70,14 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
List<Plaintext> result = new LinkedList<>(); List<Plaintext> result = new LinkedList<>();
try { try {
Statement stmt = getConnection().createStatement(); Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT \"id\", \"from\", \"to\", \"data\", \"sent\", \"received\", \"status\" FROM Message WHERE " + where); ResultSet rs = stmt.executeQuery("SELECT id, sender, recipient, data, sent, received, status FROM Message WHERE " + where);
while (rs.next()) { while (rs.next()) {
Blob data = rs.getBlob("data"); Blob data = rs.getBlob("data");
Plaintext.Builder builder = Plaintext.readWithoutSignature(data.getBinaryStream()); Plaintext.Builder builder = Plaintext.readWithoutSignature(data.getBinaryStream());
long id = rs.getLong("id"); long id = rs.getLong("id");
builder.id(id); builder.id(id);
builder.from(ctx.getAddressRepo().getAddress(rs.getString("from"))); builder.from(ctx.getAddressRepo().getAddress(rs.getString("sender")));
builder.to(ctx.getAddressRepo().getAddress(rs.getString("to"))); builder.to(ctx.getAddressRepo().getAddress(rs.getString("recipient")));
builder.sent(rs.getLong("sent")); builder.sent(rs.getLong("sent"));
builder.received(rs.getLong("received")); builder.received(rs.getLong("received"));
builder.status(Plaintext.Status.valueOf(rs.getString("status"))); builder.status(Plaintext.Status.valueOf(rs.getString("status")));
@ -94,7 +94,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
List<Label> result = new ArrayList<>(); List<Label> result = new ArrayList<>();
try { try {
Statement stmt = getConnection().createStatement(); Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT \"label\", \"color\" FROM Label WHERE id IN SELECT label_id FROM Message_Label WHERE message_id=" + messageId); ResultSet rs = stmt.executeQuery("SELECT label, color FROM Label WHERE id IN SELECT label_id FROM Message_Label WHERE message_id=" + messageId);
while (rs.next()) { while (rs.next()) {
result.add(new Label(rs.getString("label"), rs.getInt("color"))); result.add(new Label(rs.getString("label"), rs.getInt("color")));
} }
@ -151,7 +151,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
private void insert(Connection connection, Plaintext message) throws SQLException, IOException { private void insert(Connection connection, Plaintext message) throws SQLException, IOException {
PreparedStatement ps = connection.prepareStatement( PreparedStatement ps = connection.prepareStatement(
"INSERT INTO Message (\"from\", \"to\", \"data\", \"sent\", \"received\", \"status\") VALUES (?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); "INSERT INTO Message (sender, recipient, data, sent, received, status) VALUES (?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
ps.setString(1, message.getFrom().getAddress()); ps.setString(1, message.getFrom().getAddress());
ps.setString(2, message.getTo().getAddress()); ps.setString(2, message.getTo().getAddress());
writeBlob(ps, 3, message); writeBlob(ps, 3, message);
@ -168,7 +168,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
private void update(Connection connection, Plaintext message) throws SQLException, IOException { private void update(Connection connection, Plaintext message) throws SQLException, IOException {
PreparedStatement ps = connection.prepareStatement( PreparedStatement ps = connection.prepareStatement(
"UPDATE Message SET \"sent\"=?, \"received\"=?, \"status\"=?"); "UPDATE Message SET sent=?, received=?, status=?");
ps.setLong(1, message.getSent()); ps.setLong(1, message.getSent());
ps.setLong(2, message.getReceived()); ps.setLong(2, message.getReceived());
ps.setString(3, message.getStatus().name()); ps.setString(3, message.getStatus().name());

View File

@ -38,7 +38,7 @@ public class JdbcNodeRegistry extends JdbcHelper implements NodeRegistry {
List<NetworkAddress> result = new LinkedList<>(); List<NetworkAddress> result = new LinkedList<>();
try { try {
Statement stmt = getConnection().createStatement(); Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM Node WHERE Stream IN (" + join(streams) + ")"); ResultSet rs = stmt.executeQuery("SELECT * FROM Node WHERE stream IN (" + join(streams) + ")");
while (rs.next()) { while (rs.next()) {
// result.add(new NetworkAddress.Builder() // result.add(new NetworkAddress.Builder()
// .ipv6(rs.getBytes("ip")) // .ipv6(rs.getBytes("ip"))

View File

@ -1,61 +0,0 @@
/*
* 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.repository;
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;
import java.util.LinkedList;
import java.util.List;
/**
* Created by chris on 06.04.15.
*/
public class SimpleInventory implements Inventory {
@Override
public List<InventoryVector> getInventory(long... streams) {
return new LinkedList<>();
}
@Override
public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) {
return offer;
}
@Override
public ObjectMessage getObject(InventoryVector vector) {
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();
}
@Override
public void cleanup() {
throw new NotImplementedException();
}
}

View File

@ -1,37 +0,0 @@
/*
* 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.repository;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.ports.NodeRegistry;
import java.util.Collections;
import java.util.List;
/**
* Created by chris on 06.04.15.
*/
public class SimpleNodeRegistry implements NodeRegistry {
@Override
public List<NetworkAddress> getKnownAddresses(int limit, long... streams) {
return Collections.singletonList(new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(8444).build());
}
@Override
public void offerAddresses(List<NetworkAddress> addresses) {
}
}

View File

@ -1,9 +1,9 @@
CREATE TABLE Node ( CREATE TABLE Node (
"ip" BINARY(16) NOT NULL, ip BINARY(16) NOT NULL,
"port" INT NOT NULL, port INT NOT NULL,
"stream" BIGINT NOT NULL, stream BIGINT NOT NULL,
"services" BIGINT NOT NULL, services BIGINT NOT NULL,
"time" BIGINT NOT NULL, time BIGINT NOT NULL,
PRIMARY KEY ("ip", "port", "stream") PRIMARY KEY (ip, port, stream)
); );

View File

@ -1,8 +1,8 @@
CREATE TABLE Inventory ( CREATE TABLE Inventory (
"hash" BINARY(32) NOT NULL PRIMARY KEY, hash BINARY(32) NOT NULL PRIMARY KEY,
"stream" BIGINT NOT NULL, stream BIGINT NOT NULL,
"expires" BIGINT NOT NULL, expires BIGINT NOT NULL,
"data" BLOB NOT NULL, data BLOB NOT NULL,
"type" BIGINT NOT NULL, type BIGINT NOT NULL,
"version" BIGINT NOT NULL version BIGINT NOT NULL
); );

View File

@ -1,7 +1,7 @@
CREATE TABLE Address ( CREATE TABLE Address (
"address" VARCHAR(40) NOT NULL PRIMARY KEY, address VARCHAR(40) NOT NULL PRIMARY KEY,
"alias" VARCHAR(255), alias VARCHAR(255),
"public_key" BLOB, public_key BLOB,
"private_key" BLOB, private_key BLOB,
"subscribed" BIT DEFAULT '0' subscribed BIT DEFAULT '0'
); );

View File

@ -1,32 +1,35 @@
CREATE TABLE Message ( CREATE TABLE Message (
"id" BIGINT AUTO_INCREMENT PRIMARY KEY, id BIGINT AUTO_INCREMENT PRIMARY KEY,
"from" VARCHAR(40) NOT NULL, sender VARCHAR(40) NOT NULL,
"to" VARCHAR(40) NOT NULL, recipient VARCHAR(40) NOT NULL,
"data" BLOB NOT NULL, data BLOB NOT NULL,
"sent" BIGINT, sent BIGINT,
"received" BIGINT, received BIGINT,
"status" VARCHAR(20) NOT NULL status VARCHAR(20) NOT NULL,
FOREIGN KEY (sender) REFERENCES Address (address),
FOREIGN KEY (recipient) REFERENCES Address (address)
); );
CREATE TABLE Label ( CREATE TABLE Label (
"id" BIGINT AUTO_INCREMENT PRIMARY KEY, id BIGINT AUTO_INCREMENT PRIMARY KEY,
"label" VARCHAR(255) NOT NULL, label VARCHAR(255) NOT NULL,
"color" INT, color INT,
"order" BIGINT, ord BIGINT,
CONSTRAINT UC_label UNIQUE ("label"), CONSTRAINT UC_label UNIQUE (label),
CONSTRAINT UC_order UNIQUE ("order") CONSTRAINT UC_order UNIQUE (ord)
); );
CREATE TABLE Message_Label ( CREATE TABLE Message_Label (
"message_id" BIGINT NOT NULL, message_id BIGINT NOT NULL,
"label_id" BIGINT NOT NULL, label_id BIGINT NOT NULL,
PRIMARY KEY ("message_id", "label_id"), PRIMARY KEY (message_id, label_id),
FOREIGN KEY ("message_id") REFERENCES Message ("id"), FOREIGN KEY (message_id) REFERENCES Message (id),
FOREIGN KEY ("label_id") REFERENCES Label ("id") FOREIGN KEY (label_id) REFERENCES Label (id)
); );
INSERT INTO Label("label", "order") VALUES ('Inbox', 0); INSERT INTO Label(label, ord) VALUES ('Inbox', 0);
INSERT INTO Label("label", "order") VALUES ('Sent', 10); INSERT INTO Label(label, ord) VALUES ('Sent', 10);
INSERT INTO Label("label", "order") VALUES ('Drafts', 20); INSERT INTO Label(label, ord) VALUES ('Drafts', 20);
INSERT INTO Label("label", "order") VALUES ('Trash', 100); INSERT INTO Label(label, ord) VALUES ('Trash', 100);