mirror of
https://github.com/GOSTSec/ccminer
synced 2025-01-09 14:28:15 +00:00
242aa4144b
rpc2: handle properly secondary nonce(s) + api ping time fix be sure to fully recompile, structures are changed
1315 lines
33 KiB
C++
1315 lines
33 KiB
C++
/**
|
|
* XMR RPC 2.0 Specific Stuff
|
|
* tpruvot@github - October 2016
|
|
*/
|
|
|
|
#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
|
|
|
|
#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);
|
|
}
|