You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1321 lines
34 KiB
1321 lines
34 KiB
/** |
|
* XMR RPC 2.0 Stratum and BBR Scratchpad |
|
* tpruvot@github - October 2016 - Under GPLv3 Licence |
|
*/ |
|
|
|
#include <errno.h> |
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <sys/stat.h> // mkdir |
|
|
|
#include <miner.h> |
|
|
|
#ifdef _MSC_VER |
|
#include "mman.h" // mmap |
|
#include <direct.h> // _mkdir |
|
#define chdir(x) _chdir(x) |
|
#define mkdir(x) _mkdir(x) |
|
#define getcwd(d,sz) _getcwd(d,sz) |
|
#define unlink(x) _unlink(x) |
|
#define PATH_MAX MAX_PATH |
|
#else |
|
#include <sys/mman.h> // mmap |
|
#endif |
|
|
|
#if defined(__APPLE__) && !defined(MAP_HUGETLB) |
|
#define MAP_ANONYMOUS MAP_ANON |
|
#define MAP_HUGETLB 0 |
|
#define MAP_POPULATE 0 |
|
#define MADV_HUGEPAGE 0 |
|
#endif |
|
|
|
#ifndef PRIu64 |
|
#define PRIu64 "I64u" |
|
#endif |
|
|
|
#include <algos.h> |
|
#include "xmr-rpc.h" |
|
#include "wildkeccak.h" |
|
|
|
double target_to_diff_rpc2(uint32_t* target) |
|
{ |
|
// unlike other algos, xmr diff is very low |
|
if (opt_algo == ALGO_CRYPTONIGHT && target[7]) { |
|
// simplified to get 1.0 for 1000 |
|
return (double) (UINT32_MAX / target[7]) / 1000; |
|
} |
|
else if (opt_algo == ALGO_CRYPTOLIGHT && target[7]) { |
|
return (double) (UINT32_MAX / target[7]) / 1000; |
|
} |
|
else if (opt_algo == ALGO_WILDKECCAK) { |
|
return target_to_diff(target) * 1000; |
|
} |
|
return target_to_diff(target); // util.cpp |
|
} |
|
|
|
extern struct stratum_ctx stratum; |
|
|
|
bool jobj_binary(const json_t *obj, const char *key, void *buf, size_t buflen); |
|
|
|
pthread_mutex_t rpc2_job_lock; |
|
pthread_mutex_t rpc2_work_lock; |
|
pthread_mutex_t rpc2_login_lock; |
|
//pthread_mutex_t rpc2_getscratchpad_lock; |
|
|
|
char* opt_scratchpad_url = NULL; |
|
uint64_t* pscratchpad_buff = NULL; |
|
|
|
// hide addendums flood on start |
|
static bool opt_quiet_start = true; |
|
|
|
static const char * pscratchpad_local_cache = NULL; |
|
static const char cachedir_suffix[] = "boolberry"; /* scratchpad cache saved as ~/.cache/boolberry/scratchpad.bin */ |
|
static char scratchpad_file[PATH_MAX]; |
|
static time_t prev_save = 0; |
|
static struct scratchpad_hi current_scratchpad_hi; |
|
static struct addendums_array_entry add_arr[WILD_KECCAK_ADDENDUMS_ARRAY_SIZE]; |
|
|
|
static char *rpc2_job_id = NULL; |
|
static char *rpc2_blob = NULL; |
|
static uint32_t rpc2_target = 0; |
|
static size_t rpc2_bloblen = 0; |
|
static struct work rpc2_work; |
|
|
|
static char rpc2_id[64] = { 0 }; |
|
static uint64_t last_found_nonce = 0; |
|
|
|
static const char* get_json_string_param(const json_t *val, const char* param_name) |
|
{ |
|
json_t *tmp; |
|
tmp = json_object_get(val, param_name); |
|
if(!tmp) { |
|
return NULL; |
|
} |
|
return json_string_value(tmp); |
|
} |
|
|
|
static size_t hex2bin_len(unsigned char *p, const char *hexstr, size_t len) |
|
{ |
|
char hex_byte[3]; |
|
char *ep; |
|
size_t count = 0; |
|
|
|
hex_byte[2] = '\0'; |
|
|
|
while (*hexstr && len) { |
|
if (!hexstr[1]) { |
|
applog(LOG_ERR, "hex2bin str truncated"); |
|
return 0; |
|
} |
|
hex_byte[0] = hexstr[0]; |
|
hex_byte[1] = hexstr[1]; |
|
*p = (unsigned char) strtol(hex_byte, &ep, 16); |
|
if (*ep) { |
|
applog(LOG_ERR, "hex2bin failed on '%s'", hex_byte); |
|
return 0; |
|
} |
|
count++; |
|
p++; |
|
hexstr += 2; |
|
len--; |
|
} |
|
|
|
return (/*len == 0 &&*/ *hexstr == 0) ? count : 0; |
|
} |
|
|
|
static bool parse_height_info(const json_t *hi_section, struct scratchpad_hi* phi) |
|
{ |
|
unsigned char prevhash[32] = { 0 }; |
|
const char* block_id; |
|
uint64_t hi_h; |
|
size_t len; |
|
|
|
if(!phi || !hi_section) { |
|
applog(LOG_ERR, "parse_height_info: wrong params"); |
|
return false; |
|
} |
|
|
|
json_t *height = json_object_get(hi_section, "height"); |
|
if(!height) { |
|
applog(LOG_ERR, "JSON inval hi, no height param"); |
|
goto err_out; |
|
} |
|
|
|
if(!json_is_integer(height)) { |
|
applog(LOG_ERR, "JSON inval hi: height is not integer "); |
|
goto err_out; |
|
} |
|
|
|
hi_h = (uint64_t)json_integer_value(height); |
|
if(!hi_h) { |
|
applog(LOG_ERR, "JSON inval hi: height is 0"); |
|
goto err_out; |
|
} |
|
|
|
block_id = get_json_string_param(hi_section, "block_id"); |
|
if(!block_id) { |
|
applog(LOG_ERR, "JSON inval hi: block_id not found "); |
|
goto err_out; |
|
} |
|
|
|
len = hex2bin_len(prevhash, block_id, 32); |
|
if(len != 32) { |
|
applog(LOG_ERR, "JSON inval hi: block_id wrong len %d", len); |
|
goto err_out; |
|
} |
|
|
|
phi->height = hi_h; |
|
memcpy(phi->prevhash, prevhash, 32); |
|
|
|
return true; |
|
err_out: |
|
return false; |
|
} |
|
|
|
static void reset_scratchpad(void) |
|
{ |
|
current_scratchpad_hi.height = 0; |
|
scratchpad_size = 0; |
|
//unlink(scratchpad_file); |
|
} |
|
|
|
static bool patch_scratchpad_with_addendum(uint64_t global_add_startpoint, uint64_t* padd_buff, size_t count/*uint64 units*/) |
|
{ |
|
for(size_t i = 0; i < count; i += 4) { |
|
uint64_t global_offset = (padd_buff[i]%(global_add_startpoint/4))*4; |
|
for(size_t j = 0; j != 4; j++) |
|
pscratchpad_buff[global_offset + j] ^= padd_buff[i + j]; |
|
} |
|
return true; |
|
} |
|
|
|
static bool apply_addendum(uint64_t* padd_buff, size_t count/*uint64 units*/) |
|
{ |
|
if(WILD_KECCAK_SCRATCHPAD_BUFFSIZE <= (scratchpad_size + count)*8 ) { |
|
applog(LOG_ERR, "!!!!!!! WILD_KECCAK_SCRATCHPAD_BUFFSIZE overflowed !!!!!!!! please increase this constant! "); |
|
return false; |
|
} |
|
|
|
if(!patch_scratchpad_with_addendum(scratchpad_size, padd_buff, count)) { |
|
applog(LOG_ERR, "patch_scratchpad_with_addendum is broken, resetting scratchpad"); |
|
reset_scratchpad(); |
|
return false; |
|
} |
|
for(int k = 0; k != count; k++) |
|
pscratchpad_buff[scratchpad_size+k] = padd_buff[k]; |
|
|
|
scratchpad_size += count; |
|
|
|
return true; |
|
} |
|
|
|
static bool pop_addendum(struct addendums_array_entry* entry) |
|
{ |
|
if(!entry) |
|
return false; |
|
|
|
if(!entry->add_size || !entry->prev_hi.height) { |
|
applog(LOG_ERR, "wrong parameters"); |
|
return false; |
|
} |
|
patch_scratchpad_with_addendum(scratchpad_size - entry->add_size, &pscratchpad_buff[scratchpad_size - entry->add_size], (size_t) entry->add_size); |
|
scratchpad_size = scratchpad_size - entry->add_size; |
|
memcpy(¤t_scratchpad_hi, &entry->prev_hi, sizeof(entry->prev_hi)); |
|
|
|
memset(entry, 0, sizeof(struct addendums_array_entry)); |
|
return true; |
|
} |
|
|
|
// playback scratchpad addendums for whole add_arr |
|
static bool revert_scratchpad() |
|
{ |
|
size_t p = 0; |
|
size_t i = 0; |
|
size_t arr_size = ARRAY_SIZE(add_arr); |
|
|
|
for(p=0; p != arr_size; p++) { |
|
i = arr_size-(p+1); |
|
if(!add_arr[i].prev_hi.height) |
|
continue; |
|
pop_addendum(&add_arr[i]); |
|
} |
|
return true; |
|
} |
|
|
|
static bool push_addendum_info(struct scratchpad_hi* pprev_hi, uint64_t size /* uint64 units count*/) |
|
{ |
|
size_t i = 0; |
|
size_t arr_size = ARRAY_SIZE(add_arr); |
|
|
|
// Find last free entry |
|
for(i=0; i != arr_size; i++) { |
|
if(!add_arr[i].prev_hi.height) |
|
break; |
|
} |
|
|
|
if(i >= arr_size) { |
|
// Shift array |
|
memmove(&add_arr[0], &add_arr[1], (arr_size-1)*sizeof(add_arr[0])); |
|
i = arr_size - 1; |
|
} |
|
add_arr[i].prev_hi = *pprev_hi; |
|
add_arr[i].add_size = size; |
|
|
|
return true; |
|
} |
|
|
|
static bool addendum_decode(const json_t *addm) |
|
{ |
|
struct scratchpad_hi hi; |
|
unsigned char prevhash[32]; |
|
uint64_t* padd_buff; |
|
uint64_t old_height; |
|
|
|
json_t* hi_section = json_object_get(addm, "hi"); |
|
if (!hi_section) { |
|
//applog(LOG_ERR, "JSON addms field not found"); |
|
//return false; |
|
return true; |
|
} |
|
|
|
if(!parse_height_info(hi_section, &hi)) { |
|
return false; |
|
} |
|
|
|
const char* prev_id_str = get_json_string_param(addm, "prev_id"); |
|
if(!prev_id_str) { |
|
applog(LOG_ERR, "JSON prev_id is not a string"); |
|
return false; |
|
} |
|
if(!hex2bin(prevhash, prev_id_str, 32)) { |
|
applog(LOG_ERR, "JSON prev_id is not valid hex string"); |
|
return false; |
|
} |
|
|
|
if(current_scratchpad_hi.height != hi.height -1) |
|
{ |
|
if(current_scratchpad_hi.height > hi.height -1) { |
|
//skip low scratchpad |
|
applog(LOG_ERR, "addendum with hi.height=%lld skiped since current_scratchpad_hi.height=%lld", hi.height, current_scratchpad_hi.height); |
|
return true; |
|
} |
|
|
|
//TODO: ADD SPLIT HANDLING HERE |
|
applog(LOG_ERR, "JSON height in addendum-1 (%lld-1) missmatched with current_scratchpad_hi.height(%lld), reverting scratchpad and re-login", |
|
hi.height, current_scratchpad_hi.height); |
|
revert_scratchpad(); |
|
//init re-login |
|
strcpy(rpc2_id, ""); |
|
return false; |
|
} |
|
|
|
if(memcmp(prevhash, current_scratchpad_hi.prevhash, 32)) { |
|
//TODO: ADD SPLIT HANDLING HERE |
|
applog(LOG_ERR, "JSON prev_id in addendum missmatched with current_scratchpad_hi.prevhash"); |
|
return false; |
|
} |
|
|
|
const char* addm_hexstr = get_json_string_param(addm, "addm"); |
|
if(!addm_hexstr) { |
|
applog(LOG_ERR, "JSON prev_id in addendum missmatched with current_scratchpad_hi.prevhash"); |
|
return false; |
|
} |
|
size_t add_len = strlen(addm_hexstr); |
|
if(add_len%64) { |
|
applog(LOG_ERR, "JSON wrong addm hex str len"); |
|
return false; |
|
} |
|
padd_buff = (uint64_t*) calloc(1, add_len/2); |
|
if (!padd_buff) { |
|
applog(LOG_ERR, "out of memory, wanted %zu", add_len/2); |
|
return false; |
|
} |
|
|
|
if(!hex2bin((unsigned char*)padd_buff, addm_hexstr, add_len/2)) { |
|
applog(LOG_ERR, "JSON wrong addm hex str len"); |
|
goto err_out; |
|
} |
|
|
|
if(!apply_addendum(padd_buff, add_len/16)) { |
|
applog(LOG_ERR, "JSON Failed to apply_addendum!"); |
|
goto err_out; |
|
} |
|
free(padd_buff); |
|
|
|
push_addendum_info(¤t_scratchpad_hi, add_len/16); |
|
old_height = current_scratchpad_hi.height; |
|
current_scratchpad_hi = hi; |
|
|
|
if (!opt_quiet && !opt_quiet_start) |
|
applog(LOG_BLUE, "ADDENDUM APPLIED: Block %lld", (long long) current_scratchpad_hi.height); |
|
|
|
return true; |
|
err_out: |
|
free(padd_buff); |
|
return false; |
|
} |
|
|
|
static bool addendums_decode(const json_t *job) |
|
{ |
|
json_t* paddms = json_object_get(job, "addms"); |
|
if (!paddms) { |
|
//applog(LOG_ERR, "JSON addms field not found"); |
|
//return false; |
|
return true; |
|
} |
|
|
|
if(!json_is_array(paddms)) { |
|
applog(LOG_ERR, "JSON addms field is not array"); |
|
return false; |
|
} |
|
|
|
size_t add_sz = json_array_size(paddms); |
|
for (size_t i = 0; i < add_sz; i++) |
|
{ |
|
json_t *addm = json_array_get(paddms, i); |
|
if (!addm) { |
|
applog(LOG_ERR, "Internal error: failed to get addm"); |
|
return false; |
|
} |
|
if(!addendum_decode(addm)) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool rpc2_job_decode(const json_t *job, struct work *work) |
|
{ |
|
json_t *tmp; |
|
size_t blobLen; |
|
const char *job_id; |
|
const char *hexblob; |
|
|
|
tmp = json_object_get(job, "job_id"); |
|
if (!tmp) { |
|
applog(LOG_ERR, "JSON inval job id"); |
|
goto err_out; |
|
} |
|
|
|
if(opt_algo == ALGO_WILDKECCAK && !addendums_decode(job)) { |
|
applog(LOG_ERR, "JSON failed to process addendums"); |
|
goto err_out; |
|
} |
|
// now allow ADDENDUM notices (after the init) |
|
opt_quiet_start = false; |
|
|
|
job_id = json_string_value(tmp); |
|
tmp = json_object_get(job, "blob"); |
|
if (!tmp) { |
|
applog(LOG_ERR, "JSON inval blob"); |
|
goto err_out; |
|
} |
|
hexblob = json_string_value(tmp); |
|
blobLen = strlen(hexblob); |
|
if (blobLen % 2 != 0 || ((blobLen / 2) < 40 && blobLen != 0) || (blobLen / 2) > 128) |
|
{ |
|
applog(LOG_ERR, "JSON invalid blob length"); |
|
goto err_out; |
|
} |
|
|
|
if (blobLen != 0) |
|
{ |
|
pthread_mutex_lock(&rpc2_job_lock); |
|
char *blob = (char*) calloc(1, blobLen / 2); |
|
if (!hex2bin(blob, hexblob, blobLen / 2)) |
|
{ |
|
applog(LOG_ERR, "JSON inval blob"); |
|
pthread_mutex_unlock(&rpc2_job_lock); |
|
goto err_out; |
|
} |
|
if (rpc2_blob) { |
|
free(rpc2_blob); |
|
} |
|
rpc2_bloblen = blobLen / 2; |
|
rpc2_blob = (char*) malloc(rpc2_bloblen); |
|
memcpy(rpc2_blob, blob, blobLen / 2); |
|
|
|
free(blob); |
|
|
|
uint32_t target; |
|
jobj_binary(job, "target", &target, 4); |
|
if(rpc2_target != target) { |
|
double difficulty = (((double) UINT32_MAX) / target); |
|
stratum.job.diff = difficulty; |
|
rpc2_target = target; |
|
} |
|
|
|
if (rpc2_job_id) { |
|
// reset job share counter |
|
if (strcmp(rpc2_job_id, job_id)) stratum.job.shares_count = 0; |
|
free(rpc2_job_id); |
|
} |
|
rpc2_job_id = strdup(job_id); |
|
pthread_mutex_unlock(&rpc2_job_lock); |
|
} |
|
|
|
if(work) |
|
{ |
|
if (!rpc2_blob) { |
|
applog(LOG_ERR, "Requested work before work was received"); |
|
goto err_out; |
|
} |
|
memcpy(work->data, rpc2_blob, rpc2_bloblen); |
|
memset(work->target, 0xff, sizeof(work->target)); |
|
work->target[7] = rpc2_target; |
|
work->targetdiff = target_to_diff_rpc2(work->target); |
|
|
|
snprintf(work->job_id, sizeof(work->job_id), "%s", rpc2_job_id); |
|
} |
|
|
|
if (opt_algo == ALGO_WILDKECCAK) |
|
wildkeccak_scratchpad_need_update(pscratchpad_buff); |
|
return true; |
|
|
|
err_out: |
|
return false; |
|
} |
|
|
|
extern struct work _ALIGN(64) g_work; |
|
extern volatile time_t g_work_time; |
|
extern bool submit_old; |
|
|
|
bool rpc2_stratum_job(struct stratum_ctx *sctx, json_t *id, json_t *params) |
|
{ |
|
bool ret = false; |
|
pthread_mutex_lock(&rpc2_work_lock); |
|
ret = rpc2_job_decode(params, &rpc2_work); |
|
// update miner threads work |
|
ret = ret && rpc2_stratum_gen_work(sctx, &g_work); |
|
restart_threads(); |
|
pthread_mutex_unlock(&rpc2_work_lock); |
|
return ret; |
|
} |
|
|
|
bool rpc2_stratum_gen_work(struct stratum_ctx *sctx, struct work *work) |
|
{ |
|
// pthread_mutex_lock(&rpc2_work_lock); |
|
memcpy(work, &rpc2_work, sizeof(struct work)); |
|
if (stratum_diff != sctx->job.diff) { |
|
char sdiff[32] = { 0 }; |
|
stratum_diff = sctx->job.diff; |
|
if (opt_showdiff && work->targetdiff != stratum_diff) |
|
snprintf(sdiff, 32, " (%g)", work->targetdiff); |
|
if (stratum_diff >= 1e6) |
|
applog(LOG_WARNING, "Stratum difficulty set to %.1f M%s", stratum_diff/1e6, sdiff); |
|
else |
|
applog(LOG_WARNING, "Stratum difficulty set to %.0f%s", stratum_diff, sdiff); |
|
} |
|
if (work->target[7] != rpc2_target) { |
|
work->target[7] = rpc2_target; |
|
work->targetdiff = target_to_diff_rpc2(work->target); |
|
g_work_time = 0; |
|
restart_threads(); |
|
} |
|
// pthread_mutex_unlock(&rpc2_work_lock); |
|
return (work->data[0] != 0); |
|
} |
|
|
|
#define JSON_SUBMIT_BUF_LEN 512 |
|
// called by submit_upstream_work() |
|
bool rpc2_stratum_submit(struct pool_infos *pool, struct work *work) |
|
{ |
|
char _ALIGN(64) s[JSON_SUBMIT_BUF_LEN]; |
|
uint8_t _ALIGN(64) hash[32]; |
|
uint8_t _ALIGN(64) data[88]; |
|
char *noncestr, *hashhex; |
|
int idnonce = work->submit_nonce_id; |
|
|
|
memcpy(&data[0], work->data, 88); |
|
|
|
if (opt_algo == ALGO_WILDKECCAK) { |
|
// 64 bits nonce |
|
memcpy(&data[1], work->nonces, 8); |
|
// pass if the previous hash is not the current previous hash |
|
if(!submit_old && memcmp(&work->data[3], &g_work.data[3], 28)) { |
|
if (opt_debug) applog(LOG_DEBUG, "stale work detected"); |
|
pool->stales_count++; |
|
return false; |
|
} |
|
noncestr = bin2hex((unsigned char*) &data[1], 8); |
|
// "nonce":"5794ec8000000000" => 0x0000000080ec9457 |
|
memcpy(&last_found_nonce, work->nonces, 8); |
|
wildkeccak_hash(hash, data, NULL, 0); |
|
work_set_target_ratio(work, (uint32_t*) hash); |
|
} |
|
|
|
else if (opt_algo == ALGO_CRYPTOLIGHT) { |
|
uint32_t nonce = work->nonces[idnonce]; |
|
noncestr = bin2hex((unsigned char*) &nonce, 4); |
|
last_found_nonce = nonce; |
|
cryptolight_hash(hash, data, 76); |
|
work_set_target_ratio(work, (uint32_t*) hash); |
|
} |
|
|
|
else if (opt_algo == ALGO_CRYPTONIGHT) { |
|
uint32_t nonce = work->nonces[idnonce]; |
|
noncestr = bin2hex((unsigned char*) &nonce, 4); |
|
last_found_nonce = nonce; |
|
cryptonight_hash(hash, data, 76); |
|
work_set_target_ratio(work, (uint32_t*) hash); |
|
} |
|
|
|
if (hash[31] != 0) |
|
return false; // prevent bad hashes |
|
hashhex = bin2hex((unsigned char*)hash, 32); |
|
|
|
snprintf(s, sizeof(s), "{\"method\":\"submit\",\"params\":" |
|
"{\"id\":\"%s\",\"job_id\":\"%s\",\"nonce\":\"%s\",\"result\":\"%s\"}, \"id\":%u}", |
|
rpc2_id, work->job_id, noncestr, hashhex, stratum.job.shares_count + 10); |
|
|
|
free(hashhex); |
|
free(noncestr); |
|
|
|
gettimeofday(&stratum.tv_submit, NULL); |
|
|
|
if(!stratum_send_line(&stratum, s)) { |
|
applog(LOG_ERR, "%s stratum_send_line failed", __func__); |
|
return false; |
|
} |
|
|
|
//stratum.sharediff = target_to_diff_rpc2((uint32_t*)hash); |
|
stratum.sharediff = work->sharediff[idnonce]; |
|
|
|
return true; |
|
} |
|
|
|
bool rpc2_login_decode(const json_t *val) |
|
{ |
|
const char *id; |
|
const char *s; |
|
json_t *res = json_object_get(val, "result"); |
|
if(!res) { |
|
applog(LOG_ERR, "JSON invalid result"); |
|
goto err_out; |
|
} |
|
|
|
json_t *tmp; |
|
tmp = json_object_get(res, "id"); |
|
if(!tmp) { |
|
applog(LOG_ERR, "JSON inval id"); |
|
goto err_out; |
|
} |
|
id = json_string_value(tmp); |
|
if(!id) { |
|
applog(LOG_ERR, "JSON id is not a string"); |
|
goto err_out; |
|
} |
|
|
|
strncpy(rpc2_id, id, sizeof(rpc2_id)-1); |
|
|
|
if(opt_debug) |
|
applog(LOG_DEBUG, "Auth id: %s", id); |
|
|
|
tmp = json_object_get(res, "status"); |
|
if(!tmp) { |
|
applog(LOG_ERR, "JSON inval status"); |
|
goto err_out; |
|
} |
|
s = json_string_value(tmp); |
|
if(!s) { |
|
applog(LOG_ERR, "JSON status is not a string"); |
|
goto err_out; |
|
} |
|
if(strcmp(s, "OK")) { |
|
applog(LOG_ERR, "JSON returned status \"%s\"", s); |
|
goto err_out; |
|
} |
|
|
|
return true; |
|
|
|
err_out: |
|
return false; |
|
} |
|
|
|
bool store_scratchpad_to_file(bool do_fsync) |
|
{ |
|
char file_name_buff[PATH_MAX] = { 0 }; |
|
FILE *fp; |
|
int ret; |
|
|
|
if(opt_algo != ALGO_WILDKECCAK) return true; |
|
if(!scratchpad_size || !pscratchpad_buff) return true; |
|
|
|
snprintf(file_name_buff, sizeof(file_name_buff), "%s.tmp", pscratchpad_local_cache); |
|
unlink(file_name_buff); |
|
fp = fopen(file_name_buff, "wbx"); |
|
if (!fp) { |
|
applog(LOG_ERR, "failed to create file %s: %s", file_name_buff, strerror(errno)); |
|
return false; |
|
} |
|
|
|
struct scratchpad_file_header sf = { 0 }; |
|
memcpy(sf.add_arr, add_arr, sizeof(sf.add_arr)); |
|
sf.current_hi = current_scratchpad_hi; |
|
sf.scratchpad_size = scratchpad_size; |
|
|
|
if ((fwrite(&sf, sizeof(sf), 1, fp) != 1) || |
|
(fwrite(pscratchpad_buff, 8, (size_t) scratchpad_size, fp) != scratchpad_size)) { |
|
applog(LOG_ERR, "failed to write file %s: %s", file_name_buff, strerror(errno)); |
|
fclose(fp); |
|
unlink(file_name_buff); |
|
return false; |
|
} |
|
fflush(fp); |
|
/*if (do_fsync) { |
|
if (fsync(fileno(fp)) == -1) { |
|
applog(LOG_ERR, "failed to fsync file %s: %s", file_name_buff, strerror(errno)); |
|
fclose(fp); |
|
unlink(file_name_buff); |
|
return false; |
|
} |
|
}*/ |
|
if (fclose(fp) == EOF) { |
|
applog(LOG_ERR, "failed to write file %s: %s", file_name_buff, strerror(errno)); |
|
unlink(file_name_buff); |
|
return false; |
|
} |
|
ret = rename(file_name_buff, pscratchpad_local_cache); |
|
if (ret == -1) { |
|
applog(LOG_ERR, "failed to rename %s to %s: %s", |
|
file_name_buff, pscratchpad_local_cache, strerror(errno)); |
|
unlink(file_name_buff); |
|
return false; |
|
} |
|
applog(LOG_DEBUG, "saved scratchpad to %s (%zu+%zu bytes)", pscratchpad_local_cache, |
|
sizeof(struct scratchpad_file_header), (size_t)scratchpad_size * 8); |
|
return true; |
|
} |
|
|
|
/* TODO: repetitive error+log spam handling */ |
|
bool load_scratchpad_from_file(const char *fname) |
|
{ |
|
FILE *fp; |
|
long flen; |
|
|
|
if(opt_algo != ALGO_WILDKECCAK) return true; |
|
|
|
fp = fopen(fname, "rb"); |
|
if (!fp) { |
|
if (errno != ENOENT) { |
|
applog(LOG_ERR, "failed to load %s: %s", fname, strerror(errno)); |
|
} |
|
return false; |
|
} |
|
|
|
struct scratchpad_file_header fh = { 0 }; |
|
if ((fread(&fh, sizeof(fh), 1, fp) != 1)) { |
|
applog(LOG_ERR, "read error from %s: %s", fname, strerror(errno)); |
|
fclose(fp); |
|
return false; |
|
} |
|
|
|
if ((fh.scratchpad_size*8 > (WILD_KECCAK_SCRATCHPAD_BUFFSIZE)) ||(fh.scratchpad_size%4)) { |
|
applog(LOG_ERR, "file %s size invalid (%" PRIu64 "), max=%zu", |
|
fname, fh.scratchpad_size*8, WILD_KECCAK_SCRATCHPAD_BUFFSIZE); |
|
fclose(fp); |
|
return false; |
|
} |
|
|
|
if (fread(pscratchpad_buff, 8, (size_t) fh.scratchpad_size, fp) != fh.scratchpad_size) { |
|
applog(LOG_ERR, "read error from %s: %s", fname, strerror(errno)); |
|
fclose(fp); |
|
return false; |
|
} |
|
|
|
scratchpad_size = fh.scratchpad_size; |
|
current_scratchpad_hi = fh.current_hi; |
|
memcpy(&add_arr[0], &fh.add_arr[0], sizeof(fh.add_arr)); |
|
flen = (long)scratchpad_size*8; |
|
|
|
if (!opt_quiet) { |
|
applog(LOG_INFO, "Scratchpad size %ld kB at block %" PRIu64, flen/1024, current_scratchpad_hi.height); |
|
} |
|
|
|
fclose(fp); |
|
prev_save = time(NULL); |
|
|
|
return true; |
|
} |
|
|
|
bool dump_scratchpad_to_file_debug() |
|
{ |
|
char file_name_buff[1024] = { 0 }; |
|
if(opt_algo != ALGO_WILDKECCAK) return true; |
|
|
|
snprintf(file_name_buff, sizeof(file_name_buff), "scratchpad_%" PRIu64 "_%llx.scr", |
|
current_scratchpad_hi.height, (long long) last_found_nonce); |
|
|
|
/* do not bother rewriting if it exists already */ |
|
|
|
FILE *fp = fopen(file_name_buff, "w"); |
|
if(!fp) { |
|
applog(LOG_WARNING, "failed to open file %s: %s", file_name_buff, strerror(errno)); |
|
return false; |
|
} |
|
if (fwrite(pscratchpad_buff, 8, (size_t) scratchpad_size, fp) != scratchpad_size) { |
|
applog(LOG_ERR, "failed to write file %s: %s", file_name_buff, strerror(errno)); |
|
fclose(fp); |
|
return false; |
|
} |
|
if (fclose(fp) == EOF) { |
|
applog(LOG_ERR, "failed to write file %s: %s", file_name_buff, strerror(errno)); |
|
return false; |
|
} |
|
|
|
fclose(fp); |
|
return true; |
|
} |
|
|
|
static bool try_mkdir_chdir(const char *dirn) |
|
{ |
|
if (chdir(dirn) == -1) { |
|
if (errno == ENOENT) { |
|
#ifdef WIN32 |
|
if (mkdir(dirn) == -1) { |
|
#else |
|
if (mkdir(dirn, 0700) == -1) { |
|
#endif |
|
applog(LOG_ERR, "mkdir failed: %s", strerror(errno)); |
|
return false; |
|
} |
|
if (chdir(dirn) == -1) { |
|
applog(LOG_ERR, "chdir failed: %s", strerror(errno)); |
|
return false; |
|
} |
|
} else { |
|
applog(LOG_ERR, "chdir failed: %s", strerror(errno)); |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
static size_t curl_write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) |
|
{ |
|
size_t written = fwrite(ptr, size, nmemb, stream); |
|
return written; |
|
} |
|
|
|
static bool download_inital_scratchpad(const char* path_to, const char* url) |
|
{ |
|
CURL *curl; |
|
CURLcode res; |
|
char curl_error_buff[CURL_ERROR_SIZE] = { 0 }; |
|
FILE *fp = fopen(path_to,"wb"); |
|
if (!fp) { |
|
applog(LOG_ERR, "Failed to create file %s error %d", path_to, errno); |
|
return false; |
|
} |
|
|
|
applog(LOG_INFO, "Downloading scratchpad...."); |
|
|
|
curl_global_cleanup(); |
|
res = curl_global_init(CURL_GLOBAL_ALL); |
|
if (res != CURLE_OK) { |
|
applog(LOG_WARNING, "curl curl_global_init error: %d", (int) res); |
|
} |
|
|
|
curl = curl_easy_init(); |
|
if (!curl) { |
|
applog(LOG_INFO, "Failed to curl_easy_init."); |
|
fclose(fp); |
|
unlink(path_to); |
|
return false; |
|
} |
|
|
|
if (opt_protocol && opt_debug) { |
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); |
|
} |
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url); |
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); |
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300); |
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); |
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error_buff); |
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data); |
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); |
|
//curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); |
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); |
|
if (opt_cert) { |
|
curl_easy_setopt(curl, CURLOPT_CAINFO, opt_cert); |
|
} else { |
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); |
|
} |
|
|
|
res = curl_easy_perform(curl); |
|
if (res != CURLE_OK) { |
|
if (res == CURLE_OUT_OF_MEMORY) { |
|
applog(LOG_ERR, "Failed to download file, not enough memory!"); |
|
applog(LOG_ERR, "curl error: %s", curl_error_buff); |
|
} else { |
|
applog(LOG_ERR, "Failed to download file, error: %s", curl_error_buff); |
|
} |
|
} else { |
|
applog(LOG_INFO, "Scratchpad downloaded."); |
|
} |
|
/* always cleanup */ |
|
curl_easy_cleanup(curl); |
|
|
|
fflush(fp); |
|
fclose(fp); |
|
|
|
if (res != CURLE_OK) { |
|
unlink(path_to); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
#ifndef WIN32 |
|
|
|
void GetScratchpad() |
|
{ |
|
const char *phome_var_name = "HOME"; |
|
size_t sz = WILD_KECCAK_SCRATCHPAD_BUFFSIZE; |
|
char cachedir[PATH_MAX]; |
|
|
|
if(!getenv(phome_var_name)) { |
|
applog(LOG_ERR, "$%s not set", phome_var_name); |
|
exit(1); |
|
} |
|
else if(!try_mkdir_chdir(getenv(phome_var_name))) { |
|
exit(1); |
|
} |
|
|
|
if(!try_mkdir_chdir(".cache")) exit(1); |
|
|
|
if(!try_mkdir_chdir(cachedir_suffix)) exit(1); |
|
|
|
if(getcwd(cachedir, sizeof(cachedir) - 22) == NULL) { |
|
applog(LOG_ERR, "getcwd failed: %s", strerror(errno)); |
|
exit(1); |
|
} |
|
|
|
snprintf(scratchpad_file, sizeof(scratchpad_file), "%s/scratchpad.bin", cachedir); |
|
pscratchpad_local_cache = scratchpad_file; |
|
|
|
if (!opt_quiet) |
|
applog(LOG_INFO, "Scratchpad file %s", pscratchpad_local_cache); |
|
|
|
pscratchpad_buff = (uint64_t*) mmap(0, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, 0, 0); |
|
if(pscratchpad_buff == MAP_FAILED) |
|
{ |
|
if(opt_debug) applog(LOG_DEBUG, "hugetlb not available"); |
|
pscratchpad_buff = (uint64_t*) malloc(sz); |
|
if(!pscratchpad_buff) { |
|
applog(LOG_ERR, "Scratchpad allocation failed"); |
|
exit(1); |
|
} |
|
} else { |
|
if(opt_debug) applog(LOG_DEBUG, "using hugetlb"); |
|
} |
|
madvise(pscratchpad_buff, sz, MADV_RANDOM | MADV_WILLNEED | MADV_HUGEPAGE); |
|
mlock(pscratchpad_buff, sz); |
|
|
|
if(!load_scratchpad_from_file(pscratchpad_local_cache)) |
|
{ |
|
if(!opt_scratchpad_url) { |
|
applog(LOG_ERR, "Scratchpad URL not set. Please specify correct scratchpad url by -k or --scratchpad option"); |
|
exit(1); |
|
} |
|
if(!download_inital_scratchpad(pscratchpad_local_cache, opt_scratchpad_url)) { |
|
applog(LOG_ERR, "Scratchpad not found and not downloaded. Please specify correct scratchpad url by -k or --scratchpad option"); |
|
exit(1); |
|
} |
|
if(!load_scratchpad_from_file(pscratchpad_local_cache)) { |
|
applog(LOG_ERR, "Failed to load scratchpad data after downloading, probably broken scratchpad link, please restart miner with correct inital scratcpad link(-k or --scratchpad )"); |
|
unlink(pscratchpad_local_cache); |
|
exit(1); |
|
} |
|
} |
|
} |
|
|
|
#else /* Windows */ |
|
|
|
void GetScratchpad() |
|
{ |
|
bool scratchpad_need_update = false; |
|
size_t sz = WILD_KECCAK_SCRATCHPAD_BUFFSIZE; |
|
const char* phome_var_name = "LOCALAPPDATA"; |
|
char cachedir[PATH_MAX]; |
|
|
|
if(!getenv(phome_var_name)) { |
|
applog(LOG_ERR, "%s env var is not set", phome_var_name); |
|
exit(1); |
|
} |
|
else if(!try_mkdir_chdir(getenv(phome_var_name))) { |
|
exit(1); |
|
} |
|
|
|
if(!try_mkdir_chdir(".cache")) |
|
exit(1); |
|
|
|
if(!try_mkdir_chdir(cachedir_suffix)) |
|
exit(1); |
|
|
|
if(getcwd(cachedir, sizeof(cachedir) - 22) == NULL) { |
|
applog(LOG_ERR, "getcwd failed: %s", strerror(errno)); |
|
exit(1); |
|
} |
|
|
|
snprintf(scratchpad_file, sizeof(scratchpad_file), "%s\\scratchpad.bin", cachedir); |
|
pscratchpad_local_cache = scratchpad_file; |
|
|
|
if (!opt_quiet) |
|
applog(LOG_INFO, "Scratchpad file %s", pscratchpad_local_cache); |
|
|
|
if (pscratchpad_buff) { |
|
reset_scratchpad(); |
|
wildkeccak_scratchpad_need_update(NULL); |
|
scratchpad_need_update = true; |
|
free(pscratchpad_buff); |
|
pscratchpad_buff = NULL; |
|
} |
|
|
|
pscratchpad_buff = (uint64_t*) malloc(sz); |
|
if(!pscratchpad_buff) { |
|
applog(LOG_ERR, "Scratchpad allocation failed"); |
|
exit(1); |
|
} |
|
|
|
if(!load_scratchpad_from_file(pscratchpad_local_cache)) |
|
{ |
|
if(!opt_scratchpad_url) { |
|
applog(LOG_ERR, "Scratchpad URL not set. Please specify correct scratchpad url by -k or --scratchpad option"); |
|
exit(1); |
|
} |
|
free(pscratchpad_buff); |
|
pscratchpad_buff = NULL; |
|
if(!download_inital_scratchpad(pscratchpad_local_cache, opt_scratchpad_url)) { |
|
applog(LOG_ERR, "Scratchpad not found and not downloaded. Please specify correct scratchpad url by -k or --scratchpad option"); |
|
exit(1); |
|
} |
|
pscratchpad_buff = (uint64_t*) malloc(sz); |
|
if(!pscratchpad_buff) { |
|
applog(LOG_ERR, "Scratchpad allocation failed"); |
|
exit(1); |
|
} |
|
if(!load_scratchpad_from_file(pscratchpad_local_cache)) { |
|
applog(LOG_ERR, "Failed to load scratchpad data after downloading, probably broken scratchpad link, please restart miner with correct inital scratcpad link(-k or --scratchpad )"); |
|
unlink(pscratchpad_local_cache); |
|
exit(1); |
|
} |
|
} |
|
|
|
if (scratchpad_need_update) |
|
wildkeccak_scratchpad_need_update(pscratchpad_buff); |
|
} |
|
|
|
#endif /* GetScratchpad() linux */ |
|
|
|
static bool rpc2_getfullscratchpad_decode(const json_t *val) |
|
{ |
|
const char* status; |
|
const char* scratch_hex; |
|
size_t len; |
|
json_t *hi; |
|
json_t *res = json_object_get(val, "result"); |
|
if(!res) { |
|
applog(LOG_ERR, "JSON invalid result in rpc2_getfullscratchpad_decode"); |
|
goto err_out; |
|
} |
|
|
|
//check status |
|
status = get_json_string_param(res, "status"); |
|
if (!status ) { |
|
applog(LOG_ERR, "JSON status is not a string"); |
|
goto err_out; |
|
} |
|
|
|
if(strcmp(status, "OK")) { |
|
applog(LOG_ERR, "JSON returned status \"%s\"", status); |
|
goto err_out; |
|
} |
|
|
|
//parse scratchpad |
|
scratch_hex = get_json_string_param(res, "scratchpad_hex"); |
|
if (!scratch_hex) { |
|
applog(LOG_ERR, "JSON scratch_hex is not a string"); |
|
goto err_out; |
|
} |
|
|
|
len = hex2bin_len((unsigned char*)pscratchpad_buff, scratch_hex, WILD_KECCAK_SCRATCHPAD_BUFFSIZE); |
|
if (!len) { |
|
applog(LOG_ERR, "JSON scratch_hex is not valid hex"); |
|
goto err_out; |
|
} |
|
|
|
if (len%8 || len%32) { |
|
applog(LOG_ERR, "JSON scratch_hex is not valid size=%d bytes", len); |
|
goto err_out; |
|
} |
|
|
|
//parse hi |
|
hi = json_object_get(res, "hi"); |
|
if(!hi) { |
|
applog(LOG_ERR, "JSON inval hi"); |
|
goto err_out; |
|
} |
|
|
|
if(!parse_height_info(hi, ¤t_scratchpad_hi)) |
|
{ |
|
applog(LOG_ERR, "JSON inval hi, failed to parse"); |
|
goto err_out; |
|
} |
|
|
|
applog(LOG_INFO, "Fetched scratchpad size %d bytes", len); |
|
scratchpad_size = len/8; |
|
|
|
return true; |
|
|
|
err_out: return false; |
|
} |
|
|
|
static bool rpc2_stratum_getscratchpad(struct stratum_ctx *sctx) |
|
{ |
|
bool ret = false; |
|
json_t *val = NULL; |
|
json_error_t err; |
|
char *s, *sret; |
|
if(opt_algo != ALGO_WILDKECCAK) return true; |
|
|
|
s = (char*) calloc(1, 1024); |
|
if (!s) |
|
goto out; |
|
sprintf(s, "{\"method\": \"getfullscratchpad\", \"params\": {\"id\": \"%s\", \"agent\": \"" USER_AGENT "\"}, \"id\": 1}", rpc2_id); |
|
|
|
applog(LOG_INFO, "Getting full scratchpad...."); |
|
if (!stratum_send_line(sctx, s)) |
|
goto out; |
|
|
|
//sret = stratum_recv_line_timeout(sctx, 920); |
|
sret = stratum_recv_line(sctx); |
|
if (!sret) |
|
goto out; |
|
applog(LOG_DEBUG, "Getting full scratchpad received line"); |
|
|
|
val = JSON_LOADS(sret, &err); |
|
free(sret); |
|
if (!val) { |
|
applog(LOG_ERR, "JSON decode rpc2_getscratchpad response failed(%d): %s", err.line, err.text); |
|
goto out; |
|
} |
|
|
|
applog(LOG_DEBUG, "Getting full scratchpad parsed line"); |
|
|
|
ret = rpc2_getfullscratchpad_decode(val); |
|
|
|
out: |
|
free(s); |
|
if (val) |
|
json_decref(val); |
|
|
|
return ret; |
|
} |
|
|
|
bool rpc2_stratum_authorize(struct stratum_ctx *sctx, const char *user, const char *pass) |
|
{ |
|
bool ret = false; |
|
json_t *val = NULL, *res_val, *err_val, *job_val = NULL; |
|
json_error_t err; |
|
char *sret; |
|
char *s = (char*) calloc(1, 320 + strlen(user) + strlen(pass)); |
|
|
|
if (opt_algo == ALGO_WILDKECCAK) { |
|
char *prevhash = bin2hex((const unsigned char*)current_scratchpad_hi.prevhash, 32); |
|
sprintf(s, "{\"method\":\"login\",\"params\":{\"login\":\"%s\",\"pass\":\"%s\"," |
|
"\"hi\":{\"height\":%" PRIu64 ",\"block_id\":\"%s\"}," |
|
"\"agent\":\"" USER_AGENT "\"},\"id\":2}", |
|
user, pass, current_scratchpad_hi.height, prevhash); |
|
free(prevhash); |
|
} else { |
|
sprintf(s, "{\"method\":\"login\",\"params\":{\"login\":\"%s\",\"pass\":\"%s\"," |
|
"\"agent\":\"" USER_AGENT "\"},\"id\":2}", |
|
user, pass); |
|
} |
|
|
|
if (!stratum_send_line(sctx, s)) |
|
goto out; |
|
|
|
while (1) { |
|
sret = stratum_recv_line(sctx); |
|
if (!sret) |
|
goto out; |
|
if (!stratum_handle_method(sctx, sret)) |
|
break; |
|
free(sret); |
|
} |
|
|
|
val = JSON_LOADS(sret, &err); |
|
free(sret); |
|
if (!val) { |
|
applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text); |
|
goto out; |
|
} |
|
|
|
res_val = json_object_get(val, "result"); |
|
err_val = json_object_get(val, "error"); |
|
|
|
if (!res_val || json_is_false(res_val) || |
|
(err_val && !json_is_null(err_val))) { |
|
applog(LOG_ERR, "Stratum authentication failed"); |
|
if (err_val) { |
|
const char *msg = json_string_value(json_object_get(err_val,"message")); |
|
if (msg && strlen(msg)) { |
|
if (strstr(msg, "scratchpad too old") && pscratchpad_local_cache) { |
|
if (unlink(pscratchpad_local_cache) == 0) { |
|
applog(LOG_INFO, "Outdated scratchpad, deleted...", pscratchpad_local_cache); |
|
GetScratchpad(); |
|
goto out; |
|
} |
|
} |
|
applog(LOG_NOTICE, "%s", msg); |
|
} |
|
} |
|
goto out; |
|
} |
|
|
|
rpc2_login_decode(val); |
|
job_val = json_object_get(res_val, "job"); |
|
|
|
pthread_mutex_lock(&rpc2_work_lock); |
|
if(job_val) rpc2_job_decode(job_val, &rpc2_work); |
|
pthread_mutex_unlock(&rpc2_work_lock); |
|
|
|
ret = true; |
|
|
|
out: |
|
free(s); |
|
if (val) |
|
json_decref(val); |
|
|
|
return ret; |
|
} |
|
|
|
bool rpc2_stratum_request_job(struct stratum_ctx *sctx) |
|
{ |
|
json_t *val = NULL, *res_val, *err_val; |
|
json_error_t err; |
|
bool ret = false; |
|
char *sret; |
|
char *s = (char*) calloc(1, 10*2048); |
|
if (!s) { |
|
applog(LOG_ERR, "Stratum job OOM!"); |
|
return ret; |
|
} |
|
|
|
if (opt_algo == ALGO_WILDKECCAK) { |
|
char* prevhash = bin2hex((const unsigned char*)current_scratchpad_hi.prevhash, 32); |
|
sprintf(s, "{\"method\":\"getjob\",\"params\": {" |
|
"\"id\":\"%s\", \"hi\": {\"height\": %" PRIu64 ",\"block_id\":\"%s\" }, \"agent\": \"" USER_AGENT "\"}," |
|
"\"id\":1}", |
|
rpc2_id, current_scratchpad_hi.height, prevhash); |
|
free(prevhash); |
|
} else { |
|
sprintf(s, "{\"method\":\"getjob\",\"params\":{\"id\":\"%s\"},\"id\":1}", rpc2_id); |
|
} |
|
|
|
if(!stratum_send_line(sctx, s)) { |
|
applog(LOG_ERR, "Stratum failed to send getjob line"); |
|
goto out; |
|
} |
|
|
|
sret = stratum_recv_line(sctx); |
|
if (!sret) { |
|
applog(LOG_ERR, "Stratum failed to recv getjob line"); |
|
goto out; |
|
} |
|
|
|
val = JSON_LOADS(sret, &err); |
|
free(sret); |
|
if (!val) { |
|
applog(LOG_ERR, "JSON getwork decode failed(%d): %s", err.line, err.text); |
|
goto out; |
|
} |
|
|
|
res_val = json_object_get(val, "result"); |
|
err_val = json_object_get(val, "error"); |
|
|
|
if (!res_val || json_is_false(res_val) || |
|
(err_val && !json_is_null(err_val))) { |
|
applog(LOG_ERR, "Stratum getjob failed"); |
|
goto out; |
|
} |
|
|
|
pthread_mutex_lock(&rpc2_work_lock); |
|
rpc2_job_decode(res_val, &rpc2_work); |
|
pthread_mutex_unlock(&rpc2_work_lock); |
|
|
|
ret = true; |
|
out: |
|
if (val) |
|
json_decref(val); |
|
|
|
return ret; |
|
} |
|
|
|
int rpc2_stratum_thread_stuff(struct pool_infos* pool) |
|
{ |
|
int opt_fail_pause = 10; |
|
|
|
if(!strcmp(rpc2_id, "")) { |
|
if (!opt_quiet) |
|
applog(LOG_DEBUG, "disconnecting..."); |
|
stratum_disconnect(&stratum); |
|
//not logged in, try to relogin |
|
if (!opt_quiet) |
|
applog(LOG_DEBUG, "Re-connect and relogin..."); |
|
if(!stratum_connect(&stratum, stratum.url) || !stratum_authorize(&stratum, pool->user, pool->pass)) { |
|
stratum_disconnect(&stratum); |
|
applog(LOG_ERR, "Failed...retry after %d seconds", opt_fail_pause); |
|
sleep(opt_fail_pause); |
|
} |
|
} |
|
|
|
if(!scratchpad_size && opt_algo == ALGO_WILDKECCAK) { |
|
if(!rpc2_stratum_getscratchpad(&stratum)) { |
|
stratum_disconnect(&stratum); |
|
applog(LOG_ERR, "...retry after %d seconds", opt_fail_pause); |
|
sleep(opt_fail_pause); |
|
} |
|
store_scratchpad_to_file(false); |
|
prev_save = time(NULL); |
|
|
|
if(!rpc2_stratum_request_job(&stratum)) { |
|
stratum_disconnect(&stratum); |
|
applog(LOG_ERR, "...retry after %d seconds", opt_fail_pause); |
|
sleep(opt_fail_pause); |
|
} |
|
} |
|
|
|
/* save every 12 hours */ |
|
if ((time(NULL) - prev_save) > 12*3600) { |
|
store_scratchpad_to_file(false); |
|
prev_save = time(NULL); |
|
} |
|
|
|
if (rpc2_work.job_id && (!g_work_time || strcmp(rpc2_work.job_id, g_work.job_id))) { |
|
pthread_mutex_lock(&rpc2_work_lock); |
|
rpc2_stratum_gen_work(&stratum, &g_work); |
|
g_work_time = time(NULL); |
|
pthread_mutex_unlock(&rpc2_work_lock); |
|
|
|
if (opt_debug) applog(LOG_DEBUG, "Stratum detected new block"); |
|
restart_threads(); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void rpc2_init() |
|
{ |
|
memset(¤t_scratchpad_hi, 0, sizeof(struct scratchpad_hi)); |
|
memset(&rpc2_work, 0, sizeof(struct work)); |
|
|
|
pthread_mutex_init(&rpc2_job_lock, NULL); |
|
pthread_mutex_init(&rpc2_work_lock, NULL); |
|
pthread_mutex_init(&rpc2_login_lock, NULL); |
|
//pthread_mutex_init(&rpc2_getscratchpad_lock, NULL); |
|
}
|
|
|