First implementation of extended message encoding. Works as far as the PyBitmessage implementation, with some additional code.

This commit is contained in:
Christian Basler 2016-12-11 14:02:32 +01:00
parent e1dcbbf19c
commit 6d67598a40
11 changed files with 567 additions and 181 deletions

View File

@ -18,11 +18,13 @@ package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.payload.Msg; import ch.dissem.bitmessage.entity.payload.Msg;
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature; import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
import ch.dissem.bitmessage.entity.valueobject.Attachment; import ch.dissem.bitmessage.entity.valueobject.extended.Attachment;
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding; import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.entity.valueobject.extended.Message;
import ch.dissem.bitmessage.exception.ApplicationException; import ch.dissem.bitmessage.exception.ApplicationException;
import ch.dissem.bitmessage.factory.ExtendedEncodingFactory;
import ch.dissem.bitmessage.factory.Factory; 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;
@ -333,10 +335,10 @@ public class Plaintext implements Streamable {
Scanner s = new Scanner(new ByteArrayInputStream(message), "UTF-8"); Scanner s = new Scanner(new ByteArrayInputStream(message), "UTF-8");
String firstLine = s.nextLine(); String firstLine = s.nextLine();
if (encoding == EXTENDED.code) { if (encoding == EXTENDED.code) {
if (getExtendedData().getMessage() == null) { if (Message.TYPE.equals(getExtendedData().getType())) {
return null; return ((Message) extendedData.getContent()).getSubject();
} else { } else {
return extendedData.getMessage().getSubject(); return null;
} }
} else if (encoding == SIMPLE.code) { } else if (encoding == SIMPLE.code) {
return firstLine.substring("Subject:".length()).trim(); return firstLine.substring("Subject:".length()).trim();
@ -349,10 +351,10 @@ public class Plaintext implements Streamable {
public String getText() { public String getText() {
if (encoding == EXTENDED.code) { if (encoding == EXTENDED.code) {
if (getExtendedData().getMessage() == null) { if (Message.TYPE.equals(getExtendedData().getType())) {
return null; return ((Message) extendedData.getContent()).getBody();
} else { } else {
return extendedData.getMessage().getBody(); return null;
} }
} else { } else {
try { try {
@ -370,24 +372,24 @@ public class Plaintext implements Streamable {
protected ExtendedEncoding getExtendedData() { protected ExtendedEncoding getExtendedData() {
if (extendedData == null && encoding == EXTENDED.code) { if (extendedData == null && encoding == EXTENDED.code) {
// TODO: make sure errors are properly handled // TODO: make sure errors are properly handled
extendedData = ExtendedEncoding.unzip(message); extendedData = ExtendedEncodingFactory.getInstance().unzip(message);
} }
return extendedData; return extendedData;
} }
public List<InventoryVector> getParents() { public List<InventoryVector> getParents() {
if (getExtendedData() == null || extendedData.getMessage() == null) { if (Message.TYPE.equals(getExtendedData().getType())) {
return Collections.emptyList(); return ((Message) extendedData.getContent()).getParents();
} else { } else {
return extendedData.getMessage().getParents(); return Collections.emptyList();
} }
} }
public List<Attachment> getFiles() { public List<Attachment> getFiles() {
if (getExtendedData() == null || extendedData.getMessage() == null) { if (Message.TYPE.equals(getExtendedData().getType())) {
return Collections.emptyList(); return ((Message) extendedData.getContent()).getFiles();
} else { } else {
return extendedData.getMessage().getFiles(); return Collections.emptyList();
} }
} }

View File

@ -4,35 +4,38 @@ import ch.dissem.bitmessage.exception.ApplicationException;
import org.msgpack.core.MessagePack; import org.msgpack.core.MessagePack;
import org.msgpack.core.MessagePacker; import org.msgpack.core.MessagePacker;
import org.msgpack.core.MessageUnpacker; import org.msgpack.core.MessageUnpacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
/** /**
* Extended encoding message object. * Extended encoding message object.
*/ */
public class ExtendedEncoding implements Serializable { public class ExtendedEncoding implements Serializable {
private static final long serialVersionUID = 3876871488247305200L; private static final long serialVersionUID = 3876871488247305200L;
private static final Logger LOG = LoggerFactory.getLogger(ExtendedEncoding.class);
private Message message; private ExtendedType content;
public ExtendedEncoding(Message message) { public ExtendedEncoding(ExtendedType content) {
this.message = message; this.content = content;
} }
private ExtendedEncoding() { public String getType() {
if (content == null) {
return null;
} else {
return content.getType();
}
} }
public Message getMessage() { public ExtendedType getContent() {
return message; return content;
} }
public byte[] zip() { public byte[] zip() {
@ -40,10 +43,7 @@ public class ExtendedEncoding implements Serializable {
DeflaterOutputStream zipper = new DeflaterOutputStream(out)) { DeflaterOutputStream zipper = new DeflaterOutputStream(out)) {
MessagePacker packer = MessagePack.newDefaultPacker(zipper); MessagePacker packer = MessagePack.newDefaultPacker(zipper);
// FIXME: this should work for trivial cases content.pack(packer);
if (message != null) {
message.pack(packer);
}
packer.close(); packer.close();
zipper.close(); zipper.close();
return out.toByteArray(); return out.toByteArray();
@ -52,152 +52,28 @@ public class ExtendedEncoding implements Serializable {
} }
} }
public static ExtendedEncoding unzip(byte[] zippedData) {
ExtendedEncoding result = new ExtendedEncoding();
try (InflaterInputStream unzipper = new InflaterInputStream(new ByteArrayInputStream(zippedData))) {
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(unzipper);
int mapSize = unpacker.unpackMapHeader();
for (int i = 0; i < mapSize; i++) {
String key = unpacker.unpackString();
switch (key) {
case "":
switch (unpacker.unpackString()) {
case "message":
result.message = new Message();
break;
}
break;
case "subject":
result.message.subject = unpacker.unpackString();
break;
case "body":
result.message.body = unpacker.unpackString();
break;
default:
break;
}
}
} catch (IOException e) {
throw new ApplicationException(e);
}
return result;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
ExtendedEncoding that = (ExtendedEncoding) o; ExtendedEncoding that = (ExtendedEncoding) o;
return Objects.equals(message, that.message); return Objects.equals(content, that.content);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(message); return Objects.hash(content);
} }
public static class Message implements Serializable { public interface Unpacker<T extends ExtendedType> {
private static final long serialVersionUID = -2724977231484285467L; String getType();
private String subject; T unpack(MessageUnpacker unpacker, int size);
private String body;
private List<InventoryVector> parents;
private List<Attachment> files;
private Message() {
parents = Collections.emptyList();
files = Collections.emptyList();
} }
private Message(Builder builder) { public interface ExtendedType extends Serializable {
subject = builder.subject; String getType();
body = builder.body;
parents = Collections.unmodifiableList(builder.parents);
files = Collections.unmodifiableList(builder.files);
}
public String getSubject() { void pack(MessagePacker packer) throws IOException;
return subject;
}
public String getBody() {
return body;
}
public List<InventoryVector> getParents() {
return parents;
}
public List<Attachment> getFiles() {
return files;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Message message = (Message) o;
return Objects.equals(subject, message.subject) &&
Objects.equals(body, message.body) &&
Objects.equals(parents, message.parents) &&
Objects.equals(files, message.files);
}
@Override
public int hashCode() {
return Objects.hash(subject, body, parents, files);
}
public void pack(MessagePacker packer) throws IOException {
packer.packMapHeader(3);
packer.packString("");
packer.packString("message");
packer.packString("subject");
packer.packString(subject);
packer.packString("body");
packer.packString(body);
}
public static class Builder {
private String subject;
private String body;
private List<InventoryVector> parents = new LinkedList<>();
private List<Attachment> files = new LinkedList<>();
private Builder() {
}
public Builder subject(String subject) {
this.subject = subject;
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder addParent(InventoryVector iv) {
parents.add(iv);
return this;
}
public Builder addFile(Attachment file) {
files.add(file);
return this;
}
public ExtendedEncoding build() {
return new ExtendedEncoding(new Message(this));
}
}
}
public static class Builder {
public Message.Builder message() {
return new Message.Builder();
}
// TODO: vote (etc.?)
} }
} }

View File

@ -1,4 +1,4 @@
package ch.dissem.bitmessage.entity.valueobject; package ch.dissem.bitmessage.entity.valueobject.extended;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
@ -48,7 +48,7 @@ public class Attachment implements Serializable {
return Objects.hash(name, data, type, disposition); return Objects.hash(name, data, type, disposition);
} }
private enum Disposition { public enum Disposition {
inline, attachment inline, attachment
} }
@ -83,6 +83,11 @@ public class Attachment implements Serializable {
return this; return this;
} }
public Builder disposition(Disposition disposition) {
this.disposition = disposition;
return this;
}
public Attachment build() { public Attachment build() {
Attachment attachment = new Attachment(); Attachment attachment = new Attachment();
attachment.type = this.type; attachment.type = this.type;

View File

@ -0,0 +1,257 @@
package ch.dissem.bitmessage.entity.valueobject.extended;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import org.msgpack.core.MessagePacker;
import org.msgpack.core.MessageUnpacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLConnection;
import java.nio.file.Files;
import java.util.*;
/**
* Extended encoding type 'message'. Properties 'parents' and 'files' not yet supported by PyBitmessage, so they might not work
* properly with future PyBitmessage implementations.
*/
public class Message implements ExtendedEncoding.ExtendedType {
private static final long serialVersionUID = -2724977231484285467L;
private static final Logger LOG = LoggerFactory.getLogger(Message.class);
public static final String TYPE = "message";
private String subject;
private String body;
private List<InventoryVector> parents;
private List<Attachment> files;
private Message(Builder builder) {
subject = builder.subject;
body = builder.body;
parents = Collections.unmodifiableList(builder.parents);
files = Collections.unmodifiableList(builder.files);
}
@Override
public String getType() {
return TYPE;
}
public String getSubject() {
return subject;
}
public String getBody() {
return body;
}
public List<InventoryVector> getParents() {
return parents;
}
public List<Attachment> getFiles() {
return files;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Message message = (Message) o;
return Objects.equals(subject, message.subject) &&
Objects.equals(body, message.body) &&
Objects.equals(parents, message.parents) &&
Objects.equals(files, message.files);
}
@Override
public int hashCode() {
return Objects.hash(subject, body, parents, files);
}
public void pack(MessagePacker packer) throws IOException {
int size = 3;
if (!files.isEmpty()) {
size++;
}
if (!parents.isEmpty()) {
size++;
}
packer.packMapHeader(size);
packer.packString("");
packer.packString("message");
packer.packString("subject");
packer.packString(subject);
packer.packString("body");
packer.packString(body);
if (!files.isEmpty()) {
packer.packString("files");
packer.packArrayHeader(files.size());
for (Attachment file : files) {
packer.packMapHeader(4);
packer.packString("name");
packer.packString(file.getName());
packer.packString("data");
packer.packBinaryHeader(file.getData().length);
packer.writePayload(file.getData());
packer.packString("type");
packer.packString(file.getType());
packer.packString("disposition");
packer.packString(file.getDisposition().name());
}
}
if (!parents.isEmpty()) {
packer.packString("parents");
packer.packArrayHeader(parents.size());
for (InventoryVector parent : parents) {
packer.packBinaryHeader(parent.getHash().length);
packer.writePayload(parent.getHash());
}
}
}
public static class Builder {
private String subject;
private String body;
private List<InventoryVector> parents = new LinkedList<>();
private List<Attachment> files = new LinkedList<>();
public Builder subject(String subject) {
this.subject = subject;
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder addParent(Plaintext parent) {
parents.add(parent.getInventoryVector());
return this;
}
public Builder addParent(InventoryVector iv) {
parents.add(iv);
return this;
}
public Builder addFile(File file, Attachment.Disposition disposition) {
try {
files.add(new Attachment.Builder()
.name(file.getName())
.disposition(disposition)
.type(URLConnection.guessContentTypeFromStream(new FileInputStream(file)))
.data(Files.readAllBytes(file.toPath()))
.build());
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
return this;
}
public Builder addFile(Attachment file) {
files.add(file);
return this;
}
public ExtendedEncoding build() {
return new ExtendedEncoding(new Message(this));
}
}
public static class Unpacker implements ExtendedEncoding.Unpacker<Message> {
@Override
public String getType() {
return TYPE;
}
@Override
public Message unpack(MessageUnpacker unpacker, int size) {
Message.Builder builder = new Message.Builder();
try {
for (int i = 0; i < size; i++) {
String key = unpacker.unpackString();
switch (key) {
case "subject":
builder.subject(unpacker.unpackString());
break;
case "body":
builder.body(unpacker.unpackString());
break;
case "parents":
builder.parents = unpackParents(unpacker);
break;
case "files":
builder.files = unpackFiles(unpacker);
break;
default:
LOG.error("Unexpected data with key: " + key);
break;
}
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
return new Message(builder);
}
private static List<InventoryVector> unpackParents(MessageUnpacker unpacker) throws IOException {
int size = unpacker.unpackArrayHeader();
List<InventoryVector> parents = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
int binarySize = unpacker.unpackBinaryHeader();
parents.add(new InventoryVector(unpacker.readPayload(binarySize)));
}
return parents;
}
private static List<Attachment> unpackFiles(MessageUnpacker unpacker) throws IOException {
int size = unpacker.unpackArrayHeader();
List<Attachment> files = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Attachment.Builder attachment = new Attachment.Builder();
int mapSize = unpacker.unpackMapHeader();
for (int j = 0; j < mapSize; j++) {
String key = unpacker.unpackString();
switch (key) {
case "name":
attachment.name(unpacker.unpackString());
break;
case "data":
int binarySize = unpacker.unpackBinaryHeader();
attachment.data(unpacker.readPayload(binarySize));
break;
case "type":
attachment.type(unpacker.unpackString());
break;
case "disposition":
String disposition = unpacker.unpackString();
switch (disposition) {
case "inline":
attachment.inline();
break;
case "attachment":
attachment.attachment();
break;
default:
LOG.debug("Unknown disposition: " + disposition);
break;
}
break;
default:
LOG.debug("Unknown file info '" + key + "' with data: " + unpacker.unpackValue());
break;
}
}
files.add(attachment.build());
}
return files;
}
}
}

View File

@ -0,0 +1,131 @@
package ch.dissem.bitmessage.entity.valueobject.extended;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import org.msgpack.core.MessagePacker;
import org.msgpack.core.MessageUnpacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Objects;
/**
* Extended encoding type 'vote'. Specification still outstanding, so this will need some work.
*/
public class Vote implements ExtendedEncoding.ExtendedType {
private static final long serialVersionUID = -8427038604209964837L;
private static final Logger LOG = LoggerFactory.getLogger(Vote.class);
public static final String TYPE = "vote";
private InventoryVector msgId;
private String vote;
private Vote(Builder builder) {
msgId = builder.msgId;
vote = builder.vote;
}
@Override
public String getType() {
return TYPE;
}
public InventoryVector getMsgId() {
return msgId;
}
public String getVote() {
return vote;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vote vote1 = (Vote) o;
return Objects.equals(msgId, vote1.msgId) &&
Objects.equals(vote, vote1.vote);
}
@Override
public int hashCode() {
return Objects.hash(msgId, vote);
}
public void pack(MessagePacker packer) throws IOException {
packer.packMapHeader(3);
packer.packString("");
packer.packString("vote");
packer.packString("msgId");
packer.packBinaryHeader(msgId.getHash().length);
packer.writePayload(msgId.getHash());
packer.packString("vote");
packer.packString(vote);
}
public static class Builder {
private InventoryVector msgId;
private String vote;
public ExtendedEncoding up(Plaintext message) {
msgId = message.getInventoryVector();
vote = "1";
return new ExtendedEncoding(new Vote(this));
}
public ExtendedEncoding down(Plaintext message) {
msgId = message.getInventoryVector();
vote = "1";
return new ExtendedEncoding(new Vote(this));
}
public Builder msgId(InventoryVector iv) {
this.msgId = iv;
return this;
}
public Builder vote(String vote) {
this.vote = vote;
return this;
}
public ExtendedEncoding build() {
return new ExtendedEncoding(new Vote(this));
}
}
public static class Unpacker implements ExtendedEncoding.Unpacker<Vote> {
@Override
public String getType() {
return TYPE;
}
@Override
public Vote unpack(MessageUnpacker unpacker, int size) {
Vote.Builder builder = new Vote.Builder();
try {
for (int i = 0; i < size; i++) {
String key = unpacker.unpackString();
switch (key) {
case "msgId":
int binarySize = unpacker.unpackBinaryHeader();
builder.msgId(new InventoryVector(unpacker.readPayload(binarySize)));
break;
case "vote":
builder.vote(unpacker.unpackString());
break;
default:
LOG.error("Unexpected data with key: " + key);
break;
}
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
return new Vote(builder);
}
}
}

View File

@ -0,0 +1,57 @@
package ch.dissem.bitmessage.factory;
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.extended.Message;
import ch.dissem.bitmessage.entity.valueobject.extended.Vote;
import ch.dissem.bitmessage.exception.ApplicationException;
import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.InflaterInputStream;
/**
* Factory that creates {@link ExtendedEncoding} objects from byte arrays. You can register your own types by adding a
* {@link ExtendedEncoding.Unpacker} using {@link #registerFactory(ExtendedEncoding.Unpacker)}.
*/
public class ExtendedEncodingFactory {
private static final Logger LOG = LoggerFactory.getLogger(ExtendedEncodingFactory.class);
private static final ExtendedEncodingFactory INSTANCE = new ExtendedEncodingFactory();
private Map<String, ExtendedEncoding.Unpacker<?>> factories = new HashMap<>();
private ExtendedEncodingFactory() {
registerFactory(new Message.Unpacker());
registerFactory(new Vote.Unpacker());
}
public void registerFactory(ExtendedEncoding.Unpacker<?> factory) {
factories.put(factory.getType(), factory);
}
public ExtendedEncoding unzip(byte[] zippedData) {
try (InflaterInputStream unzipper = new InflaterInputStream(new ByteArrayInputStream(zippedData))) {
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(unzipper);
int mapSize = unpacker.unpackMapHeader();
String key = unpacker.unpackString();
if (!"".equals(key)) {
LOG.error("Unexpected content: " + key);
return null;
}
String type = unpacker.unpackString();
ExtendedEncoding.Unpacker<?> factory = factories.get(type);
return new ExtendedEncoding(factory.unpack(unpacker, mapSize - 1));
} catch (IOException e) {
throw new ApplicationException(e);
}
}
public static ExtendedEncodingFactory getInstance() {
return INSTANCE;
}
}

View File

@ -16,6 +16,11 @@
package ch.dissem.bitmessage.utils; package ch.dissem.bitmessage.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
/** /**
* A helper class for working with byte arrays interpreted as unsigned big endian integers. * A helper class for working with byte arrays interpreted as unsigned big endian integers.
* This is one part due to the fact that Java doesn't support unsigned numbers, and another * This is one part due to the fact that Java doesn't support unsigned numbers, and another

File diff suppressed because one or more lines are too long

View File

@ -17,9 +17,9 @@
package ch.dissem.bitmessage.entity; package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.entity.valueobject.extended.Message;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestBase;
import ch.dissem.bitmessage.utils.TestUtils; import ch.dissem.bitmessage.utils.TestUtils;
@ -31,7 +31,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class SerializationTest extends TestBase { public class SerializationTest extends TestBase {
@ -104,7 +103,7 @@ public class SerializationTest extends TestBase {
Plaintext p1 = new Plaintext.Builder(MSG) Plaintext p1 = new Plaintext.Builder(MSG)
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
.to(TestUtils.loadContact()) .to(TestUtils.loadContact())
.message(new ExtendedEncoding.Builder().message() .message(new Message.Builder()
.subject("Subject") .subject("Subject")
.body("Message") .body("Message")
.build()) .build())
@ -154,7 +153,7 @@ public class SerializationTest extends TestBase {
public void ensureNetworkMessageIsSerializedAndDeserializedCorrectly() throws Exception { public void ensureNetworkMessageIsSerializedAndDeserializedCorrectly() throws Exception {
ArrayList<InventoryVector> ivs = new ArrayList<>(50000); ArrayList<InventoryVector> ivs = new ArrayList<>(50000);
for (int i = 0; i < 50000; i++) { for (int i = 0; i < 50000; i++) {
ivs.add(new InventoryVector(cryptography().randomBytes(32))); ivs.add(TestUtils.randomInventoryVector());
} }
Inv inv = new Inv.Builder().inventory(ivs).build(); Inv inv = new Inv.Builder().inventory(ivs).build();

View File

@ -20,6 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
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.Pubkey;
import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
@ -28,6 +29,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Random;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -35,6 +37,8 @@ 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.
*/ */
public class TestUtils { public class TestUtils {
public static final Random RANDOM = new Random();
public static byte[] int16(int number) throws IOException { public static byte[] int16(int number) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Encode.int16(number, out); Encode.int16(number, out);
@ -59,6 +63,12 @@ public class TestUtils {
return out.toByteArray(); return out.toByteArray();
} }
public static InventoryVector randomInventoryVector() {
byte[] bytes = new byte[32];
RANDOM.nextBytes(bytes);
return new InventoryVector(bytes);
}
public static InputStream getResource(String resourceName) { public static InputStream getResource(String resourceName) {
return TestUtils.class.getClassLoader().getResourceAsStream(resourceName); return TestUtils.class.getClassLoader().getResourceAsStream(resourceName);
} }

View File

@ -26,6 +26,7 @@ import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.AddressRepository;
import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.MessageRepository;
import ch.dissem.bitmessage.utils.TestUtils;
import ch.dissem.bitmessage.utils.UnixTime; import ch.dissem.bitmessage.utils.UnixTime;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -162,7 +163,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
@Test @Test
public void testSave() throws Exception { public void testSave() throws Exception {
Plaintext message = new Plaintext.Builder(MSG) Plaintext message = new Plaintext.Builder(MSG)
.IV(new InventoryVector(cryptography().randomBytes(32))) .IV(TestUtils.randomInventoryVector())
.from(identity) .from(identity)
.to(contactA) .to(contactA)
.message("Subject", "Message") .message("Subject", "Message")
@ -185,7 +186,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
public void testUpdate() throws Exception { public void testUpdate() throws Exception {
List<Plaintext> messages = repo.findMessages(Plaintext.Status.DRAFT, contactA); List<Plaintext> messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
Plaintext message = messages.get(0); Plaintext message = messages.get(0);
message.setInventoryVector(new InventoryVector(cryptography().randomBytes(32))); message.setInventoryVector(TestUtils.randomInventoryVector());
repo.save(message); repo.save(message);
messages = repo.findMessages(Plaintext.Status.DRAFT, contactA); messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
@ -206,7 +207,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
@Test @Test
public void ensureUnacknowledgedMessagesAreFoundForResend() throws Exception { public void ensureUnacknowledgedMessagesAreFoundForResend() throws Exception {
Plaintext message = new Plaintext.Builder(MSG) Plaintext message = new Plaintext.Builder(MSG)
.IV(new InventoryVector(cryptography().randomBytes(32))) .IV(TestUtils.randomInventoryVector())
.from(identity) .from(identity)
.to(contactA) .to(contactA)
.message("Subject", "Message") .message("Subject", "Message")