diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java index b31c9f5..63f9663 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java @@ -16,10 +16,7 @@ package ch.dissem.bitmessage.entity; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; import static ch.dissem.bitmessage.utils.Decode.bytes; @@ -65,4 +62,12 @@ public class CustomMessage implements MessagePayload { "Programmer: did you forget to override #write()?"); } } + + public static CustomMessage error(String message) { + try { + return new CustomMessage(("ERROR\n" + message).getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java index e2fd170..909d3dd 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java @@ -16,6 +16,7 @@ package ch.dissem.bitmessage.ports; +import ch.dissem.bitmessage.entity.CustomMessage; import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.utils.Property; @@ -34,7 +35,18 @@ public interface NetworkHandler { * An implementation should disconnect if either the timeout is reached or the returned thread is interrupted. *

*/ - Future synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds); + Future synchronize(InetAddress server, int port, MessageListener listener, long timeoutInSeconds); + + /** + * Send a custom message to a specific node (that should implement handling for this message type) and returns + * the response, which in turn is expected to be a {@link CustomMessage}. + * + * @param server the node's address + * @param port the node's port + * @param request the request + * @return the response + */ + CustomMessage send(InetAddress server, int port, CustomMessage request); /** * Start a full network node, accepting incoming connections and relaying objects. diff --git a/extensions/src/main/java/ch/dissem/bitmessage/extensions/pow/ProofOfWorkRequest.java b/extensions/src/main/java/ch/dissem/bitmessage/extensions/pow/ProofOfWorkRequest.java index b247b59..2ef2f9e 100644 --- a/extensions/src/main/java/ch/dissem/bitmessage/extensions/pow/ProofOfWorkRequest.java +++ b/extensions/src/main/java/ch/dissem/bitmessage/extensions/pow/ProofOfWorkRequest.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATE; import static ch.dissem.bitmessage.utils.Decode.*; /** @@ -35,7 +36,11 @@ public class ProofOfWorkRequest implements Streamable { private final Request request; private final byte[] data; - private ProofOfWorkRequest(BitmessageAddress sender, byte[] initialHash, Request request, byte[] data) { + public ProofOfWorkRequest(BitmessageAddress sender, byte[] initialHash, Request request) { + this(sender, initialHash, request, new byte[0]); + } + + public ProofOfWorkRequest(BitmessageAddress sender, byte[] initialHash, Request request, byte[] data) { this.sender = sender; this.initialHash = initialHash; this.request = request; @@ -76,11 +81,8 @@ public class ProofOfWorkRequest implements Streamable { public enum Request { CALCULATE, - QUERY, - ERROR, - OK, - QUEUED, CALCULATING, + QUERY, COMPLETE } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 66e6c70..94f382d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java index a95adba..4d14fe2 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -260,11 +260,6 @@ public class Connection { ctx.getNodeRegistry().offerAddresses(addr.getAddresses()); break; case CUSTOM: - MessagePayload response = ctx.getCustomCommandHandler().handle((CustomMessage) messagePayload); - if (response != null) { - send(response); - } - break; case VERACK: case VERSION: throw new RuntimeException("Unexpectedly received '" + messagePayload.getCommand() + "' command"); @@ -400,6 +395,13 @@ public class Connection { break; } break; + case CUSTOM: + MessagePayload response = ctx.getCustomCommandHandler().handle((CustomMessage) msg.getPayload()); + if (response != null) { + send(response); + } + disconnect(); + break; default: throw new NodeException("Command 'version' or 'verack' expected, but was '" + msg.getPayload().getCommand() + "'"); diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java index 3944378..e934bdf 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -18,8 +18,12 @@ package ch.dissem.bitmessage.networking; import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.InternalContext.ContextHolder; +import ch.dissem.bitmessage.entity.CustomMessage; +import ch.dissem.bitmessage.entity.NetworkMessage; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; +import ch.dissem.bitmessage.exception.NodeException; +import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.NetworkHandler; import ch.dissem.bitmessage.utils.Collections; import ch.dissem.bitmessage.utils.Property; @@ -71,9 +75,9 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } @Override - public Future synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds) { + public Future synchronize(InetAddress server, int port, MessageListener listener, long timeoutInSeconds) { try { - Connection connection = Connection.sync(ctx, trustedHost, port, listener, timeoutInSeconds); + Connection connection = Connection.sync(ctx, server, port, listener, timeoutInSeconds); Future reader = pool.submit(connection.getReader()); pool.execute(connection.getWriter()); return reader; @@ -82,6 +86,27 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } } + @Override + public CustomMessage send(InetAddress server, int port, CustomMessage request) { + try (Socket socket = new Socket(server, port)) { + socket.setSoTimeout(Connection.READ_TIMEOUT); + new NetworkMessage(request).write(socket.getOutputStream()); + NetworkMessage networkMessage = Factory.getNetworkMessage(3, socket.getInputStream()); + if (networkMessage != null && networkMessage.getPayload() instanceof CustomMessage) { + return (CustomMessage) networkMessage.getPayload(); + } else { + if (networkMessage == null) { + throw new NodeException("No response from node " + server); + } else { + throw new NodeException("Unexpected response from node " + + server + ": " + networkMessage.getPayload().getCommand()); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @Override public void start(final MessageListener listener) { if (listener == null) { diff --git a/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java b/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java index 5ed9025..a2ee560 100644 --- a/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java +++ b/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java @@ -72,14 +72,14 @@ public class WifExporterTest { @Test public void testAddIdentity() throws Exception { - String expected = "[BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn]\n" + - "label = Nuked Address\n" + - "enabled = true\n" + - "decoy = false\n" + - "noncetrialsperbyte = 320\n" + - "payloadlengthextrabytes = 14000\n" + - "privsigningkey = 5KU2gbe9u4rKJ8PHYb1rvwMnZnAJj4gtV5GLwoYckeYzygWUzB9\n" + - "privencryptionkey = 5KHd4c6cavd8xv4kzo3PwnVaYuBgEfg7voPQ5V97aZKgpYBXGck\n\n"; + String expected = "[BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn]" + System.lineSeparator() + + "label = Nuked Address" + System.lineSeparator() + + "enabled = true" + System.lineSeparator() + + "decoy = false" + System.lineSeparator() + + "noncetrialsperbyte = 320" + System.lineSeparator() + + "payloadlengthextrabytes = 14000" + System.lineSeparator() + + "privsigningkey = 5KU2gbe9u4rKJ8PHYb1rvwMnZnAJj4gtV5GLwoYckeYzygWUzB9" + System.lineSeparator() + + "privencryptionkey = 5KHd4c6cavd8xv4kzo3PwnVaYuBgEfg7voPQ5V97aZKgpYBXGck" + System.lineSeparator() + System.lineSeparator(); importer = new WifImporter(ctx, expected); exporter.addIdentity(importer.getIdentities().get(0)); assertEquals(expected, exporter.toString());