Fixed tests & bugs, removed Ack payload type (a GenericPayload is now used)

- SystemTest don't work yet, sending messages seems broken
- ProofOfWorkService needs some work, the current solution is a hack (and might be the reason above tests are broken)
This commit is contained in:
Christian Basler 2016-05-02 11:11:29 +02:00
parent ea2cd7bf53
commit c7594795f0
18 changed files with 124 additions and 57 deletions

View File

@ -164,6 +164,7 @@ public class BitmessageContext {
if (msg.getFrom() == null || msg.getFrom().getPrivateKey() == null) {
throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key.");
}
msg.setStatus(Plaintext.Status.DRAFT);
BitmessageAddress to = msg.getTo();
if (to != null) {
if (to.getPubkey() == null) {

View File

@ -175,10 +175,13 @@ public class InternalContext {
object.sign(from.getPrivateKey());
}
if (payload instanceof Msg && recipient.has(Pubkey.Feature.DOES_ACK)) {
ObjectMessage ackMessage = ((Msg) payload).getPlaintext().getAckMessage();
final ObjectMessage ackMessage = ((Msg) payload).getPlaintext().getAckMessage();
cryptography.doProofOfWork(ackMessage, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES, new ProofOfWorkEngine.Callback() {
@Override
public void onNonceCalculated(byte[] initialHash, byte[] nonce) {
// FIXME: the message gets lost if calculation is cancelled
// (e.g. by terminating the application)
ackMessage.setNonce(nonce);
object.encrypt(recipient.getPubkey());
proofOfWorkService.doProofOfWork(recipient, object);
}

View File

@ -29,6 +29,8 @@ import ch.dissem.bitmessage.utils.Encode;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Objects;
import static ch.dissem.bitmessage.utils.Singleton.security;
@ -55,7 +57,7 @@ public class ObjectMessage implements MessagePayload {
expiresTime = builder.expiresTime;
objectType = builder.objectType;
version = builder.payload.getVersion();
stream = builder.streamNumber;
stream = builder.streamNumber > 0 ? builder.streamNumber : builder.payload.getStream();
payload = builder.payload;
}
@ -230,4 +232,29 @@ public class ObjectMessage implements MessagePayload {
return new ObjectMessage(this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ObjectMessage that = (ObjectMessage) o;
return expiresTime == that.expiresTime &&
objectType == that.objectType &&
version == that.version &&
stream == that.stream &&
Objects.equals(payload, that.payload);
}
@Override
public int hashCode() {
int result = Arrays.hashCode(nonce);
result = 31 * result + (int) (expiresTime ^ (expiresTime >>> 32));
result = 31 * result + (int) (objectType ^ (objectType >>> 32));
result = 31 * result + (int) (version ^ (version >>> 32));
result = 31 * result + (int) (stream ^ (stream >>> 32));
result = 31 * result + (payload != null ? payload.hashCode() : 0);
return result;
}
}

View File

@ -173,7 +173,7 @@ public class Plaintext implements Streamable {
Encode.varInt(message.length, out);
out.write(message);
if (type == Type.MSG) {
if (to.has(Pubkey.Feature.DOES_ACK)) {
if (to.has(Pubkey.Feature.DOES_ACK) && getAckMessage() != null) {
ByteArrayOutputStream ack = new ByteArrayOutputStream();
getAckMessage().write(ack);
byte[] data = ack.toByteArray();
@ -255,7 +255,7 @@ public class Plaintext implements Streamable {
return Objects.equals(encoding, plaintext.encoding) &&
Objects.equals(from, plaintext.from) &&
Arrays.equals(message, plaintext.message) &&
Arrays.equals(ackData, plaintext.ackData) &&
Objects.equals(getAckMessage(), plaintext.getAckMessage()) &&
Arrays.equals(to.getRipe(), plaintext.to.getRipe()) &&
Arrays.equals(signature, plaintext.signature) &&
Objects.equals(status, plaintext.status) &&

View File

@ -1,33 +0,0 @@
package ch.dissem.bitmessage.entity.payload;
import java.io.IOException;
import java.io.OutputStream;
/**
* Created by chrigu on 06.11.15.
*/
public class Ack extends ObjectPayload {
private final long stream;
private final byte[] data;
public Ack(long version, long stream, byte[] data) {
super(version);
this.stream = stream;
this.data = data;
}
@Override
public ObjectType getType() {
return ObjectType.MSG;
}
@Override
public long getStream() {
return stream;
}
@Override
public void write(OutputStream out) throws IOException {
out.write(data);
}
}

View File

@ -23,6 +23,7 @@ import ch.dissem.bitmessage.entity.PlaintextHolder;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import java.io.IOException;
import java.util.Objects;
import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST;
import static ch.dissem.bitmessage.utils.Singleton.security;
@ -96,4 +97,18 @@ public abstract class Broadcast extends ObjectPayload implements Encrypted, Plai
public boolean isDecrypted() {
return plaintext != null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Broadcast broadcast = (Broadcast) o;
return stream == broadcast.stream &&
(Objects.equals(encrypted, broadcast.encrypted) || Objects.equals(plaintext, broadcast.plaintext));
}
@Override
public int hashCode() {
return Objects.hash(stream);
}
}

View File

@ -39,7 +39,7 @@ public class GenericPayload extends ObjectPayload {
this.data = data;
}
public static GenericPayload read(long version, InputStream is, long stream, int length) throws IOException {
public static GenericPayload read(long version, long stream, InputStream is, int length) throws IOException {
return new GenericPayload(version, stream, Decode.bytes(is, length));
}

View File

@ -24,6 +24,7 @@ import ch.dissem.bitmessage.exception.DecryptionFailedException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
@ -108,4 +109,19 @@ public class Msg extends ObjectPayload implements Encrypted, PlaintextHolder {
if (encrypted == null) throw new IllegalStateException("Msg must be signed and encrypted before writing it.");
encrypted.write(out);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
Msg msg = (Msg) o;
return stream == msg.stream &&
(Objects.equals(encrypted, msg.encrypted) || Objects.equals(plaintext, msg.plaintext));
}
@Override
public int hashCode() {
return (int) stream;
}
}

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity.payload;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Streamable;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
import java.io.IOException;
import java.io.OutputStream;

View File

@ -31,6 +31,7 @@ import java.io.InputStream;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import static ch.dissem.bitmessage.entity.payload.ObjectType.MSG;
import static ch.dissem.bitmessage.utils.Singleton.security;
/**
@ -155,7 +156,7 @@ public class Factory {
}
// fallback: just store the message - we don't really care what it is
LOG.trace("Unexpected object type: " + objectType);
return GenericPayload.read(version, stream, streamNumber, length);
return GenericPayload.read(version, streamNumber, stream, length);
}
private static ObjectPayload parseGetPubkey(long version, long streamNumber, InputStream stream, int length) throws IOException {
@ -177,7 +178,7 @@ public class Factory {
private static ObjectPayload parsePubkey(long version, long streamNumber, InputStream stream, int length) throws IOException {
Pubkey pubkey = readPubkey(version, streamNumber, stream, length, true);
return pubkey != null ? pubkey : GenericPayload.read(version, stream, streamNumber, length);
return pubkey != null ? pubkey : GenericPayload.read(version, streamNumber, stream, length);
}
private static ObjectPayload parseMsg(long version, long streamNumber, InputStream stream, int length) throws IOException {
@ -192,7 +193,7 @@ public class Factory {
return V5Broadcast.read(stream, streamNumber, length);
default:
LOG.debug("Encountered unknown broadcast version " + version);
return GenericPayload.read(version, stream, streamNumber, length);
return GenericPayload.read(version, streamNumber, stream, length);
}
}
@ -208,7 +209,7 @@ public class Factory {
public static ObjectMessage createAck(Plaintext plaintext) {
if (plaintext == null || plaintext.getAckData() == null)
return null;
Ack ack = new Ack(3, plaintext.getFrom().getStream(), plaintext.getAckData());
return new ObjectMessage.Builder().payload(ack).build();
GenericPayload ack = new GenericPayload(3, plaintext.getFrom().getStream(), plaintext.getAckData());
return new ObjectMessage.Builder().objectType(MSG).payload(ack).build();
}
}

View File

@ -22,19 +22,22 @@ import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.ObjectType;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.ports.*;
import ch.dissem.bitmessage.utils.MessageMatchers;
import ch.dissem.bitmessage.utils.Singleton;
import ch.dissem.bitmessage.utils.TestUtils;
import ch.dissem.bitmessage.utils.*;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import java.util.Collections;
import java.util.stream.Collectors;
import static ch.dissem.bitmessage.entity.payload.ObjectType.*;
import static ch.dissem.bitmessage.utils.MessageMatchers.object;
import static ch.dissem.bitmessage.utils.Singleton.security;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.*;
@ -59,9 +62,41 @@ public class BitmessageContextTest {
.messageRepo(mock(MessageRepository.class))
.networkHandler(mock(NetworkHandler.class))
.nodeRegistry(mock(NodeRegistry.class))
.powRepo(mock(ProofOfWorkRepository.class))
.proofOfWorkEngine(mock(ProofOfWorkEngine.class))
.powRepo(spy(new ProofOfWorkRepository() {
Map<InventoryVector, Item> items = new HashMap<>();
@Override
public Item getItem(byte[] initialHash) {
return items.get(new InventoryVector(initialHash));
}
@Override
public List<byte[]> getItems() {
List<byte[]> result = new LinkedList<>();
for (InventoryVector iv : items.keySet()) {
result.add(iv.getHash());
}
return result;
}
@Override
public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
items.put(new InventoryVector(security().getInitialHash(object)), new Item(object, nonceTrialsPerByte, extraBytes));
}
@Override
public void removeObject(byte[] initialHash) {
items.remove(initialHash);
}
}))
.proofOfWorkEngine(spy(new ProofOfWorkEngine() {
@Override
public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) {
callback.onNonceCalculated(initialHash, new byte[8]);
}
}))
.build();
TTL.msg(2 * MINUTE);
}
@Test
@ -161,6 +196,7 @@ public class BitmessageContextTest {
public void ensureMessageIsSent() throws Exception {
ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), TestUtils.loadContact(),
"Subject", "Message");
assertEquals(1, ctx.internals().getProofOfWorkRepository().getItems().size());
verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce())
.putObject(object(MSG), eq(1000L), eq(1000L));
verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Plaintext.Type.MSG));

View File

@ -66,6 +66,7 @@ public class DefaultMessageListenerTest extends TestBase {
when(ctx.getMessageRepository()).thenReturn(messageRepo);
when(ctx.getInventory()).thenReturn(inventory);
when(ctx.getNetworkHandler()).thenReturn(networkHandler);
when(ctx.getLabeler()).thenReturn(mock(Labeler.class));
listener = new DefaultMessageListener(ctx, mock(Labeler.class), mock(BitmessageContext.Listener.class));
}

View File

@ -42,7 +42,7 @@ public class EncryptionTest extends TestBase {
PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000);
CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey());
GenericPayload after = GenericPayload.read(0, cryptoBox.decrypt(privateKey.getPrivateEncryptionKey()), 1, 100);
GenericPayload after = GenericPayload.read(0, 1, cryptoBox.decrypt(privateKey.getPrivateEncryptionKey()), 100);
assertEquals(before, after);
}

View File

@ -65,6 +65,7 @@ public class ProofOfWorkServiceTest {
when(ctx.getInventory()).thenReturn(inventory);
when(ctx.getNetworkHandler()).thenReturn(networkHandler);
when(ctx.getMessageRepository()).thenReturn(messageRepo);
when(ctx.getLabeler()).thenReturn(mock(Labeler.class));
proofOfWorkService = new ProofOfWorkService();
proofOfWorkService.setContext(ctx);

View File

@ -82,7 +82,7 @@ public class SerializationTest extends TestBase {
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
.to(TestUtils.loadContact())
.message("Subject", "Message")
.ack("ack".getBytes())
.ackData("ack".getBytes())
.signature(new byte[0])
.build();
ByteArrayOutputStream out = new ByteArrayOutputStream();

View File

@ -72,7 +72,7 @@ public class TestUtils {
public static BitmessageAddress loadContact() throws IOException, DecryptionFailedException {
BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h");
ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload");
ObjectMessage object = TestUtils.loadObjectMessage(3, "V4Pubkey.payload");
object.decrypt(address.getPublicDecryptionKey());
address.setPubkey((V4Pubkey) object.getPayload());
return address;

View File

@ -21,9 +21,7 @@ import java.io.IOException;
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -82,7 +80,7 @@ public class CryptographyTest {
.nonce(new byte[8])
.expiresTime(UnixTime.now(+28 * DAY))
.objectType(0)
.payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0))
.payload(GenericPayload.read(0, 1, new ByteArrayInputStream(new byte[0]), 0))
.build();
crypto.checkProofOfWork(objectMessage, 1000, 1000);
}
@ -93,7 +91,7 @@ public class CryptographyTest {
.nonce(new byte[8])
.expiresTime(UnixTime.now(+2 * MINUTE))
.objectType(0)
.payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0))
.payload(GenericPayload.read(0, 1, new ByteArrayInputStream(new byte[0]), 0))
.build();
final CallbackWaiter<byte[]> waiter = new CallbackWaiter<>();
crypto.doProofOfWork(objectMessage, 1000, 1000,

View File

@ -52,7 +52,7 @@ public class CryptoCustomMessageTest extends TestBase {
new CryptoCustomMessage.Reader<GenericPayload>() {
@Override
public GenericPayload read(BitmessageAddress ignore, InputStream in) throws IOException {
return GenericPayload.read(0, in, 1, 100);
return GenericPayload.read(0, 1, in, 100);
}
});
GenericPayload payloadAfter = messageAfter.decrypt(sendingIdentity.getPublicDecryptionKey());