Major refactoring
This commit is contained in:
parent
b143225af5
commit
1fbc4a1d74
@ -10,7 +10,7 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile project(':domain')
|
compile project(':domain')
|
||||||
compile project(':networking')
|
compile project(':networking')
|
||||||
compile project(':inventory')
|
compile project(':repositories')
|
||||||
compile 'org.slf4j:slf4j-simple:1.7.12'
|
compile 'org.slf4j:slf4j-simple:1.7.12'
|
||||||
testCompile 'junit:junit:4.11'
|
testCompile 'junit:junit:4.11'
|
||||||
}
|
}
|
@ -18,13 +18,11 @@ package ch.dissem.bitmessage.demo;
|
|||||||
|
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.repository.JdbcAddressRepository;
|
||||||
import ch.dissem.bitmessage.entity.payload.*;
|
import ch.dissem.bitmessage.repository.JdbcInventory;
|
||||||
import ch.dissem.bitmessage.inventory.JdbcAddressRepository;
|
import ch.dissem.bitmessage.repository.JdbcMessageRepository;
|
||||||
import ch.dissem.bitmessage.inventory.JdbcInventory;
|
import ch.dissem.bitmessage.repository.JdbcNodeRegistry;
|
||||||
import ch.dissem.bitmessage.inventory.JdbcNodeRegistry;
|
|
||||||
import ch.dissem.bitmessage.networking.NetworkNode;
|
import ch.dissem.bitmessage.networking.NetworkNode;
|
||||||
import ch.dissem.bitmessage.ports.NetworkHandler;
|
|
||||||
import ch.dissem.bitmessage.utils.Base58;
|
import ch.dissem.bitmessage.utils.Base58;
|
||||||
import ch.dissem.bitmessage.utils.Encode;
|
import ch.dissem.bitmessage.utils.Encode;
|
||||||
import ch.dissem.bitmessage.utils.Security;
|
import ch.dissem.bitmessage.utils.Security;
|
||||||
@ -33,9 +31,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chris on 06.04.15.
|
* Created by chris on 06.04.15.
|
||||||
@ -51,61 +46,56 @@ public class Main {
|
|||||||
.inventory(new JdbcInventory())
|
.inventory(new JdbcInventory())
|
||||||
.nodeRegistry(new JdbcNodeRegistry())
|
.nodeRegistry(new JdbcNodeRegistry())
|
||||||
.networkHandler(new NetworkNode())
|
.networkHandler(new NetworkNode())
|
||||||
|
.messageRepo(new JdbcMessageRepository())
|
||||||
.port(48444)
|
.port(48444)
|
||||||
.streams(1)
|
.streams(1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ctx.getNetworkHandler().start(new NetworkHandler.MessageListener() {
|
// ctx.startup(new BitmessageContext.Listener() {
|
||||||
@Override
|
// @Override
|
||||||
public void receive(ObjectPayload payload) {
|
// public void receive(Plaintext plaintext) {
|
||||||
// LOG.info("message received: " + payload);
|
// // TODO
|
||||||
// System.out.print('.');
|
// try {
|
||||||
if (payload instanceof V4Pubkey) {
|
// System.out.println(new String(plaintext.getMessage(), "UTF-8"));
|
||||||
V4Pubkey pubkey = (V4Pubkey) payload;
|
// } catch (UnsupportedEncodingException e) {
|
||||||
if (Arrays.equals(address.getTag(), pubkey.getTag())) {
|
// LOG.error(e.getMessage(), e);
|
||||||
System.out.println("Pubkey found!");
|
// }
|
||||||
try {
|
// }
|
||||||
address.setPubkey(pubkey);
|
// });
|
||||||
} catch (Exception ignore) {
|
|
||||||
System.err.println("Received pubkey we didn't request.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Scanner scanner = new Scanner(System.in);
|
|
||||||
// System.out.println("Press Enter to request pubkey for address " + address);
|
// Scanner scanner = new Scanner(System.in);
|
||||||
|
//// System.out.println("Press Enter to request pubkey for address " + address);
|
||||||
|
//// scanner.nextLine();
|
||||||
|
//// ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000);
|
||||||
|
//
|
||||||
|
// System.out.println("Press Enter to exit");
|
||||||
// scanner.nextLine();
|
// scanner.nextLine();
|
||||||
// ctx.send(1, address.getVersion(), new GetPubkey(address), 3000, 1000, 1000);
|
// LOG.info("Shutting down client");
|
||||||
|
// ctx.shutdown();
|
||||||
System.out.println("Press Enter to exit");
|
//
|
||||||
scanner.nextLine();
|
//
|
||||||
LOG.info("Shutting down client");
|
// List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY);
|
||||||
ctx.getNetworkHandler().stop();
|
// System.out.println("Address version: " + address.getVersion());
|
||||||
|
// System.out.println("Address stream: " + address.getStream());
|
||||||
|
// for (ObjectMessage o : objects) {
|
||||||
List<ObjectMessage> objects = new JdbcInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY);
|
//// if (!o.isSignatureValid()) System.out.println("Invalid signature.");
|
||||||
System.out.println("Address version: " + address.getVersion());
|
//// System.out.println(o.getPayload().getSignature().length);
|
||||||
System.out.println("Address stream: " + address.getStream());
|
// V4Pubkey pubkey = (V4Pubkey) o.getPayload();
|
||||||
for (ObjectMessage o : objects) {
|
// if (Arrays.equals(address.getTag(), pubkey.getTag())) {
|
||||||
// if (!o.isSignatureValid()) System.out.println("Invalid signature.");
|
// System.out.println("Pubkey found!");
|
||||||
// System.out.println(o.getPayload().getSignature().length);
|
// try {
|
||||||
V4Pubkey pubkey = (V4Pubkey) o.getPayload();
|
// System.out.println("IV: " + o.getInventoryVector());
|
||||||
if (Arrays.equals(address.getTag(), pubkey.getTag())) {
|
// address.setPubkey(pubkey);
|
||||||
System.out.println("Pubkey found!");
|
// } catch (Exception ignore) {
|
||||||
try {
|
// System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length);
|
||||||
System.out.println("IV: "+o.getInventoryVector());
|
// System.out.println("Failed address: " + generateAddress(address.getStream(), address.getVersion(), pubkey.getRipe()));
|
||||||
address.setPubkey(pubkey);
|
// if (Arrays.equals(address.getRipe(), pubkey.getRipe())) {
|
||||||
} catch (Exception ignore) {
|
// ignore.printStackTrace();
|
||||||
System.out.println("But setPubkey failed? " + address.getRipe().length + "/" + pubkey.getRipe().length);
|
// }
|
||||||
System.out.println("Failed address: " + generateAddress(address.getStream(), address.getVersion(), pubkey.getRipe()));
|
// }
|
||||||
if (Arrays.equals(address.getRipe(), pubkey.getRipe())) {
|
// }
|
||||||
ignore.printStackTrace();
|
// }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String generateAddress(long stream, long version, byte[] ripe) {
|
public static String generateAddress(long stream, long version, byte[] ripe) {
|
||||||
|
11
docs/seminar-blx.bib
Normal file
11
docs/seminar-blx.bib
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
@Comment{$ biblatex control file $}
|
||||||
|
@Comment{$ biblatex version 1.7 $}
|
||||||
|
Do not modify this file!
|
||||||
|
|
||||||
|
This is an auxiliary file used by the 'biblatex' package.
|
||||||
|
This file may safely be deleted. It will be recreated as
|
||||||
|
required.
|
||||||
|
|
||||||
|
@Control{biblatex-control,
|
||||||
|
options = {1.7:0:0:1:0:0:1:1:0:0:0:0:1:1:3:1:79:+},
|
||||||
|
}
|
@ -16,8 +16,16 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage;
|
package ch.dissem.bitmessage;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.Encrypted;
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext.Encoding;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.GetPubkey;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.Msg;
|
||||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||||
import ch.dissem.bitmessage.ports.*;
|
import ch.dissem.bitmessage.ports.*;
|
||||||
import ch.dissem.bitmessage.utils.Security;
|
import ch.dissem.bitmessage.utils.Security;
|
||||||
import ch.dissem.bitmessage.utils.UnixTime;
|
import ch.dissem.bitmessage.utils.UnixTime;
|
||||||
@ -26,48 +34,88 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.entity.Plaintext.Status.*;
|
||||||
|
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chris on 05.04.15.
|
* Created by chris on 05.04.15.
|
||||||
*/
|
*/
|
||||||
public class BitmessageContext {
|
public class BitmessageContext {
|
||||||
public static final int CURRENT_VERSION = 3;
|
public static final int CURRENT_VERSION = 3;
|
||||||
private final static Logger LOG = LoggerFactory.getLogger(BitmessageContext.class);
|
private final static Logger LOG = LoggerFactory.getLogger(BitmessageContext.class);
|
||||||
private final Inventory inventory;
|
|
||||||
private final NodeRegistry nodeRegistry;
|
|
||||||
private final NetworkHandler networkHandler;
|
|
||||||
private final AddressRepository addressRepo;
|
|
||||||
private final ProofOfWorkEngine proofOfWorkEngine;
|
|
||||||
|
|
||||||
private final TreeSet<Long> streams;
|
private final InternalContext ctx;
|
||||||
|
|
||||||
private final int port;
|
|
||||||
|
|
||||||
private long networkNonceTrialsPerByte = 1000;
|
|
||||||
private long networkExtraBytes = 1000;
|
|
||||||
|
|
||||||
private BitmessageContext(Builder builder) {
|
private BitmessageContext(Builder builder) {
|
||||||
port = builder.port;
|
ctx = new InternalContext(builder);
|
||||||
inventory = builder.inventory;
|
|
||||||
nodeRegistry = builder.nodeRegistry;
|
|
||||||
networkHandler = builder.networkHandler;
|
|
||||||
addressRepo = builder.addressRepo;
|
|
||||||
proofOfWorkEngine = builder.proofOfWorkEngine;
|
|
||||||
streams = builder.streams;
|
|
||||||
|
|
||||||
init(inventory, nodeRegistry, networkHandler, addressRepo, proofOfWorkEngine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(Object... objects) {
|
public List<BitmessageAddress> getIdentities() {
|
||||||
for (Object o : objects) {
|
return ctx.getAddressRepo().getIdentities();
|
||||||
if (o instanceof ContextHolder) {
|
|
||||||
((ContextHolder) o).setContext(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BitmessageAddress createIdentity(boolean shorter, Feature... features) {
|
||||||
|
BitmessageAddress identity = new BitmessageAddress(new PrivateKey(
|
||||||
|
shorter,
|
||||||
|
ctx.getStreams()[0],
|
||||||
|
ctx.getNetworkNonceTrialsPerByte(),
|
||||||
|
ctx.getNetworkExtraBytes(),
|
||||||
|
features
|
||||||
|
));
|
||||||
|
ctx.getAddressRepo().save(identity);
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDistributedMailingList(String address, String alias) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(BitmessageAddress from, BitmessageAddress to, String subject, String message) {
|
||||||
|
if (from.getPrivateKey() == null) {
|
||||||
|
throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key.");
|
||||||
|
}
|
||||||
|
Plaintext msg = new Plaintext.Builder()
|
||||||
|
.from(from)
|
||||||
|
.to(to)
|
||||||
|
.encoding(Encoding.SIMPLE)
|
||||||
|
.message(subject, message)
|
||||||
|
.build();
|
||||||
|
if (to.getPubkey() == null) {
|
||||||
|
requestPubkey(from, to);
|
||||||
|
msg.setStatus(PUBKEY_REQUESTED);
|
||||||
|
ctx.getMessageRepository().save(msg);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
ctx.getMessageRepository().save(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send(long stream, long version, ObjectPayload payload, long timeToLive, long nonceTrialsPerByte, long extraBytes) throws IOException {
|
private void requestPubkey(BitmessageAddress requestingIdentity, BitmessageAddress address) {
|
||||||
|
ctx.send(
|
||||||
|
requestingIdentity,
|
||||||
|
address,
|
||||||
|
new GetPubkey(address),
|
||||||
|
+28 * DAY,
|
||||||
|
ctx.getNetworkNonceTrialsPerByte(),
|
||||||
|
ctx.getNetworkExtraBytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send(long stream, long version, ObjectPayload payload, long timeToLive) {
|
||||||
|
try {
|
||||||
long expires = UnixTime.now(+timeToLive);
|
long expires = UnixTime.now(+timeToLive);
|
||||||
LOG.info("Expires at " + expires);
|
LOG.info("Expires at " + expires);
|
||||||
ObjectMessage object = new ObjectMessage.Builder()
|
ObjectMessage object = new ObjectMessage.Builder()
|
||||||
@ -76,65 +124,36 @@ public class BitmessageContext {
|
|||||||
.expiresTime(expires)
|
.expiresTime(expires)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.build();
|
.build();
|
||||||
Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes);
|
Security.doProofOfWork(object, ctx.getProofOfWorkEngine(),
|
||||||
inventory.storeObject(object);
|
ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes());
|
||||||
networkHandler.offer(object.getInventoryVector());
|
ctx.getInventory().storeObject(object);
|
||||||
|
ctx.getNetworkHandler().offer(object.getInventoryVector());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Inventory getInventory() {
|
public void startup(Listener listener) {
|
||||||
return inventory;
|
ctx.getNetworkHandler().start(new DefaultMessageListener(ctx, listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeRegistry getAddressRepository() {
|
public void shutdown() {
|
||||||
return nodeRegistry;
|
ctx.getNetworkHandler().stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkHandler getNetworkHandler() {
|
public interface Listener {
|
||||||
return networkHandler;
|
void receive(Plaintext plaintext);
|
||||||
}
|
|
||||||
|
|
||||||
public int getPort() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long[] getStreams() {
|
|
||||||
long[] result = new long[streams.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (long stream : streams) {
|
|
||||||
result[i++] = stream;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addStream(long stream) {
|
|
||||||
streams.add(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeStream(long stream) {
|
|
||||||
streams.remove(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getNetworkNonceTrialsPerByte() {
|
|
||||||
return networkNonceTrialsPerByte;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getNetworkExtraBytes() {
|
|
||||||
return networkExtraBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface ContextHolder {
|
|
||||||
void setContext(BitmessageContext context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
private int port = 8444;
|
int port = 8444;
|
||||||
private Inventory inventory;
|
Inventory inventory;
|
||||||
private NodeRegistry nodeRegistry;
|
NodeRegistry nodeRegistry;
|
||||||
private NetworkHandler networkHandler;
|
NetworkHandler networkHandler;
|
||||||
private AddressRepository addressRepo;
|
AddressRepository addressRepo;
|
||||||
private ProofOfWorkEngine proofOfWorkEngine;
|
MessageRepository messageRepo;
|
||||||
private TreeSet<Long> streams;
|
ProofOfWorkEngine proofOfWorkEngine;
|
||||||
|
TreeSet<Long> streams;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
}
|
}
|
||||||
@ -164,6 +183,11 @@ public class BitmessageContext {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder messageRepo(MessageRepository messageRepo) {
|
||||||
|
this.messageRepo = messageRepo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) {
|
public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) {
|
||||||
this.proofOfWorkEngine = proofOfWorkEngine;
|
this.proofOfWorkEngine = proofOfWorkEngine;
|
||||||
return this;
|
return this;
|
||||||
@ -187,6 +211,7 @@ public class BitmessageContext {
|
|||||||
nonNull("nodeRegistry", nodeRegistry);
|
nonNull("nodeRegistry", nodeRegistry);
|
||||||
nonNull("networkHandler", networkHandler);
|
nonNull("networkHandler", networkHandler);
|
||||||
nonNull("addressRepo", addressRepo);
|
nonNull("addressRepo", addressRepo);
|
||||||
|
nonNull("messageRepo", messageRepo);
|
||||||
if (streams == null) {
|
if (streams == null) {
|
||||||
streams(1);
|
streams(1);
|
||||||
}
|
}
|
||||||
@ -200,4 +225,5 @@ public class BitmessageContext {
|
|||||||
if (o == null) throw new IllegalStateException(name + " must not be null");
|
if (o == null) throw new IllegalStateException(name + " must not be null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* 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.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.Encrypted;
|
||||||
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.*;
|
||||||
|
import ch.dissem.bitmessage.ports.NetworkHandler;
|
||||||
|
import ch.dissem.bitmessage.utils.Security;
|
||||||
|
import ch.dissem.bitmessage.utils.UnixTime;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.entity.Plaintext.Status.DOING_PROOF_OF_WORK;
|
||||||
|
import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT;
|
||||||
|
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
||||||
|
|
||||||
|
class DefaultMessageListener implements NetworkHandler.MessageListener {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(DefaultMessageListener.class);
|
||||||
|
private final InternalContext ctx;
|
||||||
|
private final BitmessageContext.Listener listener;
|
||||||
|
|
||||||
|
public DefaultMessageListener(InternalContext context, BitmessageContext.Listener listener) {
|
||||||
|
this.ctx = context;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(ObjectMessage object) {
|
||||||
|
ObjectPayload payload = object.getPayload();
|
||||||
|
switch (payload.getType()) {
|
||||||
|
case GET_PUBKEY: {
|
||||||
|
receive(object, (GetPubkey) payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PUBKEY: {
|
||||||
|
receive(object, (Pubkey) payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MSG: {
|
||||||
|
receive(object, (Msg) payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BROADCAST: {
|
||||||
|
receive(object, (Broadcast) payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void receive(ObjectMessage object, GetPubkey getPubkey) {
|
||||||
|
BitmessageAddress identity = ctx.getAddressRepo().findIdentity(getPubkey.getRipeTag());
|
||||||
|
if (identity != null && identity.getPrivateKey() != null) {
|
||||||
|
try {
|
||||||
|
long expires = UnixTime.now(+28 * DAY);
|
||||||
|
LOG.info("Expires at " + expires);
|
||||||
|
ObjectMessage response = new ObjectMessage.Builder()
|
||||||
|
.stream(object.getStream())
|
||||||
|
.version(identity.getVersion())
|
||||||
|
.expiresTime(expires)
|
||||||
|
.payload(identity.getPubkey())
|
||||||
|
.build();
|
||||||
|
Security.doProofOfWork(response, ctx.getProofOfWorkEngine(),
|
||||||
|
ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes());
|
||||||
|
if (response.isSigned()) {
|
||||||
|
response.sign(identity.getPrivateKey());
|
||||||
|
}
|
||||||
|
if (response instanceof Encrypted) {
|
||||||
|
response.encrypt(Security.createPublicKey(identity.getPubkeyDecryptionKey()).getEncoded(false));
|
||||||
|
}
|
||||||
|
ctx.getInventory().storeObject(response);
|
||||||
|
ctx.getNetworkHandler().offer(response.getInventoryVector());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void receive(ObjectMessage object, Pubkey pubkey) {
|
||||||
|
BitmessageAddress address;
|
||||||
|
try {
|
||||||
|
if (pubkey instanceof V4Pubkey) {
|
||||||
|
V4Pubkey v4Pubkey = (V4Pubkey) pubkey;
|
||||||
|
address = ctx.getAddressRepo().findContact(v4Pubkey.getTag());
|
||||||
|
if (address != null) {
|
||||||
|
v4Pubkey.decrypt(address.getPubkeyDecryptionKey());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
address = ctx.getAddressRepo().findContact(pubkey.getRipe());
|
||||||
|
}
|
||||||
|
if (address != null) {
|
||||||
|
address.setPubkey(pubkey);
|
||||||
|
List<Plaintext> messages = ctx.getMessageRepository().findMessages(Plaintext.Status.PUBKEY_REQUESTED, address);
|
||||||
|
for (Plaintext msg:messages){
|
||||||
|
// TODO: send messages enqueued for this address
|
||||||
|
msg.setStatus(DOING_PROOF_OF_WORK);
|
||||||
|
ctx.getMessageRepository().save(msg);
|
||||||
|
ctx.send(
|
||||||
|
msg.getFrom(),
|
||||||
|
msg.getTo(),
|
||||||
|
new Msg(msg),
|
||||||
|
+2 * DAY,
|
||||||
|
ctx.getNonceTrialsPerByte(msg.getTo()),
|
||||||
|
ctx.getExtraBytes(msg.getTo())
|
||||||
|
);
|
||||||
|
msg.setStatus(SENT);
|
||||||
|
ctx.getMessageRepository().save(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | IOException e) {
|
||||||
|
LOG.debug(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void receive(ObjectMessage object, Msg msg) {
|
||||||
|
for (BitmessageAddress identity : ctx.getAddressRepo().getIdentities()) {
|
||||||
|
try {
|
||||||
|
msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey());
|
||||||
|
msg.getPlaintext().setTo(identity);
|
||||||
|
object.isSignatureValid(msg.getPlaintext().getFrom().getPubkey());
|
||||||
|
ctx.getMessageRepository().save(msg.getPlaintext());
|
||||||
|
listener.receive(msg.getPlaintext());
|
||||||
|
break;
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void receive(ObjectMessage object, Broadcast broadcast) {
|
||||||
|
// TODO this should work fine as-is, but checking the tag might be more efficient
|
||||||
|
// V5Broadcast v5 = broadcast instanceof V5Broadcast ? (V5Broadcast) broadcast : null;
|
||||||
|
for (BitmessageAddress subscription : ctx.getAddressRepo().getSubscriptions()) {
|
||||||
|
try {
|
||||||
|
broadcast.decrypt(subscription.getPubkeyDecryptionKey());
|
||||||
|
object.isSignatureValid(broadcast.getPlaintext().getFrom().getPubkey());
|
||||||
|
listener.receive(broadcast.getPlaintext());
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
domain/src/main/java/ch/dissem/bitmessage/InternalContext.java
Normal file
166
domain/src/main/java/ch/dissem/bitmessage/InternalContext.java
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.Encrypted;
|
||||||
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||||
|
import ch.dissem.bitmessage.ports.*;
|
||||||
|
import ch.dissem.bitmessage.utils.Security;
|
||||||
|
import ch.dissem.bitmessage.utils.UnixTime;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* get extended.
|
||||||
|
* <p/>
|
||||||
|
* On the other hand, if you need the BitmessageContext in a port implementation, the same thing might apply.
|
||||||
|
*/
|
||||||
|
public class InternalContext {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class);
|
||||||
|
|
||||||
|
private final Inventory inventory;
|
||||||
|
private final NodeRegistry nodeRegistry;
|
||||||
|
private final NetworkHandler networkHandler;
|
||||||
|
private final AddressRepository addressRepository;
|
||||||
|
private final MessageRepository messageRepository;
|
||||||
|
private final ProofOfWorkEngine proofOfWorkEngine;
|
||||||
|
|
||||||
|
private final TreeSet<Long> streams;
|
||||||
|
private final int port;
|
||||||
|
private long networkNonceTrialsPerByte = 1000;
|
||||||
|
private long networkExtraBytes = 1000;
|
||||||
|
|
||||||
|
public InternalContext(BitmessageContext.Builder builder) {
|
||||||
|
this.inventory = builder.inventory;
|
||||||
|
this.nodeRegistry = builder.nodeRegistry;
|
||||||
|
this.networkHandler = builder.networkHandler;
|
||||||
|
this.addressRepository = builder.addressRepo;
|
||||||
|
this.messageRepository = builder.messageRepo;
|
||||||
|
this.proofOfWorkEngine = builder.proofOfWorkEngine;
|
||||||
|
|
||||||
|
port = builder.port;
|
||||||
|
streams = builder.streams;
|
||||||
|
|
||||||
|
init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Object... objects) {
|
||||||
|
for (Object o : objects) {
|
||||||
|
if (o instanceof ContextHolder) {
|
||||||
|
((ContextHolder) o).setContext(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Inventory getInventory() {
|
||||||
|
return inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeRegistry getNodeRegistry() {
|
||||||
|
return nodeRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NetworkHandler getNetworkHandler() {
|
||||||
|
return networkHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressRepository getAddressRepo() {
|
||||||
|
return addressRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageRepository getMessageRepository() {
|
||||||
|
return messageRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProofOfWorkEngine getProofOfWorkEngine() {
|
||||||
|
return proofOfWorkEngine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getStreams() {
|
||||||
|
long[] result = new long[streams.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (long stream : streams) {
|
||||||
|
result[i++] = stream;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addStream(long stream) {
|
||||||
|
streams.add(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeStream(long stream) {
|
||||||
|
streams.remove(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getNetworkNonceTrialsPerByte() {
|
||||||
|
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(BitmessageAddress from, BitmessageAddress to, ObjectPayload payload, long timeToLive, long nonceTrialsPerByte, long extraBytes) {
|
||||||
|
try {
|
||||||
|
long expires = UnixTime.now(+timeToLive);
|
||||||
|
LOG.info("Expires at " + expires);
|
||||||
|
ObjectMessage object = new ObjectMessage.Builder()
|
||||||
|
.stream(to.getStream())
|
||||||
|
.version(to.getVersion())
|
||||||
|
.expiresTime(expires)
|
||||||
|
.payload(payload)
|
||||||
|
.build();
|
||||||
|
Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes);
|
||||||
|
if (object.isSigned()) {
|
||||||
|
object.sign(from.getPrivateKey());
|
||||||
|
}
|
||||||
|
if (object instanceof Encrypted) {
|
||||||
|
object.encrypt(to.getPubkey());
|
||||||
|
}
|
||||||
|
inventory.storeObject(object);
|
||||||
|
networkHandler.offer(object.getInventoryVector());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ContextHolder {
|
||||||
|
void setContext(InternalContext context);
|
||||||
|
}
|
||||||
|
}
|
@ -49,8 +49,9 @@ public class BitmessageAddress {
|
|||||||
private Pubkey pubkey;
|
private Pubkey pubkey;
|
||||||
|
|
||||||
private String alias;
|
private String alias;
|
||||||
|
private boolean subscribed;
|
||||||
|
|
||||||
private BitmessageAddress(long version, long stream, byte[] ripe) {
|
BitmessageAddress(long version, long stream, byte[] ripe) {
|
||||||
try {
|
try {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
@ -74,7 +75,7 @@ public class BitmessageAddress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private BitmessageAddress(Pubkey publicKey) {
|
BitmessageAddress(Pubkey publicKey) {
|
||||||
this(publicKey.getVersion(), publicKey.getStream(), publicKey.getRipe());
|
this(publicKey.getVersion(), publicKey.getStream(), publicKey.getRipe());
|
||||||
this.pubkey = publicKey;
|
this.pubkey = publicKey;
|
||||||
}
|
}
|
||||||
@ -163,6 +164,10 @@ public class BitmessageAddress {
|
|||||||
this.alias = alias;
|
this.alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSubscribed(boolean subscribed) {
|
||||||
|
this.subscribed = subscribed;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return alias != null ? alias : address;
|
return alias != null ? alias : address;
|
||||||
|
@ -19,7 +19,7 @@ package ch.dissem.bitmessage.entity;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chris on 12.05.15.
|
* Used for objects that have encrypted content
|
||||||
*/
|
*/
|
||||||
public interface Encrypted {
|
public interface Encrypted {
|
||||||
void encrypt(byte[] publicKey) throws IOException;
|
void encrypt(byte[] publicKey) throws IOException;
|
||||||
|
@ -134,10 +134,14 @@ public class ObjectMessage implements MessagePayload {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encrypt(Pubkey publicKey) throws IOException{
|
public void encrypt(Pubkey publicKey) {
|
||||||
|
try {
|
||||||
if (payload instanceof Encrypted) {
|
if (payload instanceof Encrypted) {
|
||||||
((Encrypted) payload).encrypt(publicKey.getEncryptionKey());
|
((Encrypted) payload).encrypt(publicKey.getEncryptionKey());
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSignatureValid(Pubkey pubkey) throws IOException {
|
public boolean isSignatureValid(Pubkey pubkey) throws IOException {
|
||||||
|
352
domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java
Normal file
352
domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
/*
|
||||||
|
* 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 ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
|
import ch.dissem.bitmessage.factory.Factory;
|
||||||
|
import ch.dissem.bitmessage.utils.Decode;
|
||||||
|
import ch.dissem.bitmessage.utils.Encode;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unencrypted message to be sent by 'msg' or 'broadcast'.
|
||||||
|
*/
|
||||||
|
public class Plaintext implements Streamable {
|
||||||
|
private final BitmessageAddress from;
|
||||||
|
private final long encoding;
|
||||||
|
private final byte[] message;
|
||||||
|
private final byte[] ack;
|
||||||
|
private Object id;
|
||||||
|
private BitmessageAddress to;
|
||||||
|
private byte[] signature;
|
||||||
|
private Status status;
|
||||||
|
private Long sent;
|
||||||
|
private Long received;
|
||||||
|
|
||||||
|
private Set<Label> labels;
|
||||||
|
|
||||||
|
private Plaintext(Builder builder) {
|
||||||
|
id = builder.id;
|
||||||
|
from = builder.from;
|
||||||
|
to = builder.to;
|
||||||
|
encoding = builder.encoding;
|
||||||
|
message = builder.message;
|
||||||
|
ack = builder.ack;
|
||||||
|
signature = builder.signature;
|
||||||
|
status = builder.status;
|
||||||
|
sent = builder.sent;
|
||||||
|
received = builder.received;
|
||||||
|
labels = builder.labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Plaintext read(InputStream in) throws IOException {
|
||||||
|
return readWithoutSignature(in)
|
||||||
|
.signature(Decode.varBytes(in))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Plaintext.Builder readWithoutSignature(InputStream in) throws IOException {
|
||||||
|
return new Builder()
|
||||||
|
.addressVersion(Decode.varInt(in))
|
||||||
|
.stream(Decode.varInt(in))
|
||||||
|
.behaviorBitfield(Decode.int32(in))
|
||||||
|
.publicSigningKey(Decode.bytes(in, 64))
|
||||||
|
.publicEncryptionKey(Decode.bytes(in, 64))
|
||||||
|
.nonceTrialsPerByte(Decode.varInt(in))
|
||||||
|
.extraBytes(Decode.varInt(in))
|
||||||
|
.destinationRipe(Decode.bytes(in, 20))
|
||||||
|
.encoding(Decode.varInt(in))
|
||||||
|
.message(Decode.varBytes(in))
|
||||||
|
.ack(Decode.varBytes(in));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmessageAddress getFrom() {
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitmessageAddress getTo() {
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTo(BitmessageAddress to) {
|
||||||
|
if (this.to.getVersion() != 0)
|
||||||
|
throw new RuntimeException("Correct address already set");
|
||||||
|
if (Arrays.equals(this.to.getRipe(), to.getRipe())) {
|
||||||
|
throw new RuntimeException("RIPEs don't match");
|
||||||
|
}
|
||||||
|
this.to = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Label> getLabels() {
|
||||||
|
return labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStream() {
|
||||||
|
return from.getStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSignature() {
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSignature(byte[] signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(OutputStream out, boolean includeSignature) throws IOException {
|
||||||
|
Encode.varInt(from.getVersion(), out);
|
||||||
|
Encode.varInt(from.getStream(), out);
|
||||||
|
Encode.int32(from.getPubkey().getBehaviorBitfield(), out);
|
||||||
|
out.write(from.getPubkey().getSigningKey());
|
||||||
|
out.write(from.getPubkey().getEncryptionKey());
|
||||||
|
Encode.varInt(from.getPubkey().getNonceTrialsPerByte(), out);
|
||||||
|
Encode.varInt(from.getPubkey().getExtraBytes(), out);
|
||||||
|
out.write(to.getRipe());
|
||||||
|
Encode.varInt(encoding, out);
|
||||||
|
Encode.varInt(message.length, out);
|
||||||
|
out.write(message);
|
||||||
|
Encode.varInt(ack.length, out);
|
||||||
|
out.write(ack);
|
||||||
|
if (includeSignature) {
|
||||||
|
Encode.varInt(signature.length, out);
|
||||||
|
out.write(signature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(OutputStream out) throws IOException {
|
||||||
|
write(out, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(long id) {
|
||||||
|
if (this.id != null) throw new IllegalStateException("ID already set");
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getSent() {
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getReceived() {
|
||||||
|
return received;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(Status status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Encoding {
|
||||||
|
IGNORE(0), TRIVIAL(1), SIMPLE(2);
|
||||||
|
|
||||||
|
long code;
|
||||||
|
|
||||||
|
Encoding(long code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Encoding fromCode(long code) {
|
||||||
|
for (Encoding e : values()) {
|
||||||
|
if (e.getCode() == code) return e;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Status {
|
||||||
|
PUBKEY_REQUESTED,
|
||||||
|
DOING_PROOF_OF_WORK,
|
||||||
|
SENT,
|
||||||
|
ACKNOWLEDGED
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
private Object id;
|
||||||
|
private BitmessageAddress from;
|
||||||
|
private BitmessageAddress to;
|
||||||
|
private long addressVersion;
|
||||||
|
private long stream;
|
||||||
|
private int behaviorBitfield;
|
||||||
|
private byte[] publicSigningKey;
|
||||||
|
private byte[] publicEncryptionKey;
|
||||||
|
private long nonceTrialsPerByte;
|
||||||
|
private long extraBytes;
|
||||||
|
private byte[] destinationRipe;
|
||||||
|
private long encoding;
|
||||||
|
private byte[] message = new byte[0];
|
||||||
|
private byte[] ack = new byte[0];
|
||||||
|
private byte[] signature;
|
||||||
|
private long sent;
|
||||||
|
private long received;
|
||||||
|
private Status status;
|
||||||
|
private Set<Label> labels = new TreeSet<>();
|
||||||
|
|
||||||
|
public Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder id(Object id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder from(BitmessageAddress address) {
|
||||||
|
from = address;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder to(BitmessageAddress address) {
|
||||||
|
to = address;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder addressVersion(long addressVersion) {
|
||||||
|
this.addressVersion = addressVersion;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder stream(long stream) {
|
||||||
|
this.stream = stream;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder behaviorBitfield(int behaviorBitfield) {
|
||||||
|
this.behaviorBitfield = behaviorBitfield;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder publicSigningKey(byte[] publicSigningKey) {
|
||||||
|
this.publicSigningKey = publicSigningKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder publicEncryptionKey(byte[] publicEncryptionKey) {
|
||||||
|
this.publicEncryptionKey = publicEncryptionKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder nonceTrialsPerByte(long nonceTrialsPerByte) {
|
||||||
|
this.nonceTrialsPerByte = nonceTrialsPerByte;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder extraBytes(long extraBytes) {
|
||||||
|
this.extraBytes = extraBytes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder destinationRipe(byte[] ripe) {
|
||||||
|
this.destinationRipe = ripe;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder encoding(Encoding encoding) {
|
||||||
|
this.encoding = encoding.getCode();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder encoding(long encoding) {
|
||||||
|
this.encoding = encoding;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder message(String subject, String message) {
|
||||||
|
try {
|
||||||
|
this.message = ("Subject:" + subject + '\n' + "Body:" + message).getBytes("UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder message(byte[] message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder ack(byte[] ack) {
|
||||||
|
this.ack = ack;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder signature(byte[] signature) {
|
||||||
|
this.signature = signature;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder sent(long sent) {
|
||||||
|
this.sent = sent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder received(long received) {
|
||||||
|
this.received = received;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder status(Status status) {
|
||||||
|
this.status = status;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder labels(Collection<Label> labels) {
|
||||||
|
this.labels.addAll(labels);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plaintext build() {
|
||||||
|
if (id == null) {
|
||||||
|
id = UUID.randomUUID();
|
||||||
|
}
|
||||||
|
if (from == null) {
|
||||||
|
from = new BitmessageAddress(Factory.createPubkey(
|
||||||
|
addressVersion,
|
||||||
|
stream,
|
||||||
|
publicSigningKey,
|
||||||
|
publicEncryptionKey,
|
||||||
|
nonceTrialsPerByte,
|
||||||
|
extraBytes,
|
||||||
|
Pubkey.Feature.features(behaviorBitfield)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (to == null) {
|
||||||
|
to = new BitmessageAddress(0, 0, destinationRipe);
|
||||||
|
}
|
||||||
|
return new Plaintext(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,10 +16,47 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.entity.payload;
|
package ch.dissem.bitmessage.entity.payload;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.entity.Encrypted;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users who are subscribed to the sending address will see the message appear in their inbox.
|
* Users who are subscribed to the sending address will see the message appear in their inbox.
|
||||||
* Broadcasts are version 4 or 5.
|
* Broadcasts are version 4 or 5.
|
||||||
*/
|
*/
|
||||||
public abstract class Broadcast extends ObjectPayload {
|
public abstract class Broadcast extends ObjectPayload implements Encrypted {
|
||||||
public abstract byte[] getEncrypted();
|
protected final long stream;
|
||||||
|
protected CryptoBox encrypted;
|
||||||
|
protected Plaintext plaintext;
|
||||||
|
|
||||||
|
protected Broadcast(long stream, CryptoBox encrypted, Plaintext plaintext) {
|
||||||
|
this.stream = stream;
|
||||||
|
this.encrypted = encrypted;
|
||||||
|
this.plaintext = plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getStream() {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plaintext getPlaintext() {
|
||||||
|
return plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encrypt(byte[] publicKey) throws IOException {
|
||||||
|
this.encrypted = new CryptoBox(plaintext, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decrypt(byte[] privateKey) throws IOException {
|
||||||
|
plaintext = Plaintext.read(encrypted.decrypt(privateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDecrypted() {
|
||||||
|
return plaintext != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
package ch.dissem.bitmessage.entity.payload;
|
package ch.dissem.bitmessage.entity.payload;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.utils.Bytes;
|
|
||||||
import ch.dissem.bitmessage.utils.Decode;
|
import ch.dissem.bitmessage.utils.Decode;
|
||||||
import ch.dissem.bitmessage.utils.Security;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -30,30 +28,33 @@ import java.io.OutputStream;
|
|||||||
*/
|
*/
|
||||||
public class GetPubkey extends ObjectPayload {
|
public class GetPubkey extends ObjectPayload {
|
||||||
private long stream;
|
private long stream;
|
||||||
private byte[] ripe;
|
private byte[] ripeTag;
|
||||||
private byte[] tag;
|
|
||||||
|
|
||||||
public GetPubkey(BitmessageAddress address) {
|
public GetPubkey(BitmessageAddress address) {
|
||||||
this.stream = address.getStream();
|
this.stream = address.getStream();
|
||||||
if (address.getVersion() < 4)
|
if (address.getVersion() < 4)
|
||||||
this.ripe = address.getRipe();
|
this.ripeTag = address.getRipe();
|
||||||
else
|
else
|
||||||
this.tag = address.getTag();
|
this.ripeTag = address.getTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
private GetPubkey(long stream, long version, byte[] ripeOrTag) {
|
private GetPubkey(long stream, long version, byte[] ripeOrTag) {
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
if (version < 4) {
|
this.ripeTag = ripeOrTag;
|
||||||
ripe = ripeOrTag;
|
|
||||||
} else {
|
|
||||||
tag = ripeOrTag;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GetPubkey read(InputStream is, long stream, int length, long version) throws IOException {
|
public static GetPubkey read(InputStream is, long stream, int length, long version) throws IOException {
|
||||||
return new GetPubkey(stream, version, Decode.bytes(is, length));
|
return new GetPubkey(stream, version, Decode.bytes(is, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of bytes that represent either the ripe, or the tag of an address, depending on the
|
||||||
|
* address version.
|
||||||
|
*/
|
||||||
|
public byte[] getRipeTag() {
|
||||||
|
return ripeTag;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectType getType() {
|
public ObjectType getType() {
|
||||||
return ObjectType.GET_PUBKEY;
|
return ObjectType.GET_PUBKEY;
|
||||||
@ -66,10 +67,6 @@ public class GetPubkey extends ObjectPayload {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(OutputStream stream) throws IOException {
|
public void write(OutputStream stream) throws IOException {
|
||||||
if (tag != null) {
|
stream.write(ripeTag);
|
||||||
stream.write(tag);
|
|
||||||
} else {
|
|
||||||
stream.write(ripe);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.entity.payload;
|
package ch.dissem.bitmessage.entity.payload;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.entity.Encrypted;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@ -23,26 +26,29 @@ import java.io.OutputStream;
|
|||||||
/**
|
/**
|
||||||
* Used for person-to-person messages.
|
* Used for person-to-person messages.
|
||||||
*/
|
*/
|
||||||
public class Msg extends ObjectPayload {
|
public class Msg extends ObjectPayload implements Encrypted {
|
||||||
private long stream;
|
private long stream;
|
||||||
private CryptoBox encrypted;
|
private CryptoBox encrypted;
|
||||||
private UnencryptedMessage decrypted;
|
private Plaintext plaintext;
|
||||||
|
|
||||||
private Msg(long stream, CryptoBox encrypted) {
|
private Msg(long stream, CryptoBox encrypted) {
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.encrypted = encrypted;
|
this.encrypted = encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Msg(UnencryptedMessage unencrypted, Pubkey publicKey) {
|
public Msg(Plaintext plaintext) {
|
||||||
this.stream = unencrypted.getStream();
|
this.stream = plaintext.getStream();
|
||||||
this.decrypted = unencrypted;
|
this.plaintext = plaintext;
|
||||||
this.encrypted = new CryptoBox(unencrypted, publicKey.getEncryptionKey());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Msg read(InputStream in, long stream, int length) throws IOException {
|
public static Msg read(InputStream in, long stream, int length) throws IOException {
|
||||||
return new Msg(stream, CryptoBox.read(in, length));
|
return new Msg(stream, CryptoBox.read(in, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Plaintext getPlaintext() {
|
||||||
|
return plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ObjectType getType() {
|
public ObjectType getType() {
|
||||||
return ObjectType.MSG;
|
return ObjectType.MSG;
|
||||||
@ -55,26 +61,37 @@ public class Msg extends ObjectPayload {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSigned() {
|
public boolean isSigned() {
|
||||||
return decrypted != null;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeBytesToSign(OutputStream out) throws IOException {
|
public void writeBytesToSign(OutputStream out) throws IOException {
|
||||||
decrypted.write(out, false);
|
plaintext.write(out, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getSignature() {
|
public byte[] getSignature() {
|
||||||
return decrypted.getSignature();
|
return plaintext.getSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSignature(byte[] signature) {
|
public void setSignature(byte[] signature) {
|
||||||
decrypted.setSignature(signature);
|
plaintext.setSignature(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encrypt(byte[] publicKey) throws IOException {
|
||||||
|
this.encrypted = new CryptoBox(plaintext, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void decrypt(byte[] privateKey) throws IOException {
|
public void decrypt(byte[] privateKey) throws IOException {
|
||||||
decrypted = UnencryptedMessage.read(encrypted.decrypt(privateKey));
|
plaintext = Plaintext.read(encrypted.decrypt(privateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDecrypted() {
|
||||||
|
return plaintext != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,10 +37,20 @@ public abstract class Pubkey extends ObjectPayload {
|
|||||||
|
|
||||||
public abstract byte[] getEncryptionKey();
|
public abstract byte[] getEncryptionKey();
|
||||||
|
|
||||||
|
public abstract int getBehaviorBitfield();
|
||||||
|
|
||||||
public byte[] getRipe() {
|
public byte[] getRipe() {
|
||||||
return ripemd160(sha512(getSigningKey(), getEncryptionKey()));
|
return ripemd160(sha512(getSigningKey(), getEncryptionKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getNonceTrialsPerByte() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExtraBytes() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected byte[] add0x04(byte[] key) {
|
protected byte[] add0x04(byte[] key) {
|
||||||
if (key.length == 65) return key;
|
if (key.length == 65) return key;
|
||||||
byte[] result = new byte[65];
|
byte[] result = new byte[65];
|
||||||
|
@ -1,173 +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.entity.payload;
|
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.Streamable;
|
|
||||||
import ch.dissem.bitmessage.utils.Decode;
|
|
||||||
import ch.dissem.bitmessage.utils.Encode;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The unencrypted message to be sent by 'msg' or 'broadcast'.
|
|
||||||
*/
|
|
||||||
public class UnencryptedMessage implements Streamable {
|
|
||||||
private final long addressVersion;
|
|
||||||
private final long stream;
|
|
||||||
private final int behaviorBitfield;
|
|
||||||
private final byte[] publicSigningKey;
|
|
||||||
private final byte[] publicEncryptionKey;
|
|
||||||
private final long nonceTrialsPerByte;
|
|
||||||
private final long extraBytes;
|
|
||||||
private final long encoding;
|
|
||||||
private final byte[] message;
|
|
||||||
private byte[] signature;
|
|
||||||
|
|
||||||
private UnencryptedMessage(Builder builder) {
|
|
||||||
addressVersion = builder.addressVersion;
|
|
||||||
stream = builder.stream;
|
|
||||||
behaviorBitfield = builder.behaviorBitfield;
|
|
||||||
publicSigningKey = builder.publicSigningKey;
|
|
||||||
publicEncryptionKey = builder.publicEncryptionKey;
|
|
||||||
nonceTrialsPerByte = builder.nonceTrialsPerByte;
|
|
||||||
extraBytes = builder.extraBytes;
|
|
||||||
encoding = builder.encoding;
|
|
||||||
message = builder.message;
|
|
||||||
signature = builder.signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UnencryptedMessage read(InputStream is) throws IOException {
|
|
||||||
return new Builder()
|
|
||||||
.addressVersion(Decode.varInt(is))
|
|
||||||
.stream(Decode.varInt(is))
|
|
||||||
.behaviorBitfield(Decode.int32(is))
|
|
||||||
.publicSigningKey(Decode.bytes(is, 64))
|
|
||||||
.publicEncryptionKey(Decode.bytes(is, 64))
|
|
||||||
.nonceTrialsPerByte(Decode.varInt(is))
|
|
||||||
.extraBytes(Decode.varInt(is))
|
|
||||||
.encoding(Decode.varInt(is))
|
|
||||||
.message(Decode.varBytes(is))
|
|
||||||
.signature(Decode.varBytes(is))
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getStream() {
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getSignature() {
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSignature(byte[] signature) {
|
|
||||||
this.signature = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void write(OutputStream out, boolean includeSignature) throws IOException {
|
|
||||||
Encode.varInt(addressVersion, out);
|
|
||||||
Encode.varInt(stream, out);
|
|
||||||
Encode.int32(behaviorBitfield, out);
|
|
||||||
out.write(publicSigningKey);
|
|
||||||
out.write(publicEncryptionKey);
|
|
||||||
Encode.varInt(nonceTrialsPerByte, out);
|
|
||||||
Encode.varInt(extraBytes, out);
|
|
||||||
Encode.varInt(encoding, out);
|
|
||||||
Encode.varInt(message.length, out);
|
|
||||||
out.write(message);
|
|
||||||
if (includeSignature) {
|
|
||||||
Encode.varInt(signature.length, out);
|
|
||||||
out.write(signature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(OutputStream out) throws IOException {
|
|
||||||
write(out, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Builder {
|
|
||||||
private long addressVersion;
|
|
||||||
private long stream;
|
|
||||||
private int behaviorBitfield;
|
|
||||||
private byte[] publicSigningKey;
|
|
||||||
private byte[] publicEncryptionKey;
|
|
||||||
private long nonceTrialsPerByte;
|
|
||||||
private long extraBytes;
|
|
||||||
private long encoding;
|
|
||||||
private byte[] message;
|
|
||||||
private byte[] signature;
|
|
||||||
|
|
||||||
public Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder addressVersion(long addressVersion) {
|
|
||||||
this.addressVersion = addressVersion;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder stream(long stream) {
|
|
||||||
this.stream = stream;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder behaviorBitfield(int behaviorBitfield) {
|
|
||||||
this.behaviorBitfield = behaviorBitfield;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder publicSigningKey(byte[] publicSigningKey) {
|
|
||||||
this.publicSigningKey = publicSigningKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder publicEncryptionKey(byte[] publicEncryptionKey) {
|
|
||||||
this.publicEncryptionKey = publicEncryptionKey;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder nonceTrialsPerByte(long nonceTrialsPerByte) {
|
|
||||||
this.nonceTrialsPerByte = nonceTrialsPerByte;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder extraBytes(long extraBytes) {
|
|
||||||
this.extraBytes = extraBytes;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder encoding(long encoding) {
|
|
||||||
this.encoding = encoding;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder message(byte[] message) {
|
|
||||||
this.message = message;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder signature(byte[] signature) {
|
|
||||||
this.signature = signature;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UnencryptedMessage build() {
|
|
||||||
return new UnencryptedMessage(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -76,6 +76,11 @@ public class V2Pubkey extends Pubkey {
|
|||||||
return publicEncryptionKey;
|
return publicEncryptionKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBehaviorBitfield() {
|
||||||
|
return behaviorBitfield;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(OutputStream os) throws IOException {
|
public void write(OutputStream os) throws IOException {
|
||||||
Encode.int32(behaviorBitfield, os);
|
Encode.int32(behaviorBitfield, os);
|
||||||
|
@ -18,7 +18,6 @@ package ch.dissem.bitmessage.entity.payload;
|
|||||||
|
|
||||||
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.Security;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@ -66,6 +65,14 @@ public class V3Pubkey extends V2Pubkey {
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getNonceTrialsPerByte() {
|
||||||
|
return nonceTrialsPerByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExtraBytes() {
|
||||||
|
return extraBytes;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSigned() {
|
public boolean isSigned() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.entity.payload;
|
package ch.dissem.bitmessage.entity.payload;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.utils.Decode;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@ -27,17 +25,12 @@ import java.io.OutputStream;
|
|||||||
* Broadcasts are version 4 or 5.
|
* Broadcasts are version 4 or 5.
|
||||||
*/
|
*/
|
||||||
public class V4Broadcast extends Broadcast {
|
public class V4Broadcast extends Broadcast {
|
||||||
private long stream;
|
protected V4Broadcast(long stream, CryptoBox encrypted) {
|
||||||
private byte[] encrypted;
|
super(stream, encrypted, null);
|
||||||
private UnencryptedMessage unencrypted;
|
|
||||||
|
|
||||||
protected V4Broadcast(long stream, byte[] encrypted) {
|
|
||||||
this.stream = stream;
|
|
||||||
this.encrypted = encrypted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static V4Broadcast read(InputStream is, long stream, int length) throws IOException {
|
public static V4Broadcast read(InputStream in, long stream, int length) throws IOException {
|
||||||
return new V4Broadcast(stream, Decode.bytes(is, length));
|
return new V4Broadcast(stream, CryptoBox.read(in, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -45,32 +38,23 @@ public class V4Broadcast extends Broadcast {
|
|||||||
return ObjectType.BROADCAST;
|
return ObjectType.BROADCAST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getStream() {
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getEncrypted() {
|
|
||||||
return encrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeBytesToSign(OutputStream out) throws IOException {
|
public void writeBytesToSign(OutputStream out) throws IOException {
|
||||||
unencrypted.write(out, false);
|
plaintext.write(out, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getSignature() {
|
public byte[] getSignature() {
|
||||||
return unencrypted.getSignature();
|
return plaintext.getSignature();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSignature(byte[] signature) {
|
public void setSignature(byte[] signature) {
|
||||||
unencrypted.setSignature(signature);
|
plaintext.setSignature(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(OutputStream stream) throws IOException {
|
public void write(OutputStream out) throws IOException {
|
||||||
stream.write(getEncrypted());
|
encrypted.write(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,11 @@ public class V4Pubkey extends Pubkey implements Encrypted {
|
|||||||
return decrypted.getEncryptionKey();
|
return decrypted.getEncryptionKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBehaviorBitfield() {
|
||||||
|
return decrypted.getBehaviorBitfield();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getSignature() {
|
public byte[] getSignature() {
|
||||||
if (decrypted != null)
|
if (decrypted != null)
|
||||||
@ -128,4 +133,12 @@ public class V4Pubkey extends Pubkey implements Encrypted {
|
|||||||
public boolean isSigned() {
|
public boolean isSigned() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getNonceTrialsPerByte() {
|
||||||
|
return decrypted.getNonceTrialsPerByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getExtraBytes() {
|
||||||
|
return decrypted.getExtraBytes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,13 +28,13 @@ import java.io.OutputStream;
|
|||||||
public class V5Broadcast extends V4Broadcast {
|
public class V5Broadcast extends V4Broadcast {
|
||||||
private byte[] tag;
|
private byte[] tag;
|
||||||
|
|
||||||
private V5Broadcast(long stream, byte[] tag, byte[] encrypted) {
|
private V5Broadcast(long stream, byte[] tag, CryptoBox encrypted) {
|
||||||
super(stream, encrypted);
|
super(stream, encrypted);
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static V5Broadcast read(InputStream is, long stream, int length) throws IOException {
|
public static V5Broadcast read(InputStream is, long stream, int length) throws IOException {
|
||||||
return new V5Broadcast(stream, Decode.bytes(is, 32), Decode.bytes(is, length - 32));
|
return new V5Broadcast(stream, Decode.bytes(is, 32), CryptoBox.read(is, length - 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getTag() {
|
public byte[] getTag() {
|
||||||
@ -42,8 +42,8 @@ public class V5Broadcast extends V4Broadcast {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(OutputStream stream) throws IOException {
|
public void write(OutputStream out) throws IOException {
|
||||||
stream.write(tag);
|
out.write(tag);
|
||||||
super.write(stream);
|
super.write(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.valueobject;
|
||||||
|
|
||||||
|
public class Label {
|
||||||
|
private Object id;
|
||||||
|
private String label;
|
||||||
|
private int color;
|
||||||
|
|
||||||
|
public Label(String label, int color) {
|
||||||
|
this.label = label;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RGBA representation for the color.
|
||||||
|
*/
|
||||||
|
public int getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color) {
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -20,21 +20,25 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by chris on 23.04.15.
|
|
||||||
*/
|
|
||||||
public interface AddressRepository {
|
public interface AddressRepository {
|
||||||
|
BitmessageAddress findContact(byte[] ripeOrTag);
|
||||||
|
|
||||||
|
BitmessageAddress findIdentity(byte[] ripeOrTag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all Bitmessage addresses that belong to this user, i.e. have a private key.
|
* Returns all Bitmessage addresses that belong to this user, i.e. have a private key.
|
||||||
*/
|
*/
|
||||||
List<BitmessageAddress> findIdentities();
|
List<BitmessageAddress> getIdentities();
|
||||||
|
|
||||||
|
List<BitmessageAddress> getSubscriptions();
|
||||||
/**
|
/**
|
||||||
* Returns all Bitmessage addresses that have no private key.
|
* Returns all Bitmessage addresses that have no private key.
|
||||||
*/
|
*/
|
||||||
List<BitmessageAddress> findContacts();
|
List<BitmessageAddress> getContacts();
|
||||||
|
|
||||||
void save(BitmessageAddress address);
|
void save(BitmessageAddress address);
|
||||||
|
|
||||||
void remove(BitmessageAddress address);
|
void remove(BitmessageAddress address);
|
||||||
|
|
||||||
|
BitmessageAddress getAddress(String address);
|
||||||
}
|
}
|
||||||
|
@ -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.ports;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext.Status;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface MessageRepository {
|
||||||
|
List<String> getLabels();
|
||||||
|
|
||||||
|
List<Plaintext> findMessages(Label label);
|
||||||
|
|
||||||
|
List<Plaintext> findMessages(Status status);
|
||||||
|
|
||||||
|
List<Plaintext> findMessages(Status status, BitmessageAddress recipient);
|
||||||
|
|
||||||
|
void save(Plaintext message);
|
||||||
|
|
||||||
|
void remove(Plaintext message);
|
||||||
|
}
|
@ -28,7 +28,7 @@ import java.util.List;
|
|||||||
import static ch.dissem.bitmessage.utils.Bytes.inc;
|
import static ch.dissem.bitmessage.utils.Bytes.inc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by chris on 14.04.15.
|
* A POW engine using all available CPU cores.
|
||||||
*/
|
*/
|
||||||
public class MultiThreadedPOWEngine implements ProofOfWorkEngine {
|
public class MultiThreadedPOWEngine implements ProofOfWorkEngine {
|
||||||
private static Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class);
|
private static Logger LOG = LoggerFactory.getLogger(MultiThreadedPOWEngine.class);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package ch.dissem.bitmessage.ports;
|
package ch.dissem.bitmessage.ports;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||||
|
|
||||||
@ -31,6 +32,6 @@ public interface NetworkHandler {
|
|||||||
void offer(InventoryVector iv);
|
void offer(InventoryVector iv);
|
||||||
|
|
||||||
interface MessageListener {
|
interface MessageListener {
|
||||||
void receive(ObjectPayload payload);
|
void receive(ObjectMessage object);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,8 @@ public class Security {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, long extraBytes) throws IOException {
|
public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, long extraBytes) {
|
||||||
|
try {
|
||||||
if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000;
|
if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000;
|
||||||
if (extraBytes < 1000) extraBytes = 1000;
|
if (extraBytes < 1000) extraBytes = 1000;
|
||||||
|
|
||||||
@ -99,6 +100,9 @@ public class Security {
|
|||||||
|
|
||||||
byte[] nonce = worker.calculateNonce(initialHash, target);
|
byte[] nonce = worker.calculateNonce(initialHash, target);
|
||||||
object.setNonce(nonce);
|
object.setNonce(nonce);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +20,8 @@ package ch.dissem.bitmessage.utils;
|
|||||||
* Created by chris on 18.04.15.
|
* Created by chris on 18.04.15.
|
||||||
*/
|
*/
|
||||||
public class UnixTime {
|
public class UnixTime {
|
||||||
|
public static final long DAY = 60 * 60 * 24;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the time in second based Unix time ({@link System#currentTimeMillis()}/1000)
|
* Returns the time in second based Unix time ({@link System#currentTimeMillis()}/1000)
|
||||||
*/
|
*/
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
CREATE TABLE Node (
|
|
||||||
ip BINARY(16) NOT NULL,
|
|
||||||
port INT NOT NULL,
|
|
||||||
stream BIGINT NOT NULL,
|
|
||||||
services BIGINT NOT NULL,
|
|
||||||
time BIGINT NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY (ip, port, stream)
|
|
||||||
);
|
|
@ -1,8 +0,0 @@
|
|||||||
CREATE TABLE Inventory (
|
|
||||||
hash BINARY(32) NOT NULL PRIMARY KEY,
|
|
||||||
stream BIGINT NOT NULL,
|
|
||||||
expires BIGINT NOT NULL,
|
|
||||||
data BLOB NOT NULL,
|
|
||||||
type BIGINT NOT NULL,
|
|
||||||
version BIGINT NOT NULL
|
|
||||||
);
|
|
@ -1,6 +0,0 @@
|
|||||||
CREATE TABLE Address (
|
|
||||||
address VARCHAR(40) NOT NULL PRIMARY KEY,
|
|
||||||
alias VARCHAR(255),
|
|
||||||
public_key BLOB,
|
|
||||||
private_key BLOB
|
|
||||||
);
|
|
@ -17,6 +17,7 @@
|
|||||||
package ch.dissem.bitmessage.networking;
|
package ch.dissem.bitmessage.networking;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
import ch.dissem.bitmessage.InternalContext;
|
||||||
import ch.dissem.bitmessage.entity.*;
|
import ch.dissem.bitmessage.entity.*;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||||
@ -43,7 +44,7 @@ import static ch.dissem.bitmessage.networking.Connection.State.*;
|
|||||||
public class Connection implements Runnable {
|
public class Connection implements Runnable {
|
||||||
private final static Logger LOG = LoggerFactory.getLogger(Connection.class);
|
private final static Logger LOG = LoggerFactory.getLogger(Connection.class);
|
||||||
|
|
||||||
private BitmessageContext ctx;
|
private InternalContext ctx;
|
||||||
|
|
||||||
private State state;
|
private State state;
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
@ -59,7 +60,7 @@ public class Connection implements Runnable {
|
|||||||
|
|
||||||
private Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>();
|
private Queue<MessagePayload> sendingQueue = new ConcurrentLinkedDeque<>();
|
||||||
|
|
||||||
public Connection(BitmessageContext context, State state, Socket socket, MessageListener listener) throws IOException {
|
public Connection(InternalContext context, State state, Socket socket, MessageListener listener) throws IOException {
|
||||||
this.ctx = context;
|
this.ctx = context;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
@ -161,12 +162,12 @@ public class Connection implements Runnable {
|
|||||||
LOG.debug(e.getMessage(), e);
|
LOG.debug(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
// It's probably pointless, but let the listener decide if we accept the message for the client.
|
// It's probably pointless, but let the listener decide if we accept the message for the client.
|
||||||
listener.receive(objectMessage.getPayload());
|
listener.receive(objectMessage);
|
||||||
break;
|
break;
|
||||||
case ADDR:
|
case ADDR:
|
||||||
Addr addr = (Addr) messagePayload;
|
Addr addr = (Addr) messagePayload;
|
||||||
LOG.debug("Received " + addr.getAddresses().size() + " addresses.");
|
LOG.debug("Received " + addr.getAddresses().size() + " addresses.");
|
||||||
ctx.getAddressRepository().offerAddresses(addr.getAddresses());
|
ctx.getNodeRegistry().offerAddresses(addr.getAddresses());
|
||||||
break;
|
break;
|
||||||
case VERACK:
|
case VERACK:
|
||||||
case VERSION:
|
case VERSION:
|
||||||
@ -175,7 +176,7 @@ public class Connection implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendAddresses() {
|
private void sendAddresses() {
|
||||||
List<NetworkAddress> addresses = ctx.getAddressRepository().getKnownAddresses(1000, streams);
|
List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(1000, streams);
|
||||||
sendingQueue.offer(new Addr.Builder().addresses(addresses).build());
|
sendingQueue.offer(new Addr.Builder().addresses(addresses).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.networking;
|
package ch.dissem.bitmessage.networking;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.InternalContext;
|
||||||
import ch.dissem.bitmessage.BitmessageContext.ContextHolder;
|
import ch.dissem.bitmessage.InternalContext.ContextHolder;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||||
import ch.dissem.bitmessage.ports.NetworkHandler;
|
import ch.dissem.bitmessage.ports.NetworkHandler;
|
||||||
@ -42,7 +42,7 @@ public class NetworkNode implements NetworkHandler, ContextHolder {
|
|||||||
private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class);
|
private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class);
|
||||||
private final ExecutorService pool;
|
private final ExecutorService pool;
|
||||||
private final List<Connection> connections = new LinkedList<>();
|
private final List<Connection> connections = new LinkedList<>();
|
||||||
private BitmessageContext ctx;
|
private InternalContext ctx;
|
||||||
private ServerSocket serverSocket;
|
private ServerSocket serverSocket;
|
||||||
private Thread connectionManager;
|
private Thread connectionManager;
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ public class NetworkNode implements NetworkHandler, ContextHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContext(BitmessageContext context) {
|
public void setContext(InternalContext context) {
|
||||||
this.ctx = context;
|
this.ctx = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ public class NetworkNode implements NetworkHandler, ContextHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (connections.size() < 1) {
|
if (connections.size() < 1) {
|
||||||
List<NetworkAddress> addresses = ctx.getAddressRepository().getKnownAddresses(8, ctx.getStreams());
|
List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(8, ctx.getStreams());
|
||||||
for (NetworkAddress address : addresses) {
|
for (NetworkAddress address : addresses) {
|
||||||
try {
|
try {
|
||||||
startConnection(new Connection(ctx, CLIENT, new Socket(address.toInetAddress(), address.getPort()), listener));
|
startConnection(new Connection(ctx, CLIENT, new Socket(address.toInetAddress(), address.getPort()), listener));
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.dissem.bitmessage.inventory;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||||
@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -36,12 +37,41 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(JdbcAddressRepository.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JdbcAddressRepository.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BitmessageAddress> findIdentities() {
|
public BitmessageAddress findContact(byte[] ripeOrTag) {
|
||||||
|
for (BitmessageAddress address : find("public_key is null")) {
|
||||||
|
if (address.getVersion() > 3) {
|
||||||
|
if (Arrays.equals(ripeOrTag, address.getTag())) return address;
|
||||||
|
} else {
|
||||||
|
if (Arrays.equals(ripeOrTag, address.getRipe())) return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BitmessageAddress findIdentity(byte[] ripeOrTag) {
|
||||||
|
for (BitmessageAddress address : find("private_key is not null")) {
|
||||||
|
if (address.getVersion() > 3) {
|
||||||
|
if (Arrays.equals(ripeOrTag, address.getTag())) return address;
|
||||||
|
} else {
|
||||||
|
if (Arrays.equals(ripeOrTag, address.getRipe())) return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BitmessageAddress> getIdentities() {
|
||||||
return find("private_signing_key IS NOT NULL");
|
return find("private_signing_key IS NOT NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<BitmessageAddress> findContacts() {
|
public List<BitmessageAddress> getSubscriptions() {
|
||||||
|
return find("subscribed = '1'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BitmessageAddress> getContacts() {
|
||||||
return find("private_signing_key IS NULL");
|
return find("private_signing_key IS NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +79,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
|
|||||||
List<BitmessageAddress> result = new LinkedList<>();
|
List<BitmessageAddress> result = new LinkedList<>();
|
||||||
try {
|
try {
|
||||||
Statement stmt = getConnection().createStatement();
|
Statement stmt = getConnection().createStatement();
|
||||||
ResultSet rs = stmt.executeQuery("SELECT address, alias, public_key, private_key FROM Address WHERE " + where);
|
ResultSet rs = stmt.executeQuery("SELECT address, alias, public_key, private_key, subscribed FROM Address WHERE " + where);
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
BitmessageAddress address;
|
BitmessageAddress address;
|
||||||
Blob privateKeyBlob = rs.getBlob("private_key");
|
Blob privateKeyBlob = rs.getBlob("private_key");
|
||||||
@ -66,6 +96,7 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
address.setAlias(rs.getString("alias"));
|
address.setAlias(rs.getString("alias"));
|
||||||
|
address.setSubscribed(rs.getBoolean("subscribed"));
|
||||||
|
|
||||||
result.add(address);
|
result.add(address);
|
||||||
}
|
}
|
||||||
@ -75,18 +106,49 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean exists(BitmessageAddress address) {
|
||||||
|
try {
|
||||||
|
Statement stmt = getConnection().createStatement();
|
||||||
|
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM Address WHERE address='" + address.getAddress() + "'");
|
||||||
|
rs.next();
|
||||||
|
return rs.getInt(0) > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(BitmessageAddress address) {
|
public void save(BitmessageAddress address) {
|
||||||
try {
|
try {
|
||||||
|
if (exists(address)) {
|
||||||
|
update(address);
|
||||||
|
} else {
|
||||||
|
insert(address);
|
||||||
|
}
|
||||||
|
} catch (IOException | SQLException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(BitmessageAddress address) throws IOException, SQLException {
|
||||||
|
PreparedStatement ps = getConnection().prepareStatement(
|
||||||
|
"UPDATE Address SET address=?, alias=?, public_key=?, private_key=?");
|
||||||
|
ps.setString(1, address.getAddress());
|
||||||
|
ps.setString(2, address.getAlias());
|
||||||
|
writeBlob(ps, 3, address.getPubkey());
|
||||||
|
writeBlob(ps, 4, address.getPrivateKey());
|
||||||
|
ps.executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insert(BitmessageAddress address) throws IOException, SQLException {
|
||||||
PreparedStatement ps = getConnection().prepareStatement(
|
PreparedStatement ps = getConnection().prepareStatement(
|
||||||
"INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?, ?)");
|
"INSERT INTO Address (address, alias, public_key, private_key) VALUES (?, ?, ?, ?, ?)");
|
||||||
ps.setString(1, address.getAddress());
|
ps.setString(1, address.getAddress());
|
||||||
ps.setString(2, address.getAlias());
|
ps.setString(2, address.getAlias());
|
||||||
writeBlob(ps, 3, address.getPubkey());
|
writeBlob(ps, 3, address.getPubkey());
|
||||||
writeBlob(ps, 4, address.getPrivateKey());
|
writeBlob(ps, 4, address.getPrivateKey());
|
||||||
} catch (IOException | SQLException e) {
|
ps.executeUpdate();
|
||||||
LOG.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -98,4 +160,11 @@ public class JdbcAddressRepository extends JdbcHelper implements AddressReposito
|
|||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BitmessageAddress getAddress(String address) {
|
||||||
|
List<BitmessageAddress> result = find("address = '" + address + "'");
|
||||||
|
if (result.size() > 0) return result.get(0);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.dissem.bitmessage.inventory;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.Streamable;
|
import ch.dissem.bitmessage.entity.Streamable;
|
||||||
import org.flywaydb.core.Flyway;
|
import org.flywaydb.core.Flyway;
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.dissem.bitmessage.inventory;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* 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.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class JdbcMessageRepository extends JdbcHelper implements MessageRepository, InternalContext.ContextHolder {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(JdbcMessageRepository.class);
|
||||||
|
|
||||||
|
private InternalContext ctx;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getLabels() {
|
||||||
|
List<String> result = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
Statement stmt = getConnection().createStatement();
|
||||||
|
ResultSet rs = stmt.executeQuery("SELECT label FROM Label ORDER BY order");
|
||||||
|
while (rs.next()) {
|
||||||
|
result.add(rs.getString("label"));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Plaintext> findMessages(Label label) {
|
||||||
|
return find("id IN SELECT message_id FROM Message_Label WHERE label_id=" + label.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Plaintext> findMessages(Plaintext.Status status, BitmessageAddress recipient) {
|
||||||
|
return find("status='" + status.name() + "' AND to='" + recipient.getAddress() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Plaintext> findMessages(Plaintext.Status status) {
|
||||||
|
return find("status='" + status.name() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Plaintext> find(String where) {
|
||||||
|
List<Plaintext> result = new LinkedList<>();
|
||||||
|
try {
|
||||||
|
Statement stmt = getConnection().createStatement();
|
||||||
|
ResultSet rs = stmt.executeQuery("SELECT \"id\", \"from\", \"to\", \"data\", \"sent\", \"received\", \"status\" FROM Message WHERE " + where);
|
||||||
|
while (rs.next()) {
|
||||||
|
Blob data = rs.getBlob("data");
|
||||||
|
Plaintext.Builder builder = Plaintext.readWithoutSignature(data.getBinaryStream());
|
||||||
|
long id = rs.getLong("id");
|
||||||
|
builder.id(id);
|
||||||
|
builder.from(ctx.getAddressRepo().getAddress(rs.getString("from")));
|
||||||
|
builder.to(ctx.getAddressRepo().getAddress(rs.getString("to")));
|
||||||
|
builder.sent(rs.getLong("sent"));
|
||||||
|
builder.received(rs.getLong("received"));
|
||||||
|
builder.status(Plaintext.Status.valueOf(rs.getString("status")));
|
||||||
|
builder.labels(findLabels(id));
|
||||||
|
result.add(builder.build());
|
||||||
|
}
|
||||||
|
} catch (IOException | SQLException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<Label> findLabels(long messageId) {
|
||||||
|
List<Label> result = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
Statement stmt = getConnection().createStatement();
|
||||||
|
ResultSet rs = stmt.executeQuery("SELECT \"label\", \"color\" FROM Label WHERE id IN SELECT label_id FROM Message_Label WHERE message_id=" + messageId);
|
||||||
|
while (rs.next()) {
|
||||||
|
result.add(new Label(rs.getString("label"), rs.getInt("color")));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(Plaintext message) {
|
||||||
|
// save from address if necessary
|
||||||
|
if (message.getId() == null) {
|
||||||
|
BitmessageAddress savedAddress = ctx.getAddressRepo().getAddress(message.getFrom().getAddress());
|
||||||
|
if (savedAddress.getPrivateKey() == null) {
|
||||||
|
if (savedAddress.getAlias() != null) {
|
||||||
|
message.getFrom().setAlias(savedAddress.getAlias());
|
||||||
|
}
|
||||||
|
ctx.getAddressRepo().save(message.getFrom());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection connection = getConnection();
|
||||||
|
try {
|
||||||
|
connection.setAutoCommit(false);
|
||||||
|
// save message
|
||||||
|
if (message.getId() == null) {
|
||||||
|
insert(connection, message);
|
||||||
|
|
||||||
|
// remove existing labels
|
||||||
|
Statement stmt = connection.createStatement();
|
||||||
|
stmt.executeUpdate("DELETE FROM Message_Label WHERE message_id=" + message.getId());
|
||||||
|
} else {
|
||||||
|
update(connection, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save labels
|
||||||
|
PreparedStatement ps = connection.prepareStatement("INSERT INTO Message_Label VALUES (" + message.getId() + ", ?)");
|
||||||
|
for (Label label : message.getLabels()) {
|
||||||
|
ps.setLong(1, (Long) label.getId());
|
||||||
|
ps.executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.commit();
|
||||||
|
} catch (IOException | SQLException e) {
|
||||||
|
try {
|
||||||
|
connection.rollback();
|
||||||
|
} catch (SQLException e1) {
|
||||||
|
LOG.debug(e1.getMessage(), e);
|
||||||
|
}
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insert(Connection connection, Plaintext message) throws SQLException, IOException {
|
||||||
|
PreparedStatement ps = connection.prepareStatement(
|
||||||
|
"INSERT INTO Message (\"from\", \"to\", \"data\", \"sent\", \"received\", \"status\") VALUES (?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||||
|
ps.setString(1, message.getFrom().getAddress());
|
||||||
|
ps.setString(2, message.getTo().getAddress());
|
||||||
|
writeBlob(ps, 3, message);
|
||||||
|
ps.setLong(4, message.getSent());
|
||||||
|
ps.setLong(5, message.getReceived());
|
||||||
|
ps.setString(6, message.getStatus().name());
|
||||||
|
ps.executeUpdate();
|
||||||
|
|
||||||
|
// get generated id
|
||||||
|
ResultSet rs = ps.getGeneratedKeys();
|
||||||
|
rs.next();
|
||||||
|
message.setId(rs.getLong(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(Connection connection, Plaintext message) throws SQLException, IOException {
|
||||||
|
PreparedStatement ps = connection.prepareStatement(
|
||||||
|
"UPDATE Message SET \"sent\"=?, \"received\"=?, \"status\"=?");
|
||||||
|
ps.setLong(1, message.getSent());
|
||||||
|
ps.setLong(2, message.getReceived());
|
||||||
|
ps.setString(3, message.getStatus().name());
|
||||||
|
ps.executeUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Plaintext message) {
|
||||||
|
try {
|
||||||
|
Statement stmt = getConnection().createStatement();
|
||||||
|
stmt.executeUpdate("DELETE FROM Message WHERE id = " + message.getId());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOG.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContext(InternalContext context) {
|
||||||
|
this.ctx = context;
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.dissem.bitmessage.inventory;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||||
import ch.dissem.bitmessage.ports.NodeRegistry;
|
import ch.dissem.bitmessage.ports.NodeRegistry;
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.dissem.bitmessage.inventory;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ch.dissem.bitmessage.inventory;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||||
import ch.dissem.bitmessage.ports.NodeRegistry;
|
import ch.dissem.bitmessage.ports.NodeRegistry;
|
@ -0,0 +1,9 @@
|
|||||||
|
CREATE TABLE Node (
|
||||||
|
"ip" BINARY(16) NOT NULL,
|
||||||
|
"port" INT NOT NULL,
|
||||||
|
"stream" BIGINT NOT NULL,
|
||||||
|
"services" BIGINT NOT NULL,
|
||||||
|
"time" BIGINT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY ("ip", "port", "stream")
|
||||||
|
);
|
@ -0,0 +1,8 @@
|
|||||||
|
CREATE TABLE Inventory (
|
||||||
|
"hash" BINARY(32) NOT NULL PRIMARY KEY,
|
||||||
|
"stream" BIGINT NOT NULL,
|
||||||
|
"expires" BIGINT NOT NULL,
|
||||||
|
"data" BLOB NOT NULL,
|
||||||
|
"type" BIGINT NOT NULL,
|
||||||
|
"version" BIGINT NOT NULL
|
||||||
|
);
|
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE Address (
|
||||||
|
"address" VARCHAR(40) NOT NULL PRIMARY KEY,
|
||||||
|
"alias" VARCHAR(255),
|
||||||
|
"public_key" BLOB,
|
||||||
|
"private_key" BLOB,
|
||||||
|
"subscribed" BIT DEFAULT '0'
|
||||||
|
);
|
@ -0,0 +1,32 @@
|
|||||||
|
CREATE TABLE Message (
|
||||||
|
"id" BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
"from" VARCHAR(40) NOT NULL,
|
||||||
|
"to" VARCHAR(40) NOT NULL,
|
||||||
|
"data" BLOB NOT NULL,
|
||||||
|
"sent" BIGINT,
|
||||||
|
"received" BIGINT,
|
||||||
|
"status" VARCHAR(20) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE Label (
|
||||||
|
"id" BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
"label" VARCHAR(255) NOT NULL,
|
||||||
|
"color" INT,
|
||||||
|
"order" BIGINT,
|
||||||
|
CONSTRAINT UC_label UNIQUE ("label"),
|
||||||
|
CONSTRAINT UC_order UNIQUE ("order")
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE Message_Label (
|
||||||
|
"message_id" BIGINT NOT NULL,
|
||||||
|
"label_id" BIGINT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY ("message_id", "label_id"),
|
||||||
|
FOREIGN KEY ("message_id") REFERENCES Message ("id"),
|
||||||
|
FOREIGN KEY ("label_id") REFERENCES Label ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO Label("label", "order") VALUES ('Inbox', 0);
|
||||||
|
INSERT INTO Label("label", "order") VALUES ('Sent', 10);
|
||||||
|
INSERT INTO Label("label", "order") VALUES ('Drafts', 20);
|
||||||
|
INSERT INTO Label("label", "order") VALUES ('Trash', 100);
|
@ -4,7 +4,7 @@ include 'domain'
|
|||||||
|
|
||||||
include 'networking'
|
include 'networking'
|
||||||
|
|
||||||
include 'inventory'
|
include 'repositories'
|
||||||
|
|
||||||
include 'demo'
|
include 'demo'
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user