This breaks a lot of things, but it seems necessary. Implemented the resending mechanism and fixed many problems on the way, but tests and triggers are still to do.
This commit is contained in:
@ -167,13 +167,13 @@ public class BitmessageContext {
|
||||
LOG.info("Sending message.");
|
||||
ctx.getMessageRepository().save(msg);
|
||||
if (msg.getType() == MSG) {
|
||||
ctx.send(msg, TTL.msg());
|
||||
ctx.send(msg);
|
||||
} else {
|
||||
ctx.send(
|
||||
msg.getFrom(),
|
||||
to,
|
||||
Factory.getBroadcast(msg),
|
||||
TTL.msg()
|
||||
msg.getTTL()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,11 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public void receive(ObjectMessage object) throws IOException {
|
||||
ObjectPayload payload = object.getPayload();
|
||||
if (payload.getType() == null && payload instanceof GenericPayload) {
|
||||
receive((GenericPayload) payload);
|
||||
if (payload.getType() == null) {
|
||||
if (payload instanceof GenericPayload) {
|
||||
receive((GenericPayload) payload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (payload.getType()) {
|
||||
@ -115,7 +118,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
|
||||
for (Plaintext msg : messages) {
|
||||
ctx.getLabeler().markAsSending(msg);
|
||||
ctx.getMessageRepository().save(msg);
|
||||
ctx.send(msg, TTL.msg());
|
||||
ctx.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
@ -160,12 +161,13 @@ public class InternalContext {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void send(final Plaintext plaintext, final long timeToLive) {
|
||||
public void send(final Plaintext plaintext) {
|
||||
if (plaintext.getAckMessage() != null) {
|
||||
long expires = UnixTime.now(+timeToLive);
|
||||
long expires = UnixTime.now(+plaintext.getTTL());
|
||||
LOG.info("Expires at " + expires);
|
||||
proofOfWorkService.doProofOfWorkWithAck(plaintext, expires);
|
||||
} else {
|
||||
send(plaintext.getFrom(), plaintext.getTo(), new Msg(plaintext), timeToLive);
|
||||
send(plaintext.getFrom(), plaintext.getTo(), new Msg(plaintext), plaintext.getTTL());
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,6 +280,14 @@ public class InternalContext {
|
||||
}
|
||||
}
|
||||
|
||||
public void resendUnacknowledged() {
|
||||
List<Plaintext> messages = messageRepository.findMessagesToResend();
|
||||
for (Plaintext message : messages) {
|
||||
send(message);
|
||||
messageRepository.save(message);
|
||||
}
|
||||
}
|
||||
|
||||
public long getClientNonce() {
|
||||
return clientNonce;
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC
|
||||
|
||||
public void doProofOfWorkWithAck(Plaintext plaintext, long expirationTime) {
|
||||
final ObjectMessage ack = plaintext.getAckMessage();
|
||||
messageRepo.save(plaintext);
|
||||
Item item = new Item(ack, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES,
|
||||
expirationTime, plaintext);
|
||||
powRepo.putObject(item);
|
||||
@ -84,6 +85,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC
|
||||
Plaintext plaintext = messageRepo.getMessage(initialHash);
|
||||
if (plaintext != null) {
|
||||
plaintext.setInventoryVector(object.getInventoryVector());
|
||||
plaintext.updateNextTry();
|
||||
ctx.getLabeler().markAsSent(plaintext);
|
||||
messageRepo.save(plaintext);
|
||||
}
|
||||
|
@ -17,13 +17,14 @@
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.payload.Msg;
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||
import ch.dissem.bitmessage.exception.ApplicationException;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
import ch.dissem.bitmessage.utils.Decode;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
import ch.dissem.bitmessage.utils.TTL;
|
||||
import ch.dissem.bitmessage.utils.UnixTime;
|
||||
|
||||
import java.io.*;
|
||||
@ -54,6 +55,10 @@ public class Plaintext implements Streamable {
|
||||
private Set<Label> labels;
|
||||
private byte[] initialHash;
|
||||
|
||||
private long ttl;
|
||||
private int retries;
|
||||
private Long nextTry;
|
||||
|
||||
private Plaintext(Builder builder) {
|
||||
id = builder.id;
|
||||
inventoryVector = builder.inventoryVector;
|
||||
@ -74,6 +79,9 @@ public class Plaintext implements Streamable {
|
||||
sent = builder.sent;
|
||||
received = builder.received;
|
||||
labels = builder.labels;
|
||||
ttl = builder.ttl;
|
||||
retries = builder.retries;
|
||||
nextTry = builder.nextTry;
|
||||
}
|
||||
|
||||
public static Plaintext read(Type type, InputStream in) throws IOException {
|
||||
@ -96,7 +104,7 @@ public class Plaintext implements Streamable {
|
||||
.destinationRipe(type == Type.MSG ? Decode.bytes(in, 20) : null)
|
||||
.encoding(Decode.varInt(in))
|
||||
.message(Decode.varBytes(in))
|
||||
.ack(type == Type.MSG ? Decode.varBytes(in) : null);
|
||||
.ackMessage(type == Type.MSG ? Decode.varBytes(in) : null);
|
||||
}
|
||||
|
||||
public InventoryVector getInventoryVector() {
|
||||
@ -174,12 +182,10 @@ public class Plaintext implements Streamable {
|
||||
Encode.varInt(message.length, out);
|
||||
out.write(message);
|
||||
if (type == Type.MSG) {
|
||||
if (to.has(Pubkey.Feature.DOES_ACK) && getAckMessage() != null) {
|
||||
if (to.has(Feature.DOES_ACK) && getAckMessage() != null) {
|
||||
ByteArrayOutputStream ack = new ByteArrayOutputStream();
|
||||
getAckMessage().write(ack);
|
||||
byte[] data = ack.toByteArray();
|
||||
Encode.varInt(data.length, out);
|
||||
out.write(data);
|
||||
Encode.varBytes(ack.toByteArray(), out);
|
||||
} else {
|
||||
Encode.varInt(0, out);
|
||||
}
|
||||
@ -224,6 +230,30 @@ public class Plaintext implements Streamable {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public long getTTL() {
|
||||
return ttl;
|
||||
}
|
||||
|
||||
public int getRetries() {
|
||||
return retries;
|
||||
}
|
||||
|
||||
public Long getNextTry() {
|
||||
return nextTry;
|
||||
}
|
||||
|
||||
public void updateNextTry() {
|
||||
if (nextTry == null) {
|
||||
if (sent != null && to.has(Feature.DOES_ACK)) {
|
||||
nextTry = sent + ttl;
|
||||
retries++;
|
||||
}
|
||||
} else {
|
||||
nextTry = nextTry + (1 << retries) * ttl;
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
Scanner s = new Scanner(new ByteArrayInputStream(message), "UTF-8");
|
||||
String firstLine = s.nextLine();
|
||||
@ -272,9 +302,7 @@ public class Plaintext implements Streamable {
|
||||
|
||||
public void addLabels(Label... labels) {
|
||||
if (labels != null) {
|
||||
for (Label label : labels) {
|
||||
this.labels.add(label);
|
||||
}
|
||||
Collections.addAll(this.labels, labels);
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,6 +394,9 @@ public class Plaintext implements Streamable {
|
||||
private long received;
|
||||
private Status status;
|
||||
private Set<Label> labels = new HashSet<>();
|
||||
private long ttl;
|
||||
private int retries;
|
||||
private Long nextTry;
|
||||
|
||||
public Builder(Type type) {
|
||||
this.type = type;
|
||||
@ -459,14 +490,15 @@ public class Plaintext implements Streamable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ack(byte[] ack) {
|
||||
if (type != Type.MSG && ack != null) throw new IllegalArgumentException("ack only allowed for msg");
|
||||
public Builder ackMessage(byte[] ack) {
|
||||
if (type != Type.MSG && ack != null) throw new IllegalArgumentException("ackMessage only allowed for msg");
|
||||
this.ackMessage = ack;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ackData(byte[] ackData) {
|
||||
if (type != Type.MSG && ackData != null) throw new IllegalArgumentException("ack only allowed for msg");
|
||||
if (type != Type.MSG && ackData != null)
|
||||
throw new IllegalArgumentException("ackMessage only allowed for msg");
|
||||
this.ackData = ackData;
|
||||
return this;
|
||||
}
|
||||
@ -496,6 +528,21 @@ public class Plaintext implements Streamable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder ttl(long ttl) {
|
||||
this.ttl = ttl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder retries(int retries) {
|
||||
this.retries = retries;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder nextTry(Long nextTry) {
|
||||
this.nextTry = nextTry;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Plaintext build() {
|
||||
if (from == null) {
|
||||
from = new BitmessageAddress(Factory.createPubkey(
|
||||
@ -514,6 +561,9 @@ public class Plaintext implements Streamable {
|
||||
if (type == Type.MSG && ackMessage == null && ackData == null) {
|
||||
ackData = cryptography().randomBytes(Msg.ACK_LENGTH);
|
||||
}
|
||||
if (ttl <= 0) {
|
||||
ttl = TTL.msg();
|
||||
}
|
||||
return new Plaintext(this);
|
||||
}
|
||||
}
|
||||
|
@ -145,13 +145,13 @@ public class Version implements MessagePayload {
|
||||
private String userAgent;
|
||||
private long[] streamNumbers;
|
||||
|
||||
public Builder defaults() {
|
||||
public Builder defaults(long clientNonce) {
|
||||
version = BitmessageContext.CURRENT_VERSION;
|
||||
services = 1;
|
||||
timestamp = UnixTime.now();
|
||||
nonce = new Random().nextInt();
|
||||
userAgent = "/Jabit:0.0.1/";
|
||||
streamNumbers = new long[]{1};
|
||||
nonce = clientNonce;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -212,6 +212,6 @@ public class Factory {
|
||||
if (plaintext == null || plaintext.getAckData() == null)
|
||||
return null;
|
||||
GenericPayload ack = new GenericPayload(3, plaintext.getFrom().getStream(), plaintext.getAckData());
|
||||
return new ObjectMessage.Builder().objectType(MSG).payload(ack).expiresTime(UnixTime.now(+TTL.msg())).build();
|
||||
return new ObjectMessage.Builder().objectType(MSG).payload(ack).expiresTime(UnixTime.now(plaintext.getTTL())).build();
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ public interface MessageRepository {
|
||||
|
||||
List<Plaintext> findMessages(BitmessageAddress sender);
|
||||
|
||||
List<Plaintext> findMessagesToResend();
|
||||
|
||||
void save(Plaintext message);
|
||||
|
||||
void remove(Plaintext message);
|
||||
|
@ -82,7 +82,7 @@ public class SerializationTest extends TestBase {
|
||||
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
|
||||
.to(TestUtils.loadContact())
|
||||
.message("Subject", "Message")
|
||||
.ackData("ack".getBytes())
|
||||
.ackData("ackMessage".getBytes())
|
||||
.signature(new byte[0])
|
||||
.build();
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
@ -98,6 +98,32 @@ public class SerializationTest extends TestBase {
|
||||
assertEquals(p1, p2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensurePlaintextWithAckMessageIsSerializedAndDeserializedCorrectly() throws Exception {
|
||||
Plaintext p1 = new Plaintext.Builder(MSG)
|
||||
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
|
||||
.to(TestUtils.loadContact())
|
||||
.message("Subject", "Message")
|
||||
.ackData("ackMessage".getBytes())
|
||||
.signature(new byte[0])
|
||||
.build();
|
||||
ObjectMessage ackMessage1 = p1.getAckMessage();
|
||||
assertNotNull(ackMessage1);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
p1.write(out);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
Plaintext p2 = Plaintext.read(MSG, in);
|
||||
|
||||
// Received is automatically set on deserialization, so we'll need to set it to 0
|
||||
Field received = Plaintext.class.getDeclaredField("received");
|
||||
received.setAccessible(true);
|
||||
received.set(p2, 0L);
|
||||
|
||||
assertEquals(p1, p2);
|
||||
assertEquals(ackMessage1, p2.getAckMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureNetworkMessageIsSerializedAndDeserializedCorrectly() throws Exception {
|
||||
ArrayList<InventoryVector> ivs = new ArrayList<>(50000);
|
||||
|
@ -34,6 +34,10 @@ public class NodeRegistryTest {
|
||||
assertThat(registry.getKnownAddresses(10), empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Please note that this test fails if there is no internet connection,
|
||||
* as the initial nodes' IP addresses are determined by DNS lookup.
|
||||
*/
|
||||
@Test
|
||||
public void ensureGetKnownNodesForStream1YieldsResult() {
|
||||
assertThat(registry.getKnownAddresses(10, 1), hasSize(1));
|
||||
|
Reference in New Issue
Block a user