Minimal getblocktemplate support
This commit is contained in:
parent
f167b2b07b
commit
30fae0c342
4 changed files with 554 additions and 44 deletions
339
cpu-miner.c
339
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;
|
||||
|
|
7
miner.h
7
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);
|
||||
|
|
25
minerd.1
25
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.
|
||||
|
|
227
util.c
227
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 <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <jansson.h>
|
||||
#include <curl/curl.h>
|
||||
|
@ -26,7 +29,6 @@
|
|||
#include <winsock2.h>
|
||||
#include <mstcpip.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue