2015-01-09 22:36:42 +01:00
|
|
|
// Based on <https://github.com/grant-olson/bitmessage-powfaster>
|
|
|
|
// fastcpu implementation.
|
|
|
|
// TODO(Kagami): Port it to WIN32 (see bitmessage-powfaster for an
|
|
|
|
// example).
|
|
|
|
|
2015-01-09 23:36:28 +01:00
|
|
|
#define __STDC_LIMIT_MACROS
|
2015-01-09 22:36:42 +01:00
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
2015-01-10 17:29:33 +01:00
|
|
|
#include <pthread.h>
|
2015-01-09 22:36:42 +01:00
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <openssl/sha.h>
|
2015-01-11 03:59:32 +01:00
|
|
|
#include "./pow.h"
|
2015-01-09 22:36:42 +01:00
|
|
|
|
2015-01-11 03:59:32 +01:00
|
|
|
enum PowResult {
|
|
|
|
RESULT_OK = 0,
|
|
|
|
RESULT_OVERFLOW = -1,
|
|
|
|
RESULT_ERROR = -2,
|
|
|
|
RESULT_BAD_INPUT = -3,
|
|
|
|
RESULT_NOT_READY = -4
|
|
|
|
};
|
2015-01-10 17:29:33 +01:00
|
|
|
|
|
|
|
// Global arguments.
|
|
|
|
size_t g_pool_size;
|
|
|
|
uint64_t g_target;
|
|
|
|
uint8_t* g_initial_hash;
|
|
|
|
uint64_t g_max_nonce;
|
|
|
|
|
|
|
|
// Shared variables for threads.
|
|
|
|
pthread_mutex_t g_mutex;
|
2015-01-11 03:59:32 +01:00
|
|
|
PowResult g_result = RESULT_NOT_READY;
|
2015-01-10 17:29:33 +01:00
|
|
|
uint64_t g_nonce;
|
|
|
|
|
2015-01-10 23:33:30 +01:00
|
|
|
inline uint64_t ntohll(uint64_t x) {
|
|
|
|
return (
|
|
|
|
((uint64_t)(ntohl( (unsigned int)((x << 32) >> 32) )) << 32) |
|
|
|
|
ntohl( ((unsigned int)(x >> 32)) )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set POW computation result in a thread-safe way.
|
2015-01-11 03:59:32 +01:00
|
|
|
void set_result(PowResult res, uint64_t nonce) {
|
2015-01-10 23:33:30 +01:00
|
|
|
pthread_mutex_lock(&g_mutex);
|
|
|
|
if (g_result == RESULT_NOT_READY) {
|
|
|
|
g_result = res;
|
|
|
|
g_nonce = nonce;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&g_mutex);
|
|
|
|
}
|
|
|
|
|
2015-01-10 17:29:33 +01:00
|
|
|
void* pow_thread(void* num) {
|
|
|
|
uint64_t i = *((size_t *)num);
|
2015-01-09 22:36:42 +01:00
|
|
|
uint8_t message[HASH_SIZE+sizeof(uint64_t)];
|
|
|
|
uint8_t digest[HASH_SIZE];
|
|
|
|
uint64_t* be_nonce;
|
|
|
|
uint64_t* be_trial;
|
|
|
|
SHA512_CTX sha;
|
|
|
|
|
2015-01-10 17:29:33 +01:00
|
|
|
memcpy(message+sizeof(uint64_t), g_initial_hash, HASH_SIZE);
|
2015-01-09 22:36:42 +01:00
|
|
|
be_nonce = (uint64_t *)message;
|
|
|
|
be_trial = (uint64_t *)digest;
|
2015-01-09 23:09:01 +01:00
|
|
|
|
2015-01-10 17:29:33 +01:00
|
|
|
while (g_result == RESULT_NOT_READY) {
|
2015-01-09 22:36:42 +01:00
|
|
|
// This is very unlikely to be ever happen but it's better to be
|
|
|
|
// sure anyway.
|
2015-01-10 17:29:33 +01:00
|
|
|
if (i > g_max_nonce) {
|
2015-01-10 23:33:30 +01:00
|
|
|
set_result(RESULT_OVERFLOW, 0);
|
2015-01-10 17:29:33 +01:00
|
|
|
return NULL;
|
2015-01-09 22:36:42 +01:00
|
|
|
}
|
2015-01-10 23:33:30 +01:00
|
|
|
*be_nonce = ntohll(i);
|
2015-01-09 22:36:42 +01:00
|
|
|
SHA512_Init(&sha);
|
|
|
|
SHA512_Update(&sha, message, HASH_SIZE+sizeof(uint64_t));
|
|
|
|
SHA512_Final(digest, &sha);
|
|
|
|
SHA512_Init(&sha);
|
|
|
|
SHA512_Update(&sha, digest, HASH_SIZE);
|
|
|
|
SHA512_Final(digest, &sha);
|
2015-01-10 23:33:30 +01:00
|
|
|
if (ntohll(*be_trial) <= g_target) {
|
|
|
|
set_result(RESULT_OK, i);
|
2015-01-10 17:29:33 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
i += g_pool_size;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pow(size_t pool_size,
|
|
|
|
uint64_t target,
|
|
|
|
const uint8_t* initial_hash,
|
|
|
|
uint64_t max_nonce,
|
|
|
|
uint64_t* nonce) {
|
2015-01-11 03:59:32 +01:00
|
|
|
if (pool_size < 1 || pool_size > MAX_POOL_SIZE) {
|
|
|
|
return RESULT_BAD_INPUT;
|
|
|
|
}
|
2015-01-10 17:29:33 +01:00
|
|
|
g_pool_size = pool_size;
|
|
|
|
g_target = target;
|
|
|
|
g_initial_hash = (uint8_t *)initial_hash;
|
|
|
|
g_max_nonce = max_nonce ? max_nonce : UINT64_MAX;
|
|
|
|
|
|
|
|
pthread_mutex_init(&g_mutex, NULL);
|
|
|
|
pthread_t threads[pool_size];
|
|
|
|
size_t args[pool_size];
|
|
|
|
size_t i;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
for (i = 0; i < pool_size; i++) {
|
|
|
|
args[i] = i;
|
|
|
|
error = pthread_create(&threads[i], NULL, pow_thread, &args[i]);
|
|
|
|
if (error) {
|
2015-01-10 23:33:30 +01:00
|
|
|
set_result(RESULT_ERROR, 0);
|
2015-01-09 22:36:42 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-01-09 23:09:01 +01:00
|
|
|
|
2015-01-10 17:29:33 +01:00
|
|
|
// Wait for only spawned threads.
|
|
|
|
while (i--) {
|
|
|
|
pthread_join(threads[i], NULL);
|
|
|
|
}
|
|
|
|
|
2015-01-11 03:59:32 +01:00
|
|
|
if (g_result == RESULT_OK) {
|
|
|
|
*nonce = g_nonce;
|
2015-01-10 17:29:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_destroy(&g_mutex);
|
2015-01-11 03:59:32 +01:00
|
|
|
return g_result;
|
2015-01-09 22:36:42 +01:00
|
|
|
}
|