diff --git a/cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java b/cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java index 41e2c37..377135c 100644 --- a/cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java +++ b/cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java @@ -51,19 +51,23 @@ import java.util.Arrays; */ public class BouncyCryptography extends AbstractCryptography { private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1"); + private static final String ALGORITHM_ECDSA = "ECDSA"; + private static final String PROVIDER = "BC"; static { java.security.Security.addProvider(new BouncyCastleProvider()); } public BouncyCryptography() { - super("BC"); + super(PROVIDER); } @Override public byte[] crypt(boolean encrypt, byte[] data, byte[] key_e, byte[] initializationVector) { - BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); - + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher( + new CBCBlockCipher(new AESEngine()), + new PKCS7Padding() + ); CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector); cipher.init(encrypt, params); @@ -105,9 +109,9 @@ public class BouncyCryptography extends AbstractCryptography { ECPoint Q = keyToPoint(pubkey.getSigningKey()); KeySpec keySpec = new ECPublicKeySpec(Q, spec); - PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec); + PublicKey publicKey = KeyFactory.getInstance(ALGORITHM_ECDSA, PROVIDER).generatePublic(keySpec); - Signature sig = Signature.getInstance("ECDSA", "BC"); + Signature sig = Signature.getInstance(ALGORITHM_ECDSA, PROVIDER); sig.initVerify(publicKey); sig.update(data); return sig.verify(signature); @@ -129,9 +133,10 @@ public class BouncyCryptography extends AbstractCryptography { BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey()); KeySpec keySpec = new ECPrivateKeySpec(d, spec); - java.security.PrivateKey privKey = KeyFactory.getInstance("ECDSA", "BC").generatePrivate(keySpec); + java.security.PrivateKey privKey = KeyFactory.getInstance(ALGORITHM_ECDSA, PROVIDER) + .generatePrivate(keySpec); - Signature sig = Signature.getInstance("ECDSA", "BC"); + Signature sig = Signature.getInstance(ALGORITHM_ECDSA, PROVIDER); sig.initSign(privKey); sig.update(data); return sig.sign(); diff --git a/cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java b/cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java index 066ca10..b90e1c8 100644 --- a/cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java +++ b/cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java @@ -51,19 +51,23 @@ import java.util.Arrays; */ public class SpongyCryptography extends AbstractCryptography { private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1"); + private static final String ALGORITHM_ECDSA = "ECDSA"; + private static final String PROVIDER = "SC"; static { java.security.Security.addProvider(new BouncyCastleProvider()); } public SpongyCryptography() { - super("SC"); + super(PROVIDER); } @Override public byte[] crypt(boolean encrypt, byte[] data, byte[] key_e, byte[] initializationVector) { - BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); - + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher( + new CBCBlockCipher(new AESEngine()), + new PKCS7Padding() + ); CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector); cipher.init(encrypt, params); @@ -105,9 +109,9 @@ public class SpongyCryptography extends AbstractCryptography { ECPoint Q = keyToPoint(pubkey.getSigningKey()); KeySpec keySpec = new ECPublicKeySpec(Q, spec); - PublicKey publicKey = KeyFactory.getInstance("ECDSA", "SC").generatePublic(keySpec); + PublicKey publicKey = KeyFactory.getInstance(ALGORITHM_ECDSA, PROVIDER).generatePublic(keySpec); - Signature sig = Signature.getInstance("ECDSA", "SC"); + Signature sig = Signature.getInstance(ALGORITHM_ECDSA, PROVIDER); sig.initVerify(publicKey); sig.update(data); return sig.verify(signature); @@ -129,9 +133,10 @@ public class SpongyCryptography extends AbstractCryptography { BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey()); KeySpec keySpec = new ECPrivateKeySpec(d, spec); - java.security.PrivateKey privKey = KeyFactory.getInstance("ECDSA", "SC").generatePrivate(keySpec); + java.security.PrivateKey privKey = KeyFactory.getInstance(ALGORITHM_ECDSA, PROVIDER) + .generatePrivate(keySpec); - Signature sig = Signature.getInstance("ECDSA", "SC"); + Signature sig = Signature.getInstance(ALGORITHM_ECDSA, PROVIDER); sig.initSign(privKey); sig.update(data); return sig.sign(); diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java index dab43f3..a6a6e1f 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -17,31 +17,33 @@ package ch.dissem.bitmessage.demo; import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.ports.MemoryNodeRegistry; import ch.dissem.bitmessage.repository.*; -import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.util.List; -import java.util.Scanner; + +import static ch.dissem.bitmessage.demo.CommandLine.COMMAND_BACK; +import static ch.dissem.bitmessage.demo.CommandLine.ERROR_UNKNOWN_COMMAND; /** * A simple command line Bitmessage application */ public class Application { private final static Logger LOG = LoggerFactory.getLogger(Application.class); - private final Scanner scanner; + private final CommandLine commandLine; private BitmessageContext ctx; - public Application(String syncServer, int syncPort) { + public Application(InetAddress syncServer, int syncPort) { JdbcConfig jdbcConfig = new JdbcConfig(); ctx = new BitmessageContext.Builder() .addressRepo(new JdbcAddressRepository(jdbcConfig)) @@ -68,7 +70,7 @@ public class Application { ctx.startup(); } - scanner = new Scanner(System.in); + commandLine = new CommandLine(); String command; do { @@ -84,7 +86,7 @@ public class Application { System.out.println("?) info"); System.out.println("e) exit"); - command = nextCommand(); + command = commandLine.nextCommand(); try { switch (command) { case "i": { @@ -106,10 +108,12 @@ public class Application { case "e": break; case "y": - ctx.synchronize(InetAddress.getByName(syncServer), syncPort, 120, true); + if (syncServer != null) { + ctx.synchronize(syncServer, syncPort, 120, true); + } break; default: - System.out.println("Unknown command. Please try again."); + System.out.println(ERROR_UNKNOWN_COMMAND); } } catch (Exception e) { LOG.debug(e.getMessage()); @@ -125,32 +129,16 @@ public class Application { System.out.println(ctx.status()); } - private String nextCommand() { - return scanner.nextLine().trim().toLowerCase(); - } - private void identities() { String command; List identities = ctx.addresses().getIdentities(); do { System.out.println(); - int i = 0; - for (BitmessageAddress identity : identities) { - i++; - System.out.print(i + ") "); - if (identity.getAlias() != null) { - System.out.println(identity.getAlias() + " (" + identity.getAddress() + ")"); - } else { - System.out.println(identity.getAddress()); - } - } - if (i == 0) { - System.out.println("You have no identities yet."); - } + commandLine.listAddresses(identities, "identities"); System.out.println("a) create identity"); - System.out.println("b) back"); + System.out.println(COMMAND_BACK); - command = nextCommand(); + command = commandLine.nextCommand(); switch (command) { case "a": addIdentity(); @@ -163,7 +151,7 @@ public class Application { int index = Integer.parseInt(command) - 1; address(identities.get(index)); } catch (NumberFormatException e) { - System.out.println("Unknown command. Please try again."); + System.out.println(ERROR_UNKNOWN_COMMAND); } } } while (!"b".equals(command)); @@ -171,9 +159,9 @@ public class Application { private void addIdentity() { System.out.println(); - BitmessageAddress identity = ctx.createIdentity(yesNo("would you like a shorter address? This will take some time to calculate."), Pubkey.Feature.DOES_ACK); + BitmessageAddress identity = ctx.createIdentity(commandLine.yesNo("would you like a shorter address? This will take some time to calculate."), Pubkey.Feature.DOES_ACK); System.out.println("Please enter an alias for this identity, or an empty string for none"); - String alias = scanner.nextLine().trim(); + String alias = commandLine.nextLineTrimmed(); if (alias.length() > 0) { identity.setAlias(alias); } @@ -185,24 +173,12 @@ public class Application { List contacts = ctx.addresses().getContacts(); do { System.out.println(); - int i = 0; - for (BitmessageAddress contact : contacts) { - i++; - System.out.print(i + ") "); - if (contact.getAlias() != null) { - System.out.println(contact.getAlias() + " (" + contact.getAddress() + ")"); - } else { - System.out.println(contact.getAddress()); - } - } - if (i == 0) { - System.out.println("You have no contacts yet."); - } + commandLine.listAddresses(contacts, "contacts"); System.out.println(); System.out.println("a) add contact"); - System.out.println("b) back"); + System.out.println(COMMAND_BACK); - command = nextCommand(); + command = commandLine.nextCommand(); switch (command) { case "a": addContact(false); @@ -215,7 +191,7 @@ public class Application { int index = Integer.parseInt(command) - 1; address(contacts.get(index)); } catch (NumberFormatException e) { - System.out.println("Unknown command. Please try again."); + System.out.println(ERROR_UNKNOWN_COMMAND); } } } while (!"b".equals(command)); @@ -225,9 +201,9 @@ public class Application { System.out.println(); System.out.println("Please enter the Bitmessage address you want to add"); try { - BitmessageAddress address = new BitmessageAddress(scanner.nextLine().trim()); + BitmessageAddress address = new BitmessageAddress(commandLine.nextLineTrimmed()); System.out.println("Please enter an alias for this address, or an empty string for none"); - String alias = scanner.nextLine().trim(); + String alias = commandLine.nextLineTrimmed(); if (alias.length() > 0) { address.setAlias(alias); } @@ -245,24 +221,12 @@ public class Application { List subscriptions = ctx.addresses().getSubscriptions(); do { System.out.println(); - int i = 0; - for (BitmessageAddress contact : subscriptions) { - i++; - System.out.print(i + ") "); - if (contact.getAlias() != null) { - System.out.println(contact.getAlias() + " (" + contact.getAddress() + ")"); - } else { - System.out.println(contact.getAddress()); - } - } - if (i == 0) { - System.out.println("You have no subscriptions yet."); - } + commandLine.listAddresses(subscriptions, "subscriptions"); System.out.println(); System.out.println("a) add subscription"); - System.out.println("b) back"); + System.out.println(COMMAND_BACK); - command = nextCommand(); + command = commandLine.nextCommand(); switch (command) { case "a": addContact(true); @@ -275,7 +239,7 @@ public class Application { int index = Integer.parseInt(command) - 1; address(subscriptions.get(index)); } catch (NumberFormatException e) { - System.out.println("Unknown command. Please try again."); + System.out.println(ERROR_UNKNOWN_COMMAND); } } } while (!"b".equals(command)); @@ -313,9 +277,9 @@ public class Application { System.out.println(); System.out.println("c) compose message"); System.out.println("s) compose broadcast"); - System.out.println("b) back"); + System.out.println(COMMAND_BACK); - command = scanner.nextLine().trim(); + command = commandLine.nextCommand(); switch (command) { case "c": compose(false); @@ -330,7 +294,7 @@ public class Application { int index = Integer.parseInt(command) - 1; show(messages.get(index)); } catch (NumberFormatException | IndexOutOfBoundsException e) { - System.out.println("Unknown command. Please try again."); + System.out.println(ERROR_UNKNOWN_COMMAND); } } } while (!"b".equalsIgnoreCase(command)); @@ -350,8 +314,8 @@ public class Application { do { System.out.println("r) reply"); System.out.println("d) delete"); - System.out.println("b) back"); - command = nextCommand(); + System.out.println(COMMAND_BACK); + command = commandLine.nextCommand(); switch (command) { case "r": compose(message.getTo(), message.getFrom(), "RE: " + message.getSubject()); @@ -361,18 +325,18 @@ public class Application { case "b": return; default: - System.out.println("Unknown command. Please try again."); + System.out.println(ERROR_UNKNOWN_COMMAND); } } while (!"b".equalsIgnoreCase(command)); } private void compose(boolean broadcast) { System.out.println(); - BitmessageAddress from = selectAddress(true); + BitmessageAddress from = selectIdentity(); if (from == null) { return; } - BitmessageAddress to = (broadcast ? null : selectAddress(false)); + BitmessageAddress to = (broadcast ? null : selectContact()); if (!broadcast && to == null) { return; } @@ -380,58 +344,22 @@ public class Application { compose(from, to, null); } - private BitmessageAddress selectAddress(boolean id) { - List addresses = (id ? ctx.addresses().getIdentities() : ctx.addresses().getContacts()); + private BitmessageAddress selectIdentity() { + List addresses = ctx.addresses().getIdentities(); while (addresses.size() == 0) { - if (id) { - addIdentity(); - addresses = ctx.addresses().getIdentities(); - } else { - addContact(false); - addresses = ctx.addresses().getContacts(); - } + addIdentity(); + addresses = ctx.addresses().getIdentities(); } - if (addresses.size() == 1) { - return addresses.get(0); + return commandLine.selectAddress(addresses, "From:"); + } + + private BitmessageAddress selectContact() { + List addresses = ctx.addresses().getContacts(); + while (addresses.size() == 0) { + addContact(false); + addresses = ctx.addresses().getContacts(); } - - String command; - do { - System.out.println(); - if (id) { - System.out.println("From:"); - } else { - System.out.println("To:"); - } - - int i = 0; - for (BitmessageAddress identity : addresses) { - i++; - System.out.print(i + ") "); - if (identity.getAlias() != null) { - System.out.println(identity.getAlias() + " (" + identity.getAddress() + ")"); - } else { - System.out.println(identity.getAddress()); - } - } - System.out.println("b) back"); - - command = nextCommand(); - switch (command) { - case "b": - return null; - default: - try { - int index = Integer.parseInt(command) - 1; - if (addresses.get(index) != null) { - return addresses.get(index); - } - } catch (NumberFormatException e) { - System.out.println("Unknown command. Please try again."); - } - } - } while (!"b".equals(command)); - return null; + return commandLine.selectAddress(addresses, "To:"); } private void compose(BitmessageAddress from, BitmessageAddress to, String subject) { @@ -445,29 +373,19 @@ public class Application { System.out.println("Subject: " + subject); } else { System.out.print("Subject: "); - subject = scanner.nextLine().trim(); + subject = commandLine.nextLineTrimmed(); } System.out.println("Message:"); StringBuilder message = new StringBuilder(); String line; do { - line = scanner.nextLine(); + line = commandLine.nextLine(); message.append(line).append('\n'); - } while (line.length() > 0 || !yesNo("Send message?")); + } while (line.length() > 0 || !commandLine.yesNo("Send message?")); if (broadcast) { ctx.broadcast(from, subject, message.toString()); } else { ctx.send(from, to, subject, message.toString()); } } - - private boolean yesNo(String question) { - String answer; - do { - System.out.println(question + " (y/n)"); - answer = scanner.nextLine(); - if ("y".equalsIgnoreCase(answer)) return true; - if ("n".equalsIgnoreCase(answer)) return false; - } while (true); - } } diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/CommandLine.java b/demo/src/main/java/ch/dissem/bitmessage/demo/CommandLine.java new file mode 100644 index 0000000..f04196a --- /dev/null +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/CommandLine.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.demo; + +import ch.dissem.bitmessage.entity.BitmessageAddress; + +import java.util.List; +import java.util.Scanner; + + +/** + * @author Christian Basler + */ +public class CommandLine { + public static final String COMMAND_BACK = "b) back"; + public static final String ERROR_UNKNOWN_COMMAND = "Unknown command. Please try again."; + + private Scanner scanner = new Scanner(System.in); + + public String nextCommand() { + return scanner.nextLine().trim().toLowerCase(); + } + + public String nextLine() { + return scanner.nextLine(); + } + + public String nextLineTrimmed() { + return scanner.nextLine(); + } + + public boolean yesNo(String question) { + String answer; + do { + System.out.println(question + " (y/n)"); + answer = scanner.nextLine(); + if ("y".equalsIgnoreCase(answer)) return true; + if ("n".equalsIgnoreCase(answer)) return false; + } while (true); + } + + public BitmessageAddress selectAddress(List addresses, String label) { + if (addresses.size() == 1) { + return addresses.get(0); + } + + String command; + do { + System.out.println(); + System.out.println(label); + + listAddresses(addresses, "contacts"); + System.out.println(COMMAND_BACK); + + command = nextCommand(); + switch (command) { + case "b": + return null; + default: + try { + int index = Integer.parseInt(command) - 1; + if (addresses.get(index) != null) { + return addresses.get(index); + } + } catch (NumberFormatException e) { + System.out.println(ERROR_UNKNOWN_COMMAND); + } + } + } while (!"b".equals(command)); + return null; + } + + public void listAddresses(List addresses, String kind) { + int i = 0; + for (BitmessageAddress address : addresses) { + i++; + System.out.print(i + ") "); + if (address.getAlias() == null) { + System.out.println(address.getAddress()); + } else { + System.out.println(address.getAlias() + " (" + address.getAddress() + ")"); + } + } + if (i == 0) { + System.out.println("You have no " + kind + " yet."); + } + } +} diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java index b0e114b..2532796 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -17,10 +17,10 @@ package ch.dissem.bitmessage.demo; import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.ports.MemoryNodeRegistry; import ch.dissem.bitmessage.repository.*; -import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.wif.WifExporter; import ch.dissem.bitmessage.wif.WifImporter; import org.kohsuke.args4j.CmdLineException; @@ -29,6 +29,7 @@ import org.kohsuke.args4j.Option; import java.io.File; import java.io.IOException; +import java.net.InetAddress; public class Main { public static void main(String[] args) throws IOException { @@ -64,7 +65,8 @@ public class Main { new WifImporter(ctx, options.importWIF).importAll(); } } else { - new Application(options.syncServer, options.syncPort); + InetAddress syncServer = options.syncServer == null ? null : InetAddress.getByName(options.syncServer); + new Application(syncServer, options.syncPort); } } 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 43f1e30..5781bc3 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -203,51 +203,16 @@ class Connection { private void receiveMessage(MessagePayload messagePayload) { switch (messagePayload.getCommand()) { case INV: - Inv inv = (Inv) messagePayload; - int originalSize = inv.getInventory().size(); - updateIvCache(inv.getInventory()); - List missing = ctx.getInventory().getMissing(inv.getInventory(), streams); - missing.removeAll(commonRequestedObjects); - LOG.debug("Received inventory with " + originalSize + " elements, of which are " - + missing.size() + " missing."); - send(new GetData.Builder().inventory(missing).build()); + receiveMessage((Inv) messagePayload); break; case GETDATA: - GetData getData = (GetData) messagePayload; - for (InventoryVector iv : getData.getInventory()) { - ObjectMessage om = ctx.getInventory().getObject(iv); - if (om != null) sendingQueue.offer(om); - } + receiveMessage((GetData) messagePayload); break; case OBJECT: - ObjectMessage objectMessage = (ObjectMessage) messagePayload; - try { - requestedObjects.remove(objectMessage.getInventoryVector()); - if (ctx.getInventory().contains(objectMessage)) { - LOG.trace("Received object " + objectMessage.getInventoryVector() + " - already in inventory"); - break; - } - listener.receive(objectMessage); - security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); - ctx.getInventory().storeObject(objectMessage); - // offer object to some random nodes so it gets distributed throughout the network: - networkHandler.offer(objectMessage.getInventoryVector()); - lastObjectTime = UnixTime.now(); - } catch (InsufficientProofOfWorkException e) { - LOG.warn(e.getMessage()); - // DebugUtils.saveToFile(objectMessage); // this line must not be committed active - } catch (IOException e) { - LOG.error("Stream " + objectMessage.getStream() + ", object type " + objectMessage.getType() + ": " + e.getMessage(), e); - } finally { - if (commonRequestedObjects.remove(objectMessage.getInventoryVector())) { - LOG.debug("Received object that wasn't requested."); - } - } + receiveMessage((ObjectMessage) messagePayload); break; case ADDR: - Addr addr = (Addr) messagePayload; - LOG.debug("Received " + addr.getAddresses().size() + " addresses."); - ctx.getNodeRegistry().offerAddresses(addr.getAddresses()); + receiveMessage((Addr) messagePayload); break; case CUSTOM: case VERACK: @@ -257,6 +222,53 @@ class Connection { } } + private void receiveMessage(Inv inv) { + int originalSize = inv.getInventory().size(); + updateIvCache(inv.getInventory()); + List missing = ctx.getInventory().getMissing(inv.getInventory(), streams); + missing.removeAll(commonRequestedObjects); + LOG.debug("Received inventory with " + originalSize + " elements, of which are " + + missing.size() + " missing."); + send(new GetData.Builder().inventory(missing).build()); + } + + private void receiveMessage(GetData getData) { + for (InventoryVector iv : getData.getInventory()) { + ObjectMessage om = ctx.getInventory().getObject(iv); + if (om != null) sendingQueue.offer(om); + } + } + + private void receiveMessage(ObjectMessage objectMessage) { + requestedObjects.remove(objectMessage.getInventoryVector()); + if (ctx.getInventory().contains(objectMessage)) { + LOG.trace("Received object " + objectMessage.getInventoryVector() + " - already in inventory"); + return; + } + try { + listener.receive(objectMessage); + security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); + ctx.getInventory().storeObject(objectMessage); + // offer object to some random nodes so it gets distributed throughout the network: + networkHandler.offer(objectMessage.getInventoryVector()); + lastObjectTime = UnixTime.now(); + } catch (InsufficientProofOfWorkException e) { + LOG.warn(e.getMessage()); + // DebugUtils.saveToFile(objectMessage); // this line must not be committed active + } catch (IOException e) { + LOG.error("Stream " + objectMessage.getStream() + ", object type " + objectMessage.getType() + ": " + e.getMessage(), e); + } finally { + if (commonRequestedObjects.remove(objectMessage.getInventoryVector())) { + LOG.debug("Received object that wasn't requested."); + } + } + } + + private void receiveMessage(Addr addr) { + LOG.debug("Received " + addr.getAddresses().size() + " addresses."); + ctx.getNodeRegistry().offerAddresses(addr.getAddresses()); + } + private void sendAddresses() { List addresses = ctx.getNodeRegistry().getKnownAddresses(1000, streams); sendingQueue.offer(new Addr.Builder().addresses(addresses).build()); @@ -358,77 +370,10 @@ class Connection { Thread.sleep(100); } } - try { - NetworkMessage msg = Factory.getNetworkMessage(version, in); - if (msg == null) - continue; - switch (state) { - case ACTIVE: - receiveMessage(msg.getPayload()); - break; - - default: - switch (msg.getPayload().getCommand()) { - case VERSION: - Version payload = (Version) msg.getPayload(); - if (payload.getNonce() == ctx.getClientNonce()) { - LOG.info("Tried to connect to self, disconnecting."); - disconnect(); - } else if (payload.getVersion() >= BitmessageContext.CURRENT_VERSION) { - version = payload.getVersion(); - streams = payload.getStreams(); - send(new VerAck()); - switch (mode) { - case SERVER: - send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); - break; - case CLIENT: - case SYNC: - activateConnection(); - break; - default: - // NO OP - } - } else { - LOG.info("Received unsupported version " + payload.getVersion() + ", disconnecting."); - disconnect(); - } - break; - case VERACK: - switch (mode) { - case SERVER: - activateConnection(); - break; - case CLIENT: - case SYNC: - default: - // NO OP - 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() + "'"); - } - } - if (socket.isClosed() || syncFinished(msg) || checkOpenRequests()) disconnect(); - } catch (SocketTimeoutException ignore) { - if (state == ACTIVE) { - if (syncFinished(null)) disconnect(); - } - } + receive(); } - } catch (InterruptedException | IOException | NodeException e) { + } catch (Exception e) { LOG.trace("Reader disconnected from node " + node + ": " + e.getMessage()); - } catch (RuntimeException e) { - LOG.trace("Reader disconnecting from node " + node + " due to error: " + e.getMessage(), e); } finally { disconnect(); try { @@ -438,6 +383,81 @@ class Connection { } } } + + private void receive() throws InterruptedException { + try { + NetworkMessage msg = Factory.getNetworkMessage(version, in); + if (msg == null) + return; + switch (state) { + case ACTIVE: + receiveMessage(msg.getPayload()); + break; + + default: + handleCommand(msg.getPayload()); + break; + } + if (socket.isClosed() || syncFinished(msg) || checkOpenRequests()) disconnect(); + } catch (SocketTimeoutException ignore) { + if (state == ACTIVE && syncFinished(null)) disconnect(); + } + } + + private void handleCommand(MessagePayload payload) { + switch (payload.getCommand()) { + case VERSION: + handleVersion((Version) payload); + break; + case VERACK: + switch (mode) { + case SERVER: + activateConnection(); + break; + case CLIENT: + case SYNC: + default: + // NO OP + break; + } + break; + case CUSTOM: + MessagePayload response = ctx.getCustomCommandHandler().handle((CustomMessage) payload); + if (response != null) { + send(response); + } + disconnect(); + break; + default: + throw new NodeException("Command 'version' or 'verack' expected, but was '" + + payload.getCommand() + "'"); + } + } + + private void handleVersion(Version version) { + if (version.getNonce() == ctx.getClientNonce()) { + LOG.info("Tried to connect to self, disconnecting."); + disconnect(); + } else if (version.getVersion() >= BitmessageContext.CURRENT_VERSION) { + Connection.this.version = version.getVersion(); + streams = version.getStreams(); + send(new VerAck()); + switch (mode) { + case SERVER: + send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); + break; + case CLIENT: + case SYNC: + activateConnection(); + break; + default: + // NO OP + } + } else { + LOG.info("Received unsupported version " + version.getVersion() + ", disconnecting."); + disconnect(); + } + } } private boolean checkOpenRequests() { @@ -450,10 +470,10 @@ class Connection { try (Socket socket = Connection.this.socket) { initSocket(socket); while (state != DISCONNECTED) { - if (!sendingQueue.isEmpty()) { - send(sendingQueue.poll()); - } else { + if (sendingQueue.isEmpty()) { Thread.sleep(1000); + } else { + send(sendingQueue.poll()); } } } catch (IOException | InterruptedException e) {