From d0e8e0e9c28c3c35a214be98c544dd05353520c5 Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Sun, 10 May 2020 14:42:05 +0300 Subject: [PATCH 1/3] Added -M / --max-diff option This option allows you to save on power and CPU burn by not mining if difficulty exceeds some threshold. It is intended for testnet miners that only want to mine on testnet when difficulty drops to 1.0. --- cpu-miner.c | 68 ++++++++++++++++++++++++++++++++++------------------- miner.h | 2 ++ util.c | 35 ++++++++++++++++++++------- 3 files changed, 73 insertions(+), 32 deletions(-) diff --git a/cpu-miner.c b/cpu-miner.c index 46db161a7..569f4ab71 100644 --- a/cpu-miner.c +++ b/cpu-miner.c @@ -86,7 +86,7 @@ static inline void affine_to_cpu(int id, int cpu) { } #endif - + enum workio_commands { WC_GET_WORK, WC_SUBMIT_WORK, @@ -146,6 +146,7 @@ int longpoll_thr_id = -1; int stratum_thr_id = -1; struct work_restart *work_restart = NULL; static struct stratum_ctx stratum; +static double max_diff = 0.; pthread_mutex_t applog_lock; static pthread_mutex_t stats_lock; @@ -192,6 +193,10 @@ Options:\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\ + -M, --max-diff=N specify a floating point value for the maximum block\n\ + difficulty that we will mine (default: inf); this\n\ + option is intended for testnet miners wishing to only\n\ + mine when difficulty drops to 1.0\n\ -q, --quiet disable per-thread hashmeter output\n\ -D, --debug enable debug output\n\ -P, --protocol-dump verbose dump of protocol-level activities\n" @@ -217,7 +222,7 @@ static char const short_options[] = #ifdef HAVE_SYSLOG_H "S" #endif - "a:c:Dhp:Px:qr:R:s:t:T:o:u:O:V"; + "a:c:DhM:p:Px:qr:R:s:t:T:o:u:O:V"; static struct option const options[] = { { "algo", 1, NULL, 'a' }, @@ -236,6 +241,7 @@ static struct option const options[] = { { "no-longpoll", 0, NULL, 1003 }, { "no-redirect", 0, NULL, 1009 }, { "no-stratum", 0, NULL, 1007 }, + { "max-diff", 1, NULL, 'M' }, { "pass", 1, NULL, 'p' }, { "protocol-dump", 0, NULL, 'P' }, { "proxy", 1, NULL, 'x' }, @@ -673,7 +679,7 @@ static void share_result(int result, const char *reason) hashrate += thr_hashrates[i]; result ? accepted_count++ : rejected_count++; pthread_mutex_unlock(&stats_lock); - + sprintf(s, hashrate >= 1e6 ? "%.0f" : "%.2f", 1e-3 * hashrate); applog(LOG_INFO, "accepted: %lu/%lu (%.2f%%), %s khash/s %s", accepted_count, @@ -1028,7 +1034,7 @@ static bool get_work(struct thr_info *thr, struct work *work) static bool submit_work(struct thr_info *thr, const struct work *work_in) { struct workio_cmd *wc; - + /* fill out work request message */ wc = calloc(1, sizeof(*wc)); if (!wc) @@ -1072,7 +1078,7 @@ static void stratum_gen_work(struct stratum_ctx *sctx, struct work *work) memcpy(merkle_root + 32, sctx->job.merkle[i], 32); sha256d(merkle_root, merkle_root, 64); } - + /* Increment extranonce2 */ for (i = 0; i < sctx->xnonce2_size && !++sctx->job.xnonce2[i]; i++); @@ -1130,7 +1136,7 @@ static void *miner_thread(void *userdata) thr_id, thr_id % num_processors); affine_to_cpu(thr_id, thr_id % num_processors); } - + if (opt_algo == ALGO_SCRYPT) { scratchbuf = scrypt_buffer_alloc(opt_scrypt_n); if (!scratchbuf) { @@ -1145,6 +1151,7 @@ static void *miner_thread(void *userdata) struct timeval tv_start, tv_end, diff; int64_t max64; int rc; + double work_diff; if (have_stratum) { while (time(NULL) >= g_work_time + 120) @@ -1181,7 +1188,7 @@ static void *miner_thread(void *userdata) work.data[19]++; pthread_mutex_unlock(&g_work_lock); work_restart[thr_id].restart = 0; - + /* adjust max_nonce to meet target scan time */ if (have_stratum) max64 = LP_SCANTIME; @@ -1203,25 +1210,32 @@ static void *miner_thread(void *userdata) max_nonce = end_nonce; else max_nonce = work.data[19] + max64; - + hashes_done = 0; gettimeofday(&tv_start, NULL); - /* scan nonces for a proof-of-work hash */ - switch (opt_algo) { - case ALGO_SCRYPT: - rc = scanhash_scrypt(thr_id, work.data, scratchbuf, work.target, - max_nonce, &hashes_done, opt_scrypt_n); - break; + if (max_diff > 1.0 && (work_diff = diff_from_nbits(&work.data[18])) > max_diff) { + rc = 0; + applog(LOG_INFO, "thread %d: %1.1lf > max_diff %1.1lf, sleeping 10 secs", + thr_id, work_diff, max_diff); + sleep(10); + } else { + /* scan nonces for a proof-of-work hash */ + switch (opt_algo) { + case ALGO_SCRYPT: + rc = scanhash_scrypt(thr_id, work.data, scratchbuf, work.target, + max_nonce, &hashes_done, opt_scrypt_n); + break; - case ALGO_SHA256D: - rc = scanhash_sha256d(thr_id, work.data, work.target, - max_nonce, &hashes_done); - break; + case ALGO_SHA256D: + rc = scanhash_sha256d(thr_id, work.data, work.target, + max_nonce, &hashes_done); + break; - default: - /* should never happen */ - goto out; + default: + /* should never happen */ + goto out; + } } /* record scanhash elapsed time */ @@ -1291,7 +1305,7 @@ static void *longpoll_thread(void *userdata) lp_url = hdr_path; hdr_path = NULL; } - + /* absolute path, on current server */ else { copy_start = (*hdr_path == '/') ? (hdr_path + 1) : hdr_path; @@ -1445,7 +1459,7 @@ static void *stratum_thread(void *userdata) restart_threads(); } } - + if (!stratum_socket_full(&stratum, 120)) { applog(LOG_ERR, "Stratum connection timed out"); s = NULL; @@ -1757,6 +1771,12 @@ static void parse_arg(int key, char *arg, char *pname) } strcpy(coinbase_sig, arg); break; + case 'M': /* --max-diff */ + if (sscanf(arg, "%lf", &max_diff) != 1 || max_diff <= 1.0) { + fprintf(stderr, "%s: invalid max_diff: %s\n", pname, arg); + show_usage_and_exit(1); + } + break; case 'S': use_syslog = true; break; @@ -1931,7 +1951,7 @@ int main(int argc, char *argv[]) thr_info = calloc(opt_n_threads + 3, sizeof(*thr)); if (!thr_info) return 1; - + thr_hashrates = (double *) calloc(opt_n_threads, sizeof(double)); if (!thr_hashrates) return 1; diff --git a/miner.h b/miner.h index 3f9b2f440..633b84b0a 100644 --- a/miner.h +++ b/miner.h @@ -206,6 +206,8 @@ extern int timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y); extern bool fulltest(const uint32_t *hash, const uint32_t *target); extern void diff_to_target(uint32_t *target, double diff); +/* Return the network difficulty from the work.data[18] bits as a double. */ +extern double diff_from_nbits(const void *nbits); struct stratum_job { char *job_id; diff --git a/util.c b/util.c index 23e073503..e62ab538a 100644 --- a/util.c +++ b/util.c @@ -80,7 +80,7 @@ void applog(int prio, const char *fmt, ...) va_list ap2; char *buf; int len; - + va_copy(ap2, ap); len = vsnprintf(NULL, 0, fmt, ap2) + 1; va_end(ap2); @@ -228,7 +228,7 @@ static size_t upload_data_cb(void *ptr, size_t size, size_t nmemb, static int seek_data_cb(void *user_data, curl_off_t offset, int origin) { struct upload_buffer *ub = user_data; - + switch (origin) { case SEEK_SET: ub->pos = offset; @@ -855,7 +855,7 @@ bool fulltest(const uint32_t *hash, const uint32_t *target) { int i; bool rc = true; - + for (i = 7; i >= 0; i--) { if (hash[i] > target[i]) { rc = false; @@ -870,7 +870,7 @@ bool fulltest(const uint32_t *hash, const uint32_t *target) if (opt_debug) { uint32_t hash_be[8], target_be[8]; 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]); @@ -892,7 +892,7 @@ void diff_to_target(uint32_t *target, double diff) { uint64_t m; int k; - + for (k = 6; k > 0 && diff > 1.0; k--) diff /= 4294967296.0; m = 4294901760.0 / diff; @@ -905,6 +905,25 @@ void diff_to_target(uint32_t *target, double diff) } } +double diff_from_nbits(const void *nbits_in) +{ + double numerator; + uint32_t diff32; + uint8_t pow; + int powdiff; + const uint32_t possibly_unswabbed_bits = le32dec(nbits_in); + const uint8_t * const nbits = (const uint8_t *)&possibly_unswabbed_bits; + + pow = nbits[0]; + powdiff = (8 * (0x1d - 3)) - (8 * (pow - 3)); + if (powdiff < 0) // testnet only + powdiff = 0; + diff32 = be32dec(nbits) & 0x00FFFFFF; + numerator = 0xFFFFULL << powdiff; + + return numerator / (double)diff32; +} + #ifdef WIN32 #define socket_blocks() (WSAGetLastError() == WSAEWOULDBLOCK) #else @@ -914,7 +933,7 @@ void diff_to_target(uint32_t *target, double diff) static bool send_line(struct stratum_ctx *sctx, char *s) { ssize_t len, sent = 0; - + len = strlen(s); s[len++] = '\n'; @@ -1471,7 +1490,7 @@ static bool stratum_get_version(struct stratum_ctx *sctx, json_t *id) char *s; json_t *val; bool ret; - + if (!id || json_is_null(id)) return false; @@ -1496,7 +1515,7 @@ static bool stratum_show_message(struct stratum_ctx *sctx, json_t *id, json_t *p val = json_array_get(params, 0); if (val) applog(LOG_NOTICE, "MESSAGE FROM SERVER: %s", json_string_value(val)); - + if (!id || json_is_null(id)) return true; From 1c304573dd757ff5a6da31dc31db58e5812cb6b6 Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Tue, 12 May 2020 14:42:27 +0300 Subject: [PATCH 2/3] max-diff: Added optional second component to argument, backoff time This is the amount of time to sleep (default 10). The argument is separated by a colon e.g. 10:60 would sleep 60 seconds if difficulty is > 10. --- configure.ac | 2 +- cpu-miner.c | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 38bb4653d..75ffd617b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([cpuminer], [2.5.0]) +AC_INIT([cpuminer], [2.5.0-maxdiff]) AC_PREREQ([2.59c]) AC_CANONICAL_SYSTEM diff --git a/cpu-miner.c b/cpu-miner.c index 569f4ab71..90ed8faf9 100644 --- a/cpu-miner.c +++ b/cpu-miner.c @@ -147,6 +147,7 @@ int stratum_thr_id = -1; struct work_restart *work_restart = NULL; static struct stratum_ctx stratum; static double max_diff = 0.; +static int max_diff_backoff_secs = 10; pthread_mutex_t applog_lock; static pthread_mutex_t stats_lock; @@ -193,10 +194,11 @@ Options:\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\ - -M, --max-diff=N specify a floating point value for the maximum block\n\ + -M, --max-diff=N[:T] specify a floating point value for the maximum block\n\ difficulty that we will mine (default: inf); this\n\ option is intended for testnet miners wishing to only\n\ - mine when difficulty drops to 1.0\n\ + mine when difficulty drops to 1.0 (optional :T is the\n\ + time to sleep in seconds, default: 10)\n\ -q, --quiet disable per-thread hashmeter output\n\ -D, --debug enable debug output\n\ -P, --protocol-dump verbose dump of protocol-level activities\n" @@ -1216,9 +1218,9 @@ static void *miner_thread(void *userdata) if (max_diff > 1.0 && (work_diff = diff_from_nbits(&work.data[18])) > max_diff) { rc = 0; - applog(LOG_INFO, "thread %d: %1.1lf > max_diff %1.1lf, sleeping 10 secs", - thr_id, work_diff, max_diff); - sleep(10); + applog(LOG_INFO, "thread %d: %1.1lf > max_diff %1.1lf, sleeping %d secs", + thr_id, work_diff, max_diff, max_diff_backoff_secs); + sleep(max_diff_backoff_secs); } else { /* scan nonces for a proof-of-work hash */ switch (opt_algo) { @@ -1771,10 +1773,22 @@ static void parse_arg(int key, char *arg, char *pname) } strcpy(coinbase_sig, arg); break; - case 'M': /* --max-diff */ - if (sscanf(arg, "%lf", &max_diff) != 1 || max_diff <= 1.0) { - fprintf(stderr, "%s: invalid max_diff: %s\n", pname, arg); - show_usage_and_exit(1); + case 'M': { /* --max-diff */ + char *colon; + if (sscanf(arg, "%lf", &max_diff) != 1 || max_diff <= 1.0) { + fprintf(stderr, "%s: invalid --max-diff: %s\n", pname, arg); + show_usage_and_exit(1); + } + // Optional second component to arg e.g. "10.0:5" where 5 here + // is the time to sleep. + if ((colon = strchr(arg, ':'))) { + if (sscanf(++colon, "%d", &max_diff_backoff_secs) != 1 + || max_diff_backoff_secs < 1) { + fprintf(stderr, "%s: invalid backoff time specified for" + " --max-diff: %s\n", pname, colon); + show_usage_and_exit(1); + } + } } break; case 'S': From 8c6afd8c1827fea69f1960f9b620553ca18eedfa Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Tue, 12 May 2020 15:08:52 +0300 Subject: [PATCH 3/3] Whitespace/formatting nits Our modifications to upstream had weird formatting. This has been fixed. The formatting now is more in line with what the upstream maintainer seems to be doing in the rest of the codebase. --- cpu-miner.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cpu-miner.c b/cpu-miner.c index 90ed8faf9..84b046028 100644 --- a/cpu-miner.c +++ b/cpu-miner.c @@ -1226,12 +1226,12 @@ static void *miner_thread(void *userdata) switch (opt_algo) { case ALGO_SCRYPT: rc = scanhash_scrypt(thr_id, work.data, scratchbuf, work.target, - max_nonce, &hashes_done, opt_scrypt_n); + max_nonce, &hashes_done, opt_scrypt_n); break; case ALGO_SHA256D: rc = scanhash_sha256d(thr_id, work.data, work.target, - max_nonce, &hashes_done); + max_nonce, &hashes_done); break; default: @@ -1774,23 +1774,23 @@ static void parse_arg(int key, char *arg, char *pname) strcpy(coinbase_sig, arg); break; case 'M': { /* --max-diff */ - char *colon; - if (sscanf(arg, "%lf", &max_diff) != 1 || max_diff <= 1.0) { - fprintf(stderr, "%s: invalid --max-diff: %s\n", pname, arg); + char *colon; + if (sscanf(arg, "%lf", &max_diff) != 1 || max_diff <= 1.0) { + fprintf(stderr, "%s: invalid --max-diff: %s\n", pname, arg); + show_usage_and_exit(1); + } + // Optional second component to arg e.g. "10.0:5" where 5 here + // is the time to sleep. + if ((colon = strchr(arg, ':'))) { + if (sscanf(++colon, "%d", &max_diff_backoff_secs) != 1 + || max_diff_backoff_secs < 1) { + fprintf(stderr, "%s: invalid backoff time specified for" + " --max-diff: %s\n", pname, colon); show_usage_and_exit(1); } - // Optional second component to arg e.g. "10.0:5" where 5 here - // is the time to sleep. - if ((colon = strchr(arg, ':'))) { - if (sscanf(++colon, "%d", &max_diff_backoff_secs) != 1 - || max_diff_backoff_secs < 1) { - fprintf(stderr, "%s: invalid backoff time specified for" - " --max-diff: %s\n", pname, colon); - show_usage_and_exit(1); - } - } } break; + } case 'S': use_syslog = true; break;