From 6542bd145126c7ca68da5c9969cbf5e5216dc9eb Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Sat, 4 Jul 2015 11:13:35 +0200 Subject: [PATCH 01/42] now the build should work for anyone (esp. travis) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index fc7300b..eb6ed30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ signing.keyId= signing.password= -signing.secretKeyRingFile= +#signing.secretKeyRingFile= ossrhUsername= ossrhPassword= From b8546e28afc615fb342e64f3a4c3689a6f4254bd Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Wed, 5 Aug 2015 19:52:18 +0200 Subject: [PATCH 02/42] Moving "Security" to a separate port, so there can be a Bouncycastle and a Spongycastle implementation. (BC doesn't work on Android, SC can't be used on Oracle's JVM) --- .../dissem/bitmessage/demo/Application.java | 4 +- .../java/ch/dissem/bitmessage/demo/Main.java | 4 +- domain/build.gradle | 7 +- .../dissem/bitmessage/BitmessageContext.java | 141 ++++--- .../ch/dissem/bitmessage/InternalContext.java | 48 ++- .../bitmessage/entity/BitmessageAddress.java | 23 +- .../dissem/bitmessage/entity/Encrypted.java | 1 + .../bitmessage/entity/NetworkMessage.java | 4 +- .../bitmessage/entity/ObjectMessage.java | 12 +- .../dissem/bitmessage/entity/Plaintext.java | 3 +- .../dissem/bitmessage/entity/Streamable.java | 2 + .../bitmessage/entity/payload/Broadcast.java | 5 +- .../bitmessage/entity/payload/CryptoBox.java | 78 +--- .../bitmessage/entity/payload/Pubkey.java | 7 +- .../entity/payload/V4Broadcast.java | 1 + .../entity/valueobject/InventoryVector.java | 3 +- .../entity/valueobject/PrivateKey.java | 24 +- .../ch/dissem/bitmessage/factory/Factory.java | 18 +- .../bitmessage/factory/V3MessageFactory.java | 5 +- .../bitmessage/ports/AbstractSecurity.java | 175 +++++++++ .../bitmessage/ports/NetworkHandler.java | 2 + .../ch/dissem/bitmessage/ports/Security.java | 206 ++++++++++ .../bitmessage/ports/SimplePOWEngine.java | 2 +- .../ch/dissem/bitmessage/utils/Points.java | 32 ++ .../ch/dissem/bitmessage/utils/Property.java | 7 +- .../ch/dissem/bitmessage/utils/Security.java | 365 ------------------ .../ch/dissem/bitmessage/utils/Singleton.java | 34 ++ .../ch/dissem/bitmessage/DecryptionTest.java | 3 +- .../ch/dissem/bitmessage/EncryptionTest.java | 8 +- .../ch/dissem/bitmessage/SignatureTest.java | 3 +- .../entity/BitmessageAddressTest.java | 7 +- .../ports/ProofOfWorkEngineTest.java | 9 +- .../ch/dissem/bitmessage/utils/BytesTest.java | 3 - .../dissem/bitmessage/utils/DecodeTest.java | 3 - .../dissem/bitmessage/utils/EncodeTest.java | 3 - .../ch/dissem/bitmessage/utils/TestBase.java | 28 ++ gradle/wrapper/gradle-wrapper.properties | 2 +- .../bitmessage/networking/Connection.java | 4 +- ...rkNode.java => DefaultNetworkHandler.java} | 11 +- ...st.java => DefaultNetworkHandlerTest.java} | 11 +- repositories/build.gradle | 4 +- .../bitmessage/repository/JdbcConfig.java | 6 +- .../bitmessage/repository/JdbcHelper.java | 9 +- .../repository/JdbcAddressRepositoryTest.java | 2 +- .../repository/JdbcInventoryTest.java | 2 +- .../repository/JdbcMessageRepositoryTest.java | 14 +- .../repository/JdbcNodeRegistryTest.java | 2 +- .../bitmessage/repository/TestBase.java | 38 ++ .../bitmessage/repository/TestJdbcConfig.java | 3 +- security-bc/build.gradle | 18 + .../security/bc/BouncySecurity.java | 153 ++++++++ .../bitmessage/security}/SecurityTest.java | 62 ++- security-sc/build.gradle | 17 + .../security/sc/SpongySecurity.java | 153 ++++++++ settings.gradle | 4 + wif/build.gradle | 1 + .../ch/dissem/bitmessage/wif/WifExporter.java | 4 +- .../ch/dissem/bitmessage/wif/WifImporter.java | 5 +- .../bitmessage/wif/WifExporterTest.java | 2 + .../bitmessage/wif/WifImporterTest.java | 2 + 60 files changed, 1168 insertions(+), 641 deletions(-) create mode 100644 domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java create mode 100644 domain/src/main/java/ch/dissem/bitmessage/ports/Security.java create mode 100644 domain/src/main/java/ch/dissem/bitmessage/utils/Points.java delete mode 100644 domain/src/main/java/ch/dissem/bitmessage/utils/Security.java create mode 100644 domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java create mode 100644 domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java rename networking/src/main/java/ch/dissem/bitmessage/networking/{NetworkNode.java => DefaultNetworkHandler.java} (96%) rename networking/src/test/java/ch/dissem/bitmessage/networking/{NetworkNodeTest.java => DefaultNetworkHandlerTest.java} (86%) create mode 100644 repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java create mode 100644 security-bc/build.gradle create mode 100644 security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java rename {domain/src/test/java/ch/dissem/bitmessage/utils => security-bc/src/test/java/ch/dissem/bitmessage/security}/SecurityTest.java (60%) create mode 100644 security-sc/build.gradle create mode 100644 security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java 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 0e03880..416e7b7 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -20,7 +20,7 @@ import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.Pubkey; -import ch.dissem.bitmessage.networking.NetworkNode; +import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.repository.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +45,7 @@ public class Application { .inventory(new JdbcInventory(jdbcConfig)) .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) - .networkHandler(new NetworkNode()) + .networkHandler(new DefaultNetworkHandler()) .port(48444) .build(); 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 4ba8fa7..7f934a3 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -17,7 +17,7 @@ package ch.dissem.bitmessage.demo; import ch.dissem.bitmessage.BitmessageContext; -import ch.dissem.bitmessage.networking.NetworkNode; +import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.repository.*; import ch.dissem.bitmessage.wif.WifExporter; import ch.dissem.bitmessage.wif.WifImporter; @@ -49,7 +49,7 @@ public class Main { .inventory(new JdbcInventory(jdbcConfig)) .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) - .networkHandler(new NetworkNode()) + .networkHandler(new DefaultNetworkHandler()) .port(48444) .build(); diff --git a/domain/build.gradle b/domain/build.gradle index eb83499..6bcbe6d 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -12,6 +12,7 @@ uploadArchives { dependencies { compile 'org.slf4j:slf4j-api:1.7.12' - compile 'org.bouncycastle:bcprov-jdk15on:1.52' - testCompile group: 'junit', name: 'junit', version: '4.11' -} \ No newline at end of file + testCompile 'junit:junit:4.11' + testCompile 'org.mockito:mockito-core:1.10.19' + testCompile project(':security-bc') +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 7878ada..d7c04b5 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -27,12 +27,13 @@ import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.utils.Property; -import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import static ch.dissem.bitmessage.entity.Plaintext.Status.*; import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; @@ -57,12 +58,17 @@ public class BitmessageContext { public static final int CURRENT_VERSION = 3; private final static Logger LOG = LoggerFactory.getLogger(BitmessageContext.class); + private final ExecutorService pool; + private final InternalContext ctx; private Listener listener; private BitmessageContext(Builder builder) { ctx = new InternalContext(builder); + // As this thread is used for parts that do POW, which itself uses parallel threads, only + // one should be executed at any time. + pool = Executors.newFixedThreadPool(1); } public AddressRepository addresses() { @@ -74,7 +80,7 @@ public class BitmessageContext { } public BitmessageAddress createIdentity(boolean shorter, Feature... features) { - BitmessageAddress identity = new BitmessageAddress(new PrivateKey( + final BitmessageAddress identity = new BitmessageAddress(new PrivateKey( shorter, ctx.getStreams()[0], ctx.getNetworkNonceTrialsPerByte(), @@ -82,8 +88,12 @@ public class BitmessageContext { features )); ctx.getAddressRepo().save(identity); - // TODO: this should happen in a separate thread - ctx.sendPubkey(identity, identity.getStream()); + pool.submit(new Runnable() { + @Override + public void run() { + ctx.sendPubkey(identity, identity.getStream()); + } + }); return identity; } @@ -91,63 +101,71 @@ public class BitmessageContext { // TODO } - public void broadcast(BitmessageAddress from, String subject, String message) { - // TODO: all this should happen in a separate thread - Plaintext msg = new Plaintext.Builder(BROADCAST) - .from(from) - .message(subject, message) - .build(); + public void broadcast(final BitmessageAddress from, final String subject, final String message) { + pool.submit(new Runnable() { + @Override + public void run() { + Plaintext msg = new Plaintext.Builder(BROADCAST) + .from(from) + .message(subject, message) + .build(); - LOG.info("Sending message."); - msg.setStatus(DOING_PROOF_OF_WORK); - ctx.getMessageRepository().save(msg); - ctx.send( - from, - from, - Factory.getBroadcast(from, msg), - +2 * DAY, - 0, - 0 - ); - msg.setStatus(SENT); - msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.BROADCAST, Label.Type.SENT)); - ctx.getMessageRepository().save(msg); + LOG.info("Sending message."); + msg.setStatus(DOING_PROOF_OF_WORK); + ctx.getMessageRepository().save(msg); + ctx.send( + from, + from, + Factory.getBroadcast(from, msg), + +2 * DAY, + 0, + 0 + ); + msg.setStatus(SENT); + msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.BROADCAST, Label.Type.SENT)); + ctx.getMessageRepository().save(msg); + } + }); } - public void send(BitmessageAddress from, BitmessageAddress to, String subject, String message) { + public void send(final BitmessageAddress from, final BitmessageAddress to, final String subject, final String message) { if (from.getPrivateKey() == null) { throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key."); } - // TODO: all this should happen in a separate thread - Plaintext msg = new Plaintext.Builder(MSG) - .from(from) - .to(to) - .message(subject, message) - .build(); - if (to.getPubkey() == null) { - tryToFindMatchingPubkey(to); - } - if (to.getPubkey() == null) { - LOG.info("Public key is missing from recipient. Requesting."); - requestPubkey(from, to); - msg.setStatus(PUBKEY_REQUESTED); - ctx.getMessageRepository().save(msg); - } else { - LOG.info("Sending message."); - msg.setStatus(DOING_PROOF_OF_WORK); - ctx.getMessageRepository().save(msg); - ctx.send( - from, - to, - new Msg(msg), - +2 * DAY, - ctx.getNonceTrialsPerByte(to), - ctx.getExtraBytes(to) - ); - msg.setStatus(SENT); - msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.SENT)); - ctx.getMessageRepository().save(msg); - } + pool.submit(new Runnable() { + @Override + public void run() { + Plaintext msg = new Plaintext.Builder(MSG) + .from(from) + .to(to) + .message(subject, message) + .build(); + if (to.getPubkey() == null) { + tryToFindMatchingPubkey(to); + } + if (to.getPubkey() == null) { + LOG.info("Public key is missing from recipient. Requesting."); + requestPubkey(from, to); + msg.setStatus(PUBKEY_REQUESTED); + ctx.getMessageRepository().save(msg); + } else { + LOG.info("Sending message."); + msg.setStatus(DOING_PROOF_OF_WORK); + ctx.getMessageRepository().save(msg); + ctx.send( + from, + to, + new Msg(msg), + +2 * DAY, + ctx.getNonceTrialsPerByte(to), + ctx.getExtraBytes(to) + ); + msg.setStatus(SENT); + msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.SENT)); + ctx.getMessageRepository().save(msg); + } + } + }); } private void requestPubkey(BitmessageAddress requestingIdentity, BitmessageAddress address) { @@ -169,8 +187,7 @@ public class BitmessageContext { .expiresTime(expires) .payload(payload) .build(); - Security.doProofOfWork(object, ctx.getProofOfWorkEngine(), - ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); + ctx.getSecurity().doProofOfWork(object, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); ctx.getInventory().storeObject(object); ctx.getNetworkHandler().offer(object.getInventoryVector()); } @@ -184,6 +201,10 @@ public class BitmessageContext { ctx.getNetworkHandler().stop(); } + public boolean isRunning() { + return ctx.getNetworkHandler().isRunning(); + } + public void addContact(BitmessageAddress contact) { ctx.getAddressRepo().save(contact); tryToFindMatchingPubkey(contact); @@ -258,6 +279,7 @@ public class BitmessageContext { AddressRepository addressRepo; MessageRepository messageRepo; ProofOfWorkEngine proofOfWorkEngine; + Security security; public Builder() { } @@ -292,6 +314,11 @@ public class BitmessageContext { return this; } + public Builder security(Security security) { + this.security = security; + return this; + } + public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) { this.proofOfWorkEngine = proofOfWorkEngine; return this; diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 8ac64e0..03fef80 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -21,7 +21,7 @@ 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.ports.*; -import ch.dissem.bitmessage.utils.Security; +import ch.dissem.bitmessage.utils.Singleton; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,6 +42,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY; public class InternalContext { private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class); + private final Security security; private final Inventory inventory; private final NodeRegistry nodeRegistry; private final NetworkHandler networkHandler; @@ -56,16 +57,29 @@ public class InternalContext { private long clientNonce; public InternalContext(BitmessageContext.Builder builder) { + this.security = builder.security; this.inventory = builder.inventory; this.nodeRegistry = builder.nodeRegistry; this.networkHandler = builder.networkHandler; this.addressRepository = builder.addressRepo; this.messageRepository = builder.messageRepo; this.proofOfWorkEngine = builder.proofOfWorkEngine; - this.clientNonce = Security.randomNonce(); + this.clientNonce = security.randomNonce(); + + Singleton.initialize(security); port = builder.port; - streams.add(1L); // FIXME + + // TODO: streams of new identities and subscriptions should also be added. This works only after a restart. + for (BitmessageAddress address : addressRepository.getIdentities()) { + streams.add(address.getStream()); + } + for (BitmessageAddress address : addressRepository.getSubscriptions()) { + streams.add(address.getStream()); + } + if (streams.isEmpty()) { + streams.add(1L); + } init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); } @@ -78,6 +92,10 @@ public class InternalContext { } } + public Security getSecurity() { + return security; + } + public Inventory getInventory() { return inventory; } @@ -111,14 +129,6 @@ public class InternalContext { return result; } - public void addStream(long stream) { - streams.add(stream); - } - - public void removeStream(long stream) { - streams.remove(stream); - } - public int getPort() { return port; } @@ -152,14 +162,14 @@ public class InternalContext { .payload(payload) .build(); if (object.isSigned()) { - object.sign(from.getPrivateKey()); + object.sign( from.getPrivateKey()); } if (payload instanceof Broadcast) { ((Broadcast) payload).encrypt(); } else if (payload instanceof Encrypted) { object.encrypt(to.getPubkey()); } - Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes); + security.doProofOfWork(object, nonceTrialsPerByte, extraBytes); if (payload instanceof PlaintextHolder) { Plaintext plaintext = ((PlaintextHolder) payload).getPlaintext(); plaintext.setInventoryVector(object.getInventoryVector()); @@ -182,13 +192,13 @@ public class InternalContext { .payload(identity.getPubkey()) .build(); response.sign(identity.getPrivateKey()); - response.encrypt(Security.createPublicKey(identity.getPublicDecryptionKey()).getEncoded(false)); - Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); + response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); + security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); if (response.isSigned()) { response.sign(identity.getPrivateKey()); } if (response instanceof Encrypted) { - response.encrypt(Security.createPublicKey(identity.getPublicDecryptionKey()).getEncoded(false)); + response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); } inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); @@ -206,7 +216,7 @@ public class InternalContext { .expiresTime(expires) .payload(new GetPubkey(contact)) .build(); - Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); + security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); } @@ -215,10 +225,6 @@ public class InternalContext { return clientNonce; } - public void setClientNonce(long clientNonce) { - this.clientNonce = clientNonce; - } - public interface ContextHolder { void setContext(InternalContext context); } 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 eb348a2..0441e6e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -19,22 +19,27 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; -import ch.dissem.bitmessage.utils.*; +import ch.dissem.bitmessage.utils.AccessCounter; +import ch.dissem.bitmessage.utils.Base58; +import ch.dissem.bitmessage.utils.Bytes; +import ch.dissem.bitmessage.utils.Encode; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.Serializable; import java.util.Arrays; import java.util.Objects; import static ch.dissem.bitmessage.utils.Decode.bytes; import static ch.dissem.bitmessage.utils.Decode.varInt; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * A Bitmessage address. Can be a user's private address, an address string without public keys or a recipient's address * holding private keys. */ -public class BitmessageAddress { +public class BitmessageAddress implements Serializable { private final long version; private final long stream; private final byte[] ripe; @@ -62,19 +67,19 @@ public class BitmessageAddress { Encode.varInt(version, os); Encode.varInt(stream, os); if (version < 4) { - byte[] checksum = Security.sha512(os.toByteArray(), ripe); + byte[] checksum = security().sha512(os.toByteArray(), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { // for tag and decryption key, the checksum has to be created with 0x00 padding - byte[] checksum = Security.doubleSha512(os.toByteArray(), ripe); + byte[] checksum = security().doubleSha512(os.toByteArray(), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } // but for the address and its checksum they need to be stripped int offset = Bytes.numberOfLeadingZeros(ripe); os.write(ripe, offset, ripe.length - offset); - byte[] checksum = Security.doubleSha512(os.toByteArray()); + byte[] checksum = security().doubleSha512(os.toByteArray()); os.write(checksum, 0, 4); this.address = "BM-" + Base58.encode(os.toByteArray()); } catch (IOException e) { @@ -103,18 +108,18 @@ public class BitmessageAddress { this.ripe = Bytes.expand(bytes(in, bytes.length - counter.length() - 4), 20); // test checksum - byte[] checksum = Security.doubleSha512(bytes, bytes.length - 4); + byte[] checksum = security().doubleSha512(bytes, bytes.length - 4); byte[] expectedChecksum = bytes(in, 4); for (int i = 0; i < 4; i++) { if (expectedChecksum[i] != checksum[i]) throw new IllegalArgumentException("Checksum of address failed"); } if (version < 4) { - checksum = Security.sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); + checksum = security().sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { - checksum = Security.doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); + checksum = security().doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } @@ -129,7 +134,7 @@ public class BitmessageAddress { Encode.varInt(version, out); Encode.varInt(stream, out); out.write(ripe); - return Arrays.copyOfRange(Security.doubleSha512(out.toByteArray()), 32, 64); + return Arrays.copyOfRange(security().doubleSha512(out.toByteArray()), 32, 64); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java index 8b15371..9eaa2ab 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.exception.DecryptionFailedException; +import ch.dissem.bitmessage.ports.Security; import java.io.IOException; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java index c10f942..8790d3a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java @@ -26,7 +26,7 @@ import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import static ch.dissem.bitmessage.utils.Security.sha512; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * A network message is exchanged between two nodes. @@ -48,7 +48,7 @@ public class NetworkMessage implements Streamable { * First 4 bytes of sha512(payload) */ private byte[] getChecksum(byte[] bytes) throws NoSuchProviderException, NoSuchAlgorithmException { - byte[] d = sha512(bytes); + byte[] d = security().sha512(bytes); return new byte[]{d[0], d[1], d[2], d[3]}; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java index 0ef3340..128084e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java @@ -22,14 +22,16 @@ import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; +import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.Bytes; import ch.dissem.bitmessage.utils.Encode; -import ch.dissem.bitmessage.utils.Security; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** * The 'object' command sends an object that is shared throughout the network. */ @@ -89,7 +91,9 @@ public class ObjectMessage implements MessagePayload { } public InventoryVector getInventoryVector() { - return new InventoryVector(Bytes.truncate(Security.doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32)); + return new InventoryVector( + Bytes.truncate(security().doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32) + ); } private boolean isEncrypted() { @@ -113,7 +117,7 @@ public class ObjectMessage implements MessagePayload { public void sign(PrivateKey key) { if (payload.isSigned()) { - payload.setSignature(Security.getSignature(getBytesToSign(), key)); + payload.setSignature(security().getSignature(getBytesToSign(), key)); } } @@ -147,7 +151,7 @@ public class ObjectMessage implements MessagePayload { public boolean isSignatureValid(Pubkey pubkey) throws IOException { if (isEncrypted()) throw new IllegalStateException("Payload must be decrypted first"); - return Security.isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey); + return security().isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey); } @Override 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 28cebfa..e73849f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -19,6 +19,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.factory.Factory; +import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Encode; @@ -28,7 +29,7 @@ import java.util.*; /** * The unencrypted message to be sent by 'msg' or 'broadcast'. */ -public class Plaintext implements Streamable { +public class Plaintext implements Streamable, Serializable { private final Type type; private final BitmessageAddress from; private final long encoding; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java index 9ee4cf9..0601a44 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java @@ -16,6 +16,8 @@ package ch.dissem.bitmessage.entity; +import ch.dissem.bitmessage.ports.Security; + import java.io.IOException; import java.io.OutputStream; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java index 2491d85..bf5ff7f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java @@ -21,11 +21,12 @@ import ch.dissem.bitmessage.entity.Encrypted; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.PlaintextHolder; import ch.dissem.bitmessage.exception.DecryptionFailedException; -import ch.dissem.bitmessage.utils.Security; +import ch.dissem.bitmessage.ports.Security; import java.io.IOException; import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * Users who are subscribed to the sending address will see the message appear in their inbox. @@ -78,7 +79,7 @@ public abstract class Broadcast extends ObjectPayload implements Encrypted, Plai } public void encrypt() throws IOException { - encrypt(Security.createPublicKey(plaintext.getFrom().getPublicDecryptionKey()).getEncoded(false)); + encrypt(security().createPublicKey(plaintext.getFrom().getPublicDecryptionKey())); } @Override diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java index 2018373..7f994d1 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java @@ -17,28 +17,16 @@ package ch.dissem.bitmessage.entity.payload; import ch.dissem.bitmessage.entity.Streamable; -import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.utils.*; -import org.bouncycastle.crypto.BufferedBlockCipher; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.modes.CBCBlockCipher; -import org.bouncycastle.crypto.paddings.PKCS7Padding; -import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.math.ec.ECFieldElement; -import org.bouncycastle.math.ec.ECPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; -import java.math.BigInteger; import java.util.Arrays; import static ch.dissem.bitmessage.entity.valueobject.PrivateKey.PRIVATE_KEY_SIZE; +import static ch.dissem.bitmessage.utils.Singleton.security; public class CryptoBox implements Streamable { @@ -46,35 +34,31 @@ public class CryptoBox implements Streamable { private final byte[] initializationVector; private final int curveType; - private final ECPoint R; + private final byte[] R; private final byte[] mac; private byte[] encrypted; - public CryptoBox(Streamable data, byte[] encryptionKey) throws IOException { - this(data, Security.keyToPoint(encryptionKey)); - } - - public CryptoBox(Streamable data, ECPoint K) throws IOException { + public CryptoBox(Streamable data, byte[] K) throws IOException { curveType = 0x02CA; // 1. The destination public key is called K. // 2. Generate 16 random bytes using a secure random number generator. Call them IV. - initializationVector = Security.randomBytes(16); + initializationVector = security().randomBytes(16); // 3. Generate a new random EC key pair with private key called r and public key called R. - byte[] r = Security.randomBytes(PRIVATE_KEY_SIZE); - R = Security.createPublicKey(r); + byte[] r = security().randomBytes(PRIVATE_KEY_SIZE); + R = security().createPublicKey(r); // 4. Do an EC point multiply with public key K and private key r. This gives you public key P. - ECPoint P = K.multiply(Security.keyToBigInt(r)).normalize(); - byte[] X = P.getXCoord().getEncoded(); + byte[] P = security().multiply(K, r); + byte[] X = Points.getX(P); // 5. Use the X component of public key P and calculate the SHA512 hash H. - byte[] H = Security.sha512(X); + byte[] H = security().sha512(X); // 6. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7. // 8. Encrypt the data with AES-256-CBC, using IV as initialization vector, key_e as encryption key and the padded input text as payload. Call the output cipher text. - encrypted = crypt(true, Encode.bytes(data), key_e); + encrypted = security().crypt(true, Encode.bytes(data), key_e, initializationVector); // 9. Calculate a 32 byte MAC with HMACSHA256, using key_m as salt and IV + R + cipher text as data. Call the output MAC. mac = calculateMac(key_m); @@ -84,7 +68,7 @@ public class CryptoBox implements Streamable { private CryptoBox(Builder builder) { initializationVector = builder.initializationVector; curveType = builder.curveType; - R = Security.createPoint(builder.xComponent, builder.yComponent); + R = security().createPoint(builder.xComponent, builder.yComponent); encrypted = builder.encrypted; mac = builder.mac; } @@ -102,18 +86,17 @@ public class CryptoBox implements Streamable { } /** - * @param privateKey a private key, typically should be 32 bytes long + * @param k a private key, typically should be 32 bytes long * @return an InputStream yielding the decrypted data * @throws DecryptionFailedException if the payload can't be decrypted using this private key * @see https://bitmessage.org/wiki/Encryption#Decryption */ - public InputStream decrypt(byte[] privateKey) throws DecryptionFailedException { + public InputStream decrypt(byte[] k) throws DecryptionFailedException { // 1. The private key used to decrypt is called k. - BigInteger k = Security.keyToBigInt(privateKey); // 2. Do an EC point multiply with private key k and public key R. This gives you public key P. - ECPoint P = R.multiply(k).normalize(); + byte[] P = security().multiply(R, k); // 3. Use the X component of public key P and calculate the SHA512 hash H. - byte[] H = Security.sha512(P.getXCoord().getEncoded()); + byte[] H = security().sha512(Arrays.copyOfRange(P, 1, 33)); // 4. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); byte[] key_m = Arrays.copyOfRange(H, 32, 64); @@ -126,49 +109,28 @@ public class CryptoBox implements Streamable { // 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key // and the cipher text as payload. The output is the padded input text. - return new ByteArrayInputStream(crypt(false, encrypted, key_e)); + return new ByteArrayInputStream(security().crypt(false, encrypted, key_e, initializationVector)); } private byte[] calculateMac(byte[] key_m) { try { ByteArrayOutputStream macData = new ByteArrayOutputStream(); writeWithoutMAC(macData); - return Security.mac(key_m, macData.toByteArray()); + return security().mac(key_m, macData.toByteArray()); } catch (IOException e) { throw new RuntimeException(e); } } - private byte[] crypt(boolean encrypt, byte[] data, byte[] key_e) { - BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); - - CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector); - - cipher.init(encrypt, params); - - byte[] buffer = new byte[cipher.getOutputSize(data.length)]; - int length = cipher.processBytes(data, 0, data.length, buffer, 0); - try { - length += cipher.doFinal(buffer, length); - } catch (InvalidCipherTextException e) { - throw new IllegalArgumentException(e); - } - if (length < buffer.length) { - return Arrays.copyOfRange(buffer, 0, length); - } - return buffer; - } - private void writeWithoutMAC(OutputStream out) throws IOException { out.write(initializationVector); Encode.int16(curveType, out); - writeCoordinateComponent(out, R.getXCoord()); - writeCoordinateComponent(out, R.getYCoord()); + writeCoordinateComponent(out, Points.getX(R)); + writeCoordinateComponent(out, Points.getY(R)); out.write(encrypted); } - private void writeCoordinateComponent(OutputStream out, ECFieldElement coord) throws IOException { - byte[] x = coord.getEncoded(); + private void writeCoordinateComponent(OutputStream out, byte[] x) throws IOException { int offset = Bytes.numberOfLeadingZeros(x); int length = x.length - offset; Encode.int16(length, out); diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java index 2bdcbb6..1243b32 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java @@ -20,8 +20,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import static ch.dissem.bitmessage.utils.Security.ripemd160; -import static ch.dissem.bitmessage.utils.Security.sha512; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * Public keys for signing and encryption, the answer to a 'getpubkey' request. @@ -34,7 +33,7 @@ public abstract class Pubkey extends ObjectPayload { } public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) { - return ripemd160(sha512(publicSigningKey, publicEncryptionKey)); + return security().ripemd160(security().sha512(publicSigningKey, publicEncryptionKey)); } public abstract byte[] getSigningKey(); @@ -44,7 +43,7 @@ public abstract class Pubkey extends ObjectPayload { public abstract int getBehaviorBitfield(); public byte[] getRipe() { - return ripemd160(sha512(getSigningKey(), getEncryptionKey())); + return security().ripemd160(security().sha512(getSigningKey(), getEncryptionKey())); } public long getNonceTrialsPerByte() { diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java index 39127a8..03364d2 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java @@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity.payload; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.ports.Security; import java.io.IOException; import java.io.InputStream; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java index 366d26b..f87dd13 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java @@ -21,9 +21,10 @@ import ch.dissem.bitmessage.utils.Strings; import java.io.IOException; import java.io.OutputStream; +import java.io.Serializable; import java.util.Arrays; -public class InventoryVector implements Streamable { +public class InventoryVector implements Streamable, Serializable { /** * Hash of the object */ diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java index f0956a6..d07c859 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java @@ -18,18 +18,18 @@ package ch.dissem.bitmessage.entity.valueobject; import ch.dissem.bitmessage.entity.Streamable; import ch.dissem.bitmessage.entity.payload.Pubkey; -import ch.dissem.bitmessage.entity.payload.V3Pubkey; -import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.utils.Bytes; import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Encode; -import ch.dissem.bitmessage.utils.Security; import java.io.*; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** - * Created by chris on 18.04.15. + * Represents a private key. Additional information (stream, version, features, ...) is stored in the accompanying + * {@link Pubkey} object. */ public class PrivateKey implements Streamable { public static final int PRIVATE_KEY_SIZE = 32; @@ -45,15 +45,15 @@ public class PrivateKey implements Streamable { byte[] pubEK; byte[] ripe; do { - privSK = Security.randomBytes(PRIVATE_KEY_SIZE); - privEK = Security.randomBytes(PRIVATE_KEY_SIZE); - pubSK = Security.createPublicKey(privSK).getEncoded(false); - pubEK = Security.createPublicKey(privEK).getEncoded(false); + privSK = security().randomBytes(PRIVATE_KEY_SIZE); + privEK = security().randomBytes(PRIVATE_KEY_SIZE); + pubSK = security().createPublicKey(privSK); + pubEK = security().createPublicKey(privEK); ripe = Pubkey.getRipe(pubSK, pubEK); } while (ripe[0] != 0 || (shorter && ripe[1] != 0)); this.privateSigningKey = privSK; this.privateEncryptionKey = privEK; - this.pubkey = Security.createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, + this.pubkey = security().createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } @@ -66,9 +66,9 @@ public class PrivateKey implements Streamable { public PrivateKey(long version, long stream, String passphrase, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { try { // FIXME: this is most definitely wrong - this.privateSigningKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{0}), 32); - this.privateEncryptionKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{1}), 32); - this.pubkey = Security.createPubkey(version, stream, privateSigningKey, privateEncryptionKey, + this.privateSigningKey = Bytes.truncate(security().sha512(passphrase.getBytes("UTF-8"), new byte[]{0}), 32); + this.privateEncryptionKey = Bytes.truncate(security().sha512(passphrase.getBytes("UTF-8"), new byte[]{1}), 32); + this.pubkey = security().createPubkey(version, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java index a144bb6..5584ec7 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java @@ -23,7 +23,7 @@ import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.NodeException; -import ch.dissem.bitmessage.utils.Security; +import ch.dissem.bitmessage.ports.Security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +32,8 @@ import java.io.InputStream; import java.net.SocketException; import java.net.SocketTimeoutException; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** * Creates {@link NetworkMessage} objects from {@link InputStream InputStreams} */ @@ -115,8 +117,8 @@ public class Factory { BitmessageAddress temp = new BitmessageAddress(address); PrivateKey privateKey = new PrivateKey(privateSigningKey, privateEncryptionKey, createPubkey(temp.getVersion(), temp.getStream(), - Security.createPublicKey(privateSigningKey).getEncoded(false), - Security.createPublicKey(privateEncryptionKey).getEncoded(false), + security().createPublicKey(privateSigningKey), + security().createPublicKey(privateEncryptionKey), nonceTrialsPerByte, extraBytes, behaviourBitfield)); BitmessageAddress result = new BitmessageAddress(privateKey); if (!result.getAddress().equals(address)) { @@ -126,11 +128,17 @@ public class Factory { return result; } - public static BitmessageAddress generatePrivateAddress(boolean shorter, long stream, Pubkey.Feature... features) { + public static BitmessageAddress generatePrivateAddress(boolean shorter, + long stream, + Pubkey.Feature... features) { return new BitmessageAddress(new PrivateKey(shorter, stream, 1000, 1000, features)); } - static ObjectPayload getObjectPayload(long objectType, long version, long streamNumber, InputStream stream, int length) throws IOException { + static ObjectPayload getObjectPayload(long objectType, + long version, + long streamNumber, + InputStream stream, + int length) throws IOException { ObjectType type = ObjectType.fromNumber(objectType); if (type != null) { switch (type) { diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java index 57d240d..7e48746 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java @@ -22,9 +22,9 @@ import ch.dissem.bitmessage.entity.payload.ObjectPayload; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.exception.NodeException; +import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.AccessCounter; import ch.dissem.bitmessage.utils.Decode; -import ch.dissem.bitmessage.utils.Security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import static ch.dissem.bitmessage.entity.NetworkMessage.MAGIC_BYTES; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * Creates protocol v3 network messages from {@link InputStream InputStreams} @@ -174,7 +175,7 @@ class V3MessageFactory { } private static boolean testChecksum(byte[] checksum, byte[] payload) { - byte[] payloadChecksum = Security.sha512(payload); + byte[] payloadChecksum = security().sha512(payload); for (int i = 0; i < checksum.length; i++) { if (checksum[i] != payloadChecksum[i]) { return false; diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java new file mode 100644 index 0000000..00ed1f2 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java @@ -0,0 +1,175 @@ +/* + * Copyright 2015 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.ports; + +import ch.dissem.bitmessage.InternalContext; +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException; +import ch.dissem.bitmessage.factory.Factory; +import ch.dissem.bitmessage.utils.Bytes; +import ch.dissem.bitmessage.utils.UnixTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.SecureRandom; + +/** + * Implements everything that isn't directly dependent on either Spongy- or Bouncycastle. + */ +public abstract class AbstractSecurity implements Security, InternalContext.ContextHolder { + public static final Logger LOG = LoggerFactory.getLogger(Security.class); + private static final SecureRandom RANDOM = new SecureRandom(); + private static final BigInteger TWO = BigInteger.valueOf(2); + + private final String provider; + private InternalContext context; + + protected AbstractSecurity(String provider) { + this.provider = provider; + } + + @Override + public void setContext(InternalContext context) { + this.context = context; + } + + public byte[] sha512(byte[]... data) { + return hash("SHA-512", data); + } + + public byte[] doubleSha512(byte[]... data) { + MessageDigest mda = md("SHA-512"); + for (byte[] d : data) { + mda.update(d); + } + return mda.digest(mda.digest()); + } + + public byte[] doubleSha512(byte[] data, int length) { + MessageDigest mda = md("SHA-512"); + mda.update(data, 0, length); + return mda.digest(mda.digest()); + } + + public byte[] ripemd160(byte[]... data) { + return hash("RIPEMD160", data); + } + + public byte[] doubleSha256(byte[] data, int length) { + MessageDigest mda = md("SHA-256"); + mda.update(data, 0, length); + return mda.digest(mda.digest()); + } + + public byte[] sha1(byte[]... data) { + return hash("SHA-1", data); + } + + public byte[] randomBytes(int length) { + byte[] result = new byte[length]; + RANDOM.nextBytes(result); + return result; + } + + public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, + long extraBytes) { + try { + if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; + if (extraBytes < 1000) extraBytes = 1000; + + byte[] initialHash = getInitialHash(object); + + byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); + + byte[] nonce = context.getProofOfWorkEngine().calculateNonce(initialHash, target); + object.setNonce(nonce); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) + throws IOException { + byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); + byte[] value = doubleSha512(object.getNonce(), getInitialHash(object)); + if (Bytes.lt(target, value, 8)) { + throw new InsufficientProofOfWorkException(target, value); + } + } + + private byte[] getInitialHash(ObjectMessage object) throws IOException { + return sha512(object.getPayloadBytesWithoutNonce()); + } + + private byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { + BigInteger TTL = BigInteger.valueOf(object.getExpiresTime() - UnixTime.now()); + LOG.debug("TTL: " + TTL + "s"); + BigInteger numerator = TWO.pow(64); + BigInteger powLength = BigInteger.valueOf(object.getPayloadBytesWithoutNonce().length + extraBytes); + BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte).multiply(powLength.add(powLength.multiply(TTL).divide(BigInteger.valueOf(2).pow(16)))); + return Bytes.expand(numerator.divide(denominator).toByteArray(), 8); + } + + private byte[] hash(String algorithm, byte[]... data) { + MessageDigest mda = md(algorithm); + for (byte[] d : data) { + mda.update(d); + } + return mda.digest(); + } + + private MessageDigest md(String algorithm) { + try { + return MessageDigest.getInstance(algorithm, provider); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + public byte[] mac(byte[] key_m, byte[] data) { + try { + Mac mac = Mac.getInstance("HmacSHA256", provider); + mac.init(new SecretKeySpec(key_m, "HmacSHA256")); + return mac.doFinal(data); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + public Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, + long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { + return Factory.createPubkey(version, stream, + createPublicKey(privateSigningKey), + createPublicKey(privateEncryptionKey), + nonceTrialsPerByte, extraBytes, features); + } + + public BigInteger keyToBigInt(byte[] privateKey) { + return new BigInteger(1, privateKey); + } + + public long randomNonce() { + return RANDOM.nextLong(); + } +} 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 ed2f38a..4b73629 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java @@ -34,6 +34,8 @@ public interface NetworkHandler { Property getNetworkStatus(); + boolean isRunning(); + interface MessageListener { void receive(ObjectMessage object) throws IOException; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java new file mode 100644 index 0000000..83ad43d --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java @@ -0,0 +1,206 @@ +/* + * Copyright 2015 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.ports; + +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.SecureRandom; + +/** + * Provides some methods to help with hashing and encryption. All randoms are created using {@link SecureRandom}, + * which should be secure enough. + */ +public interface Security { + /** + * A helper method to calculate SHA-512 hashes. Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in + * success on the same thread. + * + * @param data to get hashed + * @return SHA-512 hash of data + */ + byte[] sha512(byte[]... data); + + /** + * A helper method to calculate doubleSHA-512 hashes. Please note that a new {@link MessageDigest} object is created + * at each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in + * success on the same thread. + * + * @param data to get hashed + * @return SHA-512 hash of data + */ + byte[] doubleSha512(byte[]... data); + + /** + * A helper method to calculate double SHA-512 hashes. This method allows to only use a part of the available bytes + * to use for the hash calculation. + *

+ * Please note that a new {@link MessageDigest} object is created at each call (to ensure thread safety), so you + * shouldn't use this if you need to do many hash calculations in short order on the same thread. + *

+ * + * @param data to get hashed + * @param length number of bytes to be taken into account + * @return SHA-512 hash of data + */ + byte[] doubleSha512(byte[] data, int length); + + /** + * A helper method to calculate RIPEMD-160 hashes. Supplying multiple byte arrays has the same result as a + * concatenation of all arrays, but might perform better. + *

+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *

+ * + * @param data to get hashed + * @return RIPEMD-160 hash of data + */ + byte[] ripemd160(byte[]... data); + + /** + * A helper method to calculate double SHA-256 hashes. This method allows to only use a part of the available bytes + * to use for the hash calculation. + *

+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *

+ * + * @param data to get hashed + * @param length number of bytes to be taken into account + * @return SHA-256 hash of data + */ + byte[] doubleSha256(byte[] data, int length); + + /** + * A helper method to calculate SHA-1 hashes. Supplying multiple byte arrays has the same result as a + * concatenation of all arrays, but might perform better. + *

+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *

+ * + * @param data to get hashed + * @return SHA hash of data + */ + byte[] sha1(byte[]... data); + + /** + * @param length number of bytes to return + * @return an array of the given size containing random bytes + */ + byte[] randomBytes(int length); + + /** + * Calculates the proof of work. This might take a long time, depending on the hardware, message size and time to + * live. + * + * @param object to do the proof of work for + * @param nonceTrialsPerByte difficulty + * @param extraBytes bytes to add to the object size (makes it more difficult to send small messages) + */ + void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, + long extraBytes); + + /** + * @param object to be checked + * @param nonceTrialsPerByte difficulty + * @param extraBytes bytes to add to the object size + * @throws InsufficientProofOfWorkException if proof of work doesn't check out (makes it more difficult to send small messages) + */ + void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) + throws IOException; + + /** + * Calculates the MAC for a message (data) + * + * @param key_m the symmetric key used + * @param data the message data to calculate the MAC for + * @return the MAC + */ + byte[] mac(byte[] key_m, byte[] data); + + /** + * + * @param encrypt if true, encrypts data, otherwise tries to decrypt it. + * @param data + * @param key_e + * @return + */ + byte[] crypt(boolean encrypt, byte[] data, byte[] key_e, byte[] initializationVector); + + /** + * Create a new public key fom given private keys. + * + * @param version of the public key / address + * @param stream of the address + * @param privateSigningKey private key used for signing + * @param privateEncryptionKey private key used for encryption + * @param nonceTrialsPerByte proof of work difficulty + * @param extraBytes bytes to add for the proof of work (make it harder for small messages) + * @param features of the address + * @return a public key object + */ + Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, + long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features); + + /** + * @param privateKey private key as byte array + * @return a public key corresponding to the given private key + */ + byte[] createPublicKey(byte[] privateKey); + + /** + * @param privateKey private key as byte array + * @return a big integer representation (unsigned) of the given bytes + */ + BigInteger keyToBigInt(byte[] privateKey); + + /** + * @param data to check + * @param signature the signature of the message + * @param pubkey the sender's public key + * @return true if the signature is valid, false otherwise + */ + boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey); + + /** + * Calculate the signature of data, using the given private key. + * + * @param data to be signed + * @param privateKey to be used for signing + * @return the signature + */ + byte[] getSignature(byte[] data, ch.dissem.bitmessage.entity.valueobject.PrivateKey privateKey); + + /** + * @return a random number of type long + */ + long randomNonce(); + + byte[] multiply(byte[] k, byte[] r); + + byte[] createPoint(byte[] x, byte[] y); +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java index 0d9d392..211df53 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java @@ -23,7 +23,7 @@ import java.security.MessageDigest; import static ch.dissem.bitmessage.utils.Bytes.inc; /** - * Created by chris on 14.04.15. + * You should really use the MultiThreadedPOWEngine, but this one might help you grok the other one. */ public class SimplePOWEngine implements ProofOfWorkEngine { @Override diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Points.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Points.java new file mode 100644 index 0000000..937fe20 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Points.java @@ -0,0 +1,32 @@ +/* + * Copyright 2015 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.utils; + +import java.util.Arrays; + +/** + * Created by chris on 20.07.15. + */ +public class Points { + public static byte[] getX(byte[] P) { + return Arrays.copyOfRange(P, 1, ((P.length - 1) / 2) + 1); + } + + public static byte[] getY(byte[] P) { + return Arrays.copyOfRange(P, ((P.length - 1) / 2) + 1, P.length); + } +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java index 1030513..6fa0ec4 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java @@ -17,7 +17,12 @@ package ch.dissem.bitmessage.utils; /** - * Created by chris on 14.06.15. + * Some property that has a name, a value and/or other properties. This can be used for any purpose, but is for now + * used to contain different status information. It is by default displayed in some JSON inspired human readable + * notation, but you might only want to rely on the 'human readable' part. + *

+ * If you need a real JSON representation, please add a method toJson(). + *

*/ public class Property { private String name; diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java deleted file mode 100644 index 25e248e..0000000 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Security.java +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright 2015 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.utils; - -import ch.dissem.bitmessage.entity.ObjectMessage; -import ch.dissem.bitmessage.entity.payload.Pubkey; -import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException; -import ch.dissem.bitmessage.factory.Factory; -import ch.dissem.bitmessage.ports.ProofOfWorkEngine; -import org.bouncycastle.asn1.x9.X9ECParameters; -import org.bouncycastle.crypto.ec.CustomNamedCurves; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECParameterSpec; -import org.bouncycastle.jce.spec.ECPrivateKeySpec; -import org.bouncycastle.jce.spec.ECPublicKeySpec; -import org.bouncycastle.math.ec.ECPoint; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.io.IOException; -import java.math.BigInteger; -import java.security.*; -import java.security.spec.KeySpec; -import java.util.Arrays; - -/** - * Provides some methods to help with hashing and encryption. All randoms are created using {@link SecureRandom}, - * which should be secure enough. - */ -public class Security { - public static final Logger LOG = LoggerFactory.getLogger(Security.class); - private static final SecureRandom RANDOM = new SecureRandom(); - private static final BigInteger TWO = BigInteger.valueOf(2); - private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1"); - - static { - java.security.Security.addProvider(new BouncyCastleProvider()); - } - - /** - * A helper method to calculate SHA-512 hashes. Please note that a new {@link MessageDigest} object is created at - * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in - * success on the same thread. - * - * @param data to get hashed - * @return SHA-512 hash of data - */ - public static byte[] sha512(byte[]... data) { - return hash("SHA-512", data); - } - - /** - * A helper method to calculate doubleSHA-512 hashes. Please note that a new {@link MessageDigest} object is created - * at each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in - * success on the same thread. - * - * @param data to get hashed - * @return SHA-512 hash of data - */ - public static byte[] doubleSha512(byte[]... data) { - MessageDigest mda = md("SHA-512"); - for (byte[] d : data) { - mda.update(d); - } - return mda.digest(mda.digest()); - } - - /** - * A helper method to calculate double SHA-512 hashes. This method allows to only use a part of the available bytes - * to use for the hash calculation. - *

- * Please note that a new {@link MessageDigest} object is created at each call (to ensure thread safety), so you - * shouldn't use this if you need to do many hash calculations in short order on the same thread. - *

- * - * @param data to get hashed - * @param length number of bytes to be taken into account - * @return SHA-512 hash of data - */ - public static byte[] doubleSha512(byte[] data, int length) { - MessageDigest mda = md("SHA-512"); - mda.update(data, 0, length); - return mda.digest(mda.digest()); - } - - - /** - * A helper method to calculate RIPEMD-160 hashes. Supplying multiple byte arrays has the same result as a - * concatenation of all arrays, but might perform better. - *

- * Please note that a new {@link MessageDigest} object is created at - * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short - * order on the same thread. - *

- * - * @param data to get hashed - * @return RIPEMD-160 hash of data - */ - public static byte[] ripemd160(byte[]... data) { - return hash("RIPEMD160", data); - } - - /** - * A helper method to calculate double SHA-256 hashes. This method allows to only use a part of the available bytes - * to use for the hash calculation. - *

- * Please note that a new {@link MessageDigest} object is created at - * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short - * order on the same thread. - *

- * - * @param data to get hashed - * @param length number of bytes to be taken into account - * @return SHA-256 hash of data - */ - public static byte[] doubleSha256(byte[] data, int length) { - MessageDigest mda = md("SHA-256"); - mda.update(data, 0, length); - return mda.digest(mda.digest()); - } - - /** - * A helper method to calculate SHA-1 hashes. Supplying multiple byte arrays has the same result as a - * concatenation of all arrays, but might perform better. - *

- * Please note that a new {@link MessageDigest} object is created at - * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short - * order on the same thread. - *

- * - * @param data to get hashed - * @return SHA hash of data - */ - public static byte[] sha1(byte[]... data) { - return hash("SHA-1", data); - } - - /** - * @param length number of bytes to return - * @return an array of the given size containing random bytes - */ - public static byte[] randomBytes(int length) { - byte[] result = new byte[length]; - RANDOM.nextBytes(result); - return result; - } - - /** - * Calculates the proof of work. This might take a long time, depending on the hardware, message size and time to - * live. - * - * @param object to do the proof of work for - * @param worker doing the actual proof of work - * @param nonceTrialsPerByte difficulty - * @param extraBytes bytes to add to the object size (makes it more difficult to send small messages) - */ - public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, - long extraBytes) { - try { - if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; - if (extraBytes < 1000) extraBytes = 1000; - - byte[] initialHash = getInitialHash(object); - - byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); - - byte[] nonce = worker.calculateNonce(initialHash, target); - object.setNonce(nonce); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * @param object to be checked - * @param nonceTrialsPerByte difficulty - * @param extraBytes bytes to add to the object size - * @throws InsufficientProofOfWorkException if proof of work doesn't check out (makes it more difficult to send small messages) - */ - public static void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) - throws IOException { - byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); - byte[] value = Security.doubleSha512(object.getNonce(), getInitialHash(object)); - if (Bytes.lt(target, value, 8)) { - throw new InsufficientProofOfWorkException(target, value); - } - } - - private static byte[] getInitialHash(ObjectMessage object) throws IOException { - return Security.sha512(object.getPayloadBytesWithoutNonce()); - } - - private static byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { - BigInteger TTL = BigInteger.valueOf(object.getExpiresTime() - UnixTime.now()); - LOG.debug("TTL: " + TTL + "s"); - BigInteger numerator = TWO.pow(64); - BigInteger powLength = BigInteger.valueOf(object.getPayloadBytesWithoutNonce().length + extraBytes); - BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte).multiply(powLength.add(powLength.multiply(TTL).divide(BigInteger.valueOf(2).pow(16)))); - return Bytes.expand(numerator.divide(denominator).toByteArray(), 8); - } - - private static byte[] hash(String algorithm, byte[]... data) { - MessageDigest mda = md(algorithm); - for (byte[] d : data) { - mda.update(d); - } - return mda.digest(); - } - - private static MessageDigest md(String algorithm) { - try { - return MessageDigest.getInstance(algorithm, "BC"); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - /** - * Calculates the MAC for a message (data) - * - * @param key_m the symmetric key used - * @param data the message data to calculate the MAC for - * @return the MAC - */ - public static byte[] mac(byte[] key_m, byte[] data) { - try { - Mac mac = Mac.getInstance("HmacSHA256", "BC"); - mac.init(new SecretKeySpec(key_m, "HmacSHA256")); - return mac.doFinal(data); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - /** - * Create a new public key fom given private keys. - * - * @param version of the public key / address - * @param stream of the address - * @param privateSigningKey private key used for signing - * @param privateEncryptionKey private key used for encryption - * @param nonceTrialsPerByte proof of work difficulty - * @param extraBytes bytes to add for the proof of work (make it harder for small messages) - * @param features of the address - * @return a public key object - */ - public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, - long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { - return Factory.createPubkey(version, stream, - createPublicKey(privateSigningKey).getEncoded(false), - createPublicKey(privateEncryptionKey).getEncoded(false), - nonceTrialsPerByte, extraBytes, features); - } - - /** - * @param privateKey private key as byte array - * @return a public key corresponding to the given private key - */ - public static ECPoint createPublicKey(byte[] privateKey) { - return EC_CURVE_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize(); - } - - /** - * @param privateKey private key as byte array - * @return a big integer representation (unsigned) of the given bytes - */ - public static BigInteger keyToBigInt(byte[] privateKey) { - return new BigInteger(1, privateKey); - } - - public static ECPoint keyToPoint(byte[] publicKey) { - BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKey, 1, 33)); - BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKey, 33, 65)); - return EC_CURVE_PARAMETERS.getCurve().createPoint(x, y); - } - - public static ECPoint createPoint(byte[] x, byte[] y) { - return EC_CURVE_PARAMETERS.getCurve().createPoint( - new BigInteger(1, x), - new BigInteger(1, y) - ); - } - - /** - * @param data to check - * @param signature the signature of the message - * @param pubkey the sender's public key - * @return true if the signature is valid, false otherwise - */ - public static boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey) { - try { - ECParameterSpec spec = new ECParameterSpec( - EC_CURVE_PARAMETERS.getCurve(), - EC_CURVE_PARAMETERS.getG(), - EC_CURVE_PARAMETERS.getN(), - EC_CURVE_PARAMETERS.getH(), - EC_CURVE_PARAMETERS.getSeed() - ); - - ECPoint Q = keyToPoint(pubkey.getSigningKey()); - KeySpec keySpec = new ECPublicKeySpec(Q, spec); - PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec); - - Signature sig = Signature.getInstance("ECDSA", "BC"); - sig.initVerify(publicKey); - sig.update(data); - return sig.verify(signature); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Calculate the signature of data, using the given private key. - * - * @param data to be signed - * @param privateKey to be used for signing - * @return the signature - */ - public static byte[] getSignature(byte[] data, ch.dissem.bitmessage.entity.valueobject.PrivateKey privateKey) { - try { - ECParameterSpec spec = new ECParameterSpec( - EC_CURVE_PARAMETERS.getCurve(), - EC_CURVE_PARAMETERS.getG(), - EC_CURVE_PARAMETERS.getN(), - EC_CURVE_PARAMETERS.getH(), - EC_CURVE_PARAMETERS.getSeed() - ); - - BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey()); - KeySpec keySpec = new ECPrivateKeySpec(d, spec); - PrivateKey privKey = KeyFactory.getInstance("ECDSA", "BC").generatePrivate(keySpec); - - Signature sig = Signature.getInstance("ECDSA", "BC"); - sig.initSign(privKey); - sig.update(data); - return sig.sign(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * @return a random number of type long - */ - public static long randomNonce() { - return RANDOM.nextLong(); - } -} diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java new file mode 100644 index 0000000..169db77 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015 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.utils; + +import ch.dissem.bitmessage.ports.Security; + +/** + * Created by chris on 20.07.15. + */ +public class Singleton { + private static Security security; + + public static void initialize(Security security) { + Singleton.security = security; + } + + public static Security security() { + return security; + } +} diff --git a/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java b/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java index ebb3707..21f9506 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.payload.V4Broadcast; import ch.dissem.bitmessage.entity.payload.V5Broadcast; import ch.dissem.bitmessage.exception.DecryptionFailedException; +import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; @@ -29,7 +30,7 @@ import java.io.IOException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -public class DecryptionTest { +public class DecryptionTest extends TestBase { @Test public void ensureV4BroadcastIsDecryptedCorrectly() throws IOException, DecryptionFailedException { BitmessageAddress address = new BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ"); diff --git a/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java index f0f8ddb..9a24842 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java @@ -24,20 +24,20 @@ import ch.dissem.bitmessage.entity.payload.GenericPayload; import ch.dissem.bitmessage.entity.payload.Msg; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; -import ch.dissem.bitmessage.utils.Security; +import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; import java.io.IOException; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -public class EncryptionTest { +public class EncryptionTest extends TestBase { @Test public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException, DecryptionFailedException { - GenericPayload before = new GenericPayload(0, 1, Security.randomBytes(100)); + GenericPayload before = new GenericPayload(0, 1, security().randomBytes(100)); PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000); CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey()); diff --git a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java index dc2eb85..71b7d2a 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java @@ -25,6 +25,7 @@ import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; +import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; @@ -33,7 +34,7 @@ import java.util.Date; import static org.junit.Assert.*; -public class SignatureTest { +public class SignatureTest extends TestBase { @Test public void ensureValidationWorks() throws IOException { ObjectMessage object = TestUtils.loadObjectMessage(3, "V3Pubkey.payload"); diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java index 85b8098..e1fcc7f 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.Arrays; import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.DOES_ACK; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.*; public class BitmessageAddressTest { @@ -102,7 +103,7 @@ public class BitmessageAddressTest { System.out.println("\n\n" + Strings.hex(privsigningkey) + "\n\n"); BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey, - Security.createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000))); + security().createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000))); assertEquals(address_string, address.getAddress()); } @@ -119,7 +120,7 @@ public class BitmessageAddressTest { if (bytes.length != 37) throw new IOException("Unknown format: 37 bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long"); - byte[] hash = Security.doubleSha256(bytes, 33); + byte[] hash = security().doubleSha256(bytes, 33); for (int i = 0; i < 4; i++) { if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat); } @@ -132,7 +133,7 @@ public class BitmessageAddressTest { byte[] privsigningkey = getSecret("5KMWqfCyJZGFgW6QrnPJ6L9Gatz25B51y7ErgqNr1nXUVbtZbdU"); byte[] privencryptionkey = getSecret("5JXXWEuhHQEPk414SzEZk1PHDRi8kCuZd895J7EnKeQSahJPxGz"); BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey, - Security.createPubkey(4, 1, privsigningkey, privencryptionkey, 320, 14000))); + security().createPubkey(4, 1, privsigningkey, privencryptionkey, 320, 14000))); assertEquals("BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke", address.getAddress()); } diff --git a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java index aed5722..ca17a23 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java @@ -17,14 +17,11 @@ package ch.dissem.bitmessage.ports; import ch.dissem.bitmessage.utils.Bytes; -import ch.dissem.bitmessage.utils.Security; import org.junit.Test; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.assertTrue; -/** - * Created by chris on 17.04.15. - */ public class ProofOfWorkEngineTest { @Test public void testSimplePOWEngine() { @@ -38,11 +35,11 @@ public class ProofOfWorkEngineTest { private void testPOW(ProofOfWorkEngine engine) { long time = System.currentTimeMillis(); - byte[] initialHash = Security.sha512(new byte[]{1, 3, 6, 4}); + byte[] initialHash = security().sha512(new byte[]{1, 3, 6, 4}); byte[] target = {0, 0, -1, -1, -1, -1, -1, -1}; byte[] nonce = engine.calculateNonce(initialHash, target); System.out.println("Calculating nonce took " + (System.currentTimeMillis() - time) + "ms"); - assertTrue(Bytes.lt(Security.doubleSha512(nonce, initialHash), target, 8)); + assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8)); } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java index 7c83724..52eb106 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java @@ -25,9 +25,6 @@ import java.util.Random; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -/** - * Created by chris on 10.04.15. - */ public class BytesTest { public static final Random rnd = new Random(); diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java index 8c18ee7..60d882f 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java @@ -22,9 +22,6 @@ import java.io.*; import static org.junit.Assert.assertEquals; -/** - * Created by chris on 20.03.15. - */ public class DecodeTest { @Test public void ensureDecodingWorks() throws Exception { diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java index 3489112..aaba5bf 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java @@ -23,9 +23,6 @@ import java.io.IOException; import static org.junit.Assert.assertEquals; -/** - * Created by chris on 13.03.15. - */ public class EncodeTest { @Test public void testUint8() throws IOException { diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java b/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java new file mode 100644 index 0000000..1dd1335 --- /dev/null +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015 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.utils; + +import ch.dissem.bitmessage.security.bc.BouncySecurity; + +/** + * Created by chris on 20.07.15. + */ +public class TestBase { + static { + Singleton.initialize(new BouncySecurity()); + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f2e98f0..66e6c70 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.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-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 0eb0f09..d8054ff 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -26,7 +26,6 @@ import ch.dissem.bitmessage.exception.NodeException; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.NetworkHandler.MessageListener; import ch.dissem.bitmessage.utils.DebugUtils; -import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +43,7 @@ import java.util.concurrent.ConcurrentMap; import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT; import static ch.dissem.bitmessage.networking.Connection.State.*; +import static ch.dissem.bitmessage.utils.Singleton.security; import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; /** @@ -275,7 +275,7 @@ public class Connection implements Runnable { ObjectMessage objectMessage = (ObjectMessage) messagePayload; try { LOG.debug("Received object " + objectMessage.getInventoryVector()); - Security.checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); + security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); listener.receive(objectMessage); ctx.getInventory().storeObject(objectMessage); // offer object to some random nodes so it gets distributed throughout the network: diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/NetworkNode.java b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java similarity index 96% rename from networking/src/main/java/ch/dissem/bitmessage/networking/NetworkNode.java rename to networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java index f27f7ff..558d097 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/NetworkNode.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -44,9 +44,9 @@ import static ch.dissem.bitmessage.utils.DebugUtils.inc; /** * Handles all the networky stuff. */ -public class NetworkNode implements NetworkHandler, ContextHolder { +public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { public final static int NETWORK_MAGIC_NUMBER = 8; - private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class); + private final static Logger LOG = LoggerFactory.getLogger(DefaultNetworkHandler.class); private final ExecutorService pool; private final List connections = new LinkedList<>(); private InternalContext ctx; @@ -56,7 +56,7 @@ public class NetworkNode implements NetworkHandler, ContextHolder { private ConcurrentMap requestedObjects = new ConcurrentHashMap<>(); - public NetworkNode() { + public DefaultNetworkHandler() { pool = Executors.newCachedThreadPool(); } @@ -128,6 +128,11 @@ public class NetworkNode implements NetworkHandler, ContextHolder { } } + @Override + public boolean isRunning() { + return connectionManager != null && connectionManager.isAlive(); + } + @Override public void stop() { connectionManager.interrupt(); diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkNodeTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/DefaultNetworkHandlerTest.java similarity index 86% rename from networking/src/test/java/ch/dissem/bitmessage/networking/NetworkNodeTest.java rename to networking/src/test/java/ch/dissem/bitmessage/networking/DefaultNetworkHandlerTest.java index 6819311..3bec993 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkNodeTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/DefaultNetworkHandlerTest.java @@ -24,16 +24,21 @@ import org.junit.Ignore; import org.junit.Test; /** - * Created by chris on 20.03.15. + * FIXME: there really should be sensible tests for the network handler */ -public class NetworkNodeTest { +public class DefaultNetworkHandlerTest { private NetworkAddress localhost = new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(8444).build(); + // void start(MessageListener listener); + // void stop(); + // void offer(InventoryVector iv); + // Property getNetworkStatus(); + @Ignore @Test(expected = InterruptedException.class) public void testSendMessage() throws Exception { final Thread baseThread = Thread.currentThread(); - NetworkNode net = new NetworkNode(); + DefaultNetworkHandler net = new DefaultNetworkHandler(); // net.setListener(localhost, new NetworkHandler.MessageListener() { // @Override // public void receive(ObjectPayload payload) { diff --git a/repositories/build.gradle b/repositories/build.gradle index 2f467b8..76509fd 100644 --- a/repositories/build.gradle +++ b/repositories/build.gradle @@ -12,7 +12,9 @@ uploadArchives { dependencies { compile project(':domain') - compile 'com.h2database:h2:1.4.187' compile 'org.flywaydb:flyway-core:3.2.1' testCompile 'junit:junit:4.11' + testCompile 'com.h2database:h2:1.4.187' + testCompile 'org.mockito:mockito-core:1.10.19' + testCompile project(':security-bc') } \ No newline at end of file diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java index 80e079b..7448b19 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcConfig.java @@ -28,9 +28,9 @@ import java.sql.SQLException; */ public class JdbcConfig { protected final Flyway flyway; - private final String dbUrl; - private final String dbUser; - private final String dbPassword; + protected final String dbUrl; + protected final String dbUser; + protected final String dbPassword; public JdbcConfig(String dbUrl, String dbUser, String dbPassword) { this.dbUrl = dbUrl; diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java index 8795388..4fbea30 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java @@ -18,14 +18,15 @@ package ch.dissem.bitmessage.repository; import ch.dissem.bitmessage.entity.Streamable; import ch.dissem.bitmessage.entity.payload.ObjectType; -import org.flywaydb.core.Flyway; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.sql.*; +import java.sql.Blob; +import java.sql.PreparedStatement; +import java.sql.SQLException; import static ch.dissem.bitmessage.utils.Strings.hex; @@ -81,8 +82,8 @@ abstract class JdbcHelper { if (data != null) { ByteArrayOutputStream os = new ByteArrayOutputStream(); data.write(os); - ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); - ps.setBlob(parameterIndex, is); + byte[] bytes = os.toByteArray(); + ps.setBinaryStream(parameterIndex, new ByteArrayInputStream(bytes), bytes.length); } else { ps.setBlob(parameterIndex, (Blob) null); } diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcAddressRepositoryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcAddressRepositoryTest.java index 80740ca..18fc83c 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcAddressRepositoryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcAddressRepositoryTest.java @@ -25,7 +25,7 @@ import java.util.List; import static org.junit.Assert.*; -public class JdbcAddressRepositoryTest { +public class JdbcAddressRepositoryTest extends TestBase { public static final String CONTACT_A = "BM-2cW7cD5cDQJDNkE7ibmyTxfvGAmnPqa9Vt"; public static final String CONTACT_B = "BM-2cTtkBnb4BUYDndTKun6D9PjtueP2h1bQj"; public static final String CONTACT_C = "BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke"; diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java index 8b23566..3954766 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java @@ -34,7 +34,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY; import static ch.dissem.bitmessage.utils.UnixTime.now; import static org.junit.Assert.*; -public class JdbcInventoryTest { +public class JdbcInventoryTest extends TestBase { private TestJdbcConfig config; private Inventory inventory; diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java index b99e44f..3dceb24 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java @@ -25,7 +25,6 @@ import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; -import ch.dissem.bitmessage.utils.Security; import org.junit.Before; import org.junit.Test; @@ -33,10 +32,11 @@ import java.util.Arrays; import java.util.List; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -public class JdbcMessageRepositoryTest { +public class JdbcMessageRepositoryTest extends TestBase { private BitmessageAddress contactA; private BitmessageAddress contactB; private BitmessageAddress identity; @@ -54,7 +54,11 @@ public class JdbcMessageRepositoryTest { config.reset(); addressRepo = new JdbcAddressRepository(config); repo = new JdbcMessageRepository(config); - new InternalContext(new BitmessageContext.Builder().addressRepo(addressRepo).messageRepo(repo)); + new InternalContext(new BitmessageContext.Builder() + .security(security()) + .addressRepo(addressRepo) + .messageRepo(repo) + ); BitmessageAddress tmp = new BitmessageAddress(new PrivateKey(false, 1, 1000, 1000)); contactA = new BitmessageAddress(tmp.getAddress()); @@ -120,7 +124,7 @@ public class JdbcMessageRepositoryTest { @Test public void testSave() throws Exception { Plaintext message = new Plaintext.Builder(MSG) - .IV(new InventoryVector(Security.randomBytes(32))) + .IV(new InventoryVector(security().randomBytes(32))) .from(identity) .to(contactA) .message("Subject", "Message") @@ -143,7 +147,7 @@ public class JdbcMessageRepositoryTest { public void testUpdate() throws Exception { List messages = repo.findMessages(Plaintext.Status.DRAFT, contactA); Plaintext message = messages.get(0); - message.setInventoryVector(new InventoryVector(Security.randomBytes(32))); + message.setInventoryVector(new InventoryVector(security().randomBytes(32))); repo.save(message); messages = repo.findMessages(Plaintext.Status.DRAFT, contactA); diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java index d668822..ca4bcd7 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java @@ -27,7 +27,7 @@ import java.util.List; import static ch.dissem.bitmessage.utils.UnixTime.now; import static org.junit.Assert.assertEquals; -public class JdbcNodeRegistryTest { +public class JdbcNodeRegistryTest extends TestBase { private TestJdbcConfig config; private NodeRegistry registry; diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java new file mode 100644 index 0000000..c6baaa2 --- /dev/null +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015 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.repository; + +import ch.dissem.bitmessage.InternalContext; +import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine; +import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.utils.Singleton; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Created by chris on 20.07.15. + */ +public class TestBase { + static { + BouncySecurity security = new BouncySecurity(); + Singleton.initialize(security); + InternalContext ctx = mock(InternalContext.class); + when(ctx.getProofOfWorkEngine()).thenReturn(new MultiThreadedPOWEngine()); + security.setContext(ctx); + } +} diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/TestJdbcConfig.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/TestJdbcConfig.java index 909ba75..99a26c0 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/TestJdbcConfig.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/TestJdbcConfig.java @@ -17,7 +17,8 @@ package ch.dissem.bitmessage.repository; /** - * Created by chris on 02.06.15. + * JdbcConfig to be used for tests. Uses an in-memory database and adds a useful {@link #reset()} method resetting + * the database. */ public class TestJdbcConfig extends JdbcConfig { public TestJdbcConfig() { diff --git a/security-bc/build.gradle b/security-bc/build.gradle new file mode 100644 index 0000000..48f14d1 --- /dev/null +++ b/security-bc/build.gradle @@ -0,0 +1,18 @@ +uploadArchives { + repositories { + mavenDeployer { + pom.project { + name 'Jabit Spongy Security' + artifactId = 'jabit-security-spongy' + description 'The Security implementation using spongy castle (needed for Android)' + } + } + } +} + +dependencies { + compile project(':domain') + compile 'org.bouncycastle:bcprov-jdk15on:1.52' + testCompile 'junit:junit:4.11' + testCompile 'org.mockito:mockito-core:1.10.19' +} diff --git a/security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java b/security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java new file mode 100644 index 0000000..a125049 --- /dev/null +++ b/security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java @@ -0,0 +1,153 @@ +/* + * Copyright 2015 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.security.bc; + +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.entity.valueobject.PrivateKey; +import ch.dissem.bitmessage.ports.AbstractSecurity; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPrivateKeySpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.math.ec.ECPoint; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.KeySpec; +import java.util.Arrays; + +/** + * As Spongycastle can't be used on the Oracle JVM, and Bouncycastle doesn't work properly on Android (thanks, Google), + * this is the Bouncycastle implementation. + */ +public class BouncySecurity extends AbstractSecurity { + private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1"); + + static { + java.security.Security.addProvider(new BouncyCastleProvider()); + } + + public BouncySecurity() { + super("BC"); + } + + @Override + public byte[] crypt(boolean encrypt, byte[] data, byte[] key_e, byte[] initializationVector) { + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); + + CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector); + + cipher.init(encrypt, params); + + byte[] buffer = new byte[cipher.getOutputSize(data.length)]; + int length = cipher.processBytes(data, 0, data.length, buffer, 0); + try { + length += cipher.doFinal(buffer, length); + } catch (InvalidCipherTextException e) { + throw new IllegalArgumentException(e); + } + if (length < buffer.length) { + return Arrays.copyOfRange(buffer, 0, length); + } + return buffer; + } + + @Override + public byte[] createPublicKey(byte[] privateKey) { + return EC_CURVE_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize().getEncoded(false); + } + + private ECPoint keyToPoint(byte[] publicKey) { + BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKey, 1, 33)); + BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKey, 33, 65)); + return EC_CURVE_PARAMETERS.getCurve().createPoint(x, y); + } + + @Override + public boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey) { + try { + ECParameterSpec spec = new ECParameterSpec( + EC_CURVE_PARAMETERS.getCurve(), + EC_CURVE_PARAMETERS.getG(), + EC_CURVE_PARAMETERS.getN(), + EC_CURVE_PARAMETERS.getH(), + EC_CURVE_PARAMETERS.getSeed() + ); + + ECPoint Q = keyToPoint(pubkey.getSigningKey()); + KeySpec keySpec = new ECPublicKeySpec(Q, spec); + PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec); + + Signature sig = Signature.getInstance("ECDSA", "BC"); + sig.initVerify(publicKey); + sig.update(data); + return sig.verify(signature); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public byte[] getSignature(byte[] data, PrivateKey privateKey) { + try { + ECParameterSpec spec = new ECParameterSpec( + EC_CURVE_PARAMETERS.getCurve(), + EC_CURVE_PARAMETERS.getG(), + EC_CURVE_PARAMETERS.getN(), + EC_CURVE_PARAMETERS.getH(), + EC_CURVE_PARAMETERS.getSeed() + ); + + BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey()); + KeySpec keySpec = new ECPrivateKeySpec(d, spec); + java.security.PrivateKey privKey = KeyFactory.getInstance("ECDSA", "BC").generatePrivate(keySpec); + + Signature sig = Signature.getInstance("ECDSA", "BC"); + sig.initSign(privKey); + sig.update(data); + return sig.sign(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public byte[] multiply(byte[] K, byte[] r) { + return keyToPoint(K).multiply(keyToBigInt(r)).normalize().getEncoded(false); + } + + @Override + public byte[] createPoint(byte[] x, byte[] y) { + return EC_CURVE_PARAMETERS.getCurve().createPoint( + new BigInteger(1, x), + new BigInteger(1, y) + ).getEncoded(false); + } +} diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/SecurityTest.java b/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java similarity index 60% rename from domain/src/test/java/ch/dissem/bitmessage/utils/SecurityTest.java rename to security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java index 9b97eb1..94f0ede 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/SecurityTest.java +++ b/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java @@ -1,36 +1,25 @@ -/* - * Copyright 2015 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.utils; +package ch.dissem.bitmessage.security; +import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.payload.GenericPayload; import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine; +import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.utils.Singleton; +import ch.dissem.bitmessage.utils.UnixTime; import org.junit.Test; import javax.xml.bind.DatatypeConverter; import java.io.ByteArrayInputStream; import java.io.IOException; -import java.security.KeyPairGenerator; import static ch.dissem.bitmessage.utils.UnixTime.DAY; import static org.junit.Assert.assertArrayEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** - * Created by chris on 10.04.15. + * Created by chris on 19.07.15. */ public class SecurityTest { public static final byte[] TEST_VALUE = "teststring".getBytes(); @@ -42,29 +31,39 @@ public class SecurityTest { public static final byte[] TEST_RIPEMD160 = DatatypeConverter.parseHexBinary("" + "cd566972b5e50104011a92b59fa8e0b1234851ae"); + private static BouncySecurity security; + + public SecurityTest() { + security = new BouncySecurity(); + Singleton.initialize(security); + InternalContext ctx = mock(InternalContext.class); + when(ctx.getProofOfWorkEngine()).thenReturn(new MultiThreadedPOWEngine()); + security.setContext(ctx); + } + @Test public void testRipemd160() { - assertArrayEquals(TEST_RIPEMD160, Security.ripemd160(TEST_VALUE)); + assertArrayEquals(TEST_RIPEMD160, security.ripemd160(TEST_VALUE)); } @Test public void testSha1() { - assertArrayEquals(TEST_SHA1, Security.sha1(TEST_VALUE)); + assertArrayEquals(TEST_SHA1, security.sha1(TEST_VALUE)); } @Test public void testSha512() { - assertArrayEquals(TEST_SHA512, Security.sha512(TEST_VALUE)); + assertArrayEquals(TEST_SHA512, security.sha512(TEST_VALUE)); } @Test public void testChaining() { - assertArrayEquals(TEST_SHA512, Security.sha512("test".getBytes(), "string".getBytes())); + assertArrayEquals(TEST_SHA512, security.sha512("test".getBytes(), "string".getBytes())); } @Test public void testDoubleHash() { - assertArrayEquals(Security.sha512(TEST_SHA512), Security.doubleSha512(TEST_VALUE)); + assertArrayEquals(security.sha512(TEST_SHA512), security.doubleSha512(TEST_VALUE)); } @Test(expected = IOException.class) @@ -75,7 +74,7 @@ public class SecurityTest { .objectType(0) .payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0)) .build(); - Security.checkProofOfWork(objectMessage, 1000, 1000); + security.checkProofOfWork(objectMessage, 1000, 1000); } @Test @@ -86,14 +85,7 @@ public class SecurityTest { .objectType(0) .payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0)) .build(); - Security.doProofOfWork(objectMessage, new MultiThreadedPOWEngine(), 1000, 1000); - Security.checkProofOfWork(objectMessage, 1000, 1000); + security.doProofOfWork(objectMessage, 1000, 1000); + security.checkProofOfWork(objectMessage, 1000, 1000); } - - @Test - public void testECIES() throws Exception { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECIES", "BC"); -// kpg.initialize(); - kpg.generateKeyPair(); - } -} +} \ No newline at end of file diff --git a/security-sc/build.gradle b/security-sc/build.gradle new file mode 100644 index 0000000..bfbdcab --- /dev/null +++ b/security-sc/build.gradle @@ -0,0 +1,17 @@ +uploadArchives { + repositories { + mavenDeployer { + pom.project { + name 'Jabit Spongy Security' + artifactId = 'jabit-security-spongy' + description 'The Security implementation using spongy castle (needed for Android)' + } + } + } +} + +dependencies { + compile project(':domain') + compile 'com.madgag.spongycastle:prov:1.52.0.0' + testCompile 'junit:junit:4.11' +} diff --git a/security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java b/security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java new file mode 100644 index 0000000..70a0743 --- /dev/null +++ b/security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java @@ -0,0 +1,153 @@ +/* + * Copyright 2015 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.security.sc; + +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.entity.valueobject.PrivateKey; +import ch.dissem.bitmessage.ports.AbstractSecurity; +import org.spongycastle.asn1.x9.X9ECParameters; +import org.spongycastle.crypto.BufferedBlockCipher; +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.InvalidCipherTextException; +import org.spongycastle.crypto.ec.CustomNamedCurves; +import org.spongycastle.crypto.engines.AESEngine; +import org.spongycastle.crypto.modes.CBCBlockCipher; +import org.spongycastle.crypto.paddings.PKCS7Padding; +import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; +import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.spongycastle.jce.spec.ECParameterSpec; +import org.spongycastle.jce.spec.ECPrivateKeySpec; +import org.spongycastle.jce.spec.ECPublicKeySpec; +import org.spongycastle.math.ec.ECPoint; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.KeySpec; +import java.util.Arrays; + +/** + * As Spongycastle can't be used on the Oracle JVM, and Bouncycastle doesn't work properly on Android (thanks, Google), + * this is the Spongycastle implementation. + */ +public class SpongySecurity extends AbstractSecurity { + private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1"); + + static { + java.security.Security.addProvider(new BouncyCastleProvider()); + } + + public SpongySecurity() { + super("SC"); + } + + @Override + public byte[] crypt(boolean encrypt, byte[] data, byte[] key_e, byte[] initializationVector) { + BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); + + CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector); + + cipher.init(encrypt, params); + + byte[] buffer = new byte[cipher.getOutputSize(data.length)]; + int length = cipher.processBytes(data, 0, data.length, buffer, 0); + try { + length += cipher.doFinal(buffer, length); + } catch (InvalidCipherTextException e) { + throw new IllegalArgumentException(e); + } + if (length < buffer.length) { + return Arrays.copyOfRange(buffer, 0, length); + } + return buffer; + } + + @Override + public byte[] createPublicKey(byte[] privateKey) { + return EC_CURVE_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize().getEncoded(false); + } + + private ECPoint keyToPoint(byte[] publicKey) { + BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKey, 1, 33)); + BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKey, 33, 65)); + return EC_CURVE_PARAMETERS.getCurve().createPoint(x, y); + } + + @Override + public boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey) { + try { + ECParameterSpec spec = new ECParameterSpec( + EC_CURVE_PARAMETERS.getCurve(), + EC_CURVE_PARAMETERS.getG(), + EC_CURVE_PARAMETERS.getN(), + EC_CURVE_PARAMETERS.getH(), + EC_CURVE_PARAMETERS.getSeed() + ); + + ECPoint Q = keyToPoint(pubkey.getSigningKey()); + KeySpec keySpec = new ECPublicKeySpec(Q, spec); + PublicKey publicKey = KeyFactory.getInstance("ECDSA", "SC").generatePublic(keySpec); + + Signature sig = Signature.getInstance("ECDSA", "SC"); + sig.initVerify(publicKey); + sig.update(data); + return sig.verify(signature); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public byte[] getSignature(byte[] data, PrivateKey privateKey) { + try { + ECParameterSpec spec = new ECParameterSpec( + EC_CURVE_PARAMETERS.getCurve(), + EC_CURVE_PARAMETERS.getG(), + EC_CURVE_PARAMETERS.getN(), + EC_CURVE_PARAMETERS.getH(), + EC_CURVE_PARAMETERS.getSeed() + ); + + BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey()); + KeySpec keySpec = new ECPrivateKeySpec(d, spec); + java.security.PrivateKey privKey = KeyFactory.getInstance("ECDSA", "SC").generatePrivate(keySpec); + + Signature sig = Signature.getInstance("ECDSA", "SC"); + sig.initSign(privKey); + sig.update(data); + return sig.sign(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public byte[] multiply(byte[] K, byte[] r) { + return keyToPoint(K).multiply(keyToBigInt(r)).normalize().getEncoded(false); + } + + @Override + public byte[] createPoint(byte[] x, byte[] y) { + return EC_CURVE_PARAMETERS.getCurve().createPoint( + new BigInteger(1, x), + new BigInteger(1, y) + ).getEncoded(false); + } +} diff --git a/settings.gradle b/settings.gradle index 6452ec0..2d3e1f6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,3 +9,7 @@ include 'repositories' include 'demo' include 'wif' + +include 'security-sc' + +include 'security-bc' diff --git a/wif/build.gradle b/wif/build.gradle index 393928f..5b32a21 100644 --- a/wif/build.gradle +++ b/wif/build.gradle @@ -15,4 +15,5 @@ dependencies { compile 'org.ini4j:ini4j:0.5.4' testCompile 'junit:junit:4.11' testCompile 'org.mockito:mockito-core:1.10.19' + testCompile project(':security-bc') } diff --git a/wif/src/main/java/ch/dissem/bitmessage/wif/WifExporter.java b/wif/src/main/java/ch/dissem/bitmessage/wif/WifExporter.java index 26b6d63..b41d645 100644 --- a/wif/src/main/java/ch/dissem/bitmessage/wif/WifExporter.java +++ b/wif/src/main/java/ch/dissem/bitmessage/wif/WifExporter.java @@ -19,7 +19,6 @@ package ch.dissem.bitmessage.wif; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.utils.Base58; -import ch.dissem.bitmessage.utils.Security; import org.ini4j.Ini; import org.ini4j.Profile; @@ -27,6 +26,7 @@ import java.io.*; import java.util.Collection; import static ch.dissem.bitmessage.entity.valueobject.PrivateKey.PRIVATE_KEY_SIZE; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * @author Christian Basler @@ -73,7 +73,7 @@ public class WifExporter { byte[] result = new byte[37]; result[0] = (byte) 0x80; System.arraycopy(privateKey, 0, result, 1, PRIVATE_KEY_SIZE); - byte[] hash = Security.doubleSha256(result, PRIVATE_KEY_SIZE + 1); + byte[] hash = security().doubleSha256(result, PRIVATE_KEY_SIZE + 1); System.arraycopy(hash, 0, result, PRIVATE_KEY_SIZE + 1, 4); return Base58.encode(result); } diff --git a/wif/src/main/java/ch/dissem/bitmessage/wif/WifImporter.java b/wif/src/main/java/ch/dissem/bitmessage/wif/WifImporter.java index 45bbb4d..e88eaa4 100644 --- a/wif/src/main/java/ch/dissem/bitmessage/wif/WifImporter.java +++ b/wif/src/main/java/ch/dissem/bitmessage/wif/WifImporter.java @@ -21,7 +21,6 @@ import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.utils.Base58; -import ch.dissem.bitmessage.utils.Security; import org.ini4j.Ini; import org.ini4j.Profile; import org.slf4j.Logger; @@ -34,6 +33,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** * @author Christian Basler */ @@ -84,7 +85,7 @@ public class WifImporter { if (bytes.length != 37) throw new IOException("Unknown format: 37 bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long"); - byte[] hash = Security.doubleSha256(bytes, 33); + byte[] hash = security().doubleSha256(bytes, 33); for (int i = 0; i < 4; i++) { if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat); } 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 8cb9264..5ed9025 100644 --- a/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java +++ b/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java @@ -18,6 +18,7 @@ package ch.dissem.bitmessage.wif; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.ports.*; +import ch.dissem.bitmessage.security.bc.BouncySecurity; import org.junit.Before; import org.junit.Test; @@ -34,6 +35,7 @@ public class WifExporterTest { @Before public void setUp() throws Exception { ctx = new BitmessageContext.Builder() + .security(new BouncySecurity()) .networkHandler(mock(NetworkHandler.class)) .inventory(mock(Inventory.class)) .messageRepo(mock(MessageRepository.class)) diff --git a/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java b/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java index 2973efc..862b3e3 100644 --- a/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java +++ b/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java @@ -19,6 +19,7 @@ package ch.dissem.bitmessage.wif; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.ports.*; +import ch.dissem.bitmessage.security.bc.BouncySecurity; import org.junit.Before; import org.junit.Test; @@ -37,6 +38,7 @@ public class WifImporterTest { @Before public void setUp() throws Exception { ctx = new BitmessageContext.Builder() + .security(new BouncySecurity()) .networkHandler(mock(NetworkHandler.class)) .inventory(mock(Inventory.class)) .messageRepo(mock(MessageRepository.class)) From 4911c268c2373192f6e1d6e24feed8624a6526c9 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 5 Aug 2015 19:55:53 +0200 Subject: [PATCH 03/42] Changed repositories to work with SQLDroid, which seems to have very limited support for blobs, at least it didn't work when I used stream. --- .../ch/dissem/bitmessage/utils/BytesTest.java | 21 ++++++++++++++++++- .../repository/JdbcAddressRepository.java | 16 +++++++------- .../bitmessage/repository/JdbcHelper.java | 3 +-- .../bitmessage/repository/JdbcInventory.java | 9 ++++---- .../repository/JdbcMessageRepository.java | 5 +++-- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java index 52eb106..1af8d37 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java @@ -16,6 +16,7 @@ package ch.dissem.bitmessage.utils; +import org.junit.Ignore; import org.junit.Test; import java.io.IOException; @@ -31,7 +32,7 @@ public class BytesTest { @Test public void ensureExpandsCorrectly() { byte[] source = {1}; - byte[] expected = {0,1}; + byte[] expected = {0, 1}; assertArrayEquals(expected, Bytes.expand(source, 2)); } @@ -53,6 +54,24 @@ public class BytesTest { } } + /** + * This test is used to compare different implementations of the single byte lt comparison. It an safely be ignored. + */ + @Test + @Ignore + public void testLowerThanSingleByte() { + byte[] a = new byte[1]; + byte[] b = new byte[1]; + for (int i = 0; i < 255; i++) { + for (int j = 0; j < 255; j++) { + System.out.println("a = " + i + "\tb = " + j); + a[0] = (byte) i; + b[0] = (byte) j; + assertEquals(i < j, Bytes.lt(a, b)); + } + } + } + @Test public void testLowerThan() { for (int i = 0; i < 1000; i++) { diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java index f1e55d6..099690d 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java @@ -96,16 +96,17 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito ResultSet rs = stmt.executeQuery("SELECT address, alias, public_key, private_key, subscribed FROM Address WHERE " + where); while (rs.next()) { BitmessageAddress address; - Blob privateKeyBlob = rs.getBlob("private_key"); - if (privateKeyBlob != null) { - PrivateKey privateKey = PrivateKey.read(privateKeyBlob.getBinaryStream()); + + byte[] privateKeyBytes = rs.getBytes("private_key"); + if (privateKeyBytes != null) { + PrivateKey privateKey = PrivateKey.read(new ByteArrayInputStream(privateKeyBytes)); address = new BitmessageAddress(privateKey); } else { address = new BitmessageAddress(rs.getString("address")); - Blob publicKeyBlob = rs.getBlob("public_key"); - if (publicKeyBlob != null) { + byte[] publicKeyBytes = rs.getBytes("public_key"); + if (publicKeyBytes != null) { Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(), - publicKeyBlob.getBinaryStream(), (int) publicKeyBlob.length(), false); + new ByteArrayInputStream(publicKeyBytes), publicKeyBytes.length, false); if (address.getVersion() == 4 && pubkey instanceof V3Pubkey) { pubkey = new V4Pubkey((V3Pubkey) pubkey); } @@ -179,8 +180,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito if (data != null) { ByteArrayOutputStream out = new ByteArrayOutputStream(); data.writeUnencrypted(out); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - ps.setBlob(parameterIndex, in); + ps.setBytes(parameterIndex, out.toByteArray()); } else { ps.setBlob(parameterIndex, (Blob) null); } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java index 4fbea30..b6e7461 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java @@ -82,8 +82,7 @@ abstract class JdbcHelper { if (data != null) { ByteArrayOutputStream os = new ByteArrayOutputStream(); data.write(os); - byte[] bytes = os.toByteArray(); - ps.setBinaryStream(parameterIndex, new ByteArrayInputStream(bytes), bytes.length); + ps.setBytes(parameterIndex, os.toByteArray()); } else { ps.setBlob(parameterIndex, (Blob) null); } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java index c7e8873..6a9e35c 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -24,6 +24,7 @@ import ch.dissem.bitmessage.ports.Inventory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayInputStream; import java.sql.*; import java.util.LinkedList; import java.util.List; @@ -78,8 +79,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory { Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = X'" + vector + "'"); if (rs.next()) { - Blob data = rs.getBlob("data"); - return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()); + byte[] data = rs.getBytes("data"); + return Factory.getObjectMessage(rs.getInt("version"), new ByteArrayInputStream(data), data.length); } else { LOG.info("Object requested that we don't have. IV: " + vector); return null; @@ -107,8 +108,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory { ResultSet rs = stmt.executeQuery(query.toString()); List<ObjectMessage> result = new LinkedList<>(); while (rs.next()) { - Blob data = rs.getBlob("data"); - result.add(Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length())); + byte[] data = rs.getBytes("data"); + result.add(Factory.getObjectMessage(rs.getInt("version"), new ByteArrayInputStream(data), data.length)); } return result; } catch (Exception e) { diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index 278163f..a2476e5 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -25,6 +25,7 @@ import ch.dissem.bitmessage.ports.MessageRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.sql.*; import java.util.ArrayList; @@ -106,9 +107,9 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito ResultSet rs = stmt.executeQuery("SELECT id, iv, type, sender, recipient, data, sent, received, status FROM Message WHERE " + where); while (rs.next()) { byte[] iv = rs.getBytes("iv"); - Blob data = rs.getBlob("data"); + byte[] data = rs.getBytes("data"); Plaintext.Type type = Plaintext.Type.valueOf(rs.getString("type")); - Plaintext.Builder builder = Plaintext.readWithoutSignature(type, data.getBinaryStream()); + Plaintext.Builder builder = Plaintext.readWithoutSignature(type, new ByteArrayInputStream(data)); long id = rs.getLong("id"); builder.id(id); builder.IV(new InventoryVector(iv)); From f89d1a342ed3d596bae43c7c4ac08d47de6e88ca Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Fri, 28 Aug 2015 13:48:01 +0200 Subject: [PATCH 04/42] Fixed a few problems: - some bugs that creeped in when I moved security into its own adapter - improved some DB code as it doesn't work in Android anyway - all entities should be serializable (very useful in Android) --- demo/build.gradle | 2 ++ .../dissem/bitmessage/demo/Application.java | 3 ++ .../java/ch/dissem/bitmessage/demo/Main.java | 3 ++ .../ch/dissem/bitmessage/InternalContext.java | 2 +- .../dissem/bitmessage/entity/Plaintext.java | 14 +++++++-- .../dissem/bitmessage/entity/Streamable.java | 5 ++-- .../entity/payload/ObjectPayload.java | 1 + .../bitmessage/entity/valueobject/Label.java | 3 +- .../bitmessage/ports}/MemoryNodeRegistry.java | 3 +- .../bitmessage/ports/NetworkHandler.java | 3 ++ .../ch/dissem/bitmessage/utils/Bytes.java | 8 ++--- .../ch/dissem/bitmessage/utils/Encode.java | 2 ++ .../src/main/resources/nodes.txt | 0 .../bitmessage/entity/SerializationTest.java | 29 +++++++++++++++---- .../networking/DefaultNetworkHandler.java | 6 ++++ .../repository/JdbcAddressRepository.java | 15 +++++----- .../bitmessage/repository/JdbcHelper.java | 4 +-- .../bitmessage/repository/JdbcInventory.java | 9 +++--- .../repository/JdbcMessageRepository.java | 8 +++-- .../repository/JdbcNodeRegistryTest.java | 1 + 20 files changed, 83 insertions(+), 38 deletions(-) rename {repositories/src/main/java/ch/dissem/bitmessage/repository => domain/src/main/java/ch/dissem/bitmessage/ports}/MemoryNodeRegistry.java (98%) rename {repositories => domain}/src/main/resources/nodes.txt (100%) diff --git a/demo/build.gradle b/demo/build.gradle index 8d6414c..cba7d93 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -22,8 +22,10 @@ dependencies { compile project(':domain') compile project(':networking') compile project(':repositories') + compile project(':security-bc') compile project(':wif') compile 'org.slf4j:slf4j-simple:1.7.12' compile 'args4j:args4j:2.32' + compile 'com.h2database:h2:1.4.187' testCompile 'junit:junit:4.11' } 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 416e7b7..2f6f9fd 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -21,7 +21,9 @@ 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.security.bc.BouncySecurity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +48,7 @@ public class Application { .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) + .security(new BouncySecurity()) .port(48444) .build(); 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 7f934a3..ac90e88 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -18,7 +18,9 @@ package ch.dissem.bitmessage.demo; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.networking.DefaultNetworkHandler; +import ch.dissem.bitmessage.ports.MemoryNodeRegistry; import ch.dissem.bitmessage.repository.*; +import ch.dissem.bitmessage.security.bc.BouncySecurity; import ch.dissem.bitmessage.wif.WifExporter; import ch.dissem.bitmessage.wif.WifImporter; import org.kohsuke.args4j.CmdLineException; @@ -50,6 +52,7 @@ public class Main { .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) + .security(new BouncySecurity()) .port(48444) .build(); diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 03fef80..05ccd51 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -81,7 +81,7 @@ public class InternalContext { streams.add(1L); } - init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); + init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine, security); } private void init(Object... objects) { 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 e73849f..eb0a60f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -19,9 +19,9 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.factory.Factory; -import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Encode; +import ch.dissem.bitmessage.utils.UnixTime; import java.io.*; import java.util.*; @@ -29,7 +29,7 @@ import java.util.*; /** * The unencrypted message to be sent by 'msg' or 'broadcast'. */ -public class Plaintext implements Streamable, Serializable { +public class Plaintext implements Streamable { private final Type type; private final BitmessageAddress from; private final long encoding; @@ -64,6 +64,7 @@ public class Plaintext implements Streamable, Serializable { public static Plaintext read(Type type, InputStream in) throws IOException { return readWithoutSignature(type, in) .signature(Decode.varBytes(in)) + .received(UnixTime.now()) .build(); } @@ -132,6 +133,15 @@ public class Plaintext implements Streamable, Serializable { this.signature = signature; } + public boolean isUnread() { + for (Label label : labels) { + if (label.getType() == Label.Type.UNREAD) { + return true; + } + } + return false; + } + public void write(OutputStream out, boolean includeSignature) throws IOException { Encode.varInt(from.getVersion(), out); Encode.varInt(from.getStream(), out); diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java index 0601a44..cc12050 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java @@ -16,14 +16,13 @@ package ch.dissem.bitmessage.entity; -import ch.dissem.bitmessage.ports.Security; - import java.io.IOException; import java.io.OutputStream; +import java.io.Serializable; /** * An object that can be written to an {@link OutputStream} */ -public interface Streamable { +public interface Streamable extends Serializable { void write(OutputStream stream) throws IOException; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java index ef42718..0ca45cd 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.Streamable; import java.io.IOException; import java.io.OutputStream; +import java.io.Serializable; /** * The payload of an 'object' command. This is shared by the network. diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java index e1bd8f2..7c37973 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java @@ -16,9 +16,10 @@ package ch.dissem.bitmessage.entity.valueobject; +import java.io.Serializable; import java.util.Objects; -public class Label { +public class Label implements Serializable { private Object id; private String label; private Type type; diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/MemoryNodeRegistry.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java similarity index 98% rename from repositories/src/main/java/ch/dissem/bitmessage/repository/MemoryNodeRegistry.java rename to domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java index 15a4134..04fa2f9 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/MemoryNodeRegistry.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package ch.dissem.bitmessage.repository; +package ch.dissem.bitmessage.ports; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; -import ch.dissem.bitmessage.ports.NodeRegistry; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 4b73629..e07bd79 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.utils.Property; import java.io.IOException; +import java.net.InetAddress; /** * Handles incoming messages @@ -30,6 +31,8 @@ public interface NetworkHandler { void stop(); + void synchronize(InetAddress trustedHost, int port, MessageListener listener) throws IOException; + void offer(InventoryVector iv); Property getNetworkStatus(); diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java index 31a3dcc..8107eb0 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java @@ -16,12 +16,6 @@ package ch.dissem.bitmessage.utils; -import ch.dissem.bitmessage.entity.Streamable; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Arrays; - /** * A helper class for working with byte arrays interpreted as unsigned big endian integers. * This is one part due to the fact that Java doesn't support unsigned numbers, and another @@ -91,6 +85,8 @@ public class Bytes { if (a < 0) return b < 0 && a < b; if (b < 0) return a >= 0 || a < b; return a < b; + // This would be easier to understand, but is (slightly) slower: + // return (a & 0xff) < (b & 0xff); } /** diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java index 095fb78..a78d03f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java @@ -117,6 +117,8 @@ public class Encode { * @throws IOException if an I/O error occurs. */ public static byte[] bytes(Streamable streamable) throws IOException { + if (streamable == null) return null; + ByteArrayOutputStream stream = new ByteArrayOutputStream(); streamable.write(stream); return stream.toByteArray(); diff --git a/repositories/src/main/resources/nodes.txt b/domain/src/main/resources/nodes.txt similarity index 100% rename from repositories/src/main/resources/nodes.txt rename to domain/src/main/resources/nodes.txt diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java index 7e56f95..de0c807 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java @@ -17,21 +17,21 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.*; +import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.factory.Factory; +import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; +import java.util.Arrays; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -public class SerializationTest { +public class SerializationTest extends TestBase { @Test public void ensureGetPubkeyIsDeserializedAndSerializedCorrectly() throws IOException { doTest("V2GetPubkey.payload", 2, GetPubkey.class); @@ -75,7 +75,7 @@ public class SerializationTest { } @Test - public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws IOException, DecryptionFailedException { + public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws Exception { Plaintext p1 = new Plaintext.Builder(MSG) .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) .to(TestUtils.loadContact()) @@ -99,4 +99,21 @@ public class SerializationTest { assertArrayEquals(data, out.toByteArray()); assertEquals(expectedPayloadType.getCanonicalName(), object.getPayload().getClass().getCanonicalName()); } + + @Test + public void ensureSystemSerializationWorks() throws Exception { + Plaintext plaintext = new Plaintext.Builder(MSG) + .from(TestUtils.loadContact()) + .to(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .labels(Arrays.asList(new Label("Test", Label.Type.INBOX, 0))) + .message("Test", "Test Test.\nTest") + .build(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(out); + oos.writeObject(plaintext); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(in); + assertEquals(plaintext, ois.readObject()); + } } 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 558d097..e73e8ed 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -27,6 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.*; @@ -149,6 +150,11 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } } + @Override + public void synchronize(InetAddress trustedHost, int port, MessageListener listener) throws IOException { + startConnection(new Connection(ctx, CLIENT, new Socket(trustedHost, port), listener, requestedObjects)); + } + private void startConnection(Connection c) { synchronized (connections) { // prevent connecting twice to the same node diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java index 099690d..337d50a 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcAddressRepository.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.sql.*; import java.util.Arrays; import java.util.LinkedList; @@ -97,16 +98,16 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito while (rs.next()) { BitmessageAddress address; - byte[] privateKeyBytes = rs.getBytes("private_key"); - if (privateKeyBytes != null) { - PrivateKey privateKey = PrivateKey.read(new ByteArrayInputStream(privateKeyBytes)); + InputStream privateKeyStream = rs.getBinaryStream("private_key"); + if (privateKeyStream != null) { + PrivateKey privateKey = PrivateKey.read(privateKeyStream); address = new BitmessageAddress(privateKey); } else { address = new BitmessageAddress(rs.getString("address")); - byte[] publicKeyBytes = rs.getBytes("public_key"); - if (publicKeyBytes != null) { + Blob publicKeyBlob = rs.getBlob("public_key"); + if (publicKeyBlob != null) { Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(), - new ByteArrayInputStream(publicKeyBytes), publicKeyBytes.length, false); + publicKeyBlob.getBinaryStream(), (int) publicKeyBlob.length(), false); if (address.getVersion() == 4 && pubkey instanceof V3Pubkey) { pubkey = new V4Pubkey((V3Pubkey) pubkey); } @@ -182,7 +183,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito data.writeUnencrypted(out); ps.setBytes(parameterIndex, out.toByteArray()); } else { - ps.setBlob(parameterIndex, (Blob) null); + ps.setBytes(parameterIndex, null); } } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java index b6e7461..601ce29 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java @@ -21,10 +21,8 @@ import ch.dissem.bitmessage.entity.payload.ObjectType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.sql.Blob; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -84,7 +82,7 @@ abstract class JdbcHelper { data.write(os); ps.setBytes(parameterIndex, os.toByteArray()); } else { - ps.setBlob(parameterIndex, (Blob) null); + ps.setBytes(parameterIndex, null); } } } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java index 6a9e35c..98034ee 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -53,6 +53,7 @@ public class JdbcInventory extends JdbcHelper implements Inventory { } return result; } + private List<InventoryVector> getFullInventory(long... streams) { List<InventoryVector> result = new LinkedList<>(); try (Connection connection = config.getConnection()) { @@ -79,8 +80,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory { Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = X'" + vector + "'"); if (rs.next()) { - byte[] data = rs.getBytes("data"); - return Factory.getObjectMessage(rs.getInt("version"), new ByteArrayInputStream(data), data.length); + Blob data = rs.getBlob("data"); + return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()); } else { LOG.info("Object requested that we don't have. IV: " + vector); return null; @@ -108,8 +109,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory { ResultSet rs = stmt.executeQuery(query.toString()); List<ObjectMessage> result = new LinkedList<>(); while (rs.next()) { - byte[] data = rs.getBytes("data"); - result.add(Factory.getObjectMessage(rs.getInt("version"), new ByteArrayInputStream(data), data.length)); + Blob data = rs.getBlob("data"); + result.add(Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length())); } return result; } catch (Exception e) { diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index a2476e5..ec89e68 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.sql.*; import java.util.ArrayList; import java.util.Collection; @@ -107,9 +108,9 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito ResultSet rs = stmt.executeQuery("SELECT id, iv, type, sender, recipient, data, sent, received, status FROM Message WHERE " + where); while (rs.next()) { byte[] iv = rs.getBytes("iv"); - byte[] data = rs.getBytes("data"); + InputStream data = rs.getBinaryStream("data"); Plaintext.Type type = Plaintext.Type.valueOf(rs.getString("type")); - Plaintext.Builder builder = Plaintext.readWithoutSignature(type, new ByteArrayInputStream(data)); + Plaintext.Builder builder = Plaintext.readWithoutSignature(type, data); long id = rs.getLong("id"); builder.id(id); builder.IV(new InventoryVector(iv)); @@ -191,7 +192,8 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito private void insert(Connection connection, Plaintext message) throws SQLException, IOException { PreparedStatement ps = connection.prepareStatement( - "INSERT INTO Message (iv, type, sender, recipient, data, sent, received, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + "INSERT INTO Message (iv, type, sender, recipient, data, sent, received, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + Statement.RETURN_GENERATED_KEYS); ps.setBytes(1, message.getInventoryVector() != null ? message.getInventoryVector().getHash() : null); ps.setString(2, message.getType().name()); ps.setString(3, message.getFrom().getAddress()); diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java index ca4bcd7..044c44c 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcNodeRegistryTest.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.repository; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; +import ch.dissem.bitmessage.ports.MemoryNodeRegistry; import ch.dissem.bitmessage.ports.NodeRegistry; import org.junit.Before; import org.junit.Test; From d67c932fb211c555971ba9be6283f87834e7c0d8 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 24 Sep 2015 08:09:20 +0200 Subject: [PATCH 05/42] Added synchronization code and unit test. Synchronisation fails if the trusted host has no new messages - this needs to be fixed (but shouldn't be an issue for real world applications) --- .../dissem/bitmessage/BitmessageContext.java | 40 +++-- .../ch/dissem/bitmessage/InternalContext.java | 31 ++-- .../ch/dissem/bitmessage/MessageCallback.java | 52 +++++++ .../bitmessage/ports/NetworkHandler.java | 16 +- .../ch/dissem/bitmessage/utils/Property.java | 19 +++ networking/build.gradle | 4 +- .../bitmessage/networking/Connection.java | 65 +++++--- .../networking/DefaultNetworkHandler.java | 16 +- .../networking/DefaultNetworkHandlerTest.java | 64 -------- .../networking/NetworkHandlerTest.java | 147 ++++++++++++++++++ .../bitmessage/networking/TestInventory.java | 76 +++++++++ .../networking/TestNodeRegistry.java | 44 ++++++ networking/src/test/resources/V1Msg.payload | Bin 0 -> 444 bytes .../src/test/resources/V4Pubkey.payload | Bin 0 -> 396 bytes .../src/test/resources/V5Broadcast.payload | Bin 0 -> 428 bytes 15 files changed, 457 insertions(+), 117 deletions(-) create mode 100644 domain/src/main/java/ch/dissem/bitmessage/MessageCallback.java delete mode 100644 networking/src/test/java/ch/dissem/bitmessage/networking/DefaultNetworkHandlerTest.java create mode 100644 networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java create mode 100644 networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java create mode 100644 networking/src/test/java/ch/dissem/bitmessage/networking/TestNodeRegistry.java create mode 100644 networking/src/test/resources/V1Msg.payload create mode 100644 networking/src/test/resources/V4Pubkey.payload create mode 100644 networking/src/test/resources/V5Broadcast.payload diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index d7c04b5..2cd851e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -21,13 +21,13 @@ import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.*; 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.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.utils.Property; -import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -179,19 +179,6 @@ public class BitmessageContext { ); } - private void send(long stream, ObjectPayload payload, long timeToLive) { - long expires = UnixTime.now(+timeToLive); - LOG.info("Expires at " + expires); - ObjectMessage object = new ObjectMessage.Builder() - .stream(stream) - .expiresTime(expires) - .payload(payload) - .build(); - ctx.getSecurity().doProofOfWork(object, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); - ctx.getInventory().storeObject(object); - ctx.getNetworkHandler().offer(object.getInventoryVector()); - } - public void startup(Listener listener) { this.listener = listener; ctx.getNetworkHandler().start(new DefaultMessageListener(ctx, listener)); @@ -280,6 +267,7 @@ public class BitmessageContext { MessageRepository messageRepo; ProofOfWorkEngine proofOfWorkEngine; Security security; + MessageCallback messageCallback; public Builder() { } @@ -319,6 +307,11 @@ public class BitmessageContext { return this; } + public Builder messageCallback(MessageCallback callback) { + this.messageCallback = callback; + return this; + } + public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) { this.proofOfWorkEngine = proofOfWorkEngine; return this; @@ -333,6 +326,25 @@ public class BitmessageContext { if (proofOfWorkEngine == null) { proofOfWorkEngine = new MultiThreadedPOWEngine(); } + if (messageCallback == null) { + messageCallback = new MessageCallback() { + @Override + public void proofOfWorkStarted(ObjectPayload message) { + } + + @Override + public void proofOfWorkCompleted(ObjectPayload message) { + } + + @Override + public void messageOffered(ObjectPayload message, InventoryVector iv) { + } + + @Override + public void messageAcknowledged(InventoryVector iv) { + } + }; + } return new BitmessageContext(this); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 05ccd51..403e9c4 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -49,12 +49,13 @@ public class InternalContext { private final AddressRepository addressRepository; private final MessageRepository messageRepository; private final ProofOfWorkEngine proofOfWorkEngine; + private final MessageCallback messageCallback; private final TreeSet<Long> streams = new TreeSet<>(); private final int port; - private long networkNonceTrialsPerByte = 1000; - private long networkExtraBytes = 1000; - private long clientNonce; + private final long clientNonce; + private final long networkNonceTrialsPerByte = 1000; + private final long networkExtraBytes = 1000; public InternalContext(BitmessageContext.Builder builder) { this.security = builder.security; @@ -65,11 +66,11 @@ public class InternalContext { this.messageRepository = builder.messageRepo; this.proofOfWorkEngine = builder.proofOfWorkEngine; this.clientNonce = security.randomNonce(); + this.messageCallback = builder.messageCallback; + this.port = builder.port; Singleton.initialize(security); - port = builder.port; - // TODO: streams of new identities and subscriptions should also be added. This works only after a restart. for (BitmessageAddress address : addressRepository.getIdentities()) { streams.add(address.getStream()); @@ -81,7 +82,10 @@ public class InternalContext { streams.add(1L); } - init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine, security); + init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); + for (BitmessageAddress identity : addressRepository.getIdentities()) { + streams.add(identity.getStream()); + } } private void init(Object... objects) { @@ -169,7 +173,9 @@ public class InternalContext { } else if (payload instanceof Encrypted) { object.encrypt(to.getPubkey()); } + messageCallback.proofOfWorkStarted(payload); security.doProofOfWork(object, nonceTrialsPerByte, extraBytes); + messageCallback.proofOfWorkCompleted(payload); if (payload instanceof PlaintextHolder) { Plaintext plaintext = ((PlaintextHolder) payload).getPlaintext(); plaintext.setInventoryVector(object.getInventoryVector()); @@ -177,6 +183,7 @@ public class InternalContext { } inventory.storeObject(object); networkHandler.offer(object.getInventoryVector()); + messageCallback.messageOffered(payload, object.getInventoryVector()); } catch (IOException e) { throw new RuntimeException(e); } @@ -193,16 +200,13 @@ public class InternalContext { .build(); response.sign(identity.getPrivateKey()); response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); + messageCallback.proofOfWorkStarted(identity.getPubkey()); security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); - if (response.isSigned()) { - response.sign(identity.getPrivateKey()); - } - if (response instanceof Encrypted) { - response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); - } + messageCallback.proofOfWorkCompleted(identity.getPubkey()); inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); // TODO: save that the pubkey was just sent, and on which stream! + messageCallback.messageOffered(identity.getPubkey(), response.getInventoryVector()); } catch (IOException e) { throw new RuntimeException(e); } @@ -216,9 +220,12 @@ public class InternalContext { .expiresTime(expires) .payload(new GetPubkey(contact)) .build(); + messageCallback.proofOfWorkStarted(response.getPayload()); security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); + messageCallback.proofOfWorkCompleted(response.getPayload()); inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); + messageCallback.messageOffered(response.getPayload(), response.getInventoryVector()); } public long getClientNonce() { diff --git a/domain/src/main/java/ch/dissem/bitmessage/MessageCallback.java b/domain/src/main/java/ch/dissem/bitmessage/MessageCallback.java new file mode 100644 index 0000000..d09ff97 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/MessageCallback.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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; + +import ch.dissem.bitmessage.entity.payload.ObjectPayload; +import ch.dissem.bitmessage.entity.valueobject.InventoryVector; + +/** + * Callback for message sending events, mostly so the user can be notified when POW is done. + */ +public interface MessageCallback { + /** + * Called before calculation of proof of work begins. + */ + void proofOfWorkStarted(ObjectPayload message); + + /** + * Called after calculation of proof of work finished. + */ + void proofOfWorkCompleted(ObjectPayload message); + + /** + * Called once the message is offered to the network. Please note that this doesn't mean the message was sent, + * if the client is not connected to the network it's just stored in the inventory. + * <p> + * Also, please note that this is where the original payload as well as the {@link InventoryVector} of the sent + * message is available. If the callback needs the IV for some reason, it should be retrieved here. (Plaintext + * and Broadcast messages will have their IV property set automatically though.) + * </p> + */ + void messageOffered(ObjectPayload message, InventoryVector iv); + + /** + * This isn't called yet, as ACK messages aren't being processed yet. Also, this is only relevant for Plaintext + * messages. + */ + void messageAcknowledged(InventoryVector iv); +} 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 e07bd79..fd44358 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java @@ -27,12 +27,24 @@ import java.net.InetAddress; * Handles incoming messages */ public interface NetworkHandler { + /** + * Connects to the trusted host, fetches and offers new messages and disconnects afterwards. + */ + Thread synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds); + + /** + * Start a full network node, accepting incoming connections and relaying objects. + */ void start(MessageListener listener); + /** + * Stop the full network node. + */ void stop(); - void synchronize(InetAddress trustedHost, int port, MessageListener listener) throws IOException; - + /** + * Offer new objects to up to 8 random nodes. + */ void offer(InventoryVector iv); Property getNetworkStatus(); diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java index 6fa0ec4..72abe58 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java @@ -35,6 +35,25 @@ public class Property { this.properties = properties; } + public String getName() { + return name; + } + + public Object getValue() { + return value; + } + + public Property getProperty(String name) { + for (Property p : properties) { + if (name == null) { + if (p.name == null) return p; + } else { + if (name.equals(p.name)) return p; + } + } + return null; + } + @Override public String toString() { return toString(""); diff --git a/networking/build.gradle b/networking/build.gradle index 07d268e..6e9366c 100644 --- a/networking/build.gradle +++ b/networking/build.gradle @@ -12,6 +12,8 @@ uploadArchives { dependencies { compile project(':domain') - testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'junit:junit:4.11' + testCompile 'org.slf4j:slf4j-simple:1.7.12' + testCompile 'org.mockito:mockito-core:1.10.19' + testCompile project(':security-bc') } \ No newline at end of file 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 d8054ff..bb8e62c 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; @@ -54,43 +55,55 @@ public class Connection implements Runnable { private final static Logger LOG = LoggerFactory.getLogger(Connection.class); private static final int CONNECT_TIMEOUT = 5000; private final ConcurrentMap<InventoryVector, Long> ivCache; - private InternalContext ctx; - private Mode mode; + private final InternalContext ctx; + private final Mode mode; + private final Socket socket; + private final MessageListener listener; + private final NetworkAddress host; + private final NetworkAddress node; + private final Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>(); + private final Map<InventoryVector, Long> requestedObjects; + private final long syncTimeout; + private State state; - private Socket socket; private InputStream in; private OutputStream out; - private MessageListener listener; private int version; private long[] streams; - private NetworkAddress host; - private NetworkAddress node; - private Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>(); - private ConcurrentMap<InventoryVector, Long> requestedObjects; public Connection(InternalContext context, Mode mode, Socket socket, MessageListener listener, ConcurrentMap<InventoryVector, Long> requestedObjectsMap) throws IOException { - this(context, mode, listener, requestedObjectsMap); - this.socket = socket; - this.node = new NetworkAddress.Builder().ip(socket.getInetAddress()).port(socket.getPort()).stream(1).build(); + this(context, mode, listener, socket, requestedObjectsMap, + new NetworkAddress.Builder().ip(socket.getInetAddress()).port(socket.getPort()).stream(1).build(), + 0); } public Connection(InternalContext context, Mode mode, NetworkAddress node, MessageListener listener, ConcurrentMap<InventoryVector, Long> requestedObjectsMap) { - this(context, mode, listener, requestedObjectsMap); - this.socket = new Socket(); - this.node = node; + this(context, mode, listener, new Socket(), requestedObjectsMap, + node, 0); } - private Connection(InternalContext context, Mode mode, MessageListener listener, - ConcurrentMap<InventoryVector, Long> requestedObjectsMap) { + private Connection(InternalContext context, Mode mode, MessageListener listener, Socket socket, + Map<InventoryVector, Long> requestedObjectsMap, NetworkAddress node, long syncTimeout) { this.ctx = context; this.mode = mode; this.state = CONNECTING; this.listener = listener; + this.socket = socket; this.requestedObjects = requestedObjectsMap; this.host = new NetworkAddress.Builder().ipv6(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).port(0).build(); - ivCache = new ConcurrentHashMap<>(); + this.node = node; + this.syncTimeout = (syncTimeout > 0 ? UnixTime.now(+syncTimeout) : 0); + this.ivCache = new ConcurrentHashMap<>(); + } + + public static Connection sync(InternalContext ctx, InetAddress address, int port, MessageListener listener, + long timeoutInSeconds) throws IOException { + return new Connection(ctx, Mode.CLIENT, listener, new Socket(address, port), + new HashMap<InventoryVector, Long>(), + new NetworkAddress.Builder().ip(address).port(port).stream(1).build(), + timeoutInSeconds); } public Mode getMode() { @@ -168,7 +181,7 @@ public class Connection implements Runnable { + msg.getPayload().getCommand() + "'"); } } - if (socket.isClosed()) state = DISCONNECTED; + if (socket.isClosed() || syncFinished(msg)) disconnect(); } catch (SocketTimeoutException ignore) { if (state == ACTIVE) { sendQueue(); @@ -184,13 +197,27 @@ public class Connection implements Runnable { } } + @SuppressWarnings("RedundantIfStatement") + private boolean syncFinished(NetworkMessage msg) { + if (syncTimeout == 0 || state != ACTIVE) { + return false; + } + if (syncTimeout < UnixTime.now()) { + return true; + } + if (!(msg.getPayload() instanceof Addr) && requestedObjects.isEmpty() && sendingQueue.isEmpty()) { + return true; + } + return false; + } + private void activateConnection() { LOG.info("Successfully established connection with node " + node); state = ACTIVE; sendAddresses(); sendInventory(); node.setTime(UnixTime.now()); - ctx.getNodeRegistry().offerAddresses(Arrays.asList(node)); + ctx.getNodeRegistry().offerAddresses(Collections.singletonList(node)); } private void sendQueue() { 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 e73e8ed..aead8e8 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -66,6 +66,17 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { this.ctx = context; } + @Override + public Thread synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds) { + try { + Thread t = new Thread(Connection.sync(ctx, trustedHost, port, listener, timeoutInSeconds)); + t.start(); + return t; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @Override public void start(final MessageListener listener) { if (listener == null) { @@ -150,11 +161,6 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } } - @Override - public void synchronize(InetAddress trustedHost, int port, MessageListener listener) throws IOException { - startConnection(new Connection(ctx, CLIENT, new Socket(trustedHost, port), listener, requestedObjects)); - } - private void startConnection(Connection c) { synchronized (connections) { // prevent connecting twice to the same node diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/DefaultNetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/DefaultNetworkHandlerTest.java deleted file mode 100644 index 3bec993..0000000 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/DefaultNetworkHandlerTest.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2015 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.networking; - -import ch.dissem.bitmessage.entity.NetworkMessage; -import ch.dissem.bitmessage.entity.Version; -import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; -import ch.dissem.bitmessage.utils.UnixTime; -import org.junit.Ignore; -import org.junit.Test; - -/** - * FIXME: there really should be sensible tests for the network handler - */ -public class DefaultNetworkHandlerTest { - private NetworkAddress localhost = new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(8444).build(); - - // void start(MessageListener listener); - // void stop(); - // void offer(InventoryVector iv); - // Property getNetworkStatus(); - - @Ignore - @Test(expected = InterruptedException.class) - public void testSendMessage() throws Exception { - final Thread baseThread = Thread.currentThread(); - DefaultNetworkHandler net = new DefaultNetworkHandler(); -// net.setListener(localhost, new NetworkHandler.MessageListener() { -// @Override -// public void receive(ObjectPayload payload) { -// System.out.println(payload); -// baseThread.interrupt(); -// } -// }); - NetworkMessage ver = new NetworkMessage( - new Version.Builder() - .version(3) - .services(1) - .timestamp(UnixTime.now()) - .addrFrom(localhost) - .addrRecv(localhost) - .nonce(-1) - .userAgent("Test") - .streams(1, 2) - .build() - ); -// net.send(localhost, ver); - Thread.sleep(20000); - } -} diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java new file mode 100644 index 0000000..cd8a743 --- /dev/null +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2015 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.networking; + +import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; +import ch.dissem.bitmessage.ports.AddressRepository; +import ch.dissem.bitmessage.ports.MessageRepository; +import ch.dissem.bitmessage.ports.NetworkHandler; +import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.utils.Property; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +import java.net.InetAddress; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +/** + * FIXME: there really should be sensible tests for the network handler + */ +public class NetworkHandlerTest { + private static NetworkAddress localhost = new NetworkAddress.Builder().ipv4(127, 0, 0, 1).port(6001).build(); + + private static TestInventory peerInventory; + private static TestInventory nodeInventory; + + private static BitmessageContext node; + private static NetworkHandler networkHandler; + + @BeforeClass + public static void setUp() { + peerInventory = new TestInventory(); + BitmessageContext peer = new BitmessageContext.Builder() + .addressRepo(Mockito.mock(AddressRepository.class)) + .inventory(peerInventory) + .messageRepo(Mockito.mock(MessageRepository.class)) + .port(6001) + .nodeRegistry(new TestNodeRegistry()) + .networkHandler(new DefaultNetworkHandler()) + .security(new BouncySecurity()) + .build(); + peer.startup(Mockito.mock(BitmessageContext.Listener.class)); + + nodeInventory = new TestInventory(); + networkHandler = new DefaultNetworkHandler(); + node = new BitmessageContext.Builder() + .addressRepo(Mockito.mock(AddressRepository.class)) + .inventory(nodeInventory) + .messageRepo(Mockito.mock(MessageRepository.class)) + .port(6002) + .nodeRegistry(new TestNodeRegistry(localhost)) + .networkHandler(networkHandler) + .security(new BouncySecurity()) + .build(); + } + + @Test(timeout = 20_000) + public void ensureNodesAreConnecting() { + try { + node.startup(Mockito.mock(BitmessageContext.Listener.class)); + Property status; + do { + Thread.yield(); + status = node.status().getProperty("network").getProperty("connections").getProperty("stream 0"); + } while (status == null); + assertEquals(1, status.getProperty("outgoing").getValue()); + } finally { + shutdown(node); + } + } + + @Test(timeout = 5_000) + public void ensureObjectsAreSynchronizedIfBothHaveObjects() throws Exception { + peerInventory.init( + "V4Pubkey.payload", + "V5Broadcast.payload" + ); + + nodeInventory.init( + "V1Msg.payload" + ); + + Thread t = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, + mock(NetworkHandler.MessageListener.class), + 10); + t.join(); + assertEquals(3, nodeInventory.getInventory().size()); + assertEquals(3, peerInventory.getInventory().size()); + } + + @Test(timeout = 5_000) + public void ensureObjectsAreSynchronizedIfOnlyPeerHasObjects() throws Exception { + peerInventory.init( + "V4Pubkey.payload", + "V5Broadcast.payload" + ); + + nodeInventory.init(); + + Thread t = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, + mock(NetworkHandler.MessageListener.class), + 10); + t.join(); + assertEquals(2, nodeInventory.getInventory().size()); + assertEquals(2, peerInventory.getInventory().size()); + } + + @Test(timeout = 5_000) + public void ensureObjectsAreSynchronizedIfOnlyNodeHasObjects() throws Exception { + peerInventory.init(); + + nodeInventory.init( + "V1Msg.payload" + ); + + Thread t = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, + mock(NetworkHandler.MessageListener.class), + 10); + t.join(); + assertEquals(1, nodeInventory.getInventory().size()); + assertEquals(1, peerInventory.getInventory().size()); + } + + private void shutdown(BitmessageContext node) { + node.shutdown(); + do { + Thread.yield(); + } while (node.isRunning()); + } +} diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java b/networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java new file mode 100644 index 0000000..040e730 --- /dev/null +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 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.networking; + +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.payload.ObjectType; +import ch.dissem.bitmessage.entity.valueobject.InventoryVector; +import ch.dissem.bitmessage.ports.Inventory; +import ch.dissem.bitmessage.utils.TestUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TestInventory implements Inventory { + private final Map<InventoryVector, ObjectMessage> inventory; + + public TestInventory() { + this.inventory = new HashMap<>(); + } + + @Override + public List<InventoryVector> getInventory(long... streams) { + return new ArrayList<>(inventory.keySet()); + } + + @Override + public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { + return offer; + } + + @Override + public ObjectMessage getObject(InventoryVector vector) { + return inventory.get(vector); + } + + @Override + public List<ObjectMessage> getObjects(long stream, long version, ObjectType... types) { + return new ArrayList<>(inventory.values()); + } + + @Override + public void storeObject(ObjectMessage object) { + inventory.put(object.getInventoryVector(), object); + } + + @Override + public void cleanup() { + + } + + public void init(String... resources) throws IOException { + inventory.clear(); + for (String resource : resources) { + int version = Integer.parseInt(resource.substring(1, 2)); + ObjectMessage obj = TestUtils.loadObjectMessage(version, resource); + inventory.put(obj.getInventoryVector(), obj); + } + } +} diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/TestNodeRegistry.java b/networking/src/test/java/ch/dissem/bitmessage/networking/TestNodeRegistry.java new file mode 100644 index 0000000..c3abd58 --- /dev/null +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/TestNodeRegistry.java @@ -0,0 +1,44 @@ +/* + * Copyright 2015 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.networking; + +import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; +import ch.dissem.bitmessage.ports.NodeRegistry; + +import java.util.Arrays; +import java.util.List; + +/** + * Empty {@link NodeRegistry} that doesn't do anything, but shouldn't break things either. + */ +class TestNodeRegistry implements NodeRegistry { + private List<NetworkAddress> nodes; + + public TestNodeRegistry(NetworkAddress... nodes) { + this.nodes = Arrays.asList(nodes); + } + + @Override + public List<NetworkAddress> getKnownAddresses(int limit, long... streams) { + return nodes; + } + + @Override + public void offerAddresses(List<NetworkAddress> addresses) { + // Ignore + } +} diff --git a/networking/src/test/resources/V1Msg.payload b/networking/src/test/resources/V1Msg.payload new file mode 100644 index 0000000000000000000000000000000000000000..ddf21cda61a888ea022b4b8424eb96ee1db39ffe GIT binary patch literal 444 zcmV;t0Ym-(00008xvxtA00010gc-#E000630k}!J&+7f>uEI8@F)<M3;sVM5AW{Ja z{;1*UtMz4O)sr?YMu97^{!^c06!iQy{9Ok=9{?ch9l298EQ(V75O-+S9!h1*x~vFA zJ$I@8<m<pzWp?fIRZW_3N>kNn;2iFat;p{^0*meHzJ*{^Fy|8tEC&7fCXYCRC6p;- zrwSGz0<?F)O9Uf?=Y(u*Lf@Nc+Vn`!YYO<563VhGrFeHD@p=T1(w;=Hh%}>Ho|#|o zEVfmjrCC-W%N^r5)7#?V5j#CZa%OaP0g&+7Iut0XVpU6GrzwGJMd0*0#;F)6j2?3m zpz&%fvq&-208*9RwkBU10Rk0w2I?uhUA$B$(U5H_f%duH^Cj_3xbpYV?c%<HE-+kA z`#Pb71nW@JujX|pYp04R$~(+7n#qR5Vuz#06@4l%oXSrPIzFL!K|QPj)R`aSAW_6) z0B0I{H{T!2Xy_;F{U@j_6{NoCW1Q*9!^J>~y>8-%+dazRVTC$s5mxXUXECZIKhWTo mu6-%(T=mLftk~cJZQV_Vr9WEM=TwI`1+jj(Azs6mYB0@}ZPQNx literal 0 HcmV?d00001 diff --git a/networking/src/test/resources/V4Pubkey.payload b/networking/src/test/resources/V4Pubkey.payload new file mode 100644 index 0000000000000000000000000000000000000000..a5e4c5c40944e8ffc38b369e40840c06a416c5aa GIT binary patch literal 396 zcmV;70dxKU00000xAyk{00010UeGiE000350Z5r#d*j`5Pg%2ba1!PSq98oPb(^t% z;KgzK_V$mQZm9inp(ffPq;aqrDrn3?C<4j=Akdap1po|b4j_f0notM>d(L9Ml{C2@ z+T1h%bp#E0>i{5sJWl0^@?*BHMYKflgKudScHd!TX)VzSU6-cwk+pgTmYfiz1uQCj zSs4!l9plyrnujt2%tbA6X)Hz_q)G?EPXO!Uy0#j8MQVhpFP4*Qu>!6gP@)wYpVarh zgpUg62HESXiAQQMO_}jTWWG}erMJvUwXEpY1uLsPa(u$}|D#H;5*xal`le2&=b|6X zXDuFEZ4@AwqBs+8&pZ<&P7N~hBU_TcS#YQ9yYuq}#O&r(M5b62y}r$6<(Ba$=f(7# zTcA@q?ut~DeC+hIc@%8p@Hu+Tckgr{-rc17p<4cAWEv#WU`aSrVyLq*B74o@+$o~b q!`TVqn-`}!5^p-`%#4j-0g7a3xT-Zo<53UgE9%*VnnKWkIVoM&N4W9; literal 0 HcmV?d00001 diff --git a/networking/src/test/resources/V5Broadcast.payload b/networking/src/test/resources/V5Broadcast.payload new file mode 100644 index 0000000000000000000000000000000000000000..87c8c047cf284e7fe9a9f47dbbfa49c3817bbd67 GIT binary patch literal 428 zcmV;d0aN|}0000137ZE100010mbs?@000980Z5r#d*j`5Pg%2ba1!PSq98oPb(^t% z;KgzK_V$mQZr&z5B~$A7VIv`j|E{Z8{Q}AWAl#f{6t$GZv!n%i@Sw{$TJpn{a!1eo z=z%8?zJ0DxK>#4bE!e}!EtnzSQlhiRA31<nlkBEW1?ukc0RzVo$=aPeG(1uw%v>jD zOO*qpGJH6?%S3d~7}HaC-c+n%AQ()G1O?D561V<bK{~!B5i!ZkfqUi*Vl9;*=cLd} z^|!{04WE_}k3ToJ5lY33>0JBm9DO>u)Qr=g+aPQVh?!%*W*81aLJ~yN;T{l*>cnUb z*B%Nj&XS3pk4Wlw@7$u`pga`dcDrVb$+tBC{(ijJ-tx`97<96j=y~}-_O<S*lRVz% zAjtz?ufP&yHlsv6E+I-8Az7^{XZ6UxkV*nf&cG^KWaM5SJC9L!F6Keo$-PX4dkD+7 zMhuNhm;|z95HzQ80@ICtWnM)SlGWW){P>HR%gtS~T6Mu$Ih4{viO7qx3xfPsp%4y? WTaKw&M!dRV|MQaOU7}MPXm%~gpv-;% literal 0 HcmV?d00001 From 7fb837645fcb7c4df79252415db54cd0f5fd5794 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Fri, 25 Sep 2015 23:35:31 +0200 Subject: [PATCH 06/42] Synchronisation now shouldn't fail if the trusted host has no new messages - fixed tests for Gradle builds - fixed SerializationTest --- domain/build.gradle | 13 +++++++++++++ .../bitmessage/entity/SerializationTest.java | 16 +++++++++++----- networking/build.gradle | 1 + .../dissem/bitmessage/networking/Connection.java | 13 ++++++++++--- .../networking/NetworkHandlerTest.java | 2 +- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/domain/build.gradle b/domain/build.gradle index 6bcbe6d..1880c6b 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -10,6 +10,19 @@ uploadArchives { } } +configurations { + testArtifacts.extendsFrom testRuntime +} + +task testJar(type: Jar) { + classifier = 'test' + from sourceSets.test.output +} + +artifacts { + testArtifacts testJar +} + dependencies { compile 'org.slf4j:slf4j-api:1.7.12' testCompile 'junit:junit:4.11' diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java index de0c807..03b7bc5 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java @@ -18,18 +18,17 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.valueobject.Label; -import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; import java.io.*; -import java.util.Arrays; +import java.lang.reflect.Field; +import java.util.Collections; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; public class SerializationTest extends TestBase { @Test @@ -87,6 +86,12 @@ public class SerializationTest extends TestBase { 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); } @@ -95,6 +100,7 @@ public class SerializationTest extends TestBase { InputStream in = new ByteArrayInputStream(data); ObjectMessage object = Factory.getObjectMessage(version, in, data.length); ByteArrayOutputStream out = new ByteArrayOutputStream(); + assertNotNull(object); object.write(out); assertArrayEquals(data, out.toByteArray()); assertEquals(expectedPayloadType.getCanonicalName(), object.getPayload().getClass().getCanonicalName()); @@ -105,7 +111,7 @@ public class SerializationTest extends TestBase { Plaintext plaintext = new Plaintext.Builder(MSG) .from(TestUtils.loadContact()) .to(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) - .labels(Arrays.asList(new Label("Test", Label.Type.INBOX, 0))) + .labels(Collections.singletonList(new Label("Test", Label.Type.INBOX, 0))) .message("Test", "Test Test.\nTest") .build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); diff --git a/networking/build.gradle b/networking/build.gradle index 6e9366c..06bd5d0 100644 --- a/networking/build.gradle +++ b/networking/build.gradle @@ -15,5 +15,6 @@ dependencies { testCompile 'junit:junit:4.11' testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.mockito:mockito-core:1.10.19' + testCompile project(path: ':domain', configuration: 'testArtifacts') testCompile project(':security-bc') } \ No newline at end of file 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 bb8e62c..400bada 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -25,7 +25,6 @@ import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException; import ch.dissem.bitmessage.exception.NodeException; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.NetworkHandler.MessageListener; -import ch.dissem.bitmessage.utils.DebugUtils; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,6 +69,7 @@ public class Connection implements Runnable { private OutputStream out; private int version; private long[] streams; + private int readTimeoutCounter; public Connection(InternalContext context, Mode mode, Socket socket, MessageListener listener, ConcurrentMap<InventoryVector, Long> requestedObjectsMap) throws IOException { @@ -185,6 +185,7 @@ public class Connection implements Runnable { } catch (SocketTimeoutException ignore) { if (state == ACTIVE) { sendQueue(); + if (syncFinished(null)) disconnect(); } } } @@ -205,7 +206,13 @@ public class Connection implements Runnable { if (syncTimeout < UnixTime.now()) { return true; } - if (!(msg.getPayload() instanceof Addr) && requestedObjects.isEmpty() && sendingQueue.isEmpty()) { + if (msg == null) { + readTimeoutCounter++; + return readTimeoutCounter > 1; + } + readTimeoutCounter = 0; + if (!(msg.getPayload() instanceof Addr) && !(msg.getPayload() instanceof GetData) + && requestedObjects.isEmpty() && sendingQueue.isEmpty()) { return true; } return false; @@ -311,9 +318,9 @@ public class Connection implements Runnable { ctx.getNetworkHandler().offer(objectMessage.getInventoryVector()); } 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); - DebugUtils.saveToFile(objectMessage); } finally { requestedObjects.remove(objectMessage.getInventoryVector()); } diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index cd8a743..7beca24 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -122,7 +122,7 @@ public class NetworkHandlerTest { assertEquals(2, peerInventory.getInventory().size()); } - @Test(timeout = 5_000) + @Test(timeout = 10_000) public void ensureObjectsAreSynchronizedIfOnlyNodeHasObjects() throws Exception { peerInventory.init(); From c3fdee79ca10e6698febc9e61fe24dd58cec452f Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Tue, 29 Sep 2015 07:13:27 +0200 Subject: [PATCH 07/42] Some bugfixes and added findMessages by sender --- .../java/ch/dissem/bitmessage/ports/MessageRepository.java | 2 ++ .../dissem/bitmessage/networking/DefaultNetworkHandler.java | 3 ++- .../dissem/bitmessage/repository/JdbcMessageRepository.java | 5 +++++ security-bc/build.gradle | 6 +++--- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java index b698a97..6d8970b 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java @@ -34,6 +34,8 @@ public interface MessageRepository { List<Plaintext> findMessages(Status status, BitmessageAddress recipient); + List<Plaintext> findMessages(BitmessageAddress sender); + void save(Plaintext message); void remove(Plaintext message); 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 aead8e8..b6b3c69 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -221,7 +221,8 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { i++; } return new Property("network", null, - new Property("connectionManager", connectionManager.isAlive() ? "running" : "stopped"), + new Property("connectionManager", + connectionManager != null && connectionManager.isAlive() ? "running" : "stopped"), new Property("connections", null, streamProperties) ); } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index ec89e68..759012b 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -101,6 +101,11 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito return find("status='" + status.name() + "'"); } + @Override + public List<Plaintext> findMessages(BitmessageAddress sender) { + return find("sender='" + sender.getAddress() + "'"); + } + private List<Plaintext> find(String where) { List<Plaintext> result = new LinkedList<>(); try (Connection connection = config.getConnection()) { diff --git a/security-bc/build.gradle b/security-bc/build.gradle index 48f14d1..ff37994 100644 --- a/security-bc/build.gradle +++ b/security-bc/build.gradle @@ -2,9 +2,9 @@ uploadArchives { repositories { mavenDeployer { pom.project { - name 'Jabit Spongy Security' - artifactId = 'jabit-security-spongy' - description 'The Security implementation using spongy castle (needed for Android)' + name 'Jabit Bouncy Security' + artifactId = 'jabit-security-bouncy' + description 'The Security implementation using bouncy castle' } } } From f9ff22bebe0da06aee591bc3b4f1fc1877d6780a Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 7 Oct 2015 21:50:41 +0200 Subject: [PATCH 08/42] Synchronisation API and related refactorings / improvements -> lets you synchronize with the Bitmessage network without staying connected --- .../dissem/bitmessage/demo/Application.java | 26 +++++---- .../dissem/bitmessage/BitmessageContext.java | 26 +++++++-- .../ch/dissem/bitmessage/InternalContext.java | 2 +- .../ch/dissem/bitmessage/ports/Inventory.java | 2 + .../bitmessage/ports/MemoryNodeRegistry.java | 55 ++++++++++--------- .../bitmessage/ports/MessageRepository.java | 2 + domain/src/main/resources/nodes.txt | 8 ++- .../bitmessage/networking/Connection.java | 2 + .../networking/NetworkHandlerTest.java | 6 +- .../bitmessage/networking/TestInventory.java | 5 ++ .../bitmessage/repository/JdbcInventory.java | 18 +++++- .../repository/JdbcMessageRepository.java | 40 +++++++++++++- .../repository/JdbcInventoryTest.java | 11 ++++ 13 files changed, 154 insertions(+), 49 deletions(-) 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 2f6f9fd..d93c3c8 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -22,7 +22,10 @@ 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.repository.JdbcAddressRepository; +import ch.dissem.bitmessage.repository.JdbcConfig; +import ch.dissem.bitmessage.repository.JdbcInventory; +import ch.dissem.bitmessage.repository.JdbcMessageRepository; import ch.dissem.bitmessage.security.bc.BouncySecurity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,18 +53,19 @@ public class Application { .networkHandler(new DefaultNetworkHandler()) .security(new BouncySecurity()) .port(48444) + .listener(new BitmessageContext.Listener() { + @Override + public void receive(Plaintext plaintext) { + try { + System.out.println(new String(plaintext.getMessage(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + LOG.error(e.getMessage(), e); + } + } + }) .build(); - ctx.startup(new BitmessageContext.Listener() { - @Override - public void receive(Plaintext plaintext) { - try { - System.out.println(new String(plaintext.getMessage(), "UTF-8")); - } catch (UnsupportedEncodingException e) { - LOG.error(e.getMessage(), e); - } - } - }); + ctx.startup(); scanner = new Scanner(System.in); diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 2cd851e..0b3ad1d 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -31,6 +31,7 @@ import ch.dissem.bitmessage.utils.Property; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.InetAddress; import java.util.Arrays; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -62,10 +63,14 @@ public class BitmessageContext { private final InternalContext ctx; - private Listener listener; + private final Listener listener; + private final NetworkHandler.MessageListener networkListener; private BitmessageContext(Builder builder) { ctx = new InternalContext(builder); + listener = builder.listener; + networkListener = new DefaultMessageListener(ctx, listener); + // As this thread is used for parts that do POW, which itself uses parallel threads, only // one should be executed at any time. pool = Executors.newFixedThreadPool(1); @@ -179,15 +184,22 @@ public class BitmessageContext { ); } - public void startup(Listener listener) { - this.listener = listener; - ctx.getNetworkHandler().start(new DefaultMessageListener(ctx, listener)); + public void startup() { + ctx.getNetworkHandler().start(networkListener); } public void shutdown() { ctx.getNetworkHandler().stop(); } + public void synchronize(InetAddress host, int port, long timeoutInSeconds) { + ctx.getNetworkHandler().synchronize(host, port, networkListener, timeoutInSeconds); + } + + public void cleanup() { + ctx.getInventory().cleanup(); + } + public boolean isRunning() { return ctx.getNetworkHandler().isRunning(); } @@ -268,6 +280,7 @@ public class BitmessageContext { ProofOfWorkEngine proofOfWorkEngine; Security security; MessageCallback messageCallback; + Listener listener; public Builder() { } @@ -317,6 +330,11 @@ public class BitmessageContext { return this; } + public Builder listener(Listener listener) { + this.listener = listener; + return this; + } + public BitmessageContext build() { nonNull("inventory", inventory); nonNull("nodeRegistry", nodeRegistry); diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 403e9c4..6d969bd 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -82,7 +82,7 @@ public class InternalContext { streams.add(1L); } - init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); + init(security, inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); for (BitmessageAddress identity : addressRepository.getIdentities()) { streams.add(identity.getStream()); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java index 934a4d0..a10d50f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java @@ -36,5 +36,7 @@ public interface Inventory { void storeObject(ObjectMessage object); + boolean contains(ObjectMessage object); + void cleanup(); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java index 04fa2f9..9c97b87 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java @@ -34,38 +34,43 @@ import static java.util.Collections.newSetFromMap; public class MemoryNodeRegistry implements NodeRegistry { private static final Logger LOG = LoggerFactory.getLogger(MemoryNodeRegistry.class); - private final Map<Long, Set<NetworkAddress>> stableNodes = new HashMap<>(); + private final Map<Long, Set<NetworkAddress>> stableNodes = new ConcurrentHashMap<>(); private final Map<Long, Set<NetworkAddress>> knownNodes = new ConcurrentHashMap<>(); public MemoryNodeRegistry() { - try (InputStream in = getClass().getClassLoader().getResourceAsStream("nodes.txt")) { - Scanner scanner = new Scanner(in); - long stream = 0; - Set<NetworkAddress> streamSet = null; - while (scanner.hasNext()) { - try { - String line = scanner.nextLine().trim(); - if (line.startsWith("#") || line.isEmpty()) { - // Ignore - continue; - } - if (line.startsWith("[stream")) { - stream = Long.parseLong(line.substring(8, line.lastIndexOf(']'))); - streamSet = new HashSet<>(); - stableNodes.put(stream, streamSet); - } else if (streamSet != null) { - int portIndex = line.lastIndexOf(':'); - InetAddress inetAddress = InetAddress.getByName(line.substring(0, portIndex)); - int port = Integer.valueOf(line.substring(portIndex + 1)); - streamSet.add(new NetworkAddress.Builder().ip(inetAddress).port(port).stream(stream).build()); + new Thread(new Runnable() { + @Override + public void run() { + try (InputStream in = getClass().getClassLoader().getResourceAsStream("nodes.txt")) { + Scanner scanner = new Scanner(in); + long stream = 0; + Set<NetworkAddress> streamSet = null; + while (scanner.hasNext()) { + try { + String line = scanner.nextLine().trim(); + if (line.startsWith("#") || line.isEmpty()) { + // Ignore + continue; + } + if (line.startsWith("[stream")) { + stream = Long.parseLong(line.substring(8, line.lastIndexOf(']'))); + streamSet = new HashSet<>(); + stableNodes.put(stream, streamSet); + } else if (streamSet != null) { + int portIndex = line.lastIndexOf(':'); + InetAddress inetAddress = InetAddress.getByName(line.substring(0, portIndex)); + int port = Integer.valueOf(line.substring(portIndex + 1)); + streamSet.add(new NetworkAddress.Builder().ip(inetAddress).port(port).stream(stream).build()); + } + } catch (IOException e) { + LOG.warn(e.getMessage(), e); + } } } catch (IOException e) { - LOG.warn(e.getMessage(), e); + throw new RuntimeException(e); } } - } catch (IOException e) { - throw new RuntimeException(e); - } + }, "node registry initializer").start(); } @Override diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java index 6d8970b..af7b2bc 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java @@ -28,6 +28,8 @@ public interface MessageRepository { List<Label> getLabels(Label.Type... types); + int countUnread(Label label); + List<Plaintext> findMessages(Label label); List<Plaintext> findMessages(Status status); diff --git a/domain/src/main/resources/nodes.txt b/domain/src/main/resources/nodes.txt index b728ba5..ab2b69c 100644 --- a/domain/src/main/resources/nodes.txt +++ b/domain/src/main/resources/nodes.txt @@ -1,15 +1,19 @@ [stream 1] -[2604:2000:1380:9f:82e:148b:2746:d0c7]:8080 5.45.99.75:8444 75.167.159.54:8444 95.165.168.168:8444 85.180.139.241:8444 -158.222.211.81:8080 178.62.12.187:8448 + +[2604:2000:1380:9f:82e:148b:2746:d0c7]:8080 +158.222.211.81:8080 24.188.198.204:8111 109.147.204.113:1195 178.11.46.221:8444 +# Add named nodes at the end, as resolving them might take time +dissem.ch:8444 + [stream 2] # none yet \ No newline at end of file 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 400bada..5015094 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -310,6 +310,8 @@ public class Connection implements Runnable { try { LOG.debug("Received object " + objectMessage.getInventoryVector()); security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); + if (ctx.getInventory().contains(objectMessage)) + break; listener.receive(objectMessage); ctx.getInventory().storeObject(objectMessage); // offer object to some random nodes so it gets distributed throughout the network: diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 7beca24..7ba8fc4 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -55,8 +55,9 @@ public class NetworkHandlerTest { .nodeRegistry(new TestNodeRegistry()) .networkHandler(new DefaultNetworkHandler()) .security(new BouncySecurity()) + .listener(Mockito.mock(BitmessageContext.Listener.class)) .build(); - peer.startup(Mockito.mock(BitmessageContext.Listener.class)); + peer.startup(); nodeInventory = new TestInventory(); networkHandler = new DefaultNetworkHandler(); @@ -68,13 +69,14 @@ public class NetworkHandlerTest { .nodeRegistry(new TestNodeRegistry(localhost)) .networkHandler(networkHandler) .security(new BouncySecurity()) + .listener(Mockito.mock(BitmessageContext.Listener.class)) .build(); } @Test(timeout = 20_000) public void ensureNodesAreConnecting() { try { - node.startup(Mockito.mock(BitmessageContext.Listener.class)); + node.startup(); Property status; do { Thread.yield(); diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java b/networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java index 040e730..cefe316 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/TestInventory.java @@ -60,6 +60,11 @@ public class TestInventory implements Inventory { inventory.put(object.getInventoryVector(), object); } + @Override + public boolean contains(ObjectMessage object) { + return inventory.containsKey(object.getInventoryVector()); + } + @Override public void cleanup() { diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java index 98034ee..4adc532 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -24,7 +24,6 @@ import ch.dissem.bitmessage.ports.Inventory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; import java.sql.*; import java.util.LinkedList; import java.util.List; @@ -139,6 +138,23 @@ public class JdbcInventory extends JdbcHelper implements Inventory { } } + @Override + public boolean contains(ObjectMessage object) { + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT count(1) FROM Inventory WHERE hash = X'" + + object.getInventoryVector() + "'"); + if (rs.next()) { + return rs.getInt(1) > 0; + } else { + throw new RuntimeException("Couldn't query if inventory contains " + object.getInventoryVector()); + } + } catch (Exception e) { + LOG.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + @Override public void cleanup() { try (Connection connection = config.getConnection()) { diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index 759012b..4599b79 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -25,7 +25,6 @@ import ch.dissem.bitmessage.ports.MessageRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.*; @@ -86,6 +85,29 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito return result; } + @Override + public int countUnread(Label label) { + String where; + if (label != null) { + where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ") AND "; + } else { + where = ""; + } + where += "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (" + + "SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))"; + + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT count(*) FROM Message WHERE " + where); + if (rs.next()) { + return rs.getInt(1); + } + } catch (SQLException e) { + LOG.error(e.getMessage(), e); + } + return 0; + } + @Override public List<Plaintext> findMessages(Label label) { return find("id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ")"); @@ -230,8 +252,20 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito @Override public void remove(Plaintext message) { try (Connection connection = config.getConnection()) { - Statement stmt = connection.createStatement(); - stmt.executeUpdate("DELETE FROM Message WHERE id = " + message.getId()); + try { + connection.setAutoCommit(false); + Statement stmt = connection.createStatement(); + stmt.executeUpdate("DELETE FROM Message_Label WHERE message_id = " + message.getId()); + stmt.executeUpdate("DELETE FROM Message WHERE id = " + message.getId()); + connection.commit(); + } catch (SQLException e) { + try { + connection.rollback(); + } catch (SQLException e1) { + LOG.debug(e1.getMessage(), e); + } + LOG.error(e.getMessage(), e); + } } catch (SQLException e) { LOG.error(e.getMessage(), e); } diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java index 3954766..6f31299 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcInventoryTest.java @@ -110,6 +110,17 @@ public class JdbcInventoryTest extends TestBase { assertNotNull(inventory.getObject(object.getInventoryVector())); } + @Test + public void testContains() { + ObjectMessage object = getObjectMessage(5, 0, getGetPubkey()); + + assertFalse(inventory.contains(object)); + + inventory.storeObject(object); + + assertTrue(inventory.contains(object)); + } + @Test public void testCleanup() throws Exception { assertNotNull(inventory.getObject(inventoryVectorIgnore)); From ea1419eda11148c0d732e1b904bce0ac7f1f924e Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 8 Oct 2015 13:12:39 +0200 Subject: [PATCH 09/42] Synchronisation API - added option to wait for the synchronization to finish --- .../dissem/bitmessage/BitmessageContext.java | 18 ++++++++++++++++-- .../bitmessage/networking/Connection.java | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 0b3ad1d..d7d8980 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -192,8 +192,22 @@ public class BitmessageContext { ctx.getNetworkHandler().stop(); } - public void synchronize(InetAddress host, int port, long timeoutInSeconds) { - ctx.getNetworkHandler().synchronize(host, port, networkListener, timeoutInSeconds); + /** + * @param host a trusted node that must be reliable (it's used for every synchronization) + * @param port of the trusted host, default is 8444 + * @param timeoutInSeconds synchronization should end no later than about 5 seconds after the timeout elapsed, even + * if not all objects were fetched + * @param wait waits for the synchronization thread to finish + */ + public void synchronize(InetAddress host, int port, long timeoutInSeconds, boolean wait) { + Thread t = ctx.getNetworkHandler().synchronize(host, port, networkListener, timeoutInSeconds); + if (wait) { + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e.getMessage(), e); + } + } } public void cleanup() { 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 5015094..6aa4210 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -204,6 +204,7 @@ public class Connection implements Runnable { return false; } if (syncTimeout < UnixTime.now()) { + LOG.info("Synchronization timed out"); return true; } if (msg == null) { @@ -213,6 +214,7 @@ public class Connection implements Runnable { readTimeoutCounter = 0; if (!(msg.getPayload() instanceof Addr) && !(msg.getPayload() instanceof GetData) && requestedObjects.isEmpty() && sendingQueue.isEmpty()) { + LOG.info("Synchronisation completed"); return true; } return false; From 3d1bd7227b2d1e909200338898f26221bb2180cf Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Mon, 12 Oct 2015 12:42:11 +0200 Subject: [PATCH 10/42] Updated H2 version --- demo/build.gradle | 2 +- repositories/build.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/demo/build.gradle b/demo/build.gradle index cba7d93..b7f621d 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -26,6 +26,6 @@ dependencies { compile project(':wif') compile 'org.slf4j:slf4j-simple:1.7.12' compile 'args4j:args4j:2.32' - compile 'com.h2database:h2:1.4.187' + compile 'com.h2database:h2:1.4.190' testCompile 'junit:junit:4.11' } diff --git a/repositories/build.gradle b/repositories/build.gradle index 76509fd..7fc6f25 100644 --- a/repositories/build.gradle +++ b/repositories/build.gradle @@ -13,8 +13,8 @@ uploadArchives { dependencies { compile project(':domain') compile 'org.flywaydb:flyway-core:3.2.1' - testCompile 'junit:junit:4.11' - testCompile 'com.h2database:h2:1.4.187' + testCompile 'junit:junit:4.12' + testCompile 'com.h2database:h2:1.4.190' testCompile 'org.mockito:mockito-core:1.10.19' testCompile project(':security-bc') } \ No newline at end of file From 117ac3ca73b1d1f352253f9770a08b1b2af88da9 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Mon, 12 Oct 2015 12:44:13 +0200 Subject: [PATCH 11/42] Fixed some problems and added cleanup on shutdown --- .../java/ch/dissem/bitmessage/demo/Application.java | 3 ++- .../bitmessage/ports/MultiThreadedPOWEngine.java | 2 -- .../bitmessage/networking/DefaultNetworkHandler.java | 11 ++++++----- 3 files changed, 8 insertions(+), 8 deletions(-) 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 d93c3c8..7644ed8 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -52,7 +52,7 @@ public class Application { .messageRepo(new JdbcMessageRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) .security(new BouncySecurity()) - .port(48444) +// .port(48444) .listener(new BitmessageContext.Listener() { @Override public void receive(Plaintext plaintext) { @@ -109,6 +109,7 @@ public class Application { } } while (!"e".equals(command)); LOG.info("Shutting down client"); + ctx.cleanup(); ctx.shutdown(); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java index 9814c3a..3c4572e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java @@ -43,8 +43,6 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { for (int i = 0; i < cores; i++) { Worker w = new Worker(workers, (byte) cores, i, initialHash, target); workers.add(w); - } - for (Worker w : workers) { w.start(); } for (Worker w : workers) { 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 b6b3c69..69e9e16 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -48,19 +48,15 @@ import static ch.dissem.bitmessage.utils.DebugUtils.inc; public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { public final static int NETWORK_MAGIC_NUMBER = 8; private final static Logger LOG = LoggerFactory.getLogger(DefaultNetworkHandler.class); - private final ExecutorService pool; private final List<Connection> connections = new LinkedList<>(); private InternalContext ctx; private ServerSocket serverSocket; + private ExecutorService pool; private Thread serverThread; private Thread connectionManager; private ConcurrentMap<InventoryVector, Long> requestedObjects = new ConcurrentHashMap<>(); - public DefaultNetworkHandler() { - pool = Executors.newCachedThreadPool(); - } - @Override public void setContext(InternalContext context) { this.ctx = context; @@ -82,7 +78,12 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { if (listener == null) { throw new IllegalStateException("Listener must be set at start"); } + if (pool != null && !pool.isShutdown()) { + throw new IllegalStateException("Network already running - you need to stop first."); + } try { + pool = Executors.newCachedThreadPool(); + connections.clear(); serverSocket = new ServerSocket(ctx.getPort()); serverThread = new Thread(new Runnable() { @Override From 511b3c1754d7dda5a584756987f1d25338ec8fc3 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 14 Oct 2015 18:37:43 +0200 Subject: [PATCH 12/42] Connections now use two separate threads for writing and listening - this should avoid dead locks, specifically when connecting to Jabit :/ - also, Java 8 features are now allowed in modules not needed by Android clients --- demo/build.gradle | 2 + .../dissem/bitmessage/demo/Application.java | 2 +- .../ch/dissem/bitmessage/factory/Factory.java | 3 +- .../ch/dissem/bitmessage/ports/Inventory.java | 15 ++ domain/src/main/resources/nodes.txt | 1 - .../bitmessage/networking/Connection.java | 234 ++++++++++-------- .../networking/DefaultNetworkHandler.java | 95 +++---- repositories/build.gradle | 2 + .../bitmessage/repository/JdbcInventory.java | 72 +++--- 9 files changed, 246 insertions(+), 180 deletions(-) diff --git a/demo/build.gradle b/demo/build.gradle index b7f621d..e4c51a7 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -14,6 +14,8 @@ uploadArchives { } } +sourceCompatibility = 1.8 + task fatCapsule(type: FatCapsule) { applicationClass 'ch.dissem.bitmessage.demo.Main' } 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 7644ed8..a065de9 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -52,7 +52,7 @@ public class Application { .messageRepo(new JdbcMessageRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) .security(new BouncySecurity()) -// .port(48444) + .port(48444) .listener(new BitmessageContext.Listener() { @Override public void receive(Plaintext plaintext) { diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java index 5584ec7..33604ab 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java @@ -23,7 +23,6 @@ import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.NodeException; -import ch.dissem.bitmessage.ports.Security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -155,7 +154,7 @@ public class Factory { } } // fallback: just store the message - we don't really care what it is -// LOG.info("Unexpected object type: " + objectType); + LOG.trace("Unexpected object type: " + objectType); return GenericPayload.read(version, stream, streamNumber, length); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java index a10d50f..6af65c1 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java @@ -26,17 +26,32 @@ import java.util.List; * The Inventory stores and retrieves objects, cleans up outdated objects and can tell which objects are still missing. */ public interface Inventory { + /** + * Returns the IVs of all valid objects we have for the given streams + */ List<InventoryVector> getInventory(long... streams); + /** + * Returns the IVs of all objects in the offer that we don't have already. Implementations are allowed to + * ignore the streams parameter, but it must be set when calling this method. + */ List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams); ObjectMessage getObject(InventoryVector vector); + /** + * This method is mainly used to search for public keys to newly added addresses or broadcasts from new + * subscriptions. + */ List<ObjectMessage> getObjects(long stream, long version, ObjectType... types); void storeObject(ObjectMessage object); boolean contains(ObjectMessage object); + /** + * Deletes all objects that expired 5 minutes ago or earlier + * (so we don't accidentally request objects we just deleted) + */ void cleanup(); } diff --git a/domain/src/main/resources/nodes.txt b/domain/src/main/resources/nodes.txt index ab2b69c..e0a334e 100644 --- a/domain/src/main/resources/nodes.txt +++ b/domain/src/main/resources/nodes.txt @@ -12,7 +12,6 @@ 109.147.204.113:1195 178.11.46.221:8444 -# Add named nodes at the end, as resolving them might take time dissem.ch:8444 [stream 2] 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 6aa4210..6312557 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -49,7 +49,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; /** * A connection to a specific node */ -public class Connection implements Runnable { +public class Connection { public static final int READ_TIMEOUT = 2000; private final static Logger LOG = LoggerFactory.getLogger(Connection.class); private static final int CONNECT_TIMEOUT = 5000; @@ -63,13 +63,16 @@ public class Connection implements Runnable { private final Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>(); private final Map<InventoryVector, Long> requestedObjects; private final long syncTimeout; + private final ReaderRunnable reader = new ReaderRunnable(); + private final WriterRunnable writer = new WriterRunnable(); - private State state; + private volatile State state; private InputStream in; private OutputStream out; private int version; private long[] streams; private int readTimeoutCounter; + private boolean socketInitialized; public Connection(InternalContext context, Mode mode, Socket socket, MessageListener listener, ConcurrentMap<InventoryVector, Long> requestedObjectsMap) throws IOException { @@ -118,86 +121,6 @@ public class Connection implements Runnable { return node; } - @Override - public void run() { - try (Socket socket = this.socket) { - if (!socket.isConnected()) { - LOG.debug("Trying to connect to node " + node); - socket.connect(new InetSocketAddress(node.toInetAddress(), node.getPort()), CONNECT_TIMEOUT); - } - socket.setSoTimeout(READ_TIMEOUT); - this.in = socket.getInputStream(); - this.out = socket.getOutputStream(); - if (mode == CLIENT) { - send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); - } - while (state != DISCONNECTED) { - try { - NetworkMessage msg = Factory.getNetworkMessage(version, in); - if (msg == null) - continue; - switch (state) { - case ACTIVE: - receiveMessage(msg.getPayload()); - sendQueue(); - 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) { - this.version = payload.getVersion(); - this.streams = payload.getStreams(); - send(new VerAck()); - switch (mode) { - case SERVER: - send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); - break; - case CLIENT: - activateConnection(); - break; - } - } else { - LOG.info("Received unsupported version " + payload.getVersion() + ", disconnecting."); - disconnect(); - } - break; - case VERACK: - switch (mode) { - case SERVER: - activateConnection(); - break; - case CLIENT: - // NO OP - break; - } - break; - default: - throw new NodeException("Command 'version' or 'verack' expected, but was '" - + msg.getPayload().getCommand() + "'"); - } - } - if (socket.isClosed() || syncFinished(msg)) disconnect(); - } catch (SocketTimeoutException ignore) { - if (state == ACTIVE) { - sendQueue(); - if (syncFinished(null)) disconnect(); - } - } - } - } catch (IOException | NodeException e) { - disconnect(); - LOG.debug("Disconnected from node " + node + ": " + e.getMessage()); - } catch (RuntimeException e) { - disconnect(); - throw e; - } - } - @SuppressWarnings("RedundantIfStatement") private boolean syncFinished(NetworkMessage msg) { if (syncTimeout == 0 || state != ACTIVE) { @@ -229,15 +152,6 @@ public class Connection implements Runnable { ctx.getNodeRegistry().offerAddresses(Collections.singletonList(node)); } - private void sendQueue() { - if (sendingQueue.size() > 0) { - LOG.debug("Sending " + sendingQueue.size() + " messages to node " + node); - } - for (MessagePayload msg = sendingQueue.poll(); msg != null; msg = sendingQueue.poll()) { - send(msg); - } - } - private void cleanupIvCache() { Long fiveMinutesAgo = UnixTime.now(-5 * MINUTE); for (Map.Entry<InventoryVector, Long> entry : ivCache.entrySet()) { @@ -310,10 +224,12 @@ public class Connection implements Runnable { case OBJECT: ObjectMessage objectMessage = (ObjectMessage) messagePayload; try { + if (ctx.getInventory().contains(objectMessage)) { + LOG.debug("Received object " + objectMessage.getInventoryVector() + " - already in inventory"); + break; + } LOG.debug("Received object " + objectMessage.getInventoryVector()); security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); - if (ctx.getInventory().contains(objectMessage)) - break; listener.receive(objectMessage); ctx.getInventory().storeObject(objectMessage); // offer object to some random nodes so it gets distributed throughout the network: @@ -392,14 +308,136 @@ public class Connection implements Runnable { return Objects.hash(node); } - public void request(InventoryVector key) { - sendingQueue.offer(new GetData.Builder() - .addInventoryVector(key) - .build() - ); + private synchronized void initSocket(Socket socket) throws IOException { + if (!socketInitialized) { + if (!socket.isConnected()) { + LOG.debug("Trying to connect to node " + node); + socket.connect(new InetSocketAddress(node.toInetAddress(), node.getPort()), CONNECT_TIMEOUT); + } + socket.setSoTimeout(READ_TIMEOUT); + in = socket.getInputStream(); + out = socket.getOutputStream(); + if (!socket.isConnected()) { + LOG.debug("Trying to connect to node " + node); + socket.connect(new InetSocketAddress(node.toInetAddress(), node.getPort()), CONNECT_TIMEOUT); + } + socket.setSoTimeout(READ_TIMEOUT); + in = socket.getInputStream(); + out = socket.getOutputStream(); + socketInitialized = true; + } + } + + public ReaderRunnable getReader() { + return reader; + } + + public WriterRunnable getWriter() { + return writer; } public enum Mode {SERVER, CLIENT} public enum State {CONNECTING, ACTIVE, DISCONNECTED} + + public class ReaderRunnable implements Runnable { + @Override + public void run() { + try (Socket socket = Connection.this.socket) { + initSocket(socket); + if (mode == CLIENT) { + send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); + } + while (state != DISCONNECTED) { + 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: + activateConnection(); + break; + } + } else { + LOG.info("Received unsupported version " + payload.getVersion() + ", disconnecting."); + disconnect(); + } + break; + case VERACK: + switch (mode) { + case SERVER: + activateConnection(); + break; + case CLIENT: + // NO OP + break; + } + break; + default: + throw new NodeException("Command 'version' or 'verack' expected, but was '" + + msg.getPayload().getCommand() + "'"); + } + } + if (socket.isClosed() || syncFinished(msg)) disconnect(); + } catch (SocketTimeoutException ignore) { + if (state == ACTIVE) { + if (syncFinished(null)) disconnect(); + } + } + Thread.yield(); + } + } catch (IOException | NodeException e) { + disconnect(); + LOG.debug("Reader disconnected from node " + node + ": " + e.getMessage()); + } catch (RuntimeException e) { + LOG.debug("Reader disconnecting from node " + node + " due to error: " + e.getMessage(), e); + disconnect(); + } finally { + try { + socket.close(); + } catch (Exception e) { + LOG.debug(e.getMessage(), e); + } + } + } + } + + public class WriterRunnable implements Runnable { + @Override + public void run() { + try (Socket socket = Connection.this.socket) { + initSocket(socket); + while (state != DISCONNECTED) { + if (sendingQueue.size() > 0) { + LOG.debug("Sending " + sendingQueue.size() + " messages to node " + node); + send(sendingQueue.poll()); + } else { + Thread.sleep(100); + } + } + } catch (IOException | InterruptedException e) { + LOG.debug("Writer disconnected from node " + node + ": " + e.getMessage()); + disconnect(); + } + } + } } 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 69e9e16..e563152 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -49,14 +49,17 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { public final static int NETWORK_MAGIC_NUMBER = 8; private final static Logger LOG = LoggerFactory.getLogger(DefaultNetworkHandler.class); private final List<Connection> connections = new LinkedList<>(); + private final ExecutorService pool; private InternalContext ctx; private ServerSocket serverSocket; - private ExecutorService pool; - private Thread serverThread; - private Thread connectionManager; + private volatile boolean running; private ConcurrentMap<InventoryVector, Long> requestedObjects = new ConcurrentHashMap<>(); + public DefaultNetworkHandler() { + pool = Executors.newCachedThreadPool(); + } + @Override public void setContext(InternalContext context) { this.ctx = context; @@ -65,9 +68,12 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { @Override public Thread synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds) { try { - Thread t = new Thread(Connection.sync(ctx, trustedHost, port, listener, timeoutInSeconds)); - t.start(); - return t; + Connection connection = Connection.sync(ctx, trustedHost, port, listener, timeoutInSeconds); + Thread tr = new Thread(connection.getReader()); + Thread tw = new Thread(connection.getWriter()); + tr.start(); + tw.start(); + return tr; } catch (IOException e) { throw new RuntimeException(e); } @@ -78,14 +84,14 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { if (listener == null) { throw new IllegalStateException("Listener must be set at start"); } - if (pool != null && !pool.isShutdown()) { + if (running) { throw new IllegalStateException("Network already running - you need to stop first."); } try { - pool = Executors.newCachedThreadPool(); + running = true; connections.clear(); serverSocket = new ServerSocket(ctx.getPort()); - serverThread = new Thread(new Runnable() { + pool.execute(new Runnable() { @Override public void run() { while (!serverSocket.isClosed()) { @@ -98,44 +104,46 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } } } - }, "server"); - serverThread.start(); - connectionManager = new Thread(new Runnable() { + }); + pool.execute(new Runnable() { @Override public void run() { - while (!Thread.interrupted()) { - try { - int active = 0; - synchronized (connections) { - for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) { - Connection c = iterator.next(); - if (c.getState() == DISCONNECTED) { - // Remove the current element from the iterator and the list. - iterator.remove(); - } - if (c.getState() == ACTIVE) { - active++; + try { + while (running) { + try { + int active = 0; + synchronized (connections) { + for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) { + Connection c = iterator.next(); + if (c.getState() == DISCONNECTED) { + // Remove the current element from the iterator and the list. + iterator.remove(); + } + if (c.getState() == ACTIVE) { + active++; + } } } - } - if (active < NETWORK_MAGIC_NUMBER) { - List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses( - NETWORK_MAGIC_NUMBER - active, ctx.getStreams()); - for (NetworkAddress address : addresses) { - startConnection(new Connection(ctx, CLIENT, address, listener, requestedObjects)); + if (active < NETWORK_MAGIC_NUMBER) { + List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses( + NETWORK_MAGIC_NUMBER - active, ctx.getStreams()); + for (NetworkAddress address : addresses) { + startConnection(new Connection(ctx, CLIENT, address, listener, requestedObjects)); + } } + Thread.sleep(30000); + } catch (InterruptedException e) { + running = false; + } catch (Exception e) { + LOG.error("Error in connection manager. Ignored.", e); } - Thread.sleep(30000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - LOG.error("Error in connection manager. Ignored.", e); } + } finally { + LOG.debug("Connection manager shutting down."); + running = false; } - LOG.debug("Connection manager shutting down."); } - }, "connection-manager"); - connectionManager.start(); + }); } catch (IOException e) { throw new RuntimeException(e); } @@ -143,18 +151,17 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { @Override public boolean isRunning() { - return connectionManager != null && connectionManager.isAlive(); + return running; } @Override public void stop() { - connectionManager.interrupt(); + running = false; try { serverSocket.close(); } catch (IOException e) { LOG.debug(e.getMessage(), e); } - pool.shutdown(); synchronized (connections) { for (Connection c : connections) { c.disconnect(); @@ -170,7 +177,8 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } connections.add(c); } - pool.execute(c); + pool.execute(c.getReader()); + pool.execute(c.getWriter()); } @Override @@ -222,8 +230,7 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { i++; } return new Property("network", null, - new Property("connectionManager", - connectionManager != null && connectionManager.isAlive() ? "running" : "stopped"), + new Property("connectionManager", running ? "running" : "stopped"), new Property("connections", null, streamProperties) ); } diff --git a/repositories/build.gradle b/repositories/build.gradle index 7fc6f25..fecebce 100644 --- a/repositories/build.gradle +++ b/repositories/build.gradle @@ -10,6 +10,8 @@ uploadArchives { } } +sourceCompatibility = 1.8 + dependencies { compile project(':domain') compile 'org.flywaydb:flyway-core:3.2.1' diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java index 4adc532..9c9c9e7 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -27,12 +27,17 @@ import org.slf4j.LoggerFactory; import java.sql.*; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; import static ch.dissem.bitmessage.utils.UnixTime.now; public class JdbcInventory extends JdbcHelper implements Inventory { private static final Logger LOG = LoggerFactory.getLogger(JdbcInventory.class); + private final Map<Long, Map<InventoryVector, Long>> cache = new ConcurrentHashMap<>(); + public JdbcInventory(JdbcConfig config) { super(config); } @@ -40,36 +45,43 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public List<InventoryVector> getInventory(long... streams) { List<InventoryVector> result = new LinkedList<>(); - try (Connection connection = config.getConnection()) { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE expires > " + now() + - " AND stream IN (" + join(streams) + ")"); - while (rs.next()) { - result.add(new InventoryVector(rs.getBytes("hash"))); - } - } catch (SQLException e) { - LOG.error(e.getMessage(), e); + for (long stream : streams) { + getCache(stream).entrySet().stream() + .filter(e -> e.getValue() > now()) + .forEach(e -> result.add(e.getKey())); } return result; } - private List<InventoryVector> getFullInventory(long... streams) { - List<InventoryVector> result = new LinkedList<>(); - try (Connection connection = config.getConnection()) { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT hash FROM Inventory WHERE stream IN (" + join(streams) + ")"); - while (rs.next()) { - result.add(new InventoryVector(rs.getBytes("hash"))); + private Map<InventoryVector, Long> getCache(long stream) { + Map<InventoryVector, Long> result = cache.get(stream); + if (result == null) { + synchronized (cache) { + if (cache.get(stream) == null) { + result = new ConcurrentHashMap<>(); + cache.put(stream, result); + + try (Connection connection = config.getConnection()) { + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT hash, expires FROM Inventory WHERE expires > " + + now(-5 * MINUTE) + " AND stream = " + stream); + while (rs.next()) { + result.put(new InventoryVector(rs.getBytes("hash")), rs.getLong("expires")); + } + } catch (SQLException e) { + LOG.error(e.getMessage(), e); + } + } } - } catch (SQLException e) { - LOG.error(e.getMessage(), e); } return result; } @Override public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { - offer.removeAll(getFullInventory(streams)); + for (long stream : streams) { + getCache(stream).forEach((iv, t) -> offer.remove(iv)); + } return offer; } @@ -131,6 +143,7 @@ public class JdbcInventory extends JdbcHelper implements Inventory { ps.setLong(5, object.getType()); ps.setLong(6, object.getVersion()); ps.executeUpdate(); + getCache(object.getStream()).put(iv, object.getExpiresTime()); } catch (SQLException e) { LOG.debug("Error storing object of type " + object.getPayload().getClass().getSimpleName(), e); } catch (Exception e) { @@ -140,28 +153,19 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public boolean contains(ObjectMessage object) { - try (Connection connection = config.getConnection()) { - Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT count(1) FROM Inventory WHERE hash = X'" - + object.getInventoryVector() + "'"); - if (rs.next()) { - return rs.getInt(1) > 0; - } else { - throw new RuntimeException("Couldn't query if inventory contains " + object.getInventoryVector()); - } - } catch (Exception e) { - LOG.error(e.getMessage(), e); - throw new RuntimeException(e); - } + return getCache(object.getStream()).entrySet().stream() + .anyMatch(x -> x.getKey().equals(object.getInventoryVector())); } @Override public void cleanup() { try (Connection connection = config.getConnection()) { - // We delete only objects that expired 5 minutes ago or earlier, so we don't request objects we just deleted - connection.createStatement().executeUpdate("DELETE FROM Inventory WHERE expires < " + (now() - 300)); + connection.createStatement().executeUpdate("DELETE FROM Inventory WHERE expires < " + now(-5 * MINUTE)); } catch (SQLException e) { LOG.debug(e.getMessage(), e); } + for (Map<InventoryVector, Long> c : cache.values()) { + c.entrySet().removeIf(e -> e.getValue() < now(-5 * MINUTE)); + } } } From 409100ab2039fd1030115511ab6ac39d69d046e0 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 14 Oct 2015 22:56:46 +0200 Subject: [PATCH 13/42] Work-around for parsing problem (and made code more robust for different parsing problems) --- .../java/ch/dissem/bitmessage/factory/V3MessageFactory.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java index 7e48746..8dca6d2 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java @@ -22,7 +22,6 @@ import ch.dissem.bitmessage.entity.payload.ObjectPayload; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.exception.NodeException; -import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.AccessCounter; import ch.dissem.bitmessage.utils.Decode; import org.slf4j.Logger; @@ -93,7 +92,7 @@ class V3MessageFactory { try { ByteArrayInputStream dataStream = new ByteArrayInputStream(data); payload = Factory.getObjectPayload(objectType, version, stream, dataStream, data.length); - } catch (IOException e) { + } catch (Exception e) { LOG.trace("Could not parse object payload - using generic payload instead", e); payload = new GenericPayload(version, stream, data); } From d39342b12ff334c22607b0c5a69e9401a140858d Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 15 Oct 2015 08:21:41 +0200 Subject: [PATCH 14/42] Update .travis.yml Travis seems to use Java 7 by default, probably because it's defined in the base `build.gradle`. This causes a problem for modules that use Java 8. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a98b760..9bcf999 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,3 @@ language: java - +jdk: + - oraclejdk8 From 4913a21b11ba90626e971b834643b8789166c976 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 15 Oct 2015 15:34:04 +0200 Subject: [PATCH 15/42] Fixed NetworkHandlerTest --- build.gradle | 6 ++++++ .../bitmessage/networking/NetworkHandlerTest.java | 14 +++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 0e1e063..02a3903 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,12 @@ subprojects { mavenCentral() } + test { + testLogging { + exceptionFormat = 'full' + } + } + task javadocJar(type: Jar) { classifier = 'javadoc' from javadoc diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 7ba8fc4..ee437e1 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -104,7 +104,7 @@ public class NetworkHandlerTest { 10); t.join(); assertEquals(3, nodeInventory.getInventory().size()); - assertEquals(3, peerInventory.getInventory().size()); + assertInventorySize(3, peerInventory); } @Test(timeout = 5_000) @@ -121,7 +121,7 @@ public class NetworkHandlerTest { 10); t.join(); assertEquals(2, nodeInventory.getInventory().size()); - assertEquals(2, peerInventory.getInventory().size()); + assertInventorySize(2, peerInventory); } @Test(timeout = 10_000) @@ -137,7 +137,7 @@ public class NetworkHandlerTest { 10); t.join(); assertEquals(1, nodeInventory.getInventory().size()); - assertEquals(1, peerInventory.getInventory().size()); + assertInventorySize(1, peerInventory); } private void shutdown(BitmessageContext node) { @@ -146,4 +146,12 @@ public class NetworkHandlerTest { Thread.yield(); } while (node.isRunning()); } + + private void assertInventorySize(int expected, TestInventory inventory) throws InterruptedException { + long timeout = System.currentTimeMillis() + 1000; + while (expected != inventory.getInventory().size() && System.currentTimeMillis() < timeout) { + Thread.sleep(10); + } + assertEquals(expected, inventory.getInventory().size()); + } } From ac70a4b6323fc4ae77cc423dc7cf226d0a0f4ec0 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 15 Oct 2015 17:59:32 +0200 Subject: [PATCH 16/42] We probably shouldn't leave the bitmessage node running after the test is finished --- .../networking/NetworkHandlerTest.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index ee437e1..53ce36d 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -23,6 +23,7 @@ import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.NetworkHandler; import ch.dissem.bitmessage.security.bc.BouncySecurity; import ch.dissem.bitmessage.utils.Property; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; @@ -41,13 +42,14 @@ public class NetworkHandlerTest { private static TestInventory peerInventory; private static TestInventory nodeInventory; + private static BitmessageContext peer; private static BitmessageContext node; private static NetworkHandler networkHandler; @BeforeClass public static void setUp() { peerInventory = new TestInventory(); - BitmessageContext peer = new BitmessageContext.Builder() + peer = new BitmessageContext.Builder() .addressRepo(Mockito.mock(AddressRepository.class)) .inventory(peerInventory) .messageRepo(Mockito.mock(MessageRepository.class)) @@ -73,6 +75,21 @@ public class NetworkHandlerTest { .build(); } + @AfterClass + public static void cleanUp() { + shutdown(peer); + } + + private static void shutdown(BitmessageContext node) { + node.shutdown(); + do { + try { + Thread.sleep(100); + } catch (InterruptedException ignore) { + } + } while (node.isRunning()); + } + @Test(timeout = 20_000) public void ensureNodesAreConnecting() { try { @@ -140,13 +157,6 @@ public class NetworkHandlerTest { assertInventorySize(1, peerInventory); } - private void shutdown(BitmessageContext node) { - node.shutdown(); - do { - Thread.yield(); - } while (node.isRunning()); - } - private void assertInventorySize(int expected, TestInventory inventory) throws InterruptedException { long timeout = System.currentTimeMillis() + 1000; while (expected != inventory.getInventory().size() && System.currentTimeMillis() < timeout) { From 1e605f56a50a99c7ab49bc1247b03b50b72d5771 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 18 Oct 2015 18:22:49 +0200 Subject: [PATCH 17/42] Added method to retrieve all properties --- domain/src/main/java/ch/dissem/bitmessage/utils/Property.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java index 72abe58..e00d193 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java @@ -54,6 +54,10 @@ public class Property { return null; } + public Property[] getProperties() { + return properties; + } + @Override public String toString() { return toString(""); From fb300c873102a669767ddc9f624efd0353b77b34 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Mon, 19 Oct 2015 15:08:11 +0200 Subject: [PATCH 18/42] Fixed possible ConcurrentModificationException --- .../ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java index 3c4572e..a4f02f5 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java @@ -43,6 +43,10 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { for (int i = 0; i < cores; i++) { Worker w = new Worker(workers, (byte) cores, i, initialHash, target); workers.add(w); + } + for (Worker w : workers) { + // Doing this in the previous loop might cause a ConcurrentModificationException in the worker + // if a worker finds a nonce while new ones are still being added. w.start(); } for (Worker w : workers) { From a398b072b57ae4f1ca15dff62c4dd520c3f2150f Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Mon, 19 Oct 2015 15:14:25 +0200 Subject: [PATCH 19/42] (probably) fixed another concurrency problem in the tests --- .../ch/dissem/bitmessage/networking/NetworkHandlerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 53ce36d..512de72 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -120,7 +120,7 @@ public class NetworkHandlerTest { mock(NetworkHandler.MessageListener.class), 10); t.join(); - assertEquals(3, nodeInventory.getInventory().size()); + assertInventorySize(3, nodeInventory); assertInventorySize(3, peerInventory); } @@ -137,7 +137,7 @@ public class NetworkHandlerTest { mock(NetworkHandler.MessageListener.class), 10); t.join(); - assertEquals(2, nodeInventory.getInventory().size()); + assertInventorySize(2, nodeInventory); assertInventorySize(2, peerInventory); } @@ -153,7 +153,7 @@ public class NetworkHandlerTest { mock(NetworkHandler.MessageListener.class), 10); t.join(); - assertEquals(1, nodeInventory.getInventory().size()); + assertInventorySize(1, nodeInventory); assertInventorySize(1, peerInventory); } From bdc8e025c1bd9b315ae26b7daf16390bdb733b8e Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 24 Oct 2015 12:08:23 +0200 Subject: [PATCH 20/42] Connections are now severed after a configurable time (12h by default) or when a limit is exceeded (150 by default) --- .../ch/dissem/bitmessage/BitmessageContext.java | 13 +++++++++++++ .../java/ch/dissem/bitmessage/InternalContext.java | 14 +++++++++++++- .../java/ch/dissem/bitmessage/utils/Singleton.java | 6 +++++- .../dissem/bitmessage/networking/Connection.java | 9 ++++++++- .../networking/DefaultNetworkHandler.java | 13 +++++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index d7d8980..1e4e905 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -40,6 +40,7 @@ import static ch.dissem.bitmessage.entity.Plaintext.Status.*; import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; import static ch.dissem.bitmessage.utils.UnixTime.DAY; +import static ch.dissem.bitmessage.utils.UnixTime.HOUR; /** * <p>Use this class if you want to create a Bitmessage client.</p> @@ -295,6 +296,8 @@ public class BitmessageContext { Security security; MessageCallback messageCallback; Listener listener; + int connectionLimit = 150; + long connectionTTL = 12 * HOUR; public Builder() { } @@ -349,6 +352,16 @@ public class BitmessageContext { return this; } + public Builder connectionLimit(int connectionLimit) { + this.connectionLimit = connectionLimit; + return this; + } + + public Builder connectionTTL(int hours) { + this.connectionTTL = hours * HOUR; + return this; + } + public BitmessageContext build() { nonNull("inventory", inventory); nonNull("nodeRegistry", nodeRegistry); diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 6d969bd..bbdedb6 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -56,6 +56,8 @@ public class InternalContext { private final long clientNonce; private final long networkNonceTrialsPerByte = 1000; private final long networkExtraBytes = 1000; + private long connectionTTL; + private int connectionLimit; public InternalContext(BitmessageContext.Builder builder) { this.security = builder.security; @@ -68,6 +70,8 @@ public class InternalContext { this.clientNonce = security.randomNonce(); this.messageCallback = builder.messageCallback; this.port = builder.port; + this.connectionLimit = builder.connectionLimit; + this.connectionTTL = builder.connectionTTL; Singleton.initialize(security); @@ -166,7 +170,7 @@ public class InternalContext { .payload(payload) .build(); if (object.isSigned()) { - object.sign( from.getPrivateKey()); + object.sign(from.getPrivateKey()); } if (payload instanceof Broadcast) { ((Broadcast) payload).encrypt(); @@ -232,6 +236,14 @@ public class InternalContext { return clientNonce; } + public long getConnectionTTL() { + return connectionTTL; + } + + public int getConnectionLimit() { + return connectionLimit; + } + public interface ContextHolder { void setContext(InternalContext context); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java index 169db77..d272beb 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java @@ -25,7 +25,11 @@ public class Singleton { private static Security security; public static void initialize(Security security) { - Singleton.security = security; + synchronized (Singleton.class) { + if (Singleton.security == null) { + Singleton.security = security; + } + } } public static Security security() { 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 6312557..f0ff5ed 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -51,8 +51,10 @@ import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; */ public class Connection { public static final int READ_TIMEOUT = 2000; - private final static Logger LOG = LoggerFactory.getLogger(Connection.class); + private static final Logger LOG = LoggerFactory.getLogger(Connection.class); private static final int CONNECT_TIMEOUT = 5000; + + private final long startTime; private final ConcurrentMap<InventoryVector, Long> ivCache; private final InternalContext ctx; private final Mode mode; @@ -89,6 +91,7 @@ public class Connection { private Connection(InternalContext context, Mode mode, MessageListener listener, Socket socket, Map<InventoryVector, Long> requestedObjectsMap, NetworkAddress node, long syncTimeout) { + this.startTime = UnixTime.now(); this.ctx = context; this.mode = mode; this.state = CONNECTING; @@ -109,6 +112,10 @@ public class Connection { timeoutInSeconds); } + public long getStartTime() { + return startTime; + } + public Mode getMode() { return mode; } 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 e563152..7b49978 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -23,6 +23,7 @@ import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.ports.NetworkHandler; import ch.dissem.bitmessage.utils.Collections; import ch.dissem.bitmessage.utils.Property; +import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -112,9 +113,21 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { while (running) { try { int active = 0; + long now = UnixTime.now(); synchronized (connections) { + int diff = connections.size() - ctx.getConnectionLimit(); + if (diff > 0) { + for (Connection c : connections) { + c.disconnect(); + diff--; + if (diff == 0) break; + } + } for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) { Connection c = iterator.next(); + if (now - c.getStartTime() > ctx.getConnectionTTL()) { + c.disconnect(); + } if (c.getState() == DISCONNECTED) { // Remove the current element from the iterator and the list. iterator.remove(); From 36fe7807660da84b9661ec99bc28522e21e59f11 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Mon, 26 Oct 2015 09:49:49 +0100 Subject: [PATCH 21/42] The nonce is now set over a callback method in the POW engine. This should make some POW implementations easier. --- .../ch/dissem/bitmessage/InternalContext.java | 73 ++++++++++++------- .../bitmessage/ports/AbstractSecurity.java | 5 +- .../ports/MultiThreadedPOWEngine.java | 66 ++++++++++------- .../bitmessage/ports/ProofOfWorkEngine.java | 16 +++- .../ch/dissem/bitmessage/ports/Security.java | 4 +- .../bitmessage/ports/SimplePOWEngine.java | 4 +- .../bitmessage/utils/CallbackWaiter.java | 41 +++++++++++ .../ports/ProofOfWorkEngineTest.java | 21 ++++-- .../bitmessage/security/SecurityTest.java | 14 +++- 9 files changed, 172 insertions(+), 72 deletions(-) create mode 100644 domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index bbdedb6..7a89978 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -159,12 +159,13 @@ public class InternalContext { return networkExtraBytes > extraBytes ? networkExtraBytes : extraBytes; } - public void send(BitmessageAddress from, BitmessageAddress to, ObjectPayload payload, long timeToLive, long nonceTrialsPerByte, long extraBytes) { + 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; long expires = UnixTime.now(+timeToLive); LOG.info("Expires at " + expires); - ObjectMessage object = new ObjectMessage.Builder() + final ObjectMessage object = new ObjectMessage.Builder() .stream(to.getStream()) .expiresTime(expires) .payload(payload) @@ -178,26 +179,32 @@ public class InternalContext { object.encrypt(to.getPubkey()); } messageCallback.proofOfWorkStarted(payload); - security.doProofOfWork(object, nonceTrialsPerByte, extraBytes); - 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()); + 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); } } - public void sendPubkey(BitmessageAddress identity, long targetStream) { + public void sendPubkey(final BitmessageAddress identity, final long targetStream) { try { long expires = UnixTime.now(+28 * DAY); LOG.info("Expires at " + expires); - ObjectMessage response = new ObjectMessage.Builder() + final ObjectMessage response = new ObjectMessage.Builder() .stream(targetStream) .expiresTime(expires) .payload(identity.getPubkey()) @@ -205,31 +212,43 @@ public class InternalContext { response.sign(identity.getPrivateKey()); response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); messageCallback.proofOfWorkStarted(identity.getPubkey()); - security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); - messageCallback.proofOfWorkCompleted(identity.getPubkey()); - inventory.storeObject(response); - networkHandler.offer(response.getInventoryVector()); - // TODO: save that the pubkey was just sent, and on which stream! - messageCallback.messageOffered(identity.getPubkey(), response.getInventoryVector()); + security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes, + new ProofOfWorkEngine.Callback() { + @Override + public void onNonceCalculated(byte[] nonce) { + response.setNonce(nonce); + messageCallback.proofOfWorkCompleted(identity.getPubkey()); + inventory.storeObject(response); + networkHandler.offer(response.getInventoryVector()); + // TODO: save that the pubkey was just sent, and on which stream! + messageCallback.messageOffered(identity.getPubkey(), response.getInventoryVector()); + } + }); } catch (IOException e) { throw new RuntimeException(e); } } - public void requestPubkey(BitmessageAddress contact) { + public void requestPubkey(final BitmessageAddress contact) { long expires = UnixTime.now(+2 * DAY); LOG.info("Expires at " + expires); - ObjectMessage response = new ObjectMessage.Builder() + final ObjectMessage response = new ObjectMessage.Builder() .stream(contact.getStream()) .expiresTime(expires) .payload(new GetPubkey(contact)) .build(); messageCallback.proofOfWorkStarted(response.getPayload()); - security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); - messageCallback.proofOfWorkCompleted(response.getPayload()); - inventory.storeObject(response); - networkHandler.offer(response.getInventoryVector()); - messageCallback.messageOffered(response.getPayload(), response.getInventoryVector()); + security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes, + new ProofOfWorkEngine.Callback() { + @Override + public void onNonceCalculated(byte[] nonce) { + response.setNonce(nonce); + messageCallback.proofOfWorkCompleted(response.getPayload()); + inventory.storeObject(response); + networkHandler.offer(response.getInventoryVector()); + messageCallback.messageOffered(response.getPayload(), response.getInventoryVector()); + } + }); } public long getClientNonce() { diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java index 00ed1f2..1c4b4dc 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java @@ -93,7 +93,7 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont } public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, - long extraBytes) { + long extraBytes, ProofOfWorkEngine.Callback callback) { try { if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; if (extraBytes < 1000) extraBytes = 1000; @@ -102,8 +102,7 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); - byte[] nonce = context.getProofOfWorkEngine().calculateNonce(initialHash, target); - object.setNonce(nonce); + context.getProofOfWorkEngine().calculateNonce(initialHash, target, callback); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java index a4f02f5..f9db392 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java @@ -34,14 +34,15 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { private static Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class); @Override - public byte[] calculateNonce(byte[] initialHash, byte[] target) { + public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) { + callback = new CallbackWrapper(callback); int cores = Runtime.getRuntime().availableProcessors(); if (cores > 255) cores = 255; LOG.info("Doing POW using " + cores + " cores"); long time = System.currentTimeMillis(); List<Worker> workers = new ArrayList<>(cores); for (int i = 0; i < cores; i++) { - Worker w = new Worker(workers, (byte) cores, i, initialHash, target); + Worker w = new Worker(workers, (byte) cores, i, initialHash, target, callback); workers.add(w); } for (Worker w : workers) { @@ -49,30 +50,20 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { // if a worker finds a nonce while new ones are still being added. w.start(); } - for (Worker w : workers) { - try { - w.join(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - if (w.isSuccessful()) { - LOG.info("Nonce calculated in " + ((System.currentTimeMillis() - time) / 1000) + " seconds"); - return w.getNonce(); - } - } - throw new RuntimeException("All workers ended without yielding a nonce - something is seriously broken!"); } private static class Worker extends Thread { + private final Callback callback; private final byte numberOfCores; private final List<Worker> workers; private final byte[] initialHash; private final byte[] target; private final MessageDigest mda; private final byte[] nonce = new byte[8]; - private boolean successful = false; - public Worker(List<Worker> workers, byte numberOfCores, int core, byte[] initialHash, byte[] target) { + public Worker(List<Worker> workers, byte numberOfCores, int core, byte[] initialHash, byte[] target, + Callback callback) { + this.callback = callback; this.numberOfCores = numberOfCores; this.workers = workers; this.initialHash = initialHash; @@ -86,14 +77,6 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { } } - public boolean isSuccessful() { - return successful; - } - - public byte[] getNonce() { - return nonce; - } - @Override public void run() { do { @@ -101,13 +84,42 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { mda.update(nonce); mda.update(initialHash); if (!Bytes.lt(target, mda.digest(mda.digest()), 8)) { - successful = true; - for (Worker w : workers) { - w.interrupt(); + synchronized (callback) { + if (!Thread.interrupted()) { + try { + callback.onNonceCalculated(nonce); + } finally { + for (Worker w : workers) { + w.interrupt(); + } + } + } } return; } } while (!Thread.interrupted()); } } + + public static class CallbackWrapper implements Callback { + private final Callback callback; + private final long startTime; + private boolean waiting = true; + + public CallbackWrapper(Callback callback) { + this.startTime = System.currentTimeMillis(); + this.callback = callback; + } + + @Override + public void onNonceCalculated(byte[] nonce) { + synchronized (this) { + if (waiting) { + LOG.info("Nonce calculated in " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds"); + waiting = false; + callback.onNonceCalculated(nonce); + } + } + } + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java index 31f6657..90513dc 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java @@ -24,9 +24,17 @@ public interface ProofOfWorkEngine { * Returns a nonce, such that the first 8 bytes from sha512(sha512(nonce||initialHash)) represent a unsigned long * smaller than target. * - * @param initialHash the SHA-512 hash of the object to send, sans nonce - * @param target the target, representing an unsigned long - * @return 8 bytes nonce + * @param initialHash the SHA-512 hash of the object to send, sans nonce + * @param target the target, representing an unsigned long + * @param callback called with the calculated nonce as argument. The ProofOfWorkEngine implementation must make + * sure this is only called once. */ - byte[] calculateNonce(byte[] initialHash, byte[] target); + void calculateNonce(byte[] initialHash, byte[] target, Callback callback); + + interface Callback { + /** + * @param nonce 8 bytes nonce + */ + void onNonceCalculated(byte[] nonce); + } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java index 83ad43d..c5fcb8f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java @@ -120,9 +120,10 @@ public interface Security { * @param object to do the proof of work for * @param nonceTrialsPerByte difficulty * @param extraBytes bytes to add to the object size (makes it more difficult to send small messages) + * @param callback to handle nonce once it's calculated */ void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, - long extraBytes); + long extraBytes, ProofOfWorkEngine.Callback callback); /** * @param object to be checked @@ -143,7 +144,6 @@ public interface Security { byte[] mac(byte[] key_m, byte[] data); /** - * * @param encrypt if true, encrypts data, otherwise tries to decrypt it. * @param data * @param key_e diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java index 211df53..25d51aa 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java @@ -27,7 +27,7 @@ import static ch.dissem.bitmessage.utils.Bytes.inc; */ public class SimplePOWEngine implements ProofOfWorkEngine { @Override - public byte[] calculateNonce(byte[] initialHash, byte[] target) { + public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) { byte[] nonce = new byte[8]; MessageDigest mda; try { @@ -40,6 +40,6 @@ public class SimplePOWEngine implements ProofOfWorkEngine { mda.update(nonce); mda.update(initialHash); } while (Bytes.lt(target, mda.digest(mda.digest()), 8)); - return nonce; + callback.onNonceCalculated(nonce); } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java b/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java new file mode 100644 index 0000000..e664945 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java @@ -0,0 +1,41 @@ +/* + * Copyright 2015 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.utils; + +/** + * Waits for a value within a callback method to be set. + */ +public class CallbackWaiter<T> { + private volatile boolean isSet; + private volatile T value; + + public void setValue(T value) { + synchronized (this) { + this.isSet = true; + this.value = value; + } + } + + public T waitForValue() throws InterruptedException { + while (!isSet) { + Thread.sleep(100); + } + synchronized (this) { + return value; + } + } +} diff --git a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java index ca17a23..38e52a8 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java @@ -17,29 +17,40 @@ package ch.dissem.bitmessage.ports; import ch.dissem.bitmessage.utils.Bytes; +import ch.dissem.bitmessage.utils.CallbackWaiter; +import ch.dissem.bitmessage.utils.TestBase; import org.junit.Test; import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.assertTrue; -public class ProofOfWorkEngineTest { +public class ProofOfWorkEngineTest extends TestBase { @Test - public void testSimplePOWEngine() { + public void testSimplePOWEngine() throws InterruptedException { testPOW(new SimplePOWEngine()); } @Test - public void testThreadedPOWEngine() { + public void testThreadedPOWEngine() throws InterruptedException { testPOW(new MultiThreadedPOWEngine()); } - private void testPOW(ProofOfWorkEngine engine) { + private void testPOW(ProofOfWorkEngine engine) throws InterruptedException { long time = System.currentTimeMillis(); byte[] initialHash = security().sha512(new byte[]{1, 3, 6, 4}); byte[] target = {0, 0, -1, -1, -1, -1, -1, -1}; - byte[] nonce = engine.calculateNonce(initialHash, target); + final CallbackWaiter<byte[]> waiter = new CallbackWaiter<>(); + engine.calculateNonce(initialHash, target, + new ProofOfWorkEngine.Callback() { + @Override + public void onNonceCalculated(byte[] nonce) { + waiter.setValue(nonce); + } + }); + byte[] nonce = waiter.waitForValue(); System.out.println("Calculating nonce took " + (System.currentTimeMillis() - time) + "ms"); assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8)); } + } diff --git a/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java b/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java index 94f0ede..46a8ae6 100644 --- a/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java +++ b/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java @@ -4,7 +4,9 @@ import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.payload.GenericPayload; import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine; +import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.utils.CallbackWaiter; import ch.dissem.bitmessage.utils.Singleton; import ch.dissem.bitmessage.utils.UnixTime; import org.junit.Test; @@ -78,14 +80,22 @@ public class SecurityTest { } @Test - public void testDoProofOfWork() throws IOException { + public void testDoProofOfWork() throws Exception { ObjectMessage objectMessage = new ObjectMessage.Builder() .nonce(new byte[8]) .expiresTime(UnixTime.now(+2 * DAY)) .objectType(0) .payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0)) .build(); - security.doProofOfWork(objectMessage, 1000, 1000); + final CallbackWaiter<byte[]> waiter = new CallbackWaiter<>(); + security.doProofOfWork(objectMessage, 1000, 1000, + new ProofOfWorkEngine.Callback() { + @Override + public void onNonceCalculated(byte[] nonce) { + waiter.setValue(nonce); + } + }); + objectMessage.setNonce(waiter.waitForValue()); security.checkProofOfWork(objectMessage, 1000, 1000); } } \ No newline at end of file From b496f81b207a55a1faef70b9c2e6cf2e5b7f52cf Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Tue, 27 Oct 2015 07:50:20 +0100 Subject: [PATCH 22/42] Fixed POWEngine and improved test. (Locks can't be released from a different thread, we need to use a semaphore) --- .../ports/MultiThreadedPOWEngine.java | 19 ++++++++++++-- .../ports/ProofOfWorkEngineTest.java | 26 ++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java index f9db392..2907473 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java @@ -24,6 +24,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Semaphore; import static ch.dissem.bitmessage.utils.Bytes.inc; @@ -31,15 +32,28 @@ import static ch.dissem.bitmessage.utils.Bytes.inc; * A POW engine using all available CPU cores. */ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { - private static Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class); + private static final Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class); + private static final Semaphore semaphore = new Semaphore(1, true); + /** + * Although it has a callback, this method will block until all pending nonce calculations are done. (It gets very + * inefficient if multiple nonce are calculated at the same time. + * + * @param initialHash the SHA-512 hash of the object to send, sans nonce + * @param target the target, representing an unsigned long + * @param callback called with the calculated nonce as argument. The ProofOfWorkEngine implementation must make + */ @Override public void calculateNonce(byte[] initialHash, byte[] target, Callback callback) { + try { + semaphore.acquire(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } callback = new CallbackWrapper(callback); int cores = Runtime.getRuntime().availableProcessors(); if (cores > 255) cores = 255; LOG.info("Doing POW using " + cores + " cores"); - long time = System.currentTimeMillis(); List<Worker> workers = new ArrayList<>(cores); for (int i = 0; i < cores; i++) { Worker w = new Worker(workers, (byte) cores, i, initialHash, target, callback); @@ -89,6 +103,7 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { try { callback.onNonceCalculated(nonce); } finally { + semaphore.release(); for (Worker w : workers) { w.interrupt(); } diff --git a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java index 38e52a8..b4fd194 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java @@ -25,12 +25,12 @@ import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.assertTrue; public class ProofOfWorkEngineTest extends TestBase { - @Test + @Test(timeout = 90_000) public void testSimplePOWEngine() throws InterruptedException { testPOW(new SimplePOWEngine()); } - @Test + @Test(timeout = 90_000) public void testThreadedPOWEngine() throws InterruptedException { testPOW(new MultiThreadedPOWEngine()); } @@ -49,8 +49,28 @@ public class ProofOfWorkEngineTest extends TestBase { } }); byte[] nonce = waiter.waitForValue(); - System.out.println("Calculating nonce took " + (System.currentTimeMillis() - time) + "ms"); + time = System.currentTimeMillis() - time; + System.out.println("Calculating nonce took " + time + "ms"); assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8)); + + // Let's add a second (shorter) run to find possible multi threading issues + long time2 = System.currentTimeMillis(); + byte[] initialHash2 = security().sha512(new byte[]{1, 3, 6, 5}); + byte[] target2 = {0, -1, -1, -1, -1, -1, -1, -1}; + + final CallbackWaiter<byte[]> waiter2 = new CallbackWaiter<>(); + engine.calculateNonce(initialHash2, target2, + new ProofOfWorkEngine.Callback() { + @Override + public void onNonceCalculated(byte[] nonce) { + waiter2.setValue(nonce); + } + }); + byte[] nonce2 = waiter2.waitForValue(); + time2 = System.currentTimeMillis() - time2; + System.out.println("Calculating nonce took " + time2 + "ms"); + assertTrue(Bytes.lt(security().doubleSha512(nonce2, initialHash2), target2, 8)); + assertTrue("Second nonce must be quicker to find", time > time2); } } From 9c2d8589bfdad356aec7b168a871358ee8a564ba Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 28 Oct 2015 16:56:09 +0100 Subject: [PATCH 23/42] Improved POW test --- .../ports/MultiThreadedPOWEngine.java | 5 +++-- .../bitmessage/utils/CallbackWaiter.java | 11 ++++++++-- .../ports/ProofOfWorkEngineTest.java | 20 ++++++++----------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java index 2907473..ac65d3d 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java @@ -36,8 +36,9 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { private static final Semaphore semaphore = new Semaphore(1, true); /** - * Although it has a callback, this method will block until all pending nonce calculations are done. (It gets very - * inefficient if multiple nonce are calculated at the same time. + * This method will block until all pending nonce calculations are done, but not wait for its own calculation + * to finish. + * (This implementation becomes very inefficient if multiple nonce are calculated at the same time.) * * @param initialHash the SHA-512 hash of the object to send, sans nonce * @param target the target, representing an unsigned long diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java b/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java index e664945..775a5f3 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java @@ -20,13 +20,16 @@ package ch.dissem.bitmessage.utils; * Waits for a value within a callback method to be set. */ public class CallbackWaiter<T> { + private final long startTime = System.currentTimeMillis(); private volatile boolean isSet; - private volatile T value; + private T value; + private long time; public void setValue(T value) { synchronized (this) { - this.isSet = true; + this.time = System.currentTimeMillis() - startTime; this.value = value; + this.isSet = true; } } @@ -38,4 +41,8 @@ public class CallbackWaiter<T> { return value; } } + + public long getTime() { + return time; + } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java index b4fd194..ba5307d 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java @@ -36,27 +36,24 @@ public class ProofOfWorkEngineTest extends TestBase { } private void testPOW(ProofOfWorkEngine engine) throws InterruptedException { - long time = System.currentTimeMillis(); byte[] initialHash = security().sha512(new byte[]{1, 3, 6, 4}); - byte[] target = {0, 0, -1, -1, -1, -1, -1, -1}; + byte[] target = {0, 0, 0, -1, -1, -1, -1, -1}; - final CallbackWaiter<byte[]> waiter = new CallbackWaiter<>(); + final CallbackWaiter<byte[]> waiter1 = new CallbackWaiter<>(); engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() { @Override public void onNonceCalculated(byte[] nonce) { - waiter.setValue(nonce); + waiter1.setValue(nonce); } }); - byte[] nonce = waiter.waitForValue(); - time = System.currentTimeMillis() - time; - System.out.println("Calculating nonce took " + time + "ms"); + byte[] nonce = waiter1.waitForValue(); + System.out.println("Calculating nonce took " + waiter1.getTime() + "ms"); assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8)); // Let's add a second (shorter) run to find possible multi threading issues - long time2 = System.currentTimeMillis(); byte[] initialHash2 = security().sha512(new byte[]{1, 3, 6, 5}); - byte[] target2 = {0, -1, -1, -1, -1, -1, -1, -1}; + byte[] target2 = {0, 0, -1, -1, -1, -1, -1, -1}; final CallbackWaiter<byte[]> waiter2 = new CallbackWaiter<>(); engine.calculateNonce(initialHash2, target2, @@ -67,10 +64,9 @@ public class ProofOfWorkEngineTest extends TestBase { } }); byte[] nonce2 = waiter2.waitForValue(); - time2 = System.currentTimeMillis() - time2; - System.out.println("Calculating nonce took " + time2 + "ms"); + System.out.println("Calculating nonce took " + waiter2.getTime() + "ms"); assertTrue(Bytes.lt(security().doubleSha512(nonce2, initialHash2), target2, 8)); - assertTrue("Second nonce must be quicker to find", time > time2); + assertTrue("Second nonce must be quicker to find", waiter1.getTime() > waiter2.getTime()); } } From c9c0806e0d23423f2bbc84a9cc91ba1ccd0f8f27 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 29 Oct 2015 12:34:29 +0100 Subject: [PATCH 24/42] Attempt to disconnect on thread interrupt --- .../src/main/java/ch/dissem/bitmessage/BitmessageContext.java | 3 ++- .../main/java/ch/dissem/bitmessage/ports/NetworkHandler.java | 3 +++ .../main/java/ch/dissem/bitmessage/networking/Connection.java | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 1e4e905..d3a1329 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -206,7 +206,8 @@ public class BitmessageContext { try { t.join(); } catch (InterruptedException e) { - throw new RuntimeException(e.getMessage(), e); + LOG.info("Thread was interrupted. Trying to shut down synchronization and returning."); + t.interrupt(); } } } 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 fd44358..2c79b1f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java @@ -29,6 +29,9 @@ import java.net.InetAddress; public interface NetworkHandler { /** * Connects to the trusted host, fetches and offers new messages and disconnects afterwards. + * <p> + * An implementation should disconnect if either the timeout is reached or the returned thread is interrupted. + * </p> */ Thread synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds); 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 f0ff5ed..b2c2e40 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -130,6 +130,9 @@ public class Connection { @SuppressWarnings("RedundantIfStatement") private boolean syncFinished(NetworkMessage msg) { + if (Thread.interrupted()) { + return true; + } if (syncTimeout == 0 || state != ACTIVE) { return false; } From 2a8834e3c6636b148fe7c33770db7c88a582b13a Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Fri, 30 Oct 2015 20:05:55 +0100 Subject: [PATCH 25/42] Timeout might have been too short for Travis CI (or there is an actual problem with concurrency) --- .../ch/dissem/bitmessage/networking/NetworkHandlerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 512de72..74b3ff1 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -124,7 +124,7 @@ public class NetworkHandlerTest { assertInventorySize(3, peerInventory); } - @Test(timeout = 5_000) + @Test(timeout = 10_000) public void ensureObjectsAreSynchronizedIfOnlyPeerHasObjects() throws Exception { peerInventory.init( "V4Pubkey.payload", From 1f05a52f05c21bc3dde09bd4bd8eeb84294aec21 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 8 Nov 2015 10:14:37 +0100 Subject: [PATCH 26/42] Improvements - Massively reduced logging, especially at debug level - Optimizations to reduce system load - Use bootstrapping to find stable nodes --- .../dissem/bitmessage/BitmessageContext.java | 13 ++-- .../bitmessage/DefaultMessageListener.java | 7 +- .../bitmessage/entity/payload/CryptoBox.java | 2 +- .../bitmessage/ports/AbstractSecurity.java | 1 - .../bitmessage/ports/MemoryNodeRegistry.java | 73 +++++++++++-------- .../bitmessage/ports/NetworkHandler.java | 3 +- domain/src/main/resources/nodes.txt | 14 +--- .../bitmessage/networking/Connection.java | 27 ++----- .../networking/DefaultNetworkHandler.java | 29 ++++---- .../networking/NetworkHandlerTest.java | 13 ++-- 10 files changed, 88 insertions(+), 94 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index d3a1329..9f4d9a3 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -33,8 +33,7 @@ import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.util.Arrays; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; import static ch.dissem.bitmessage.entity.Plaintext.Status.*; import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; @@ -201,13 +200,15 @@ public class BitmessageContext { * @param wait waits for the synchronization thread to finish */ public void synchronize(InetAddress host, int port, long timeoutInSeconds, boolean wait) { - Thread t = ctx.getNetworkHandler().synchronize(host, port, networkListener, timeoutInSeconds); + Future<?> future = ctx.getNetworkHandler().synchronize(host, port, networkListener, timeoutInSeconds); if (wait) { try { - t.join(); + future.get(); } catch (InterruptedException e) { LOG.info("Thread was interrupted. Trying to shut down synchronization and returning."); - t.interrupt(); + future.cancel(true); + } catch (CancellationException | ExecutionException e) { + LOG.debug(e.getMessage(), e); } } } @@ -241,7 +242,7 @@ public class BitmessageContext { ctx.getAddressRepo().save(address); break; } else { - LOG.debug("Found pubkey for " + address + " but signature is invalid"); + LOG.info("Found pubkey for " + address + " but signature is invalid"); } } } else { diff --git a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java index 105cc96..e069704 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java +++ b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java @@ -71,7 +71,8 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { protected void receive(ObjectMessage object, GetPubkey getPubkey) { BitmessageAddress identity = ctx.getAddressRepo().findIdentity(getPubkey.getRipeTag()); if (identity != null && identity.getPrivateKey() != null) { - LOG.debug("Got pubkey request for identity " + identity); + LOG.info("Got pubkey request for identity " + identity); + // FIXME: only send pubkey if it wasn't sent in the last 28 days ctx.sendPubkey(identity, object.getStream()); } } @@ -90,10 +91,10 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { } if (address != null) { address.setPubkey(pubkey); - LOG.debug("Got pubkey for contact " + address); + LOG.info("Got pubkey for contact " + address); ctx.getAddressRepo().save(address); List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address); - LOG.debug("Sending " + messages.size() + " messages for contact " + address); + LOG.info("Sending " + messages.size() + " messages for contact " + address); for (Plaintext msg : messages) { msg.setStatus(DOING_PROOF_OF_WORK); ctx.getMessageRepository().save(msg); diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java index 7f994d1..a870b32 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java @@ -157,7 +157,7 @@ public class CryptoBox implements Streamable { } public Builder curveType(int curveType) { - if (curveType != 0x2CA) LOG.debug("Unexpected curve type " + curveType); + if (curveType != 0x2CA) LOG.trace("Unexpected curve type " + curveType); this.curveType = curveType; return this; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java index 1c4b4dc..bd55180 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java @@ -123,7 +123,6 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont private byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { BigInteger TTL = BigInteger.valueOf(object.getExpiresTime() - UnixTime.now()); - LOG.debug("TTL: " + TTL + "s"); BigInteger numerator = TWO.pow(64); BigInteger powLength = BigInteger.valueOf(object.getPayloadBytesWithoutNonce().length + extraBytes); BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte).multiply(powLength.add(powLength.multiply(TTL).divide(BigInteger.valueOf(2).pow(16)))); diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java index 9c97b87..8d43423 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java @@ -37,40 +37,42 @@ public class MemoryNodeRegistry implements NodeRegistry { private final Map<Long, Set<NetworkAddress>> stableNodes = new ConcurrentHashMap<>(); private final Map<Long, Set<NetworkAddress>> knownNodes = new ConcurrentHashMap<>(); - public MemoryNodeRegistry() { - new Thread(new Runnable() { - @Override - public void run() { - try (InputStream in = getClass().getClassLoader().getResourceAsStream("nodes.txt")) { - Scanner scanner = new Scanner(in); - long stream = 0; - Set<NetworkAddress> streamSet = null; - while (scanner.hasNext()) { - try { - String line = scanner.nextLine().trim(); - if (line.startsWith("#") || line.isEmpty()) { - // Ignore - continue; - } - if (line.startsWith("[stream")) { - stream = Long.parseLong(line.substring(8, line.lastIndexOf(']'))); - streamSet = new HashSet<>(); - stableNodes.put(stream, streamSet); - } else if (streamSet != null) { - int portIndex = line.lastIndexOf(':'); - InetAddress inetAddress = InetAddress.getByName(line.substring(0, portIndex)); - int port = Integer.valueOf(line.substring(portIndex + 1)); - streamSet.add(new NetworkAddress.Builder().ip(inetAddress).port(port).stream(stream).build()); - } - } catch (IOException e) { - LOG.warn(e.getMessage(), e); + private void loadStableNodes() { + try (InputStream in = getClass().getClassLoader().getResourceAsStream("nodes.txt")) { + Scanner scanner = new Scanner(in); + long stream = 0; + Set<NetworkAddress> streamSet = null; + while (scanner.hasNext()) { + try { + String line = scanner.nextLine().trim(); + if (line.startsWith("#") || line.isEmpty()) { + // Ignore + continue; + } + if (line.startsWith("[stream")) { + stream = Long.parseLong(line.substring(8, line.lastIndexOf(']'))); + streamSet = new HashSet<>(); + stableNodes.put(stream, streamSet); + } else if (streamSet != null) { + int portIndex = line.lastIndexOf(':'); + InetAddress[] inetAddresses = InetAddress.getAllByName(line.substring(0, portIndex)); + int port = Integer.valueOf(line.substring(portIndex + 1)); + for (InetAddress inetAddress : inetAddresses) { + streamSet.add(new NetworkAddress.Builder().ip(inetAddress).port(port).stream(stream).build()); } } } catch (IOException e) { - throw new RuntimeException(e); + LOG.warn(e.getMessage(), e); } } - }, "node registry initializer").start(); + if (LOG.isDebugEnabled()) { + for (Map.Entry<Long, Set<NetworkAddress>> e : stableNodes.entrySet()) { + LOG.debug("Stream " + e.getKey() + ": loaded " + e.getValue().size() + " bootstrap nodes."); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override @@ -86,9 +88,16 @@ public class MemoryNodeRegistry implements NodeRegistry { known.remove(node); } } - } else if (stableNodes.containsKey(stream)) { - // To reduce load on stable nodes, only return one - result.add(selectRandom(stableNodes.get(stream))); + } else { + Set<NetworkAddress> nodes = stableNodes.get(stream); + if (nodes == null || nodes.isEmpty()) { + loadStableNodes(); + nodes = stableNodes.get(stream); + } + if (nodes != null && !nodes.isEmpty()) { + // To reduce load on stable nodes, only return one + result.add(selectRandom(nodes)); + } } } return selectRandom(limit, result); 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 2c79b1f..e2fd170 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java @@ -22,6 +22,7 @@ import ch.dissem.bitmessage.utils.Property; import java.io.IOException; import java.net.InetAddress; +import java.util.concurrent.Future; /** * Handles incoming messages @@ -33,7 +34,7 @@ public interface NetworkHandler { * An implementation should disconnect if either the timeout is reached or the returned thread is interrupted. * </p> */ - Thread synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds); + Future<?> synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds); /** * Start a full network node, accepting incoming connections and relaying objects. diff --git a/domain/src/main/resources/nodes.txt b/domain/src/main/resources/nodes.txt index e0a334e..9466a85 100644 --- a/domain/src/main/resources/nodes.txt +++ b/domain/src/main/resources/nodes.txt @@ -1,18 +1,8 @@ [stream 1] -5.45.99.75:8444 -75.167.159.54:8444 -95.165.168.168:8444 -85.180.139.241:8444 -178.62.12.187:8448 - -[2604:2000:1380:9f:82e:148b:2746:d0c7]:8080 -158.222.211.81:8080 -24.188.198.204:8111 -109.147.204.113:1195 -178.11.46.221:8444 - dissem.ch:8444 +bootstrap8080.bitmessage.org:8080 +bootstrap8444.bitmessage.org:8444 [stream 2] # none yet \ No newline at end of file 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 b2c2e40..8ed2fb4 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -235,10 +235,9 @@ public class Connection { ObjectMessage objectMessage = (ObjectMessage) messagePayload; try { if (ctx.getInventory().contains(objectMessage)) { - LOG.debug("Received object " + objectMessage.getInventoryVector() + " - already in inventory"); + LOG.trace("Received object " + objectMessage.getInventoryVector() + " - already in inventory"); break; } - LOG.debug("Received object " + objectMessage.getInventoryVector()); security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); listener.receive(objectMessage); ctx.getInventory().storeObject(objectMessage); @@ -294,7 +293,6 @@ public class Connection { } public void offer(InventoryVector iv) { - LOG.debug("Offering " + iv + " to node " + node.toString()); sendingQueue.offer(new Inv.Builder() .addInventoryVector(iv) .build()); @@ -321,14 +319,7 @@ public class Connection { private synchronized void initSocket(Socket socket) throws IOException { if (!socketInitialized) { if (!socket.isConnected()) { - LOG.debug("Trying to connect to node " + node); - socket.connect(new InetSocketAddress(node.toInetAddress(), node.getPort()), CONNECT_TIMEOUT); - } - socket.setSoTimeout(READ_TIMEOUT); - in = socket.getInputStream(); - out = socket.getOutputStream(); - if (!socket.isConnected()) { - LOG.debug("Trying to connect to node " + node); + LOG.trace("Trying to connect to node " + node); socket.connect(new InetSocketAddress(node.toInetAddress(), node.getPort()), CONNECT_TIMEOUT); } socket.setSoTimeout(READ_TIMEOUT); @@ -359,6 +350,7 @@ public class Connection { send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); } while (state != DISCONNECTED) { + Thread.sleep(100); try { NetworkMessage msg = Factory.getNetworkMessage(version, in); if (msg == null) @@ -413,15 +405,13 @@ public class Connection { if (syncFinished(null)) disconnect(); } } - Thread.yield(); } - } catch (IOException | NodeException e) { - disconnect(); - LOG.debug("Reader disconnected from node " + node + ": " + e.getMessage()); + } catch (InterruptedException | IOException | NodeException e) { + LOG.trace("Reader disconnected from node " + node + ": " + e.getMessage()); } catch (RuntimeException e) { - LOG.debug("Reader disconnecting from node " + node + " due to error: " + e.getMessage(), e); - disconnect(); + LOG.trace("Reader disconnecting from node " + node + " due to error: " + e.getMessage(), e); } finally { + disconnect(); try { socket.close(); } catch (Exception e) { @@ -438,14 +428,13 @@ public class Connection { initSocket(socket); while (state != DISCONNECTED) { if (sendingQueue.size() > 0) { - LOG.debug("Sending " + sendingQueue.size() + " messages to node " + node); send(sendingQueue.poll()); } else { Thread.sleep(100); } } } catch (IOException | InterruptedException e) { - LOG.debug("Writer disconnected from node " + node + ": " + e.getMessage()); + LOG.trace("Writer disconnected from node " + node + ": " + e.getMessage()); disconnect(); } } 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 7b49978..3944378 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -32,10 +32,7 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT; import static ch.dissem.bitmessage.networking.Connection.Mode.SERVER; @@ -58,7 +55,14 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { private ConcurrentMap<InventoryVector, Long> requestedObjects = new ConcurrentHashMap<>(); public DefaultNetworkHandler() { - pool = Executors.newCachedThreadPool(); + pool = Executors.newCachedThreadPool(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setPriority(Thread.MIN_PRIORITY); + return thread; + } + }); } @Override @@ -67,14 +71,12 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } @Override - public Thread synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds) { + public Future<?> synchronize(InetAddress trustedHost, int port, MessageListener listener, long timeoutInSeconds) { try { Connection connection = Connection.sync(ctx, trustedHost, port, listener, timeoutInSeconds); - Thread tr = new Thread(connection.getReader()); - Thread tw = new Thread(connection.getWriter()); - tr.start(); - tw.start(); - return tr; + Future<?> reader = pool.submit(connection.getReader()); + pool.execute(connection.getWriter()); + return reader; } catch (IOException e) { throw new RuntimeException(e); } @@ -143,8 +145,10 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { for (NetworkAddress address : addresses) { startConnection(new Connection(ctx, CLIENT, address, listener, requestedObjects)); } + Thread.sleep(10000); + } else { + Thread.sleep(30000); } - Thread.sleep(30000); } catch (InterruptedException e) { running = false; } catch (Exception e) { @@ -204,7 +208,6 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } } } - LOG.debug(target.size() + " connections available to offer " + iv); List<Connection> randomSubset = Collections.selectRandom(NETWORK_MAGIC_NUMBER, target); for (Connection connection : randomSubset) { connection.offer(iv); diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 74b3ff1..9fb2ea5 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -29,6 +29,7 @@ import org.junit.Test; import org.mockito.Mockito; import java.net.InetAddress; +import java.util.concurrent.Future; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; @@ -116,10 +117,10 @@ public class NetworkHandlerTest { "V1Msg.payload" ); - Thread t = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, + Future<?> future = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, mock(NetworkHandler.MessageListener.class), 10); - t.join(); + future.get(); assertInventorySize(3, nodeInventory); assertInventorySize(3, peerInventory); } @@ -133,10 +134,10 @@ public class NetworkHandlerTest { nodeInventory.init(); - Thread t = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, + Future<?> future = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, mock(NetworkHandler.MessageListener.class), 10); - t.join(); + future.get(); assertInventorySize(2, nodeInventory); assertInventorySize(2, peerInventory); } @@ -149,10 +150,10 @@ public class NetworkHandlerTest { "V1Msg.payload" ); - Thread t = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, + Future<?> future = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, mock(NetworkHandler.MessageListener.class), 10); - t.join(); + future.get(); assertInventorySize(1, nodeInventory); assertInventorySize(1, peerInventory); } From 99266712faeff0b392557512f73a751dd4fa7d7b Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 28 Nov 2015 20:27:05 +0100 Subject: [PATCH 27/42] Some extensions for server POW --- .../dissem/bitmessage/BitmessageContext.java | 18 ++- .../ch/dissem/bitmessage/InternalContext.java | 6 + .../bitmessage/entity/BitmessageAddress.java | 2 +- .../bitmessage/entity/CustomMessage.java | 68 +++++++++ .../bitmessage/entity/MessagePayload.java | 2 +- .../bitmessage/entity/payload/Broadcast.java | 1 - .../bitmessage/entity/payload/CryptoBox.java | 9 +- .../bitmessage/factory/V3MessageFactory.java | 6 + .../ports/CustomCommandHandler.java | 27 ++++ .../ch/dissem/bitmessage/utils/Encode.java | 16 +- extensions/build.gradle | 36 +++++ .../extensions/CryptoCustomMessage.java | 139 ++++++++++++++++++ .../extensions/pow/ProofOfWorkRequest.java | 86 +++++++++++ .../extensions/CryptoCustomMessageTest.java | 58 ++++++++ .../bitmessage/networking/Connection.java | 6 + .../bitmessage/repository/JdbcHelper.java | 2 +- settings.gradle | 2 + 17 files changed, 472 insertions(+), 12 deletions(-) create mode 100644 domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java create mode 100644 domain/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java create mode 100644 extensions/build.gradle create mode 100644 extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java create mode 100644 extensions/src/main/java/ch/dissem/bitmessage/extensions/pow/ProofOfWorkRequest.java create mode 100644 extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.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..9d4abd7 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -16,9 +16,7 @@ package ch.dissem.bitmessage; -import ch.dissem.bitmessage.entity.BitmessageAddress; -import ch.dissem.bitmessage.entity.ObjectMessage; -import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.*; import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.payload.Pubkey.Feature; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; @@ -297,6 +295,7 @@ public class BitmessageContext { ProofOfWorkEngine proofOfWorkEngine; Security security; MessageCallback messageCallback; + CustomCommandHandler customCommandHandler; Listener listener; int connectionLimit = 150; long connectionTTL = 12 * HOUR; @@ -344,6 +343,11 @@ public class BitmessageContext { return this; } + public Builder customCommandHandler(CustomCommandHandler handler) { + this.customCommandHandler = handler; + return this; + } + public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) { this.proofOfWorkEngine = proofOfWorkEngine; return this; @@ -392,6 +396,14 @@ public class BitmessageContext { } }; } + if (customCommandHandler == null) { + customCommandHandler = new CustomCommandHandler() { + @Override + public MessagePayload handle(CustomMessage request) { + throw new RuntimeException("Received custom request, but no custom command handler configured."); + } + }; + } return new BitmessageContext(this); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 7a89978..95cd8d8 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -50,6 +50,7 @@ public class InternalContext { private final MessageRepository messageRepository; private final ProofOfWorkEngine proofOfWorkEngine; private final MessageCallback messageCallback; + private final CustomCommandHandler customCommandHandler; private final TreeSet<Long> streams = new TreeSet<>(); private final int port; @@ -69,6 +70,7 @@ public class InternalContext { this.proofOfWorkEngine = builder.proofOfWorkEngine; this.clientNonce = security.randomNonce(); this.messageCallback = builder.messageCallback; + this.customCommandHandler = builder.customCommandHandler; this.port = builder.port; this.connectionLimit = builder.connectionLimit; this.connectionTTL = builder.connectionTTL; @@ -263,6 +265,10 @@ public class InternalContext { return connectionLimit; } + public CustomCommandHandler getCustomCommandHandler() { + return customCommandHandler; + } + public interface ContextHolder { void setContext(InternalContext context); } 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..931776c 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -87,7 +87,7 @@ public class BitmessageAddress implements Serializable { } } - BitmessageAddress(Pubkey publicKey) { + public BitmessageAddress(Pubkey publicKey) { this(publicKey.getVersion(), publicKey.getStream(), publicKey.getRipe()); this.pubkey = publicKey; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java new file mode 100644 index 0000000..b31c9f5 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 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.entity; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static ch.dissem.bitmessage.utils.Decode.bytes; + +/** + * @author Christian Basler + */ +public class CustomMessage implements MessagePayload { + private final byte[] data; + + public CustomMessage() { + this.data = null; + } + + public CustomMessage(byte[] data) { + this.data = data; + } + + public static MessagePayload read(InputStream in, int length) throws IOException { + return new CustomMessage(bytes(in, length)); + } + + @Override + public Command getCommand() { + return Command.CUSTOM; + } + + public byte[] getData() throws IOException { + if (data != null) { + return data; + } else { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write(out); + return out.toByteArray(); + } + } + + @Override + public void write(OutputStream out) throws IOException { + if (data != null) { + out.write(data); + } else { + throw new RuntimeException("Tried to write custom message without data. " + + "Programmer: did you forget to override #write()?"); + } + } +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java b/domain/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java index e6f6f0a..994952b 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java @@ -23,6 +23,6 @@ public interface MessagePayload extends Streamable { Command getCommand(); enum Command { - VERSION, VERACK, ADDR, INV, GETDATA, OBJECT + VERSION, VERACK, ADDR, INV, GETDATA, OBJECT, CUSTOM } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java index bf5ff7f..47bf539 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java @@ -21,7 +21,6 @@ import ch.dissem.bitmessage.entity.Encrypted; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.PlaintextHolder; import ch.dissem.bitmessage.exception.DecryptionFailedException; -import ch.dissem.bitmessage.ports.Security; import java.io.IOException; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java index a870b32..fe45ac5 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java @@ -38,7 +38,14 @@ public class CryptoBox implements Streamable { private final byte[] mac; private byte[] encrypted; + private long addressVersion; + + public CryptoBox(Streamable data, byte[] K) throws IOException { + this(Encode.bytes(data), K); + } + + public CryptoBox(byte[] data, byte[] K) throws IOException { curveType = 0x02CA; // 1. The destination public key is called K. @@ -58,7 +65,7 @@ public class CryptoBox implements Streamable { byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7. // 8. Encrypt the data with AES-256-CBC, using IV as initialization vector, key_e as encryption key and the padded input text as payload. Call the output cipher text. - encrypted = security().crypt(true, Encode.bytes(data), key_e, initializationVector); + encrypted = security().crypt(true, data, key_e, initializationVector); // 9. Calculate a 32 byte MAC with HMACSHA256, using key_m as salt and IV + R + cipher text as data. Call the output MAC. mac = calculateMac(key_m); diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java index 8dca6d2..9e15b3d 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java @@ -73,12 +73,18 @@ class V3MessageFactory { return parseGetData(stream); case "object": return readObject(stream, length); + case "custom": + return readCustom(stream, length); default: LOG.debug("Unknown command: " + command); return null; } } + private static MessagePayload readCustom(InputStream in, int length) throws IOException { + return CustomMessage.read(in, length); + } + public static ObjectMessage readObject(InputStream in, int length) throws IOException { AccessCounter counter = new AccessCounter(); byte nonce[] = Decode.bytes(in, 8, counter); diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java b/domain/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java new file mode 100644 index 0000000..8e49586 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java @@ -0,0 +1,27 @@ +/* + * Copyright 2015 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.ports; + +import ch.dissem.bitmessage.entity.CustomMessage; +import ch.dissem.bitmessage.entity.MessagePayload; + +/** + * @author Christian Basler + */ +public interface CustomCommandHandler { + MessagePayload handle(CustomMessage request); +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java index a78d03f..2cdc262 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java @@ -103,15 +103,23 @@ public class Encode { inc(counter, 8); } - public static void varString(String value, OutputStream stream) throws IOException { + public static void varString(String value, OutputStream out) throws IOException { byte[] bytes = value.getBytes("utf-8"); - // FIXME: technically, it says the length in characters, but I think this one might be correct + // Technically, it says the length in characters, but I think this one might be correct. + // It doesn't really matter, as only ASCII characters are being used. // see also Decode#varString() - varInt(bytes.length, stream); - stream.write(bytes); + varInt(bytes.length, out); + out.write(bytes); + } + + public static void varBytes(byte[] data, OutputStream out) throws IOException { + varInt(data.length, out); + out.write(data); } /** + * Serializes a {@link Streamable} object and returns the byte array. + * * @param streamable the object to be serialized * @return an array of bytes representing the given streamable object. * @throws IOException if an I/O error occurs. diff --git a/extensions/build.gradle b/extensions/build.gradle new file mode 100644 index 0000000..0ae9fd4 --- /dev/null +++ b/extensions/build.gradle @@ -0,0 +1,36 @@ +/* + * Copyright 2015 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. + */ + +uploadArchives { + repositories { + mavenDeployer { + pom.project { + name 'Jabit Extensions' + artifactId = 'jabit-extensions' + description 'Protocol extensions used for some extended features, e.g. server and mobile client.' + } + } + } +} + +dependencies { + compile project(':domain') + testCompile 'junit:junit:4.11' + testCompile 'org.slf4j:slf4j-simple:1.7.12' + testCompile 'org.mockito:mockito-core:1.10.19' + testCompile project(path: ':domain', configuration: 'testArtifacts') + testCompile project(':security-bc') +} diff --git a/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java b/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java new file mode 100644 index 0000000..5d82f4d --- /dev/null +++ b/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java @@ -0,0 +1,139 @@ +/* + * Copyright 2015 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.extensions; + +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.CustomMessage; +import ch.dissem.bitmessage.entity.Streamable; +import ch.dissem.bitmessage.entity.payload.CryptoBox; +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.exception.DecryptionFailedException; +import ch.dissem.bitmessage.factory.Factory; +import ch.dissem.bitmessage.utils.Encode; + +import java.io.*; + +import static ch.dissem.bitmessage.utils.Decode.*; +import static ch.dissem.bitmessage.utils.Singleton.security; + +/** + * A {@link CustomMessage} implementation that contains signed and encrypted data. + * + * @author Christian Basler + */ +public class CryptoCustomMessage<T extends Streamable> extends CustomMessage { + private final Reader<T> dataReader; + private CryptoBox container; + private BitmessageAddress sender; + private T data; + + public CryptoCustomMessage(T data) throws IOException { + this.data = data; + this.dataReader = null; + } + + private CryptoCustomMessage(CryptoBox container, Reader<T> dataReader) { + this.container = container; + this.dataReader = dataReader; + } + + public static <T extends Streamable> CryptoCustomMessage<T> read(byte[] data, Reader<T> dataReader) throws IOException { + CryptoBox cryptoBox = CryptoBox.read(new ByteArrayInputStream(data), data.length); + return new CryptoCustomMessage<>(cryptoBox, dataReader); + } + + public BitmessageAddress getSender() { + return sender; + } + + public void signAndEncrypt(BitmessageAddress identity, byte[] publicKey) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + Encode.varInt(identity.getVersion(), out); + Encode.varInt(identity.getStream(), out); + Encode.int32(identity.getPubkey().getBehaviorBitfield(), out); + out.write(identity.getPubkey().getSigningKey(), 1, 64); + out.write(identity.getPubkey().getEncryptionKey(), 1, 64); + if (identity.getVersion() >= 3) { + Encode.varInt(identity.getPubkey().getNonceTrialsPerByte(), out); + Encode.varInt(identity.getPubkey().getExtraBytes(), out); + } + + data.write(out); + Encode.varBytes(security().getSignature(out.toByteArray(), identity.getPrivateKey()), out); + container = new CryptoBox(out.toByteArray(), publicKey); + } + + public T decrypt(byte[] privateKey) throws IOException, DecryptionFailedException { + SignatureCheckingInputStream in = new SignatureCheckingInputStream(container.decrypt(privateKey)); + + long addressVersion = varInt(in); + long stream = varInt(in); + int behaviorBitfield = int32(in); + byte[] publicSigningKey = bytes(in, 64); + byte[] publicEncryptionKey = bytes(in, 64); + long nonceTrialsPerByte = addressVersion >= 3 ? varInt(in) : 0; + long extraBytes = addressVersion >= 3 ? varInt(in) : 0; + + sender = new BitmessageAddress(Factory.createPubkey( + addressVersion, + stream, + publicSigningKey, + publicEncryptionKey, + nonceTrialsPerByte, + extraBytes, + behaviorBitfield + )); + + data = dataReader.read(sender, in); + + in.checkSignature(sender.getPubkey()); + + return data; + } + + @Override + public void write(OutputStream out) throws IOException { + container.write(out); + } + + public interface Reader<T> { + T read(BitmessageAddress sender, InputStream in) throws IOException; + } + + private class SignatureCheckingInputStream extends InputStream { + private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + private final InputStream wrapped; + + private SignatureCheckingInputStream(InputStream wrapped) { + this.wrapped = wrapped; + } + + @Override + public int read() throws IOException { + int read = wrapped.read(); + if (read >= 0) out.write(read); + return read; + } + + public void checkSignature(Pubkey pubkey) throws IOException, RuntimeException { + if (!security().isSignatureValid(out.toByteArray(), varBytes(wrapped), pubkey)) { + throw new RuntimeException("Signature check failed"); + } + } + } +} 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 new file mode 100644 index 0000000..b247b59 --- /dev/null +++ b/extensions/src/main/java/ch/dissem/bitmessage/extensions/pow/ProofOfWorkRequest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2015 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.extensions.pow; + +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Streamable; +import ch.dissem.bitmessage.utils.Encode; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static ch.dissem.bitmessage.utils.Decode.*; + +/** + * @author Christian Basler + */ +public class ProofOfWorkRequest implements Streamable { + private final BitmessageAddress sender; + private final byte[] initialHash; + private final Request request; + private final byte[] data; + + private ProofOfWorkRequest(BitmessageAddress sender, byte[] initialHash, Request request, byte[] data) { + this.sender = sender; + this.initialHash = initialHash; + this.request = request; + this.data = data; + } + + public static ProofOfWorkRequest read(BitmessageAddress client, InputStream in) throws IOException { + return new ProofOfWorkRequest( + client, + bytes(in, 64), + Request.valueOf(varString(in)), + varBytes(in) + ); + } + + public BitmessageAddress getSender() { + return sender; + } + + public byte[] getInitialHash() { + return initialHash; + } + + public Request getRequest() { + return request; + } + + public byte[] getData() { + return data; + } + + @Override + public void write(OutputStream out) throws IOException { + out.write(initialHash); + Encode.varString(request.name(), out); + Encode.varBytes(data, out); + } + + public enum Request { + CALCULATE, + QUERY, + ERROR, + OK, + QUEUED, + CALCULATING, + COMPLETE + } +} diff --git a/extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.java b/extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.java new file mode 100644 index 0000000..98e97a1 --- /dev/null +++ b/extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 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.extensions; + +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.payload.GenericPayload; +import ch.dissem.bitmessage.entity.valueobject.PrivateKey; +import ch.dissem.bitmessage.utils.TestBase; +import ch.dissem.bitmessage.utils.TestUtils; +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import static ch.dissem.bitmessage.utils.Singleton.security; +import static org.junit.Assert.assertEquals; + +public class CryptoCustomMessageTest extends TestBase { + @Test + public void testEncryptThenDecrypt() throws Exception { + PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey")); + BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey); + + GenericPayload payloadBefore = new GenericPayload(0, 1, security().randomBytes(100)); + CryptoCustomMessage<GenericPayload> messageBefore = new CryptoCustomMessage<>(payloadBefore); + messageBefore.signAndEncrypt(sendingIdentity, security().createPublicKey(sendingIdentity.getPublicDecryptionKey())); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + messageBefore.write(out); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + + CryptoCustomMessage<GenericPayload> messageAfter = CryptoCustomMessage.read(out.toByteArray(), new CryptoCustomMessage.Reader<GenericPayload>() { + @Override + public GenericPayload read(BitmessageAddress ignore, InputStream in) throws IOException { + return GenericPayload.read(0, in, 1, 100); + } + }); + GenericPayload payloadAfter = messageAfter.decrypt(sendingIdentity.getPublicDecryptionKey()); + + assertEquals(payloadBefore, payloadAfter); + } +} 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 8ed2fb4..a95adba 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -259,6 +259,12 @@ public class Connection { LOG.debug("Received " + addr.getAddresses().size() + " addresses."); 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"); diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java index 601ce29..d583a71 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcHelper.java @@ -31,7 +31,7 @@ import static ch.dissem.bitmessage.utils.Strings.hex; /** * Helper class that does Flyway migration, provides JDBC connections and some helper methods. */ -abstract class JdbcHelper { +public abstract class JdbcHelper { private static final Logger LOG = LoggerFactory.getLogger(JdbcHelper.class); protected final JdbcConfig config; diff --git a/settings.gradle b/settings.gradle index 2d3e1f6..adecd45 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,3 +13,5 @@ include 'wif' include 'security-sc' include 'security-bc' + +include 'extensions' \ No newline at end of file From 991a0e5f869a6c6087901ff405175e2039e69457 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Wed, 2 Dec 2015 17:45:50 +0100 Subject: [PATCH 28/42] Some improvements for custom message handling --- .../bitmessage/entity/CustomMessage.java | 13 ++++++--- .../bitmessage/ports/NetworkHandler.java | 14 ++++++++- .../extensions/pow/ProofOfWorkRequest.java | 12 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- .../bitmessage/networking/Connection.java | 12 ++++---- .../networking/DefaultNetworkHandler.java | 29 +++++++++++++++++-- .../bitmessage/wif/WifExporterTest.java | 16 +++++----- 7 files changed, 72 insertions(+), 26 deletions(-) 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. * </p> */ - 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()); From ab6a3c56dd2a72d3cfc28bb5aeea3a573e7ab183 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Tue, 8 Dec 2015 20:27:32 +0100 Subject: [PATCH 29/42] The POW callback is now a service and its state stored. The proof of work engine therefore just has to remember its initial hash making server based POW easier. --- .../dissem/bitmessage/demo/Application.java | 6 +- .../java/ch/dissem/bitmessage/demo/Main.java | 1 + .../dissem/bitmessage/BitmessageContext.java | 32 +++++---- .../bitmessage/DefaultMessageListener.java | 16 ++--- .../ch/dissem/bitmessage/InternalContext.java | 69 +++++-------------- .../dissem/bitmessage/ProofOfWorkService.java | 62 +++++++++++++++++ .../bitmessage/entity/CustomMessage.java | 23 +++++-- .../bitmessage/entity/ObjectMessage.java | 6 +- .../dissem/bitmessage/entity/Plaintext.java | 9 +++ .../bitmessage/ports/AbstractSecurity.java | 9 ++- .../bitmessage/ports/MessageRepository.java | 2 + .../ports/MultiThreadedPOWEngine.java | 6 +- .../bitmessage/ports/ProofOfWorkEngine.java | 2 +- .../ports/ProofOfWorkRepository.java | 16 +++++ .../ch/dissem/bitmessage/ports/Security.java | 2 + .../bitmessage/ports/SimplePOWEngine.java | 2 +- .../ch/dissem/bitmessage/utils/Decode.java | 8 ++- .../ch/dissem/bitmessage/utils/Numbers.java | 10 +++ .../ports/ProofOfWorkEngineTest.java | 4 +- .../extensions/CryptoCustomMessage.java | 3 + .../repository/JdbcMessageRepository.java | 26 +++++-- .../repository/JdbcProofOfWorkRepository.java | 69 +++++++++++++++++++ .../migration/V2.0__Update_table_message.sql | 2 + .../db/migration/V2.1__Create_table_POW.sql | 5 ++ .../bitmessage/security/SecurityTest.java | 2 +- 25 files changed, 289 insertions(+), 103 deletions(-) create mode 100644 domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java create mode 100644 domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java create mode 100644 domain/src/main/java/ch/dissem/bitmessage/utils/Numbers.java create mode 100644 repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java create mode 100644 repositories/src/main/resources/db/migration/V2.0__Update_table_message.sql create mode 100644 repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql 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 a065de9..72da50d 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -22,10 +22,7 @@ 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.JdbcAddressRepository; -import ch.dissem.bitmessage.repository.JdbcConfig; -import ch.dissem.bitmessage.repository.JdbcInventory; -import ch.dissem.bitmessage.repository.JdbcMessageRepository; +import ch.dissem.bitmessage.repository.*; import ch.dissem.bitmessage.security.bc.BouncySecurity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,6 +47,7 @@ public class Application { .inventory(new JdbcInventory(jdbcConfig)) .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) + .powRepo(new JdbcProofOfWorkRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) .security(new BouncySecurity()) .port(48444) 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 ac90e88..6dbfc14 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -51,6 +51,7 @@ public class Main { .inventory(new JdbcInventory(jdbcConfig)) .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) + .powRepo(new JdbcProofOfWorkRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) .security(new BouncySecurity()) .port(48444) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 9d4abd7..def0a4f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -75,7 +75,7 @@ public class BitmessageContext { } public AddressRepository addresses() { - return ctx.getAddressRepo(); + return ctx.getAddressRepository(); } public MessageRepository messages() { @@ -90,7 +90,7 @@ public class BitmessageContext { ctx.getNetworkExtraBytes(), features )); - ctx.getAddressRepo().save(identity); + ctx.getAddressRepository().save(identity); pool.submit(new Runnable() { @Override public void run() { @@ -102,6 +102,7 @@ public class BitmessageContext { public void addDistributedMailingList(String address, String alias) { // TODO + throw new RuntimeException("not implemented"); } public void broadcast(final BitmessageAddress from, final String subject, final String message) { @@ -120,9 +121,7 @@ public class BitmessageContext { from, from, Factory.getBroadcast(from, msg), - +2 * DAY, - 0, - 0 + +2 * DAY ); msg.setStatus(SENT); msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.BROADCAST, Label.Type.SENT)); @@ -159,9 +158,7 @@ public class BitmessageContext { from, to, new Msg(msg), - +2 * DAY, - ctx.getNonceTrialsPerByte(to), - ctx.getExtraBytes(to) + +2 * DAY ); msg.setStatus(SENT); msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.SENT)); @@ -176,9 +173,7 @@ public class BitmessageContext { requestingIdentity, address, new GetPubkey(address), - +28 * DAY, - ctx.getNetworkNonceTrialsPerByte(), - ctx.getNetworkExtraBytes() + +28 * DAY ); } @@ -220,7 +215,7 @@ public class BitmessageContext { } public void addContact(BitmessageAddress contact) { - ctx.getAddressRepo().save(contact); + ctx.getAddressRepository().save(contact); tryToFindMatchingPubkey(contact); if (contact.getPubkey() == null) { ctx.requestPubkey(contact); @@ -237,7 +232,7 @@ public class BitmessageContext { v4Pubkey.decrypt(address.getPublicDecryptionKey()); if (object.isSignatureValid(v4Pubkey)) { address.setPubkey(v4Pubkey); - ctx.getAddressRepo().save(address); + ctx.getAddressRepository().save(address); break; } else { LOG.info("Found pubkey for " + address + " but signature is invalid"); @@ -246,7 +241,7 @@ public class BitmessageContext { } else { if (Arrays.equals(pubkey.getRipe(), address.getRipe())) { address.setPubkey(pubkey); - ctx.getAddressRepo().save(address); + ctx.getAddressRepository().save(address); break; } } @@ -258,7 +253,7 @@ public class BitmessageContext { public void addSubscribtion(BitmessageAddress address) { address.setSubscribed(true); - ctx.getAddressRepo().save(address); + ctx.getAddressRepository().save(address); tryToFindBroadcastsForAddress(address); } @@ -292,6 +287,7 @@ public class BitmessageContext { NetworkHandler networkHandler; AddressRepository addressRepo; MessageRepository messageRepo; + ProofOfWorkRepository proofOfWorkRepository; ProofOfWorkEngine proofOfWorkEngine; Security security; MessageCallback messageCallback; @@ -333,6 +329,11 @@ public class BitmessageContext { return this; } + public Builder powRepo(ProofOfWorkRepository proofOfWorkRepository) { + this.proofOfWorkRepository = proofOfWorkRepository; + return this; + } + public Builder security(Security security) { this.security = security; return this; @@ -374,6 +375,7 @@ public class BitmessageContext { nonNull("networkHandler", networkHandler); nonNull("addressRepo", addressRepo); nonNull("messageRepo", messageRepo); + nonNull("proofOfWorkRepo", proofOfWorkRepository); if (proofOfWorkEngine == null) { proofOfWorkEngine = new MultiThreadedPOWEngine(); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java index e069704..eb22f03 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java +++ b/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java @@ -69,7 +69,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { } protected void receive(ObjectMessage object, GetPubkey getPubkey) { - BitmessageAddress identity = ctx.getAddressRepo().findIdentity(getPubkey.getRipeTag()); + BitmessageAddress identity = ctx.getAddressRepository().findIdentity(getPubkey.getRipeTag()); if (identity != null && identity.getPrivateKey() != null) { LOG.info("Got pubkey request for identity " + identity); // FIXME: only send pubkey if it wasn't sent in the last 28 days @@ -82,17 +82,17 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { try { if (pubkey instanceof V4Pubkey) { V4Pubkey v4Pubkey = (V4Pubkey) pubkey; - address = ctx.getAddressRepo().findContact(v4Pubkey.getTag()); + address = ctx.getAddressRepository().findContact(v4Pubkey.getTag()); if (address != null) { v4Pubkey.decrypt(address.getPublicDecryptionKey()); } } else { - address = ctx.getAddressRepo().findContact(pubkey.getRipe()); + address = ctx.getAddressRepository().findContact(pubkey.getRipe()); } if (address != null) { address.setPubkey(pubkey); LOG.info("Got pubkey for contact " + address); - ctx.getAddressRepo().save(address); + ctx.getAddressRepository().save(address); List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address); LOG.info("Sending " + messages.size() + " messages for contact " + address); for (Plaintext msg : messages) { @@ -102,9 +102,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { msg.getFrom(), msg.getTo(), new Msg(msg), - +2 * DAY, - ctx.getNonceTrialsPerByte(msg.getTo()), - ctx.getExtraBytes(msg.getTo()) + +2 * DAY ); msg.setStatus(SENT); ctx.getMessageRepository().save(msg); @@ -115,7 +113,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { } protected void receive(ObjectMessage object, Msg msg) throws IOException { - for (BitmessageAddress identity : ctx.getAddressRepo().getIdentities()) { + for (BitmessageAddress identity : ctx.getAddressRepository().getIdentities()) { try { msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey()); msg.getPlaintext().setTo(identity); @@ -136,7 +134,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { protected void receive(ObjectMessage object, Broadcast broadcast) throws IOException { byte[] tag = broadcast instanceof V5Broadcast ? ((V5Broadcast) broadcast).getTag() : null; - for (BitmessageAddress subscription : ctx.getAddressRepo().getSubscriptions(broadcast.getVersion())) { + for (BitmessageAddress subscription : ctx.getAddressRepository().getSubscriptions(broadcast.getVersion())) { if (tag != null && !Arrays.equals(tag, subscription.getTag())) { continue; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 95cd8d8..d139de5 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -48,9 +48,11 @@ public class InternalContext { private final NetworkHandler networkHandler; private final AddressRepository addressRepository; private final MessageRepository messageRepository; + private final ProofOfWorkRepository proofOfWorkRepository; private final ProofOfWorkEngine proofOfWorkEngine; private final MessageCallback messageCallback; private final CustomCommandHandler customCommandHandler; + private final ProofOfWorkService proofOfWorkService; private final TreeSet<Long> streams = new TreeSet<>(); private final int port; @@ -67,6 +69,8 @@ public class InternalContext { this.networkHandler = builder.networkHandler; this.addressRepository = builder.addressRepo; this.messageRepository = builder.messageRepo; + this.proofOfWorkRepository = builder.proofOfWorkRepository; + this.proofOfWorkService = new ProofOfWorkService(); this.proofOfWorkEngine = builder.proofOfWorkEngine; this.clientNonce = security.randomNonce(); this.messageCallback = builder.messageCallback; @@ -88,7 +92,9 @@ public class InternalContext { streams.add(1L); } - init(security, inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); + init(security, inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, + proofOfWorkRepository, proofOfWorkService, proofOfWorkEngine, + messageCallback, customCommandHandler); for (BitmessageAddress identity : addressRepository.getIdentities()) { streams.add(identity.getStream()); } @@ -118,7 +124,7 @@ public class InternalContext { return networkHandler; } - public AddressRepository getAddressRepo() { + public AddressRepository getAddressRepository() { return addressRepository; } @@ -126,6 +132,10 @@ public class InternalContext { return messageRepository; } + public ProofOfWorkRepository getProofOfWorkRepository() { + return proofOfWorkRepository; + } + public ProofOfWorkEngine getProofOfWorkEngine() { return proofOfWorkEngine; } @@ -147,22 +157,12 @@ public class InternalContext { return networkNonceTrialsPerByte; } - public long getNonceTrialsPerByte(BitmessageAddress address) { - long nonceTrialsPerByte = address.getPubkey().getNonceTrialsPerByte(); - return networkNonceTrialsPerByte > nonceTrialsPerByte ? networkNonceTrialsPerByte : nonceTrialsPerByte; - } - public long getNetworkExtraBytes() { return networkExtraBytes; } - public long getExtraBytes(BitmessageAddress address) { - long extraBytes = address.getPubkey().getExtraBytes(); - return networkExtraBytes > extraBytes ? networkExtraBytes : extraBytes; - } - public void send(final BitmessageAddress from, BitmessageAddress to, final ObjectPayload payload, - final long timeToLive, final long nonceTrialsPerByte, final long extraBytes) { + final long timeToLive) { try { if (to == null) to = from; long expires = UnixTime.now(+timeToLive); @@ -181,22 +181,7 @@ public class InternalContext { object.encrypt(to.getPubkey()); } 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()); - } - }); + proofOfWorkService.doProofOfWork(to, object); } catch (IOException e) { throw new RuntimeException(e); } @@ -214,18 +199,8 @@ public class InternalContext { response.sign(identity.getPrivateKey()); response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); messageCallback.proofOfWorkStarted(identity.getPubkey()); - security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes, - new ProofOfWorkEngine.Callback() { - @Override - public void onNonceCalculated(byte[] nonce) { - response.setNonce(nonce); - messageCallback.proofOfWorkCompleted(identity.getPubkey()); - inventory.storeObject(response); - networkHandler.offer(response.getInventoryVector()); - // TODO: save that the pubkey was just sent, and on which stream! - messageCallback.messageOffered(identity.getPubkey(), response.getInventoryVector()); - } - }); + // TODO: remember that the pubkey is just about to be sent, and on which stream! + proofOfWorkService.doProofOfWork(response); } catch (IOException e) { throw new RuntimeException(e); } @@ -240,17 +215,7 @@ public class InternalContext { .payload(new GetPubkey(contact)) .build(); messageCallback.proofOfWorkStarted(response.getPayload()); - security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes, - new ProofOfWorkEngine.Callback() { - @Override - public void onNonceCalculated(byte[] nonce) { - response.setNonce(nonce); - messageCallback.proofOfWorkCompleted(response.getPayload()); - inventory.storeObject(response); - networkHandler.offer(response.getInventoryVector()); - messageCallback.messageOffered(response.getPayload(), response.getInventoryVector()); - } - }); + proofOfWorkService.doProofOfWork(response); } public long getClientNonce() { diff --git a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java new file mode 100644 index 0000000..3cf46ef --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java @@ -0,0 +1,62 @@ +package ch.dissem.bitmessage; + +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.PlaintextHolder; +import ch.dissem.bitmessage.ports.MessageRepository; +import ch.dissem.bitmessage.ports.ProofOfWorkEngine; +import ch.dissem.bitmessage.ports.ProofOfWorkRepository; +import ch.dissem.bitmessage.ports.Security; + +import static ch.dissem.bitmessage.utils.Singleton.security; + +/** + * @author Christian Basler + */ +public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalContext.ContextHolder { + private Security security; + private InternalContext ctx; + private ProofOfWorkRepository powRepo; + private MessageRepository messageRepo; + + public void doProofOfWork(ObjectMessage object) { + doProofOfWork(null, object); + } + + public void doProofOfWork(BitmessageAddress recipient, ObjectMessage object) { + long nonceTrialsPerByte = recipient == null ? 0 : recipient.getPubkey().getNonceTrialsPerByte(); + long extraBytes = recipient == null ? 0 : recipient.getPubkey().getExtraBytes(); + + powRepo.putObject(object, nonceTrialsPerByte, extraBytes); + if (object.getPayload() instanceof PlaintextHolder){ + Plaintext plaintext = ((PlaintextHolder) object.getPayload()).getPlaintext(); + plaintext.setInitialHash(security.getInitialHash(object)); + messageRepo.save(plaintext); + } + security.doProofOfWork(object, nonceTrialsPerByte, extraBytes, this); + } + + @Override + public void onNonceCalculated(byte[] initialHash, byte[] nonce) { + ObjectMessage object = powRepo.getObject(initialHash); + object.setNonce(nonce); +// messageCallback.proofOfWorkCompleted(payload); + Plaintext plaintext = messageRepo.getMessage(initialHash); + if (plaintext != null) { + plaintext.setInventoryVector(object.getInventoryVector()); + messageRepo.save(plaintext); + } + ctx.getInventory().storeObject(object); + ctx.getNetworkHandler().offer(object.getInventoryVector()); +// messageCallback.messageOffered(payload, object.getInventoryVector()); + } + + @Override + public void setContext(InternalContext ctx) { + this.ctx = ctx; + this.security = security(); + this.powRepo = ctx.getProofOfWorkRepository(); + this.messageRepo = ctx.getMessageRepository(); + } +} 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 63f9663..a5caf3a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java @@ -16,26 +16,36 @@ package ch.dissem.bitmessage.entity; +import ch.dissem.bitmessage.utils.AccessCounter; +import ch.dissem.bitmessage.utils.Encode; + import java.io.*; import static ch.dissem.bitmessage.utils.Decode.bytes; +import static ch.dissem.bitmessage.utils.Decode.varString; /** * @author Christian Basler */ public class CustomMessage implements MessagePayload { + public static final String COMMAND_ERROR = "ERROR"; + + private final String command; private final byte[] data; - public CustomMessage() { + public CustomMessage(String command) { + this.command = command; this.data = null; } - public CustomMessage(byte[] data) { + public CustomMessage(String command, byte[] data) { + this.command = command; this.data = data; } public static MessagePayload read(InputStream in, int length) throws IOException { - return new CustomMessage(bytes(in, length)); + AccessCounter counter = new AccessCounter(); + return new CustomMessage(varString(in, counter), bytes(in, length - counter.length())); } @Override @@ -56,6 +66,7 @@ public class CustomMessage implements MessagePayload { @Override public void write(OutputStream out) throws IOException { if (data != null) { + Encode.varString(command, out); out.write(data); } else { throw new RuntimeException("Tried to write custom message without data. " + @@ -63,9 +74,13 @@ public class CustomMessage implements MessagePayload { } } + public boolean isError() { + return COMMAND_ERROR.equals(command); + } + public static CustomMessage error(String message) { try { - return new CustomMessage(("ERROR\n" + message).getBytes("UTF-8")); + return new CustomMessage(COMMAND_ERROR, message.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java index 128084e..9e89c42 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java @@ -156,7 +156,11 @@ public class ObjectMessage implements MessagePayload { @Override public void write(OutputStream out) throws IOException { - out.write(nonce); + if (nonce != null) { + out.write(nonce); + } else { + out.write(new byte[8]); + } out.write(getPayloadBytesWithoutNonce()); } 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..fbd5d48 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -44,6 +44,7 @@ public class Plaintext implements Streamable { private Long received; private Set<Label> labels; + private byte[] initialHash; private Plaintext(Builder builder) { id = builder.id; @@ -260,6 +261,14 @@ public class Plaintext implements Streamable { } } + public void setInitialHash(byte[] initialHash) { + this.initialHash = initialHash; + } + + public byte[] getInitialHash() { + return initialHash; + } + public enum Encoding { IGNORE(0), TRIVIAL(1), SIMPLE(2); diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java index bd55180..0dea04c 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java @@ -34,6 +34,8 @@ import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.security.SecureRandom; +import static ch.dissem.bitmessage.utils.Numbers.max; + /** * Implements everything that isn't directly dependent on either Spongy- or Bouncycastle. */ @@ -95,8 +97,8 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes, ProofOfWorkEngine.Callback callback) { try { - if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; - if (extraBytes < 1000) extraBytes = 1000; + nonceTrialsPerByte = max(nonceTrialsPerByte, context.getNetworkNonceTrialsPerByte()); + extraBytes = max(extraBytes, context.getNetworkExtraBytes()); byte[] initialHash = getInitialHash(object); @@ -117,7 +119,8 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont } } - private byte[] getInitialHash(ObjectMessage object) throws IOException { + @Override + public byte[] getInitialHash(ObjectMessage object) { return sha512(object.getPayloadBytesWithoutNonce()); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java index af7b2bc..9e949a7 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java @@ -30,6 +30,8 @@ public interface MessageRepository { int countUnread(Label label); + Plaintext getMessage(byte[] initialHash); + List<Plaintext> findMessages(Label label); List<Plaintext> findMessages(Status status); diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java index ac65d3d..5e00e33 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java @@ -102,7 +102,7 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { synchronized (callback) { if (!Thread.interrupted()) { try { - callback.onNonceCalculated(nonce); + callback.onNonceCalculated(initialHash, nonce); } finally { semaphore.release(); for (Worker w : workers) { @@ -128,12 +128,12 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { } @Override - public void onNonceCalculated(byte[] nonce) { + public void onNonceCalculated(byte[] initialHash, byte[] nonce) { synchronized (this) { if (waiting) { LOG.info("Nonce calculated in " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds"); waiting = false; - callback.onNonceCalculated(nonce); + callback.onNonceCalculated(initialHash, nonce); } } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java index 90513dc..fc7b4c2 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java @@ -35,6 +35,6 @@ public interface ProofOfWorkEngine { /** * @param nonce 8 bytes nonce */ - void onNonceCalculated(byte[] nonce); + void onNonceCalculated(byte[] initialHash, byte[] nonce); } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java new file mode 100644 index 0000000..9971ad5 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java @@ -0,0 +1,16 @@ +package ch.dissem.bitmessage.ports; + +import ch.dissem.bitmessage.entity.ObjectMessage; + +/** + * Objects that proof of work is currently being done for. + * + * @author Christian Basler + */ +public interface ProofOfWorkRepository { + ObjectMessage getObject(byte[] initialHash); + + void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes); + + void removeObject(ObjectMessage object); +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java index c5fcb8f..8fc7e20 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java @@ -134,6 +134,8 @@ public interface Security { void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException; + byte[] getInitialHash(ObjectMessage object); + /** * Calculates the MAC for a message (data) * diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java index 25d51aa..06d234b 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java @@ -40,6 +40,6 @@ public class SimplePOWEngine implements ProofOfWorkEngine { mda.update(nonce); mda.update(initialHash); } while (Bytes.lt(target, mda.digest(mda.digest()), 8)); - callback.onNonceCalculated(nonce); + callback.onNonceCalculated(initialHash, nonce); } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java index b539aa9..47b0ee3 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java @@ -130,9 +130,13 @@ public class Decode { } public static String varString(InputStream stream) throws IOException { - int length = (int) varInt(stream); + return varString(stream, null); + } + + public static String varString(InputStream stream, AccessCounter counter) throws IOException { + int length = (int) varInt(stream, counter); // FIXME: technically, it says the length in characters, but I think this one might be correct // otherwise it will get complicated, as we'll need to read UTF-8 char by char... - return new String(bytes(stream, length), "utf-8"); + return new String(bytes(stream, length, counter), "utf-8"); } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Numbers.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Numbers.java new file mode 100644 index 0000000..b1ace02 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Numbers.java @@ -0,0 +1,10 @@ +package ch.dissem.bitmessage.utils; + +/** + * Created by chrig on 07.12.2015. + */ +public class Numbers { + public static long max(long a, long b) { + return a > b ? a : b; + } +} diff --git a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java index ba5307d..1ed4aac 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java @@ -43,7 +43,7 @@ public class ProofOfWorkEngineTest extends TestBase { engine.calculateNonce(initialHash, target, new ProofOfWorkEngine.Callback() { @Override - public void onNonceCalculated(byte[] nonce) { + public void onNonceCalculated(byte[] initialHash, byte[] nonce) { waiter1.setValue(nonce); } }); @@ -59,7 +59,7 @@ public class ProofOfWorkEngineTest extends TestBase { engine.calculateNonce(initialHash2, target2, new ProofOfWorkEngine.Callback() { @Override - public void onNonceCalculated(byte[] nonce) { + public void onNonceCalculated(byte[] initialHash, byte[] nonce) { waiter2.setValue(nonce); } }); diff --git a/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java b/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java index 5d82f4d..9a9e2dc 100644 --- a/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java +++ b/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java @@ -36,17 +36,20 @@ import static ch.dissem.bitmessage.utils.Singleton.security; * @author Christian Basler */ public class CryptoCustomMessage<T extends Streamable> extends CustomMessage { + public static final String COMMAND = "ENCRYPTED"; private final Reader<T> dataReader; private CryptoBox container; private BitmessageAddress sender; private T data; public CryptoCustomMessage(T data) throws IOException { + super(COMMAND); this.data = data; this.dataReader = null; } private CryptoCustomMessage(CryptoBox container, Reader<T> dataReader) { + super(COMMAND); this.container = container; this.dataReader = dataReader; } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index 4599b79..48b8df5 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -22,6 +22,7 @@ import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.MessageRepository; +import ch.dissem.bitmessage.utils.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -108,6 +109,20 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito return 0; } + @Override + public Plaintext getMessage(byte[] initialHash) { + List<Plaintext> plaintexts = find("initial_hash=X'" + Strings.hex(initialHash) + "'"); + switch (plaintexts.size()) { + case 0: + return null; + case 1: + return plaintexts.get(0); + default: + throw new RuntimeException("This shouldn't happen, found " + plaintexts.size() + + " messages, one or none was expected"); + } + } + @Override public List<Plaintext> findMessages(Label label) { return find("id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ")"); @@ -141,8 +156,8 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito long id = rs.getLong("id"); builder.id(id); builder.IV(new InventoryVector(iv)); - builder.from(ctx.getAddressRepo().getAddress(rs.getString("sender"))); - builder.to(ctx.getAddressRepo().getAddress(rs.getString("recipient"))); + builder.from(ctx.getAddressRepository().getAddress(rs.getString("sender"))); + builder.to(ctx.getAddressRepository().getAddress(rs.getString("recipient"))); builder.sent(rs.getLong("sent")); builder.received(rs.getLong("received")); builder.status(Plaintext.Status.valueOf(rs.getString("status"))); @@ -173,12 +188,12 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito public void save(Plaintext message) { // save from address if necessary if (message.getId() == null) { - BitmessageAddress savedAddress = ctx.getAddressRepo().getAddress(message.getFrom().getAddress()); + BitmessageAddress savedAddress = ctx.getAddressRepository().getAddress(message.getFrom().getAddress()); if (savedAddress == null || savedAddress.getPrivateKey() == null) { if (savedAddress != null && savedAddress.getAlias() != null) { message.getFrom().setAlias(savedAddress.getAlias()); } - ctx.getAddressRepo().save(message.getFrom()); + ctx.getAddressRepository().save(message.getFrom()); } } @@ -219,7 +234,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito private void insert(Connection connection, Plaintext message) throws SQLException, IOException { PreparedStatement ps = connection.prepareStatement( - "INSERT INTO Message (iv, type, sender, recipient, data, sent, received, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO Message (iv, type, sender, recipient, data, sent, received, status, initial_hash) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); ps.setBytes(1, message.getInventoryVector() != null ? message.getInventoryVector().getHash() : null); ps.setString(2, message.getType().name()); @@ -229,6 +244,7 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito ps.setLong(6, message.getSent()); ps.setLong(7, message.getReceived()); ps.setString(8, message.getStatus() != null ? message.getStatus().name() : null); + ps.setBytes(9, message.getInitialHash()); ps.executeUpdate(); diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java new file mode 100644 index 0000000..aa8ca23 --- /dev/null +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java @@ -0,0 +1,69 @@ +package ch.dissem.bitmessage.repository; + +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.factory.Factory; +import ch.dissem.bitmessage.ports.ProofOfWorkRepository; +import ch.dissem.bitmessage.utils.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.*; + +import static ch.dissem.bitmessage.utils.Singleton.security; + +/** + * @author Christian Basler + */ +public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWorkRepository { + private static final Logger LOG = LoggerFactory.getLogger(JdbcProofOfWorkRepository.class); + + public JdbcProofOfWorkRepository(JdbcConfig config) { + super(config); + } + + @Override + public ObjectMessage getObject(byte[] initialHash) { + try (Connection connection = config.getConnection()) { + PreparedStatement ps = connection.prepareStatement("SELECT data, version FROM POW WHERE initial_hash=?"); + ps.setBytes(1, initialHash); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Blob data = rs.getBlob("data"); + return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()); + } else { + throw new RuntimeException("Object requested that we don't have. Initial hash: " + Strings.hex(initialHash)); + } + } catch (Exception e) { + LOG.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + + @Override + public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) { + try (Connection connection = config.getConnection()) { + PreparedStatement ps = connection.prepareStatement("INSERT INTO POW (initial_hash, data, version) VALUES (?, ?, ?)"); + ps.setBytes(1, security().getInitialHash(object)); + writeBlob(ps, 2, object); + ps.setLong(3, object.getVersion()); + ps.executeUpdate(); + } catch (SQLException e) { + LOG.debug("Error storing object of type " + object.getPayload().getClass().getSimpleName(), e); + throw new RuntimeException(e); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + + @Override + public void removeObject(ObjectMessage object) { + try (Connection connection = config.getConnection()) { + PreparedStatement ps = connection.prepareStatement("DELETE FROM POW WHERE initial_hash=?"); + ps.setBytes(1, security().getInitialHash(object)); + ps.executeUpdate(); + } catch (SQLException e) { + LOG.debug(e.getMessage(), e); + } + } +} diff --git a/repositories/src/main/resources/db/migration/V2.0__Update_table_message.sql b/repositories/src/main/resources/db/migration/V2.0__Update_table_message.sql new file mode 100644 index 0000000..0d81858 --- /dev/null +++ b/repositories/src/main/resources/db/migration/V2.0__Update_table_message.sql @@ -0,0 +1,2 @@ +ALTER TABLE Message ADD COLUMN initial_hash BINARY(64); +ALTER TABLE Message ADD CONSTRAINT initial_hash_unique UNIQUE(initial_hash); \ No newline at end of file diff --git a/repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql b/repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql new file mode 100644 index 0000000..4f54698 --- /dev/null +++ b/repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql @@ -0,0 +1,5 @@ +CREATE TABLE POW ( + initial_hash BINARY(64) PRIMARY KEY, + data BLOB NOT NULL, + version BIGINT NOT NULL +); diff --git a/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java b/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java index 46a8ae6..3aef7a8 100644 --- a/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java +++ b/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java @@ -91,7 +91,7 @@ public class SecurityTest { security.doProofOfWork(objectMessage, 1000, 1000, new ProofOfWorkEngine.Callback() { @Override - public void onNonceCalculated(byte[] nonce) { + public void onNonceCalculated(byte[] initialHash, byte[] nonce) { waiter.setValue(nonce); } }); From 51bf3b8bd2991b3455e36412995d3bdfc8e7904d Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 12 Dec 2015 11:05:13 +0100 Subject: [PATCH 30/42] Fixed tests --- .../ch/dissem/bitmessage/networking/NetworkHandlerTest.java | 3 +++ .../test/java/ch/dissem/bitmessage/wif/WifExporterTest.java | 1 + .../test/java/ch/dissem/bitmessage/wif/WifImporterTest.java | 1 + 3 files changed, 5 insertions(+) diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 9fb2ea5..ccd2b67 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.NetworkHandler; +import ch.dissem.bitmessage.ports.ProofOfWorkRepository; import ch.dissem.bitmessage.security.bc.BouncySecurity; import ch.dissem.bitmessage.utils.Property; import org.junit.AfterClass; @@ -54,6 +55,7 @@ public class NetworkHandlerTest { .addressRepo(Mockito.mock(AddressRepository.class)) .inventory(peerInventory) .messageRepo(Mockito.mock(MessageRepository.class)) + .powRepo(Mockito.mock(ProofOfWorkRepository.class)) .port(6001) .nodeRegistry(new TestNodeRegistry()) .networkHandler(new DefaultNetworkHandler()) @@ -68,6 +70,7 @@ public class NetworkHandlerTest { .addressRepo(Mockito.mock(AddressRepository.class)) .inventory(nodeInventory) .messageRepo(Mockito.mock(MessageRepository.class)) + .powRepo(Mockito.mock(ProofOfWorkRepository.class)) .port(6002) .nodeRegistry(new TestNodeRegistry(localhost)) .networkHandler(networkHandler) 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 a2ee560..3e75a15 100644 --- a/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java +++ b/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java @@ -39,6 +39,7 @@ public class WifExporterTest { .networkHandler(mock(NetworkHandler.class)) .inventory(mock(Inventory.class)) .messageRepo(mock(MessageRepository.class)) + .powRepo(mock(ProofOfWorkRepository.class)) .nodeRegistry(mock(NodeRegistry.class)) .addressRepo(repo) .build(); diff --git a/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java b/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java index 862b3e3..d889523 100644 --- a/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java +++ b/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java @@ -42,6 +42,7 @@ public class WifImporterTest { .networkHandler(mock(NetworkHandler.class)) .inventory(mock(Inventory.class)) .messageRepo(mock(MessageRepository.class)) + .powRepo(mock(ProofOfWorkRepository.class)) .nodeRegistry(mock(NodeRegistry.class)) .addressRepo(repo) .build(); From 61788802c57a01d9d41932a5f5beb04e87f3b7c4 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Fri, 18 Dec 2015 16:42:17 +0100 Subject: [PATCH 31/42] Some POW improvements --- .../dissem/bitmessage/BitmessageContext.java | 30 ++++++++++++++++ .../ch/dissem/bitmessage/InternalContext.java | 4 +++ .../dissem/bitmessage/ProofOfWorkService.java | 12 +++++-- .../bitmessage/entity/CustomMessage.java | 16 ++++++--- .../ports/ProofOfWorkRepository.java | 20 +++++++++-- .../extensions/pow/ProofOfWorkRequest.java | 18 ++++++++-- .../repository/JdbcProofOfWorkRepository.java | 36 +++++++++++++++---- .../db/migration/V2.1__Create_table_POW.sql | 8 +++-- 8 files changed, 125 insertions(+), 19 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index def0a4f..511aaea 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -31,6 +31,8 @@ import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.util.Arrays; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.*; import static ch.dissem.bitmessage.entity.Plaintext.Status.*; @@ -72,6 +74,13 @@ public class BitmessageContext { // As this thread is used for parts that do POW, which itself uses parallel threads, only // one should be executed at any time. pool = Executors.newFixedThreadPool(1); + + new Timer().schedule(new TimerTask() { + @Override + public void run() { + ctx.getProofOfWorkService().doMissingProofOfWork(); + } + }, 30_000); // After 30 seconds } public AddressRepository addresses() { @@ -206,6 +215,19 @@ public class BitmessageContext { } } + /** + * 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 + */ + public CustomMessage send(InetAddress server, int port, CustomMessage request) { + return ctx.getNetworkHandler().send(server, port, request); + } + public void cleanup() { ctx.getInventory().cleanup(); } @@ -276,6 +298,14 @@ public class BitmessageContext { ); } + /** + * Returns the {@link InternalContext} - normally you wouldn't need it, + * unless you are doing something crazy with the protocol. + */ + public InternalContext internals() { + return ctx; + } + public interface Listener { void receive(Plaintext plaintext); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index d139de5..89f3082 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -140,6 +140,10 @@ public class InternalContext { return proofOfWorkEngine; } + public ProofOfWorkService getProofOfWorkService() { + return proofOfWorkService; + } + public long[] getStreams() { long[] result = new long[streams.size()]; int i = 0; diff --git a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java index 3cf46ef..da59105 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java @@ -20,6 +20,13 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC private ProofOfWorkRepository powRepo; private MessageRepository messageRepo; + public void doMissingProofOfWork() { + for (byte[] initialHash : powRepo.getItems()) { + ProofOfWorkRepository.Item item = powRepo.getItem(initialHash); + security.doProofOfWork(item.object, item.nonceTrialsPerByte, item.extraBytes, this); + } + } + public void doProofOfWork(ObjectMessage object) { doProofOfWork(null, object); } @@ -29,7 +36,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC long extraBytes = recipient == null ? 0 : recipient.getPubkey().getExtraBytes(); powRepo.putObject(object, nonceTrialsPerByte, extraBytes); - if (object.getPayload() instanceof PlaintextHolder){ + if (object.getPayload() instanceof PlaintextHolder) { Plaintext plaintext = ((PlaintextHolder) object.getPayload()).getPlaintext(); plaintext.setInitialHash(security.getInitialHash(object)); messageRepo.save(plaintext); @@ -39,7 +46,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC @Override public void onNonceCalculated(byte[] initialHash, byte[] nonce) { - ObjectMessage object = powRepo.getObject(initialHash); + ObjectMessage object = powRepo.getItem(initialHash).object; object.setNonce(nonce); // messageCallback.proofOfWorkCompleted(payload); Plaintext plaintext = messageRepo.getMessage(initialHash); @@ -48,6 +55,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC messageRepo.save(plaintext); } ctx.getInventory().storeObject(object); + ctx.getProofOfWorkRepository().removeObject(initialHash); ctx.getNetworkHandler().offer(object.getInventoryVector()); // messageCallback.messageOffered(payload, object.getInventoryVector()); } 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 a5caf3a..126b808 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java @@ -53,13 +53,21 @@ public class CustomMessage implements MessagePayload { return Command.CUSTOM; } - public byte[] getData() throws IOException { + public String getCustomCommand() { + return command; + } + + public byte[] getData() { if (data != null) { return data; } else { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - write(out); - return out.toByteArray(); + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write(out); + return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java index 9971ad5..739c172 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java @@ -2,15 +2,31 @@ package ch.dissem.bitmessage.ports; import ch.dissem.bitmessage.entity.ObjectMessage; +import java.util.List; + /** * Objects that proof of work is currently being done for. * * @author Christian Basler */ public interface ProofOfWorkRepository { - ObjectMessage getObject(byte[] initialHash); + Item getItem(byte[] initialHash); + + List<byte[]> getItems(); void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes); - void removeObject(ObjectMessage object); + void removeObject(byte[] initialHash); + + class Item { + public final ObjectMessage object; + public final long nonceTrialsPerByte; + public final long extraBytes; + + public Item(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) { + this.object = object; + this.nonceTrialsPerByte = nonceTrialsPerByte; + this.extraBytes = extraBytes; + } + } } 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 2ef2f9e..196005d 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 @@ -18,13 +18,13 @@ package ch.dissem.bitmessage.extensions.pow; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Streamable; +import ch.dissem.bitmessage.extensions.CryptoCustomMessage; import ch.dissem.bitmessage.utils.Encode; 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.*; /** @@ -34,6 +34,7 @@ public class ProofOfWorkRequest implements Streamable { private final BitmessageAddress sender; private final byte[] initialHash; private final Request request; + private final byte[] data; public ProofOfWorkRequest(BitmessageAddress sender, byte[] initialHash, Request request) { @@ -79,10 +80,23 @@ public class ProofOfWorkRequest implements Streamable { Encode.varBytes(data, out); } + public static class Reader implements CryptoCustomMessage.Reader<ProofOfWorkRequest> { + private final BitmessageAddress identity; + + public Reader(BitmessageAddress identity) { + this.identity = identity; + } + + @Override + public ProofOfWorkRequest read(BitmessageAddress sender, InputStream in) throws IOException { + return ProofOfWorkRequest.read(identity, in); + } + } + + public enum Request { CALCULATE, CALCULATING, - QUERY, COMPLETE } } diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java index aa8ca23..9268311 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcProofOfWorkRepository.java @@ -8,6 +8,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.*; +import java.util.LinkedList; +import java.util.List; import static ch.dissem.bitmessage.utils.Singleton.security; @@ -22,14 +24,18 @@ public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWork } @Override - public ObjectMessage getObject(byte[] initialHash) { + public Item getItem(byte[] initialHash) { try (Connection connection = config.getConnection()) { - PreparedStatement ps = connection.prepareStatement("SELECT data, version FROM POW WHERE initial_hash=?"); + PreparedStatement ps = connection.prepareStatement("SELECT data, version, nonce_trials_per_byte, extra_bytes FROM POW WHERE initial_hash=?"); ps.setBytes(1, initialHash); ResultSet rs = ps.executeQuery(); if (rs.next()) { Blob data = rs.getBlob("data"); - return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()); + return new Item( + Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()), + rs.getLong("nonce_trials_per_byte"), + rs.getLong("extra_bytes") + ); } else { throw new RuntimeException("Object requested that we don't have. Initial hash: " + Strings.hex(initialHash)); } @@ -39,13 +45,31 @@ public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWork } } + @Override + public List<byte[]> getItems() { + try (Connection connection = config.getConnection()) { + List<byte[]> result = new LinkedList<>(); + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT initial_hash FROM POW"); + while (rs.next()) { + result.add(rs.getBytes("initial_hash")); + } + return result; + } catch (SQLException e) { + LOG.error(e.getMessage(), e); + throw new RuntimeException(e); + } + } + @Override public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) { try (Connection connection = config.getConnection()) { - PreparedStatement ps = connection.prepareStatement("INSERT INTO POW (initial_hash, data, version) VALUES (?, ?, ?)"); + PreparedStatement ps = connection.prepareStatement("INSERT INTO POW (initial_hash, data, version, nonce_trials_per_byte, extra_bytes) VALUES (?, ?, ?, ?, ?)"); ps.setBytes(1, security().getInitialHash(object)); writeBlob(ps, 2, object); ps.setLong(3, object.getVersion()); + ps.setLong(4, nonceTrialsPerByte); + ps.setLong(5, extraBytes); ps.executeUpdate(); } catch (SQLException e) { LOG.debug("Error storing object of type " + object.getPayload().getClass().getSimpleName(), e); @@ -57,10 +81,10 @@ public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWork } @Override - public void removeObject(ObjectMessage object) { + public void removeObject(byte[] initialHash) { try (Connection connection = config.getConnection()) { PreparedStatement ps = connection.prepareStatement("DELETE FROM POW WHERE initial_hash=?"); - ps.setBytes(1, security().getInitialHash(object)); + ps.setBytes(1, initialHash); ps.executeUpdate(); } catch (SQLException e) { LOG.debug(e.getMessage(), e); diff --git a/repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql b/repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql index 4f54698..b39c6c5 100644 --- a/repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql +++ b/repositories/src/main/resources/db/migration/V2.1__Create_table_POW.sql @@ -1,5 +1,7 @@ CREATE TABLE POW ( - initial_hash BINARY(64) PRIMARY KEY, - data BLOB NOT NULL, - version BIGINT NOT NULL + initial_hash BINARY(64) PRIMARY KEY, + data BLOB NOT NULL, + version BIGINT NOT NULL, + nonce_trials_per_byte BIGINT NOT NULL, + extra_bytes BIGINT NOT NULL ); From fad3e07871e44cddad64ebe39ac85b34db37bfb5 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Mon, 21 Dec 2015 15:13:48 +0100 Subject: [PATCH 32/42] Some changes needed for POW server and some general improvements --- .../dissem/bitmessage/BitmessageContext.java | 44 ++++++++++++++++--- .../ch/dissem/bitmessage/InternalContext.java | 12 ++--- .../dissem/bitmessage/ProofOfWorkService.java | 18 ++++++-- .../bitmessage/entity/CustomMessage.java | 2 +- .../bitmessage/ports/AbstractSecurity.java | 31 ++++++++----- .../ch/dissem/bitmessage/ports/Security.java | 2 + .../extensions/CryptoCustomMessage.java | 5 ++- .../extensions/pow/ProofOfWorkRequest.java | 24 +++++++++- .../extensions/CryptoCustomMessageTest.java | 42 +++++++++++++++--- 9 files changed, 143 insertions(+), 37 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 511aaea..1c4295e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -66,6 +66,8 @@ public class BitmessageContext { private final Listener listener; private final NetworkHandler.MessageListener networkListener; + private final boolean sendPubkeyOnIdentityCreation; + private BitmessageContext(Builder builder) { ctx = new InternalContext(builder); listener = builder.listener; @@ -75,6 +77,8 @@ public class BitmessageContext { // one should be executed at any time. pool = Executors.newFixedThreadPool(1); + sendPubkeyOnIdentityCreation = builder.sendPubkeyOnIdentityCreation; + new Timer().schedule(new TimerTask() { @Override public void run() { @@ -100,12 +104,14 @@ public class BitmessageContext { features )); ctx.getAddressRepository().save(identity); - pool.submit(new Runnable() { - @Override - public void run() { - ctx.sendPubkey(identity, identity.getStream()); - } - }); + if (sendPubkeyOnIdentityCreation) { + pool.submit(new Runnable() { + @Override + public void run() { + ctx.sendPubkey(identity, identity.getStream()); + } + }); + } return identity; } @@ -325,6 +331,8 @@ public class BitmessageContext { Listener listener; int connectionLimit = 150; long connectionTTL = 12 * HOUR; + boolean sendPubkeyOnIdentityCreation = true; + long pubkeyTTL = 28; public Builder() { } @@ -399,6 +407,30 @@ public class BitmessageContext { return this; } + /** + * By default a client will send the public key when an identity is being created. On weaker devices + * this behaviour might not be desirable. + */ + public Builder doNotSendPubkeyOnIdentityCreation() { + this.sendPubkeyOnIdentityCreation = false; + return this; + } + + /** + * Time to live in seconds for public keys the client sends. Defaults to the maximum of 28 days, + * but on weak devices smaller values might be desirable. + * <p> + * Please be aware that this might cause some problems where you can't receive a message (the + * sender can't receive your public key) in some special situations. Also note that it's probably + * not a good idea to set it too low. + * </p> + */ + public Builder pubkeyTTL(long days) { + if (days < 0 || days > 28 * DAY) throw new IllegalArgumentException("TTL must be between 1 and 28 days"); + this.pubkeyTTL = days; + return this; + } + public BitmessageContext build() { nonNull("inventory", inventory); nonNull("nodeRegistry", nodeRegistry); diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 89f3082..1fe8007 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -16,7 +16,9 @@ package ch.dissem.bitmessage; -import ch.dissem.bitmessage.entity.*; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Encrypted; +import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.payload.Broadcast; import ch.dissem.bitmessage.entity.payload.GetPubkey; import ch.dissem.bitmessage.entity.payload.ObjectPayload; @@ -29,8 +31,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.TreeSet; -import static ch.dissem.bitmessage.utils.UnixTime.DAY; - /** * The internal context should normally only be used for port implementations. If you need it in your client * implementation, you're either doing something wrong, something very weird, or the BitmessageContext should @@ -59,6 +59,7 @@ public class InternalContext { private final long clientNonce; private final long networkNonceTrialsPerByte = 1000; private final long networkExtraBytes = 1000; + private final long pubkeyTTL; private long connectionTTL; private int connectionLimit; @@ -78,6 +79,7 @@ public class InternalContext { this.port = builder.port; this.connectionLimit = builder.connectionLimit; this.connectionTTL = builder.connectionTTL; + this.pubkeyTTL = builder.pubkeyTTL; Singleton.initialize(security); @@ -193,7 +195,7 @@ public class InternalContext { public void sendPubkey(final BitmessageAddress identity, final long targetStream) { try { - long expires = UnixTime.now(+28 * DAY); + long expires = UnixTime.now(pubkeyTTL); LOG.info("Expires at " + expires); final ObjectMessage response = new ObjectMessage.Builder() .stream(targetStream) @@ -211,7 +213,7 @@ public class InternalContext { } public void requestPubkey(final BitmessageAddress contact) { - long expires = UnixTime.now(+2 * DAY); + long expires = UnixTime.now(+pubkeyTTL); LOG.info("Expires at " + expires); final ObjectMessage response = new ObjectMessage.Builder() .stream(contact.getStream()) diff --git a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java index da59105..82c384c 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java @@ -8,6 +8,10 @@ import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import ch.dissem.bitmessage.ports.ProofOfWorkRepository; import ch.dissem.bitmessage.ports.Security; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; import static ch.dissem.bitmessage.utils.Singleton.security; @@ -15,13 +19,19 @@ import static ch.dissem.bitmessage.utils.Singleton.security; * @author Christian Basler */ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalContext.ContextHolder { + private final static Logger LOG = LoggerFactory.getLogger(ProofOfWorkService.class); + private Security security; private InternalContext ctx; private ProofOfWorkRepository powRepo; private MessageRepository messageRepo; public void doMissingProofOfWork() { - for (byte[] initialHash : powRepo.getItems()) { + List<byte[]> items = powRepo.getItems(); + if (items.isEmpty()) return; + + LOG.info("Doing POW for " + items.size() + " tasks."); + for (byte[] initialHash : items) { ProofOfWorkRepository.Item item = powRepo.getItem(initialHash); security.doProofOfWork(item.object, item.nonceTrialsPerByte, item.extraBytes, this); } @@ -32,8 +42,10 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC } public void doProofOfWork(BitmessageAddress recipient, ObjectMessage object) { - long nonceTrialsPerByte = recipient == null ? 0 : recipient.getPubkey().getNonceTrialsPerByte(); - long extraBytes = recipient == null ? 0 : recipient.getPubkey().getExtraBytes(); + long nonceTrialsPerByte = recipient == null ? + ctx.getNetworkNonceTrialsPerByte() : recipient.getPubkey().getNonceTrialsPerByte(); + long extraBytes = recipient == null ? + ctx.getNetworkExtraBytes() : recipient.getPubkey().getExtraBytes(); powRepo.putObject(object, nonceTrialsPerByte, extraBytes); if (object.getPayload() instanceof PlaintextHolder) { 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 126b808..5702b6e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java @@ -43,7 +43,7 @@ public class CustomMessage implements MessagePayload { this.data = data; } - public static MessagePayload read(InputStream in, int length) throws IOException { + public static CustomMessage read(InputStream in, int length) throws IOException { AccessCounter counter = new AccessCounter(); return new CustomMessage(varString(in, counter), bytes(in, length - counter.length())); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java index 0dea04c..053a776 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java @@ -43,6 +43,8 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont public static final Logger LOG = LoggerFactory.getLogger(Security.class); private static final SecureRandom RANDOM = new SecureRandom(); private static final BigInteger TWO = BigInteger.valueOf(2); + private static final BigInteger TWO_POW_64 = TWO.pow(64); + private static final BigInteger TWO_POW_16 = TWO.pow(16); private final String provider; private InternalContext context; @@ -96,18 +98,14 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes, ProofOfWorkEngine.Callback callback) { - try { - nonceTrialsPerByte = max(nonceTrialsPerByte, context.getNetworkNonceTrialsPerByte()); - extraBytes = max(extraBytes, context.getNetworkExtraBytes()); + nonceTrialsPerByte = max(nonceTrialsPerByte, context.getNetworkNonceTrialsPerByte()); + extraBytes = max(extraBytes, context.getNetworkExtraBytes()); - byte[] initialHash = getInitialHash(object); + byte[] initialHash = getInitialHash(object); - byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); + byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); - context.getProofOfWorkEngine().calculateNonce(initialHash, target, callback); - } catch (IOException e) { - throw new RuntimeException(e); - } + context.getProofOfWorkEngine().calculateNonce(initialHash, target, callback); } public void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) @@ -124,11 +122,20 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont return sha512(object.getPayloadBytesWithoutNonce()); } - private byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { + @Override + public byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) { + if (nonceTrialsPerByte == 0) nonceTrialsPerByte = context.getNetworkNonceTrialsPerByte(); + if (extraBytes == 0) extraBytes = context.getNetworkExtraBytes(); + BigInteger TTL = BigInteger.valueOf(object.getExpiresTime() - UnixTime.now()); - BigInteger numerator = TWO.pow(64); + BigInteger numerator = TWO_POW_64; BigInteger powLength = BigInteger.valueOf(object.getPayloadBytesWithoutNonce().length + extraBytes); - BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte).multiply(powLength.add(powLength.multiply(TTL).divide(BigInteger.valueOf(2).pow(16)))); + BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte) + .multiply( + powLength.add( + powLength.multiply(TTL).divide(TWO_POW_16) + ) + ); return Bytes.expand(numerator.divide(denominator).toByteArray(), 8); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java index 8fc7e20..e76b21f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java @@ -136,6 +136,8 @@ public interface Security { byte[] getInitialHash(ObjectMessage object); + byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes); + /** * Calculates the MAC for a message (data) * diff --git a/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java b/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java index 9a9e2dc..49c6f1b 100644 --- a/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java +++ b/extensions/src/main/java/ch/dissem/bitmessage/extensions/CryptoCustomMessage.java @@ -54,8 +54,8 @@ public class CryptoCustomMessage<T extends Streamable> extends CustomMessage { this.dataReader = dataReader; } - public static <T extends Streamable> CryptoCustomMessage<T> read(byte[] data, Reader<T> dataReader) throws IOException { - CryptoBox cryptoBox = CryptoBox.read(new ByteArrayInputStream(data), data.length); + public static <T extends Streamable> CryptoCustomMessage<T> read(CustomMessage data, Reader<T> dataReader) throws IOException { + CryptoBox cryptoBox = CryptoBox.read(new ByteArrayInputStream(data.getData()), data.getData().length); return new CryptoCustomMessage<>(cryptoBox, dataReader); } @@ -111,6 +111,7 @@ public class CryptoCustomMessage<T extends Streamable> extends CustomMessage { @Override public void write(OutputStream out) throws IOException { + Encode.varString(COMMAND, out); container.write(out); } 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 196005d..0024aaa 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 ch.dissem.bitmessage.utils.Encode; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Arrays; import static ch.dissem.bitmessage.utils.Decode.*; @@ -80,6 +81,28 @@ public class ProofOfWorkRequest implements Streamable { Encode.varBytes(data, out); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ProofOfWorkRequest other = (ProofOfWorkRequest) o; + + if (!sender.equals(other.sender)) return false; + if (!Arrays.equals(initialHash, other.initialHash)) return false; + if (request != other.request) return false; + return Arrays.equals(data, other.data); + } + + @Override + public int hashCode() { + int result = sender.hashCode(); + result = 31 * result + Arrays.hashCode(initialHash); + result = 31 * result + request.hashCode(); + result = 31 * result + Arrays.hashCode(data); + return result; + } + public static class Reader implements CryptoCustomMessage.Reader<ProofOfWorkRequest> { private final BitmessageAddress identity; @@ -93,7 +116,6 @@ public class ProofOfWorkRequest implements Streamable { } } - public enum Request { CALCULATE, CALCULATING, diff --git a/extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.java b/extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.java index 98e97a1..c1303e3 100644 --- a/extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.java +++ b/extensions/src/test/java/ch/dissem/bitmessage/extensions/CryptoCustomMessageTest.java @@ -17,8 +17,10 @@ package ch.dissem.bitmessage.extensions; import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.CustomMessage; import ch.dissem.bitmessage.entity.payload.GenericPayload; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; +import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest; import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; @@ -33,7 +35,7 @@ import static org.junit.Assert.assertEquals; public class CryptoCustomMessageTest extends TestBase { @Test - public void testEncryptThenDecrypt() throws Exception { + public void ensureEncryptThenDecryptYieldsSameObject() throws Exception { PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey")); BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey); @@ -45,14 +47,40 @@ public class CryptoCustomMessageTest extends TestBase { messageBefore.write(out); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - CryptoCustomMessage<GenericPayload> messageAfter = CryptoCustomMessage.read(out.toByteArray(), new CryptoCustomMessage.Reader<GenericPayload>() { - @Override - public GenericPayload read(BitmessageAddress ignore, InputStream in) throws IOException { - return GenericPayload.read(0, in, 1, 100); - } - }); + CustomMessage customMessage = CustomMessage.read(in, out.size()); + CryptoCustomMessage<GenericPayload> messageAfter = CryptoCustomMessage.read(customMessage, + new CryptoCustomMessage.Reader<GenericPayload>() { + @Override + public GenericPayload read(BitmessageAddress ignore, InputStream in) throws IOException { + return GenericPayload.read(0, in, 1, 100); + } + }); GenericPayload payloadAfter = messageAfter.decrypt(sendingIdentity.getPublicDecryptionKey()); assertEquals(payloadBefore, payloadAfter); } + + @Test + public void testWithActualRequest() throws Exception { + PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey")); + final BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey); + + ProofOfWorkRequest requestBefore = new ProofOfWorkRequest(sendingIdentity, security().randomBytes(64), + ProofOfWorkRequest.Request.CALCULATE); + + CryptoCustomMessage<ProofOfWorkRequest> messageBefore = new CryptoCustomMessage<>(requestBefore); + messageBefore.signAndEncrypt(sendingIdentity, security().createPublicKey(sendingIdentity.getPublicDecryptionKey())); + + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + messageBefore.write(out); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + + CustomMessage customMessage = CustomMessage.read(in, out.size()); + CryptoCustomMessage<ProofOfWorkRequest> messageAfter = CryptoCustomMessage.read(customMessage, + new ProofOfWorkRequest.Reader(sendingIdentity)); + ProofOfWorkRequest requestAfter = messageAfter.decrypt(sendingIdentity.getPublicDecryptionKey()); + + assertEquals(requestBefore, requestAfter); + } } From 549c8854ed7c8b5ec8319e8d174470483cf78327 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 10 Jan 2016 13:38:32 +0100 Subject: [PATCH 33/42] Refactoring: renamed 'security' to 'cryptography' --- README.md | 1 + {security-bc => cryptography-bc}/build.gradle | 6 +++--- .../cryptography/bc/BouncyCryptography.java | 8 ++++---- .../bitmessage/security/CryptographyTest.java | 10 +++++----- {security-sc => cryptography-sc}/build.gradle | 6 +++--- .../cryptography/sc/SpongyCryptography.java | 8 ++++---- demo/build.gradle | 2 +- .../ch/dissem/bitmessage/demo/Application.java | 4 ++-- .../java/ch/dissem/bitmessage/demo/Main.java | 4 ++-- domain/build.gradle | 2 +- .../ch/dissem/bitmessage/BitmessageContext.java | 6 +++--- .../ch/dissem/bitmessage/InternalContext.java | 16 ++++++++-------- .../ch/dissem/bitmessage/ProofOfWorkService.java | 12 ++++++------ .../ch/dissem/bitmessage/entity/Encrypted.java | 1 - .../dissem/bitmessage/entity/ObjectMessage.java | 1 - .../bitmessage/entity/payload/V4Broadcast.java | 1 - ...ctSecurity.java => AbstractCryptography.java} | 6 +++--- .../ports/{Security.java => Cryptography.java} | 2 +- .../ch/dissem/bitmessage/utils/Singleton.java | 14 +++++++------- .../ch/dissem/bitmessage/utils/TestBase.java | 4 ++-- extensions/build.gradle | 2 +- networking/build.gradle | 2 +- .../networking/NetworkHandlerTest.java | 6 +++--- repositories/build.gradle | 2 +- .../repository/JdbcMessageRepositoryTest.java | 2 +- .../dissem/bitmessage/repository/TestBase.java | 4 ++-- settings.gradle | 4 ++-- wif/build.gradle | 2 +- .../dissem/bitmessage/wif/WifExporterTest.java | 4 ++-- .../dissem/bitmessage/wif/WifImporterTest.java | 4 ++-- 30 files changed, 72 insertions(+), 74 deletions(-) rename {security-bc => cryptography-bc}/build.gradle (61%) rename security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java => cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java (96%) rename security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java => cryptography-bc/src/test/java/ch/dissem/bitmessage/security/CryptographyTest.java (94%) rename {security-sc => cryptography-sc}/build.gradle (54%) rename security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java => cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java (96%) rename domain/src/main/java/ch/dissem/bitmessage/ports/{AbstractSecurity.java => AbstractCryptography.java} (96%) rename domain/src/main/java/ch/dissem/bitmessage/ports/{Security.java => Cryptography.java} (99%) diff --git a/README.md b/README.md index eb5cdfc..19ce171 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ BitmessageContext ctx = new BitmessageContext.Builder() .messageRepo(new JdbcMessageRepository(jdbcConfig)) .nodeRegistry(new MemoryNodeRegistry()) .networkHandler(new NetworkNode()) + .cryptography(new BouncyCryptography()) .build(); ``` This creates a simple context using a H2 database that will be created in the user's home directory. Next you'll need to diff --git a/security-bc/build.gradle b/cryptography-bc/build.gradle similarity index 61% rename from security-bc/build.gradle rename to cryptography-bc/build.gradle index ff37994..0634154 100644 --- a/security-bc/build.gradle +++ b/cryptography-bc/build.gradle @@ -2,9 +2,9 @@ uploadArchives { repositories { mavenDeployer { pom.project { - name 'Jabit Bouncy Security' - artifactId = 'jabit-security-bouncy' - description 'The Security implementation using bouncy castle' + name 'Jabit Bouncy Cryptography' + artifactId = 'jabit-cryptography-bouncy' + description 'The Cryptography implementation using bouncy castle' } } } diff --git a/security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java b/cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java similarity index 96% rename from security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java rename to cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java index a125049..28be67a 100644 --- a/security-bc/src/main/java/ch/dissem/bitmessage/security/bc/BouncySecurity.java +++ b/cryptography-bc/src/main/java/ch/dissem/bitmessage/cryptography/bc/BouncyCryptography.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package ch.dissem.bitmessage.security.bc; +package ch.dissem.bitmessage.cryptography.bc; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; -import ch.dissem.bitmessage.ports.AbstractSecurity; +import ch.dissem.bitmessage.ports.AbstractCryptography; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherParameters; @@ -47,14 +47,14 @@ import java.util.Arrays; * As Spongycastle can't be used on the Oracle JVM, and Bouncycastle doesn't work properly on Android (thanks, Google), * this is the Bouncycastle implementation. */ -public class BouncySecurity extends AbstractSecurity { +public class BouncyCryptography extends AbstractCryptography { private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1"); static { java.security.Security.addProvider(new BouncyCastleProvider()); } - public BouncySecurity() { + public BouncyCryptography() { super("BC"); } diff --git a/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java b/cryptography-bc/src/test/java/ch/dissem/bitmessage/security/CryptographyTest.java similarity index 94% rename from security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java rename to cryptography-bc/src/test/java/ch/dissem/bitmessage/security/CryptographyTest.java index 3aef7a8..3a68968 100644 --- a/security-bc/src/test/java/ch/dissem/bitmessage/security/SecurityTest.java +++ b/cryptography-bc/src/test/java/ch/dissem/bitmessage/security/CryptographyTest.java @@ -5,7 +5,7 @@ import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.payload.GenericPayload; import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine; import ch.dissem.bitmessage.ports.ProofOfWorkEngine; -import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.utils.CallbackWaiter; import ch.dissem.bitmessage.utils.Singleton; import ch.dissem.bitmessage.utils.UnixTime; @@ -23,7 +23,7 @@ import static org.mockito.Mockito.when; /** * Created by chris on 19.07.15. */ -public class SecurityTest { +public class CryptographyTest { public static final byte[] TEST_VALUE = "teststring".getBytes(); public static final byte[] TEST_SHA1 = DatatypeConverter.parseHexBinary("" + "b8473b86d4c2072ca9b08bd28e373e8253e865c4"); @@ -33,10 +33,10 @@ public class SecurityTest { public static final byte[] TEST_RIPEMD160 = DatatypeConverter.parseHexBinary("" + "cd566972b5e50104011a92b59fa8e0b1234851ae"); - private static BouncySecurity security; + private static BouncyCryptography security; - public SecurityTest() { - security = new BouncySecurity(); + public CryptographyTest() { + security = new BouncyCryptography(); Singleton.initialize(security); InternalContext ctx = mock(InternalContext.class); when(ctx.getProofOfWorkEngine()).thenReturn(new MultiThreadedPOWEngine()); diff --git a/security-sc/build.gradle b/cryptography-sc/build.gradle similarity index 54% rename from security-sc/build.gradle rename to cryptography-sc/build.gradle index bfbdcab..2d4f82f 100644 --- a/security-sc/build.gradle +++ b/cryptography-sc/build.gradle @@ -2,9 +2,9 @@ uploadArchives { repositories { mavenDeployer { pom.project { - name 'Jabit Spongy Security' - artifactId = 'jabit-security-spongy' - description 'The Security implementation using spongy castle (needed for Android)' + name 'Jabit Spongy Cryptography' + artifactId = 'jabit-cryptography-spongy' + description 'The Cryptography implementation using spongy castle (needed for Android)' } } } diff --git a/security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java b/cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java similarity index 96% rename from security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java rename to cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java index 70a0743..c9506fb 100644 --- a/security-sc/src/main/java/ch/dissem/bitmessage/security/sc/SpongySecurity.java +++ b/cryptography-sc/src/main/java/ch/dissem/bitmessage/cryptography/sc/SpongyCryptography.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package ch.dissem.bitmessage.security.sc; +package ch.dissem.bitmessage.cryptography.sc; import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; -import ch.dissem.bitmessage.ports.AbstractSecurity; +import ch.dissem.bitmessage.ports.AbstractCryptography; import org.spongycastle.asn1.x9.X9ECParameters; import org.spongycastle.crypto.BufferedBlockCipher; import org.spongycastle.crypto.CipherParameters; @@ -47,14 +47,14 @@ import java.util.Arrays; * As Spongycastle can't be used on the Oracle JVM, and Bouncycastle doesn't work properly on Android (thanks, Google), * this is the Spongycastle implementation. */ -public class SpongySecurity extends AbstractSecurity { +public class SpongyCryptography extends AbstractCryptography { private static final X9ECParameters EC_CURVE_PARAMETERS = CustomNamedCurves.getByName("secp256k1"); static { java.security.Security.addProvider(new BouncyCastleProvider()); } - public SpongySecurity() { + public SpongyCryptography() { super("SC"); } diff --git a/demo/build.gradle b/demo/build.gradle index e4c51a7..e520157 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -24,7 +24,7 @@ dependencies { compile project(':domain') compile project(':networking') compile project(':repositories') - compile project(':security-bc') + compile project(':cryptography-bc') compile project(':wif') compile 'org.slf4j:slf4j-simple:1.7.12' compile 'args4j:args4j:2.32' 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 72da50d..f35bcc5 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -23,7 +23,7 @@ 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.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +49,7 @@ public class Application { .messageRepo(new JdbcMessageRepository(jdbcConfig)) .powRepo(new JdbcProofOfWorkRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) - .security(new BouncySecurity()) + .cryptography(new BouncyCryptography()) .port(48444) .listener(new BitmessageContext.Listener() { @Override 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 6dbfc14..a7b63d1 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -20,7 +20,7 @@ import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.ports.MemoryNodeRegistry; import ch.dissem.bitmessage.repository.*; -import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.wif.WifExporter; import ch.dissem.bitmessage.wif.WifImporter; import org.kohsuke.args4j.CmdLineException; @@ -53,7 +53,7 @@ public class Main { .messageRepo(new JdbcMessageRepository(jdbcConfig)) .powRepo(new JdbcProofOfWorkRepository(jdbcConfig)) .networkHandler(new DefaultNetworkHandler()) - .security(new BouncySecurity()) + .cryptography(new BouncyCryptography()) .port(48444) .build(); diff --git a/domain/build.gradle b/domain/build.gradle index 1880c6b..484abfd 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -27,5 +27,5 @@ dependencies { compile 'org.slf4j:slf4j-api:1.7.12' testCompile 'junit:junit:4.11' testCompile 'org.mockito:mockito-core:1.10.19' - testCompile project(':security-bc') + testCompile project(':cryptography-bc') } diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 1c4295e..1f34c9d 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -325,7 +325,7 @@ public class BitmessageContext { MessageRepository messageRepo; ProofOfWorkRepository proofOfWorkRepository; ProofOfWorkEngine proofOfWorkEngine; - Security security; + Cryptography cryptography; MessageCallback messageCallback; CustomCommandHandler customCommandHandler; Listener listener; @@ -372,8 +372,8 @@ public class BitmessageContext { return this; } - public Builder security(Security security) { - this.security = security; + public Builder cryptography(Cryptography cryptography) { + this.cryptography = cryptography; return this; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 1fe8007..9971b9b 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -42,7 +42,7 @@ import java.util.TreeSet; public class InternalContext { private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class); - private final Security security; + private final Cryptography cryptography; private final Inventory inventory; private final NodeRegistry nodeRegistry; private final NetworkHandler networkHandler; @@ -64,7 +64,7 @@ public class InternalContext { private int connectionLimit; public InternalContext(BitmessageContext.Builder builder) { - this.security = builder.security; + this.cryptography = builder.cryptography; this.inventory = builder.inventory; this.nodeRegistry = builder.nodeRegistry; this.networkHandler = builder.networkHandler; @@ -73,7 +73,7 @@ public class InternalContext { this.proofOfWorkRepository = builder.proofOfWorkRepository; this.proofOfWorkService = new ProofOfWorkService(); this.proofOfWorkEngine = builder.proofOfWorkEngine; - this.clientNonce = security.randomNonce(); + this.clientNonce = cryptography.randomNonce(); this.messageCallback = builder.messageCallback; this.customCommandHandler = builder.customCommandHandler; this.port = builder.port; @@ -81,7 +81,7 @@ public class InternalContext { this.connectionTTL = builder.connectionTTL; this.pubkeyTTL = builder.pubkeyTTL; - Singleton.initialize(security); + Singleton.initialize(cryptography); // TODO: streams of new identities and subscriptions should also be added. This works only after a restart. for (BitmessageAddress address : addressRepository.getIdentities()) { @@ -94,7 +94,7 @@ public class InternalContext { streams.add(1L); } - init(security, inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, + init(cryptography, inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkRepository, proofOfWorkService, proofOfWorkEngine, messageCallback, customCommandHandler); for (BitmessageAddress identity : addressRepository.getIdentities()) { @@ -110,8 +110,8 @@ public class InternalContext { } } - public Security getSecurity() { - return security; + public Cryptography getCryptography() { + return cryptography; } public Inventory getInventory() { @@ -203,7 +203,7 @@ public class InternalContext { .payload(identity.getPubkey()) .build(); response.sign(identity.getPrivateKey()); - response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); + response.encrypt(cryptography.createPublicKey(identity.getPublicDecryptionKey())); messageCallback.proofOfWorkStarted(identity.getPubkey()); // TODO: remember that the pubkey is just about to be sent, and on which stream! proofOfWorkService.doProofOfWork(response); diff --git a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java index 82c384c..19e231f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java @@ -7,7 +7,7 @@ import ch.dissem.bitmessage.entity.PlaintextHolder; import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import ch.dissem.bitmessage.ports.ProofOfWorkRepository; -import ch.dissem.bitmessage.ports.Security; +import ch.dissem.bitmessage.ports.Cryptography; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +21,7 @@ import static ch.dissem.bitmessage.utils.Singleton.security; public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalContext.ContextHolder { private final static Logger LOG = LoggerFactory.getLogger(ProofOfWorkService.class); - private Security security; + private Cryptography cryptography; private InternalContext ctx; private ProofOfWorkRepository powRepo; private MessageRepository messageRepo; @@ -33,7 +33,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC LOG.info("Doing POW for " + items.size() + " tasks."); for (byte[] initialHash : items) { ProofOfWorkRepository.Item item = powRepo.getItem(initialHash); - security.doProofOfWork(item.object, item.nonceTrialsPerByte, item.extraBytes, this); + cryptography.doProofOfWork(item.object, item.nonceTrialsPerByte, item.extraBytes, this); } } @@ -50,10 +50,10 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC powRepo.putObject(object, nonceTrialsPerByte, extraBytes); if (object.getPayload() instanceof PlaintextHolder) { Plaintext plaintext = ((PlaintextHolder) object.getPayload()).getPlaintext(); - plaintext.setInitialHash(security.getInitialHash(object)); + plaintext.setInitialHash(cryptography.getInitialHash(object)); messageRepo.save(plaintext); } - security.doProofOfWork(object, nonceTrialsPerByte, extraBytes, this); + cryptography.doProofOfWork(object, nonceTrialsPerByte, extraBytes, this); } @Override @@ -75,7 +75,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC @Override public void setContext(InternalContext ctx) { this.ctx = ctx; - this.security = security(); + this.cryptography = security(); this.powRepo = ctx.getProofOfWorkRepository(); this.messageRepo = ctx.getMessageRepository(); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java index 9eaa2ab..8b15371 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java @@ -17,7 +17,6 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.exception.DecryptionFailedException; -import ch.dissem.bitmessage.ports.Security; import java.io.IOException; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java index 9e89c42..99b3aec 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java @@ -22,7 +22,6 @@ import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; -import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.Bytes; import ch.dissem.bitmessage.utils.Encode; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java index 03364d2..39127a8 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java @@ -18,7 +18,6 @@ package ch.dissem.bitmessage.entity.payload; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; -import ch.dissem.bitmessage.ports.Security; import java.io.IOException; import java.io.InputStream; diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java similarity index 96% rename from domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java rename to domain/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java index 053a776..3cf3f0d 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java @@ -39,8 +39,8 @@ import static ch.dissem.bitmessage.utils.Numbers.max; /** * Implements everything that isn't directly dependent on either Spongy- or Bouncycastle. */ -public abstract class AbstractSecurity implements Security, InternalContext.ContextHolder { - public static final Logger LOG = LoggerFactory.getLogger(Security.class); +public abstract class AbstractCryptography implements Cryptography, InternalContext.ContextHolder { + public static final Logger LOG = LoggerFactory.getLogger(Cryptography.class); private static final SecureRandom RANDOM = new SecureRandom(); private static final BigInteger TWO = BigInteger.valueOf(2); private static final BigInteger TWO_POW_64 = TWO.pow(64); @@ -49,7 +49,7 @@ public abstract class AbstractSecurity implements Security, InternalContext.Cont private final String provider; private InternalContext context; - protected AbstractSecurity(String provider) { + protected AbstractCryptography(String provider) { this.provider = provider; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java similarity index 99% rename from domain/src/main/java/ch/dissem/bitmessage/ports/Security.java rename to domain/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java index e76b21f..48739ea 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java @@ -29,7 +29,7 @@ import java.security.SecureRandom; * Provides some methods to help with hashing and encryption. All randoms are created using {@link SecureRandom}, * which should be secure enough. */ -public interface Security { +public interface Cryptography { /** * A helper method to calculate SHA-512 hashes. Please note that a new {@link MessageDigest} object is created at * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java index d272beb..0c7134b 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java @@ -16,23 +16,23 @@ package ch.dissem.bitmessage.utils; -import ch.dissem.bitmessage.ports.Security; +import ch.dissem.bitmessage.ports.Cryptography; /** * Created by chris on 20.07.15. */ public class Singleton { - private static Security security; + private static Cryptography cryptography; - public static void initialize(Security security) { + public static void initialize(Cryptography cryptography) { synchronized (Singleton.class) { - if (Singleton.security == null) { - Singleton.security = security; + if (Singleton.cryptography == null) { + Singleton.cryptography = cryptography; } } } - public static Security security() { - return security; + public static Cryptography security() { + return cryptography; } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java b/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java index 1dd1335..e757d91 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java @@ -16,13 +16,13 @@ package ch.dissem.bitmessage.utils; -import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; /** * Created by chris on 20.07.15. */ public class TestBase { static { - Singleton.initialize(new BouncySecurity()); + Singleton.initialize(new BouncyCryptography()); } } diff --git a/extensions/build.gradle b/extensions/build.gradle index 0ae9fd4..c92eb96 100644 --- a/extensions/build.gradle +++ b/extensions/build.gradle @@ -32,5 +32,5 @@ dependencies { testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.mockito:mockito-core:1.10.19' testCompile project(path: ':domain', configuration: 'testArtifacts') - testCompile project(':security-bc') + testCompile project(':cryptography-bc') } diff --git a/networking/build.gradle b/networking/build.gradle index 06bd5d0..788bc4d 100644 --- a/networking/build.gradle +++ b/networking/build.gradle @@ -16,5 +16,5 @@ dependencies { testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.mockito:mockito-core:1.10.19' testCompile project(path: ':domain', configuration: 'testArtifacts') - testCompile project(':security-bc') + testCompile project(':cryptography-bc') } \ No newline at end of file diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index ccd2b67..499e377 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -22,7 +22,7 @@ import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.NetworkHandler; import ch.dissem.bitmessage.ports.ProofOfWorkRepository; -import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.utils.Property; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -59,7 +59,7 @@ public class NetworkHandlerTest { .port(6001) .nodeRegistry(new TestNodeRegistry()) .networkHandler(new DefaultNetworkHandler()) - .security(new BouncySecurity()) + .cryptography(new BouncyCryptography()) .listener(Mockito.mock(BitmessageContext.Listener.class)) .build(); peer.startup(); @@ -74,7 +74,7 @@ public class NetworkHandlerTest { .port(6002) .nodeRegistry(new TestNodeRegistry(localhost)) .networkHandler(networkHandler) - .security(new BouncySecurity()) + .cryptography(new BouncyCryptography()) .listener(Mockito.mock(BitmessageContext.Listener.class)) .build(); } diff --git a/repositories/build.gradle b/repositories/build.gradle index fecebce..cfbc9ea 100644 --- a/repositories/build.gradle +++ b/repositories/build.gradle @@ -18,5 +18,5 @@ dependencies { testCompile 'junit:junit:4.12' testCompile 'com.h2database:h2:1.4.190' testCompile 'org.mockito:mockito-core:1.10.19' - testCompile project(':security-bc') + testCompile project(':cryptography-bc') } \ No newline at end of file diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java index 3dceb24..c7c3614 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/JdbcMessageRepositoryTest.java @@ -55,7 +55,7 @@ public class JdbcMessageRepositoryTest extends TestBase { addressRepo = new JdbcAddressRepository(config); repo = new JdbcMessageRepository(config); new InternalContext(new BitmessageContext.Builder() - .security(security()) + .cryptography(security()) .addressRepo(addressRepo) .messageRepo(repo) ); diff --git a/repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java b/repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java index c6baaa2..be386cd 100644 --- a/repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java +++ b/repositories/src/test/java/ch/dissem/bitmessage/repository/TestBase.java @@ -18,7 +18,7 @@ package ch.dissem.bitmessage.repository; import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.ports.MultiThreadedPOWEngine; -import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import ch.dissem.bitmessage.utils.Singleton; import static org.mockito.Mockito.mock; @@ -29,7 +29,7 @@ import static org.mockito.Mockito.when; */ public class TestBase { static { - BouncySecurity security = new BouncySecurity(); + BouncyCryptography security = new BouncyCryptography(); Singleton.initialize(security); InternalContext ctx = mock(InternalContext.class); when(ctx.getProofOfWorkEngine()).thenReturn(new MultiThreadedPOWEngine()); diff --git a/settings.gradle b/settings.gradle index adecd45..634c473 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,8 +10,8 @@ include 'demo' include 'wif' -include 'security-sc' +include 'cryptography-sc' -include 'security-bc' +include 'cryptography-bc' include 'extensions' \ No newline at end of file diff --git a/wif/build.gradle b/wif/build.gradle index 5b32a21..ef16682 100644 --- a/wif/build.gradle +++ b/wif/build.gradle @@ -15,5 +15,5 @@ dependencies { compile 'org.ini4j:ini4j:0.5.4' testCompile 'junit:junit:4.11' testCompile 'org.mockito:mockito-core:1.10.19' - testCompile project(':security-bc') + testCompile project(':cryptography-bc') } 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 3e75a15..b930c0a 100644 --- a/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java +++ b/wif/src/test/java/ch/dissem/bitmessage/wif/WifExporterTest.java @@ -18,7 +18,7 @@ package ch.dissem.bitmessage.wif; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.ports.*; -import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import org.junit.Before; import org.junit.Test; @@ -35,7 +35,7 @@ public class WifExporterTest { @Before public void setUp() throws Exception { ctx = new BitmessageContext.Builder() - .security(new BouncySecurity()) + .cryptography(new BouncyCryptography()) .networkHandler(mock(NetworkHandler.class)) .inventory(mock(Inventory.class)) .messageRepo(mock(MessageRepository.class)) diff --git a/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java b/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java index d889523..fe8e15c 100644 --- a/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java +++ b/wif/src/test/java/ch/dissem/bitmessage/wif/WifImporterTest.java @@ -19,7 +19,7 @@ package ch.dissem.bitmessage.wif; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.ports.*; -import ch.dissem.bitmessage.security.bc.BouncySecurity; +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography; import org.junit.Before; import org.junit.Test; @@ -38,7 +38,7 @@ public class WifImporterTest { @Before public void setUp() throws Exception { ctx = new BitmessageContext.Builder() - .security(new BouncySecurity()) + .cryptography(new BouncyCryptography()) .networkHandler(mock(NetworkHandler.class)) .inventory(mock(Inventory.class)) .messageRepo(mock(MessageRepository.class)) From 87646428780fdc34ed28dc2ffa8eb3d183027a46 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 16 Jan 2016 08:47:50 +0100 Subject: [PATCH 34/42] Major fixes and improvements to the network module, fixing problems where objects where requested multiple times or not at all in some situations. --- .../dissem/bitmessage/BitmessageContext.java | 43 ++++++- .../ch/dissem/bitmessage/entity/GetData.java | 6 +- .../ch/dissem/bitmessage/utils/Property.java | 20 ++- .../bitmessage/networking/Connection.java | 117 ++++++++++-------- .../networking/DefaultNetworkHandler.java | 92 ++++++++++++-- .../networking/NetworkHandlerTest.java | 4 +- 6 files changed, 202 insertions(+), 80 deletions(-) diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 1f34c9d..46d76d7 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -40,6 +40,7 @@ import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; import static ch.dissem.bitmessage.utils.UnixTime.DAY; import static ch.dissem.bitmessage.utils.UnixTime.HOUR; +import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; /** * <p>Use this class if you want to create a Bitmessage client.</p> @@ -183,6 +184,40 @@ public class BitmessageContext { }); } + public void send(final Plaintext msg) { + if (msg.getFrom() == null || msg.getFrom().getPrivateKey() == null) { + throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key."); + } + pool.submit(new Runnable() { + @Override + public void run() { + BitmessageAddress to = msg.getTo(); + if (to.getPubkey() == null) { + tryToFindMatchingPubkey(to); + } + if (to.getPubkey() == null) { + LOG.info("Public key is missing from recipient. Requesting."); + requestPubkey(msg.getFrom(), to); + msg.setStatus(PUBKEY_REQUESTED); + ctx.getMessageRepository().save(msg); + } else { + LOG.info("Sending message."); + msg.setStatus(DOING_PROOF_OF_WORK); + ctx.getMessageRepository().save(msg); + ctx.send( + msg.getFrom(), + to, + new Msg(msg), + +2 * DAY + ); + msg.setStatus(SENT); + msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.SENT)); + ctx.getMessageRepository().save(msg); + } + } + }); + } + private void requestPubkey(BitmessageAddress requestingIdentity, BitmessageAddress address) { ctx.send( requestingIdentity, @@ -330,7 +365,7 @@ public class BitmessageContext { CustomCommandHandler customCommandHandler; Listener listener; int connectionLimit = 150; - long connectionTTL = 12 * HOUR; + long connectionTTL = 30 * MINUTE; boolean sendPubkeyOnIdentityCreation = true; long pubkeyTTL = 28; @@ -420,9 +455,9 @@ public class BitmessageContext { * Time to live in seconds for public keys the client sends. Defaults to the maximum of 28 days, * but on weak devices smaller values might be desirable. * <p> - * Please be aware that this might cause some problems where you can't receive a message (the - * sender can't receive your public key) in some special situations. Also note that it's probably - * not a good idea to set it too low. + * Please be aware that this might cause some problems where you can't receive a message (the + * sender can't receive your public key) in some special situations. Also note that it's probably + * not a good idea to set it too low. * </p> */ public Builder pubkeyTTL(long days) { diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/GetData.java b/domain/src/main/java/ch/dissem/bitmessage/entity/GetData.java index b62e6e5..e272bbc 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/GetData.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/GetData.java @@ -44,10 +44,10 @@ public class GetData implements MessagePayload { } @Override - public void write(OutputStream stream) throws IOException { - Encode.varInt(inventory.size(), stream); + public void write(OutputStream out) throws IOException { + Encode.varInt(inventory.size(), out); for (InventoryVector iv : inventory) { - iv.write(stream); + iv.write(out); } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java index e00d193..b823eb5 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java @@ -16,6 +16,9 @@ package ch.dissem.bitmessage.utils; +import java.util.Arrays; +import java.util.Objects; + /** * Some property that has a name, a value and/or other properties. This can be used for any purpose, but is for now * used to contain different status information. It is by default displayed in some JSON inspired human readable @@ -43,12 +46,19 @@ public class Property { return value; } - public Property getProperty(String name) { + /** + * Returns the property if available or <code>null</code> otherwise. + * Subproperties can be requested by submitting the sequence of properties. + */ + public Property getProperty(String... name) { + if (name == null || name.length == 0) return null; + for (Property p : properties) { - if (name == null) { - if (p.name == null) return p; - } else { - if (name.equals(p.name)) return p; + if (Objects.equals(name[0], p.name)) { + if (name.length == 1) + return p; + else + return p.getProperty(Arrays.copyOfRange(name, 1, name.length)); } } return null; 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 4d14fe2..3155645 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -42,6 +42,7 @@ import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentMap; import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT; +import static ch.dissem.bitmessage.networking.Connection.Mode.SYNC; import static ch.dissem.bitmessage.networking.Connection.State.*; import static ch.dissem.bitmessage.utils.Singleton.security; import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; @@ -49,7 +50,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; /** * A connection to a specific node */ -public class Connection { +class Connection { public static final int READ_TIMEOUT = 2000; private static final Logger LOG = LoggerFactory.getLogger(Connection.class); private static final int CONNECT_TIMEOUT = 5000; @@ -63,10 +64,12 @@ public class Connection { private final NetworkAddress host; private final NetworkAddress node; private final Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>(); - private final Map<InventoryVector, Long> requestedObjects; + private final Set<InventoryVector> commonRequestedObjects; + private final Set<InventoryVector> requestedObjects; private final long syncTimeout; private final ReaderRunnable reader = new ReaderRunnable(); private final WriterRunnable writer = new WriterRunnable(); + private final DefaultNetworkHandler networkHandler; private volatile State state; private InputStream in; @@ -75,39 +78,45 @@ public class Connection { private long[] streams; private int readTimeoutCounter; private boolean socketInitialized; + private long lastObjectTime; public Connection(InternalContext context, Mode mode, Socket socket, MessageListener listener, - ConcurrentMap<InventoryVector, Long> requestedObjectsMap) throws IOException { + Set<InventoryVector> requestedObjectsMap) throws IOException { this(context, mode, listener, socket, requestedObjectsMap, + Collections.newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(10_000)), new NetworkAddress.Builder().ip(socket.getInetAddress()).port(socket.getPort()).stream(1).build(), 0); } public Connection(InternalContext context, Mode mode, NetworkAddress node, MessageListener listener, - ConcurrentMap<InventoryVector, Long> requestedObjectsMap) { + Set<InventoryVector> requestedObjectsMap) { this(context, mode, listener, new Socket(), requestedObjectsMap, + Collections.newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(10_000)), node, 0); } private Connection(InternalContext context, Mode mode, MessageListener listener, Socket socket, - Map<InventoryVector, Long> requestedObjectsMap, NetworkAddress node, long syncTimeout) { + Set<InventoryVector> commonRequestedObjects, Set<InventoryVector> requestedObjects, NetworkAddress node, long syncTimeout) { this.startTime = UnixTime.now(); this.ctx = context; this.mode = mode; this.state = CONNECTING; this.listener = listener; this.socket = socket; - this.requestedObjects = requestedObjectsMap; + this.commonRequestedObjects = commonRequestedObjects; + this.requestedObjects = requestedObjects; this.host = new NetworkAddress.Builder().ipv6(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).port(0).build(); this.node = node; this.syncTimeout = (syncTimeout > 0 ? UnixTime.now(+syncTimeout) : 0); this.ivCache = new ConcurrentHashMap<>(); + this.networkHandler = (DefaultNetworkHandler) ctx.getNetworkHandler(); } public static Connection sync(InternalContext ctx, InetAddress address, int port, MessageListener listener, long timeoutInSeconds) throws IOException { - return new Connection(ctx, Mode.CLIENT, listener, new Socket(address, port), - new HashMap<InventoryVector, Long>(), + return new Connection(ctx, Mode.SYNC, listener, new Socket(address, port), + new HashSet<InventoryVector>(), + new HashSet<InventoryVector>(), new NetworkAddress.Builder().ip(address).port(port).stream(1).build(), timeoutInSeconds); } @@ -141,25 +150,26 @@ public class Connection { return true; } if (msg == null) { + if (requestedObjects.isEmpty() && sendingQueue.isEmpty()) + return true; + readTimeoutCounter++; return readTimeoutCounter > 1; + } else { + readTimeoutCounter = 0; + return false; } - readTimeoutCounter = 0; - if (!(msg.getPayload() instanceof Addr) && !(msg.getPayload() instanceof GetData) - && requestedObjects.isEmpty() && sendingQueue.isEmpty()) { - LOG.info("Synchronisation completed"); - return true; - } - return false; } private void activateConnection() { LOG.info("Successfully established connection with node " + node); state = ACTIVE; - sendAddresses(); + if (mode != SYNC) { + sendAddresses(); + ctx.getNodeRegistry().offerAddresses(Collections.singletonList(node)); + } sendInventory(); node.setTime(UnixTime.now()); - ctx.getNodeRegistry().offerAddresses(Collections.singletonList(node)); } private void cleanupIvCache() { @@ -187,41 +197,15 @@ public class Connection { } } - private void updateRequestedObjects(List<InventoryVector> missing) { - Long now = UnixTime.now(); - Long fiveMinutesAgo = now - 5 * MINUTE; - Long tenMinutesAgo = now - 10 * MINUTE; - List<InventoryVector> stillMissing = new LinkedList<>(); - for (Map.Entry<InventoryVector, Long> entry : requestedObjects.entrySet()) { - if (entry.getValue() < fiveMinutesAgo) { - stillMissing.add(entry.getKey()); - // If it's still not available after 10 minutes, we won't look for it - // any longer (except it's announced again) - if (entry.getValue() < tenMinutesAgo) { - requestedObjects.remove(entry.getKey()); - } - } - } - - for (InventoryVector iv : missing) { - requestedObjects.put(iv, now); - } - if (!stillMissing.isEmpty()) { - LOG.debug(stillMissing.size() + " items are still missing."); - missing.addAll(stillMissing); - } - } - private void receiveMessage(MessagePayload messagePayload) { switch (messagePayload.getCommand()) { case INV: Inv inv = (Inv) messagePayload; updateIvCache(inv.getInventory()); List<InventoryVector> missing = ctx.getInventory().getMissing(inv.getInventory(), streams); - missing.removeAll(requestedObjects.keySet()); + missing.removeAll(commonRequestedObjects); LOG.debug("Received inventory with " + inv.getInventory().size() + " elements, of which are " + missing.size() + " missing."); - updateRequestedObjects(missing); send(new GetData.Builder().inventory(missing).build()); break; case GETDATA: @@ -234,24 +218,33 @@ public class Connection { 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; } - security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); 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: // FIXME: don't do this while we catch up after initialising our first connection // (that might be a bit tricky to do) - ctx.getNetworkHandler().offer(objectMessage.getInventoryVector()); + 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 { - requestedObjects.remove(objectMessage.getInventoryVector()); + if (commonRequestedObjects.remove(objectMessage.getInventoryVector())) { + LOG.debug("Received object that wasn't requested."); +// if (!requestedObjects.isEmpty()) { +// DebugUtils.saveToFile(objectMessage); +// LOG.debug(objectMessage.getInventoryVector() + " was not in " +// + requestedObjects.toString()); +// } + } } break; case ADDR: @@ -282,10 +275,16 @@ public class Connection { public void disconnect() { state = DISCONNECTED; + + // Make sure objects that are still missing are requested from other nodes + networkHandler.request(requestedObjects); } - private void send(MessagePayload payload) { + void send(MessagePayload payload) { try { + if (payload instanceof GetData) { + requestedObjects.addAll(((GetData) payload).getInventory()); + } new NetworkMessage(payload).write(out); } catch (IOException e) { LOG.error(e.getMessage(), e); @@ -338,7 +337,7 @@ public class Connection { return writer; } - public enum Mode {SERVER, CLIENT} + public enum Mode {SERVER, CLIENT, SYNC} public enum State {CONNECTING, ACTIVE, DISCONNECTED} @@ -347,11 +346,15 @@ public class Connection { public void run() { try (Socket socket = Connection.this.socket) { initSocket(socket); - if (mode == CLIENT) { + if (mode == CLIENT || mode == SYNC) { send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); } while (state != DISCONNECTED) { - Thread.sleep(100); + if (requestedObjects.isEmpty()) { + Thread.sleep(1000); + } else { + Thread.sleep(100); + } try { NetworkMessage msg = Factory.getNetworkMessage(version, in); if (msg == null) @@ -377,6 +380,7 @@ public class Connection { send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); break; case CLIENT: + case SYNC: activateConnection(); break; } @@ -391,6 +395,7 @@ public class Connection { activateConnection(); break; case CLIENT: + case SYNC: // NO OP break; } @@ -407,7 +412,7 @@ public class Connection { + msg.getPayload().getCommand() + "'"); } } - if (socket.isClosed() || syncFinished(msg)) disconnect(); + if (socket.isClosed() || syncFinished(msg) || checkOpenRequests()) disconnect(); } catch (SocketTimeoutException ignore) { if (state == ACTIVE) { if (syncFinished(null)) disconnect(); @@ -429,16 +434,20 @@ public class Connection { } } + private boolean checkOpenRequests() { + return !requestedObjects.isEmpty() && lastObjectTime > 0 && (UnixTime.now() - lastObjectTime) > 2 * MINUTE; + } + public class WriterRunnable implements Runnable { @Override public void run() { try (Socket socket = Connection.this.socket) { initSocket(socket); while (state != DISCONNECTED) { - if (sendingQueue.size() > 0) { + if (!sendingQueue.isEmpty()) { send(sendingQueue.poll()); } else { - Thread.sleep(100); + Thread.sleep(1000); } } } catch (IOException | InterruptedException e) { 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 e934bdf..bd58d8e 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -19,6 +19,7 @@ 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.GetData; import ch.dissem.bitmessage.entity.NetworkMessage; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; @@ -41,8 +42,9 @@ import java.util.concurrent.*; import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT; import static ch.dissem.bitmessage.networking.Connection.Mode.SERVER; import static ch.dissem.bitmessage.networking.Connection.State.ACTIVE; -import static ch.dissem.bitmessage.networking.Connection.State.DISCONNECTED; import static ch.dissem.bitmessage.utils.DebugUtils.inc; +import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; +import static java.util.Collections.newSetFromMap; /** * Handles all the networky stuff. @@ -50,13 +52,14 @@ import static ch.dissem.bitmessage.utils.DebugUtils.inc; public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { public final static int NETWORK_MAGIC_NUMBER = 8; private final static Logger LOG = LoggerFactory.getLogger(DefaultNetworkHandler.class); - private final List<Connection> connections = new LinkedList<>(); + private static final Random RANDOM = new Random(); + private final Collection<Connection> connections = new ConcurrentLinkedQueue<>(); private final ExecutorService pool; private InternalContext ctx; private ServerSocket serverSocket; private volatile boolean running; - private ConcurrentMap<InventoryVector, Long> requestedObjects = new ConcurrentHashMap<>(); + private Set<InventoryVector> requestedObjects = newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(50_000)); public DefaultNetworkHandler() { pool = Executors.newCachedThreadPool(new ThreadFactory() { @@ -134,6 +137,8 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } }); pool.execute(new Runnable() { + public Connection initialConnection; + @Override public void run() { try { @@ -152,25 +157,38 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) { Connection c = iterator.next(); - if (now - c.getStartTime() > ctx.getConnectionTTL()) { + // Just in case they were all created at the same time, don't disconnect + // all at once. + if (now - c.getStartTime() + RANDOM.nextInt(5 * MINUTE) > ctx.getConnectionTTL()) { c.disconnect(); } - if (c.getState() == DISCONNECTED) { - // Remove the current element from the iterator and the list. - iterator.remove(); - } - if (c.getState() == ACTIVE) { - active++; + switch (c.getState()) { + case DISCONNECTED: + iterator.remove(); + break; + case ACTIVE: + active++; + break; } } } if (active < NETWORK_MAGIC_NUMBER) { List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses( NETWORK_MAGIC_NUMBER - active, ctx.getStreams()); + boolean first = active == 0 && initialConnection == null; for (NetworkAddress address : addresses) { - startConnection(new Connection(ctx, CLIENT, address, listener, requestedObjects)); + Connection c = new Connection(ctx, CLIENT, address, listener, requestedObjects); + if (first) { + initialConnection = c; + first = false; + } + startConnection(c); } Thread.sleep(10000); + } else if (initialConnection != null) { + initialConnection.disconnect(); + initialConnection = null; + Thread.sleep(10000); } else { Thread.sleep(30000); } @@ -209,6 +227,7 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { c.disconnect(); } } + requestedObjects.clear(); } private void startConnection(Connection c) { @@ -272,7 +291,56 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } return new Property("network", null, new Property("connectionManager", running ? "running" : "stopped"), - new Property("connections", null, streamProperties) + new Property("connections", null, streamProperties), + new Property("requestedObjects", requestedObjects.size()) ); } + + void request(Set<InventoryVector> inventoryVectors) { + if (!running || inventoryVectors.isEmpty()) return; + synchronized (connections) { + Map<Connection, List<InventoryVector>> distribution = new HashMap<>(); + for (Connection connection : connections) { + if (connection.getState() == ACTIVE) { + distribution.put(connection, new LinkedList<InventoryVector>()); + } + } + Iterator<InventoryVector> iterator = inventoryVectors.iterator(); + boolean firstRound = true; + InventoryVector next = iterator.next(); + while (firstRound || iterator.hasNext()) { + if (!firstRound) { + next = iterator.next(); + firstRound = true; + } else { + firstRound = false; + } + for (Connection connection : distribution.keySet()) { + if (connection.knowsOf(next)) { + List<InventoryVector> ivs = distribution.get(connection); + if (ivs.size() == 50_000) { + connection.send(new GetData.Builder().inventory(ivs).build()); + ivs.clear(); + } + ivs.add(next); + iterator.remove(); + + if (iterator.hasNext()) { + next = iterator.next(); + firstRound = true; + } else { + firstRound = false; + break; + } + } + } + } + for (Connection connection : distribution.keySet()) { + List<InventoryVector> ivs = distribution.get(connection); + if (!ivs.isEmpty()) { + connection.send(new GetData.Builder().inventory(ivs).build()); + } + } + } + } } diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 499e377..e850ffb 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -101,7 +101,7 @@ public class NetworkHandlerTest { Property status; do { Thread.yield(); - status = node.status().getProperty("network").getProperty("connections").getProperty("stream 0"); + status = node.status().getProperty("network", "connections", "stream 0"); } while (status == null); assertEquals(1, status.getProperty("outgoing").getValue()); } finally { @@ -109,7 +109,7 @@ public class NetworkHandlerTest { } } - @Test(timeout = 5_000) + @Test(timeout = 10_000) public void ensureObjectsAreSynchronizedIfBothHaveObjects() throws Exception { peerInventory.init( "V4Pubkey.payload", From ac6f291964c8ecd2bbb7a944af2b200d48a08878 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 17 Jan 2016 05:42:48 +0100 Subject: [PATCH 35/42] Renamed module 'domain' to 'core' to make its purpose more clear --- README.md | 5 ++++- {domain => core}/build.gradle | 4 ++-- .../ch/dissem/bitmessage/BitmessageContext.java | 0 .../dissem/bitmessage/DefaultMessageListener.java | 0 .../java/ch/dissem/bitmessage/InternalContext.java | 0 .../java/ch/dissem/bitmessage/MessageCallback.java | 0 .../ch/dissem/bitmessage/ProofOfWorkService.java | 0 .../main/java/ch/dissem/bitmessage/entity/Addr.java | 0 .../dissem/bitmessage/entity/BitmessageAddress.java | 0 .../ch/dissem/bitmessage/entity/CustomMessage.java | 0 .../java/ch/dissem/bitmessage/entity/Encrypted.java | 0 .../java/ch/dissem/bitmessage/entity/GetData.java | 0 .../main/java/ch/dissem/bitmessage/entity/Inv.java | 0 .../ch/dissem/bitmessage/entity/MessagePayload.java | 0 .../ch/dissem/bitmessage/entity/NetworkMessage.java | 0 .../ch/dissem/bitmessage/entity/ObjectMessage.java | 0 .../java/ch/dissem/bitmessage/entity/Plaintext.java | 0 .../dissem/bitmessage/entity/PlaintextHolder.java | 0 .../ch/dissem/bitmessage/entity/Streamable.java | 0 .../java/ch/dissem/bitmessage/entity/VerAck.java | 0 .../java/ch/dissem/bitmessage/entity/Version.java | 0 .../dissem/bitmessage/entity/payload/Broadcast.java | 0 .../dissem/bitmessage/entity/payload/CryptoBox.java | 0 .../bitmessage/entity/payload/GenericPayload.java | 0 .../dissem/bitmessage/entity/payload/GetPubkey.java | 0 .../ch/dissem/bitmessage/entity/payload/Msg.java | 0 .../bitmessage/entity/payload/ObjectPayload.java | 0 .../bitmessage/entity/payload/ObjectType.java | 0 .../ch/dissem/bitmessage/entity/payload/Pubkey.java | 0 .../dissem/bitmessage/entity/payload/V2Pubkey.java | 0 .../dissem/bitmessage/entity/payload/V3Pubkey.java | 0 .../bitmessage/entity/payload/V4Broadcast.java | 0 .../dissem/bitmessage/entity/payload/V4Pubkey.java | 0 .../bitmessage/entity/payload/V5Broadcast.java | 0 .../entity/valueobject/InventoryVector.java | 0 .../dissem/bitmessage/entity/valueobject/Label.java | 0 .../entity/valueobject/NetworkAddress.java | 0 .../bitmessage/entity/valueobject/PrivateKey.java | 0 .../exception/AddressFormatException.java | 0 .../exception/DecryptionFailedException.java | 0 .../exception/InsufficientProofOfWorkException.java | 0 .../dissem/bitmessage/exception/NodeException.java | 0 .../java/ch/dissem/bitmessage/factory/Factory.java | 0 .../dissem/bitmessage/factory/V3MessageFactory.java | 0 .../bitmessage/ports/AbstractCryptography.java | 0 .../dissem/bitmessage/ports/AddressRepository.java | 0 .../ch/dissem/bitmessage/ports/Cryptography.java | 0 .../bitmessage/ports/CustomCommandHandler.java | 0 .../java/ch/dissem/bitmessage/ports/Inventory.java | 0 .../dissem/bitmessage/ports/MemoryNodeRegistry.java | 0 .../dissem/bitmessage/ports/MessageRepository.java | 0 .../bitmessage/ports/MultiThreadedPOWEngine.java | 0 .../ch/dissem/bitmessage/ports/NetworkHandler.java | 0 .../ch/dissem/bitmessage/ports/NodeRegistry.java | 0 .../dissem/bitmessage/ports/ProofOfWorkEngine.java | 0 .../bitmessage/ports/ProofOfWorkRepository.java | 0 .../ch/dissem/bitmessage/ports/SimplePOWEngine.java | 0 .../ch/dissem/bitmessage/utils/AccessCounter.java | 0 .../java/ch/dissem/bitmessage/utils/Base58.java | 0 .../main/java/ch/dissem/bitmessage/utils/Bytes.java | 0 .../ch/dissem/bitmessage/utils/CallbackWaiter.java | 0 .../ch/dissem/bitmessage/utils/Collections.java | 0 .../java/ch/dissem/bitmessage/utils/DebugUtils.java | 0 .../java/ch/dissem/bitmessage/utils/Decode.java | 0 .../java/ch/dissem/bitmessage/utils/Encode.java | 0 .../java/ch/dissem/bitmessage/utils/Numbers.java | 0 .../java/ch/dissem/bitmessage/utils/Points.java | 0 .../java/ch/dissem/bitmessage/utils/Property.java | 0 .../java/ch/dissem/bitmessage/utils/Singleton.java | 0 .../java/ch/dissem/bitmessage/utils/Strings.java | 0 .../java/ch/dissem/bitmessage/utils/UnixTime.java | 0 {domain => core}/src/main/resources/nodes.txt | 0 .../java/ch/dissem/bitmessage/DecryptionTest.java | 0 .../java/ch/dissem/bitmessage/EncryptionTest.java | 0 .../java/ch/dissem/bitmessage/SignatureTest.java | 0 .../bitmessage/entity/BitmessageAddressTest.java | 0 .../dissem/bitmessage/entity/SerializationTest.java | 0 .../bitmessage/ports/ProofOfWorkEngineTest.java | 0 .../java/ch/dissem/bitmessage/utils/BytesTest.java | 0 .../ch/dissem/bitmessage/utils/CollectionsTest.java | 0 .../java/ch/dissem/bitmessage/utils/DecodeTest.java | 0 .../java/ch/dissem/bitmessage/utils/EncodeTest.java | 0 .../ch/dissem/bitmessage/utils/StringsTest.java | 0 .../java/ch/dissem/bitmessage/utils/TestBase.java | 0 .../java/ch/dissem/bitmessage/utils/TestUtils.java | 0 .../BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ.pubkey | Bin .../BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey | Bin .../BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h.pubkey | Bin {domain => core}/src/test/resources/V1Msg.payload | Bin .../src/test/resources/V1MsgStrangeData.payload | Bin .../src/test/resources/V2GetPubkey.payload | Bin .../src/test/resources/V2Pubkey.payload | Bin .../src/test/resources/V3GetPubkey.payload | Bin .../src/test/resources/V3Pubkey.payload | Bin .../src/test/resources/V4Broadcast.payload | Bin .../src/test/resources/V4GetPubkey.payload | Bin .../src/test/resources/V4Pubkey.payload | Bin .../src/test/resources/V5Broadcast.payload | Bin cryptography-bc/build.gradle | 2 +- cryptography-sc/build.gradle | 2 +- demo/build.gradle | 2 +- extensions/build.gradle | 4 ++-- networking/build.gradle | 4 ++-- repositories/build.gradle | 4 ++-- settings.gradle | 2 +- wif/build.gradle | 2 +- 106 files changed, 17 insertions(+), 14 deletions(-) rename {domain => core}/build.gradle (89%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/BitmessageContext.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/InternalContext.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/MessageCallback.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/Addr.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/GetData.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/Inv.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/PlaintextHolder.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/Streamable.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/VerAck.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/Version.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/Msg.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectType.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/V2Pubkey.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/payload/V5Broadcast.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/valueobject/NetworkAddress.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/exception/NodeException.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/factory/Factory.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/Inventory.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/NodeRegistry.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Base58.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Bytes.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Collections.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/DebugUtils.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Decode.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Encode.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Numbers.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Points.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Property.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Singleton.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/Strings.java (100%) rename {domain => core}/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java (100%) rename {domain => core}/src/main/resources/nodes.txt (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/DecryptionTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/EncryptionTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/SignatureTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/utils/CollectionsTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/utils/TestBase.java (100%) rename {domain => core}/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java (100%) rename {domain => core}/src/test/resources/BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ.pubkey (100%) rename {domain => core}/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey (100%) rename {domain => core}/src/test/resources/BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h.pubkey (100%) rename {domain => core}/src/test/resources/V1Msg.payload (100%) rename {domain => core}/src/test/resources/V1MsgStrangeData.payload (100%) rename {domain => core}/src/test/resources/V2GetPubkey.payload (100%) rename {domain => core}/src/test/resources/V2Pubkey.payload (100%) rename {domain => core}/src/test/resources/V3GetPubkey.payload (100%) rename {domain => core}/src/test/resources/V3Pubkey.payload (100%) rename {domain => core}/src/test/resources/V4Broadcast.payload (100%) rename {domain => core}/src/test/resources/V4GetPubkey.payload (100%) rename {domain => core}/src/test/resources/V4Pubkey.payload (100%) rename {domain => core}/src/test/resources/V5Broadcast.payload (100%) diff --git a/README.md b/README.md index 19ce171..fb45cc6 100644 --- a/README.md +++ b/README.md @@ -29,18 +29,21 @@ Setup Add Jabit as Gradle dependency: ```Gradle -compile 'ch.dissem.jabit:jabit-domain:0.2.0' +compile 'ch.dissem.jabit:jabit-core:0.2.0' ``` Unless you want to implement your own, also add the following: ```Gradle compile 'ch.dissem.jabit:jabit-networking:0.2.0' compile 'ch.dissem.jabit:jabit-repositories:0.2.0' +compile 'ch.dissem.jabit:jabit-cryptography-bc:0.2.0' ``` And if you want to import from or export to the Wallet Import Format (used by PyBitmessage) you might also want to add: ```Gradle compile 'ch.dissem.jabit:jabit-wif:0.2.0' ``` +For Android clients use `jabit-cryptography-sc` instead of `jabit-cryptography-bc`. + Usage ----- diff --git a/domain/build.gradle b/core/build.gradle similarity index 89% rename from domain/build.gradle rename to core/build.gradle index 484abfd..8754cb3 100644 --- a/domain/build.gradle +++ b/core/build.gradle @@ -2,8 +2,8 @@ uploadArchives { repositories { mavenDeployer { pom.project { - name 'Jabit Domain' - artifactId = 'jabit-domain' + name 'Jabit Core' + artifactId = 'jabit-core' description 'A Java implementation of the Bitmessage protocol. This is the core part. You\'ll either need the networking and repositories modules, too, or implement your own.' } } diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java rename to core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java b/core/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java rename to core/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/core/src/main/java/ch/dissem/bitmessage/InternalContext.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/InternalContext.java rename to core/src/main/java/ch/dissem/bitmessage/InternalContext.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/MessageCallback.java b/core/src/main/java/ch/dissem/bitmessage/MessageCallback.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/MessageCallback.java rename to core/src/main/java/ch/dissem/bitmessage/MessageCallback.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java b/core/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java rename to core/src/main/java/ch/dissem/bitmessage/ProofOfWorkService.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Addr.java b/core/src/main/java/ch/dissem/bitmessage/entity/Addr.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/Addr.java rename to core/src/main/java/ch/dissem/bitmessage/entity/Addr.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java b/core/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java rename to core/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java b/core/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java rename to core/src/main/java/ch/dissem/bitmessage/entity/CustomMessage.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java b/core/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java rename to core/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/GetData.java b/core/src/main/java/ch/dissem/bitmessage/entity/GetData.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/GetData.java rename to core/src/main/java/ch/dissem/bitmessage/entity/GetData.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Inv.java b/core/src/main/java/ch/dissem/bitmessage/entity/Inv.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/Inv.java rename to core/src/main/java/ch/dissem/bitmessage/entity/Inv.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java b/core/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java rename to core/src/main/java/ch/dissem/bitmessage/entity/MessagePayload.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java b/core/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java rename to core/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java b/core/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java rename to core/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java b/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java rename to core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/PlaintextHolder.java b/core/src/main/java/ch/dissem/bitmessage/entity/PlaintextHolder.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/PlaintextHolder.java rename to core/src/main/java/ch/dissem/bitmessage/entity/PlaintextHolder.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java b/core/src/main/java/ch/dissem/bitmessage/entity/Streamable.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java rename to core/src/main/java/ch/dissem/bitmessage/entity/Streamable.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/VerAck.java b/core/src/main/java/ch/dissem/bitmessage/entity/VerAck.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/VerAck.java rename to core/src/main/java/ch/dissem/bitmessage/entity/VerAck.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Version.java b/core/src/main/java/ch/dissem/bitmessage/entity/Version.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/Version.java rename to core/src/main/java/ch/dissem/bitmessage/entity/Version.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/GenericPayload.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/GetPubkey.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Msg.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/Msg.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/Msg.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/Msg.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectPayload.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectType.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectType.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectType.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/ObjectType.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V2Pubkey.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/V2Pubkey.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/V2Pubkey.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/V2Pubkey.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/V3Pubkey.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/V4Pubkey.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V5Broadcast.java b/core/src/main/java/ch/dissem/bitmessage/entity/payload/V5Broadcast.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/payload/V5Broadcast.java rename to core/src/main/java/ch/dissem/bitmessage/entity/payload/V5Broadcast.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java rename to core/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java rename to core/src/main/java/ch/dissem/bitmessage/entity/valueobject/Label.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/NetworkAddress.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/NetworkAddress.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/NetworkAddress.java rename to core/src/main/java/ch/dissem/bitmessage/entity/valueobject/NetworkAddress.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java rename to core/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java b/core/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java rename to core/src/main/java/ch/dissem/bitmessage/exception/AddressFormatException.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java b/core/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java rename to core/src/main/java/ch/dissem/bitmessage/exception/DecryptionFailedException.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java b/core/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java rename to core/src/main/java/ch/dissem/bitmessage/exception/InsufficientProofOfWorkException.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/exception/NodeException.java b/core/src/main/java/ch/dissem/bitmessage/exception/NodeException.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/exception/NodeException.java rename to core/src/main/java/ch/dissem/bitmessage/exception/NodeException.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java b/core/src/main/java/ch/dissem/bitmessage/factory/Factory.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java rename to core/src/main/java/ch/dissem/bitmessage/factory/Factory.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java b/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java rename to core/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java b/core/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java rename to core/src/main/java/ch/dissem/bitmessage/ports/AbstractCryptography.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java b/core/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java rename to core/src/main/java/ch/dissem/bitmessage/ports/AddressRepository.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java b/core/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java rename to core/src/main/java/ch/dissem/bitmessage/ports/Cryptography.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java b/core/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java rename to core/src/main/java/ch/dissem/bitmessage/ports/CustomCommandHandler.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java b/core/src/main/java/ch/dissem/bitmessage/ports/Inventory.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/Inventory.java rename to core/src/main/java/ch/dissem/bitmessage/ports/Inventory.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java b/core/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java rename to core/src/main/java/ch/dissem/bitmessage/ports/MemoryNodeRegistry.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java b/core/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java rename to core/src/main/java/ch/dissem/bitmessage/ports/MessageRepository.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/core/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java rename to core/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java b/core/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java rename to core/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/NodeRegistry.java b/core/src/main/java/ch/dissem/bitmessage/ports/NodeRegistry.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/NodeRegistry.java rename to core/src/main/java/ch/dissem/bitmessage/ports/NodeRegistry.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java b/core/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java rename to core/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkEngine.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java b/core/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java rename to core/src/main/java/ch/dissem/bitmessage/ports/ProofOfWorkRepository.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java b/core/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java rename to core/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java b/core/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java rename to core/src/main/java/ch/dissem/bitmessage/utils/AccessCounter.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java b/core/src/main/java/ch/dissem/bitmessage/utils/Base58.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Base58.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Base58.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java b/core/src/main/java/ch/dissem/bitmessage/utils/Bytes.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Bytes.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Bytes.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java b/core/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java rename to core/src/main/java/ch/dissem/bitmessage/utils/CallbackWaiter.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Collections.java b/core/src/main/java/ch/dissem/bitmessage/utils/Collections.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Collections.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Collections.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/DebugUtils.java b/core/src/main/java/ch/dissem/bitmessage/utils/DebugUtils.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/DebugUtils.java rename to core/src/main/java/ch/dissem/bitmessage/utils/DebugUtils.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java b/core/src/main/java/ch/dissem/bitmessage/utils/Decode.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Decode.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Decode.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java b/core/src/main/java/ch/dissem/bitmessage/utils/Encode.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Encode.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Encode.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Numbers.java b/core/src/main/java/ch/dissem/bitmessage/utils/Numbers.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Numbers.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Numbers.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Points.java b/core/src/main/java/ch/dissem/bitmessage/utils/Points.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Points.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Points.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java b/core/src/main/java/ch/dissem/bitmessage/utils/Property.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Property.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Property.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java b/core/src/main/java/ch/dissem/bitmessage/utils/Singleton.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Singleton.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java b/core/src/main/java/ch/dissem/bitmessage/utils/Strings.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/Strings.java rename to core/src/main/java/ch/dissem/bitmessage/utils/Strings.java diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java b/core/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java similarity index 100% rename from domain/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java rename to core/src/main/java/ch/dissem/bitmessage/utils/UnixTime.java diff --git a/domain/src/main/resources/nodes.txt b/core/src/main/resources/nodes.txt similarity index 100% rename from domain/src/main/resources/nodes.txt rename to core/src/main/resources/nodes.txt diff --git a/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java b/core/src/test/java/ch/dissem/bitmessage/DecryptionTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java rename to core/src/test/java/ch/dissem/bitmessage/DecryptionTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java b/core/src/test/java/ch/dissem/bitmessage/EncryptionTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java rename to core/src/test/java/ch/dissem/bitmessage/EncryptionTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java b/core/src/test/java/ch/dissem/bitmessage/SignatureTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java rename to core/src/test/java/ch/dissem/bitmessage/SignatureTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java b/core/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java rename to core/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java b/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java rename to core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java b/core/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java rename to core/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java b/core/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java rename to core/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/CollectionsTest.java b/core/src/test/java/ch/dissem/bitmessage/utils/CollectionsTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/utils/CollectionsTest.java rename to core/src/test/java/ch/dissem/bitmessage/utils/CollectionsTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java b/core/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java rename to core/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java b/core/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java rename to core/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java b/core/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java rename to core/src/test/java/ch/dissem/bitmessage/utils/StringsTest.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java b/core/src/test/java/ch/dissem/bitmessage/utils/TestBase.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java rename to core/src/test/java/ch/dissem/bitmessage/utils/TestBase.java diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java b/core/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java similarity index 100% rename from domain/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java rename to core/src/test/java/ch/dissem/bitmessage/utils/TestUtils.java diff --git a/domain/src/test/resources/BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ.pubkey b/core/src/test/resources/BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ.pubkey similarity index 100% rename from domain/src/test/resources/BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ.pubkey rename to core/src/test/resources/BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ.pubkey diff --git a/domain/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey b/core/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey similarity index 100% rename from domain/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey rename to core/src/test/resources/BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey diff --git a/domain/src/test/resources/BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h.pubkey b/core/src/test/resources/BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h.pubkey similarity index 100% rename from domain/src/test/resources/BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h.pubkey rename to core/src/test/resources/BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h.pubkey diff --git a/domain/src/test/resources/V1Msg.payload b/core/src/test/resources/V1Msg.payload similarity index 100% rename from domain/src/test/resources/V1Msg.payload rename to core/src/test/resources/V1Msg.payload diff --git a/domain/src/test/resources/V1MsgStrangeData.payload b/core/src/test/resources/V1MsgStrangeData.payload similarity index 100% rename from domain/src/test/resources/V1MsgStrangeData.payload rename to core/src/test/resources/V1MsgStrangeData.payload diff --git a/domain/src/test/resources/V2GetPubkey.payload b/core/src/test/resources/V2GetPubkey.payload similarity index 100% rename from domain/src/test/resources/V2GetPubkey.payload rename to core/src/test/resources/V2GetPubkey.payload diff --git a/domain/src/test/resources/V2Pubkey.payload b/core/src/test/resources/V2Pubkey.payload similarity index 100% rename from domain/src/test/resources/V2Pubkey.payload rename to core/src/test/resources/V2Pubkey.payload diff --git a/domain/src/test/resources/V3GetPubkey.payload b/core/src/test/resources/V3GetPubkey.payload similarity index 100% rename from domain/src/test/resources/V3GetPubkey.payload rename to core/src/test/resources/V3GetPubkey.payload diff --git a/domain/src/test/resources/V3Pubkey.payload b/core/src/test/resources/V3Pubkey.payload similarity index 100% rename from domain/src/test/resources/V3Pubkey.payload rename to core/src/test/resources/V3Pubkey.payload diff --git a/domain/src/test/resources/V4Broadcast.payload b/core/src/test/resources/V4Broadcast.payload similarity index 100% rename from domain/src/test/resources/V4Broadcast.payload rename to core/src/test/resources/V4Broadcast.payload diff --git a/domain/src/test/resources/V4GetPubkey.payload b/core/src/test/resources/V4GetPubkey.payload similarity index 100% rename from domain/src/test/resources/V4GetPubkey.payload rename to core/src/test/resources/V4GetPubkey.payload diff --git a/domain/src/test/resources/V4Pubkey.payload b/core/src/test/resources/V4Pubkey.payload similarity index 100% rename from domain/src/test/resources/V4Pubkey.payload rename to core/src/test/resources/V4Pubkey.payload diff --git a/domain/src/test/resources/V5Broadcast.payload b/core/src/test/resources/V5Broadcast.payload similarity index 100% rename from domain/src/test/resources/V5Broadcast.payload rename to core/src/test/resources/V5Broadcast.payload diff --git a/cryptography-bc/build.gradle b/cryptography-bc/build.gradle index 0634154..c09b0db 100644 --- a/cryptography-bc/build.gradle +++ b/cryptography-bc/build.gradle @@ -11,7 +11,7 @@ uploadArchives { } dependencies { - compile project(':domain') + compile project(':core') compile 'org.bouncycastle:bcprov-jdk15on:1.52' testCompile 'junit:junit:4.11' testCompile 'org.mockito:mockito-core:1.10.19' diff --git a/cryptography-sc/build.gradle b/cryptography-sc/build.gradle index 2d4f82f..16771fc 100644 --- a/cryptography-sc/build.gradle +++ b/cryptography-sc/build.gradle @@ -11,7 +11,7 @@ uploadArchives { } dependencies { - compile project(':domain') + compile project(':core') compile 'com.madgag.spongycastle:prov:1.52.0.0' testCompile 'junit:junit:4.11' } diff --git a/demo/build.gradle b/demo/build.gradle index e520157..84d5907 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -21,7 +21,7 @@ task fatCapsule(type: FatCapsule) { } dependencies { - compile project(':domain') + compile project(':core') compile project(':networking') compile project(':repositories') compile project(':cryptography-bc') diff --git a/extensions/build.gradle b/extensions/build.gradle index c92eb96..d44f900 100644 --- a/extensions/build.gradle +++ b/extensions/build.gradle @@ -27,10 +27,10 @@ uploadArchives { } dependencies { - compile project(':domain') + compile project(':core') testCompile 'junit:junit:4.11' testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.mockito:mockito-core:1.10.19' - testCompile project(path: ':domain', configuration: 'testArtifacts') + testCompile project(path: ':core', configuration: 'testArtifacts') testCompile project(':cryptography-bc') } diff --git a/networking/build.gradle b/networking/build.gradle index 788bc4d..984f585 100644 --- a/networking/build.gradle +++ b/networking/build.gradle @@ -11,10 +11,10 @@ uploadArchives { } dependencies { - compile project(':domain') + compile project(':core') testCompile 'junit:junit:4.11' testCompile 'org.slf4j:slf4j-simple:1.7.12' testCompile 'org.mockito:mockito-core:1.10.19' - testCompile project(path: ':domain', configuration: 'testArtifacts') + testCompile project(path: ':core', configuration: 'testArtifacts') testCompile project(':cryptography-bc') } \ No newline at end of file diff --git a/repositories/build.gradle b/repositories/build.gradle index cfbc9ea..abb8651 100644 --- a/repositories/build.gradle +++ b/repositories/build.gradle @@ -2,7 +2,7 @@ uploadArchives { repositories { mavenDeployer { pom.project { - name 'Jabit Domain' + name 'Jabit Repositories' artifactId = 'jabit-repositories' description 'A Java implementation of the Bitmessage protocol. This contains JDBC implementations of the repositories.' } @@ -13,7 +13,7 @@ uploadArchives { sourceCompatibility = 1.8 dependencies { - compile project(':domain') + compile project(':core') compile 'org.flywaydb:flyway-core:3.2.1' testCompile 'junit:junit:4.12' testCompile 'com.h2database:h2:1.4.190' diff --git a/settings.gradle b/settings.gradle index 634c473..4caa813 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,6 @@ rootProject.name = 'Jabit' -include 'domain' +include 'core' include 'networking' diff --git a/wif/build.gradle b/wif/build.gradle index ef16682..93a0248 100644 --- a/wif/build.gradle +++ b/wif/build.gradle @@ -11,7 +11,7 @@ uploadArchives { } dependencies { - compile project(':domain') + compile project(':core') compile 'org.ini4j:ini4j:0.5.4' testCompile 'junit:junit:4.11' testCompile 'org.mockito:mockito-core:1.10.19' From 35077243b0a8f8ad83f39667c1dd983069940c28 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sun, 17 Jan 2016 07:13:29 +0100 Subject: [PATCH 36/42] Fixed synchronization --- .../java/ch/dissem/bitmessage/networking/Connection.java | 2 +- .../dissem/bitmessage/networking/NetworkHandlerTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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 3155645..176517b 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -350,7 +350,7 @@ class Connection { send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); } while (state != DISCONNECTED) { - if (requestedObjects.isEmpty()) { + if (mode != SYNC && state == ACTIVE && requestedObjects.isEmpty()) { Thread.sleep(1000); } else { Thread.sleep(100); diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index e850ffb..77cad46 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -94,7 +94,7 @@ public class NetworkHandlerTest { } while (node.isRunning()); } - @Test(timeout = 20_000) + @Test(timeout = 5_000) public void ensureNodesAreConnecting() { try { node.startup(); @@ -109,7 +109,7 @@ public class NetworkHandlerTest { } } - @Test(timeout = 10_000) + @Test(timeout = 5_000) public void ensureObjectsAreSynchronizedIfBothHaveObjects() throws Exception { peerInventory.init( "V4Pubkey.payload", @@ -128,7 +128,7 @@ public class NetworkHandlerTest { assertInventorySize(3, peerInventory); } - @Test(timeout = 10_000) + @Test(timeout = 5_000) public void ensureObjectsAreSynchronizedIfOnlyPeerHasObjects() throws Exception { peerInventory.init( "V4Pubkey.payload", @@ -145,7 +145,7 @@ public class NetworkHandlerTest { assertInventorySize(2, peerInventory); } - @Test(timeout = 10_000) + @Test(timeout = 5_000) public void ensureObjectsAreSynchronizedIfOnlyNodeHasObjects() throws Exception { peerInventory.init(); From e29310102f7805893cd5d95e25d0214ef2419082 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Tue, 19 Jan 2016 21:07:26 +0100 Subject: [PATCH 37/42] Updated UML diagram --- Bitmessage.uml | 1541 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 1261 insertions(+), 280 deletions(-) diff --git a/Bitmessage.uml b/Bitmessage.uml index 997ab34..598f220 100644 --- a/Bitmessage.uml +++ b/Bitmessage.uml @@ -3,325 +3,1306 @@ <ID>JAVA</ID> <OriginalElement /> <nodes> - <node x="1022.2999999999998" y="1241.5">ch.dissem.bitmessage.entity.Encrypted</node> - <node x="3262.195225464191" y="1703.088063660477">ch.dissem.bitmessage.ports.Inventory</node> - <node x="953.8409281305113" y="1454.0">ch.dissem.bitmessage.entity.payload.V4Pubkey</node> - <node x="3639.070833333333" y="552.0">ch.dissem.bitmessage.entity.Addr</node> - <node x="61.3125" y="574.5">ch.dissem.bitmessage.entity.payload.Broadcast</node> - <node x="1759.485339506172" y="1777.0">ch.dissem.bitmessage.factory.Factory</node> - <node x="3809.027006172839" y="132.0">ch.dissem.bitmessage.entity.valueobject.NetworkAddress</node> - <node x="1252.7999999999997" y="840.5">ch.dissem.bitmessage.entity.payload.V2Pubkey</node> - <node x="2422.195225464191" y="1725.088063660477">ch.dissem.bitmessage.ports.AddressRepository</node> - <node x="1237.2999999999997" y="1174.5">ch.dissem.bitmessage.entity.payload.V3Pubkey</node> - <node x="950.1249999999999" y="198.0">ch.dissem.bitmessage.entity.payload.ObjectPayload</node> - <node x="3265.695833333333" y="242.5">ch.dissem.bitmessage.entity.MessagePayload</node> - <node x="2347.852579365079" y="540.5">ch.dissem.bitmessage.entity.NetworkMessage</node> - <node x="3910.070833333333" y="475.0">ch.dissem.bitmessage.entity.Version</node> - <node x="3278.195225464191" y="1915.0880636604768">ch.dissem.bitmessage.BitmessageContext</node> - <node x="3995.195225464191" y="1758.088063660477">ch.dissem.bitmessage.ports.ProofOfWorkEngine</node> - <node x="1602.2999999999997" y="1153.0">ch.dissem.bitmessage.entity.BitmessageAddress</node> - <node x="217.375" y="1205.3604060913706">ch.dissem.bitmessage.entity.payload.UnencryptedMessage</node> - <node x="2394.152006172839" y="907.5">ch.dissem.bitmessage.factory.V3MessageFactory</node> - <node x="703.2999999999997" y="1230.5">ch.dissem.bitmessage.entity.payload.CryptoBox</node> - <node x="2841.277006172839" y="530.0">ch.dissem.bitmessage.entity.valueobject.InventoryVector</node> - <node x="40.0" y="1821.0">ch.dissem.bitmessage.entity.payload.V5Broadcast</node> - <node x="2026.7485780423274" y="874.0">ch.dissem.bitmessage.entity.valueobject.PrivateKey</node> - <node x="4063.945225464191" y="2026.0880636604768">ch.dissem.bitmessage.ports.MultiThreadedPOWEngine</node> - <node x="3440.320833333333" y="896.0">ch.dissem.bitmessage.entity.Inv</node> - <node x="1508.9249999999997" y="518.5">ch.dissem.bitmessage.entity.payload.Pubkey</node> - <node x="236.3125" y="541.0">ch.dissem.bitmessage.entity.payload.GetPubkey</node> - <node x="2357.445833333333" y="0.0">ch.dissem.bitmessage.entity.Streamable</node> - <node x="4050.4279840848803" y="1283.5018567639252">ch.dissem.bitmessage.entity.payload.ObjectType</node> - <node x="2715.4985780423276" y="786.0">ch.dissem.bitmessage.entity.ObjectMessage</node> - <node x="1835.727579365079" y="519.0">ch.dissem.bitmessage.entity.payload.GenericPayload</node> - <node x="3709.195225464191" y="1736.088063660477">ch.dissem.bitmessage.ports.NetworkHandler</node> - <node x="2544.852579365079" y="563.0">ch.dissem.bitmessage.entity.VerAck</node> - <node x="3169.320833333333" y="896.0">ch.dissem.bitmessage.entity.GetData</node> - <node x="447.0" y="1487.0">ch.dissem.bitmessage.entity.payload.Msg</node> - <node x="2817.195225464191" y="1747.088063660477">ch.dissem.bitmessage.ports.NodeRegistry</node> - <node x="0.0" y="1498.0">ch.dissem.bitmessage.entity.payload.V4Broadcast</node> + <node x="1818.2000000000003" y="313.0">ch.dissem.bitmessage.entity.valueobject.Label.Type</node> + <node x="5324.841228070176" y="1368.0">ch.dissem.bitmessage.networking.Connection.WriterRunnable</node> + <node x="6921.341228070176" y="1368.0">ch.dissem.bitmessage.entity.valueobject.NetworkAddress</node> + <node x="6705.341228070176" y="1368.0">ch.dissem.bitmessage.factory.V3MessageFactory</node> + <node x="3281.2958333333336" y="695.0">ch.dissem.bitmessage.ProofOfWorkService</node> + <node x="4125.573772883003" y="1368.0">ch.dissem.bitmessage.cryptography.bc.BouncyCryptography</node> + <node x="5470.131996533652" y="1127.0">ch.dissem.bitmessage.entity.ObjectMessage</node> + <node x="3970.5737728830027" y="1368.0">ch.dissem.bitmessage.repository.JdbcHelper</node> + <node x="4642.726882897288" y="936.0">ch.dissem.bitmessage.exception.InsufficientProofOfWorkException</node> + <node x="1314.3071062163358" y="313.0">ch.dissem.bitmessage.utils.AccessCounter</node> + <node x="3075.4000000000005" y="695.0">ch.dissem.bitmessage.MessageCallback</node> + <node x="6475.841228070176" y="1368.0">ch.dissem.bitmessage.networking.Connection.ReaderRunnable</node> + <node x="700.8568660309461" y="313.0">ch.dissem.bitmessage.entity.payload.V2Pubkey</node> + <node x="793.4083333333338" y="1127.0">ch.dissem.bitmessage.extensions.CryptoCustomMessage.SignatureCheckingInputStream</node> + <node x="1647.183132897289" y="695.0">ch.dissem.bitmessage.entity.Plaintext</node> + <node x="3045.735216230623" y="936.0">ch.dissem.bitmessage.InternalContext</node> + <node x="10.5" y="695.0">ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest</node> + <node x="2730.8810495639564" y="936.0">ch.dissem.bitmessage.ports.NetworkHandler.MessageListener</node> + <node x="1183.8250000000003" y="936.0">ch.dissem.bitmessage.entity.payload.Msg</node> + <node x="5873.841228070176" y="1368.0">ch.dissem.bitmessage.networking.Connection.Mode</node> + <node x="3620.073772883002" y="695.0">ch.dissem.bitmessage.utils.Singleton</node> + <node x="3582.948772883002" y="444.0">ch.dissem.bitmessage.ports.Cryptography</node> + <node x="4962.31390446195" y="444.0">ch.dissem.bitmessage.ports.ProofOfWorkRepository</node> + <node x="3599.601882897289" y="1539.0">ch.dissem.bitmessage.repository.JdbcMessageRepository</node> + <node x="4301.179822446958" y="1539.0">ch.dissem.bitmessage.repository.JdbcInventory</node> + <node x="5719.653728070176" y="1127.0">ch.dissem.bitmessage.exception.NodeException</node> + <node x="2292.7333333333336" y="695.0">ch.dissem.bitmessage.entity.payload.GetPubkey</node> + <node x="6192.841228070176" y="1127.0">ch.dissem.bitmessage.entity.GetData</node> + <node x="7132.716228070176" y="1539.0">ch.dissem.bitmessage.entity.Addr</node> + <node x="3867.573772883002" y="444.0">ch.dissem.bitmessage.InternalContext.ContextHolder</node> + <node x="1294.4083333333338" y="1127.0">ch.dissem.bitmessage.entity.CustomMessage</node> + <node x="1592.8250000000003" y="1127.0">ch.dissem.bitmessage.DefaultMessageListener</node> + <node x="2645.7333333333336" y="695.0">ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.Worker</node> + <node x="2444.7333333333336" y="695.0">ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.CallbackWrapper</node> + <node x="2771.7333333333336" y="695.0">ch.dissem.bitmessage.ports.CustomCommandHandler</node> + <node x="4469.976882897288" y="1127.0">ch.dissem.bitmessage.utils.Property</node> + <node x="318.09979956395637" y="1539.0">ch.dissem.bitmessage.repository.JdbcAddressRepository</node> + <node x="1564.5750000000003" y="1368.0">ch.dissem.bitmessage.BitmessageContext</node> + <node x="6582.341228070176" y="1127.0">ch.dissem.bitmessage.entity.VerAck</node> + <node x="3970.0737728830027" y="1127.0">ch.dissem.bitmessage.repository.JdbcConfig</node> + <node x="6639.341228070176" y="1539.0">ch.dissem.bitmessage.ports.MemoryNodeRegistry</node> + <node x="5551.954714912281" y="444.0">ch.dissem.bitmessage.entity.valueobject.InventoryVector</node> + <node x="327.2000000000003" y="936.0">ch.dissem.bitmessage.entity.payload.V4Pubkey</node> + <node x="57.5" y="1368.0">ch.dissem.bitmessage.entity.payload.V5Broadcast</node> + <node x="6475.841228070176" y="1127.0">ch.dissem.bitmessage.entity.Inv</node> + <node x="2074.7333333333336" y="695.0">ch.dissem.bitmessage.ports.AddressRepository</node> + <node x="904.8568660309461" y="222.0">ch.dissem.bitmessage.entity.payload.Pubkey</node> + <node x="56.375" y="444.0">ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request</node> + <node x="3168.5839493642798" y="444.0">ch.dissem.bitmessage.ports.MessageRepository</node> + <node x="1643.0750000000003" y="1539.0">ch.dissem.bitmessage.wif.WifExporter</node> + <node x="1063.731866030946" y="313.0">ch.dissem.bitmessage.entity.valueobject.PrivateKey</node> + <node x="2357.5893828972894" y="936.0">ch.dissem.bitmessage.ports.MultiThreadedPOWEngine</node> + <node x="671.4083333333338" y="1127.0">ch.dissem.bitmessage.extensions.CryptoCustomMessage.Reader</node> + <node x="249.5" y="444.0">ch.dissem.bitmessage.entity.payload.V3Pubkey</node> + <node x="846.8568660309461" y="313.0">ch.dissem.bitmessage.entity.payload.Pubkey.Feature</node> + <node x="3762.073772883002" y="695.0">ch.dissem.bitmessage.ports.NetworkHandler</node> + <node x="4604.976882897288" y="1127.0">ch.dissem.bitmessage.ports.AbstractCryptography</node> + <node x="4360.573772883003" y="1368.0">ch.dissem.bitmessage.cryptography.sc.SpongyCryptography</node> + <node x="4257.198772883003" y="444.0">ch.dissem.bitmessage.ports.SimplePOWEngine</node> + <node x="5279.360964912281" y="695.0">ch.dissem.bitmessage.ports.NodeRegistry</node> + <node x="1447.9083333333335" y="1539.0">ch.dissem.bitmessage.wif.WifImporter</node> + <node x="6323.841228070176" y="1127.0">ch.dissem.bitmessage.entity.MessagePayload.Command</node> + <node x="5988.841228070176" y="1127.0">ch.dissem.bitmessage.entity.NetworkMessage</node> + <node x="1722.7833333333335" y="936.0">ch.dissem.bitmessage.entity.Plaintext.Encoding</node> + <node x="1680.9187500000003" y="444.0">ch.dissem.bitmessage.entity.Plaintext.Type</node> + <node x="551.3250000000003" y="695.0">ch.dissem.bitmessage.entity.payload.CryptoBox</node> + <node x="50.59979956395637" y="1539.0">ch.dissem.bitmessage.factory.Factory</node> + <node x="5513.841228070176" y="1368.0">ch.dissem.bitmessage.networking.DefaultNetworkHandler</node> + <node x="4568.448772883003" y="1539.0">ch.dissem.bitmessage.repository.JdbcProofOfWorkRepository</node> + <node x="1007.5333333333338" y="444.0">ch.dissem.bitmessage.entity.BitmessageAddress</node> + <node x="5766.841228070176" y="1368.0">ch.dissem.bitmessage.networking.Connection.State</node> + <node x="10.5" y="1127.0">ch.dissem.bitmessage.entity.payload.V4Broadcast</node> + <node x="405.3250000000003" y="695.0">ch.dissem.bitmessage.entity.Encrypted</node> + <node x="5808.4446460551835" y="1539.0">ch.dissem.bitmessage.networking.Connection</node> + <node x="2882.840439549669" y="313.0">ch.dissem.bitmessage.ports.ProofOfWorkEngine</node> + <node x="6308.341228070176" y="936.0">ch.dissem.bitmessage.entity.MessagePayload</node> + <node x="2082.307106216336" y="0.0">ch.dissem.bitmessage.entity.Streamable</node> + <node x="1595.7833333333335" y="936.0">ch.dissem.bitmessage.BitmessageContext.Listener</node> + <node x="4182.420873082679" y="695.0">ch.dissem.bitmessage.ports.Inventory</node> + <node x="2507.2333333333336" y="444.0">ch.dissem.bitmessage.ports.ProofOfWorkEngine.Callback</node> + <node x="6757.674561403509" y="222.0">ch.dissem.bitmessage.entity.payload.GenericPayload</node> + <node x="7006.716228070176" y="1539.0">ch.dissem.bitmessage.entity.Version</node> + <node x="2140.6125397493456" y="121.0">ch.dissem.bitmessage.entity.payload.ObjectPayload</node> + <node x="480.5571062163358" y="936.0">ch.dissem.bitmessage.entity.payload.Broadcast</node> + <node x="693.4361111111111" y="1368.0">ch.dissem.bitmessage.extensions.CryptoCustomMessage</node> + <node x="531.8071062163353" y="444.0">ch.dissem.bitmessage.exception.DecryptionFailedException</node> + <node x="1551.1583333333335" y="444.0">ch.dissem.bitmessage.entity.Plaintext.Status</node> + <node x="1106.4083333333338" y="695.0">ch.dissem.bitmessage.entity.PlaintextHolder</node> + <node x="1817.4500000000003" y="444.0">ch.dissem.bitmessage.entity.valueobject.Label</node> + <node x="4736.948772883003" y="1368.0">ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item</node> </nodes> <notes /> <edges> - <edge source="ch.dissem.bitmessage.entity.GetData" target="ch.dissem.bitmessage.entity.MessagePayload"> - <point x="62.75" y="-48.5" /> - <point x="0.0" y="26.0" /> + <edge source="ch.dissem.bitmessage.repository.JdbcProofOfWorkRepository" target="ch.dissem.bitmessage.ports.ProofOfWorkRepository"> + <point x="0.0" y="-20.5" /> + <point x="4706.448772883003" y="1519.0" /> + <point x="4726.448772883003" y="1519.0" /> + <point x="4726.448772883003" y="1298.0" /> + <point x="5247.735964912281" y="1298.0" /> + <point x="5247.735964912281" y="625.0" /> + <point x="5112.31390446195" y="625.0" /> + <point x="30.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.valueobject.PrivateKey" target="ch.dissem.bitmessage.entity.payload.Pubkey"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.NetworkHandler"> + <point x="55.666666666666515" y="-20.5" /> + <point x="3184.9018828972894" y="896.0" /> + <point x="3850.073772883002" y="896.0" /> + <point x="0.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.valueobject.NetworkAddress" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="0.0" y="-136.5" /> - <point x="4023.527006172839" y="92.0" /> - <point x="2555.258333333333" y="92.0" /> - <point x="92.3125" y="26.0" /> + <edge source="ch.dissem.bitmessage.extensions.CryptoCustomMessage" target="ch.dissem.bitmessage.entity.BitmessageAddress"> + <point x="77.66666666666652" y="-20.5" /> + <point x="887.6027777777776" y="1338.0" /> + <point x="1283.9083333333338" y="1338.0" /> + <point x="1283.9083333333338" y="675.0" /> + <point x="1121.1583333333338" y="675.0" /> + <point x="12.625" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.repository.JdbcMessageRepository" target="ch.dissem.bitmessage.repository.JdbcHelper"> + <point x="99.58333333333348" y="-20.5" /> + <point x="3818.6852162306222" y="1519.0" /> + <point x="4021.1987728830027" y="1519.0" /> + <point x="-16.875" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.NetworkMessage" target="ch.dissem.bitmessage.entity.MessagePayload"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.MessageRepository"> + <point x="-33.40000000000009" y="-20.5" /> + <point x="3095.835216230623" y="826.0" /> + <point x="3064.9000000000005" y="826.0" /> + <point x="3064.9000000000005" y="625.0" /> + <point x="3202.417282697613" y="625.0" /> + <point x="-67.66666666666652" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.payload.Broadcast" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> - <point x="0.0" y="-26.0" /> - <point x="138.8125" y="435.0" /> - <point x="980.7083333333334" y="435.0" /> - <point x="-152.91666666666652" y="70.5" /> + <edge source="ch.dissem.bitmessage.cryptography.sc.SpongyCryptography" target="ch.dissem.bitmessage.ports.AbstractCryptography"> + <point x="0.0" y="-20.5" /> + <point x="4470.073772883003" y="1258.0" /> + <point x="4772.226882897288" y="1258.0" /> + <point x="55.75" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.payload.V5Broadcast" target="ch.dissem.bitmessage.entity.payload.V4Broadcast"> - <point x="0.0" y="-48.5" /> - <point x="0.0" y="92.5" /> - </edge> - <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.ports.NetworkHandler"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.Addr" target="ch.dissem.bitmessage.entity.MessagePayload"> - <point x="-62.75" y="-48.5" /> - <point x="3701.820833333333" y="445.0" /> - <point x="3410.2672619047617" y="445.0" /> - <point x="52.57142857142867" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.VerAck"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine"> - <point x="0.0" y="-26.0" /> - <point x="68.75" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.valueobject.PrivateKey" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="78.25" y="-70.5" /> - <point x="2261.4985780423276" y="766.0" /> - <point x="2282.227579365079" y="766.0" /> - <point x="2282.227579365079" y="102.0" /> - <point x="2449.758333333333" y="102.0" /> - <point x="-13.1875" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.payload.V4Pubkey"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.VerAck" target="ch.dissem.bitmessage.entity.MessagePayload"> - <point x="0.0" y="-37.5" /> - <point x="2660.352579365079" y="445.0" /> - <point x="3305.1244047619048" y="445.0" /> - <point x="-52.571428571428555" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.V4Pubkey" target="ch.dissem.bitmessage.entity.payload.CryptoBox"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.payload.UnencryptedMessage"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.MessagePayload" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="0.0" y="-26.0" /> - <point x="3357.695833333333" y="102.0" /> - <point x="2528.883333333333" y="102.0" /> - <point x="65.9375" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.BitmessageAddress"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.ports.Inventory"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.V4Broadcast" target="ch.dissem.bitmessage.entity.payload.UnencryptedMessage"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.valueobject.InventoryVector" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="0.0" y="-70.5" /> - <point x="2980.277006172839" y="425.0" /> - <point x="2940.790244708994" y="425.0" /> - <point x="2940.790244708994" y="112.0" /> - <point x="2502.508333333333" y="112.0" /> - <point x="39.5625" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.ports.AddressRepository"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.ObjectPayload" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="0.0" y="-70.5" /> - <point x="1133.625" y="92.0" /> - <point x="2423.383333333333" y="92.0" /> - <point x="-39.5625" y="26.0" /> + <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.exception.NodeException"> + <point x="-81.66666666666697" y="-20.5" /> + <point x="6721.674561403509" y="1328.0" /> + <point x="5865.778728070176" y="1328.0" /> + <point x="62.625" y="20.5" /> </edge> <edge source="ch.dissem.bitmessage.entity.payload.V4Pubkey" target="ch.dissem.bitmessage.entity.payload.V3Pubkey"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.V2Pubkey" target="ch.dissem.bitmessage.entity.payload.Pubkey"> - <point x="0.0" y="-104.0" /> - <point x="1409.2999999999997" y="766.0" /> - <point x="1545.5499999999997" y="766.0" /> - <point x="-109.875" y="82.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.V3Pubkey" target="ch.dissem.bitmessage.entity.payload.V2Pubkey"> - <point x="0.0" y="-104.0" /> - <point x="0.0" y="104.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.V4Broadcast" target="ch.dissem.bitmessage.entity.payload.Broadcast"> - <point x="-106.75" y="-92.5" /> - <point x="106.75" y="1434.0" /> - <point x="138.8125" y="1434.0" /> - <point x="0.0" y="26.0" /> + <point x="-50.40000000000009" y="-20.5" /> + <point x="339.8000000000002" y="916.0" /> + <point x="312.5" y="916.0" /> + <point x="0.0" y="20.5" /> </edge> <edge source="ch.dissem.bitmessage.entity.payload.V4Pubkey" target="ch.dissem.bitmessage.entity.Encrypted"> - <point x="0.0" y="-136.5" /> - <point x="1199.3409281305112" y="1434.0" /> - <point x="1119.7999999999997" y="1434.0" /> - <point x="0.0" y="37.0" /> + <point x="0.0" y="-20.5" /> + <point x="390.2000000000003" y="816.0" /> + <point x="426.3250000000003" y="816.0" /> + <point x="-42.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.payload.CryptoBox" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="0.0" y="-48.0" /> - <point x="852.7999999999997" y="1123.0" /> - <point x="50.8125" y="1123.0" /> - <point x="50.8125" y="82.0" /> - <point x="2397.008333333333" y="82.0" /> - <point x="-65.9375" y="26.0" /> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.entity.valueobject.NetworkAddress"> + <point x="64.89473684210498" y="-20.5" /> + <point x="5941.8393828972885" y="1509.0" /> + <point x="6943.716228070176" y="1509.0" /> + <point x="-67.125" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.valueobject.PrivateKey"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.repository.JdbcInventory" target="ch.dissem.bitmessage.ports.Inventory"> + <point x="-19.75" y="-20.5" /> + <point x="4360.429822446958" y="1499.0" /> + <point x="4590.073772883003" y="1499.0" /> + <point x="4590.073772883003" y="1268.0" /> + <point x="5154.673464912281" y="1268.0" /> + <point x="5154.673464912281" y="906.0" /> + <point x="4273.920873082679" y="906.0" /> + <point x="30.5" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.BitmessageAddress" target="ch.dissem.bitmessage.entity.valueobject.PrivateKey"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.entity.MessagePayload" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="0.0" y="-20.5" /> + <point x="6398.341228070176" y="71.0" /> + <point x="2195.6404395496693" y="71.0" /> + <point x="45.333333333333485" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.AddressRepository"> + <point x="-77.9333333333334" y="-20.5" /> + <point x="3051.3018828972895" y="866.0" /> + <point x="2223.2333333333336" y="866.0" /> + <point x="49.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection.Mode" target="ch.dissem.bitmessage.networking.Connection"> + <point x="23.5" y="20.5" /> + <point x="5944.341228070176" y="1439.0" /> + <point x="5891.365698686763" y="1439.0" /> + <point x="14.421052631579187" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.utils.AccessCounter"> + <point x="49.0" y="-20.5" /> + <point x="6852.341228070176" y="1348.0" /> + <point x="6830.674561403509" y="1348.0" /> + <point x="6830.674561403509" y="384.0" /> + <point x="1456.9321062163358" y="384.0" /> + <point x="61.125" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.payload.V4Broadcast"> + <point x="-26.25" y="-20.5" /> + <point x="76.84979956395637" y="1509.0" /> + <point x="47.0" y="1509.0" /> + <point x="-36.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcMessageRepository" target="ch.dissem.bitmessage.InternalContext"> + <point x="-59.75" y="-20.5" /> + <point x="3659.351882897289" y="1489.0" /> + <point x="3129.235216230623" y="1489.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.CustomCommandHandler"> + <point x="-55.666666666666515" y="-20.5" /> + <point x="3073.5685495639564" y="846.0" /> + <point x="2899.2333333333336" y="846.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine" target="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.CallbackWrapper"> + <point x="-102.0" y="-20.5" /> + <point x="2383.0893828972894" y="876.0" /> + <point x="2489.9833333333336" y="876.0" /> + <point x="-45.25" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.PlaintextHolder"> + <point x="6.8333333333332575" y="-20.5" /> + <point x="41.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection.WriterRunnable" target="ch.dissem.bitmessage.networking.Connection"> + <point x="56.33333333333303" y="20.5" /> + <point x="5465.674561403509" y="1479.0" /> + <point x="5840.892014476236" y="1479.0" /> + <point x="-36.05263157894751" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.InternalContext"> + <point x="54.0" y="-20.5" /> + <point x="1717.5750000000003" y="1218.0" /> + <point x="1859.3250000000003" y="1218.0" /> + <point x="1859.3250000000003" y="1057.0" /> + <point x="3081.520930516337" y="1057.0" /> + <point x="-47.71428571428578" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext" target="ch.dissem.bitmessage.entity.BitmessageAddress"> + <point x="-49.4375" y="-20.5" /> + <point x="1654.245632897289" y="645.0" /> + <point x="1196.9083333333338" y="645.0" /> + <point x="88.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.V5Broadcast" target="ch.dissem.bitmessage.entity.payload.V4Broadcast"> + <point x="0.0" y="-20.5" /> + <point x="130.5" y="1348.0" /> + <point x="120.0" y="1348.0" /> + <point x="36.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.CustomMessage" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="44.75" y="-20.5" /> + <point x="1428.6583333333338" y="1077.0" /> + <point x="6317.341228070176" y="1077.0" /> + <point x="-81.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.Cryptography"> + <point x="-11.133333333333212" y="-20.5" /> + <point x="3118.1018828972897" y="826.0" /> + <point x="3270.7958333333336" y="826.0" /> + <point x="3270.7958333333336" y="575.0" /> + <point x="3602.573772883002" y="575.0" /> + <point x="-58.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.DefaultMessageListener" target="ch.dissem.bitmessage.ports.NetworkHandler.MessageListener"> + <point x="29.25" y="-20.5" /> + <point x="1739.0750000000003" y="1027.0" /> + <point x="2760.3810495639564" y="1027.0" /> + <point x="-59.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.valueobject.NetworkAddress" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="0.0" y="-20.5" /> + <point x="7010.841228070176" y="61.0" /> + <point x="2210.75155066078" y="61.0" /> + <point x="60.44444444444434" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Broadcast" target="ch.dissem.bitmessage.entity.PlaintextHolder"> + <point x="31.25" y="-20.5" /> + <point x="574.3071062163358" y="866.0" /> + <point x="1148.1583333333338" y="866.0" /> + <point x="-41.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.CryptoCustomMessage" target="ch.dissem.bitmessage.entity.payload.CryptoBox"> + <point x="-77.66666666666652" y="-20.5" /> + <point x="732.2694444444446" y="1338.0" /> + <point x="660.9083333333338" y="1338.0" /> + <point x="660.9083333333338" y="846.0" /> + <point x="640.7000000000003" y="846.0" /> + <point x="24.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcAddressRepository" target="ch.dissem.bitmessage.entity.payload.V4Pubkey"> + <point x="-29.25" y="-20.5" /> + <point x="405.84979956395637" y="1489.0" /> + <point x="421.7000000000003" y="1489.0" /> + <point x="31.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcProofOfWorkRepository" target="ch.dissem.bitmessage.repository.JdbcHelper"> + <point x="-92.0" y="-20.5" /> + <point x="4614.448772883003" y="1489.0" /> + <point x="4088.6987728830027" y="1489.0" /> + <point x="50.625" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.NodeRegistry"> + <point x="77.9333333333334" y="-20.5" /> + <point x="3207.1685495639563" y="916.0" /> + <point x="5318.110964912281" y="916.0" /> + <point x="-38.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection.ReaderRunnable" target="ch.dissem.bitmessage.entity.VerAck"> + <point x="44.0" y="-20.5" /> + <point x="-25.5" y="20.5" /> </edge> <edge source="ch.dissem.bitmessage.entity.ObjectMessage" target="ch.dissem.bitmessage.entity.MessagePayload"> - <point x="141.33333333333326" y="-158.5" /> - <point x="3068.8319113756606" y="766.0" /> - <point x="3242.277006172839" y="766.0" /> - <point x="3242.277006172839" y="455.0" /> - <point x="3331.410119047619" y="455.0" /> - <point x="-26.28571428571422" y="26.0" /> + <point x="56.33333333333303" y="-20.5" /> + <point x="5610.9653298669855" y="1087.0" /> + <point x="6335.341228070176" y="1087.0" /> + <point x="-63.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.payload.GetPubkey" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> - <point x="0.0" y="-59.5" /> - <point x="432.3125" y="445.0" /> - <point x="1041.875" y="445.0" /> - <point x="-91.75" y="70.5" /> + <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.Worker" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine.Callback"> + <point x="0.0" y="-20.5" /> + <point x="2698.2333333333336" y="615.0" /> + <point x="2577.2333333333336" y="615.0" /> + <point x="14.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.payload.GenericPayload"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.DefaultMessageListener"> + <point x="0.0" y="-20.5" /> + <point x="1663.5750000000003" y="1188.0" /> + <point x="1709.8250000000003" y="1188.0" /> + <point x="0.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.ObjectMessage" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.networking.Connection.Mode"> + <point x="7.210526315789139" y="-20.5" /> + <point x="5884.155172370973" y="1429.0" /> + <point x="5897.341228070176" y="1429.0" /> + <point x="-23.5" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.GetData" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.Inventory"> + <point x="66.80000000000018" y="-20.5" /> + <point x="3196.035216230623" y="906.0" /> + <point x="4212.920873082679" y="906.0" /> + <point x="-30.5" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> - <point x="-49.5" y="-103.5" /> - <point x="595.5" y="1434.0" /> - <point x="638.8125" y="1434.0" /> - <point x="638.8125" y="455.0" /> - <point x="1103.0416666666665" y="455.0" /> - <point x="-30.583333333333485" y="70.5" /> + <edge source="ch.dissem.bitmessage.ProofOfWorkService" target="ch.dissem.bitmessage.ports.MessageRepository"> + <point x="-41.80000000000018" y="-20.5" /> + <point x="3343.9958333333334" y="555.0" /> + <point x="3270.0839493642798" y="555.0" /> + <point x="0.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.payload.Pubkey" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> - <point x="0.0" y="-82.0" /> - <point x="1655.4249999999997" y="455.0" /> - <point x="1164.2083333333335" y="455.0" /> - <point x="30.583333333333485" y="70.5" /> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.networking.Connection.WriterRunnable"> + <point x="-43.26315789473665" y="-20.5" /> + <point x="5833.681488160447" y="1489.0" /> + <point x="5409.341228070176" y="1489.0" /> + <point x="0.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.entity.Version" target="ch.dissem.bitmessage.entity.MessagePayload"> - <point x="-97.75" y="-125.5" /> - <point x="4007.820833333333" y="435.0" /> - <point x="3436.5529761904763" y="435.0" /> - <point x="78.85714285714289" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.NetworkMessage"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.ObjectMessage" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.UnencryptedMessage" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="0.0" y="-70.5" /> - <point x="408.375" y="1133.0" /> - <point x="39.8125" y="1133.0" /> - <point x="39.8125" y="72.0" /> - <point x="2370.633333333333" y="72.0" /> - <point x="-92.3125" y="26.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.Addr" target="ch.dissem.bitmessage.entity.valueobject.NetworkAddress"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.payload.CryptoBox"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.GenericPayload" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> - <point x="0.0" y="-81.5" /> - <point x="2048.227579365079" y="445.0" /> - <point x="1225.375" y="445.0" /> - <point x="91.75" y="70.5" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.payload.V4Pubkey" target="ch.dissem.bitmessage.entity.payload.Pubkey"> - <point x="196.4000000000001" y="-136.5" /> - <point x="1395.7409281305113" y="1434.0" /> - <point x="1591.7999999999997" y="1434.0" /> - <point x="1591.7999999999997" y="766.0" /> - <point x="1618.7999999999997" y="766.0" /> - <point x="-36.625" y="82.0" /> - </edge> - <edge source="ch.dissem.bitmessage.entity.NetworkMessage" target="ch.dissem.bitmessage.entity.Streamable"> - <point x="-44.25" y="-60.0" /> - <point x="2392.102579365079" y="112.0" /> - <point x="2476.133333333333" y="112.0" /> - <point x="13.1875" y="26.0" /> + <edge source="ch.dissem.bitmessage.entity.payload.Broadcast" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> + <point x="-52.083333333333485" y="-20.5" /> + <point x="490.9737728830023" y="796.0" /> + <point x="239.0" y="796.0" /> + <point x="239.0" y="182.0" /> + <point x="2154.1125397493456" y="182.0" /> + <point x="-67.5" y="20.5" /> </edge> <edge source="ch.dissem.bitmessage.entity.Version" target="ch.dissem.bitmessage.entity.valueobject.NetworkAddress"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <point x="-26.5" y="-20.5" /> + <point x="22.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.valueobject.InventoryVector" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="0.0" y="-20.5" /> + <point x="5638.454714912281" y="91.0" /> + <point x="2165.418217327447" y="91.0" /> + <point x="15.111111111111313" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcAddressRepository" target="ch.dissem.bitmessage.repository.JdbcHelper"> + <point x="87.75" y="-20.5" /> + <point x="522.8497995639564" y="1509.0" /> + <point x="3987.4487728830027" y="1509.0" /> + <point x="-50.625" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.NetworkMessage" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="46.0" y="-20.5" /> + <point x="6126.841228070176" y="1097.0" /> + <point x="6353.341228070176" y="1097.0" /> + <point x="-45.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext" target="ch.dissem.bitmessage.entity.Plaintext.Type"> + <point x="-7.0625" y="-20.5" /> + <point x="1696.620632897289" y="675.0" /> + <point x="1702.1687500000003" y="675.0" /> + <point x="-21.25" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="16.33333333333303" y="-20.5" /> + <point x="6819.674561403509" y="505.0" /> + <point x="5717.091078548645" y="505.0" /> + <point x="78.63636363636397" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ProofOfWorkService" target="ch.dissem.bitmessage.ports.Cryptography"> + <point x="0.0" y="-20.5" /> + <point x="3385.7958333333336" y="585.0" /> + <point x="3641.823772883002" y="585.0" /> + <point x="-19.625" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest" target="ch.dissem.bitmessage.entity.BitmessageAddress"> + <point x="81.75" y="-20.5" /> + <point x="201.25" y="655.0" /> + <point x="1020.1583333333338" y="655.0" /> + <point x="-88.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.DefaultMessageListener" target="ch.dissem.bitmessage.BitmessageContext.Listener"> + <point x="-29.25" y="-20.5" /> + <point x="1680.5750000000003" y="1067.0" /> + <point x="1649.2833333333335" y="1067.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.utils.Property"> + <point x="90.0" y="-20.5" /> + <point x="1753.5750000000003" y="1238.0" /> + <point x="4498.726882897288" y="1238.0" /> + <point x="-28.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.utils.Singleton" target="ch.dissem.bitmessage.ports.Cryptography"> + <point x="0.0" y="-20.5" /> + <point x="19.625" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item" target="ch.dissem.bitmessage.ports.ProofOfWorkRepository"> + <point x="-20.75" y="-20.5" /> + <point x="4757.698772883003" y="1308.0" /> + <point x="5268.860964912281" y="1308.0" /> + <point x="5268.860964912281" y="615.0" /> + <point x="5172.31390446195" y="615.0" /> + <point x="90.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MemoryNodeRegistry" target="ch.dissem.bitmessage.ports.NodeRegistry"> + <point x="-55.5" y="-20.5" /> + <point x="6694.841228070176" y="886.0" /> + <point x="5395.610964912281" y="886.0" /> + <point x="38.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ProofOfWorkService" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine.Callback"> + <point x="-83.59999999999991" y="-20.5" /> + <point x="3302.1958333333337" y="565.0" /> + <point x="2605.2333333333336" y="565.0" /> + <point x="42.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="49.4375" y="-20.5" /> + <point x="1753.120632897289" y="635.0" /> + <point x="5559.818351275917" y="635.0" /> + <point x="-78.63636363636397" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.wif.WifImporter" target="ch.dissem.bitmessage.BitmessageContext"> + <point x="36.0" y="-20.5" /> + <point x="1555.9083333333335" y="1489.0" /> + <point x="1614.0750000000003" y="1489.0" /> + <point x="-49.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Addr" target="ch.dissem.bitmessage.entity.valueobject.NetworkAddress"> + <point x="-21.75" y="-20.5" /> + <point x="7154.466228070176" y="1509.0" /> + <point x="7077.966228070176" y="1509.0" /> + <point x="67.125" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcMessageRepository" target="ch.dissem.bitmessage.ports.MessageRepository"> + <point x="-19.916666666666515" y="-20.5" /> + <point x="3699.1852162306222" y="1479.0" /> + <point x="3609.573772883002" y="1479.0" /> + <point x="3609.573772883002" y="545.0" /> + <point x="3337.750616030946" y="545.0" /> + <point x="67.66666666666652" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Broadcast" target="ch.dissem.bitmessage.entity.Encrypted"> + <point x="-31.25" y="-20.5" /> + <point x="511.8071062163358" y="786.0" /> + <point x="468.3250000000003" y="786.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> + <point x="34.166666666666515" y="-20.5" /> + <point x="1258.9916666666668" y="896.0" /> + <point x="1915.9500000000003" y="896.0" /> + <point x="1915.9500000000003" y="202.0" /> + <point x="2208.1125397493456" y="202.0" /> + <point x="-13.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection.ReaderRunnable" target="ch.dissem.bitmessage.networking.Connection"> + <point x="58.66666666666697" y="20.5" /> + <point x="6622.507894736843" y="1499.0" /> + <point x="5934.628856581499" y="1499.0" /> + <point x="57.68421052631584" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext" target="ch.dissem.bitmessage.entity.Plaintext.Status"> + <point x="-35.3125" y="-20.5" /> + <point x="1668.370632897289" y="635.0" /> + <point x="1575.1583333333335" y="635.0" /> + <point x="-24.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.entity.NetworkMessage"> + <point x="101.9375" y="-20.5" /> + <point x="5732.278728070176" y="1348.0" /> + <point x="6019.507894736843" y="1348.0" /> + <point x="-61.33333333333303" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.valueobject.PrivateKey" target="ch.dissem.bitmessage.entity.payload.Pubkey"> + <point x="-32.0" y="-20.5" /> + <point x="1095.731866030946" y="283.0" /> + <point x="999.3568660309461" y="283.0" /> + <point x="42.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.cryptography.bc.BouncyCryptography" target="ch.dissem.bitmessage.ports.AbstractCryptography"> + <point x="0.0" y="-20.5" /> + <point x="4233.073772883003" y="1248.0" /> + <point x="4660.726882897288" y="1248.0" /> + <point x="-55.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.networking.DefaultNetworkHandler"> + <point x="-28.842105263158373" y="-20.5" /> + <point x="5848.102540792025" y="1469.0" /> + <point x="5552.674561403509" y="1469.0" /> + <point x="-77.66666666666697" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext.Encoding" target="ch.dissem.bitmessage.entity.Plaintext"> + <point x="0.0" y="-20.5" /> + <point x="1783.2833333333335" y="886.0" /> + <point x="1741.3497995639557" y="886.0" /> + <point x="37.66666666666674" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.DefaultMessageListener" target="ch.dissem.bitmessage.InternalContext"> + <point x="87.75" y="-20.5" /> + <point x="1797.5750000000003" y="1047.0" /> + <point x="3057.6637876591944" y="1047.0" /> + <point x="-71.57142857142844" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.AbstractCryptography" target="ch.dissem.bitmessage.exception.InsufficientProofOfWorkException"> + <point x="27.875" y="-20.5" /> + <point x="4744.351882897288" y="1057.0" /> + <point x="4800.726882897288" y="1057.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Broadcast" target="ch.dissem.bitmessage.entity.payload.CryptoBox"> + <point x="-10.416666666666629" y="-20.5" /> + <point x="532.6404395496693" y="846.0" /> + <point x="591.9500000000003" y="846.0" /> + <point x="-24.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcAddressRepository" target="ch.dissem.bitmessage.entity.BitmessageAddress"> + <point x="-87.75" y="-20.5" /> + <point x="347.34979956395637" y="1489.0" /> + <point x="272.0" y="1489.0" /> + <point x="272.0" y="675.0" /> + <point x="1070.6583333333338" y="675.0" /> + <point x="-37.875" y="20.5" /> </edge> <edge source="ch.dissem.bitmessage.entity.BitmessageAddress" target="ch.dissem.bitmessage.entity.payload.Pubkey"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <point x="-67.33333333333303" y="-20.5" /> + <point x="1041.2000000000007" y="384.0" /> + <point x="1053.231866030946" y="384.0" /> + <point x="1053.231866030946" y="293.0" /> + <point x="978.3568660309461" y="293.0" /> + <point x="21.0" y="20.5" /> </edge> - <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.ports.NodeRegistry"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <edge source="ch.dissem.bitmessage.entity.ObjectMessage" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="0.0" y="-20.5" /> + <point x="5554.631996533652" y="1067.0" /> + <point x="5549.424411881978" y="1067.0" /> + <point x="5549.424411881978" y="675.0" /> + <point x="5622.727442185008" y="675.0" /> + <point x="-15.727272727272975" y="20.5" /> </edge> <edge source="ch.dissem.bitmessage.entity.Inv" target="ch.dissem.bitmessage.entity.MessagePayload"> - <point x="62.75" y="-48.5" /> - <point x="3628.570833333333" y="455.0" /> - <point x="3383.9815476190474" y="455.0" /> - <point x="26.28571428571422" y="26.0" /> + <point x="-17.75" y="-20.5" /> + <point x="6493.591228070176" y="1097.0" /> + <point x="6425.341228070176" y="1097.0" /> + <point x="27.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Pubkey.Feature" target="ch.dissem.bitmessage.entity.payload.Pubkey"> + <point x="0.0" y="-20.5" /> + <point x="899.3568660309461" y="293.0" /> + <point x="936.3568660309461" y="293.0" /> + <point x="-21.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest" target="ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request"> + <point x="-27.25" y="-20.5" /> + <point x="92.25" y="675.0" /> + <point x="84.125" y="675.0" /> + <point x="-27.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext.Status" target="ch.dissem.bitmessage.entity.Plaintext"> + <point x="24.0" y="20.5" /> + <point x="1623.1583333333335" y="625.0" /> + <point x="1682.495632897289" y="625.0" /> + <point x="-21.1875" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.ports.NetworkHandler.MessageListener"> + <point x="36.0" y="-20.5" /> + <point x="1699.5750000000003" y="1208.0" /> + <point x="1848.3250000000003" y="1208.0" /> + <point x="1848.3250000000003" y="1037.0" /> + <point x="2819.3810495639564" y="1037.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcMessageRepository" target="ch.dissem.bitmessage.InternalContext.ContextHolder"> + <point x="19.916666666666515" y="-20.5" /> + <point x="3739.0185495639553" y="1489.0" /> + <point x="3948.573772883002" y="1489.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Version" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="26.5" y="-20.5" /> + <point x="7086.216228070176" y="1519.0" /> + <point x="7110.841228070176" y="1519.0" /> + <point x="7110.841228070176" y="1077.0" /> + <point x="6461.341228070176" y="1077.0" /> + <point x="63.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.CustomMessage" target="ch.dissem.bitmessage.utils.AccessCounter"> + <point x="-44.75" y="-20.5" /> + <point x="1339.1583333333338" y="424.0" /> + <point x="1416.1821062163358" y="424.0" /> + <point x="20.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.valueobject.Label" target="ch.dissem.bitmessage.entity.valueobject.Label.Type"> + <point x="-22.0" y="-20.5" /> + <point x="-21.25" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcMessageRepository" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="59.75" y="-20.5" /> + <point x="3778.851882897289" y="1499.0" /> + <point x="3959.573772883002" y="1499.0" /> + <point x="3959.573772883002" y="645.0" /> + <point x="5575.54562400319" y="645.0" /> + <point x="-62.90909090909099" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.CallbackWrapper" target="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine"> + <point x="45.25" y="20.5" /> + <point x="2580.4833333333336" y="886.0" /> + <point x="2434.0893828972894" y="886.0" /> + <point x="-51.0" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.Plaintext"> + <point x="20.5" y="-20.5" /> + <point x="1245.3250000000003" y="886.0" /> + <point x="1703.683132897289" y="886.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.entity.payload.GetPubkey"> + <point x="-36.0" y="-20.5" /> + <point x="1627.5750000000003" y="1318.0" /> + <point x="1562.6583333333335" y="1318.0" /> + <point x="1562.6583333333335" y="916.0" /> + <point x="2325.7333333333336" y="916.0" /> + <point x="-33.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MemoryNodeRegistry" target="ch.dissem.bitmessage.entity.valueobject.NetworkAddress"> + <point x="55.5" y="-20.5" /> + <point x="6805.841228070176" y="1519.0" /> + <point x="6988.466228070176" y="1519.0" /> + <point x="-22.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext.Listener" target="ch.dissem.bitmessage.BitmessageContext"> + <point x="-35.66666666666674" y="20.5" /> + <point x="1613.6166666666668" y="1067.0" /> + <point x="1582.3250000000003" y="1067.0" /> + <point x="1582.3250000000003" y="1308.0" /> + <point x="1645.5750000000003" y="1308.0" /> + <point x="-18.0" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.VerAck" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="0.0" y="-20.5" /> + <point x="6633.341228070176" y="1087.0" /> + <point x="6443.341228070176" y="1087.0" /> + <point x="45.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.entity.NetworkMessage"> + <point x="28.842105263157464" y="-20.5" /> + <point x="5905.786751318341" y="1459.0" /> + <point x="6080.841228070176" y="1459.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.Worker" target="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine"> + <point x="26.25" y="20.5" /> + <point x="2724.4833333333336" y="906.0" /> + <point x="2536.0893828972894" y="906.0" /> + <point x="51.0" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcAddressRepository" target="ch.dissem.bitmessage.ports.AddressRepository"> + <point x="29.25" y="-20.5" /> + <point x="464.34979956395637" y="1489.0" /> + <point x="440.59979956395637" y="1489.0" /> + <point x="440.59979956395637" y="1067.0" /> + <point x="1551.6583333333335" y="1067.0" /> + <point x="1551.6583333333335" y="906.0" /> + <point x="2124.2333333333336" y="906.0" /> + <point x="-49.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext.Type" target="ch.dissem.bitmessage.entity.Plaintext"> + <point x="21.25" y="20.5" /> + <point x="1744.6687500000003" y="605.0" /> + <point x="1710.745632897289" y="605.0" /> + <point x="7.0625" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.AbstractCryptography" target="ch.dissem.bitmessage.InternalContext.ContextHolder"> + <point x="83.625" y="-20.5" /> + <point x="4800.101882897288" y="1067.0" /> + <point x="4969.226882897288" y="1067.0" /> + <point x="4969.226882897288" y="575.0" /> + <point x="3980.9737728830023" y="575.0" /> + <point x="32.40000000000009" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.Encrypted"> + <point x="-34.16666666666674" y="-20.5" /> + <point x="1190.6583333333338" y="776.0" /> + <point x="510.3250000000003" y="776.0" /> + <point x="42.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.valueobject.PrivateKey"> + <point x="-43.75" y="-20.5" /> + <point x="59.34979956395637" y="1519.0" /> + <point x="0.0" y="1519.0" /> + <point x="0.0" y="394.0" /> + <point x="1085.0651993642796" y="394.0" /> + <point x="-42.666666666666515" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.AbstractCryptography" target="ch.dissem.bitmessage.InternalContext"> + <point x="-83.625" y="-20.5" /> + <point x="4632.851882897288" y="1027.0" /> + <point x="3153.0923590877655" y="1027.0" /> + <point x="23.857142857142662" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.BitmessageAddress"> + <point x="8.75" y="-20.5" /> + <point x="111.84979956395637" y="1479.0" /> + <point x="250.0" y="1479.0" /> + <point x="250.0" y="665.0" /> + <point x="1045.4083333333338" y="665.0" /> + <point x="-63.125" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.SimplePOWEngine" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine"> + <point x="0.0" y="-20.5" /> + <point x="4354.698772883003" y="424.0" /> + <point x="3063.965439549669" y="424.0" /> + <point x="77.625" y="20.5" /> </edge> <edge source="ch.dissem.bitmessage.entity.Inv" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> - <point x="0.0" y="0.0" /> - <point x="0.0" y="0.0" /> + <point x="17.75" y="-20.5" /> + <point x="6529.091228070176" y="515.0" /> + <point x="5701.363805821372" y="515.0" /> + <point x="62.90909090909099" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.GetData" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="-27.75" y="-20.5" /> + <point x="6220.591228070176" y="525.0" /> + <point x="5685.636533094099" y="525.0" /> + <point x="47.18181818181802" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.InternalContext"> + <point x="-72.8125" y="-20.5" /> + <point x="5557.528728070176" y="1208.0" /> + <point x="5423.9758960551835" y="1208.0" /> + <point x="5423.9758960551835" y="997.0" /> + <point x="3200.8066448020513" y="997.0" /> + <point x="71.57142857142844" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.MessagePayload.Command" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="0.0" y="-20.5" /> + <point x="-9.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ProofOfWorkService" target="ch.dissem.bitmessage.InternalContext.ContextHolder"> + <point x="41.80000000000018" y="-20.5" /> + <point x="3427.5958333333338" y="595.0" /> + <point x="3883.773772883002" y="595.0" /> + <point x="-64.80000000000018" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcHelper" target="ch.dissem.bitmessage.repository.JdbcConfig"> + <point x="0.0" y="-20.5" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.CallbackWrapper" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine.Callback"> + <point x="45.25" y="-20.5" /> + <point x="2580.4833333333336" y="625.0" /> + <point x="2549.2333333333336" y="625.0" /> + <point x="-14.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.ProofOfWorkEngine.Callback" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine"> + <point x="0.0" y="-20.5" /> + <point x="2563.2333333333336" y="414.0" /> + <point x="2908.715439549669" y="414.0" /> + <point x="-77.625" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.CryptoBox" target="ch.dissem.bitmessage.exception.DecryptionFailedException"> + <point x="0.0" y="-20.5" /> + <point x="616.3250000000003" y="635.0" /> + <point x="659.8071062163353" y="635.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="21.1875" y="-20.5" /> + <point x="1724.870632897289" y="615.0" /> + <point x="1776.4187500000003" y="615.0" /> + <point x="1776.4187500000003" y="101.0" /> + <point x="2135.1959951052245" y="101.0" /> + <point x="-15.111111111111313" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.networking.Connection.State"> + <point x="-7.210526315789139" y="-20.5" /> + <point x="5869.734119739394" y="1439.0" /> + <point x="5788.591228070176" y="1439.0" /> + <point x="-21.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcInventory" target="ch.dissem.bitmessage.repository.JdbcHelper"> + <point x="-59.25" y="-20.5" /> + <point x="4320.929822446958" y="1519.0" /> + <point x="4054.9487728830027" y="1519.0" /> + <point x="16.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine"> + <point x="102.0" y="-20.5" /> + <point x="2587.0893828972894" y="916.0" /> + <point x="2761.2333333333336" y="916.0" /> + <point x="2761.2333333333336" y="424.0" /> + <point x="2960.465439549669" y="424.0" /> + <point x="-25.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Addr" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="21.75" y="-20.5" /> + <point x="7197.966228070176" y="1067.0" /> + <point x="6479.341228070176" y="1067.0" /> + <point x="81.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.exception.NodeException"> + <point x="72.8125" y="-20.5" /> + <point x="5703.153728070176" y="1338.0" /> + <point x="5782.278728070176" y="1338.0" /> + <point x="-20.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.BitmessageAddress" target="ch.dissem.bitmessage.entity.valueobject.PrivateKey"> + <point x="0.0" y="-20.5" /> + <point x="1108.5333333333338" y="394.0" /> + <point x="1127.731866030946" y="394.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.GetData" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="27.75" y="-20.5" /> + <point x="6276.091228070176" y="1107.0" /> + <point x="6371.341228070176" y="1107.0" /> + <point x="-27.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcInventory" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="19.75" y="-20.5" /> + <point x="4399.929822446958" y="1509.0" /> + <point x="4601.073772883003" y="1509.0" /> + <point x="4601.073772883003" y="1278.0" /> + <point x="5165.673464912281" y="1278.0" /> + <point x="5165.673464912281" y="655.0" /> + <point x="5591.272896730463" y="655.0" /> + <point x="-47.18181818181802" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.ProofOfWorkRepository"> + <point x="44.5333333333333" y="-20.5" /> + <point x="3173.768549563956" y="876.0" /> + <point x="3579.340439549669" y="876.0" /> + <point x="3579.340439549669" y="625.0" /> + <point x="5052.31390446195" y="625.0" /> + <point x="-30.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.valueobject.PrivateKey" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="32.0" y="-20.5" /> + <point x="1159.731866030946" y="91.0" /> + <point x="2120.0848839941136" y="91.0" /> + <point x="-30.22222222222217" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.networking.Connection"> + <point x="0.0" y="20.5" /> + <point x="5630.341228070176" y="1459.0" /> + <point x="5855.313067107815" y="1459.0" /> + <point x="-21.631578947368325" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.ObjectMessage" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> + <point x="-56.33333333333303" y="-20.5" /> + <point x="5498.298663200319" y="1067.0" /> + <point x="5503.288048245615" y="1067.0" /> + <point x="5503.288048245615" y="192.0" /> + <point x="2262.1125397493456" y="192.0" /> + <point x="40.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.entity.payload.GetPubkey"> + <point x="-66.80000000000018" y="-20.5" /> + <point x="3062.4352162306227" y="856.0" /> + <point x="2391.7333333333336" y="856.0" /> + <point x="33.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.NetworkHandler.MessageListener" target="ch.dissem.bitmessage.ports.NetworkHandler"> + <point x="0.0" y="-20.5" /> + <point x="2819.3810495639564" y="886.0" /> + <point x="3791.4071062163357" y="886.0" /> + <point x="-58.666666666666515" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.entity.MessagePayload"> + <point x="36.05263157894751" y="-20.5" /> + <point x="5912.997277634131" y="1469.0" /> + <point x="6465.341228070176" y="1469.0" /> + <point x="6465.341228070176" y="1107.0" /> + <point x="6407.341228070176" y="1107.0" /> + <point x="9.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.payload.GenericPayload"> + <point x="81.66666666666697" y="-20.5" /> + <point x="6885.007894736843" y="1338.0" /> + <point x="6841.674561403509" y="1338.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ProofOfWorkService" target="ch.dissem.bitmessage.InternalContext"> + <point x="69.66666666666652" y="20.5" /> + <point x="3455.4625" y="856.0" /> + <point x="3151.5018828972898" y="856.0" /> + <point x="22.26666666666688" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.BitmessageAddress" target="ch.dissem.bitmessage.utils.AccessCounter"> + <point x="67.33333333333326" y="-20.5" /> + <point x="1175.8666666666668" y="414.0" /> + <point x="1375.4321062163358" y="414.0" /> + <point x="-20.375" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.V2Pubkey" target="ch.dissem.bitmessage.entity.payload.Pubkey"> + <point x="0.0" y="-20.5" /> + <point x="763.8568660309461" y="283.0" /> + <point x="915.3568660309461" y="283.0" /> + <point x="-42.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.NetworkMessage" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="-46.0" y="-20.5" /> + <point x="6034.841228070176" y="81.0" /> + <point x="2180.529328438558" y="81.0" /> + <point x="30.22222222222217" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcProofOfWorkRepository" target="ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item"> + <point x="92.0" y="-20.5" /> + <point x="4798.448772883003" y="1519.0" /> + <point x="4778.448772883003" y="1519.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item" target="ch.dissem.bitmessage.entity.ObjectMessage"> + <point x="20.75" y="-20.5" /> + <point x="4799.198772883003" y="1318.0" /> + <point x="5554.631996533652" y="1318.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Msg" target="ch.dissem.bitmessage.entity.payload.CryptoBox"> + <point x="-20.5" y="-20.5" /> + <point x="1204.3250000000003" y="766.0" /> + <point x="656.9500000000003" y="766.0" /> + <point x="40.625" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.DefaultMessageListener" target="ch.dissem.bitmessage.entity.payload.Msg"> + <point x="-87.75" y="-20.5" /> + <point x="1622.0750000000003" y="1047.0" /> + <point x="1245.3250000000003" y="1047.0" /> + <point x="20.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.InternalContext"> + <point x="-57.68421052631584" y="-20.5" /> + <point x="5819.260435528868" y="1509.0" /> + <point x="5314.341228070176" y="1509.0" /> + <point x="5314.341228070176" y="1007.0" /> + <point x="3176.9495019449087" y="1007.0" /> + <point x="47.71428571428578" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request" target="ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest"> + <point x="27.75" y="20.5" /> + <point x="139.625" y="675.0" /> + <point x="146.75" y="675.0" /> + <point x="27.25" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.repository.JdbcMessageRepository" target="ch.dissem.bitmessage.entity.valueobject.Label"> + <point x="-99.58333333333348" y="-20.5" /> + <point x="3619.5185495639553" y="1499.0" /> + <point x="1883.4500000000003" y="1499.0" /> + <point x="22.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.MessageCallback"> + <point x="-22.26666666666688" y="-20.5" /> + <point x="3106.968549563956" y="816.0" /> + <point x="3166.4000000000005" y="816.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.V4Broadcast" target="ch.dissem.bitmessage.entity.payload.Broadcast"> + <point x="0.0" y="-20.5" /> + <point x="83.5" y="1057.0" /> + <point x="543.0571062163358" y="1057.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.CryptoCustomMessage" target="ch.dissem.bitmessage.entity.CustomMessage"> + <point x="103.55555555555566" y="-20.5" /> + <point x="913.4916666666668" y="1348.0" /> + <point x="1383.9083333333338" y="1348.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.CryptoCustomMessage.SignatureCheckingInputStream" target="ch.dissem.bitmessage.extensions.CryptoCustomMessage"> + <point x="73.0" y="20.5" /> + <point x="1012.4083333333338" y="1318.0" /> + <point x="835.8249999999998" y="1318.0" /> + <point x="25.888888888888687" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.exception.NodeException"> + <point x="43.75" y="-20.5" /> + <point x="146.84979956395637" y="1499.0" /> + <point x="1808.339382897289" y="1499.0" /> + <point x="1808.339382897289" y="1328.0" /> + <point x="5740.528728070176" y="1328.0" /> + <point x="-62.625" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.CryptoBox" target="ch.dissem.bitmessage.utils.AccessCounter"> + <point x="43.333333333333485" y="-20.5" /> + <point x="659.6583333333338" y="645.0" /> + <point x="839.936072446958" y="645.0" /> + <point x="839.936072446958" y="404.0" /> + <point x="1334.6821062163358" y="404.0" /> + <point x="-61.125" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.CryptoBox" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="-43.333333333333485" y="-20.5" /> + <point x="572.9916666666668" y="645.0" /> + <point x="489.40833333333376" y="645.0" /> + <point x="489.40833333333376" y="81.0" /> + <point x="2104.9737728830023" y="81.0" /> + <point x="-45.333333333333485" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.ObjectPayload" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="0.0" y="-20.5" /> + <point x="2221.6125397493456" y="101.0" /> + <point x="2150.307106216336" y="101.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.BitmessageContext.Listener"> + <point x="18.0" y="-20.5" /> + <point x="1681.5750000000003" y="1198.0" /> + <point x="1837.3250000000003" y="1198.0" /> + <point x="1837.3250000000003" y="1017.0" /> + <point x="1684.9500000000003" y="1017.0" /> + <point x="35.66666666666674" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="21.631578947368325" y="-20.5" /> + <point x="5898.576225002552" y="1449.0" /> + <point x="5978.341228070176" y="1449.0" /> + <point x="5978.341228070176" y="535.0" /> + <point x="5669.909260366826" y="535.0" /> + <point x="31.45454545454504" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.GetPubkey" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> + <point x="0.0" y="-20.5" /> + <point x="2358.7333333333336" y="202.0" /> + <point x="2235.1125397493456" y="202.0" /> + <point x="13.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.CryptoCustomMessage.Reader" target="ch.dissem.bitmessage.extensions.CryptoCustomMessage"> + <point x="25.5" y="20.5" /> + <point x="747.9083333333338" y="1318.0" /> + <point x="784.0472222222224" y="1318.0" /> + <point x="-25.888888888888687" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext.ContextHolder" target="ch.dissem.bitmessage.InternalContext"> + <point x="-32.40000000000009" y="20.5" /> + <point x="3916.173772883002" y="605.0" /> + <point x="3500.7958333333336" y="605.0" /> + <point x="3500.7958333333336" y="866.0" /> + <point x="3162.635216230623" y="866.0" /> + <point x="33.40000000000009" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.payload.V4Pubkey"> + <point x="26.25" y="-20.5" /> + <point x="129.34979956395637" y="1489.0" /> + <point x="261.0" y="1489.0" /> + <point x="261.0" y="1047.0" /> + <point x="358.7000000000003" y="1047.0" /> + <point x="-31.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.ports.NetworkHandler"> + <point x="-43.6875" y="-20.5" /> + <point x="5586.653728070176" y="1288.0" /> + <point x="5649.631996533652" y="1288.0" /> + <point x="5649.631996533652" y="896.0" /> + <point x="3908.7404395496687" y="896.0" /> + <point x="58.666666666666515" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Broadcast" target="ch.dissem.bitmessage.entity.Plaintext"> + <point x="52.08333333333337" y="-20.5" /> + <point x="595.1404395496693" y="876.0" /> + <point x="1666.0164662306222" y="876.0" /> + <point x="-37.66666666666674" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.entity.valueobject.PrivateKey"> + <point x="-54.0" y="-20.5" /> + <point x="1609.5750000000003" y="1328.0" /> + <point x="1540.6583333333335" y="1328.0" /> + <point x="1540.6583333333335" y="394.0" /> + <point x="1170.3985326976126" y="394.0" /> + <point x="42.666666666666515" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.entity.BitmessageAddress"> + <point x="-72.0" y="-20.5" /> + <point x="1591.5750000000003" y="1338.0" /> + <point x="1529.6583333333335" y="1338.0" /> + <point x="1529.6583333333335" y="655.0" /> + <point x="1171.6583333333338" y="655.0" /> + <point x="63.125" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.CryptoCustomMessage" target="ch.dissem.bitmessage.extensions.CryptoCustomMessage.SignatureCheckingInputStream"> + <point x="0.0" y="-20.5" /> + <point x="809.9361111111111" y="1308.0" /> + <point x="866.4083333333338" y="1308.0" /> + <point x="-73.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.NetworkMessage"> + <point x="-49.0" y="-20.5" /> + <point x="6754.341228070176" y="1318.0" /> + <point x="6142.174561403509" y="1318.0" /> + <point x="61.33333333333303" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.GenericPayload" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> + <point x="0.0" y="-20.5" /> + <point x="6841.674561403509" y="182.0" /> + <point x="2289.1125397493456" y="182.0" /> + <point x="67.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.AbstractCryptography" target="ch.dissem.bitmessage.ports.Cryptography"> + <point x="-27.875" y="-20.5" /> + <point x="4688.601882897288" y="1017.0" /> + <point x="4632.226882897288" y="1017.0" /> + <point x="4632.226882897288" y="585.0" /> + <point x="3720.323772883002" y="585.0" /> + <point x="58.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.valueobject.Label.Type" target="ch.dissem.bitmessage.entity.valueobject.Label"> + <point x="21.25" y="20.5" /> + <point x="1881.9500000000003" y="424.0" /> + <point x="1883.4500000000003" y="424.0" /> + <point x="22.0" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection.State" target="ch.dissem.bitmessage.networking.Connection"> + <point x="21.75" y="20.5" /> + <point x="5832.091228070176" y="1429.0" /> + <point x="5876.9446460551835" y="1429.0" /> + <point x="0.0" y="-20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.V3Pubkey" target="ch.dissem.bitmessage.entity.payload.V2Pubkey"> + <point x="0.0" y="-20.5" /> + <point x="312.5" y="374.0" /> + <point x="763.8568660309461" y="374.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.Factory" target="ch.dissem.bitmessage.entity.payload.V5Broadcast"> + <point x="-8.75" y="-20.5" /> + <point x="94.34979956395637" y="1469.0" /> + <point x="130.5" y="1469.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ports.ProofOfWorkEngine"> + <point x="-44.5333333333333" y="-20.5" /> + <point x="3084.7018828972896" y="836.0" /> + <point x="3037.2333333333336" y="836.0" /> + <point x="3037.2333333333336" y="424.0" /> + <point x="3012.215439549669" y="424.0" /> + <point x="25.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.Plaintext" target="ch.dissem.bitmessage.entity.valueobject.Label"> + <point x="35.3125" y="-20.5" /> + <point x="1738.995632897289" y="625.0" /> + <point x="1839.4500000000003" y="625.0" /> + <point x="-22.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection.ReaderRunnable" target="ch.dissem.bitmessage.exception.NodeException"> + <point x="-44.0" y="-20.5" /> + <point x="6519.841228070176" y="1338.0" /> + <point x="5824.028728070176" y="1338.0" /> + <point x="20.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.wif.WifImporter" target="ch.dissem.bitmessage.entity.BitmessageAddress"> + <point x="-36.0" y="-20.5" /> + <point x="1483.9083333333335" y="665.0" /> + <point x="1146.4083333333338" y="665.0" /> + <point x="37.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.InternalContext.ContextHolder"> + <point x="-14.5625" y="-20.5" /> + <point x="5615.778728070176" y="1298.0" /> + <point x="5669.6946460551835" y="1298.0" /> + <point x="5669.6946460551835" y="565.0" /> + <point x="4013.3737728830024" y="565.0" /> + <point x="64.80000000000018" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.Pubkey" target="ch.dissem.bitmessage.entity.payload.ObjectPayload"> + <point x="0.0" y="-20.5" /> + <point x="957.3568660309461" y="192.0" /> + <point x="2181.1125397493456" y="192.0" /> + <point x="-40.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest" target="ch.dissem.bitmessage.entity.Streamable"> + <point x="-81.75" y="-20.5" /> + <point x="37.75" y="675.0" /> + <point x="24.375" y="675.0" /> + <point x="24.375" y="71.0" /> + <point x="2089.8626617718915" y="71.0" /> + <point x="-60.44444444444434" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.wif.WifExporter" target="ch.dissem.bitmessage.BitmessageContext"> + <point x="0.0" y="-20.5" /> + <point x="49.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.utils.Property"> + <point x="-101.9375" y="-20.5" /> + <point x="5528.403728070176" y="1218.0" /> + <point x="4556.226882897288" y="1218.0" /> + <point x="28.75" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine" target="ch.dissem.bitmessage.ports.MultiThreadedPOWEngine.Worker"> + <point x="0.0" y="-20.5" /> + <point x="2485.0893828972894" y="896.0" /> + <point x="2671.9833333333336" y="896.0" /> + <point x="-26.25" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.ports.NetworkHandler.MessageListener"> + <point x="-64.89473684210498" y="-20.5" /> + <point x="5812.0499092130785" y="1519.0" /> + <point x="4859.25140446195" y="1519.0" /> + <point x="4859.25140446195" y="1228.0" /> + <point x="4459.476882897288" y="1228.0" /> + <point x="4459.476882897288" y="1037.0" /> + <point x="2878.3810495639564" y="1037.0" /> + <point x="59.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.factory.V3MessageFactory" target="ch.dissem.bitmessage.entity.VerAck"> + <point x="-16.33333333333303" y="-20.5" /> + <point x="6787.007894736843" y="1308.0" /> + <point x="6658.841228070176" y="1308.0" /> + <point x="25.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.V4Pubkey" target="ch.dissem.bitmessage.entity.payload.Pubkey"> + <point x="-25.200000000000273" y="-20.5" /> + <point x="365.0" y="806.0" /> + <point x="386.0" y="806.0" /> + <point x="386.0" y="384.0" /> + <point x="962.3568660309461" y="384.0" /> + <point x="962.3568660309461" y="293.0" /> + <point x="957.3568660309461" y="293.0" /> + <point x="0.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.extensions.CryptoCustomMessage" target="ch.dissem.bitmessage.extensions.CryptoCustomMessage.Reader"> + <point x="-51.777777777777715" y="-20.5" /> + <point x="758.1583333333333" y="1328.0" /> + <point x="696.9083333333338" y="1328.0" /> + <point x="-25.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.InternalContext" target="ch.dissem.bitmessage.ProofOfWorkService"> + <point x="0.0" y="-20.5" /> + <point x="3129.235216230623" y="836.0" /> + <point x="3316.129166666667" y="836.0" /> + <point x="-69.66666666666652" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.entity.payload.V4Pubkey" target="ch.dissem.bitmessage.entity.payload.CryptoBox"> + <point x="25.19999999999999" y="-20.5" /> + <point x="415.4000000000001" y="826.0" /> + <point x="559.4500000000003" y="826.0" /> + <point x="-56.875" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.BitmessageContext" target="ch.dissem.bitmessage.entity.payload.Msg"> + <point x="-90.0" y="-20.5" /> + <point x="1573.5750000000003" y="1348.0" /> + <point x="1518.6583333333335" y="1348.0" /> + <point x="1518.6583333333335" y="1057.0" /> + <point x="1204.3250000000003" y="1057.0" /> + <point x="-20.5" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.DefaultNetworkHandler" target="ch.dissem.bitmessage.entity.valueobject.InventoryVector"> + <point x="43.6875" y="-20.5" /> + <point x="5674.028728070176" y="1318.0" /> + <point x="5709.153728070176" y="1318.0" /> + <point x="5709.153728070176" y="545.0" /> + <point x="5654.181987639554" y="545.0" /> + <point x="15.727272727272975" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.ProofOfWorkService" target="ch.dissem.bitmessage.ports.ProofOfWorkRepository"> + <point x="83.59999999999991" y="-20.5" /> + <point x="3469.3958333333335" y="615.0" /> + <point x="4992.31390446195" y="615.0" /> + <point x="-90.0" y="20.5" /> + </edge> + <edge source="ch.dissem.bitmessage.networking.Connection" target="ch.dissem.bitmessage.networking.Connection.ReaderRunnable"> + <point x="50.4736842105267" y="-20.5" /> + <point x="5927.41833026571" y="1489.0" /> + <point x="6563.841228070176" y="1489.0" /> + <point x="0.0" y="20.5" /> </edge> </edges> - <settings layout="Hierarchic Group" zoom="0.37884615384615383" x="630.736040609137" y="1186.0" /> + <settings layout="Hierarchic Group" zoom="0.25607734806629834" x="3610.0" y="790.0" /> <SelectedNodes /> <Categories> - <Category>Fields</Category> - <Category>Methods</Category> - <Category>Properties</Category> + <Category>Inner Classes</Category> </Categories> - <SCOPE>All</SCOPE> + <SCOPE>Production</SCOPE> <VISIBILITY>protected</VISIBILITY> </Diagram> From 733335ef4294b9ef76d0dfb91020a2566348666a Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Tue, 19 Jan 2016 21:09:46 +0100 Subject: [PATCH 38/42] Improved performance and network stability --- .../bitmessage/networking/Connection.java | 8 +++----- .../networking/DefaultNetworkHandler.java | 17 ++++++++++++----- .../bitmessage/repository/JdbcInventory.java | 5 ++++- 3 files changed, 19 insertions(+), 11 deletions(-) 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 176517b..76094ff 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -139,6 +139,9 @@ class Connection { @SuppressWarnings("RedundantIfStatement") private boolean syncFinished(NetworkMessage msg) { + if (mode != SYNC){ + return false; + } if (Thread.interrupted()) { return true; } @@ -239,11 +242,6 @@ class Connection { } finally { if (commonRequestedObjects.remove(objectMessage.getInventoryVector())) { LOG.debug("Received object that wasn't requested."); -// if (!requestedObjects.isEmpty()) { -// DebugUtils.saveToFile(objectMessage); -// LOG.debug(objectMessage.getInventoryVector() + " was not in " -// + requestedObjects.toString()); -// } } } break; 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 bd58d8e..d3bec17 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -43,16 +43,16 @@ import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT; import static ch.dissem.bitmessage.networking.Connection.Mode.SERVER; import static ch.dissem.bitmessage.networking.Connection.State.ACTIVE; import static ch.dissem.bitmessage.utils.DebugUtils.inc; -import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; import static java.util.Collections.newSetFromMap; /** * Handles all the networky stuff. */ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { - public final static int NETWORK_MAGIC_NUMBER = 8; private final static Logger LOG = LoggerFactory.getLogger(DefaultNetworkHandler.class); - private static final Random RANDOM = new Random(); + + public final static int NETWORK_MAGIC_NUMBER = 8; + private final Collection<Connection> connections = new ConcurrentLinkedQueue<>(); private final ExecutorService pool; private InternalContext ctx; @@ -155,12 +155,14 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { if (diff == 0) break; } } + boolean forcedDisconnect = false; for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) { Connection c = iterator.next(); // Just in case they were all created at the same time, don't disconnect // all at once. - if (now - c.getStartTime() + RANDOM.nextInt(5 * MINUTE) > ctx.getConnectionTTL()) { + if (!forcedDisconnect && now - c.getStartTime() > ctx.getConnectionTTL()) { c.disconnect(); + forcedDisconnect = true; } switch (c.getState()) { case DISCONNECTED: @@ -306,8 +308,13 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { } } Iterator<InventoryVector> iterator = inventoryVectors.iterator(); + InventoryVector next; + if (iterator.hasNext()) { + next = iterator.next(); + } else { + return; + } boolean firstRound = true; - InventoryVector next = iterator.next(); while (firstRound || iterator.hasNext()) { if (!firstRound) { next = iterator.next(); diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java index 9c9c9e7..3336475 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcInventory.java @@ -80,7 +80,7 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public List<InventoryVector> getMissing(List<InventoryVector> offer, long... streams) { for (long stream : streams) { - getCache(stream).forEach((iv, t) -> offer.remove(iv)); + offer.removeAll(getCache(stream).keySet()); } return offer; } @@ -132,6 +132,9 @@ public class JdbcInventory extends JdbcHelper implements Inventory { @Override public void storeObject(ObjectMessage object) { + if (getCache(object.getStream()).containsKey(object.getInventoryVector())) + return; + try (Connection connection = config.getConnection()) { PreparedStatement ps = connection.prepareStatement("INSERT INTO Inventory (hash, stream, expires, data, type, version) VALUES (?, ?, ?, ?, ?, ?)"); InventoryVector iv = object.getInventoryVector(); From 9f05af8bb7af3d87ce31e3a51535178cc439d9b0 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Thu, 21 Jan 2016 20:32:23 +0100 Subject: [PATCH 39/42] Finally fixed the bug that was haunting me for the last week. --- .../dissem/bitmessage/BitmessageContext.java | 1 + .../bitmessage/DefaultMessageListener.java | 40 +++++++++++-------- .../java/ch/dissem/bitmessage/entity/Inv.java | 6 +-- .../entity/valueobject/InventoryVector.java | 1 - .../bitmessage/factory/V3MessageFactory.java | 7 +++- .../bitmessage/entity/SerializationTest.java | 20 ++++++++++ .../dissem/bitmessage/demo/Application.java | 13 +++++- .../java/ch/dissem/bitmessage/demo/Main.java | 8 +++- .../bitmessage/networking/Connection.java | 24 ++++++----- .../networking/NetworkHandlerTest.java | 3 +- .../repository/JdbcMessageRepository.java | 5 ++- 11 files changed, 89 insertions(+), 39 deletions(-) diff --git a/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 46d76d7..28e8483 100644 --- a/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/core/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -157,6 +157,7 @@ public class BitmessageContext { .from(from) .to(to) .message(subject, message) + .labels(messages().getLabels(Label.Type.SENT)) .build(); if (to.getPubkey() == null) { tryToFindMatchingPubkey(to); diff --git a/core/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java b/core/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java index eb22f03..b2366e4 100644 --- a/core/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java +++ b/core/src/main/java/ch/dissem/bitmessage/DefaultMessageListener.java @@ -90,28 +90,32 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { address = ctx.getAddressRepository().findContact(pubkey.getRipe()); } if (address != null) { - address.setPubkey(pubkey); - LOG.info("Got pubkey for contact " + address); - ctx.getAddressRepository().save(address); - List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address); - LOG.info("Sending " + messages.size() + " messages for contact " + address); - for (Plaintext msg : messages) { - msg.setStatus(DOING_PROOF_OF_WORK); - ctx.getMessageRepository().save(msg); - ctx.send( - msg.getFrom(), - msg.getTo(), - new Msg(msg), - +2 * DAY - ); - msg.setStatus(SENT); - ctx.getMessageRepository().save(msg); - } + updatePubkey(address, pubkey); } } catch (DecryptionFailedException ignore) { } } + private void updatePubkey(BitmessageAddress address, Pubkey pubkey){ + address.setPubkey(pubkey); + LOG.info("Got pubkey for contact " + address); + ctx.getAddressRepository().save(address); + List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address); + LOG.info("Sending " + messages.size() + " messages for contact " + address); + for (Plaintext msg : messages) { + msg.setStatus(DOING_PROOF_OF_WORK); + ctx.getMessageRepository().save(msg); + ctx.send( + msg.getFrom(), + msg.getTo(), + new Msg(msg), + +2 * DAY + ); + msg.setStatus(SENT); + ctx.getMessageRepository().save(msg); + } + } + protected void receive(ObjectMessage object, Msg msg) throws IOException { for (BitmessageAddress identity : ctx.getAddressRepository().getIdentities()) { try { @@ -125,6 +129,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { msg.getPlaintext().setInventoryVector(object.getInventoryVector()); ctx.getMessageRepository().save(msg.getPlaintext()); listener.receive(msg.getPlaintext()); + updatePubkey(msg.getPlaintext().getFrom(), msg.getPlaintext().getFrom().getPubkey()); } break; } catch (DecryptionFailedException ignore) { @@ -148,6 +153,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener { broadcast.getPlaintext().setInventoryVector(object.getInventoryVector()); ctx.getMessageRepository().save(broadcast.getPlaintext()); listener.receive(broadcast.getPlaintext()); + updatePubkey(broadcast.getPlaintext().getFrom(), broadcast.getPlaintext().getFrom().getPubkey()); } } catch (DecryptionFailedException ignore) { } diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/Inv.java b/core/src/main/java/ch/dissem/bitmessage/entity/Inv.java index df1b380..0135ec0 100644 --- a/core/src/main/java/ch/dissem/bitmessage/entity/Inv.java +++ b/core/src/main/java/ch/dissem/bitmessage/entity/Inv.java @@ -44,10 +44,10 @@ public class Inv implements MessagePayload { } @Override - public void write(OutputStream stream) throws IOException { - Encode.varInt(inventory.size(), stream); + public void write(OutputStream out) throws IOException { + Encode.varInt(inventory.size(), out); for (InventoryVector iv : inventory) { - iv.write(stream); + iv.write(out); } } diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java index f87dd13..fc67422 100644 --- a/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java +++ b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java @@ -38,7 +38,6 @@ public class InventoryVector implements Streamable, Serializable { InventoryVector that = (InventoryVector) o; return Arrays.equals(hash, that.hash); - } @Override diff --git a/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java b/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java index 9e15b3d..d13e73e 100644 --- a/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java +++ b/core/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java @@ -44,6 +44,9 @@ class V3MessageFactory { findMagic(in); String command = getCommand(in); int length = (int) Decode.uint32(in); + if (length > 1600003) { + throw new NodeException("Payload of " + length + " bytes received, no more than 1600003 was expected."); + } byte[] checksum = Decode.bytes(in, 4); byte[] payloadBytes = Decode.bytes(in, length); @@ -191,10 +194,10 @@ class V3MessageFactory { private static String getCommand(InputStream stream) throws IOException { byte[] bytes = new byte[12]; - int end = -1; + int end = bytes.length; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte) stream.read(); - if (end == -1) { + if (end == bytes.length) { if (bytes[i] == 0) end = i; } else { if (bytes[i] != 0) throw new IOException("'\\0' padding expected for command"); diff --git a/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java b/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java index 03b7bc5..1bcb8e7 100644 --- a/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java +++ b/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.*; +import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.utils.TestBase; @@ -25,9 +26,11 @@ import org.junit.Test; import java.io.*; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Collections; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.*; public class SerializationTest extends TestBase { @@ -95,6 +98,23 @@ public class SerializationTest extends TestBase { assertEquals(p1, p2); } + @Test + public void ensureNetworkMessageIsSerializedAndDeserializedCorrectly() throws Exception { + ArrayList<InventoryVector> ivs = new ArrayList<>(50000); + for (int i = 0; i < 50000; i++) { + ivs.add(new InventoryVector(security().randomBytes(32))); + } + + Inv inv = new Inv.Builder().inventory(ivs).build(); + NetworkMessage before = new NetworkMessage(inv); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + before.write(out); + + NetworkMessage after = Factory.getNetworkMessage(3, new ByteArrayInputStream(out.toByteArray())); + Inv invAfter = (Inv) after.getPayload(); + assertEquals(ivs, invAfter.getInventory()); + } + private void doTest(String resourceName, int version, Class<?> expectedPayloadType) throws IOException { byte[] data = TestUtils.getBytes(resourceName); InputStream in = new ByteArrayInputStream(data); 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 f35bcc5..26d6652 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -28,6 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; +import java.net.InetAddress; import java.util.List; import java.util.Scanner; @@ -40,7 +41,7 @@ public class Application { private BitmessageContext ctx; - public Application() { + public Application(String syncServer, int syncPort) { JdbcConfig jdbcConfig = new JdbcConfig(); ctx = new BitmessageContext.Builder() .addressRepo(new JdbcAddressRepository(jdbcConfig)) @@ -63,7 +64,9 @@ public class Application { }) .build(); - ctx.startup(); + if (syncServer == null) { + ctx.startup(); + } scanner = new Scanner(System.in); @@ -75,6 +78,9 @@ public class Application { System.out.println("c) contacts"); System.out.println("s) subscriptions"); System.out.println("m) messages"); + if (syncServer != null) { + System.out.println("y) sync"); + } System.out.println("?) info"); System.out.println("e) exit"); @@ -99,6 +105,9 @@ public class Application { break; case "e": break; + case "y": + ctx.synchronize(InetAddress.getByName(syncServer), syncPort, 120, true); + break; default: System.out.println("Unknown command. Please try again."); } 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 a7b63d1..b0e114b 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -64,7 +64,7 @@ public class Main { new WifImporter(ctx, options.importWIF).importAll(); } } else { - new Application(); + new Application(options.syncServer, options.syncPort); } } @@ -74,5 +74,11 @@ public class Main { @Option(name = "-export", usage = "Export to WIF file.") private File exportWIF; + + @Option(name = "-syncServer", usage = "Use manual synchronization with the given server instead of starting a full node.") + private String syncServer; + + @Option(name = "-syncPort", usage = "Port to use for synchronisation") + private int syncPort = 8444; } } 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 76094ff..54bfee4 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -139,13 +139,13 @@ class Connection { @SuppressWarnings("RedundantIfStatement") private boolean syncFinished(NetworkMessage msg) { - if (mode != SYNC){ + if (mode != SYNC) { return false; } if (Thread.interrupted()) { return true; } - if (syncTimeout == 0 || state != ACTIVE) { + if (state != ACTIVE) { return false; } if (syncTimeout < UnixTime.now()) { @@ -204,10 +204,11 @@ class Connection { switch (messagePayload.getCommand()) { case INV: Inv inv = (Inv) messagePayload; + int originalSize = inv.getInventory().size(); updateIvCache(inv.getInventory()); List<InventoryVector> missing = ctx.getInventory().getMissing(inv.getInventory(), streams); missing.removeAll(commonRequestedObjects); - LOG.debug("Received inventory with " + inv.getInventory().size() + " elements, of which are " + LOG.debug("Received inventory with " + originalSize + " elements, of which are " + missing.size() + " missing."); send(new GetData.Builder().inventory(missing).build()); break; @@ -230,8 +231,6 @@ class Connection { security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); ctx.getInventory().storeObject(objectMessage); // offer object to some random nodes so it gets distributed throughout the network: - // FIXME: don't do this while we catch up after initialising our first connection - // (that might be a bit tricky to do) networkHandler.offer(objectMessage.getInventoryVector()); lastObjectTime = UnixTime.now(); } catch (InsufficientProofOfWorkException e) { @@ -283,7 +282,9 @@ class Connection { if (payload instanceof GetData) { requestedObjects.addAll(((GetData) payload).getInventory()); } - new NetworkMessage(payload).write(out); + synchronized (this) { + new NetworkMessage(payload).write(out); + } } catch (IOException e) { LOG.error(e.getMessage(), e); disconnect(); @@ -342,16 +343,19 @@ class Connection { public class ReaderRunnable implements Runnable { @Override public void run() { + lastObjectTime = 0; try (Socket socket = Connection.this.socket) { initSocket(socket); if (mode == CLIENT || mode == SYNC) { send(new Version.Builder().defaults().addrFrom(host).addrRecv(node).build()); } while (state != DISCONNECTED) { - if (mode != SYNC && state == ACTIVE && requestedObjects.isEmpty()) { - Thread.sleep(1000); - } else { - Thread.sleep(100); + if (mode != SYNC) { + if (state == ACTIVE && requestedObjects.isEmpty() && sendingQueue.isEmpty()) { + Thread.sleep(1000); + } else { + Thread.sleep(100); + } } try { NetworkMessage msg = Factory.getNetworkMessage(version, in); diff --git a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java index 77cad46..a45ec47 100644 --- a/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java +++ b/networking/src/test/java/ch/dissem/bitmessage/networking/NetworkHandlerTest.java @@ -117,7 +117,8 @@ public class NetworkHandlerTest { ); nodeInventory.init( - "V1Msg.payload" + "V1Msg.payload", + "V4Pubkey.payload" ); Future<?> future = networkHandler.synchronize(InetAddress.getLocalHost(), 6001, diff --git a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java index 48b8df5..69890c3 100644 --- a/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java +++ b/repositories/src/main/java/ch/dissem/bitmessage/repository/JdbcMessageRepository.java @@ -256,12 +256,13 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito private void update(Connection connection, Plaintext message) throws SQLException, IOException { PreparedStatement ps = connection.prepareStatement( - "UPDATE Message SET iv=?, sent=?, received=?, status=? WHERE id=?"); + "UPDATE Message SET iv=?, sent=?, received=?, status=?, initial_hash=? WHERE id=?"); ps.setBytes(1, message.getInventoryVector() != null ? message.getInventoryVector().getHash() : null); ps.setLong(2, message.getSent()); ps.setLong(3, message.getReceived()); ps.setString(4, message.getStatus() != null ? message.getStatus().name() : null); - ps.setLong(5, (Long) message.getId()); + ps.setBytes(5, message.getInitialHash()); + ps.setLong(6, (Long) message.getId()); ps.executeUpdate(); } From 07b349563ff02bc892227c66cef2cd64da973596 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 23 Jan 2016 17:18:25 +0100 Subject: [PATCH 40/42] Fixed an issue in the POW engine --- .../bitmessage/ports/MultiThreadedPOWEngine.java | 14 +++++++------- .../dissem/bitmessage/ports/SimplePOWEngine.java | 4 ++++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java b/core/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java index 5e00e33..790e3b7 100644 --- a/core/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java +++ b/core/src/main/java/ch/dissem/bitmessage/ports/MultiThreadedPOWEngine.java @@ -101,14 +101,12 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { if (!Bytes.lt(target, mda.digest(mda.digest()), 8)) { synchronized (callback) { if (!Thread.interrupted()) { - try { - callback.onNonceCalculated(initialHash, nonce); - } finally { - semaphore.release(); - for (Worker w : workers) { - w.interrupt(); - } + for (Worker w : workers) { + w.interrupt(); } + // Clear interrupted flag for callback + Thread.interrupted(); + callback.onNonceCalculated(initialHash, nonce); } } return; @@ -129,8 +127,10 @@ public class MultiThreadedPOWEngine implements ProofOfWorkEngine { @Override public void onNonceCalculated(byte[] initialHash, byte[] nonce) { + // Prevents the callback from being called twice if two nonces are found simultaneously synchronized (this) { if (waiting) { + semaphore.release(); LOG.info("Nonce calculated in " + ((System.currentTimeMillis() - startTime) / 1000) + " seconds"); waiting = false; callback.onNonceCalculated(initialHash, nonce); diff --git a/core/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java b/core/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java index 06d234b..e8d649b 100644 --- a/core/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java +++ b/core/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java @@ -24,6 +24,10 @@ import static ch.dissem.bitmessage.utils.Bytes.inc; /** * You should really use the MultiThreadedPOWEngine, but this one might help you grok the other one. + * <p> + * <strong>Warning:</strong> implementations probably depend on POW being asynchronous, that's + * another reason not to use this one. + * </p> */ public class SimplePOWEngine implements ProofOfWorkEngine { @Override From 6c0eae59191e742b56918d5939acc143fcfba06f Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 23 Jan 2016 17:19:36 +0100 Subject: [PATCH 41/42] Added CONTRIBUTING.md --- CONTRIBUTING.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..34c84ba --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,24 @@ +# Contributing + +We love pull requests from everyone. Please be nice and forgive us +if we can't process your request right away. + +Fork, then clone the repo: + + git clone git@github.com:your-username/Jabit.git + +Make sure the tests pass: + + ./gradlew test + +Make your change. Add tests for your change. Make the tests pass: + + ./gradlew test + +Push to your fork and [submit a pull request][pr]. + +[pr]: https://github.com/Dissem/Jabit/compare/ + +Unfortunately we can't always answer right away, so we ask you to have +some patience. Then we may suggest some changes or improvements or +alternatives. From 9a91351091cd326e8e3118264fb9767de0337d21 Mon Sep 17 00:00:00 2001 From: Christian Basler <chrigu.meyer@gmail.com> Date: Sat, 23 Jan 2016 17:21:33 +0100 Subject: [PATCH 42/42] Version 1.0.0 bump --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 02a3903..dc918be 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ subprojects { sourceCompatibility = 1.7 group = 'ch.dissem.jabit' - version = '0.2.1-SNAPSHOT' + version = '1.0.0' ext.isReleaseVersion = !version.endsWith("SNAPSHOT")