Merge branch 'feature/server-pow' into develop
This commit is contained in:
commit
fa766cb6f1
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
# Project specific files
|
||||
admins.conf
|
||||
clients.conf
|
||||
*list.conf
|
||||
config.properties
|
||||
/*.db
|
||||
|
@ -44,6 +44,7 @@ dependencies {
|
||||
compile 'ch.dissem.jabit:jabit-networking:0.2.1-SNAPSHOT'
|
||||
compile 'ch.dissem.jabit:jabit-repositories:0.2.1-SNAPSHOT'
|
||||
compile 'ch.dissem.jabit:jabit-security-bouncy:0.2.1-SNAPSHOT'
|
||||
compile 'ch.dissem.jabit:jabit-extensions:0.2.1-SNAPSHOT'
|
||||
|
||||
compile 'com.h2database:h2:1.4.187'
|
||||
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
@ -7,7 +23,7 @@ import org.slf4j.LoggerFactory;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Created by chrigu on 04.10.15.
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class CleanupJob extends TimerTask {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CleanupJob.class);
|
||||
|
13
src/main/java/ch/dissem/bitmessage/server/Constants.java
Normal file
13
src/main/java/ch/dissem/bitmessage/server/Constants.java
Normal file
@ -0,0 +1,13 @@
|
||||
package ch.dissem.bitmessage.server;
|
||||
|
||||
/**
|
||||
* Created by chrigu on 22.11.15.
|
||||
*/
|
||||
public interface Constants {
|
||||
String ADMIN_LIST = "admins.conf";
|
||||
String CLIENT_LIST = "clients.conf";
|
||||
|
||||
String WHITELIST = "whitelist.conf";
|
||||
String SHORTLIST = "shortlist.conf";
|
||||
String BLACKLIST = "blacklist.conf";
|
||||
}
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
|
@ -1,13 +1,29 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.networking.DefaultNetworkHandler;
|
||||
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
|
||||
import ch.dissem.bitmessage.repository.JdbcAddressRepository;
|
||||
import ch.dissem.bitmessage.repository.JdbcConfig;
|
||||
import ch.dissem.bitmessage.repository.JdbcInventory;
|
||||
import ch.dissem.bitmessage.repository.JdbcMessageRepository;
|
||||
import ch.dissem.bitmessage.ports.*;
|
||||
import ch.dissem.bitmessage.repository.*;
|
||||
import ch.dissem.bitmessage.security.bc.BouncySecurity;
|
||||
import ch.dissem.bitmessage.server.repository.ServerProofOfWorkRepository;
|
||||
import ch.dissem.bitmessage.utils.Singleton;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -16,6 +32,9 @@ import springfox.documentation.spring.web.plugins.Docket;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static ch.dissem.bitmessage.server.Constants.*;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
|
||||
@Configuration
|
||||
public class JabitServerConfig {
|
||||
public static final int SHORTLIST_SIZE = 5;
|
||||
@ -27,28 +46,102 @@ public class JabitServerConfig {
|
||||
@Value("${bitmessage.connection.limit}")
|
||||
private int connectionLimit;
|
||||
|
||||
@Bean
|
||||
public JdbcConfig jdbcConfig() {
|
||||
return new JdbcConfig("jdbc:h2:file:./jabit;AUTO_SERVER=TRUE;DB_CLOSE_DELAY=10", "sa", null);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AddressRepository addressRepo() {
|
||||
return new JdbcAddressRepository(jdbcConfig());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Inventory inventory() {
|
||||
return new JdbcInventory(jdbcConfig());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MessageRepository messageRepo() {
|
||||
return new JdbcMessageRepository(jdbcConfig());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProofOfWorkRepository proofOfWorkRepo() {
|
||||
return new JdbcProofOfWorkRepository(jdbcConfig());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NodeRegistry nodeRegistry() {
|
||||
return new MemoryNodeRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public NetworkHandler networkHandler() {
|
||||
return new DefaultNetworkHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Security security() {
|
||||
BouncySecurity security = new BouncySecurity();
|
||||
Singleton.initialize(security); // needed for admins and clients
|
||||
return security;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BitmessageContext.Listener serverListener() {
|
||||
return new ServerListener(admins(), clients(), whitelist(), shortlist(), blacklist());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServerProofOfWorkRepository serverProofOfWorkRepository() {
|
||||
return new ServerProofOfWorkRepository(jdbcConfig());
|
||||
}
|
||||
@Bean
|
||||
public CustomCommandHandler commandHandler() {
|
||||
return new ProofOfWorkRequestHandler(serverProofOfWorkRepository(), clients());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BitmessageContext bitmessageContext() {
|
||||
JdbcConfig config = new JdbcConfig("jdbc:h2:file:./jabit;AUTO_SERVER=TRUE", "sa", null);
|
||||
return new BitmessageContext.Builder()
|
||||
.addressRepo(new JdbcAddressRepository(config))
|
||||
.inventory(new JdbcInventory(config))
|
||||
.messageRepo(new JdbcMessageRepository(config))
|
||||
.nodeRegistry(new MemoryNodeRegistry())
|
||||
.networkHandler(new DefaultNetworkHandler())
|
||||
.security(new BouncySecurity())
|
||||
.addressRepo(addressRepo())
|
||||
.inventory(inventory())
|
||||
.messageRepo(messageRepo())
|
||||
.nodeRegistry(nodeRegistry())
|
||||
.powRepo(proofOfWorkRepo())
|
||||
.networkHandler(networkHandler())
|
||||
.listener(serverListener())
|
||||
.customCommandHandler(commandHandler())
|
||||
.security(security())
|
||||
.port(port)
|
||||
.connectionLimit(connectionLimit)
|
||||
.connectionTTL(connectionTTL)
|
||||
.listener(plaintext -> {
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Set<BitmessageAddress> admins() {
|
||||
security();
|
||||
return Utils.readOrCreateList(
|
||||
ADMIN_LIST,
|
||||
"# Admins can send commands to the server.\n"
|
||||
).stream().map(BitmessageAddress::new).collect(toSet());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Set<BitmessageAddress> clients() {
|
||||
security();
|
||||
return Utils.readOrCreateList(
|
||||
CLIENT_LIST,
|
||||
"# Clients may send incomplete objects for proof of work.\n"
|
||||
).stream().map(BitmessageAddress::new).collect(toSet());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Set<String> whitelist() {
|
||||
return Utils.readOrCreateList(
|
||||
"whitelist.conf",
|
||||
WHITELIST,
|
||||
"# If there are any Bitmessage addresses in the whitelist, only those will be shown.\n" +
|
||||
"# blacklist.conf will be ignored, but shortlist.conf will be applied to whitelisted addresses.\n"
|
||||
);
|
||||
@ -57,7 +150,7 @@ public class JabitServerConfig {
|
||||
@Bean
|
||||
public Set<String> shortlist() {
|
||||
return Utils.readOrCreateList(
|
||||
"shortlist.conf",
|
||||
SHORTLIST,
|
||||
"# Broadcasts of these addresses will be restricted to the last " + SHORTLIST_SIZE + " entries.\n\n" +
|
||||
"# Time Service:\n" +
|
||||
"BM-BcbRqcFFSQUUmXFKsPJgVQPSiFA3Xash\n\n" +
|
||||
@ -69,7 +162,7 @@ public class JabitServerConfig {
|
||||
@Bean
|
||||
public Set<String> blacklist() {
|
||||
return Utils.readOrCreateList(
|
||||
"blacklist.conf",
|
||||
BLACKLIST,
|
||||
"# Bitmessage addresses in this file are being ignored and their broadcasts won't be returned.\n"
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import ch.dissem.bitmessage.InternalContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.CustomMessage;
|
||||
import ch.dissem.bitmessage.entity.MessagePayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
||||
import ch.dissem.bitmessage.extensions.CryptoCustomMessage;
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest;
|
||||
import ch.dissem.bitmessage.ports.CustomCommandHandler;
|
||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||
import ch.dissem.bitmessage.server.repository.ServerProofOfWorkRepository;
|
||||
import ch.dissem.bitmessage.utils.UnixTime;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.CALCULATING;
|
||||
import static ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest.Request.COMPLETE;
|
||||
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class ProofOfWorkRequestHandler implements CustomCommandHandler, InternalContext.ContextHolder {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ProofOfWorkRequestHandler.class);
|
||||
|
||||
private final List<byte[]> decryptionKeys;
|
||||
private final ServerProofOfWorkRepository repo;
|
||||
private BitmessageAddress serverIdentity;
|
||||
private ProofOfWorkEngine engine;
|
||||
private InternalContext context;
|
||||
|
||||
public ProofOfWorkRequestHandler(ServerProofOfWorkRepository repo, Collection<BitmessageAddress> clients) {
|
||||
this.repo = repo;
|
||||
decryptionKeys = clients.stream().map(BitmessageAddress::getPublicDecryptionKey).collect(Collectors.toList());
|
||||
new Timer().schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
doMissingProofOfWork();
|
||||
}
|
||||
}, 15_000); // After 15 seconds
|
||||
new Timer().schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
repo.cleanupTasks(7 * DAY);
|
||||
}
|
||||
}, 60_000, DAY * 1000); // First time after 1 minute, then daily
|
||||
}
|
||||
|
||||
public void doMissingProofOfWork() {
|
||||
List<ServerProofOfWorkRepository.Task> incompleteTasks = repo.getIncompleteTasks();
|
||||
LOG.info("Doing POW for " + incompleteTasks.size() + " tasks.");
|
||||
for (ServerProofOfWorkRepository.Task task : incompleteTasks) {
|
||||
engine.calculateNonce(task.initialHash, task.target, repo::updateTask);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessagePayload handle(CustomMessage message) {
|
||||
try {
|
||||
CryptoCustomMessage<ProofOfWorkRequest> cryptoMessage = CryptoCustomMessage.read(message,
|
||||
ProofOfWorkRequest::read);
|
||||
ProofOfWorkRequest request = decrypt(cryptoMessage);
|
||||
if (request == null) {
|
||||
return CustomMessage.error(
|
||||
"Unknown sender. Please ask the server's administrator to add you as a client. " +
|
||||
"For this he'll need your identity."
|
||||
);
|
||||
}
|
||||
switch (request.getRequest()) {
|
||||
case CALCULATE:
|
||||
if (!repo.hasTask(request.getInitialHash())) {
|
||||
repo.storeTask(request);
|
||||
// TODO: This is probably the place to do some book-keeping
|
||||
// if we want to bill our customers.
|
||||
engine.calculateNonce(request.getInitialHash(), request.getData(), repo::updateTask);
|
||||
return new CryptoCustomMessage<>(
|
||||
new ProofOfWorkRequest(getIdentity(), request.getInitialHash(), CALCULATING, new byte[0])
|
||||
);
|
||||
} else {
|
||||
byte[] nonce = repo.getNonce(request);
|
||||
CryptoCustomMessage<ProofOfWorkRequest> response;
|
||||
if (nonce != null) {
|
||||
response = new CryptoCustomMessage<>(
|
||||
new ProofOfWorkRequest(getIdentity(), request.getInitialHash(), COMPLETE, nonce)
|
||||
);
|
||||
} else {
|
||||
response = new CryptoCustomMessage<>(
|
||||
new ProofOfWorkRequest(getIdentity(), request.getInitialHash(), CALCULATING, new byte[0])
|
||||
);
|
||||
}
|
||||
response.signAndEncrypt(serverIdentity, request.getSender().getPubkey().getEncryptionKey());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return CustomMessage.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private BitmessageAddress getIdentity() {
|
||||
if (serverIdentity == null) {
|
||||
synchronized (this) {
|
||||
if (serverIdentity == null) {
|
||||
serverIdentity = context.getAddressRepository().getIdentities().stream().findFirst().orElseGet(() -> {
|
||||
final BitmessageAddress identity = new BitmessageAddress(new PrivateKey(
|
||||
false,
|
||||
context.getStreams()[0],
|
||||
context.getNetworkNonceTrialsPerByte(),
|
||||
context.getNetworkExtraBytes()
|
||||
));
|
||||
context.getAddressRepository().save(identity);
|
||||
return identity;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return serverIdentity;
|
||||
}
|
||||
|
||||
private ProofOfWorkRequest decrypt(CryptoCustomMessage<ProofOfWorkRequest> cryptoMessage) {
|
||||
for (byte[] key : decryptionKeys) {
|
||||
try {
|
||||
return cryptoMessage.decrypt(key);
|
||||
} catch (DecryptionFailedException ignore) {
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(InternalContext context) {
|
||||
this.context = context;
|
||||
this.engine = context.getProofOfWorkEngine();
|
||||
}
|
||||
}
|
137
src/main/java/ch/dissem/bitmessage/server/ServerListener.java
Normal file
137
src/main/java/ch/dissem/bitmessage/server/ServerListener.java
Normal file
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import ch.dissem.bitmessage.BitmessageContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.Plaintext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Scanner;
|
||||
|
||||
import static ch.dissem.bitmessage.server.Constants.*;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class ServerListener implements BitmessageContext.Listener {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ServerListener.class);
|
||||
|
||||
private final Collection<BitmessageAddress> admins;
|
||||
private final Collection<BitmessageAddress> clients;
|
||||
|
||||
private final Collection<String> whitelist;
|
||||
private final Collection<String> shortlist;
|
||||
private final Collection<String> blacklist;
|
||||
|
||||
public ServerListener(Collection<BitmessageAddress> admins,
|
||||
Collection<BitmessageAddress> clients,
|
||||
Collection<String> whitelist,
|
||||
Collection<String> shortlist,
|
||||
Collection<String> blacklist) {
|
||||
this.admins = admins;
|
||||
this.clients = clients;
|
||||
this.whitelist = whitelist;
|
||||
this.shortlist = shortlist;
|
||||
this.blacklist = blacklist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Plaintext message) {
|
||||
if (admins.contains(message.getFrom())) {
|
||||
String[] command = message.getSubject().trim().toLowerCase().split("\\s+");
|
||||
String data = message.getText();
|
||||
if (command.length == 2) {
|
||||
switch (command[1]) {
|
||||
case "client":
|
||||
case "clients":
|
||||
updateUserList(CLIENT_LIST, clients, command[0], data);
|
||||
break;
|
||||
case "admin":
|
||||
case "admins":
|
||||
case "administrator":
|
||||
case "administrators":
|
||||
updateUserList(ADMIN_LIST, admins, command[0], data);
|
||||
break;
|
||||
case "whitelist":
|
||||
updateList(WHITELIST, whitelist, command[0], data);
|
||||
break;
|
||||
case "shortlist":
|
||||
updateList(SHORTLIST, shortlist, command[0], data);
|
||||
break;
|
||||
case "blacklist":
|
||||
updateList(BLACKLIST, blacklist, command[0], data);
|
||||
break;
|
||||
default:
|
||||
LOG.trace("ignoring unknown command " + message.getSubject());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUserList(String file, Collection<BitmessageAddress> list, String command, String data) {
|
||||
switch (command) {
|
||||
case "set":
|
||||
list.clear();
|
||||
case "add":
|
||||
Scanner scanner = new Scanner(data);
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
try {
|
||||
list.add(new BitmessageAddress(line));
|
||||
} catch (Exception e) {
|
||||
LOG.info(command + " " + file + ": ignoring line: " + line);
|
||||
}
|
||||
}
|
||||
Utils.saveList(file, list.stream().map(BitmessageAddress::getAddress));
|
||||
break;
|
||||
case "remove":
|
||||
list.removeIf(address -> data.contains(address.getAddress()));
|
||||
Utils.saveList(file, list.stream().map(BitmessageAddress::getAddress));
|
||||
break;
|
||||
default:
|
||||
LOG.info("unknown command " + command + " on list " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateList(String file, Collection<String> list, String command, String data) {
|
||||
switch (command) {
|
||||
case "set":
|
||||
list.clear();
|
||||
case "add":
|
||||
Scanner scanner = new Scanner(data);
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
try {
|
||||
list.add(new BitmessageAddress(line).getAddress());
|
||||
} catch (Exception e) {
|
||||
LOG.info(command + " " + file + ": ignoring line: " + line);
|
||||
}
|
||||
}
|
||||
Utils.saveList(file, list.stream());
|
||||
break;
|
||||
case "remove":
|
||||
list.removeIf(data::contains);
|
||||
Utils.saveList(file, list.stream());
|
||||
break;
|
||||
default:
|
||||
LOG.info("unknown command " + command + " on list " + file);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,29 @@
|
||||
/*
|
||||
* 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.server;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Utils {
|
||||
public static Set<String> readOrCreateList(String filename, String content) {
|
||||
@ -23,6 +42,24 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveList(String filename, Stream<String> content) {
|
||||
try {
|
||||
File file = new File(filename);
|
||||
try (FileWriter fw = new FileWriter(file)) {
|
||||
content.forEach(l -> {
|
||||
try {
|
||||
fw.write(l);
|
||||
fw.write(System.lineSeparator());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> readList(File file) {
|
||||
Set<String> result = new HashSet<>();
|
||||
try {
|
||||
@ -38,4 +75,11 @@ public class Utils {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean zero(byte[] nonce) {
|
||||
for (byte b : nonce) {
|
||||
if (b != 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.server.entities;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class Update<T> {
|
||||
public final T oldValue;
|
||||
public final T newValue;
|
||||
|
||||
public Update(T oldValue, T newValue) {
|
||||
this.oldValue = oldValue;
|
||||
this.newValue = newValue;
|
||||
}
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.server.repository;
|
||||
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest;
|
||||
import ch.dissem.bitmessage.repository.JdbcConfig;
|
||||
import ch.dissem.bitmessage.repository.JdbcHelper;
|
||||
import ch.dissem.bitmessage.utils.UnixTime;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class ServerProofOfWorkRepository extends JdbcHelper {
|
||||
|
||||
public ServerProofOfWorkRepository(JdbcConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* client (can be removed once the new IV is returned)
|
||||
* IV (without nonce)
|
||||
* IV (with nonce, can be removed once the new IV is returned)
|
||||
* status: calculating, finished, confirmed
|
||||
* data (can be removed once POW calculation is done)
|
||||
*/
|
||||
public void storeTask(ProofOfWorkRequest request) {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"INSERT INTO ProofOfWorkTask (initial_hash, client, target, timestamp) VALUES (?, ?, ?, ?)");
|
||||
ps.setBytes(1, request.getInitialHash());
|
||||
ps.setString(2, request.getSender().getAddress());
|
||||
ps.setBytes(3, request.getData());
|
||||
ps.setLong(4, UnixTime.now());
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTask(byte[] initalHash, byte[] nonce) {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE ProofOfWorkTask SET nonce = ? WHERE initial_hash = ?");
|
||||
ps.setBytes(1, nonce);
|
||||
ps.setBytes(2, initalHash);
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getNonce(ProofOfWorkRequest request) {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement("SELECT nonce FROM ProofOfWorkTask WHERE initial_hash = ?");
|
||||
ps.setBytes(1, request.getInitialHash());
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
return rs.getBytes(1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasTask(byte[] initialHash) {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement("SELECT count(1) FROM ProofOfWorkTask WHERE initial_hash = ?");
|
||||
ps.setBytes(1, initialHash);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
rs.next();
|
||||
return rs.getInt(1) > 0;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Task> getIncompleteTasks() {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
List<Task> result = new LinkedList<>();
|
||||
ResultSet rs = connection.createStatement().executeQuery(
|
||||
"SELECT initial_hash, target FROM ProofOfWorkTask WHERE nonce IS NULL");
|
||||
while (rs.next()) {
|
||||
result.add(new Task(
|
||||
rs.getBytes(1),
|
||||
rs.getBytes(2)
|
||||
));
|
||||
}
|
||||
return result;
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanupTasks(long ageInSeconds) {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"DELETE FROM ProofOfWorkTask WHERE timestamp < ?");
|
||||
ps.setLong(1, UnixTime.now(-ageInSeconds));
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Task {
|
||||
public final byte[] initialHash;
|
||||
public final byte[] target;
|
||||
|
||||
private Task(byte[] initialHash, byte[] target) {
|
||||
this.initialHash = initialHash;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
CREATE TABLE ProofOfWorkTask (
|
||||
initial_hash BINARY(64) NOT NULL PRIMARY KEY,
|
||||
client VARCHAR(40) NOT NULL,
|
||||
target BINARY(32),
|
||||
nonce BINARY(8),
|
||||
timestamp BIGINT NOT NULL,
|
||||
);
|
Loading…
Reference in New Issue
Block a user