Minimal getblocktemplate support
This commit is contained in:
parent
f167b2b07b
commit
30fae0c342
4 changed files with 554 additions and 44 deletions
335
cpu-miner.c
335
cpu-miner.c
|
@ -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;
|
||||||
|
|
7
miner.h
7
miner.h
|
@ -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);
|
||||||
|
|
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
|
.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
227
util.c
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue