Server POW (work in progress, major refactoring)
This commit is contained in:
parent
48ddfbbbd0
commit
891294f267
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'
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class JabitServerConfig {
|
||||
.messageRepo(new JdbcMessageRepository(config))
|
||||
.nodeRegistry(new MemoryNodeRegistry())
|
||||
.networkHandler(new DefaultNetworkHandler())
|
||||
.objectListener(new ServerObjectListener(admins(), clients(), whitelist(), shortlist(), blacklist()))
|
||||
.listener(new ServerListener(admins(), clients(), whitelist(), shortlist(), blacklist()))
|
||||
.security(new BouncySecurity())
|
||||
.port(port)
|
||||
.connectionLimit(connectionLimit)
|
||||
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.CustomMessage;
|
||||
import ch.dissem.bitmessage.entity.MessagePayload;
|
||||
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.ProofOfWorkRepository;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class ProofOfWorkRequestHandler implements CustomCommandHandler {
|
||||
private final List<byte[]> decryptionKeys = new ArrayList<>();
|
||||
private ProofOfWorkRepository repo;
|
||||
private ProofOfWorkEngine engine;
|
||||
|
||||
@Override
|
||||
public MessagePayload handle(CustomMessage message) {
|
||||
try {
|
||||
CryptoCustomMessage<ProofOfWorkRequest> cryptoMessage = CryptoCustomMessage.read(message.getData(),
|
||||
(sender, in) -> ProofOfWorkRequest.read(sender, in));
|
||||
ProofOfWorkRequest request = decrypt(cryptoMessage);
|
||||
if (request == null) return error("Unknown encryption key.");
|
||||
switch (request.getRequest()) {
|
||||
case CALCULATE:
|
||||
repo.storeTask(request); // FIXME
|
||||
engine.calculateNonce(request.getInitialHash(), request.getData(), nonce -> {
|
||||
|
||||
});
|
||||
}
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private MessagePayload error(String message) {
|
||||
try {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write("ERROR\n".getBytes("UTF-8"));
|
||||
out.write(message.getBytes("UTF-8"));
|
||||
return new CustomMessage(out.toByteArray());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
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,177 +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.server;
|
||||
|
||||
import ch.dissem.bitmessage.DefaultObjectListener;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.Plaintext;
|
||||
import ch.dissem.bitmessage.entity.payload.Broadcast;
|
||||
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Scanner;
|
||||
|
||||
import static ch.dissem.bitmessage.factory.Factory.getObjectMessage;
|
||||
import static ch.dissem.bitmessage.server.Constants.*;
|
||||
import static ch.dissem.bitmessage.server.Utils.zero;
|
||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class ServerObjectListener extends DefaultObjectListener {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ServerObjectListener.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 ServerObjectListener(Collection<BitmessageAddress> admins, Collection<BitmessageAddress> clients, Collection<String> whitelist, Collection<String> shortlist, Collection<String> blacklist) {
|
||||
super(p -> {
|
||||
});
|
||||
this.admins = admins;
|
||||
this.clients = clients;
|
||||
this.whitelist = whitelist;
|
||||
this.shortlist = shortlist;
|
||||
this.blacklist = blacklist;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void receive(ObjectMessage object, Broadcast broadcast) throws IOException {
|
||||
processCommands(broadcast);
|
||||
if (zero(object.getNonce())) {
|
||||
calculateNonceForClient(object, broadcast);
|
||||
} else {
|
||||
super.receive(object, broadcast);
|
||||
}
|
||||
}
|
||||
|
||||
private void processCommands(Broadcast broadcast) throws IOException {
|
||||
for (BitmessageAddress admin : admins) {
|
||||
try {
|
||||
broadcast.decrypt(admin);
|
||||
Plaintext message = broadcast.getPlaintext();
|
||||
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(WHITELIST, shortlist, command[0], data);
|
||||
break;
|
||||
case "blacklist":
|
||||
updateList(WHITELIST, blacklist, command[0], data);
|
||||
break;
|
||||
default:
|
||||
LOG.trace("ignoring unknown command " + message.getSubject());
|
||||
}
|
||||
}
|
||||
} catch (DecryptionFailedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateNonceForClient(ObjectMessage object, Broadcast broadcast) throws IOException {
|
||||
// TODO: prevent doing calculation twice
|
||||
for (BitmessageAddress client : clients) {
|
||||
try {
|
||||
broadcast.decrypt(client);
|
||||
byte[] message = broadcast.getPlaintext().getMessage();
|
||||
final ObjectMessage toRelay = getObjectMessage(3, new ByteArrayInputStream(message), message.length);
|
||||
security().doProofOfWork(toRelay,
|
||||
ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes(),
|
||||
(nonce) -> {
|
||||
toRelay.setNonce(nonce);
|
||||
ctx.getInventory().storeObject(object);
|
||||
ctx.getNetworkHandler().offer(object.getInventoryVector());
|
||||
}
|
||||
);
|
||||
} catch (DecryptionFailedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,124 @@
|
||||
/*
|
||||
* 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.InternalContext;
|
||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.extensions.pow.ProofOfWorkRequest;
|
||||
import ch.dissem.bitmessage.repository.JdbcConfig;
|
||||
import ch.dissem.bitmessage.repository.JdbcHelper;
|
||||
import ch.dissem.bitmessage.server.entities.Update;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static ch.dissem.bitmessage.server.repository.ProofOfWorkRepository.Status.*;
|
||||
|
||||
/**
|
||||
* @author Christian Basler
|
||||
*/
|
||||
public class ProofOfWorkRepository extends JdbcHelper implements InternalContext.ContextHolder {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ProofOfWorkRepository.class);
|
||||
|
||||
private InternalContext context;
|
||||
|
||||
protected ProofOfWorkRepository(JdbcConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContext(InternalContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, status) VALUES (?, ?, ?, ?)");
|
||||
ps.setBytes(1, request.getInitialHash());
|
||||
ps.setString(2, request.getClient().getAddress());
|
||||
ps.setBytes(3, request.getData());
|
||||
ps.setString(4, CALCULATING.name());
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTask(InventoryVector temporaryIV, InventoryVector newIV) {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE ProofOfWorkTask SET IV = ?, status = ?, data = NULL WHERE temporaryIV = ?");
|
||||
ps.setBytes(1, newIV.getHash());
|
||||
ps.setString(2, FINISHED.name());
|
||||
ps.setBytes(3, temporaryIV.getHash());
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Update<InventoryVector>> getUnconfirmed(BitmessageAddress client) {
|
||||
List<Update<InventoryVector>> result = new LinkedList<>();
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement("SELECT temporaryIV, IV FROM ProofOfWorkTask WHERE client = ? AND status = ?");
|
||||
ps.setString(1, client.getAddress());
|
||||
ps.setString(2, FINISHED.name());
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while (rs.next()) {
|
||||
InventoryVector temporaryIV = new InventoryVector(rs.getBytes(1));
|
||||
InventoryVector iv = new InventoryVector(rs.getBytes(2));
|
||||
result.add(new Update<>(temporaryIV, iv));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void confirm(Stream<InventoryVector> unconfirmed) {
|
||||
try (Connection connection = config.getConnection()) {
|
||||
PreparedStatement ps = connection.prepareStatement(
|
||||
"UPDATE ProofOfWorkTask SET status = ?, IV = NULL, client = NULL WHERE IV = ANY(?)");
|
||||
ps.setString(1, CONFIRMED.name());
|
||||
ps.setArray(2, connection.createArrayOf("BINARY", unconfirmed.map(InventoryVector::getHash).toArray()));
|
||||
ps.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
CALCULATING, FINISHED, CONFIRMED
|
||||
}
|
||||
}
|
7
src/main/resources/db/migration/V1.0_ProofOfWorkTask.sql
Normal file
7
src/main/resources/db/migration/V1.0_ProofOfWorkTask.sql
Normal file
@ -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),
|
||||
status VARCHAR(20),
|
||||
);
|
Loading…
Reference in New Issue
Block a user