diff --git a/cpu-miner.c b/cpu-miner.c index 62fad3c..35e8d3f 100644 --- a/cpu-miner.c +++ b/cpu-miner.c @@ -116,6 +116,8 @@ static bool opt_benchmark = false; bool opt_redirect = true; bool want_longpoll = true; bool have_longpoll = false; +bool have_gbt = true; +bool allow_getwork = true; bool want_stratum = true; bool have_stratum = false; static bool submit_old = false; @@ -134,6 +136,8 @@ static int num_processors; static char *rpc_url; static char *rpc_userpass; static char *rpc_user, *rpc_pass; +static int pk_script_size; +static unsigned char pk_script[25]; char *opt_cert; char *opt_proxy; long opt_proxy_type; @@ -181,7 +185,10 @@ Options:\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\ 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-getwork disable getwork support\n\ + --no-gbt disable getblocktemplate support\n\ --no-stratum disable X-Stratum support\n\ --no-redirect ignore requests to change the URL of the mining server\n\ -q, --quiet disable per-thread hashmeter output\n\ @@ -218,9 +225,12 @@ static struct option const options[] = { #endif { "benchmark", 0, NULL, 1005 }, { "cert", 1, NULL, 1001 }, + { "coinbase-addr", 1, NULL, 1013 }, { "config", 1, NULL, 'c' }, { "debug", 0, NULL, 'D' }, { "help", 0, NULL, 'h' }, + { "no-gbt", 0, NULL, 1011 }, + { "no-getwork", 0, NULL, 1010 }, { "no-longpoll", 0, NULL, 1003 }, { "no-redirect", 0, NULL, 1009 }, { "no-stratum", 0, NULL, 1007 }, @@ -247,6 +257,10 @@ struct work { uint32_t data[32]; uint32_t target[8]; + int height; + char *txs; + char *workid; + char *job_id; size_t xnonce2_len; unsigned char *xnonce2; @@ -258,6 +272,8 @@ static pthread_mutex_t g_work_lock; static inline void work_free(struct work *w) { + free(w->txs); + free(w->workid); free(w->job_id); 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) { 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) dest->job_id = strdup(src->job_id); if (src->xnonce2) { @@ -298,13 +318,13 @@ static bool jobj_binary(const json_t *obj, const char *key, static bool work_decode(const json_t *val, struct work *work) { int i; - + 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; } 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; } @@ -319,6 +339,204 @@ err_out: 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) { 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) { - char *str = NULL; json_t *val, *res, *reason; + char data_str[2 * sizeof(work->data) + 1]; char s[345]; int i; bool rc = false; @@ -361,38 +579,81 @@ static bool submit_upstream_work(CURL *curl, struct work *work) if (have_stratum) { uint32_t ntime, nonce; - char *ntimestr, *noncestr, *xnonce2str; + char ntimestr[9], noncestr[9], *xnonce2str; 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); + bin2hex(ntimestr, (const unsigned char *)(&ntime), 4); + bin2hex(noncestr, (const unsigned char *)(&nonce), 4); + xnonce2str = abin2hex(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 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 { /* 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; - } + bin2hex(data_str, (unsigned char *)work->data, sizeof(work->data)); /* build JSON-RPC request */ sprintf(s, "{\"method\": \"getwork\", \"params\": [ \"%s\" ], \"id\":1}\r\n", - str); + data_str); /* issue JSON-RPC request */ 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; out: - free(str); return rc; } -static const char *rpc_req = +static const char *getwork_req = "{\"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) { json_t *val; + int err; bool rc; struct timeval tv_start, tv_end, diff; +start: 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); if (have_stratum) { @@ -434,10 +702,26 @@ static bool get_upstream_work(CURL *curl, struct work *work) 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) return false; - rc = work_decode(json_object_get(val, "result"), work); + if (have_gbt) + rc = gbt_work_decode(json_object_get(val, "result"), work); + else + rc = work_decode(json_object_get(val, "result"), work); if (opt_debug && rc) { timeval_subtract(&diff, &tv_end, &tv_start); @@ -670,7 +954,7 @@ static void stratum_gen_work(struct stratum_ctx *sctx, struct work *work) pthread_mutex_unlock(&sctx->work_lock); 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", work->job_id, xnonce2str, swab32(work->data[17])); free(xnonce2str); @@ -876,7 +1160,7 @@ start: json_t *val, *soval; 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); if (have_stratum) { if (val) @@ -1239,6 +1523,17 @@ static void parse_arg (int key, char *arg) case 1009: opt_redirect = false; 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': use_syslog = true; break; diff --git a/miner.h b/miner.h index 1505869..499d65f 100644 --- a/miner.h +++ b/miner.h @@ -173,6 +173,8 @@ extern bool opt_redirect; extern int opt_timeout; extern bool want_longpoll; extern bool have_longpoll; +extern bool have_gbt; +extern bool allow_getwork; extern bool want_stratum; extern bool have_stratum; extern char *opt_cert; @@ -191,8 +193,11 @@ extern struct work_restart *work_restart; extern void applog(int prio, const char *fmt, ...); extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, 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 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, struct timeval *y); extern bool fulltest(const uint32_t *hash, const uint32_t *target); diff --git a/minerd.1 b/minerd.1 index f5561a0..2be4d38 100644 --- a/minerd.1 +++ b/minerd.1 @@ -1,4 +1,4 @@ -.TH MINERD 1 "February 2014" "cpuminer 2.3.3" +.TH MINERD 1 "April 2014" "cpuminer 2.3.3" .SH NAME minerd \- CPU miner for Bitcoin and Litecoin .SH SYNOPSIS @@ -7,7 +7,8 @@ minerd \- CPU miner for Bitcoin and Litecoin .SH DESCRIPTION .B minerd 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 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. @@ -46,7 +47,8 @@ authenticating with username "rpcuser" and password "rpcpass": .PP .nf .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 .fi .PP @@ -84,6 +86,11 @@ Run in the background as a daemon. Set an SSL certificate to use with the mining server. Only supported when using the HTTPS protocol. .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 Load options from a configuration file. \fIFILE\fR must contain a JSON object @@ -106,6 +113,12 @@ Enable debug output. \fB\-h\fR, \fB\-\-help\fR Print a help message and exit. .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 Do not use long polling. .TP @@ -121,6 +134,12 @@ Supported schemes are \fBhttp\fR, \fBhttps\fR and \fBstratum+tcp\fR. If no scheme is specified, http is assumed. Specifying a \fIPATH\fR is only supported for HTTP and HTTPS. 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 \fB\-O\fR, \fB\-\-userpass\fR=\fIUSERNAME\fR:\fIPASSWORD\fR Set the credentials to use for connecting to the mining server. diff --git a/util.c b/util.c index f2afed8..d69f146 100644 --- a/util.c +++ b/util.c @@ -1,5 +1,6 @@ /* * Copyright 2010 Jeff Garzik + * Copyright 2012 Luke Dashjr * Copyright 2012-2014 pooler * * This program is free software; you can redistribute it and/or modify it @@ -18,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -26,7 +29,6 @@ #include #include #else -#include #include #include #include @@ -119,6 +121,57 @@ void applog(int prio, const char *fmt, ...) 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) { if (!db) @@ -303,6 +356,7 @@ json_t *json_rpc_call(CURL *curl, const char *url, long http_rc; struct data_buffer all_data = {0}; struct upload_buffer upload_data; + char *json_buf; json_error_t err; struct curl_slist *headers = NULL; 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) && !((flags & JSON_RPC_QUIET_404) && http_rc == 404)) 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; } @@ -387,7 +443,8 @@ json_t *json_rpc_call(CURL *curl, const char *url, } /* 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; tq_push(thr_info[longpoll_thr_id].q, hi.lp_path); hi.lp_path = NULL; @@ -398,7 +455,10 @@ json_t *json_rpc_call(CURL *curl, const char *url, 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) { applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text); goto err_out; @@ -410,13 +470,11 @@ json_t *json_rpc_call(CURL *curl, const char *url, free(s); } - /* JSON-RPC valid response returns a non-null 'result', - * and a null 'error'. */ + /* JSON-RPC valid response returns a 'result' and a null 'error'. */ 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 (!res_val || (err_val && !json_is_null(err_val))) { char *s; if (err_val) @@ -449,16 +507,19 @@ err_out: return NULL; } -char *bin2hex(const unsigned char *p, size_t len) +void bin2hex(char *s, const unsigned char *p, size_t len) { 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); if (!s) return NULL; - - for (i = 0; i < len; i++) - sprintf(s + (i * 2), "%02x", (unsigned int) p[i]); - + bin2hex(s, p, len); return s; } @@ -489,6 +550,139 @@ bool hex2bin(unsigned char *p, const char *hexstr, size_t len) 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, storing the result in RESULT. 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) { 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++) { be32enc(hash_be + i, hash[7 - i]); be32enc(target_be + i, target[7 - i]); } - hash_str = bin2hex((unsigned char *)hash_be, 32); - target_str = bin2hex((unsigned char *)target_be, 32); + bin2hex(hash_str, (unsigned char *)hash_be, 32); + bin2hex(target_str, (unsigned char *)target_be, 32); applog(LOG_DEBUG, "DEBUG: %s\nHash: %s\nTarget: %s", rc ? "hash <= target" : "hash > target (false positive)", hash_str, target_str); - - free(hash_str); - free(target_str); } return rc;