Add Stratum support
This commit is contained in:
parent
3c4eb509a5
commit
ee7b535ea6
5 changed files with 1088 additions and 112 deletions
|
@ -102,8 +102,8 @@ else
|
||||||
JANSSON_LIBS=-ljansson
|
JANSSON_LIBS=-ljansson
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LIBCURL_CHECK_CONFIG(, 7.10.1, ,
|
LIBCURL_CHECK_CONFIG(, 7.15.2, ,
|
||||||
[AC_MSG_ERROR([Missing required libcurl >= 7.10.1])])
|
[AC_MSG_ERROR([Missing required libcurl >= 7.15.2])])
|
||||||
|
|
||||||
AC_SUBST(JANSSON_LIBS)
|
AC_SUBST(JANSSON_LIBS)
|
||||||
AC_SUBST(PTHREAD_FLAGS)
|
AC_SUBST(PTHREAD_FLAGS)
|
||||||
|
|
412
cpu-miner.c
412
cpu-miner.c
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2010 Jeff Garzik, 2012 pooler
|
* Copyright 2010 Jeff Garzik
|
||||||
|
* Copyright 2012-2013 pooler
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
@ -116,6 +117,8 @@ bool opt_protocol = false;
|
||||||
static bool opt_benchmark = false;
|
static bool opt_benchmark = false;
|
||||||
bool want_longpoll = true;
|
bool want_longpoll = true;
|
||||||
bool have_longpoll = false;
|
bool have_longpoll = false;
|
||||||
|
bool want_stratum = true;
|
||||||
|
bool have_stratum = false;
|
||||||
static bool submit_old = false;
|
static bool submit_old = false;
|
||||||
bool use_syslog = false;
|
bool use_syslog = false;
|
||||||
static bool opt_background = false;
|
static bool opt_background = false;
|
||||||
|
@ -136,8 +139,11 @@ char *opt_proxy;
|
||||||
long opt_proxy_type;
|
long opt_proxy_type;
|
||||||
struct thr_info *thr_info;
|
struct thr_info *thr_info;
|
||||||
static int work_thr_id;
|
static int work_thr_id;
|
||||||
int longpoll_thr_id;
|
int longpoll_thr_id = -1;
|
||||||
|
int stratum_thr_id = -1;
|
||||||
struct work_restart *work_restart = NULL;
|
struct work_restart *work_restart = NULL;
|
||||||
|
static struct stratum_ctx stratum;
|
||||||
|
|
||||||
pthread_mutex_t applog_lock;
|
pthread_mutex_t applog_lock;
|
||||||
pthread_mutex_t stats_lock;
|
pthread_mutex_t stats_lock;
|
||||||
|
|
||||||
|
@ -175,6 +181,7 @@ Options:\n\
|
||||||
-s, --scantime=N upper bound on time spent scanning current work when\n\
|
-s, --scantime=N upper bound on time spent scanning current work when\n\
|
||||||
long polling is unavailable, in seconds (default: 5)\n\
|
long polling is unavailable, in seconds (default: 5)\n\
|
||||||
--no-longpoll disable X-Long-Polling support\n\
|
--no-longpoll disable X-Long-Polling support\n\
|
||||||
|
--no-stratum disable X-Stratum support\n\
|
||||||
-q, --quiet disable per-thread hashmeter output\n\
|
-q, --quiet disable per-thread hashmeter output\n\
|
||||||
-D, --debug enable debug output\n\
|
-D, --debug enable debug output\n\
|
||||||
-P, --protocol-dump verbose dump of protocol-level activities\n"
|
-P, --protocol-dump verbose dump of protocol-level activities\n"
|
||||||
|
@ -212,6 +219,7 @@ static struct option const options[] = {
|
||||||
{ "debug", 0, NULL, 'D' },
|
{ "debug", 0, NULL, 'D' },
|
||||||
{ "help", 0, NULL, 'h' },
|
{ "help", 0, NULL, 'h' },
|
||||||
{ "no-longpoll", 0, NULL, 1003 },
|
{ "no-longpoll", 0, NULL, 1003 },
|
||||||
|
{ "no-stratum", 0, NULL, 1007 },
|
||||||
{ "pass", 1, NULL, 'p' },
|
{ "pass", 1, NULL, 'p' },
|
||||||
{ "protocol-dump", 0, NULL, 'P' },
|
{ "protocol-dump", 0, NULL, 'P' },
|
||||||
{ "proxy", 1, NULL, 'x' },
|
{ "proxy", 1, NULL, 'x' },
|
||||||
|
@ -234,6 +242,10 @@ static struct option const options[] = {
|
||||||
struct work {
|
struct work {
|
||||||
uint32_t data[32];
|
uint32_t data[32];
|
||||||
uint32_t target[8];
|
uint32_t target[8];
|
||||||
|
|
||||||
|
char job_id[128];
|
||||||
|
size_t xnonce2_len;
|
||||||
|
unsigned char xnonce2[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct work g_work;
|
static struct work g_work;
|
||||||
|
@ -286,13 +298,37 @@ err_out:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool submit_upstream_work(CURL *curl, struct work *work)
|
static void share_result(int result, const char *reason)
|
||||||
{
|
{
|
||||||
char *hexstr = NULL;
|
|
||||||
json_t *val, *res;
|
|
||||||
char s[345];
|
char s[345];
|
||||||
double hashrate;
|
double hashrate;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
hashrate = 0.;
|
||||||
|
pthread_mutex_lock(&stats_lock);
|
||||||
|
for (i = 0; i < opt_n_threads; i++)
|
||||||
|
hashrate += thr_hashrates[i];
|
||||||
|
result ? accepted_count++ : rejected_count++;
|
||||||
|
pthread_mutex_unlock(&stats_lock);
|
||||||
|
|
||||||
|
sprintf(s, hashrate >= 1e6 ? "%.0f" : "%.2f", 1e-3 * hashrate);
|
||||||
|
applog(LOG_INFO, "accepted: %lu/%lu (%.2f%%), %s khash/s %s",
|
||||||
|
accepted_count,
|
||||||
|
accepted_count + rejected_count,
|
||||||
|
100. * accepted_count / (accepted_count + rejected_count),
|
||||||
|
s,
|
||||||
|
result ? "(yay!!!)" : "(booooo)");
|
||||||
|
|
||||||
|
if (opt_debug && reason)
|
||||||
|
applog(LOG_DEBUG, "DEBUG: reject reason: %s", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool submit_upstream_work(CURL *curl, struct work *work)
|
||||||
|
{
|
||||||
|
char *str = NULL;
|
||||||
|
json_t *val, *res, *reason;
|
||||||
|
char s[345];
|
||||||
|
int i;
|
||||||
bool rc = false;
|
bool rc = false;
|
||||||
|
|
||||||
/* pass if the previous hash is not the current previous hash */
|
/* pass if the previous hash is not the current previous hash */
|
||||||
|
@ -302,58 +338,61 @@ static bool submit_upstream_work(CURL *curl, struct work *work)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build hex string */
|
if (have_stratum) {
|
||||||
for (i = 0; i < ARRAY_SIZE(work->data); i++)
|
uint32_t ntime, nonce;
|
||||||
le32enc(work->data + i, work->data[i]);
|
char *ntimestr, *noncestr, *xnonce2str;
|
||||||
hexstr = bin2hex((unsigned char *)work->data, sizeof(work->data));
|
|
||||||
if (unlikely(!hexstr)) {
|
if (!work->job_id)
|
||||||
applog(LOG_ERR, "submit_upstream_work OOM");
|
return true;
|
||||||
goto out;
|
le32enc(&ntime, work->data[17]);
|
||||||
|
le32enc(&nonce, work->data[19]);
|
||||||
|
ntimestr = bin2hex((const unsigned char *)(&ntime), 4);
|
||||||
|
noncestr = bin2hex((const unsigned char *)(&nonce), 4);
|
||||||
|
xnonce2str = bin2hex(work->xnonce2, work->xnonce2_len);
|
||||||
|
sprintf(s,
|
||||||
|
"{\"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\":4}",
|
||||||
|
rpc_user, work->job_id, xnonce2str, ntimestr, noncestr);
|
||||||
|
free(ntimestr);
|
||||||
|
free(noncestr);
|
||||||
|
free(xnonce2str);
|
||||||
|
|
||||||
|
if (unlikely(!stratum_send_line(&stratum, s))) {
|
||||||
|
applog(LOG_ERR, "submit_upstream_work stratum_send_line failed");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* build hex string */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(work->data); i++)
|
||||||
|
le32enc(work->data + i, work->data[i]);
|
||||||
|
str = bin2hex((unsigned char *)work->data, sizeof(work->data));
|
||||||
|
if (unlikely(!str)) {
|
||||||
|
applog(LOG_ERR, "submit_upstream_work OOM");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build JSON-RPC request */
|
||||||
|
sprintf(s,
|
||||||
|
"{\"method\": \"getwork\", \"params\": [ \"%s\" ], \"id\":1}\r\n",
|
||||||
|
str);
|
||||||
|
|
||||||
|
/* issue JSON-RPC request */
|
||||||
|
val = json_rpc_call(curl, rpc_url, rpc_userpass, s, false, false, NULL);
|
||||||
|
if (unlikely(!val)) {
|
||||||
|
applog(LOG_ERR, "submit_upstream_work json_rpc_call failed");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = json_object_get(val, "result");
|
||||||
|
reason = json_object_get(val, "reject-reason");
|
||||||
|
share_result(json_is_true(res), reason ? json_string_value(reason) : NULL);
|
||||||
|
|
||||||
|
json_decref(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* build JSON-RPC request */
|
|
||||||
sprintf(s,
|
|
||||||
"{\"method\": \"getwork\", \"params\": [ \"%s\" ], \"id\":1}\r\n",
|
|
||||||
hexstr);
|
|
||||||
|
|
||||||
/* issue JSON-RPC request */
|
|
||||||
val = json_rpc_call(curl, rpc_url, rpc_userpass, s, false, false, NULL);
|
|
||||||
if (unlikely(!val)) {
|
|
||||||
applog(LOG_ERR, "submit_upstream_work json_rpc_call failed");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = json_object_get(val, "result");
|
|
||||||
|
|
||||||
hashrate = 0.;
|
|
||||||
pthread_mutex_lock(&stats_lock);
|
|
||||||
for (i = 0; i < opt_n_threads; i++)
|
|
||||||
hashrate += thr_hashrates[i];
|
|
||||||
json_is_true(res) ? accepted_count++ : rejected_count++;
|
|
||||||
pthread_mutex_unlock(&stats_lock);
|
|
||||||
|
|
||||||
sprintf(s, hashrate >= 1e6 ? "%.0f" : "%.2f", 1e-3 * hashrate);
|
|
||||||
applog(LOG_INFO, "accepted: %lu/%lu (%.2f%%), %s khash/s %s",
|
|
||||||
accepted_count,
|
|
||||||
accepted_count + rejected_count,
|
|
||||||
100. * accepted_count / (accepted_count + rejected_count),
|
|
||||||
s,
|
|
||||||
json_is_true(res) ? "(yay!!!)" : "(booooo)");
|
|
||||||
|
|
||||||
if (opt_debug) {
|
|
||||||
json_t *tmp;
|
|
||||||
const char *reason;
|
|
||||||
tmp = json_object_get(val, "reject-reason");
|
|
||||||
if (tmp && (reason = json_string_value(tmp)))
|
|
||||||
applog(LOG_DEBUG, "DEBUG: reject reason: %s", reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
json_decref(val);
|
|
||||||
|
|
||||||
rc = true;
|
rc = true;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(hexstr);
|
free(str);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,9 +578,6 @@ static bool get_work(struct thr_info *thr, struct work *work)
|
||||||
static bool submit_work(struct thr_info *thr, const struct work *work_in)
|
static bool submit_work(struct thr_info *thr, const struct work *work_in)
|
||||||
{
|
{
|
||||||
struct workio_cmd *wc;
|
struct workio_cmd *wc;
|
||||||
|
|
||||||
if (opt_benchmark)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* fill out work request message */
|
/* fill out work request message */
|
||||||
wc = calloc(1, sizeof(*wc));
|
wc = calloc(1, sizeof(*wc));
|
||||||
|
@ -567,6 +603,54 @@ err_out:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void stratum_gen_work(struct stratum_ctx *sctx, struct work *work)
|
||||||
|
{
|
||||||
|
unsigned char merkle_root[64];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sctx->work_lock);
|
||||||
|
|
||||||
|
strcpy(work->job_id, sctx->job.job_id);
|
||||||
|
work->xnonce2_len = sctx->xnonce2_size;
|
||||||
|
memcpy(work->xnonce2, sctx->job.xnonce2, sctx->xnonce2_size);
|
||||||
|
|
||||||
|
/* Generate merkle root */
|
||||||
|
sha256d(merkle_root, sctx->job.coinbase, sctx->job.coinbase_size);
|
||||||
|
for (i = 0; i < sctx->job.merkle_count; i++) {
|
||||||
|
memcpy(merkle_root + 32, sctx->job.merkle[i], 32);
|
||||||
|
sha256d(merkle_root, merkle_root, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increment extranonce2 */
|
||||||
|
for (i = 0; i < sctx->xnonce2_size && !++sctx->job.xnonce2[i]; i++);
|
||||||
|
|
||||||
|
/* Assemble block header */
|
||||||
|
memset(work->data, 0, 128);
|
||||||
|
work->data[0] = le32dec(sctx->job.version);
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
work->data[1 + i] = le32dec((uint32_t *)sctx->job.prevhash + i);
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
work->data[9 + i] = be32dec((uint32_t *)merkle_root + i);
|
||||||
|
work->data[17] = le32dec(sctx->job.ntime);
|
||||||
|
work->data[18] = le32dec(sctx->job.nbits);
|
||||||
|
work->data[20] = 0x80000000;
|
||||||
|
work->data[31] = 0x00000280;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&sctx->work_lock);
|
||||||
|
|
||||||
|
if (opt_debug) {
|
||||||
|
char *xnonce2str = bin2hex(work->xnonce2, sctx->xnonce2_size);
|
||||||
|
applog(LOG_DEBUG, "DEBUG: job_id='%s' extranonce2=%s ntime=%08x",
|
||||||
|
work->job_id, xnonce2str, swab32(work->data[17]));
|
||||||
|
free(xnonce2str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt_algo == ALGO_SCRYPT)
|
||||||
|
diff_to_target(work->target, sctx->job.diff / 65536.0);
|
||||||
|
else
|
||||||
|
diff_to_target(work->target, sctx->job.diff);
|
||||||
|
}
|
||||||
|
|
||||||
static void *miner_thread(void *userdata)
|
static void *miner_thread(void *userdata)
|
||||||
{
|
{
|
||||||
struct thr_info *mythr = userdata;
|
struct thr_info *mythr = userdata;
|
||||||
|
@ -602,17 +686,30 @@ static void *miner_thread(void *userdata)
|
||||||
int64_t max64;
|
int64_t max64;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* obtain new work from internal workio thread */
|
if (have_stratum) {
|
||||||
pthread_mutex_lock(&g_work_lock);
|
while (!*g_work.job_id || time(NULL) >= g_work_time + 120)
|
||||||
if (!have_longpoll || time(NULL) >= g_work_time + LP_SCANTIME*3/4
|
sleep(1);
|
||||||
|| work.data[19] >= end_nonce) {
|
pthread_mutex_lock(&g_work_lock);
|
||||||
if (unlikely(!get_work(mythr, &g_work))) {
|
if (work.data[19] >= end_nonce)
|
||||||
applog(LOG_ERR, "work retrieval failed, exiting "
|
stratum_gen_work(&stratum, &g_work);
|
||||||
"mining thread %d", mythr->id);
|
} else {
|
||||||
pthread_mutex_unlock(&g_work_lock);
|
/* obtain new work from internal workio thread */
|
||||||
goto out;
|
pthread_mutex_lock(&g_work_lock);
|
||||||
|
if (!(have_longpoll || have_stratum) ||
|
||||||
|
time(NULL) >= g_work_time + LP_SCANTIME*3/4 ||
|
||||||
|
work.data[19] >= end_nonce) {
|
||||||
|
if (unlikely(!get_work(mythr, &g_work))) {
|
||||||
|
applog(LOG_ERR, "work retrieval failed, exiting "
|
||||||
|
"mining thread %d", mythr->id);
|
||||||
|
pthread_mutex_unlock(&g_work_lock);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
time(&g_work_time);
|
||||||
|
}
|
||||||
|
if (have_stratum) {
|
||||||
|
pthread_mutex_unlock(&g_work_lock);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
time(&g_work_time);
|
|
||||||
}
|
}
|
||||||
if (memcmp(work.data, g_work.data, 76)) {
|
if (memcmp(work.data, g_work.data, 76)) {
|
||||||
memcpy(&work, &g_work, sizeof(struct work));
|
memcpy(&work, &g_work, sizeof(struct work));
|
||||||
|
@ -623,8 +720,11 @@ static void *miner_thread(void *userdata)
|
||||||
work_restart[thr_id].restart = 0;
|
work_restart[thr_id].restart = 0;
|
||||||
|
|
||||||
/* adjust max_nonce to meet target scan time */
|
/* adjust max_nonce to meet target scan time */
|
||||||
max64 = g_work_time + (have_longpoll ? LP_SCANTIME : opt_scantime)
|
if (have_stratum)
|
||||||
- time(NULL);
|
max64 = LP_SCANTIME;
|
||||||
|
else
|
||||||
|
max64 = g_work_time + (have_longpoll ? LP_SCANTIME : opt_scantime)
|
||||||
|
- time(NULL);
|
||||||
max64 *= thr_hashrates[thr_id];
|
max64 *= thr_hashrates[thr_id];
|
||||||
if (max64 <= 0)
|
if (max64 <= 0)
|
||||||
max64 = opt_algo == ALGO_SCRYPT ? 0xfffLL : 0x1fffffLL;
|
max64 = opt_algo == ALGO_SCRYPT ? 0xfffLL : 0x1fffffLL;
|
||||||
|
@ -679,7 +779,7 @@ static void *miner_thread(void *userdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if nonce found, submit work */
|
/* if nonce found, submit work */
|
||||||
if (rc && !submit_work(mythr, &work))
|
if (rc && !opt_benchmark && !submit_work(mythr, &work))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,6 +842,11 @@ start:
|
||||||
|
|
||||||
val = json_rpc_call(curl, lp_url, rpc_userpass, rpc_req,
|
val = json_rpc_call(curl, lp_url, rpc_userpass, rpc_req,
|
||||||
false, true, &err);
|
false, true, &err);
|
||||||
|
if (have_stratum) {
|
||||||
|
if (val)
|
||||||
|
json_decref(val);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if (likely(val)) {
|
if (likely(val)) {
|
||||||
applog(LOG_INFO, "LONGPOLL detected new block");
|
applog(LOG_INFO, "LONGPOLL detected new block");
|
||||||
soval = json_object_get(json_object_get(val, "result"), "submitold");
|
soval = json_object_get(json_object_get(val, "result"), "submitold");
|
||||||
|
@ -783,6 +888,99 @@ out:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool stratum_handle_response(char *buf)
|
||||||
|
{
|
||||||
|
json_t *val, *err_val, *res_val, *id_val;
|
||||||
|
json_error_t err;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
val = JSON_LOADS(buf, &err);
|
||||||
|
if (!val) {
|
||||||
|
applog(LOG_INFO, "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");
|
||||||
|
id_val = json_object_get(val, "id");
|
||||||
|
|
||||||
|
if (!id_val || json_is_null(id_val) || !res_val)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
share_result(json_is_true(res_val),
|
||||||
|
err_val ? json_string_value(json_array_get(err_val, 1)) : NULL);
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
out:
|
||||||
|
if (val)
|
||||||
|
json_decref(val);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *stratum_thread(void *userdata)
|
||||||
|
{
|
||||||
|
struct thr_info *mythr = userdata;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
stratum.url = tq_pop(mythr->q, NULL);
|
||||||
|
if (!stratum.url)
|
||||||
|
goto out;
|
||||||
|
applog(LOG_INFO, "Starting Stratum on %s", stratum.url);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int failures = 0;
|
||||||
|
|
||||||
|
while (!stratum.curl) {
|
||||||
|
pthread_mutex_lock(&g_work_lock);
|
||||||
|
g_work_time = 0;
|
||||||
|
pthread_mutex_unlock(&g_work_lock);
|
||||||
|
restart_threads();
|
||||||
|
|
||||||
|
if (!stratum_connect(&stratum, stratum.url) ||
|
||||||
|
!stratum_subscribe(&stratum) ||
|
||||||
|
!stratum_authorize(&stratum, rpc_user, rpc_pass)) {
|
||||||
|
stratum_disconnect(&stratum);
|
||||||
|
if (opt_retries >= 0 && ++failures > opt_retries) {
|
||||||
|
applog(LOG_ERR, "...terminating workio thread");
|
||||||
|
tq_push(thr_info[work_thr_id].q, NULL);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
applog(LOG_ERR, "...retry after %d seconds", opt_fail_pause);
|
||||||
|
sleep(opt_fail_pause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(stratum.job.job_id, g_work.job_id) || !g_work_time) {
|
||||||
|
pthread_mutex_lock(&g_work_lock);
|
||||||
|
stratum_gen_work(&stratum, &g_work);
|
||||||
|
time(&g_work_time);
|
||||||
|
pthread_mutex_unlock(&g_work_lock);
|
||||||
|
if (stratum.job.clean) {
|
||||||
|
applog(LOG_INFO, "Stratum detected new block");
|
||||||
|
restart_threads();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stratum_socket_full(&stratum, 120)) {
|
||||||
|
applog(LOG_ERR, "Stratum connection timed out");
|
||||||
|
s = NULL;
|
||||||
|
} else
|
||||||
|
s = stratum_recv_line(&stratum);
|
||||||
|
if (!s) {
|
||||||
|
stratum_disconnect(&stratum);
|
||||||
|
applog(LOG_ERR, "Stratum connection interrupted");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!stratum_handle_method(&stratum, s))
|
||||||
|
stratum_handle_response(s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void show_version_and_exit(void)
|
static void show_version_and_exit(void)
|
||||||
{
|
{
|
||||||
printf("%s\n%s\n", PACKAGE_STRING, curl_version());
|
printf("%s\n%s\n", PACKAGE_STRING, curl_version());
|
||||||
|
@ -883,7 +1081,8 @@ static void parse_arg (int key, char *arg)
|
||||||
case 'o': /* --url */
|
case 'o': /* --url */
|
||||||
p = strstr(arg, "://");
|
p = strstr(arg, "://");
|
||||||
if (p) {
|
if (p) {
|
||||||
if (strncmp(arg, "http://", 7) && strncmp(arg, "https://", 8))
|
if (strncasecmp(arg, "http://", 7) && strncasecmp(arg, "https://", 8) &&
|
||||||
|
strncasecmp(arg, "stratum+tcp://", 14))
|
||||||
show_usage_and_exit(1);
|
show_usage_and_exit(1);
|
||||||
free(rpc_url);
|
free(rpc_url);
|
||||||
rpc_url = strdup(arg);
|
rpc_url = strdup(arg);
|
||||||
|
@ -891,38 +1090,52 @@ static void parse_arg (int key, char *arg)
|
||||||
if (!strlen(arg) || *arg == '/')
|
if (!strlen(arg) || *arg == '/')
|
||||||
show_usage_and_exit(1);
|
show_usage_and_exit(1);
|
||||||
free(rpc_url);
|
free(rpc_url);
|
||||||
rpc_url = malloc((strlen(arg) + 8) * sizeof(char));
|
rpc_url = malloc(strlen(arg) + 8);
|
||||||
sprintf(rpc_url, "http://%s", arg);
|
sprintf(rpc_url, "http://%s", arg);
|
||||||
}
|
}
|
||||||
p = strrchr(rpc_url, '@');
|
p = strrchr(rpc_url, '@');
|
||||||
if (p) {
|
if (p) {
|
||||||
char *ap = strstr(rpc_url, "://") + 3;
|
char *sp, *ap;
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
if (strchr(ap, ':')) {
|
ap = strstr(rpc_url, "://") + 3;
|
||||||
|
sp = strchr(ap, ':');
|
||||||
|
if (sp) {
|
||||||
free(rpc_userpass);
|
free(rpc_userpass);
|
||||||
rpc_userpass = strdup(ap);
|
rpc_userpass = strdup(ap);
|
||||||
|
free(rpc_user);
|
||||||
|
rpc_user = calloc(sp - ap + 1, 1);
|
||||||
|
strncpy(rpc_user, ap, sp - ap);
|
||||||
|
free(rpc_pass);
|
||||||
|
rpc_pass = strdup(sp + 1);
|
||||||
} else {
|
} else {
|
||||||
free(rpc_user);
|
free(rpc_user);
|
||||||
rpc_user = strdup(ap);
|
rpc_user = strdup(ap);
|
||||||
}
|
}
|
||||||
memmove(ap, p + 1, (strlen(p + 1) + 1) * sizeof(char));
|
memmove(ap, p + 1, strlen(p + 1) + 1);
|
||||||
}
|
}
|
||||||
|
have_stratum = !strncasecmp(rpc_url, "stratum", 7);
|
||||||
break;
|
break;
|
||||||
case 'O': /* --userpass */
|
case 'O': /* --userpass */
|
||||||
if (!strchr(arg, ':'))
|
p = strchr(arg, ':');
|
||||||
|
if (!p)
|
||||||
show_usage_and_exit(1);
|
show_usage_and_exit(1);
|
||||||
free(rpc_userpass);
|
free(rpc_userpass);
|
||||||
rpc_userpass = strdup(arg);
|
rpc_userpass = strdup(arg);
|
||||||
|
free(rpc_user);
|
||||||
|
rpc_user = calloc(p - arg + 1, 1);
|
||||||
|
strncpy(rpc_user, arg, p - arg);
|
||||||
|
free(rpc_pass);
|
||||||
|
rpc_pass = strdup(p + 1);
|
||||||
break;
|
break;
|
||||||
case 'x': /* --proxy */
|
case 'x': /* --proxy */
|
||||||
if (!strncmp(arg, "socks4://", 9))
|
if (!strncasecmp(arg, "socks4://", 9))
|
||||||
opt_proxy_type = CURLPROXY_SOCKS4;
|
opt_proxy_type = CURLPROXY_SOCKS4;
|
||||||
else if (!strncmp(arg, "socks5://", 9))
|
else if (!strncasecmp(arg, "socks5://", 9))
|
||||||
opt_proxy_type = CURLPROXY_SOCKS5;
|
opt_proxy_type = CURLPROXY_SOCKS5;
|
||||||
#if LIBCURL_VERSION_NUM >= 0x071200
|
#if LIBCURL_VERSION_NUM >= 0x071200
|
||||||
else if (!strncmp(arg, "socks4a://", 10))
|
else if (!strncasecmp(arg, "socks4a://", 10))
|
||||||
opt_proxy_type = CURLPROXY_SOCKS4A;
|
opt_proxy_type = CURLPROXY_SOCKS4A;
|
||||||
else if (!strncmp(arg, "socks5h://", 10))
|
else if (!strncasecmp(arg, "socks5h://", 10))
|
||||||
opt_proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
|
opt_proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
|
@ -932,9 +1145,15 @@ static void parse_arg (int key, char *arg)
|
||||||
break;
|
break;
|
||||||
case 1005:
|
case 1005:
|
||||||
opt_benchmark = true;
|
opt_benchmark = true;
|
||||||
|
want_longpoll = false;
|
||||||
|
want_stratum = false;
|
||||||
|
break;
|
||||||
case 1003:
|
case 1003:
|
||||||
want_longpoll = false;
|
want_longpoll = false;
|
||||||
break;
|
break;
|
||||||
|
case 1007:
|
||||||
|
want_stratum = false;
|
||||||
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
use_syslog = true;
|
use_syslog = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1028,6 +1247,8 @@ int main(int argc, char *argv[])
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
rpc_url = strdup(DEF_RPC_URL);
|
rpc_url = strdup(DEF_RPC_URL);
|
||||||
|
rpc_user = strdup("");
|
||||||
|
rpc_pass = strdup("");
|
||||||
|
|
||||||
/* parse command line */
|
/* parse command line */
|
||||||
parse_cmdline(argc, argv);
|
parse_cmdline(argc, argv);
|
||||||
|
@ -1052,6 +1273,8 @@ int main(int argc, char *argv[])
|
||||||
pthread_mutex_init(&applog_lock, NULL);
|
pthread_mutex_init(&applog_lock, NULL);
|
||||||
pthread_mutex_init(&stats_lock, NULL);
|
pthread_mutex_init(&stats_lock, NULL);
|
||||||
pthread_mutex_init(&g_work_lock, NULL);
|
pthread_mutex_init(&g_work_lock, NULL);
|
||||||
|
pthread_mutex_init(&stratum.sock_lock, NULL);
|
||||||
|
pthread_mutex_init(&stratum.work_lock, NULL);
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
SYSTEM_INFO sysinfo;
|
SYSTEM_INFO sysinfo;
|
||||||
|
@ -1071,11 +1294,7 @@ int main(int argc, char *argv[])
|
||||||
if (!opt_n_threads)
|
if (!opt_n_threads)
|
||||||
opt_n_threads = num_processors;
|
opt_n_threads = num_processors;
|
||||||
|
|
||||||
if (!rpc_userpass && (rpc_user || rpc_pass)) {
|
if (!rpc_userpass) {
|
||||||
if (!rpc_user)
|
|
||||||
rpc_user = strdup("");
|
|
||||||
if (!rpc_pass)
|
|
||||||
rpc_pass = strdup("");
|
|
||||||
rpc_userpass = malloc(strlen(rpc_user) + strlen(rpc_pass) + 2);
|
rpc_userpass = malloc(strlen(rpc_user) + strlen(rpc_pass) + 2);
|
||||||
if (!rpc_userpass)
|
if (!rpc_userpass)
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1091,7 +1310,7 @@ int main(int argc, char *argv[])
|
||||||
if (!work_restart)
|
if (!work_restart)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
thr_info = calloc(opt_n_threads + 2, sizeof(*thr));
|
thr_info = calloc(opt_n_threads + 3, sizeof(*thr));
|
||||||
if (!thr_info)
|
if (!thr_info)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
@ -1113,8 +1332,8 @@ int main(int argc, char *argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* init longpoll thread info */
|
if (want_longpoll && !have_stratum) {
|
||||||
if (want_longpoll) {
|
/* init longpoll thread info */
|
||||||
longpoll_thr_id = opt_n_threads + 1;
|
longpoll_thr_id = opt_n_threads + 1;
|
||||||
thr = &thr_info[longpoll_thr_id];
|
thr = &thr_info[longpoll_thr_id];
|
||||||
thr->id = longpoll_thr_id;
|
thr->id = longpoll_thr_id;
|
||||||
|
@ -1127,8 +1346,25 @@ int main(int argc, char *argv[])
|
||||||
applog(LOG_ERR, "longpoll thread create failed");
|
applog(LOG_ERR, "longpoll thread create failed");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
longpoll_thr_id = -1;
|
if (want_stratum) {
|
||||||
|
/* init stratum thread info */
|
||||||
|
stratum_thr_id = opt_n_threads + 2;
|
||||||
|
thr = &thr_info[stratum_thr_id];
|
||||||
|
thr->id = stratum_thr_id;
|
||||||
|
thr->q = tq_new();
|
||||||
|
if (!thr->q)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* start stratum thread */
|
||||||
|
if (unlikely(pthread_create(&thr->pth, NULL, stratum_thread, thr))) {
|
||||||
|
applog(LOG_ERR, "stratum thread create failed");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_stratum)
|
||||||
|
tq_push(thr_info[stratum_thr_id].q, strdup(rpc_url));
|
||||||
|
}
|
||||||
|
|
||||||
/* start mining threads */
|
/* start mining threads */
|
||||||
for (i = 0; i < opt_n_threads; i++) {
|
for (i = 0; i < opt_n_threads; i++) {
|
||||||
|
|
59
miner.h
59
miner.h
|
@ -42,6 +42,7 @@ void *alloca (size_t);
|
||||||
enum {
|
enum {
|
||||||
LOG_ERR,
|
LOG_ERR,
|
||||||
LOG_WARNING,
|
LOG_WARNING,
|
||||||
|
LOG_NOTICE,
|
||||||
LOG_INFO,
|
LOG_INFO,
|
||||||
LOG_DEBUG,
|
LOG_DEBUG,
|
||||||
};
|
};
|
||||||
|
@ -121,8 +122,17 @@ static inline void le32enc(void *pp, uint32_t x)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if JANSSON_MAJOR_VERSION >= 2
|
||||||
|
#define JSON_LOADS(str, err_ptr) json_loads((str), 0, (err_ptr))
|
||||||
|
#else
|
||||||
|
#define JSON_LOADS(str, err_ptr) json_loads((str), (err_ptr))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define USER_AGENT PACKAGE_NAME "/" PACKAGE_VERSION
|
||||||
|
|
||||||
void sha256_init(uint32_t *state);
|
void sha256_init(uint32_t *state);
|
||||||
void sha256_transform(uint32_t *state, const uint32_t *block, int swap);
|
void sha256_transform(uint32_t *state, const uint32_t *block, int swap);
|
||||||
|
void sha256d(unsigned char *hash, const unsigned char *data, int len);
|
||||||
|
|
||||||
#if defined(__ARM_NEON__) || defined(__i386__) || defined(__x86_64__)
|
#if defined(__ARM_NEON__) || defined(__i386__) || defined(__x86_64__)
|
||||||
#define HAVE_SHA256_4WAY 1
|
#define HAVE_SHA256_4WAY 1
|
||||||
|
@ -155,12 +165,15 @@ extern bool opt_protocol;
|
||||||
extern int opt_timeout;
|
extern int opt_timeout;
|
||||||
extern bool want_longpoll;
|
extern bool want_longpoll;
|
||||||
extern bool have_longpoll;
|
extern bool have_longpoll;
|
||||||
|
extern bool want_stratum;
|
||||||
|
extern bool have_stratum;
|
||||||
extern char *opt_proxy;
|
extern char *opt_proxy;
|
||||||
extern long opt_proxy_type;
|
extern long opt_proxy_type;
|
||||||
extern bool use_syslog;
|
extern bool use_syslog;
|
||||||
extern pthread_mutex_t applog_lock;
|
extern pthread_mutex_t applog_lock;
|
||||||
extern struct thr_info *thr_info;
|
extern struct thr_info *thr_info;
|
||||||
extern int longpoll_thr_id;
|
extern int longpoll_thr_id;
|
||||||
|
extern int stratum_thr_id;
|
||||||
extern struct work_restart *work_restart;
|
extern struct work_restart *work_restart;
|
||||||
|
|
||||||
extern void applog(int prio, const char *fmt, ...);
|
extern void applog(int prio, const char *fmt, ...);
|
||||||
|
@ -171,6 +184,52 @@ extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len);
|
||||||
extern int timeval_subtract(struct timeval *result, struct timeval *x,
|
extern int timeval_subtract(struct timeval *result, struct timeval *x,
|
||||||
struct timeval *y);
|
struct timeval *y);
|
||||||
extern bool fulltest(const uint32_t *hash, const uint32_t *target);
|
extern bool fulltest(const uint32_t *hash, const uint32_t *target);
|
||||||
|
extern void diff_to_target(uint32_t *target, double diff);
|
||||||
|
|
||||||
|
struct stratum_job {
|
||||||
|
char *job_id;
|
||||||
|
unsigned char prevhash[32];
|
||||||
|
size_t coinbase_size;
|
||||||
|
unsigned char *coinbase;
|
||||||
|
unsigned char *xnonce2;
|
||||||
|
int merkle_count;
|
||||||
|
unsigned char **merkle;
|
||||||
|
unsigned char version[4];
|
||||||
|
unsigned char nbits[4];
|
||||||
|
unsigned char ntime[4];
|
||||||
|
bool clean;
|
||||||
|
double diff;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stratum_ctx {
|
||||||
|
char *url;
|
||||||
|
|
||||||
|
CURL *curl;
|
||||||
|
char *curl_url;
|
||||||
|
char curl_err_str[CURL_ERROR_SIZE];
|
||||||
|
curl_socket_t sock;
|
||||||
|
size_t sockbuf_size;
|
||||||
|
char *sockbuf;
|
||||||
|
pthread_mutex_t sock_lock;
|
||||||
|
|
||||||
|
double next_diff;
|
||||||
|
|
||||||
|
char *session_id;
|
||||||
|
size_t xnonce1_size;
|
||||||
|
unsigned char *xnonce1;
|
||||||
|
size_t xnonce2_size;
|
||||||
|
struct stratum_job job;
|
||||||
|
pthread_mutex_t work_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool stratum_socket_full(struct stratum_ctx *sctx, int timeout);
|
||||||
|
bool stratum_send_line(struct stratum_ctx *sctx, char *s);
|
||||||
|
char *stratum_recv_line(struct stratum_ctx *sctx);
|
||||||
|
bool stratum_connect(struct stratum_ctx *sctx, const char *url);
|
||||||
|
void stratum_disconnect(struct stratum_ctx *sctx);
|
||||||
|
bool stratum_subscribe(struct stratum_ctx *sctx);
|
||||||
|
bool stratum_authorize(struct stratum_ctx *sctx, const char *user, const char *pass);
|
||||||
|
bool stratum_handle_method(struct stratum_ctx *sctx, const char *s);
|
||||||
|
|
||||||
struct thread_q;
|
struct thread_q;
|
||||||
|
|
||||||
|
|
34
sha2.c
34
sha2.c
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2011 ArtForz, 2011-2012 pooler
|
* Copyright 2011 ArtForz
|
||||||
|
* Copyright 2011-2013 pooler
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
@ -180,7 +181,7 @@ static const uint32_t sha256d_hash1[16] = {
|
||||||
0x00000000, 0x00000000, 0x00000000, 0x00000100
|
0x00000000, 0x00000000, 0x00000000, 0x00000100
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sha256d(uint32_t *hash, uint32_t *data)
|
static void sha256d_80(uint32_t *hash, const uint32_t *data)
|
||||||
{
|
{
|
||||||
uint32_t S[16];
|
uint32_t S[16];
|
||||||
|
|
||||||
|
@ -192,6 +193,31 @@ static void sha256d(uint32_t *hash, uint32_t *data)
|
||||||
sha256_transform(hash, S, 0);
|
sha256_transform(hash, S, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sha256d(unsigned char *hash, const unsigned char *data, int len)
|
||||||
|
{
|
||||||
|
uint32_t S[16], T[16];
|
||||||
|
int i, r;
|
||||||
|
|
||||||
|
sha256_init(S);
|
||||||
|
for (r = len; r > -9; r -= 64) {
|
||||||
|
if (r < 64)
|
||||||
|
memset(T, 0, 64);
|
||||||
|
memcpy(T, data + len - r, r > 64 ? 64 : (r < 0 ? 0 : r));
|
||||||
|
if (r < 64)
|
||||||
|
((unsigned char *)T)[r] = 0x80;
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
T[i] = be32dec(T + i);
|
||||||
|
if (r < 56)
|
||||||
|
T[15] = 8 * len;
|
||||||
|
sha256_transform(S, T, 0);
|
||||||
|
}
|
||||||
|
memcpy(S + 8, sha256d_hash1 + 8, 32);
|
||||||
|
sha256_init(T);
|
||||||
|
sha256_transform(T, S, 0);
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
be32enc((uint32_t *)hash + i, T[i]);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void sha256d_preextend(uint32_t *W)
|
static inline void sha256d_preextend(uint32_t *W)
|
||||||
{
|
{
|
||||||
W[16] = s1(W[14]) + W[ 9] + s0(W[ 1]) + W[ 0];
|
W[16] = s1(W[14]) + W[ 9] + s0(W[ 1]) + W[ 0];
|
||||||
|
@ -477,7 +503,7 @@ static inline int scanhash_sha256d_4way(int thr_id, uint32_t *pdata,
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
if (hash[4 * 7 + i] <= Htarg) {
|
if (hash[4 * 7 + i] <= Htarg) {
|
||||||
pdata[19] = data[4 * 3 + i];
|
pdata[19] = data[4 * 3 + i];
|
||||||
sha256d(hash, pdata);
|
sha256d_80(hash, pdata);
|
||||||
if (fulltest(hash, ptarget)) {
|
if (fulltest(hash, ptarget)) {
|
||||||
*hashes_done = n - first_nonce + 1;
|
*hashes_done = n - first_nonce + 1;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -523,7 +549,7 @@ int scanhash_sha256d(int thr_id, uint32_t *pdata, const uint32_t *ptarget,
|
||||||
sha256d_ms(hash, data, midstate, prehash);
|
sha256d_ms(hash, data, midstate, prehash);
|
||||||
if (hash[7] <= Htarg) {
|
if (hash[7] <= Htarg) {
|
||||||
pdata[19] = data[3];
|
pdata[19] = data[3];
|
||||||
sha256d(hash, pdata);
|
sha256d_80(hash, pdata);
|
||||||
if (fulltest(hash, ptarget)) {
|
if (fulltest(hash, ptarget)) {
|
||||||
*hashes_done = n - first_nonce + 1;
|
*hashes_done = n - first_nonce + 1;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
691
util.c
691
util.c
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2010 Jeff Garzik, 2012 pooler
|
* Copyright 2010 Jeff Garzik
|
||||||
|
* Copyright 2012-2013 pooler
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <mstcpip.h>
|
#include <mstcpip.h>
|
||||||
#else
|
#else
|
||||||
|
#include <errno.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
@ -47,6 +49,7 @@ struct upload_buffer {
|
||||||
struct header_info {
|
struct header_info {
|
||||||
char *lp_path;
|
char *lp_path;
|
||||||
char *reason;
|
char *reason;
|
||||||
|
char *stratum_url;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tq_ent {
|
struct tq_ent {
|
||||||
|
@ -235,6 +238,11 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
|
||||||
val = NULL;
|
val = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcasecmp("X-Stratum", key)) {
|
||||||
|
hi->stratum_url = val; /* steal memory reference */
|
||||||
|
val = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
free(key);
|
free(key);
|
||||||
free(val);
|
free(val);
|
||||||
|
@ -242,7 +250,7 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LIBCURL_VERSION_NUM >= 0x070f06
|
#if LIBCURL_VERSION_NUM >= 0x070f06
|
||||||
static int json_rpc_call_lp_cb(void *userdata, curl_socket_t fd,
|
static int sockopt_keepalive_cb(void *userdata, curl_socket_t fd,
|
||||||
curlsocktype purpose)
|
curlsocktype purpose)
|
||||||
{
|
{
|
||||||
int keepalive = 1;
|
int keepalive = 1;
|
||||||
|
@ -333,7 +341,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
|
||||||
}
|
}
|
||||||
#if LIBCURL_VERSION_NUM >= 0x070f06
|
#if LIBCURL_VERSION_NUM >= 0x070f06
|
||||||
if (longpoll)
|
if (longpoll)
|
||||||
curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_lp_cb);
|
curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_keepalive_cb);
|
||||||
#endif
|
#endif
|
||||||
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
||||||
|
|
||||||
|
@ -346,13 +354,10 @@ json_t *json_rpc_call(CURL *curl, const char *url,
|
||||||
sprintf(len_hdr, "Content-Length: %lu",
|
sprintf(len_hdr, "Content-Length: %lu",
|
||||||
(unsigned long) upload_data.len);
|
(unsigned long) upload_data.len);
|
||||||
|
|
||||||
headers = curl_slist_append(headers,
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||||
"Content-Type: application/json");
|
|
||||||
headers = curl_slist_append(headers, len_hdr);
|
headers = curl_slist_append(headers, len_hdr);
|
||||||
headers = curl_slist_append(headers,
|
headers = curl_slist_append(headers, "User-Agent: " USER_AGENT);
|
||||||
"User-Agent: " PACKAGE_NAME "/" PACKAGE_VERSION);
|
headers = curl_slist_append(headers, "X-Mining-Extensions: midstate");
|
||||||
headers = curl_slist_append(headers,
|
|
||||||
"X-Mining-Extensions: midstate");
|
|
||||||
headers = curl_slist_append(headers, "Accept:"); /* disable Accept hdr*/
|
headers = curl_slist_append(headers, "Accept:"); /* disable Accept hdr*/
|
||||||
headers = curl_slist_append(headers, "Expect:"); /* disable Expect hdr*/
|
headers = curl_slist_append(headers, "Expect:"); /* disable Expect hdr*/
|
||||||
|
|
||||||
|
@ -367,24 +372,27 @@ json_t *json_rpc_call(CURL *curl, const char *url,
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If X-Stratum was found, activate Stratum */
|
||||||
|
if (want_stratum && hi.stratum_url &&
|
||||||
|
!strncasecmp(hi.stratum_url, "stratum+tcp://", 14)) {
|
||||||
|
have_stratum = true;
|
||||||
|
tq_push(thr_info[stratum_thr_id].q, hi.stratum_url);
|
||||||
|
hi.stratum_url = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* If X-Long-Polling was found, activate long polling */
|
/* If X-Long-Polling was found, activate long polling */
|
||||||
if (lp_scanning && hi.lp_path) {
|
if (lp_scanning && hi.lp_path && !have_stratum) {
|
||||||
have_longpoll = true;
|
have_longpoll = true;
|
||||||
tq_push(thr_info[longpoll_thr_id].q, hi.lp_path);
|
tq_push(thr_info[longpoll_thr_id].q, hi.lp_path);
|
||||||
} else
|
hi.lp_path = NULL;
|
||||||
free(hi.lp_path);
|
}
|
||||||
hi.lp_path = NULL;
|
|
||||||
|
|
||||||
if (!all_data.buf) {
|
if (!all_data.buf) {
|
||||||
applog(LOG_ERR, "Empty data received in json_rpc_call.");
|
applog(LOG_ERR, "Empty data received in json_rpc_call.");
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if JANSSON_VERSION_HEX >= 0x020000
|
val = JSON_LOADS(all_data.buf, &err);
|
||||||
val = json_loads(all_data.buf, 0, &err);
|
|
||||||
#else
|
|
||||||
val = json_loads(all_data.buf, &err);
|
|
||||||
#endif
|
|
||||||
if (!val) {
|
if (!val) {
|
||||||
applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text);
|
applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text);
|
||||||
goto err_out;
|
goto err_out;
|
||||||
|
@ -426,6 +434,9 @@ json_t *json_rpc_call(CURL *curl, const char *url,
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
|
free(hi.lp_path);
|
||||||
|
free(hi.reason);
|
||||||
|
free(hi.stratum_url);
|
||||||
databuf_free(&all_data);
|
databuf_free(&all_data);
|
||||||
curl_slist_free_all(headers);
|
curl_slist_free_all(headers);
|
||||||
curl_easy_reset(curl);
|
curl_easy_reset(curl);
|
||||||
|
@ -542,6 +553,650 @@ bool fulltest(const uint32_t *hash, const uint32_t *target)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void diff_to_target(uint32_t *target, double diff)
|
||||||
|
{
|
||||||
|
uint64_t m;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
for (k = 6; k > 0 && diff > 1.0; k--)
|
||||||
|
diff /= 4294967296.0;
|
||||||
|
m = 4294901760.0 / diff;
|
||||||
|
if (m == 0 && k == 6)
|
||||||
|
memset(target, 0xff, 32);
|
||||||
|
else {
|
||||||
|
memset(target, 0, 32);
|
||||||
|
target[k] = (uint32_t)m;
|
||||||
|
target[k + 1] = (uint32_t)(m >> 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#define socket_blocks() (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||||
|
#else
|
||||||
|
#define socket_blocks() (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool send_line(curl_socket_t sock, char *s)
|
||||||
|
{
|
||||||
|
ssize_t len, sent = 0;
|
||||||
|
|
||||||
|
len = strlen(s);
|
||||||
|
s[len++] = '\n';
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
struct timeval timeout = {0, 0};
|
||||||
|
ssize_t n;
|
||||||
|
fd_set wd;
|
||||||
|
|
||||||
|
FD_ZERO(&wd);
|
||||||
|
FD_SET(sock, &wd);
|
||||||
|
if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1)
|
||||||
|
return false;
|
||||||
|
n = send(sock, s + sent, len, 0);
|
||||||
|
if (n < 0) {
|
||||||
|
if (!socket_blocks())
|
||||||
|
return false;
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
sent += n;
|
||||||
|
len -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stratum_send_line(struct stratum_ctx *sctx, char *s)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (opt_protocol)
|
||||||
|
applog(LOG_DEBUG, "> %s", s);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sctx->sock_lock);
|
||||||
|
ret = send_line(sctx->sock, s);
|
||||||
|
pthread_mutex_unlock(&sctx->sock_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool socket_full(curl_socket_t sock, int timeout)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
fd_set rd;
|
||||||
|
|
||||||
|
FD_ZERO(&rd);
|
||||||
|
FD_SET(sock, &rd);
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
if (select(sock + 1, &rd, NULL, NULL, &tv) > 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stratum_socket_full(struct stratum_ctx *sctx, int timeout)
|
||||||
|
{
|
||||||
|
return strlen(sctx->sockbuf) || socket_full(sctx->sock, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RBUFSIZE 2048
|
||||||
|
#define RECVSIZE (RBUFSIZE - 4)
|
||||||
|
|
||||||
|
static void stratum_buffer_append(struct stratum_ctx *sctx, const char *s)
|
||||||
|
{
|
||||||
|
size_t old, new;
|
||||||
|
|
||||||
|
old = strlen(sctx->sockbuf);
|
||||||
|
new = old + strlen(s) + 1;
|
||||||
|
if (new >= sctx->sockbuf_size) {
|
||||||
|
sctx->sockbuf_size = new + (RBUFSIZE - (new % RBUFSIZE));
|
||||||
|
sctx->sockbuf = realloc(sctx->sockbuf, sctx->sockbuf_size);
|
||||||
|
}
|
||||||
|
strcpy(sctx->sockbuf + old, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *stratum_recv_line(struct stratum_ctx *sctx)
|
||||||
|
{
|
||||||
|
ssize_t len, buflen;
|
||||||
|
char *tok, *sret = NULL;
|
||||||
|
|
||||||
|
if (!strstr(sctx->sockbuf, "\n")) {
|
||||||
|
bool ret = true;
|
||||||
|
time_t rstart;
|
||||||
|
|
||||||
|
time(&rstart);
|
||||||
|
if (!socket_full(sctx->sock, 60)) {
|
||||||
|
applog(LOG_ERR, "stratum_recv_line timed out");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
char s[RBUFSIZE];
|
||||||
|
ssize_t n;
|
||||||
|
|
||||||
|
memset(s, 0, RBUFSIZE);
|
||||||
|
n = recv(sctx->sock, s, RECVSIZE, 0);
|
||||||
|
if (!n) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (n < 0) {
|
||||||
|
if (!socket_blocks() || !socket_full(sctx->sock, 1)) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
stratum_buffer_append(sctx, s);
|
||||||
|
} while (time(NULL) - rstart < 60 && !strstr(sctx->sockbuf, "\n"));
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
applog(LOG_ERR, "stratum_recv_line failed");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buflen = strlen(sctx->sockbuf);
|
||||||
|
tok = strtok(sctx->sockbuf, "\n");
|
||||||
|
if (!tok) {
|
||||||
|
applog(LOG_ERR, "stratum_recv_line failed to parse a newline-terminated string");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
sret = strdup(tok);
|
||||||
|
len = strlen(sret);
|
||||||
|
|
||||||
|
if (buflen > len + 1)
|
||||||
|
memmove(sctx->sockbuf, sctx->sockbuf + len + 1, buflen - len + 1);
|
||||||
|
else
|
||||||
|
sctx->sockbuf[0] = '\0';
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (sret && opt_protocol)
|
||||||
|
applog(LOG_DEBUG, "< %s", sret);
|
||||||
|
return sret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LIBCURL_VERSION_NUM >= 0x071101
|
||||||
|
static curl_socket_t opensocket_grab_cb(void *clientp, curlsocktype purpose,
|
||||||
|
struct curl_sockaddr *addr)
|
||||||
|
{
|
||||||
|
curl_socket_t *sock = clientp;
|
||||||
|
*sock = socket(addr->family, addr->socktype, addr->protocol);
|
||||||
|
return *sock;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool stratum_connect(struct stratum_ctx *sctx, const char *url)
|
||||||
|
{
|
||||||
|
CURL *curl;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sctx->sock_lock);
|
||||||
|
if (sctx->curl)
|
||||||
|
curl_easy_cleanup(sctx->curl);
|
||||||
|
sctx->curl = curl_easy_init();
|
||||||
|
if (!sctx->curl) {
|
||||||
|
applog(LOG_ERR, "CURL initialization failed");
|
||||||
|
pthread_mutex_unlock(&sctx->sock_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
curl = sctx->curl;
|
||||||
|
if (!sctx->sockbuf) {
|
||||||
|
sctx->sockbuf = calloc(RBUFSIZE, 1);
|
||||||
|
sctx->sockbuf_size = RBUFSIZE;
|
||||||
|
}
|
||||||
|
sctx->sockbuf[0] = '\0';
|
||||||
|
pthread_mutex_unlock(&sctx->sock_lock);
|
||||||
|
|
||||||
|
if (url != sctx->url) {
|
||||||
|
free(sctx->url);
|
||||||
|
sctx->url = strdup(url);
|
||||||
|
}
|
||||||
|
free(sctx->curl_url);
|
||||||
|
sctx->curl_url = malloc(strlen(url));
|
||||||
|
sprintf(sctx->curl_url, "http%s", strstr(url, "://"));
|
||||||
|
|
||||||
|
if (opt_protocol)
|
||||||
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, sctx->curl_url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, sctx->curl_err_str);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
|
||||||
|
if (opt_proxy && opt_proxy_type != CURLPROXY_HTTP) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXY, opt_proxy);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, opt_proxy_type);
|
||||||
|
} else if (getenv("http_proxy")) {
|
||||||
|
if (getenv("all_proxy"))
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXY, getenv("all_proxy"));
|
||||||
|
else if (getenv("ALL_PROXY"))
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXY, getenv("ALL_PROXY"));
|
||||||
|
else
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXY, "");
|
||||||
|
}
|
||||||
|
#if LIBCURL_VERSION_NUM >= 0x070f06
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_keepalive_cb);
|
||||||
|
#endif
|
||||||
|
#if LIBCURL_VERSION_NUM >= 0x071101
|
||||||
|
curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket_grab_cb);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &sctx->sock);
|
||||||
|
#endif
|
||||||
|
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1);
|
||||||
|
|
||||||
|
rc = curl_easy_perform(curl);
|
||||||
|
if (rc) {
|
||||||
|
applog(LOG_ERR, "Stratum connection failed: %s", sctx->curl_err_str);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
sctx->curl = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LIBCURL_VERSION_NUM < 0x071101
|
||||||
|
/* CURLINFO_LASTSOCKET is broken on Win64; only use it as a last resort */
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, (long *)&sctx->sock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stratum_disconnect(struct stratum_ctx *sctx)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&sctx->sock_lock);
|
||||||
|
if (sctx->curl) {
|
||||||
|
curl_easy_cleanup(sctx->curl);
|
||||||
|
sctx->curl = NULL;
|
||||||
|
sctx->sockbuf[0] = '\0';
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&sctx->sock_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_stratum_session_id(json_t *val)
|
||||||
|
{
|
||||||
|
json_t *arr_val;
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
arr_val = json_array_get(val, 0);
|
||||||
|
if (!arr_val || !json_is_array(arr_val))
|
||||||
|
return NULL;
|
||||||
|
n = json_array_size(arr_val);
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
const char *notify;
|
||||||
|
json_t *arr = json_array_get(arr_val, i);
|
||||||
|
|
||||||
|
if (!arr || !json_is_array(arr))
|
||||||
|
break;
|
||||||
|
notify = json_string_value(json_array_get(arr, 0));
|
||||||
|
if (!notify)
|
||||||
|
continue;
|
||||||
|
if (!strcasecmp(notify, "mining.notify"))
|
||||||
|
return json_string_value(json_array_get(arr, 1));
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stratum_subscribe(struct stratum_ctx *sctx)
|
||||||
|
{
|
||||||
|
char *s, *sret = NULL;
|
||||||
|
const char *sid, *xnonce1;
|
||||||
|
int xn2_size;
|
||||||
|
json_t *val = NULL, *res_val, *err_val;
|
||||||
|
json_error_t err;
|
||||||
|
bool ret = false, retry = false;
|
||||||
|
|
||||||
|
start:
|
||||||
|
s = malloc(128 + (sctx->session_id ? strlen(sctx->session_id) : 0));
|
||||||
|
if (retry)
|
||||||
|
sprintf(s, "{\"id\": 1, \"method\": \"mining.subscribe\", \"params\": []}");
|
||||||
|
else if (sctx->session_id)
|
||||||
|
sprintf(s, "{\"id\": 1, \"method\": \"mining.subscribe\", \"params\": [\"" USER_AGENT "\", \"%s\"]}", sctx->session_id);
|
||||||
|
else
|
||||||
|
sprintf(s, "{\"id\": 1, \"method\": \"mining.subscribe\", \"params\": [\"" USER_AGENT "\"]}");
|
||||||
|
|
||||||
|
if (!stratum_send_line(sctx, s))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (!socket_full(sctx->sock, 30)) {
|
||||||
|
applog(LOG_ERR, "stratum_subscribe timed out");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sret = stratum_recv_line(sctx);
|
||||||
|
if (!sret)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
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_null(res_val) ||
|
||||||
|
(err_val && !json_is_null(err_val))) {
|
||||||
|
if (opt_debug || retry) {
|
||||||
|
free(s);
|
||||||
|
if (err_val)
|
||||||
|
s = json_dumps(err_val, JSON_INDENT(3));
|
||||||
|
else
|
||||||
|
s = strdup("(unknown reason)");
|
||||||
|
applog(LOG_ERR, "JSON-RPC call failed: %s", s);
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
sid = get_stratum_session_id(res_val);
|
||||||
|
if (opt_debug && !sid)
|
||||||
|
applog(LOG_DEBUG, "Failed to get Stratum session id");
|
||||||
|
xnonce1 = json_string_value(json_array_get(res_val, 1));
|
||||||
|
if (!xnonce1) {
|
||||||
|
applog(LOG_ERR, "Failed to get extranonce1");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
xn2_size = json_integer_value(json_array_get(res_val, 2));
|
||||||
|
if (!xn2_size) {
|
||||||
|
applog(LOG_ERR, "Failed to get extranonce2_size");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sctx->work_lock);
|
||||||
|
free(sctx->session_id);
|
||||||
|
free(sctx->xnonce1);
|
||||||
|
sctx->session_id = sid ? strdup(sid) : NULL;
|
||||||
|
sctx->xnonce1_size = strlen(xnonce1) / 2;
|
||||||
|
sctx->xnonce1 = malloc(sctx->xnonce1_size);
|
||||||
|
hex2bin(sctx->xnonce1, xnonce1, sctx->xnonce1_size);
|
||||||
|
sctx->xnonce2_size = xn2_size;
|
||||||
|
sctx->next_diff = 1.0;
|
||||||
|
pthread_mutex_unlock(&sctx->work_lock);
|
||||||
|
|
||||||
|
if (opt_debug && sid)
|
||||||
|
applog(LOG_DEBUG, "Stratum session id: %s", sctx->session_id);
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(s);
|
||||||
|
if (val)
|
||||||
|
json_decref(val);
|
||||||
|
|
||||||
|
if (!ret) {
|
||||||
|
if (sret && !retry) {
|
||||||
|
retry = true;
|
||||||
|
goto start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stratum_authorize(struct stratum_ctx *sctx, const char *user, const char *pass)
|
||||||
|
{
|
||||||
|
json_t *val = NULL, *res_val, *err_val;
|
||||||
|
char *s, *sret;
|
||||||
|
json_error_t err;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
s = malloc(80 + strlen(user) + strlen(pass));
|
||||||
|
sprintf(s, "{\"id\": 2, \"method\": \"mining.authorize\", \"params\": [\"%s\", \"%s\"]}",
|
||||||
|
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_true(res_val) ||
|
||||||
|
(err_val && !json_is_null(err_val))) {
|
||||||
|
applog(LOG_ERR, "Stratum authentication failed");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(s);
|
||||||
|
if (val)
|
||||||
|
json_decref(val);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stratum_notify(struct stratum_ctx *sctx, json_t *params)
|
||||||
|
{
|
||||||
|
const char *job_id, *prevhash, *coinb1, *coinb2, *version, *nbits, *ntime;
|
||||||
|
size_t coinb1_size, coinb2_size;
|
||||||
|
bool clean, ret = false;
|
||||||
|
int merkle_count, i;
|
||||||
|
json_t *merkle_arr;
|
||||||
|
unsigned char **merkle;
|
||||||
|
|
||||||
|
job_id = json_string_value(json_array_get(params, 0));
|
||||||
|
prevhash = json_string_value(json_array_get(params, 1));
|
||||||
|
coinb1 = json_string_value(json_array_get(params, 2));
|
||||||
|
coinb2 = json_string_value(json_array_get(params, 3));
|
||||||
|
merkle_arr = json_array_get(params, 4);
|
||||||
|
if (!merkle_arr || !json_is_array(merkle_arr))
|
||||||
|
goto out;
|
||||||
|
merkle_count = json_array_size(merkle_arr);
|
||||||
|
version = json_string_value(json_array_get(params, 5));
|
||||||
|
nbits = json_string_value(json_array_get(params, 6));
|
||||||
|
ntime = json_string_value(json_array_get(params, 7));
|
||||||
|
clean = json_is_true(json_array_get(params, 8));
|
||||||
|
|
||||||
|
if (!job_id || !prevhash || !coinb1 || !coinb2 || !version || !nbits || !ntime ||
|
||||||
|
strlen(prevhash) != 64 || strlen(version) != 8 ||
|
||||||
|
strlen(nbits) != 8 || strlen(ntime) != 8) {
|
||||||
|
applog(LOG_ERR, "Stratum notify: invalid parameters");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
merkle = malloc(merkle_count * sizeof(char *));
|
||||||
|
for (i = 0; i < merkle_count; i++) {
|
||||||
|
const char *s = json_string_value(json_array_get(merkle_arr, i));
|
||||||
|
if (!s || strlen(s) != 64) {
|
||||||
|
while (i--)
|
||||||
|
free(merkle[i]);
|
||||||
|
free(merkle);
|
||||||
|
applog(LOG_ERR, "Stratum notify: invalid Merkle branch");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
merkle[i] = malloc(32);
|
||||||
|
hex2bin(merkle[i], s, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sctx->work_lock);
|
||||||
|
|
||||||
|
coinb1_size = strlen(coinb1) / 2;
|
||||||
|
coinb2_size = strlen(coinb2) / 2;
|
||||||
|
sctx->job.coinbase_size = coinb1_size + sctx->xnonce1_size +
|
||||||
|
sctx->xnonce2_size + coinb2_size;
|
||||||
|
sctx->job.coinbase = realloc(sctx->job.coinbase, sctx->job.coinbase_size);
|
||||||
|
sctx->job.xnonce2 = sctx->job.coinbase + coinb1_size + sctx->xnonce1_size;
|
||||||
|
hex2bin(sctx->job.coinbase, coinb1, coinb1_size);
|
||||||
|
memcpy(sctx->job.coinbase + coinb1_size, sctx->xnonce1, sctx->xnonce1_size);
|
||||||
|
if (!sctx->job.job_id || strcmp(sctx->job.job_id, job_id))
|
||||||
|
memset(sctx->job.xnonce2, 0, sctx->xnonce2_size);
|
||||||
|
hex2bin(sctx->job.xnonce2 + sctx->xnonce2_size, coinb2, coinb2_size);
|
||||||
|
|
||||||
|
free(sctx->job.job_id);
|
||||||
|
sctx->job.job_id = strdup(job_id);
|
||||||
|
hex2bin(sctx->job.prevhash, prevhash, 32);
|
||||||
|
|
||||||
|
for (i = 0; i < sctx->job.merkle_count; i++)
|
||||||
|
free(sctx->job.merkle[i]);
|
||||||
|
free(sctx->job.merkle);
|
||||||
|
sctx->job.merkle = merkle;
|
||||||
|
sctx->job.merkle_count = merkle_count;
|
||||||
|
|
||||||
|
hex2bin(sctx->job.version, version, 4);
|
||||||
|
hex2bin(sctx->job.nbits, nbits, 4);
|
||||||
|
hex2bin(sctx->job.ntime, ntime, 4);
|
||||||
|
sctx->job.clean = clean;
|
||||||
|
|
||||||
|
sctx->job.diff = sctx->next_diff;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&sctx->work_lock);
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stratum_set_difficulty(struct stratum_ctx *sctx, json_t *params)
|
||||||
|
{
|
||||||
|
double diff;
|
||||||
|
|
||||||
|
diff = json_number_value(json_array_get(params, 0));
|
||||||
|
if (diff == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sctx->work_lock);
|
||||||
|
sctx->next_diff = diff;
|
||||||
|
pthread_mutex_unlock(&sctx->work_lock);
|
||||||
|
|
||||||
|
if (opt_debug)
|
||||||
|
applog(LOG_DEBUG, "Stratum difficulty set to %g", diff);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stratum_reconnect(struct stratum_ctx *sctx, json_t *params)
|
||||||
|
{
|
||||||
|
json_t *port_val;
|
||||||
|
const char *host;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
host = json_string_value(json_array_get(params, 0));
|
||||||
|
port_val = json_array_get(params, 1);
|
||||||
|
if (json_is_string(port_val))
|
||||||
|
port = atoi(json_string_value(port_val));
|
||||||
|
else
|
||||||
|
port = json_integer_value(port_val);
|
||||||
|
if (!host || !port)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
free(sctx->url);
|
||||||
|
sctx->url = malloc(32 + strlen(host));
|
||||||
|
sprintf(sctx->url, "stratum+tcp://%s:%d", host, port);
|
||||||
|
|
||||||
|
applog(LOG_NOTICE, "Server requested reconnection to %s", sctx->url);
|
||||||
|
|
||||||
|
stratum_disconnect(sctx);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stratum_get_version(struct stratum_ctx *sctx, json_t *id)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
json_t *val;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
if (!id || json_is_null(id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
val = json_object();
|
||||||
|
json_object_set(val, "id", id);
|
||||||
|
json_object_set_new(val, "error", json_null());
|
||||||
|
json_object_set_new(val, "result", json_string(USER_AGENT));
|
||||||
|
s = json_dumps(val, 0);
|
||||||
|
ret = stratum_send_line(sctx, s);
|
||||||
|
json_decref(val);
|
||||||
|
free(s);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stratum_show_message(struct stratum_ctx *sctx, json_t *id, json_t *params)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
json_t *val;
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
val = json_array_get(params, 0);
|
||||||
|
if (val)
|
||||||
|
applog(LOG_NOTICE, "MESSAGE FROM SERVER: %s", json_string_value(val));
|
||||||
|
|
||||||
|
if (!id || json_is_null(id))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
val = json_object();
|
||||||
|
json_object_set(val, "id", id);
|
||||||
|
json_object_set_new(val, "error", json_null());
|
||||||
|
json_object_set_new(val, "result", json_true());
|
||||||
|
s = json_dumps(val, 0);
|
||||||
|
ret = stratum_send_line(sctx, s);
|
||||||
|
json_decref(val);
|
||||||
|
free(s);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool stratum_handle_method(struct stratum_ctx *sctx, const char *s)
|
||||||
|
{
|
||||||
|
json_t *val, *id, *params;
|
||||||
|
json_error_t err;
|
||||||
|
const char *method;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
val = JSON_LOADS(s, &err);
|
||||||
|
if (!val) {
|
||||||
|
applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
method = json_string_value(json_object_get(val, "method"));
|
||||||
|
if (!method)
|
||||||
|
goto out;
|
||||||
|
id = json_object_get(val, "id");
|
||||||
|
params = json_object_get(val, "params");
|
||||||
|
|
||||||
|
if (!strcasecmp(method, "mining.notify")) {
|
||||||
|
ret = stratum_notify(sctx, params);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!strcasecmp(method, "mining.set_difficulty")) {
|
||||||
|
ret = stratum_set_difficulty(sctx, params);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!strcasecmp(method, "client.reconnect")) {
|
||||||
|
ret = stratum_reconnect(sctx, params);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!strcasecmp(method, "client.get_version")) {
|
||||||
|
ret = stratum_get_version(sctx, id);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!strcasecmp(method, "client.show_message")) {
|
||||||
|
ret = stratum_show_message(sctx, id, params);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (val)
|
||||||
|
json_decref(val);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
struct thread_q *tq_new(void)
|
struct thread_q *tq_new(void)
|
||||||
{
|
{
|
||||||
struct thread_q *tq;
|
struct thread_q *tq;
|
||||||
|
|
Loading…
Reference in a new issue