From 2fae90c43373f3255226bfa5870a9e7d11b8e2bb Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Sun, 8 Nov 2015 19:29:26 +0100 Subject: [PATCH 01/20] Some code for sending acknowledgements - some of it isn't tested - somehow the ack part seems to be empty, even though the flag should be set --- .../dissem/bitmessage/BitmessageContext.java | 4 +- .../bitmessage/DefaultMessageListener.java | 23 ++++-- .../ch/dissem/bitmessage/InternalContext.java | 75 ++++++++++++------- .../bitmessage/entity/BitmessageAddress.java | 8 ++ .../dissem/bitmessage/entity/Plaintext.java | 72 +++++++++++++++--- .../dissem/bitmessage/entity/payload/Ack.java | 33 ++++++++ .../bitmessage/entity/payload/Pubkey.java | 4 + .../bitmessage/entity/valueobject/Label.java | 1 + .../ch/dissem/bitmessage/factory/Factory.java | 7 ++ 9 files changed, 181 insertions(+), 46 deletions(-) create mode 100644 domain/src/main/java/ch/dissem/bitmessage/entity/payload/Ack.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 9f4d9a3..8603063 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -156,6 +156,7 @@ public class BitmessageContext { } else { LOG.info("Sending message."); msg.setStatus(DOING_PROOF_OF_WORK); + msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.OUTBOX)); ctx.getMessageRepository().save(msg); ctx.send( from, @@ -165,9 +166,6 @@ public class BitmessageContext { ctx.getNonceTrialsPerByte(to), ctx.getExtraBytes(to) ); - msg.setStatus(SENT); - msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.SENT)); - ctx.getMessageRepository().save(msg); } } }); diff --git a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java index e069704..c409327 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java +++ b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java @@ -118,15 +118,24 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { for (BitmessageAddress identity : ctx.getAddressRepo().getIdentities()) { try { msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey()); - msg.getPlaintext().setTo(identity); - if (!object.isSignatureValid(msg.getPlaintext().getFrom().getPubkey())) { + Plaintext plaintext = msg.getPlaintext(); + plaintext.setTo(identity); + if (!object.isSignatureValid(plaintext.getFrom().getPubkey())) { LOG.warn("Msg with IV " + object.getInventoryVector() + " was successfully decrypted, but signature check failed. Ignoring."); } else { - msg.getPlaintext().setStatus(RECEIVED); - msg.getPlaintext().addLabels(ctx.getMessageRepository().getLabels(Label.Type.INBOX, Label.Type.UNREAD)); - msg.getPlaintext().setInventoryVector(object.getInventoryVector()); - ctx.getMessageRepository().save(msg.getPlaintext()); - listener.receive(msg.getPlaintext()); + plaintext.setStatus(RECEIVED); + plaintext.addLabels(ctx.getMessageRepository().getLabels(Label.Type.INBOX, Label.Type.UNREAD)); + plaintext.setInventoryVector(object.getInventoryVector()); + ctx.getMessageRepository().save(plaintext); + listener.receive(plaintext); + + if (identity.has(Pubkey.Feature.DOES_ACK)) { + ObjectMessage ack = plaintext.getAckMessage(); + if (ack != null) { + ctx.getInventory().storeObject(ack); + ctx.getNetworkHandler().offer(ack.getInventoryVector()); + } + } } break; } catch (DecryptionFailedException ignore) { diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 7a89978..1614548 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -17,9 +17,8 @@ package ch.dissem.bitmessage; import ch.dissem.bitmessage.entity.*; -import ch.dissem.bitmessage.entity.payload.Broadcast; -import ch.dissem.bitmessage.entity.payload.GetPubkey; -import ch.dissem.bitmessage.entity.payload.ObjectPayload; +import ch.dissem.bitmessage.entity.payload.*; +import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.utils.Singleton; import ch.dissem.bitmessage.utils.UnixTime; @@ -29,6 +28,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.TreeSet; +import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT; import static ch.dissem.bitmessage.utils.UnixTime.DAY; /** @@ -162,39 +162,35 @@ public class InternalContext { public void send(final BitmessageAddress from, BitmessageAddress to, final ObjectPayload payload, final long timeToLive, final long nonceTrialsPerByte, final long extraBytes) { try { - if (to == null) to = from; + final BitmessageAddress recipient = (to != null ? to : from); long expires = UnixTime.now(+timeToLive); LOG.info("Expires at " + expires); final ObjectMessage object = new ObjectMessage.Builder() - .stream(to.getStream()) + .stream(recipient.getStream()) .expiresTime(expires) .payload(payload) .build(); if (object.isSigned()) { object.sign(from.getPrivateKey()); } - if (payload instanceof Broadcast) { - ((Broadcast) payload).encrypt(); - } else if (payload instanceof Encrypted) { - object.encrypt(to.getPubkey()); + if (payload instanceof Msg && recipient.has(Pubkey.Feature.DOES_ACK)) { + ObjectMessage ackMessage = ((Msg) payload).getPlaintext().getAckMessage(); + messageCallback.proofOfWorkStarted(payload); + security.doProofOfWork(ackMessage, networkNonceTrialsPerByte, networkExtraBytes, new ProofOfWorkEngine.Callback() { + @Override + public void onNonceCalculated(byte[] nonce) { + object.encrypt(recipient.getPubkey()); + security.doProofOfWork(object, nonceTrialsPerByte, extraBytes, new ProofOfWorkCallback(object, payload)); + } + }); + } else { + if (payload instanceof Broadcast) { + ((Broadcast) payload).encrypt(); + } else if (payload instanceof Encrypted) { + object.encrypt(recipient.getPubkey()); + } + security.doProofOfWork(object, nonceTrialsPerByte, extraBytes, new ProofOfWorkCallback(object, payload)); } - messageCallback.proofOfWorkStarted(payload); - security.doProofOfWork(object, nonceTrialsPerByte, extraBytes, - new ProofOfWorkEngine.Callback() { - @Override - public void onNonceCalculated(byte[] nonce) { - object.setNonce(nonce); - messageCallback.proofOfWorkCompleted(payload); - if (payload instanceof PlaintextHolder) { - Plaintext plaintext = ((PlaintextHolder) payload).getPlaintext(); - plaintext.setInventoryVector(object.getInventoryVector()); - messageRepository.save(plaintext); - } - inventory.storeObject(object); - networkHandler.offer(object.getInventoryVector()); - messageCallback.messageOffered(payload, object.getInventoryVector()); - } - }); } catch (IOException e) { throw new RuntimeException(e); } @@ -266,4 +262,31 @@ public class InternalContext { public interface ContextHolder { void setContext(InternalContext context); } + + private class ProofOfWorkCallback implements ProofOfWorkEngine.Callback { + private final ObjectMessage object; + private final ObjectPayload payload; + + private ProofOfWorkCallback(ObjectMessage object, ObjectPayload payload) { + this.object = object; + this.payload = payload; + } + + @Override + public void onNonceCalculated(byte[] nonce) { + object.setNonce(nonce); + messageCallback.proofOfWorkCompleted(payload); + if (payload instanceof PlaintextHolder) { + Plaintext plaintext = ((PlaintextHolder) payload).getPlaintext(); + plaintext.setInventoryVector(object.getInventoryVector()); + plaintext.setStatus(SENT); + plaintext.removeLabel(Label.Type.OUTBOX); + plaintext.addLabels(messageRepository.getLabels(Label.Type.SENT)); + messageRepository.save(plaintext); + } + inventory.storeObject(object); + networkHandler.offer(object.getInventoryVector()); + messageCallback.messageOffered(payload, object.getInventoryVector()); + } + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java index 0441e6e..380e431 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.entity.payload.Pubkey.Feature; import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.utils.AccessCounter; @@ -220,4 +221,11 @@ public class BitmessageAddress implements Serializable { public void setSubscribed(boolean subscribed) { this.subscribed = subscribed; } + + public boolean has(Feature feature) { + if (pubkey == null || feature == null) { + return false; + } + return feature.isActive(pubkey.getBehaviorBitfield()); + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java index eb0a60f..34ecd52 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -16,6 +16,7 @@ package ch.dissem.bitmessage.entity; +import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.factory.Factory; @@ -26,6 +27,8 @@ import ch.dissem.bitmessage.utils.UnixTime; import java.io.*; import java.util.*; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** * The unencrypted message to be sent by 'msg' or 'broadcast'. */ @@ -34,7 +37,8 @@ public class Plaintext implements Streamable { private final BitmessageAddress from; private final long encoding; private final byte[] message; - private final byte[] ack; + private final byte[] ackData; + private ObjectMessage ackMessage; private Object id; private InventoryVector inventoryVector; private BitmessageAddress to; @@ -53,7 +57,13 @@ public class Plaintext implements Streamable { to = builder.to; encoding = builder.encoding; message = builder.message; - ack = builder.ack; + ackData = builder.ackData; + if (builder.ackMessage != null) { + ackMessage = Factory.getObjectMessage( + 3, + new ByteArrayInputStream(builder.ackMessage), + builder.ackMessage.length); + } signature = builder.signature; status = builder.status; sent = builder.sent; @@ -159,8 +169,15 @@ public class Plaintext implements Streamable { Encode.varInt(message.length, out); out.write(message); if (type == Type.MSG) { - Encode.varInt(ack.length, out); - out.write(ack); + if (to.has(Pubkey.Feature.DOES_ACK)) { + ByteArrayOutputStream ack = new ByteArrayOutputStream(); + ackMessage.write(ack); + byte[] data = ack.toByteArray(); + Encode.varInt(data.length, out); + out.write(data); + } else { + Encode.varInt(0, out); + } } if (includeSignature) { if (signature == null) { @@ -234,7 +251,7 @@ public class Plaintext implements Streamable { return Objects.equals(encoding, plaintext.encoding) && Objects.equals(from, plaintext.from) && Arrays.equals(message, plaintext.message) && - Arrays.equals(ack, plaintext.ack) && + Arrays.equals(ackData, plaintext.ackData) && Arrays.equals(to.getRipe(), plaintext.to.getRipe()) && Arrays.equals(signature, plaintext.signature) && Objects.equals(status, plaintext.status) && @@ -245,21 +262,46 @@ public class Plaintext implements Streamable { @Override public int hashCode() { - return Objects.hash(from, encoding, message, ack, to, signature, status, sent, received, labels); + return Objects.hash(from, encoding, message, ackData, to, signature, status, sent, received, labels); } public void addLabels(Label... labels) { if (labels != null) { - Collections.addAll(this.labels, labels); + for (Label label : labels) { + this.labels.add(label); + } } } public void addLabels(Collection