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)
This commit is contained in:
Christian Basler 2015-08-28 13:48:01 +02:00
parent 4911c268c2
commit f89d1a342e
20 changed files with 83 additions and 38 deletions

View File

@ -22,8 +22,10 @@ dependencies {
compile project(':domain') compile project(':domain')
compile project(':networking') compile project(':networking')
compile project(':repositories') compile project(':repositories')
compile project(':security-bc')
compile project(':wif') compile project(':wif')
compile 'org.slf4j:slf4j-simple:1.7.12' compile 'org.slf4j:slf4j-simple:1.7.12'
compile 'args4j:args4j:2.32' compile 'args4j:args4j:2.32'
compile 'com.h2database:h2:1.4.187'
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.11'
} }

View File

@ -21,7 +21,9 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.Pubkey; import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
import ch.dissem.bitmessage.repository.*; import ch.dissem.bitmessage.repository.*;
import ch.dissem.bitmessage.security.bc.BouncySecurity;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -46,6 +48,7 @@ public class Application {
.nodeRegistry(new MemoryNodeRegistry()) .nodeRegistry(new MemoryNodeRegistry())
.messageRepo(new JdbcMessageRepository(jdbcConfig)) .messageRepo(new JdbcMessageRepository(jdbcConfig))
.networkHandler(new DefaultNetworkHandler()) .networkHandler(new DefaultNetworkHandler())
.security(new BouncySecurity())
.port(48444) .port(48444)
.build(); .build();

View File

@ -18,7 +18,9 @@ package ch.dissem.bitmessage.demo;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
import ch.dissem.bitmessage.repository.*; import ch.dissem.bitmessage.repository.*;
import ch.dissem.bitmessage.security.bc.BouncySecurity;
import ch.dissem.bitmessage.wif.WifExporter; import ch.dissem.bitmessage.wif.WifExporter;
import ch.dissem.bitmessage.wif.WifImporter; import ch.dissem.bitmessage.wif.WifImporter;
import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineException;
@ -50,6 +52,7 @@ public class Main {
.nodeRegistry(new MemoryNodeRegistry()) .nodeRegistry(new MemoryNodeRegistry())
.messageRepo(new JdbcMessageRepository(jdbcConfig)) .messageRepo(new JdbcMessageRepository(jdbcConfig))
.networkHandler(new DefaultNetworkHandler()) .networkHandler(new DefaultNetworkHandler())
.security(new BouncySecurity())
.port(48444) .port(48444)
.build(); .build();

View File

@ -81,7 +81,7 @@ public class InternalContext {
streams.add(1L); streams.add(1L);
} }
init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine, security);
} }
private void init(Object... objects) { private void init(Object... objects) {

View File

@ -19,9 +19,9 @@ package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.ports.Security;
import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Decode;
import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Encode;
import ch.dissem.bitmessage.utils.UnixTime;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
@ -29,7 +29,7 @@ import java.util.*;
/** /**
* The unencrypted message to be sent by 'msg' or 'broadcast'. * 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 Type type;
private final BitmessageAddress from; private final BitmessageAddress from;
private final long encoding; private final long encoding;
@ -64,6 +64,7 @@ public class Plaintext implements Streamable, Serializable {
public static Plaintext read(Type type, InputStream in) throws IOException { public static Plaintext read(Type type, InputStream in) throws IOException {
return readWithoutSignature(type, in) return readWithoutSignature(type, in)
.signature(Decode.varBytes(in)) .signature(Decode.varBytes(in))
.received(UnixTime.now())
.build(); .build();
} }
@ -132,6 +133,15 @@ public class Plaintext implements Streamable, Serializable {
this.signature = signature; 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 { public void write(OutputStream out, boolean includeSignature) throws IOException {
Encode.varInt(from.getVersion(), out); Encode.varInt(from.getVersion(), out);
Encode.varInt(from.getStream(), out); Encode.varInt(from.getStream(), out);

View File

@ -16,14 +16,13 @@
package ch.dissem.bitmessage.entity; package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.ports.Security;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Serializable;
/** /**
* An object that can be written to an {@link OutputStream} * An object that can be written to an {@link OutputStream}
*/ */
public interface Streamable { public interface Streamable extends Serializable {
void write(OutputStream stream) throws IOException; void write(OutputStream stream) throws IOException;
} }

View File

@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.Streamable;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Serializable;
/** /**
* The payload of an 'object' command. This is shared by the network. * The payload of an 'object' command. This is shared by the network.

View File

@ -16,9 +16,10 @@
package ch.dissem.bitmessage.entity.valueobject; package ch.dissem.bitmessage.entity.valueobject;
import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
public class Label { public class Label implements Serializable {
private Object id; private Object id;
private String label; private String label;
private Type type; private Type type;

View File

@ -14,10 +14,9 @@
* limitations under the License. * 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.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.ports.NodeRegistry;
import ch.dissem.bitmessage.utils.UnixTime; import ch.dissem.bitmessage.utils.UnixTime;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.utils.Property; import ch.dissem.bitmessage.utils.Property;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress;
/** /**
* Handles incoming messages * Handles incoming messages
@ -30,6 +31,8 @@ public interface NetworkHandler {
void stop(); void stop();
void synchronize(InetAddress trustedHost, int port, MessageListener listener) throws IOException;
void offer(InventoryVector iv); void offer(InventoryVector iv);
Property getNetworkStatus(); Property getNetworkStatus();

View File

@ -16,12 +16,6 @@
package ch.dissem.bitmessage.utils; 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. * 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 * 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 (a < 0) return b < 0 && a < b;
if (b < 0) return a >= 0 || a < b; if (b < 0) return a >= 0 || a < b;
return a < b; return a < b;
// This would be easier to understand, but is (slightly) slower:
// return (a & 0xff) < (b & 0xff);
} }
/** /**

View File

@ -117,6 +117,8 @@ public class Encode {
* @throws IOException if an I/O error occurs. * @throws IOException if an I/O error occurs.
*/ */
public static byte[] bytes(Streamable streamable) throws IOException { public static byte[] bytes(Streamable streamable) throws IOException {
if (streamable == null) return null;
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
streamable.write(stream); streamable.write(stream);
return stream.toByteArray(); return stream.toByteArray();

View File

@ -17,21 +17,21 @@
package ch.dissem.bitmessage.entity; package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.utils.TestBase;
import ch.dissem.bitmessage.utils.TestUtils; import ch.dissem.bitmessage.utils.TestUtils;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.ByteArrayOutputStream; import java.util.Arrays;
import java.io.IOException;
import java.io.InputStream;
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public class SerializationTest { public class SerializationTest extends TestBase {
@Test @Test
public void ensureGetPubkeyIsDeserializedAndSerializedCorrectly() throws IOException { public void ensureGetPubkeyIsDeserializedAndSerializedCorrectly() throws IOException {
doTest("V2GetPubkey.payload", 2, GetPubkey.class); doTest("V2GetPubkey.payload", 2, GetPubkey.class);
@ -75,7 +75,7 @@ public class SerializationTest {
} }
@Test @Test
public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws IOException, DecryptionFailedException { public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws Exception {
Plaintext p1 = new Plaintext.Builder(MSG) Plaintext p1 = new Plaintext.Builder(MSG)
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
.to(TestUtils.loadContact()) .to(TestUtils.loadContact())
@ -99,4 +99,21 @@ public class SerializationTest {
assertArrayEquals(data, out.toByteArray()); assertArrayEquals(data, out.toByteArray());
assertEquals(expectedPayloadType.getCanonicalName(), object.getPayload().getClass().getCanonicalName()); 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());
}
} }

View File

@ -27,6 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.*; 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) { private void startConnection(Connection c) {
synchronized (connections) { synchronized (connections) {
// prevent connecting twice to the same node // prevent connecting twice to the same node

View File

@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.sql.*; import java.sql.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
@ -97,16 +98,16 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
while (rs.next()) { while (rs.next()) {
BitmessageAddress address; BitmessageAddress address;
byte[] privateKeyBytes = rs.getBytes("private_key"); InputStream privateKeyStream = rs.getBinaryStream("private_key");
if (privateKeyBytes != null) { if (privateKeyStream != null) {
PrivateKey privateKey = PrivateKey.read(new ByteArrayInputStream(privateKeyBytes)); PrivateKey privateKey = PrivateKey.read(privateKeyStream);
address = new BitmessageAddress(privateKey); address = new BitmessageAddress(privateKey);
} else { } else {
address = new BitmessageAddress(rs.getString("address")); address = new BitmessageAddress(rs.getString("address"));
byte[] publicKeyBytes = rs.getBytes("public_key"); Blob publicKeyBlob = rs.getBlob("public_key");
if (publicKeyBytes != null) { if (publicKeyBlob != null) {
Pubkey pubkey = Factory.readPubkey(address.getVersion(), address.getStream(), 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) { if (address.getVersion() == 4 && pubkey instanceof V3Pubkey) {
pubkey = new V4Pubkey((V3Pubkey) pubkey); pubkey = new V4Pubkey((V3Pubkey) pubkey);
} }
@ -182,7 +183,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
data.writeUnencrypted(out); data.writeUnencrypted(out);
ps.setBytes(parameterIndex, out.toByteArray()); ps.setBytes(parameterIndex, out.toByteArray());
} else { } else {
ps.setBlob(parameterIndex, (Blob) null); ps.setBytes(parameterIndex, null);
} }
} }

View File

@ -21,10 +21,8 @@ import ch.dissem.bitmessage.entity.payload.ObjectType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.sql.Blob;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
@ -84,7 +82,7 @@ abstract class JdbcHelper {
data.write(os); data.write(os);
ps.setBytes(parameterIndex, os.toByteArray()); ps.setBytes(parameterIndex, os.toByteArray());
} else { } else {
ps.setBlob(parameterIndex, (Blob) null); ps.setBytes(parameterIndex, null);
} }
} }
} }

View File

@ -53,6 +53,7 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
} }
return result; return result;
} }
private List<InventoryVector> getFullInventory(long... streams) { private List<InventoryVector> getFullInventory(long... streams) {
List<InventoryVector> result = new LinkedList<>(); List<InventoryVector> result = new LinkedList<>();
try (Connection connection = config.getConnection()) { try (Connection connection = config.getConnection()) {
@ -79,8 +80,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
Statement stmt = connection.createStatement(); Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = X'" + vector + "'"); ResultSet rs = stmt.executeQuery("SELECT data, version FROM Inventory WHERE hash = X'" + vector + "'");
if (rs.next()) { if (rs.next()) {
byte[] data = rs.getBytes("data"); Blob data = rs.getBlob("data");
return Factory.getObjectMessage(rs.getInt("version"), new ByteArrayInputStream(data), data.length); return Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length());
} else { } else {
LOG.info("Object requested that we don't have. IV: " + vector); LOG.info("Object requested that we don't have. IV: " + vector);
return null; return null;
@ -108,8 +109,8 @@ public class JdbcInventory extends JdbcHelper implements Inventory {
ResultSet rs = stmt.executeQuery(query.toString()); ResultSet rs = stmt.executeQuery(query.toString());
List<ObjectMessage> result = new LinkedList<>(); List<ObjectMessage> result = new LinkedList<>();
while (rs.next()) { while (rs.next()) {
byte[] data = rs.getBytes("data"); Blob data = rs.getBlob("data");
result.add(Factory.getObjectMessage(rs.getInt("version"), new ByteArrayInputStream(data), data.length)); result.add(Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()));
} }
return result; return result;
} catch (Exception e) { } catch (Exception e) {

View File

@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.sql.*; import java.sql.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; 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); ResultSet rs = stmt.executeQuery("SELECT id, iv, type, sender, recipient, data, sent, received, status FROM Message WHERE " + where);
while (rs.next()) { while (rs.next()) {
byte[] iv = rs.getBytes("iv"); 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.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"); long id = rs.getLong("id");
builder.id(id); builder.id(id);
builder.IV(new InventoryVector(iv)); 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 { private void insert(Connection connection, Plaintext message) throws SQLException, IOException {
PreparedStatement ps = connection.prepareStatement( 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.setBytes(1, message.getInventoryVector() != null ? message.getInventoryVector().getHash() : null);
ps.setString(2, message.getType().name()); ps.setString(2, message.getType().name());
ps.setString(3, message.getFrom().getAddress()); ps.setString(3, message.getFrom().getAddress());

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.repository; package ch.dissem.bitmessage.repository;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
import ch.dissem.bitmessage.ports.NodeRegistry; import ch.dissem.bitmessage.ports.NodeRegistry;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;