Added concierge service to clean up KeyTree
This commit is contained in:
parent
ce7ef852bb
commit
4112f70dc3
14 changed files with 622 additions and 32 deletions
6
arch.md
6
arch.md
|
@ -46,3 +46,9 @@ if SUC:
|
|||
|
||||
## REP ##
|
||||
TODO
|
||||
|
||||
# TODO #
|
||||
|
||||
[ ] Admin communication
|
||||
[ ] Admin interface
|
||||
[ ] Adapt brainflyer
|
||||
|
|
14
bfclient/hash/hash160.h
Normal file
14
bfclient/hash/hash160.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* Copyright (c) 2015 Ryan Castellucci, All Rights Reserved */
|
||||
#ifndef __BRAINFLAYER_HASH160_H_
|
||||
#define __BRAINFLAYER_HASH160_H_
|
||||
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/ripemd.h>
|
||||
|
||||
typedef union hash160_u {
|
||||
unsigned char uc[RIPEMD160_DIGEST_LENGTH];
|
||||
uint32_t ul[RIPEMD160_DIGEST_LENGTH>>2];
|
||||
} hash160_t;
|
||||
|
||||
/* vim: set ts=2 sw=2 et ai si: */
|
||||
#endif /* __BRAINFLAYER_HASH160_H_ */
|
286
bfclient/hash/ripemd160_256.c
Normal file
286
bfclient/hash/ripemd160_256.c
Normal file
|
@ -0,0 +1,286 @@
|
|||
#define _RIPEMD160_C_ 1
|
||||
|
||||
#include "ripemd160_256.h"
|
||||
|
||||
// adapted by Pieter Wuille in 2012; all changes are in the public domain
|
||||
// modified by Ryan Castellucci in 2015; all changes are in the public domain
|
||||
|
||||
/*
|
||||
*
|
||||
* RIPEMD160.c : RIPEMD-160 implementation
|
||||
*
|
||||
* Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
*
|
||||
* ===================================================================
|
||||
* The contents of this file are dedicated to the public domain. To
|
||||
* the extent that dedication to the public domain is not available,
|
||||
* everyone is granted a worldwide, perpetual, royalty-free,
|
||||
* non-exclusive license to exercise all rights associated with the
|
||||
* contents of this file for any purpose whatsoever.
|
||||
* No rights are reserved.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
* ===================================================================
|
||||
*
|
||||
* Country of origin: Canada
|
||||
*
|
||||
* This implementation (written in C) is based on an implementation the author
|
||||
* wrote in Python.
|
||||
*
|
||||
* This implementation was written with reference to the RIPEMD-160
|
||||
* specification, which is available at:
|
||||
* http://homes.esat.kuleuven.be/~cosicart/pdf/AB-9601/
|
||||
*
|
||||
* It is also documented in the _Handbook of Applied Cryptography_, as
|
||||
* Algorithm 9.55. It's on page 30 of the following PDF file:
|
||||
* http://www.cacr.math.uwaterloo.ca/hac/about/chap9.pdf
|
||||
*
|
||||
* The RIPEMD-160 specification doesn't really tell us how to do padding, but
|
||||
* since RIPEMD-160 is inspired by MD4, you can use the padding algorithm from
|
||||
* RFC 1320.
|
||||
*
|
||||
* According to http://www.users.zetnet.co.uk/hopwood/crypto/scan/md.html:
|
||||
* "RIPEMD-160 is big-bit-endian, little-byte-endian, and left-justified."
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define RIPEMD160_DIGEST_SIZE 20
|
||||
#define BLOCK_SIZE 64
|
||||
|
||||
/* cyclic left-shift the 32-bit word n left by s bits */
|
||||
#define ROL(s, n) (((n) << (s)) | ((n) >> (32-(s))))
|
||||
|
||||
/* Initial values for the chaining variables.
|
||||
* This is just 0123456789ABCDEFFEDCBA9876543210F0E1D2C3 in little-endian. */
|
||||
static const uint32_t initial_h[5] = { 0x67452301u, 0xEFCDAB89u, 0x98BADCFEu, 0x10325476u, 0xC3D2E1F0u };
|
||||
|
||||
/* Ordering of message words. Based on the permutations rho(i) and pi(i), defined as follows:
|
||||
*
|
||||
* rho(i) := { 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8 }[i] 0 <= i <= 15
|
||||
*
|
||||
* pi(i) := 9*i + 5 (mod 16)
|
||||
*
|
||||
* Line | Round 1 | Round 2 | Round 3 | Round 4 | Round 5
|
||||
* -------+-----------+-----------+-----------+-----------+-----------
|
||||
* left | id | rho | rho^2 | rho^3 | rho^4
|
||||
* right | pi | rho pi | rho^2 pi | rho^3 pi | rho^4 pi
|
||||
*/
|
||||
|
||||
/* Left line */
|
||||
static const uint8_t RL[5][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, /* Round 1: id */
|
||||
{ 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8 }, /* Round 2: rho */
|
||||
{ 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12 }, /* Round 3: rho^2 */
|
||||
{ 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2 }, /* Round 4: rho^3 */
|
||||
{ 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 } /* Round 5: rho^4 */
|
||||
};
|
||||
|
||||
/* Right line */
|
||||
static const uint8_t RR[5][16] = {
|
||||
{ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12 }, /* Round 1: pi */
|
||||
{ 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2 }, /* Round 2: rho pi */
|
||||
{ 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13 }, /* Round 3: rho^2 pi */
|
||||
{ 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14 }, /* Round 4: rho^3 pi */
|
||||
{ 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 } /* Round 5: rho^4 pi */
|
||||
};
|
||||
|
||||
/*
|
||||
* Shifts - Since we don't actually re-order the message words according to
|
||||
* the permutations above (we could, but it would be slower), these tables
|
||||
* come with the permutations pre-applied.
|
||||
*/
|
||||
|
||||
/* Shifts, left line */
|
||||
static const uint8_t SL[5][16] = {
|
||||
{ 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8 }, /* Round 1 */
|
||||
{ 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12 }, /* Round 2 */
|
||||
{ 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5 }, /* Round 3 */
|
||||
{ 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12 }, /* Round 4 */
|
||||
{ 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 } /* Round 5 */
|
||||
};
|
||||
|
||||
/* Shifts, right line */
|
||||
static const uint8_t SR[5][16] = {
|
||||
{ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6 }, /* Round 1 */
|
||||
{ 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11 }, /* Round 2 */
|
||||
{ 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5 }, /* Round 3 */
|
||||
{ 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8 }, /* Round 4 */
|
||||
{ 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 } /* Round 5 */
|
||||
};
|
||||
|
||||
/* static padding for 256 bit input */
|
||||
static const uint8_t pad256[32] = {
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* length 256 bits, little endian uint64_t */
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* Boolean functions */
|
||||
|
||||
#define F1(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define F2(x, y, z) (((x) & (y)) | (~(x) & (z)))
|
||||
#define F3(x, y, z) (((x) | ~(y)) ^ (z))
|
||||
#define F4(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define F5(x, y, z) ((x) ^ ((y) | ~(z)))
|
||||
|
||||
/* Round constants, left line */
|
||||
static const uint32_t KL[5] = {
|
||||
0x00000000u, /* Round 1: 0 */
|
||||
0x5A827999u, /* Round 2: floor(2**30 * sqrt(2)) */
|
||||
0x6ED9EBA1u, /* Round 3: floor(2**30 * sqrt(3)) */
|
||||
0x8F1BBCDCu, /* Round 4: floor(2**30 * sqrt(5)) */
|
||||
0xA953FD4Eu /* Round 5: floor(2**30 * sqrt(7)) */
|
||||
};
|
||||
|
||||
/* Round constants, right line */
|
||||
static const uint32_t KR[5] = {
|
||||
0x50A28BE6u, /* Round 1: floor(2**30 * cubert(2)) */
|
||||
0x5C4DD124u, /* Round 2: floor(2**30 * cubert(3)) */
|
||||
0x6D703EF3u, /* Round 3: floor(2**30 * cubert(5)) */
|
||||
0x7A6D76E9u, /* Round 4: floor(2**30 * cubert(7)) */
|
||||
0x00000000u /* Round 5: 0 */
|
||||
};
|
||||
|
||||
static inline void byteswap32(uint32_t *v)
|
||||
{
|
||||
union { uint32_t w; uint8_t b[4]; } x, y;
|
||||
|
||||
x.w = *v;
|
||||
y.b[0] = x.b[3];
|
||||
y.b[1] = x.b[2];
|
||||
y.b[2] = x.b[1];
|
||||
y.b[3] = x.b[0];
|
||||
*v = y.w;
|
||||
|
||||
/* Wipe temporary variables */
|
||||
x.w = y.w = 0;
|
||||
}
|
||||
|
||||
static inline void byteswap_digest(uint32_t *p)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
byteswap32(p++);
|
||||
byteswap32(p++);
|
||||
byteswap32(p++);
|
||||
byteswap32(p++);
|
||||
}
|
||||
}
|
||||
|
||||
/* The RIPEMD160 compression function. */
|
||||
static inline void ripemd160_rawcompress(void *pbuf, void *ph)
|
||||
{
|
||||
uint8_t w, round;
|
||||
uint32_t T;
|
||||
uint32_t AL, BL, CL, DL, EL; /* left line */
|
||||
uint32_t AR, BR, CR, DR, ER; /* right line */
|
||||
|
||||
uint32_t *buf = pbuf;
|
||||
uint32_t *h = ph;
|
||||
|
||||
/* Byte-swap the buffer if we're on a big-endian machine */
|
||||
#ifdef PCT_BIG_ENDIAN
|
||||
byteswap_digest(buf);
|
||||
#endif
|
||||
|
||||
/* initialize state */
|
||||
memcpy(h, initial_h, RIPEMD160_DIGEST_SIZE);
|
||||
|
||||
/* Load the left and right lines with the initial state */
|
||||
AL = AR = h[0];
|
||||
BL = BR = h[1];
|
||||
CL = CR = h[2];
|
||||
DL = DR = h[3];
|
||||
EL = ER = h[4];
|
||||
|
||||
/* Round 1 */
|
||||
round = 0;
|
||||
for (w = 0; w < 16; w++) { /* left line */
|
||||
T = ROL(SL[round][w], AL + F1(BL, CL, DL) + buf[RL[round][w]] + KL[round]) + EL;
|
||||
AL = EL; EL = DL; DL = ROL(10, CL); CL = BL; BL = T;
|
||||
}
|
||||
for (w = 0; w < 16; w++) { /* right line */
|
||||
T = ROL(SR[round][w], AR + F5(BR, CR, DR) + buf[RR[round][w]] + KR[round]) + ER;
|
||||
AR = ER; ER = DR; DR = ROL(10, CR); CR = BR; BR = T;
|
||||
}
|
||||
|
||||
/* Round 2 */
|
||||
round++;
|
||||
for (w = 0; w < 16; w++) { /* left line */
|
||||
T = ROL(SL[round][w], AL + F2(BL, CL, DL) + buf[RL[round][w]] + KL[round]) + EL;
|
||||
AL = EL; EL = DL; DL = ROL(10, CL); CL = BL; BL = T;
|
||||
}
|
||||
for (w = 0; w < 16; w++) { /* right line */
|
||||
T = ROL(SR[round][w], AR + F4(BR, CR, DR) + buf[RR[round][w]] + KR[round]) + ER;
|
||||
AR = ER; ER = DR; DR = ROL(10, CR); CR = BR; BR = T;
|
||||
}
|
||||
|
||||
/* Round 3 */
|
||||
round++;
|
||||
for (w = 0; w < 16; w++) { /* left line */
|
||||
T = ROL(SL[round][w], AL + F3(BL, CL, DL) + buf[RL[round][w]] + KL[round]) + EL;
|
||||
AL = EL; EL = DL; DL = ROL(10, CL); CL = BL; BL = T;
|
||||
}
|
||||
for (w = 0; w < 16; w++) { /* right line */
|
||||
T = ROL(SR[round][w], AR + F3(BR, CR, DR) + buf[RR[round][w]] + KR[round]) + ER;
|
||||
AR = ER; ER = DR; DR = ROL(10, CR); CR = BR; BR = T;
|
||||
}
|
||||
|
||||
/* Round 4 */
|
||||
round++;
|
||||
for (w = 0; w < 16; w++) { /* left line */
|
||||
T = ROL(SL[round][w], AL + F4(BL, CL, DL) + buf[RL[round][w]] + KL[round]) + EL;
|
||||
AL = EL; EL = DL; DL = ROL(10, CL); CL = BL; BL = T;
|
||||
}
|
||||
for (w = 0; w < 16; w++) { /* right line */
|
||||
T = ROL(SR[round][w], AR + F2(BR, CR, DR) + buf[RR[round][w]] + KR[round]) + ER;
|
||||
AR = ER; ER = DR; DR = ROL(10, CR); CR = BR; BR = T;
|
||||
}
|
||||
|
||||
/* Round 5 */
|
||||
round++;
|
||||
for (w = 0; w < 16; w++) { /* left line */
|
||||
T = ROL(SL[round][w], AL + F5(BL, CL, DL) + buf[RL[round][w]] + KL[round]) + EL;
|
||||
AL = EL; EL = DL; DL = ROL(10, CL); CL = BL; BL = T;
|
||||
}
|
||||
for (w = 0; w < 16; w++) { /* right line */
|
||||
T = ROL(SR[round][w], AR + F1(BR, CR, DR) + buf[RR[round][w]] + KR[round]) + ER;
|
||||
AR = ER; ER = DR; DR = ROL(10, CR); CR = BR; BR = T;
|
||||
}
|
||||
|
||||
/* Final mixing stage */
|
||||
T = h[1] + CL + DR;
|
||||
h[1] = h[2] + DL + ER;
|
||||
h[2] = h[3] + EL + AR;
|
||||
h[3] = h[4] + AL + BR;
|
||||
h[4] = h[0] + BL + CR;
|
||||
h[0] = T;
|
||||
|
||||
/* Byte-swap the output if we're on a big-endian machine */
|
||||
#ifdef PCT_BIG_ENDIAN
|
||||
byteswap_digest(h);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ripemd160_256(const void *in, void *out) {
|
||||
unsigned char buf[64];
|
||||
/* copy input data */
|
||||
memcpy(buf + 0, in, 32);
|
||||
/* append fixed padding */
|
||||
memcpy(buf + 32, pad256, 32);
|
||||
/* compute and output hash */
|
||||
ripemd160_rawcompress(buf, out);
|
||||
}
|
8
bfclient/hash/ripemd160_256.h
Normal file
8
bfclient/hash/ripemd160_256.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef __RIPEMD160_256_H_
|
||||
#define __RIPEMD160_256_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void ripemd160_256(const void *in, void *out);
|
||||
|
||||
#endif//__RIPEMD160_256_H_
|
54
client/src/client/ClientRunner.java
Normal file
54
client/src/client/ClientRunner.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
package client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
|
||||
public class ClientRunner implements Runnable {
|
||||
long sleep;
|
||||
private String host;
|
||||
private Integer port;
|
||||
private boolean skip;
|
||||
|
||||
public ClientRunner(String host, Integer port, long sleep, boolean skip) {
|
||||
this.sleep = sleep;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.skip = skip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Socket socket = new Socket(host, port);
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
|
||||
|
||||
out.println("WRK");
|
||||
String work = in.readLine();
|
||||
System.out.println("Sleep: "+sleep+", skip: "+skip+", work: "+work);
|
||||
|
||||
socket.close();
|
||||
if (!skip) {
|
||||
Thread.sleep(sleep);
|
||||
|
||||
socket = new Socket(host, port);
|
||||
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
|
||||
|
||||
out.println("RES");
|
||||
out.println(work);
|
||||
|
||||
socket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
28
client/src/client/CommClient.java
Normal file
28
client/src/client/CommClient.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package client;
|
||||
|
||||
public class CommClient {
|
||||
private static final String HOST = "localhost";
|
||||
private static final Integer PORT = 26765;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
new Thread(new ClientRunner(HOST, PORT, 5000, false)).start();
|
||||
new Thread(new ClientRunner(HOST, PORT, 15000, false)).start();
|
||||
new Thread(new ClientRunner(HOST, PORT, 0, true)).start();
|
||||
new Thread(new ClientRunner(HOST, PORT, 5000, false)).start();
|
||||
}
|
||||
|
||||
Thread.sleep(20000);
|
||||
System.out.println("******** BATCH 2 ********");
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
new Thread(new ClientRunner(HOST, PORT, 5000, false)).start();
|
||||
new Thread(new ClientRunner(HOST, PORT, 5000, false)).start();
|
||||
|
||||
new Thread(new ClientRunner(HOST, PORT, 15000, false)).start();
|
||||
new Thread(new ClientRunner(HOST, PORT, 0, true)).start();
|
||||
new Thread(new ClientRunner(HOST, PORT, 5000, false)).start();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,12 +18,14 @@ public class CnC {
|
|||
public static final int MAX_BITS = 62;
|
||||
|
||||
public static void main(String[] args) {
|
||||
KeyServer.init(55, 20);
|
||||
KeyServer.init(10, 7, 10000);
|
||||
CommServer server = new CommServer(PORT);
|
||||
try {
|
||||
server.listen();
|
||||
} catch (CommException e) {
|
||||
log.error("CommServer couldn't be started");
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ public class ClientWorker implements Runnable {
|
|||
public void run() {
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
|
||||
out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
|
||||
out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()), true);
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't open communication channel", e);
|
||||
closeCommChannel();
|
||||
|
@ -53,6 +53,8 @@ public class ClientWorker implements Runnable {
|
|||
default:
|
||||
assert false;
|
||||
}
|
||||
|
||||
shutDown();
|
||||
}
|
||||
|
||||
private void closeCommChannel() {
|
||||
|
@ -71,12 +73,45 @@ public class ClientWorker implements Runnable {
|
|||
|
||||
private void sendWork() {
|
||||
KeyServer ks = KeyServer.getInstance();
|
||||
KeyRange kr = ks.getRange();
|
||||
out.println(kr.getStart() + " " + kr.getEnd() + " " + kr.getTotal());
|
||||
out.flush();
|
||||
KeyRange kr;
|
||||
|
||||
synchronized (ks) {
|
||||
kr = ks.getRange();
|
||||
ks.setInWork(kr);
|
||||
}
|
||||
|
||||
String work = Long.toHexString(kr.getStart()) + " " + Long.toHexString(kr.getEnd()) + " " + kr.getTotal();
|
||||
|
||||
out.println(work);
|
||||
|
||||
log.debug("Work <{}> sent to {}", work, clientSocket.getInetAddress());
|
||||
}
|
||||
|
||||
private void retrieveResult() {
|
||||
String[] result;
|
||||
try {
|
||||
result = in.readLine().split(" ");
|
||||
KeyRange kr = new KeyRange(Long.parseLong(result[0], 16), Long.parseLong(result[1], 16));
|
||||
|
||||
synchronized (KeyServer.getInstance()) {
|
||||
KeyServer.getInstance().setSearched(kr);
|
||||
}
|
||||
|
||||
log.debug("Got result for {}", kr);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't read result", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutDown() {
|
||||
closeCommChannel();
|
||||
if (!clientSocket.isClosed()) {
|
||||
try {
|
||||
clientSocket.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't close client socket", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ public class CommServer {
|
|||
log.debug("Opening socket on port {}", port);
|
||||
|
||||
try {
|
||||
this.serverSocket = new ServerSocket();
|
||||
this.serverSocket = new ServerSocket(port);
|
||||
} catch (IOException e) {
|
||||
log.error("Couldn't allocate server socket", e);
|
||||
|
||||
|
|
|
@ -5,28 +5,38 @@ import java.util.BitSet;
|
|||
import org.btcollider.cnc.CnC;
|
||||
|
||||
public class KeyRange {
|
||||
private BitSet start;
|
||||
private BitSet end;
|
||||
private Long start;
|
||||
private Long end;
|
||||
|
||||
public KeyRange(BitSet start, BitSet end) {
|
||||
assert start.length() <= CnC.MAX_BITS && end.length() <= CnC.MAX_BITS;
|
||||
|
||||
this.start = start.toLongArray()[0];
|
||||
this.end = end.toLongArray()[0];
|
||||
}
|
||||
|
||||
public KeyRange(Long start, Long end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
public BitSet getStart() {
|
||||
|
||||
public Long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public BitSet getEnd() {
|
||||
public Long getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public Long getTotal() {
|
||||
// asserted that <= MAX_BITS, first long contains all significant bits
|
||||
Long total = getStart().toLongArray()[0] - getEnd().toLongArray()[0];
|
||||
Long total = getEnd() - getStart() + 1; // +1 bc start is included too
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KeyRange [start=" + start + ", end=" + end + ", total=" + getTotal() +"]";
|
||||
}
|
||||
}
|
||||
|
|
45
cnc/src/main/java/org/btcollider/cnc/keysrv/KTConcierge.java
Normal file
45
cnc/src/main/java/org/btcollider/cnc/keysrv/KTConcierge.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package org.btcollider.cnc.keysrv;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class KTConcierge implements Runnable {
|
||||
final Logger log = LoggerFactory.getLogger(KeyServer.class);
|
||||
|
||||
private KeyTree keyTree;
|
||||
private long maxWorkSpan;
|
||||
|
||||
public KTConcierge(KeyTree keyTree, long maxWorkSpan) {
|
||||
this.keyTree = keyTree;
|
||||
this.maxWorkSpan = maxWorkSpan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
recCheckInWork(keyTree);
|
||||
}
|
||||
|
||||
private void recCheckInWork(KeyTree node) {
|
||||
if (node == null) return;
|
||||
|
||||
if (node.inWork()) {
|
||||
long inWorkSpan = System.currentTimeMillis() - node.inWorkSince();
|
||||
if (inWorkSpan >= maxWorkSpan) {
|
||||
if (node.isLeaf()) {
|
||||
StringBuilder key = new StringBuilder();
|
||||
key.append(node.getValue() ? "1": "0");
|
||||
KeyTree parent = node.getParent();
|
||||
while (parent != null) {
|
||||
key.insert(0, parent.getValue() ? "1": "0");
|
||||
parent = parent.getParent();
|
||||
}
|
||||
log.debug("{}: Retracting Work for key {}, inWorkSince: {}", System.currentTimeMillis(), key, node.inWorkSince());
|
||||
}
|
||||
node.clearInWork();
|
||||
}
|
||||
}
|
||||
|
||||
recCheckInWork(node.getLeft());
|
||||
recCheckInWork(node.getRight());
|
||||
}
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
package org.btcollider.cnc.keysrv;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.btcollider.cnc.CnC;
|
||||
import org.btcollider.cnc.dto.KeyRange;
|
||||
|
@ -9,30 +15,36 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
public class KeyServer {
|
||||
final Logger log = LoggerFactory.getLogger(KeyServer.class);
|
||||
|
||||
|
||||
private static KeyServer instance;
|
||||
|
||||
private KeyTree root;
|
||||
private int index;
|
||||
private int depth;
|
||||
|
||||
protected KeyServer(int index, int depth) {
|
||||
protected KeyServer(int index, int depth, long maxWorkSpan) {
|
||||
assert Math.pow(2, CnC.MAX_BITS) <= Long.MAX_VALUE;
|
||||
assert index < CnC.MAX_BITS && depth <= CnC.MAX_BITS;
|
||||
|
||||
this.depth = depth;
|
||||
this.index = index;
|
||||
this.root = generateKeyTree(depth);
|
||||
|
||||
log.info("Starting KeyTree concierge");
|
||||
ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor();
|
||||
es.scheduleWithFixedDelay(new KTConcierge(root, maxWorkSpan), 0, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void init(int index, int depth) {
|
||||
instance = new KeyServer(index, depth);
|
||||
public static void init(int index, int depth, long maxWorkSpan) {
|
||||
if (instance == null)
|
||||
instance = new KeyServer(index, depth, maxWorkSpan);
|
||||
else
|
||||
throw new IllegalAccessError("KeyServer already initialized");
|
||||
}
|
||||
|
||||
public static KeyServer getInstance() {
|
||||
if (instance == null)
|
||||
throw new IllegalAccessError("KeyServer is not initialized");
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -62,11 +74,49 @@ public class KeyServer {
|
|||
}
|
||||
|
||||
public void setSearched(KeyRange keyRange) {
|
||||
markSearched(keyRange.getStart());
|
||||
markSearched(BitSet.valueOf(new long[] { keyRange.getStart() }));
|
||||
}
|
||||
|
||||
public void setInWork(KeyRange keyRange) {
|
||||
markInWork(keyRange.getStart());
|
||||
markInWork(BitSet.valueOf(new long[] { keyRange.getStart() }));
|
||||
}
|
||||
|
||||
public String printDot() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("strict graph { \n");
|
||||
|
||||
Queue<KeyTree> frontier = new LinkedList<>();
|
||||
frontier.add(root);
|
||||
|
||||
while (!frontier.isEmpty()) {
|
||||
KeyTree node = frontier.poll();
|
||||
buffer.append(dotNodeName(node) + ";\n");
|
||||
|
||||
if (node.getParent() != null) {
|
||||
buffer.append(dotNodeName(node.getParent()) + " -- " + dotNodeName(node) + ";\n");
|
||||
}
|
||||
|
||||
if (node.getLeft() != null)
|
||||
frontier.add(node.getLeft());
|
||||
if (node.getRight() != null)
|
||||
frontier.add(node.getRight());
|
||||
}
|
||||
|
||||
buffer.append("\n}");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private String dotNodeName(KeyTree node) {
|
||||
String nodeName = "\"" + node.hashCode() + "_" + node.getValue();
|
||||
|
||||
if (node.isSearched())
|
||||
nodeName += "_s";
|
||||
if (node.inWork())
|
||||
nodeName += "_w";
|
||||
|
||||
nodeName += "\"";
|
||||
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
private void markInWork(BitSet key) {
|
||||
|
@ -103,10 +153,10 @@ public class KeyServer {
|
|||
return;
|
||||
}
|
||||
|
||||
// Mark parent searched too, check next level
|
||||
// Mark parent inWork too, check next level
|
||||
parent.setInWork(Long.max(parent.getLeft().inWorkSince(), parent.getRight().inWorkSince()));
|
||||
|
||||
recMarkSearchedParents(parent);
|
||||
recMarkInWorkParents(parent);
|
||||
}
|
||||
|
||||
private void markSearched(BitSet key) {
|
||||
|
@ -181,18 +231,40 @@ public class KeyServer {
|
|||
KeyTree right = kt.getRight();
|
||||
|
||||
// Finish criteria
|
||||
if (left.isLeaf() && !left.isSearched()) {
|
||||
keyStart.set(curIndex - 1, left.getValue());
|
||||
return;
|
||||
} else if (right.isLeaf() && !right.isSearched()) {
|
||||
keyStart.set(curIndex - 1, right.getValue());
|
||||
if (left.isLeaf() || right.isLeaf()) {
|
||||
assert left.isLeaf() && right.isLeaf(); // fully balanced binary tree by construction
|
||||
|
||||
// No choice finish
|
||||
if (!right.isFree())
|
||||
keyStart.set(curIndex - 1, left.getValue());
|
||||
else if (!left.isFree())
|
||||
keyStart.set(curIndex - 1, right.getValue());
|
||||
else if (left.isLeaf() && right.isLeaf()) { // Randomized finish
|
||||
Random r = new Random();
|
||||
boolean leftFirst = r.nextBoolean();
|
||||
if(leftFirst) keyStart.set(curIndex - 1, left.getValue());
|
||||
else keyStart.set(curIndex - 1, right.getValue());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Recursive depth-first traversal
|
||||
if (!left.isSearched())
|
||||
recCollectKey(left, keyStart, curIndex - 1);
|
||||
else
|
||||
// No choice depth-first traversal
|
||||
if (!left.isFree())
|
||||
recCollectKey(right, keyStart, curIndex - 1);
|
||||
else if (!right.isFree())
|
||||
recCollectKey(left, keyStart, curIndex - 1);
|
||||
else { // Randomized depth-first traversal
|
||||
Random r = new Random();
|
||||
boolean leftFirst = r.nextBoolean();
|
||||
if (leftFirst)
|
||||
recCollectKey(left, keyStart, curIndex - 1);
|
||||
else
|
||||
recCollectKey(right, keyStart, curIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected KeyTree getKeyTree() {
|
||||
return this.root;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ public class KeyTree {
|
|||
this.searched = false;
|
||||
this.inWorkSince = -1;
|
||||
}
|
||||
|
||||
public boolean isFree() {
|
||||
return !isSearched() && !inWork();
|
||||
}
|
||||
|
||||
public boolean isLeaf() {
|
||||
return left == null && right == null;
|
||||
|
@ -34,13 +38,21 @@ public class KeyTree {
|
|||
return left;
|
||||
}
|
||||
|
||||
public boolean hasLeft() {
|
||||
return left != null;
|
||||
}
|
||||
|
||||
public void setLeft(KeyTree left) {
|
||||
this.left = left;
|
||||
}
|
||||
|
||||
|
||||
public KeyTree getRight() {
|
||||
return right;
|
||||
}
|
||||
|
||||
public boolean hasRight() {
|
||||
return right != null;
|
||||
}
|
||||
|
||||
public void setRight(KeyTree right) {
|
||||
this.right = right;
|
||||
|
@ -55,12 +67,16 @@ public class KeyTree {
|
|||
}
|
||||
|
||||
public void setInWork(long since) {
|
||||
this.inWorkSince = System.currentTimeMillis();
|
||||
this.inWorkSince = since;
|
||||
}
|
||||
|
||||
public void setInWork() {
|
||||
this.inWorkSince = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void clearInWork() {
|
||||
this.inWorkSince = -1;
|
||||
}
|
||||
|
||||
public boolean inWork() {
|
||||
return inWorkSince != -1;
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package org.btcollider.cnc.keysrv;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class KeyServerTest {
|
||||
|
||||
@Test
|
||||
void testInitEmptyTree() {
|
||||
assertThrows(AssertionError.class, () -> KeyServer.init(0, 0, 0));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue