Minimal getblocktemplate support

This commit is contained in:
pooler 2014-04-30 23:03:58 +02:00
parent f167b2b07b
commit 30fae0c342
4 changed files with 554 additions and 44 deletions

View file

@ -116,6 +116,8 @@ static bool opt_benchmark = false;
bool opt_redirect = true; bool opt_redirect = true;
bool want_longpoll = true; bool want_longpoll = true;
bool have_longpoll = false; bool have_longpoll = false;
bool have_gbt = true;
bool allow_getwork = true;
bool want_stratum = true; bool want_stratum = true;
bool have_stratum = false; bool have_stratum = false;
static bool submit_old = false; static bool submit_old = false;
@ -134,6 +136,8 @@ static int num_processors;
static char *rpc_url; static char *rpc_url;
static char *rpc_userpass; static char *rpc_userpass;
static char *rpc_user, *rpc_pass; static char *rpc_user, *rpc_pass;
static int pk_script_size;
static unsigned char pk_script[25];
char *opt_cert; char *opt_cert;
char *opt_proxy; char *opt_proxy;
long opt_proxy_type; long opt_proxy_type;
@ -181,7 +185,10 @@ Options:\n\
-T, --timeout=N timeout for long polling, in seconds (default: none)\n\ -T, --timeout=N timeout for long polling, in seconds (default: none)\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\
--coinbase-addr=ADDR payout address for solo mining\n\
--no-longpoll disable X-Long-Polling support\n\ --no-longpoll disable X-Long-Polling support\n\
--no-getwork disable getwork support\n\
--no-gbt disable getblocktemplate support\n\
--no-stratum disable X-Stratum support\n\ --no-stratum disable X-Stratum support\n\
--no-redirect ignore requests to change the URL of the mining server\n\ --no-redirect ignore requests to change the URL of the mining server\n\
-q, --quiet disable per-thread hashmeter output\n\ -q, --quiet disable per-thread hashmeter output\n\
@ -218,9 +225,12 @@ static struct option const options[] = {
#endif #endif
{ "benchmark", 0, NULL, 1005 }, { "benchmark", 0, NULL, 1005 },
{ "cert", 1, NULL, 1001 }, { "cert", 1, NULL, 1001 },
{ "coinbase-addr", 1, NULL, 1013 },
{ "config", 1, NULL, 'c' }, { "config", 1, NULL, 'c' },
{ "debug", 0, NULL, 'D' }, { "debug", 0, NULL, 'D' },
{ "help", 0, NULL, 'h' }, { "help", 0, NULL, 'h' },
{ "no-gbt", 0, NULL, 1011 },
{ "no-getwork", 0, NULL, 1010 },
{ "no-longpoll", 0, NULL, 1003 }, { "no-longpoll", 0, NULL, 1003 },
{ "no-redirect", 0, NULL, 1009 }, { "no-redirect", 0, NULL, 1009 },
{ "no-stratum", 0, NULL, 1007 }, { "no-stratum", 0, NULL, 1007 },
@ -247,6 +257,10 @@ struct work {
uint32_t data[32]; uint32_t data[32];
uint32_t target[8]; uint32_t target[8];
int height;
char *txs;
char *workid;
char *job_id; char *job_id;
size_t xnonce2_len; size_t xnonce2_len;
unsigned char *xnonce2; unsigned char *xnonce2;
@ -258,6 +272,8 @@ static pthread_mutex_t g_work_lock;
static inline void work_free(struct work *w) static inline void work_free(struct work *w)
{ {
free(w->txs);
free(w->workid);
free(w->job_id); free(w->job_id);
free(w->xnonce2); free(w->xnonce2);
} }
@ -265,6 +281,10 @@ static inline void work_free(struct work *w)
static inline void work_copy(struct work *dest, const struct work *src) static inline void work_copy(struct work *dest, const struct work *src)
{ {
memcpy(dest, src, sizeof(struct work)); memcpy(dest, src, sizeof(struct work));
if (src->txs)
dest->txs = strdup(src->txs);
if (src->workid)
dest->workid = strdup(src->workid);
if (src->job_id) if (src->job_id)
dest->job_id = strdup(src->job_id); dest->job_id = strdup(src->job_id);
if (src->xnonce2) { if (src->xnonce2) {
@ -300,11 +320,11 @@ static bool work_decode(const json_t *val, struct work *work)
int i; int i;
if (unlikely(!jobj_binary(val, "data", work->data, sizeof(work->data)))) { if (unlikely(!jobj_binary(val, "data", work->data, sizeof(work->data)))) {
applog(LOG_ERR, "JSON inval data"); applog(LOG_ERR, "JSON invalid data");
goto err_out; goto err_out;
} }
if (unlikely(!jobj_binary(val, "target", work->target, sizeof(work->target)))) { if (unlikely(!jobj_binary(val, "target", work->target, sizeof(work->target)))) {
applog(LOG_ERR, "JSON inval target"); applog(LOG_ERR, "JSON invalid target");
goto err_out; goto err_out;
} }
@ -319,6 +339,204 @@ err_out:
return false; return false;
} }
static bool gbt_work_decode(const json_t *val, struct work *work)
{
int i, n;
uint32_t version, curtime, bits;
uint32_t prevhash[8];
uint32_t target[8];
int cbtx_size;
unsigned char *cbtx = NULL;
int tx_count, tx_size;
unsigned char txc_vi[9];
unsigned char (*merkle_tree)[32] = NULL;
json_t *tmp, *txa;
bool rc = false;
tmp = json_object_get(val, "height");
if (!tmp || !json_is_integer(tmp)) {
applog(LOG_ERR, "JSON invalid height");
goto out;
}
work->height = json_integer_value(tmp);
tmp = json_object_get(val, "version");
if (!tmp || !json_is_integer(tmp)) {
applog(LOG_ERR, "JSON invalid version");
goto out;
}
version = json_integer_value(tmp);
if (unlikely(!jobj_binary(val, "previousblockhash", prevhash, sizeof(prevhash)))) {
applog(LOG_ERR, "JSON invalid previousblockhash");
goto out;
}
tmp = json_object_get(val, "curtime");
if (!tmp || !json_is_integer(tmp)) {
applog(LOG_ERR, "JSON invalid curtime");
goto out;
}
curtime = json_integer_value(tmp);
if (unlikely(!jobj_binary(val, "bits", &bits, sizeof(bits)))) {
applog(LOG_ERR, "JSON invalid bits");
goto out;
}
/* find count and size of transactions */
txa = json_object_get(val, "transactions");
if (!txa || !json_is_array(txa)) {
applog(LOG_ERR, "JSON invalid transactions");
goto out;
}
tx_count = json_array_size(txa);
tx_size = 0;
for (i = 0; i < tx_count; i++) {
const json_t *tx = json_array_get(txa, i);
const char *tx_hex = json_string_value(json_object_get(tx, "data"));
if (!tx_hex) {
applog(LOG_ERR, "JSON invalid transactions");
goto out;
}
tx_size += strlen(tx_hex) / 2;
}
/* build coinbase transaction */
tmp = json_object_get(val, "coinbasetxn");
if (tmp) {
const char *cbtx_hex = json_string_value(json_object_get(tmp, "data"));
cbtx_size = cbtx_hex ? strlen(cbtx_hex) / 2 : 0;
cbtx = malloc(cbtx_size);
if (!cbtx_hex || !hex2bin(cbtx, cbtx_hex, cbtx_size)) {
applog(LOG_ERR, "JSON invalid coinbasetxn");
goto out;
}
} else {
if (!pk_script_size) {
applog(LOG_ERR, "No payout address provided");
goto out;
}
tmp = json_object_get(val, "coinbasevalue");
if (!tmp || !json_is_number(tmp)) {
applog(LOG_ERR, "JSON invalid coinbasevalue");
goto out;
}
int64_t cbvalue = json_is_integer(tmp) ? json_integer_value(tmp) : json_number_value(tmp);
cbtx = malloc(256);
le32enc((uint32_t *)cbtx, 1); /* version */
cbtx[4] = 1; /* in-counter */
memset(cbtx+5, 0x00, 32); /* prev txout hash */
le32enc((uint32_t *)(cbtx+37), 0xffffffff); /* prev txout index */
/* BIP 34: height in coinbase */
int h = work->height;
for (i = 0; h; i++) {
cbtx[43 + i] = h & 0xff;
h >>= 8;
}
cbtx[42] = i;
cbtx_size = 43 + i;
tmp = json_object_get(val, "coinbaseaux");
if (tmp && json_is_object(tmp)) {
void *iter = json_object_iter(tmp);
while (iter) {
unsigned char buf[100];
const char *s = json_string_value(json_object_iter_value(iter));
n = s ? strlen(s) / 2 : 0;
if (!s || n > 100 || !hex2bin(buf, s, n)) {
applog(LOG_ERR, "JSON invalid coinbaseaux");
break;
}
if (cbtx_size - 42 + (n > 75 ? 2 : 1) + n <= 100) {
if (n > 75)
cbtx[cbtx_size++] = 0x4c; /* OP_PUSHDATA1 */
cbtx[cbtx_size++] = n;
memcpy(cbtx+cbtx_size, buf, n);
cbtx_size += n;
}
iter = json_object_iter_next(tmp, iter);
}
}
cbtx[41] = cbtx_size - 42; /* txin-script length */
le32enc((uint32_t *)(cbtx+cbtx_size), 0xffffffff); /* sequence */
cbtx_size += 4;
cbtx[cbtx_size++] = 1; /* out-counter */
le32enc((uint32_t *)(cbtx+cbtx_size), (uint32_t)cbvalue); /* value */
le32enc((uint32_t *)(cbtx+cbtx_size+4), cbvalue >> 32);
cbtx_size += 8;
cbtx[cbtx_size++] = pk_script_size; /* txout-script length */
memcpy(cbtx+cbtx_size, pk_script, pk_script_size);
cbtx_size += pk_script_size;
le32enc((uint32_t *)(cbtx+cbtx_size), 0); /* lock time */
cbtx_size += 4;
}
n = varint_encode(txc_vi, 1 + tx_count);
work->txs = malloc(2 * (n + cbtx_size + tx_size) + 1);
bin2hex(work->txs, txc_vi, n);
bin2hex(work->txs + 2*n, cbtx, cbtx_size);
/* generate merkle root */
merkle_tree = malloc(32 * ((1 + tx_count + 1) & ~1));
sha256d(merkle_tree[0], cbtx, cbtx_size);
for (i = 0; i < tx_count; i++) {
tmp = json_array_get(txa, i);
const char *tx_hex = json_string_value(json_object_get(tmp, "data"));
const int tx_size = tx_hex ? strlen(tx_hex) / 2 : 0;
unsigned char *tx = malloc(tx_size);
if (!tx_hex || !hex2bin(tx, tx_hex, tx_size)) {
applog(LOG_ERR, "JSON invalid transactions");
free(tx);
goto out;
}
strcat(work->txs, tx_hex);
sha256d(merkle_tree[1 + i], tx, tx_size);
}
n = 1 + tx_count;
while (n > 1) {
if (n % 2) {
memcpy(merkle_tree[n], merkle_tree[n-1], 32);
++n;
}
n /= 2;
for (i = 0; i < n; i++)
sha256d(merkle_tree[i], merkle_tree[2*i], 64);
}
/* assemble block header */
work->data[0] = swab32(version);
for (i = 0; i < 8; i++)
work->data[8 - i] = le32dec(prevhash + i);
for (i = 0; i < 8; i++)
work->data[9 + i] = be32dec((uint32_t *)merkle_tree[0] + i);
work->data[17] = swab32(curtime);
work->data[18] = le32dec(&bits);
memset(work->data + 19, 0x00, 52);
work->data[20] = 0x80000000;
work->data[31] = 0x00000280;
if (unlikely(!jobj_binary(val, "target", target, sizeof(target)))) {
applog(LOG_ERR, "JSON invalid target");
goto out;
}
for (i = 0; i < ARRAY_SIZE(work->target); i++)
work->target[7 - i] = be32dec(target + i);
tmp = json_object_get(val, "workid");
if (tmp) {
if (!json_is_string(tmp)) {
applog(LOG_ERR, "JSON invalid workid");
goto out;
}
work->workid = strdup(json_string_value(tmp));
}
rc = true;
out:
free(cbtx);
free(merkle_tree);
return rc;
}
static void share_result(int result, const char *reason) static void share_result(int result, const char *reason)
{ {
char s[345]; char s[345];
@ -346,8 +564,8 @@ static void share_result(int result, const char *reason)
static bool submit_upstream_work(CURL *curl, struct work *work) static bool submit_upstream_work(CURL *curl, struct work *work)
{ {
char *str = NULL;
json_t *val, *res, *reason; json_t *val, *res, *reason;
char data_str[2 * sizeof(work->data) + 1];
char s[345]; char s[345];
int i; int i;
bool rc = false; bool rc = false;
@ -361,38 +579,81 @@ static bool submit_upstream_work(CURL *curl, struct work *work)
if (have_stratum) { if (have_stratum) {
uint32_t ntime, nonce; uint32_t ntime, nonce;
char *ntimestr, *noncestr, *xnonce2str; char ntimestr[9], noncestr[9], *xnonce2str;
le32enc(&ntime, work->data[17]); le32enc(&ntime, work->data[17]);
le32enc(&nonce, work->data[19]); le32enc(&nonce, work->data[19]);
ntimestr = bin2hex((const unsigned char *)(&ntime), 4); bin2hex(ntimestr, (const unsigned char *)(&ntime), 4);
noncestr = bin2hex((const unsigned char *)(&nonce), 4); bin2hex(noncestr, (const unsigned char *)(&nonce), 4);
xnonce2str = bin2hex(work->xnonce2, work->xnonce2_len); xnonce2str = abin2hex(work->xnonce2, work->xnonce2_len);
sprintf(s, sprintf(s,
"{\"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\":4}", "{\"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\":4}",
rpc_user, work->job_id, xnonce2str, ntimestr, noncestr); rpc_user, work->job_id, xnonce2str, ntimestr, noncestr);
free(ntimestr);
free(noncestr);
free(xnonce2str); free(xnonce2str);
if (unlikely(!stratum_send_line(&stratum, s))) { if (unlikely(!stratum_send_line(&stratum, s))) {
applog(LOG_ERR, "submit_upstream_work stratum_send_line failed"); applog(LOG_ERR, "submit_upstream_work stratum_send_line failed");
goto out; goto out;
} }
} else if (work->txs) {
char *req;
for (i = 0; i < ARRAY_SIZE(work->data); i++)
be32enc(work->data + i, work->data[i]);
bin2hex(data_str, (unsigned char *)work->data, 80);
if (work->workid) {
char *params;
val = json_object();
json_object_set_new(val, "workid", json_string(work->workid));
params = json_dumps(val, 0);
json_decref(val);
req = malloc(128 + 2*80 + strlen(work->txs) + strlen(params));
sprintf(req,
"{\"method\": \"submitblock\", \"params\": [\"%s%s\", %s], \"id\":1}\r\n",
data_str, work->txs, params);
free(params);
} else {
req = malloc(128 + 2*80 + strlen(work->txs));
sprintf(req,
"{\"method\": \"submitblock\", \"params\": [\"%s%s\"], \"id\":1}\r\n",
data_str, work->txs);
}
val = json_rpc_call(curl, rpc_url, rpc_userpass, req, NULL, 0);
free(req);
if (unlikely(!val)) {
applog(LOG_ERR, "submit_upstream_work json_rpc_call failed");
goto out;
}
res = json_object_get(val, "result");
if (json_is_object(res)) {
char *res_str;
bool sumres = false;
void *iter = json_object_iter(res);
while (iter) {
if (json_is_null(json_object_iter_value(iter))) {
sumres = true;
break;
}
iter = json_object_iter_next(res, iter);
}
res_str = json_dumps(res, 0);
share_result(sumres, res_str);
free(res_str);
} else
share_result(json_is_null(res), json_string_value(res));
json_decref(val);
} else { } else {
/* build hex string */ /* build hex string */
for (i = 0; i < ARRAY_SIZE(work->data); i++) for (i = 0; i < ARRAY_SIZE(work->data); i++)
le32enc(work->data + i, work->data[i]); le32enc(work->data + i, work->data[i]);
str = bin2hex((unsigned char *)work->data, sizeof(work->data)); bin2hex(data_str, (unsigned char *)work->data, sizeof(work->data));
if (unlikely(!str)) {
applog(LOG_ERR, "submit_upstream_work OOM");
goto out;
}
/* build JSON-RPC request */ /* build JSON-RPC request */
sprintf(s, sprintf(s,
"{\"method\": \"getwork\", \"params\": [ \"%s\" ], \"id\":1}\r\n", "{\"method\": \"getwork\", \"params\": [ \"%s\" ], \"id\":1}\r\n",
str); data_str);
/* issue JSON-RPC request */ /* issue JSON-RPC request */
val = json_rpc_call(curl, rpc_url, rpc_userpass, s, NULL, 0); val = json_rpc_call(curl, rpc_url, rpc_userpass, s, NULL, 0);
@ -411,21 +672,28 @@ static bool submit_upstream_work(CURL *curl, struct work *work)
rc = true; rc = true;
out: out:
free(str);
return rc; return rc;
} }
static const char *rpc_req = static const char *getwork_req =
"{\"method\": \"getwork\", \"params\": [], \"id\":0}\r\n"; "{\"method\": \"getwork\", \"params\": [], \"id\":0}\r\n";
static const char *gbt_req =
"{\"method\": \"getblocktemplate\", \"params\": [{\"capabilities\":"
" [\"coinbasetxn\", \"coinbasevalue\", \"workid\"]}], \"id\":0}\r\n";
static bool get_upstream_work(CURL *curl, struct work *work) static bool get_upstream_work(CURL *curl, struct work *work)
{ {
json_t *val; json_t *val;
int err;
bool rc; bool rc;
struct timeval tv_start, tv_end, diff; struct timeval tv_start, tv_end, diff;
start:
gettimeofday(&tv_start, NULL); gettimeofday(&tv_start, NULL);
val = json_rpc_call(curl, rpc_url, rpc_userpass, rpc_req, NULL, 0); val = json_rpc_call(curl, rpc_url, rpc_userpass,
have_gbt ? gbt_req : getwork_req,
&err, have_gbt ? JSON_RPC_QUIET_404 : 0);
gettimeofday(&tv_end, NULL); gettimeofday(&tv_end, NULL);
if (have_stratum) { if (have_stratum) {
@ -434,9 +702,25 @@ static bool get_upstream_work(CURL *curl, struct work *work)
return true; return true;
} }
if (!have_gbt && !allow_getwork) {
applog(LOG_ERR, "No usable protocol");
if (val)
json_decref(val);
return false;
}
if (have_gbt && allow_getwork && !val && err == CURLE_OK) {
applog(LOG_INFO, "getblocktemplate failed, falling back to getwork");
have_gbt = false;
goto start;
}
if (!val) if (!val)
return false; return false;
if (have_gbt)
rc = gbt_work_decode(json_object_get(val, "result"), work);
else
rc = work_decode(json_object_get(val, "result"), work); rc = work_decode(json_object_get(val, "result"), work);
if (opt_debug && rc) { if (opt_debug && rc) {
@ -670,7 +954,7 @@ static void stratum_gen_work(struct stratum_ctx *sctx, struct work *work)
pthread_mutex_unlock(&sctx->work_lock); pthread_mutex_unlock(&sctx->work_lock);
if (opt_debug) { if (opt_debug) {
char *xnonce2str = bin2hex(work->xnonce2, work->xnonce2_len); char *xnonce2str = abin2hex(work->xnonce2, work->xnonce2_len);
applog(LOG_DEBUG, "DEBUG: job_id='%s' extranonce2=%s ntime=%08x", applog(LOG_DEBUG, "DEBUG: job_id='%s' extranonce2=%s ntime=%08x",
work->job_id, xnonce2str, swab32(work->data[17])); work->job_id, xnonce2str, swab32(work->data[17]));
free(xnonce2str); free(xnonce2str);
@ -876,7 +1160,7 @@ start:
json_t *val, *soval; json_t *val, *soval;
int err; int err;
val = json_rpc_call(curl, lp_url, rpc_userpass, rpc_req, &err, val = json_rpc_call(curl, lp_url, rpc_userpass, getwork_req, &err,
JSON_RPC_LONGPOLL); JSON_RPC_LONGPOLL);
if (have_stratum) { if (have_stratum) {
if (val) if (val)
@ -1239,6 +1523,17 @@ static void parse_arg (int key, char *arg)
case 1009: case 1009:
opt_redirect = false; opt_redirect = false;
break; break;
case 1010:
allow_getwork = false;
break;
case 1011:
have_gbt = false;
break;
case 1013: /* --coinbase-addr */
pk_script_size = address_to_script(pk_script, sizeof(pk_script), arg);
if (!pk_script_size)
show_usage_and_exit(1);
break;
case 'S': case 'S':
use_syslog = true; use_syslog = true;
break; break;

View file

@ -173,6 +173,8 @@ extern bool opt_redirect;
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 have_gbt;
extern bool allow_getwork;
extern bool want_stratum; extern bool want_stratum;
extern bool have_stratum; extern bool have_stratum;
extern char *opt_cert; extern char *opt_cert;
@ -191,8 +193,11 @@ extern struct work_restart *work_restart;
extern void applog(int prio, const char *fmt, ...); extern void applog(int prio, const char *fmt, ...);
extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass,
const char *rpc_req, int *curl_err, int flags); const char *rpc_req, int *curl_err, int flags);
extern char *bin2hex(const unsigned char *p, size_t len); extern void bin2hex(char *s, const unsigned char *p, size_t len);
extern char *abin2hex(const unsigned char *p, size_t len);
extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len); extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len);
extern int varint_encode(unsigned char *p, uint64_t n);
extern size_t address_to_script(unsigned char *out, size_t outsz, const char *addr);
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);

View file

@ -1,4 +1,4 @@
.TH MINERD 1 "February 2014" "cpuminer 2.3.3" .TH MINERD 1 "April 2014" "cpuminer 2.3.3"
.SH NAME .SH NAME
minerd \- CPU miner for Bitcoin and Litecoin minerd \- CPU miner for Bitcoin and Litecoin
.SH SYNOPSIS .SH SYNOPSIS
@ -7,7 +7,8 @@ minerd \- CPU miner for Bitcoin and Litecoin
.SH DESCRIPTION .SH DESCRIPTION
.B minerd .B minerd
is a multi-threaded CPU miner for Bitcoin, Litecoin and other cryptocurrencies. is a multi-threaded CPU miner for Bitcoin, Litecoin and other cryptocurrencies.
It supports the getwork mining protocol as well as the Stratum mining protocol. It supports the getwork and getblocktemplate (BIP 22) methods,
as well as the Stratum mining protocol.
.PP .PP
In its normal mode of operation, \fBminerd\fR connects to a mining server In its normal mode of operation, \fBminerd\fR connects to a mining server
(specified with the \fB\-o\fR option), receives work from it and starts hashing. (specified with the \fB\-o\fR option), receives work from it and starts hashing.
@ -46,7 +47,8 @@ authenticating with username "rpcuser" and password "rpcpass":
.PP .PP
.nf .nf
.RS .RS
minerd \-a sha256d \-o http://localhost:18332 \-O rpcuser:rpcpass minerd \-a sha256d \-o http://localhost:18332 \-O rpcuser:rpcpass \\
\-\-coinbase\-addr=mpXwg4jMtRhuSpVq4xS3HFHmCmWp9NyGKt
.RE .RE
.fi .fi
.PP .PP
@ -84,6 +86,11 @@ Run in the background as a daemon.
Set an SSL certificate to use with the mining server. Set an SSL certificate to use with the mining server.
Only supported when using the HTTPS protocol. Only supported when using the HTTPS protocol.
.TP .TP
\fB\-\-coinbase\-addr\fR=\fIADDRESS\fR
Set a payout address for solo mining.
This is only used in getblocktemplate mode,
and only if the server does not provide a coinbase transaction.
.TP
\fB\-c\fR, \fB\-\-config\fR=\fIFILE\fR \fB\-c\fR, \fB\-\-config\fR=\fIFILE\fR
Load options from a configuration file. Load options from a configuration file.
\fIFILE\fR must contain a JSON object \fIFILE\fR must contain a JSON object
@ -106,6 +113,12 @@ Enable debug output.
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR
Print a help message and exit. Print a help message and exit.
.TP .TP
\fB\-\-no\-gbt\fR
Do not use the getblocktemplate RPC method.
.TP
\fB\-\-no\-getwork\fR
Do not use the getwork RPC method.
.TP
\fB\-\-no\-longpoll\fR \fB\-\-no\-longpoll\fR
Do not use long polling. Do not use long polling.
.TP .TP
@ -121,6 +134,12 @@ Supported schemes are \fBhttp\fR, \fBhttps\fR and \fBstratum+tcp\fR.
If no scheme is specified, http is assumed. If no scheme is specified, http is assumed.
Specifying a \fIPATH\fR is only supported for HTTP and HTTPS. Specifying a \fIPATH\fR is only supported for HTTP and HTTPS.
Specifying credentials has the same effect as using the \fB\-O\fR option. Specifying credentials has the same effect as using the \fB\-O\fR option.
By default, on HTTP and HTTPS,
the miner tries to use the getblocktemplate RPC method,
and falls back to using getwork if getblocktemplate is unavailable.
This behavior can be modified by using the \fB\-\-no\-gbt\fR
and \fB\-\-no\-getwork\fR options.
.TP .TP
\fB\-O\fR, \fB\-\-userpass\fR=\fIUSERNAME\fR:\fIPASSWORD\fR \fB\-O\fR, \fB\-\-userpass\fR=\fIUSERNAME\fR:\fIPASSWORD\fR
Set the credentials to use for connecting to the mining server. Set the credentials to use for connecting to the mining server.

227
util.c
View file

@ -1,5 +1,6 @@
/* /*
* Copyright 2010 Jeff Garzik * Copyright 2010 Jeff Garzik
* Copyright 2012 Luke Dashjr
* Copyright 2012-2014 pooler * Copyright 2012-2014 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
@ -18,6 +19,8 @@
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <jansson.h> #include <jansson.h>
#include <curl/curl.h> #include <curl/curl.h>
@ -26,7 +29,6 @@
#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>
@ -119,6 +121,57 @@ void applog(int prio, const char *fmt, ...)
va_end(ap); va_end(ap);
} }
/* Modify the representation of integer numbers which would cause an overflow
* so that they are treated as floating-point numbers.
* This is a hack to overcome the limitations of some versions of Jansson. */
static char *hack_json_numbers(const char *in)
{
char *out;
int i, off, intoff;
bool in_str, in_int;
out = calloc(2 * strlen(in) + 1, 1);
if (!out)
return NULL;
off = intoff = 0;
in_str = in_int = false;
for (i = 0; in[i]; i++) {
char c = in[i];
if (c == '"') {
in_str = !in_str;
} else if (c == '\\') {
out[off++] = c;
if (!in[++i])
break;
} else if (!in_str && !in_int && isdigit(c)) {
intoff = off;
in_int = true;
} else if (in_int && !isdigit(c)) {
if (c != '.' && c != 'e' && c != 'E' && c != '+' && c != '-') {
in_int = false;
if (off - intoff > 4) {
char *end;
#if JSON_INTEGER_IS_LONG_LONG
errno = 0;
strtoll(out + intoff, &end, 10);
if (!*end && errno == ERANGE) {
#else
long l;
errno = 0;
l = strtol(out + intoff, &end, 10);
if (!*end && (errno == ERANGE || l > INT_MAX)) {
#endif
out[off++] = '.';
out[off++] = '0';
}
}
}
}
out[off++] = in[i];
}
return out;
}
static void databuf_free(struct data_buffer *db) static void databuf_free(struct data_buffer *db)
{ {
if (!db) if (!db)
@ -303,6 +356,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
long http_rc; long http_rc;
struct data_buffer all_data = {0}; struct data_buffer all_data = {0};
struct upload_buffer upload_data; struct upload_buffer upload_data;
char *json_buf;
json_error_t err; json_error_t err;
struct curl_slist *headers = NULL; struct curl_slist *headers = NULL;
char len_hdr[64]; char len_hdr[64];
@ -375,6 +429,8 @@ json_t *json_rpc_call(CURL *curl, const char *url,
if (!((flags & JSON_RPC_LONGPOLL) && rc == CURLE_OPERATION_TIMEDOUT) && if (!((flags & JSON_RPC_LONGPOLL) && rc == CURLE_OPERATION_TIMEDOUT) &&
!((flags & JSON_RPC_QUIET_404) && http_rc == 404)) !((flags & JSON_RPC_QUIET_404) && http_rc == 404))
applog(LOG_ERR, "HTTP request failed: %s", curl_err_str); applog(LOG_ERR, "HTTP request failed: %s", curl_err_str);
if (curl_err && (flags & JSON_RPC_QUIET_404) && http_rc == 404)
*curl_err = CURLE_OK;
goto err_out; goto err_out;
} }
@ -387,7 +443,8 @@ json_t *json_rpc_call(CURL *curl, const char *url,
} }
/* If X-Long-Polling was found, activate long polling */ /* If X-Long-Polling was found, activate long polling */
if (!have_longpoll && want_longpoll && hi.lp_path && !have_stratum) { if (!have_longpoll && want_longpoll && hi.lp_path && !have_gbt &&
allow_getwork && !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);
hi.lp_path = NULL; hi.lp_path = NULL;
@ -398,7 +455,10 @@ json_t *json_rpc_call(CURL *curl, const char *url,
goto err_out; goto err_out;
} }
val = JSON_LOADS(all_data.buf, &err); json_buf = hack_json_numbers(all_data.buf);
errno = 0; /* needed for Jansson < 2.1 */
val = JSON_LOADS(json_buf, &err);
free(json_buf);
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;
@ -410,13 +470,11 @@ json_t *json_rpc_call(CURL *curl, const char *url,
free(s); free(s);
} }
/* JSON-RPC valid response returns a non-null 'result', /* JSON-RPC valid response returns a 'result' and a null 'error'. */
* and a null 'error'. */
res_val = json_object_get(val, "result"); res_val = json_object_get(val, "result");
err_val = json_object_get(val, "error"); err_val = json_object_get(val, "error");
if (!res_val || json_is_null(res_val) || if (!res_val || (err_val && !json_is_null(err_val))) {
(err_val && !json_is_null(err_val))) {
char *s; char *s;
if (err_val) if (err_val)
@ -449,16 +507,19 @@ err_out:
return NULL; return NULL;
} }
char *bin2hex(const unsigned char *p, size_t len) void bin2hex(char *s, const unsigned char *p, size_t len)
{ {
int i; int i;
for (i = 0; i < len; i++)
sprintf(s + (i * 2), "%02x", (unsigned int) p[i]);
}
char *abin2hex(const unsigned char *p, size_t len)
{
char *s = malloc((len * 2) + 1); char *s = malloc((len * 2) + 1);
if (!s) if (!s)
return NULL; return NULL;
bin2hex(s, p, len);
for (i = 0; i < len; i++)
sprintf(s + (i * 2), "%02x", (unsigned int) p[i]);
return s; return s;
} }
@ -489,6 +550,139 @@ bool hex2bin(unsigned char *p, const char *hexstr, size_t len)
return (len == 0 && *hexstr == 0) ? true : false; return (len == 0 && *hexstr == 0) ? true : false;
} }
int varint_encode(unsigned char *p, uint64_t n)
{
int i;
if (n < 0xfd) {
p[0] = n;
return 1;
}
if (n <= 0xffff) {
p[0] = 0xfd;
p[1] = n & 0xff;
p[2] = n >> 8;
return 3;
}
if (n <= 0xffffffff) {
p[0] = 0xfe;
for (i = 1; i < 5; i++) {
p[i] = n & 0xff;
n >>= 8;
}
return 5;
}
p[0] = 0xff;
for (i = 1; i < 9; i++) {
p[i] = n & 0xff;
n >>= 8;
}
return 9;
}
static const char b58digits[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static bool b58dec(unsigned char *bin, size_t binsz, const char *b58)
{
size_t i, j;
uint64_t t;
uint32_t c;
uint32_t *outi;
size_t outisz = (binsz + 3) / 4;
int rem = binsz % 4;
uint32_t remmask = 0xffffffff << (8 * rem);
size_t b58sz = strlen(b58);
bool rc = false;
outi = calloc(outisz, sizeof(*outi));
for (i = 0; i < b58sz; ++i) {
for (c = 0; b58digits[c] != b58[i]; c++)
if (!b58digits[c])
goto out;
for (j = outisz; j--; ) {
t = (uint64_t)outi[j] * 58 + c;
c = t >> 32;
outi[j] = t & 0xffffffff;
}
if (c || outi[0] & remmask)
goto out;
}
j = 0;
switch (rem) {
case 3:
*(bin++) = (outi[0] >> 16) & 0xff;
case 2:
*(bin++) = (outi[0] >> 8) & 0xff;
case 1:
*(bin++) = outi[0] & 0xff;
++j;
default:
break;
}
for (; j < outisz; ++j) {
be32enc((uint32_t *)bin, outi[j]);
bin += sizeof(uint32_t);
}
rc = true;
out:
free(outi);
return rc;
}
static int b58check(unsigned char *bin, size_t binsz, const char *b58)
{
unsigned char buf[32];
int i;
sha256d(buf, bin, binsz - 4);
if (memcmp(&bin[binsz - 4], buf, 4))
return -1;
/* Check number of zeros is correct AFTER verifying checksum
* (to avoid possibility of accessing the string beyond the end) */
for (i = 0; bin[i] == '\0' && b58[i] == '1'; ++i);
if (bin[i] == '\0' || b58[i] == '1')
return -3;
return bin[0];
}
size_t address_to_script(unsigned char *out, size_t outsz, const char *addr)
{
unsigned char addrbin[25];
int addrver;
size_t rv;
if (!b58dec(addrbin, sizeof(addrbin), addr))
return 0;
addrver = b58check(addrbin, sizeof(addrbin), addr);
if (addrver < 0)
return 0;
switch (addrver) {
case 5: /* Bitcoin script hash */
case 196: /* Testnet script hash */
if (outsz < (rv = 23))
return rv;
out[ 0] = 0xa9; /* OP_HASH160 */
out[ 1] = 0x14; /* push 20 bytes */
memcpy(&out[2], &addrbin[1], 20);
out[22] = 0x87; /* OP_EQUAL */
return rv;
default:
if (outsz < (rv = 25))
return rv;
out[ 0] = 0x76; /* OP_DUP */
out[ 1] = 0xa9; /* OP_HASH160 */
out[ 2] = 0x14; /* push 20 bytes */
memcpy(&out[3], &addrbin[1], 20);
out[23] = 0x88; /* OP_EQUALVERIFY */
out[24] = 0xac; /* OP_CHECKSIG */
return rv;
}
}
/* Subtract the `struct timeval' values X and Y, /* Subtract the `struct timeval' values X and Y,
storing the result in RESULT. storing the result in RESULT.
Return 1 if the difference is negative, otherwise 0. */ Return 1 if the difference is negative, otherwise 0. */
@ -534,23 +728,20 @@ bool fulltest(const uint32_t *hash, const uint32_t *target)
if (opt_debug) { if (opt_debug) {
uint32_t hash_be[8], target_be[8]; uint32_t hash_be[8], target_be[8];
char *hash_str, *target_str; char hash_str[65], target_str[65];
for (i = 0; i < 8; i++) { for (i = 0; i < 8; i++) {
be32enc(hash_be + i, hash[7 - i]); be32enc(hash_be + i, hash[7 - i]);
be32enc(target_be + i, target[7 - i]); be32enc(target_be + i, target[7 - i]);
} }
hash_str = bin2hex((unsigned char *)hash_be, 32); bin2hex(hash_str, (unsigned char *)hash_be, 32);
target_str = bin2hex((unsigned char *)target_be, 32); bin2hex(target_str, (unsigned char *)target_be, 32);
applog(LOG_DEBUG, "DEBUG: %s\nHash: %s\nTarget: %s", applog(LOG_DEBUG, "DEBUG: %s\nHash: %s\nTarget: %s",
rc ? "hash <= target" rc ? "hash <= target"
: "hash > target (false positive)", : "hash > target (false positive)",
hash_str, hash_str,
target_str); target_str);
free(hash_str);
free(target_str);
} }
return rc; return rc;