/* * Copyright 2010 Jeff Garzik * Copyright 2012-2014 pooler * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. See COPYING for more details. */ #include "cpuminer-config.h" #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WIN32 #include #include #else #include #include #if HAVE_SYS_SYSCTL_H #include #if HAVE_SYS_PARAM_H #include #endif #include #endif #endif #include "compat.h" #include "miner.h" #ifdef WIN32 #include #pragma comment(lib, "winmm.lib") #include "compat/winansi.h" BOOL WINAPI ConsoleHandler(DWORD); #endif #define PROGRAM_NAME "ccminer" #define LP_SCANTIME 60 #define HEAVYCOIN_BLKHDR_SZ 84 #define MNR_BLKHDR_SZ 80 // from heavy.cu #ifdef __cplusplus extern "C" { #endif int cuda_num_devices(); void cuda_devicenames(); void cuda_devicereset(); int cuda_finddevice(char *name); #ifdef __cplusplus } #endif #ifdef __linux /* Linux specific policy and affinity management */ #include static inline void drop_policy(void) { struct sched_param param; param.sched_priority = 0; #ifdef SCHED_IDLE if (unlikely(sched_setscheduler(0, SCHED_IDLE, ¶m) == -1)) #endif #ifdef SCHED_BATCH sched_setscheduler(0, SCHED_BATCH, ¶m); #endif } static inline void affine_to_cpu(int id, int cpu) { cpu_set_t set; CPU_ZERO(&set); CPU_SET(cpu, &set); sched_setaffinity(0, sizeof(&set), &set); } #elif defined(__FreeBSD__) /* FreeBSD specific policy and affinity management */ #include static inline void drop_policy(void) { } static inline void affine_to_cpu(int id, int cpu) { cpuset_t set; CPU_ZERO(&set); CPU_SET(cpu, &set); cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset_t), &set); } #else static inline void drop_policy(void) { } static inline void affine_to_cpu(int id, int cpu) { } #endif enum workio_commands { WC_GET_WORK, WC_SUBMIT_WORK, }; struct workio_cmd { enum workio_commands cmd; struct thr_info *thr; union { struct work *work; } u; }; typedef enum { ALGO_ANIME, ALGO_BLAKE, ALGO_BLAKECOIN, ALGO_DEEP, ALGO_DOOM, ALGO_FRESH, ALGO_FUGUE256, /* Fugue256 */ ALGO_GROESTL, ALGO_HEAVY, /* Heavycoin hash */ ALGO_KECCAK, ALGO_JACKPOT, ALGO_LUFFA_DOOM, ALGO_MJOLLNIR, /* Mjollnir hash */ ALGO_MYR_GR, ALGO_NIST5, ALGO_PENTABLAKE, ALGO_QUARK, ALGO_QUBIT, ALGO_WHC, ALGO_X11, ALGO_X13, ALGO_X14, ALGO_X15, ALGO_X17, ALGO_DMD_GR, } sha256_algos; static const char *algo_names[] = { "anime", "blake", "blakecoin", "deep", "doom", /* is luffa */ "fresh", "fugue256", "groestl", "heavy", "keccak", "jackpot", "luffa", "mjollnir", "myr-gr", "nist5", "penta", "quark", "qubit", "whirl", "x11", "x13", "x14", "x15", "x17", "dmd-gr", }; bool opt_debug = false; bool opt_protocol = false; bool opt_benchmark = false; bool want_longpoll = true; bool have_longpoll = false; bool want_stratum = true; bool have_stratum = false; static bool submit_old = false; bool use_syslog = false; bool use_colors = true; static bool opt_background = false; bool opt_quiet = false; static int opt_retries = -1; static int opt_fail_pause = 30; int opt_timeout = 270; static int opt_scantime = 5; static json_t *opt_config; static const bool opt_time = true; static sha256_algos opt_algo = ALGO_HEAVY; int opt_n_threads = 0; static double opt_difficulty = 1; // CH bool opt_trust_pool = false; uint16_t opt_vote = 9999; static int num_processors; int device_map[8] = {0,1,2,3,4,5,6,7}; // CB char *device_name[8]; // CB static char *rpc_url; static char *rpc_userpass; static char *rpc_user, *rpc_pass; static char *short_url = NULL; char *opt_cert; char *opt_proxy; long opt_proxy_type; struct thr_info *thr_info; static int work_thr_id; int longpoll_thr_id = -1; int stratum_thr_id = -1; struct work_restart *work_restart = NULL; static struct stratum_ctx stratum; pthread_mutex_t applog_lock; static pthread_mutex_t stats_lock; static unsigned long accepted_count = 0L; static unsigned long rejected_count = 0L; static double *thr_hashrates; uint64_t global_hashrate = 0; #ifdef HAVE_GETOPT_LONG #include #else struct option { const char *name; int has_arg; int *flag; int val; }; #endif static char const usage[] = "\ Usage: " PROGRAM_NAME " [OPTIONS]\n\ Options:\n\ -a, --algo=ALGO specify the hash algorithm to use\n\ anime Animecoin\n\ blake Blake 256 (SFR/NEOS)\n\ blakecoin Fast Blake 256 (8 rounds)\n\ deep Deepcoin\n\ dmd-gr Diamond-Groestl\n\ fresh Freshcoin (shavite 80)\n\ fugue256 Fuguecoin\n\ groestl Groestlcoin\n\ heavy Heavycoin\n\ jackpot Jackpot\n\ keccak Keccak-256 (Maxcoin)\n\ luffa Doomcoin\n\ mjollnir Mjollnircoin\n\ myr-gr Myriad-Groestl\n\ nist5 NIST5 (TalkCoin)\n\ penta Pentablake hash (5x Blake 512)\n\ quark Quark\n\ qubit Qubit\n\ x11 X11 (DarkCoin)\n\ x13 X13 (MaruCoin)\n\ x14 X14\n\ x15 X15\n\ x17 X17 (peoplecurrency)\n\ whirl Whirlcoin (old whirlpool)\n\ -d, --devices Comma separated list of CUDA devices to use.\n\ Device IDs start counting from 0! Alternatively takes\n\ string names of your cards like gtx780ti or gt640#2\n\ (matching 2nd gt640 in the PC)\n\ -f, --diff Divide difficulty by this factor (std is 1) \n\ -v, --vote=VOTE block reward vote (for HeavyCoin)\n\ -m, --trust-pool trust the max block reward vote (maxvote) sent by the pool\n\ -o, --url=URL URL of mining server\n\ -O, --userpass=U:P username:password pair for mining server\n\ -u, --user=USERNAME username for mining server\n\ -p, --pass=PASSWORD password for mining server\n\ --cert=FILE certificate for mining server using SSL\n\ -x, --proxy=[PROTOCOL://]HOST[:PORT] connect through a proxy\n\ -t, --threads=N number of miner threads (default: number of nVidia GPUs)\n\ -r, --retries=N number of times to retry if a network call fails\n\ (default: retry indefinitely)\n\ -R, --retry-pause=N time to pause between retries, in seconds (default: 30)\n\ -T, --timeout=N network timeout, in seconds (default: 270)\n\ -s, --scantime=N upper bound on time spent scanning current work when\n\ long polling is unavailable, in seconds (default: 5)\n\ --no-longpoll disable X-Long-Polling support\n\ --no-stratum disable X-Stratum support\n\ -q, --quiet disable per-thread hashmeter output\n\ -K, --no-color disable colored output\n\ -D, --debug enable debug output\n\ -P, --protocol-dump verbose dump of protocol-level activities\n" #ifdef HAVE_SYSLOG_H "\ -S, --syslog use system log for output messages\n" #endif #ifndef WIN32 "\ -B, --background run the miner in the background\n" #endif "\ --benchmark run in offline benchmark mode\n\ --cputest debug hashes from cpu algorithms\n\ -c, --config=FILE load a JSON-format configuration file\n\ -V, --version display version information and exit\n\ -h, --help display this help text and exit\n\ "; static char const short_options[] = #ifndef WIN32 "B" #endif #ifdef HAVE_SYSLOG_H "S" #endif "a:c:CKDhp:Px:qr:R:s:t:T:o:u:O:Vd:f:mv:"; static struct option const options[] = { { "algo", 1, NULL, 'a' }, #ifndef WIN32 { "background", 0, NULL, 'B' }, #endif { "benchmark", 0, NULL, 1005 }, { "cputest", 0, NULL, 1006 }, { "cert", 1, NULL, 1001 }, { "config", 1, NULL, 'c' }, { "no-color", 0, NULL, 'K' }, { "debug", 0, NULL, 'D' }, { "help", 0, NULL, 'h' }, { "no-longpoll", 0, NULL, 1003 }, { "no-stratum", 0, NULL, 1007 }, { "pass", 1, NULL, 'p' }, { "protocol-dump", 0, NULL, 'P' }, { "proxy", 1, NULL, 'x' }, { "quiet", 0, NULL, 'q' }, { "retries", 1, NULL, 'r' }, { "retry-pause", 1, NULL, 'R' }, { "scantime", 1, NULL, 's' }, #ifdef HAVE_SYSLOG_H { "syslog", 0, NULL, 'S' }, #endif { "threads", 1, NULL, 't' }, { "vote", 1, NULL, 'v' }, { "trust-pool", 0, NULL, 'm' }, { "timeout", 1, NULL, 'T' }, { "url", 1, NULL, 'o' }, { "user", 1, NULL, 'u' }, { "userpass", 1, NULL, 'O' }, { "version", 0, NULL, 'V' }, { "devices", 1, NULL, 'd' }, { "diff", 1, NULL, 'f' }, { 0, 0, 0, 0 } }; struct work { uint32_t data[32]; uint32_t target[8]; uint32_t maxvote; char job_id[128]; size_t xnonce2_len; unsigned char xnonce2[32]; union { uint32_t u32[2]; uint64_t u64[1]; } noncerange; double difficulty; uint32_t scanned_from; uint32_t scanned_to; }; static struct work g_work; static time_t g_work_time; static pthread_mutex_t g_work_lock; void proper_exit(int reason) { cuda_devicereset(); hashlog_purge_all(); exit(reason); } static bool jobj_binary(const json_t *obj, const char *key, void *buf, size_t buflen) { const char *hexstr; json_t *tmp; tmp = json_object_get(obj, key); if (unlikely(!tmp)) { applog(LOG_ERR, "JSON key '%s' not found", key); return false; } hexstr = json_string_value(tmp); if (unlikely(!hexstr)) { applog(LOG_ERR, "JSON key '%s' is not a string", key); return false; } if (!hex2bin((unsigned char*)buf, hexstr, buflen)) return false; return true; } 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"); goto err_out; } if (unlikely(!jobj_binary(val, "target", work->target, sizeof(work->target)))) { applog(LOG_ERR, "JSON inval target"); goto err_out; } if (opt_algo == ALGO_HEAVY) { if (unlikely(!jobj_binary(val, "maxvote", &work->maxvote, sizeof(work->maxvote)))) { work->maxvote = 1024; } } else work->maxvote = 0; for (i = 0; i < ARRAY_SIZE(work->data); i++) work->data[i] = le32dec(work->data + i); for (i = 0; i < ARRAY_SIZE(work->target); i++) work->target[i] = le32dec(work->target + i); json_t *jr = json_object_get(val, "noncerange"); if (jr) { const char * hexstr = json_string_value(jr); if (likely(hexstr)) { // never seen yet... hex2bin((unsigned char*)work->noncerange.u64, hexstr, 8); applog(LOG_DEBUG, "received noncerange: %08x-%08x", work->noncerange.u32[0], work->noncerange.u32[1]); } } /* use work ntime as job id (solo-mining) */ cbin2hex(work->job_id, (const char*)&work->data[17], 4); return true; err_out: return false; } /** * Calculate the work difficulty as double */ static void calc_diff(struct work *work, int known) { // sample for diff 32.53 : 00000007de5f0000 const uint64_t diffone = 0xFFFF000000000000ull; uint64_t *data64, d64; char rtarget[32]; swab256(rtarget, work->target); data64 = (uint64_t *)(rtarget + 3); /* todo: index (3) can be tuned here */ d64 = swab64(*data64); if (unlikely(!d64)) d64 = 1; work->difficulty = (double)diffone / d64; if (opt_difficulty > 0.) { work->difficulty /= opt_difficulty; } } static int share_result(int result, const char *reason) { char s[345]; double hashrate; int i, ret = 0; hashrate = 0.; pthread_mutex_lock(&stats_lock); for (i = 0; i < opt_n_threads; i++) hashrate += thr_hashrates[i]; result ? accepted_count++ : rejected_count++; pthread_mutex_unlock(&stats_lock); global_hashrate = llround(hashrate); sprintf(s, hashrate >= 1e6 ? "%.0f" : "%.2f", 1e-3 * hashrate); applog(LOG_NOTICE, "accepted: %lu/%lu (%.2f%%), %s khash/s %s", accepted_count, accepted_count + rejected_count, 100. * accepted_count / (accepted_count + rejected_count), s, use_colors ? (result ? CL_GRN "yay!!!" : CL_RED "booooo") : (result ? "(yay!!!)" : "(booooo)")); if (reason) { applog(LOG_WARNING, "reject reason: %s", reason); if (strncmp(reason, "low difficulty share", 20) == 0) { opt_difficulty = (opt_difficulty * 2.0) / 3.0; applog(LOG_WARNING, "factor reduced to : %0.2f", opt_difficulty); return 0; } } return 1; } static bool submit_upstream_work(CURL *curl, struct work *work) { char *str = NULL; json_t *val, *res, *reason; char s[345]; int i; bool rc = false; /* pass if the previous hash is not the current previous hash */ pthread_mutex_lock(&g_work_lock); if (memcmp(work->data + 1, g_work.data + 1, 32)) { pthread_mutex_unlock(&g_work_lock); if (opt_debug) applog(LOG_DEBUG, "DEBUG: stale work detected, discarding"); return true; } calc_diff(work, 0); pthread_mutex_unlock(&g_work_lock); if (have_stratum) { uint32_t sent; uint32_t ntime, nonce; uint16_t nvote; char *ntimestr, *noncestr, *xnonce2str, *nvotestr; le32enc(&ntime, work->data[17]); le32enc(&nonce, work->data[19]); be16enc(&nvote, *((uint16_t*)&work->data[20])); ntimestr = bin2hex((const unsigned char *)(&ntime), 4); noncestr = bin2hex((const unsigned char *)(&nonce), 4); xnonce2str = bin2hex(work->xnonce2, work->xnonce2_len); nvotestr = bin2hex((const unsigned char *)(&nvote), 2); sent = hashlog_already_submittted(work->job_id, nonce); if (sent > 0) { sent = (uint32_t) time(NULL) - sent; if (!opt_quiet) { applog(LOG_WARNING, "skip submit, nonce %s was already sent %u seconds ago", noncestr, sent); hashlog_dump_job(work->job_id); } rc = true; goto out; } if (opt_algo == ALGO_HEAVY) { sprintf(s, "{\"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\":4}", rpc_user, work->job_id + 8, xnonce2str, ntimestr, noncestr, nvotestr); } else { sprintf(s, "{\"method\": \"mining.submit\", \"params\": [\"%s\", \"%s\", \"%s\", \"%s\", \"%s\"], \"id\":4}", rpc_user, work->job_id + 8, xnonce2str, ntimestr, noncestr); } free(ntimestr); free(noncestr); free(xnonce2str); free(nvotestr); if (unlikely(!stratum_send_line(&stratum, s))) { applog(LOG_ERR, "submit_upstream_work stratum_send_line failed"); goto out; } hashlog_remember_submit(work->job_id, nonce, work->scanned_from); } else { /* build hex string */ if (opt_algo != ALGO_HEAVY && opt_algo != ALGO_MJOLLNIR) { 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; } /* build JSON-RPC request */ sprintf(s, "{\"method\": \"getwork\", \"params\": [ \"%s\" ], \"id\":1}\r\n", str); /* issue JSON-RPC request */ val = json_rpc_call(curl, rpc_url, rpc_userpass, s, false, false, NULL); if (unlikely(!val)) { applog(LOG_ERR, "submit_upstream_work json_rpc_call failed"); goto out; } res = json_object_get(val, "result"); reason = json_object_get(val, "reject-reason"); if (!share_result(json_is_true(res), reason ? json_string_value(reason) : NULL)) hashlog_purge_job(work->job_id); json_decref(val); } rc = true; out: free(str); return rc; } static const char *rpc_req = "{\"method\": \"getwork\", \"params\": [], \"id\":0}\r\n"; static bool get_upstream_work(CURL *curl, struct work *work) { json_t *val; bool rc; struct timeval tv_start, tv_end, diff; gettimeofday(&tv_start, NULL); val = json_rpc_call(curl, rpc_url, rpc_userpass, rpc_req, want_longpoll, false, NULL); gettimeofday(&tv_end, NULL); if (have_stratum) { if (val) json_decref(val); return true; } if (!val) return false; rc = work_decode(json_object_get(val, "result"), work); if (opt_protocol && rc) { timeval_subtract(&diff, &tv_end, &tv_start); /* show time because curl can be slower against versions/config */ applog(LOG_DEBUG, "got new work in %u µs", diff.tv_sec * 1000000 + diff.tv_usec); } json_decref(val); return rc; } static void workio_cmd_free(struct workio_cmd *wc) { if (!wc) return; switch (wc->cmd) { case WC_SUBMIT_WORK: free(wc->u.work); break; default: /* do nothing */ break; } memset(wc, 0, sizeof(*wc)); /* poison */ free(wc); } static bool workio_get_work(struct workio_cmd *wc, CURL *curl) { struct work *ret_work; int failures = 0; ret_work = (struct work*)calloc(1, sizeof(*ret_work)); if (!ret_work) return false; /* obtain new work from bitcoin via JSON-RPC */ while (!get_upstream_work(curl, ret_work)) { if (unlikely((opt_retries >= 0) && (++failures > opt_retries))) { applog(LOG_ERR, "json_rpc_call failed, terminating workio thread"); free(ret_work); return false; } /* pause, then restart work-request loop */ applog(LOG_ERR, "json_rpc_call failed, retry after %d seconds", opt_fail_pause); sleep(opt_fail_pause); } /* send work to requesting thread */ if (!tq_push(wc->thr->q, ret_work)) free(ret_work); return true; } static bool workio_submit_work(struct workio_cmd *wc, CURL *curl) { int failures = 0; /* submit solution to bitcoin via JSON-RPC */ while (!submit_upstream_work(curl, wc->u.work)) { if (unlikely((opt_retries >= 0) && (++failures > opt_retries))) { applog(LOG_ERR, "...terminating workio thread"); return false; } /* pause, then restart work-request loop */ if (!opt_benchmark) applog(LOG_ERR, "...retry after %d seconds", opt_fail_pause); sleep(opt_fail_pause); } return true; } static void *workio_thread(void *userdata) { struct thr_info *mythr = (struct thr_info*)userdata; CURL *curl; bool ok = true; curl = curl_easy_init(); if (unlikely(!curl)) { applog(LOG_ERR, "CURL initialization failed"); return NULL; } while (ok) { struct workio_cmd *wc; /* wait for workio_cmd sent to us, on our queue */ wc = (struct workio_cmd *)tq_pop(mythr->q, NULL); if (!wc) { ok = false; break; } /* process workio_cmd */ switch (wc->cmd) { case WC_GET_WORK: ok = workio_get_work(wc, curl); break; case WC_SUBMIT_WORK: ok = workio_submit_work(wc, curl); break; default: /* should never happen */ ok = false; break; } workio_cmd_free(wc); } tq_freeze(mythr->q); curl_easy_cleanup(curl); return NULL; } static bool get_work(struct thr_info *thr, struct work *work) { struct workio_cmd *wc; struct work *work_heap; if (opt_benchmark) { memset(work->data, 0x55, 76); work->data[17] = swab32((uint32_t)time(NULL)); memset(work->data + 19, 0x00, 52); work->data[20] = 0x80000000; work->data[31] = 0x00000280; memset(work->target, 0x00, sizeof(work->target)); return true; } /* fill out work request message */ wc = (struct workio_cmd *)calloc(1, sizeof(*wc)); if (!wc) return false; wc->cmd = WC_GET_WORK; wc->thr = thr; /* send work request to workio thread */ if (!tq_push(thr_info[work_thr_id].q, wc)) { workio_cmd_free(wc); return false; } /* wait for response, a unit of work */ work_heap = (struct work *)tq_pop(thr->q, NULL); if (!work_heap) return false; /* copy returned work into storage provided by caller */ memcpy(work, work_heap, sizeof(*work)); free(work_heap); return true; } static bool submit_work(struct thr_info *thr, const struct work *work_in) { struct workio_cmd *wc; /* fill out work request message */ wc = (struct workio_cmd *)calloc(1, sizeof(*wc)); if (!wc) return false; wc->u.work = (struct work *)malloc(sizeof(*work_in)); if (!wc->u.work) goto err_out; wc->cmd = WC_SUBMIT_WORK; wc->thr = thr; memcpy(wc->u.work, work_in, sizeof(*work_in)); /* send solution to workio thread */ if (!tq_push(thr_info[work_thr_id].q, wc)) goto err_out; return true; err_out: workio_cmd_free(wc); return false; } static void stratum_gen_work(struct stratum_ctx *sctx, struct work *work) { unsigned char merkle_root[64]; int i; if (!sctx->job.job_id) { /* job not yet retrieved */ return; } pthread_mutex_lock(&sctx->work_lock); // store the job ntime as high part of jobid snprintf(work->job_id, sizeof(work->job_id), "%07x %s", be32dec(sctx->job.ntime) & 0xfffffff, sctx->job.job_id); work->xnonce2_len = sctx->xnonce2_size; memcpy(work->xnonce2, sctx->job.xnonce2, sctx->xnonce2_size); /* Generate merkle root */ switch (opt_algo) { case ALGO_HEAVY: case ALGO_MJOLLNIR: heavycoin_hash(merkle_root, sctx->job.coinbase, (int)sctx->job.coinbase_size); break; case ALGO_FUGUE256: case ALGO_GROESTL: case ALGO_KECCAK: case ALGO_BLAKECOIN: case ALGO_WHC: SHA256((uint8_t*)sctx->job.coinbase, sctx->job.coinbase_size, (uint8_t*)merkle_root); break; default: sha256d(merkle_root, sctx->job.coinbase, (int)sctx->job.coinbase_size); } for (i = 0; i < sctx->job.merkle_count; i++) { memcpy(merkle_root + 32, sctx->job.merkle[i], 32); if (opt_algo == ALGO_HEAVY || opt_algo == ALGO_MJOLLNIR) heavycoin_hash(merkle_root, merkle_root, 64); else sha256d(merkle_root, merkle_root, 64); } /* Increment extranonce2 */ for (i = 0; i < (int)sctx->xnonce2_size && !++sctx->job.xnonce2[i]; i++); /* Assemble block header */ memset(work->data, 0, sizeof(work->data)); work->data[0] = le32dec(sctx->job.version); for (i = 0; i < 8; i++) work->data[1 + i] = le32dec((uint32_t *)sctx->job.prevhash + i); for (i = 0; i < 8; i++) work->data[9 + i] = be32dec((uint32_t *)merkle_root + i); work->data[17] = le32dec(sctx->job.ntime); work->data[18] = le32dec(sctx->job.nbits); if (opt_algo == ALGO_MJOLLNIR) { for (i = 0; i < 20; i++) work->data[i] = be32dec((uint32_t *)&work->data[i]); } work->data[20] = 0x80000000; work->data[31] = (opt_algo == ALGO_MJOLLNIR) ? 0x000002A0 : 0x00000280; // HeavyCoin if (opt_algo == ALGO_HEAVY) { uint16_t *ext; work->maxvote = 1024; ext = (uint16_t*)(&work->data[20]); ext[0] = opt_vote; ext[1] = be16dec(sctx->job.nreward); for (i = 0; i < 20; i++) work->data[i] = be32dec((uint32_t *)&work->data[i]); } // pthread_mutex_unlock(&sctx->work_lock); if (opt_debug) { char *tm = atime2str(swab32(work->data[17]) - sctx->srvtime_diff); char *xnonce2str = bin2hex(work->xnonce2, sctx->xnonce2_size); applog(LOG_DEBUG, "DEBUG: job_id=%s xnonce2=%s time=%s", work->job_id, xnonce2str, tm); free(tm); free(xnonce2str); } if (opt_algo == ALGO_JACKPOT) diff_to_target(work->target, sctx->job.diff / (65536.0 * opt_difficulty)); else if (opt_algo == ALGO_FUGUE256 || opt_algo == ALGO_GROESTL || opt_algo == ALGO_DMD_GR || opt_algo == ALGO_FRESH) diff_to_target(work->target, sctx->job.diff / (256.0 * opt_difficulty)); else if (opt_algo == ALGO_KECCAK) diff_to_target(work->target, sctx->job.diff / (128.0 * opt_difficulty)); else diff_to_target(work->target, sctx->job.diff / opt_difficulty); } static void *miner_thread(void *userdata) { struct thr_info *mythr = (struct thr_info *)userdata; int thr_id = mythr->id; struct work work; uint32_t max_nonce; uint32_t end_nonce = 0xffffffffU / opt_n_threads * (thr_id + 1) - (thr_id + 1); unsigned char *scratchbuf = NULL; bool work_done = false; bool extrajob = false; char s[16]; int rc = 0; memset(&work, 0, sizeof(work)); // prevent work from being used uninitialized /* Set worker threads to nice 19 and then preferentially to SCHED_IDLE * and if that fails, then SCHED_BATCH. No need for this to be an * error if it fails */ if (!opt_benchmark) { setpriority(PRIO_PROCESS, 0, 19); drop_policy(); } /* Cpu affinity only makes sense if the number of threads is a multiple * of the number of CPUs */ if (num_processors > 1 && opt_n_threads % num_processors == 0) { if (!opt_quiet) applog(LOG_DEBUG, "Binding thread %d to cpu %d", thr_id, thr_id % num_processors); affine_to_cpu(thr_id, thr_id % num_processors); } while (1) { unsigned long hashes_done; uint32_t start_nonce; struct timeval tv_start, tv_end, diff; int64_t max64; uint64_t umax64; // &work.data[19] int wcmplen = 76; uint32_t *nonceptr = (uint32_t*) (((char*)work.data) + wcmplen); if (have_stratum) { uint32_t sleeptime = 0; while (!work_done && time(NULL) >= (g_work_time + opt_scantime)) { usleep(100*1000); if (sleeptime > 4) { extrajob = true; break; } sleeptime++; } if (sleeptime && opt_debug && !opt_quiet) applog(LOG_DEBUG, "sleeptime: %u ms", sleeptime*100); nonceptr = (uint32_t*) (((char*)work.data) + wcmplen); pthread_mutex_lock(&g_work_lock); extrajob |= work_done; if ((*nonceptr) >= end_nonce || extrajob) { work_done = false; extrajob = false; stratum_gen_work(&stratum, &g_work); } } else { int min_scantime = have_longpoll ? LP_SCANTIME : opt_scantime; /* obtain new work from internal workio thread */ pthread_mutex_lock(&g_work_lock); if (time(NULL) - g_work_time >= min_scantime || (*nonceptr) >= end_nonce) { if (unlikely(!get_work(mythr, &g_work))) { applog(LOG_ERR, "work retrieval failed, exiting " "mining thread %d", mythr->id); pthread_mutex_unlock(&g_work_lock); goto out; } g_work_time = time(NULL); } } #if 0 if (!opt_benchmark && g_work.job_id[0] == '\0') { applog(LOG_ERR, "work data not read yet"); extrajob = true; work_done = true; sleep(1); //continue; } #endif if (rc > 1) { /* if we found more than one on last loop */ /* todo: handle an array to get them directly */ pthread_mutex_unlock(&g_work_lock); goto continue_scan; } if (memcmp(work.target, g_work.target, sizeof(work.target))) { calc_diff(&g_work, 0); if (opt_debug) { uint64_t target64 = g_work.target[7] * 0x100000000ULL + g_work.target[6]; applog(LOG_DEBUG, "job %s target change: %llx (%.1f)", g_work.job_id, target64, g_work.difficulty); } memcpy(work.target, g_work.target, sizeof(work.target)); work.difficulty = g_work.difficulty; (*nonceptr) = (0xffffffffUL / opt_n_threads) * thr_id; // 0 if single thr /* on new target, ignoring nonce, clear sent data (hashlog) */ if (memcmp(work.target, g_work.target, sizeof(work.target))) { hashlog_purge_job(work.job_id); } } if (memcmp(work.data, g_work.data, wcmplen)) { if (opt_debug) { #if 0 for (int n=0; n <= (wcmplen-8); n+=8) { if (memcmp(work.data + n, g_work.data + n, 8)) { applog(LOG_DEBUG, "job %s work updated at offset %d:", g_work.job_id, n); applog_hash((uint8_t*) work.data + n); applog_compare_hash((uint8_t*) g_work.data + n, (uint8_t*) work.data + n); } } #endif } memcpy(&work, &g_work, sizeof(struct work)); (*nonceptr) = (0xffffffffUL / opt_n_threads) * thr_id; // 0 if single thr } else (*nonceptr)++; //?? work_restart[thr_id].restart = 0; if (opt_debug) applog(LOG_DEBUG, "job %s %08x", g_work.job_id, (*nonceptr)); pthread_mutex_unlock(&g_work_lock); /* adjust max_nonce to meet target scan time */ if (have_stratum) max64 = LP_SCANTIME; else max64 = g_work_time + (have_longpoll ? LP_SCANTIME : opt_scantime) - time(NULL); max64 *= (int64_t)thr_hashrates[thr_id]; if (max64 <= 0) { /* should not be set too high, else you can miss multiple nounces */ switch (opt_algo) { case ALGO_JACKPOT: max64 = 0x1fffLL; break; case ALGO_BLAKECOIN: max64 = 0x3ffffffLL; break; case ALGO_BLAKE: /* based on the 750Ti hashrate (100kH) */ max64 = 0x1ffffffLL; break; default: max64 = 0xfffffLL; break; } } start_nonce = *nonceptr; /* do not recompute something already scanned */ if (opt_algo == ALGO_BLAKE && opt_n_threads == 1) { union { uint64_t data; uint32_t scanned[2]; } range; range.data = hashlog_get_scan_range(work.job_id); if (range.data) { bool stall = false; if (range.scanned[0] == 1 && range.scanned[1] == 0xFFFFFFFFUL) { applog(LOG_WARNING, "detected a rescan of fully scanned job!"); } else if (range.scanned[0] > 0 && range.scanned[1] > 0 && range.scanned[1] < 0xFFFFFFF0UL) { /* continue scan the end */ start_nonce = range.scanned[1] + 1; //applog(LOG_DEBUG, "scan the next part %x + 1 (%x-%x)", range.scanned[1], range.scanned[0], range.scanned[1]); } stall = (start_nonce == work.scanned_from && end_nonce == work.scanned_to); stall |= (start_nonce == work.scanned_from && start_nonce == range.scanned[1] + 1); stall |= (start_nonce > range.scanned[0] && start_nonce < range.scanned[1]); if (stall) { if (opt_debug && !opt_quiet) applog(LOG_DEBUG, "job done, wait for a new one..."); work_restart[thr_id].restart = 1; hashlog_purge_old(); // wait a bit for a new job... usleep(500*1000); (*nonceptr) = end_nonce + 1; work_done = true; continue; } } } umax64 = (uint64_t) max64; if ((umax64 + start_nonce) >= end_nonce) max_nonce = end_nonce; else max_nonce = (uint32_t) umax64 + start_nonce; work.scanned_from = start_nonce; (*nonceptr) = start_nonce; hashes_done = 0; continue_scan: gettimeofday(&tv_start, NULL); /* scan nonces for a proof-of-work hash */ switch (opt_algo) { case ALGO_HEAVY: rc = scanhash_heavy(thr_id, work.data, work.target, max_nonce, &hashes_done, work.maxvote, HEAVYCOIN_BLKHDR_SZ); break; case ALGO_KECCAK: rc = scanhash_keccak256(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_MJOLLNIR: rc = scanhash_heavy(thr_id, work.data, work.target, max_nonce, &hashes_done, 0, MNR_BLKHDR_SZ); break; case ALGO_DEEP: rc = scanhash_deep(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_DOOM: case ALGO_LUFFA_DOOM: rc = scanhash_doom(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_FUGUE256: rc = scanhash_fugue256(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_GROESTL: case ALGO_DMD_GR: rc = scanhash_groestlcoin(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_MYR_GR: rc = scanhash_myriad(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_JACKPOT: rc = scanhash_jackpot(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_QUARK: rc = scanhash_quark(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_QUBIT: rc = scanhash_qubit(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_ANIME: rc = scanhash_anime(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_BLAKECOIN: rc = scanhash_blake256(thr_id, work.data, work.target, max_nonce, &hashes_done, 8); break; case ALGO_BLAKE: rc = scanhash_blake256(thr_id, work.data, work.target, max_nonce, &hashes_done, 14); break; case ALGO_FRESH: rc = scanhash_fresh(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_NIST5: rc = scanhash_nist5(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_PENTABLAKE: rc = scanhash_pentablake(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_WHC: rc = scanhash_whc(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_X11: rc = scanhash_x11(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_X13: rc = scanhash_x13(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_X14: rc = scanhash_x14(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_X15: rc = scanhash_x15(thr_id, work.data, work.target, max_nonce, &hashes_done); break; case ALGO_X17: rc = scanhash_x17(thr_id, work.data, work.target, max_nonce, &hashes_done); break; default: /* should never happen */ goto out; } /* record scanhash elapsed time */ gettimeofday(&tv_end, NULL); if (rc && opt_debug) applog(LOG_NOTICE, CL_CYN "found => %08x" CL_GRN " %08x", *nonceptr, swab32(*nonceptr)); timeval_subtract(&diff, &tv_end, &tv_start); if (diff.tv_usec || diff.tv_sec) { pthread_mutex_lock(&stats_lock); if (diff.tv_sec + 1e-6 * diff.tv_usec > 0.0) { thr_hashrates[thr_id] = hashes_done / (diff.tv_sec + 1e-6 * diff.tv_usec); if (rc > 1) thr_hashrates[thr_id] = (rc * hashes_done) / (diff.tv_sec + 1e-6 * diff.tv_usec); } pthread_mutex_unlock(&stats_lock); } if (!opt_quiet) { sprintf(s, thr_hashrates[thr_id] >= 1e6 ? "%.0f" : "%.2f", 1e-3 * thr_hashrates[thr_id]); applog(LOG_INFO, "GPU #%d: %s, %s kH/s", device_map[thr_id], device_name[thr_id], s); } if (thr_id == opt_n_threads - 1) { double hashrate = 0.; for (int i = 0; i < opt_n_threads && thr_hashrates[i]; i++) hashrate += thr_hashrates[i]; global_hashrate = llround(hashrate); if (opt_benchmark) { sprintf(s, hashrate >= 1e6 ? "%.0f" : "%.2f", hashrate / 1000.); applog(LOG_NOTICE, "Total: %s kH/s", s); } } if (rc) { work.scanned_to = *nonceptr; } else { work.scanned_to = max_nonce; } // could be used to store speeds too.. hashlog_remember_scan_range(work.job_id, work.scanned_from, work.scanned_to); /* if nonce found, submit work */ if (rc) { if (!opt_benchmark && !submit_work(mythr, &work)) break; } } out: tq_freeze(mythr->q); return NULL; } static void restart_threads(void) { int i; for (i = 0; i < opt_n_threads; i++) work_restart[i].restart = 1; } static void *longpoll_thread(void *userdata) { struct thr_info *mythr = (struct thr_info *)userdata; CURL *curl = NULL; char *copy_start, *hdr_path = NULL, *lp_url = NULL; bool need_slash = false; curl = curl_easy_init(); if (unlikely(!curl)) { applog(LOG_ERR, "CURL initialization failed"); goto out; } start: hdr_path = (char*)tq_pop(mythr->q, NULL); if (!hdr_path) goto out; /* full URL */ if (strstr(hdr_path, "://")) { lp_url = hdr_path; hdr_path = NULL; } /* absolute path, on current server */ else { copy_start = (*hdr_path == '/') ? (hdr_path + 1) : hdr_path; if (rpc_url[strlen(rpc_url) - 1] != '/') need_slash = true; lp_url = (char*)malloc(strlen(rpc_url) + strlen(copy_start) + 2); if (!lp_url) goto out; sprintf(lp_url, "%s%s%s", rpc_url, need_slash ? "/" : "", copy_start); } applog(LOG_INFO, "Long-polling activated for %s", lp_url); while (1) { json_t *val, *soval; int err; val = json_rpc_call(curl, lp_url, rpc_userpass, rpc_req, false, true, &err); if (have_stratum) { if (val) json_decref(val); goto out; } if (likely(val)) { if (!opt_quiet) applog(LOG_INFO, "LONGPOLL detected new block"); soval = json_object_get(json_object_get(val, "result"), "submitold"); submit_old = soval ? json_is_true(soval) : false; pthread_mutex_lock(&g_work_lock); if (work_decode(json_object_get(val, "result"), &g_work)) { if (opt_debug) applog(LOG_BLUE, "LONGPOLL pushed new work"); time(&g_work_time); restart_threads(); } pthread_mutex_unlock(&g_work_lock); json_decref(val); } else { pthread_mutex_lock(&g_work_lock); g_work_time -= LP_SCANTIME; pthread_mutex_unlock(&g_work_lock); if (err == CURLE_OPERATION_TIMEDOUT) { restart_threads(); } else { have_longpoll = false; restart_threads(); free(hdr_path); free(lp_url); lp_url = NULL; sleep(opt_fail_pause); goto start; } } } out: free(hdr_path); free(lp_url); tq_freeze(mythr->q); if (curl) curl_easy_cleanup(curl); return NULL; } static bool stratum_handle_response(char *buf) { json_t *val, *err_val, *res_val, *id_val; json_error_t err; bool ret = false; val = JSON_LOADS(buf, &err); if (!val) { applog(LOG_INFO, "JSON decode failed(%d): %s", err.line, err.text); goto out; } res_val = json_object_get(val, "result"); err_val = json_object_get(val, "error"); id_val = json_object_get(val, "id"); if (!id_val || json_is_null(id_val) || !res_val) goto out; share_result(json_is_true(res_val), err_val ? json_string_value(json_array_get(err_val, 1)) : NULL); ret = true; out: if (val) json_decref(val); return ret; } static void *stratum_thread(void *userdata) { struct thr_info *mythr = (struct thr_info *)userdata; char *s; stratum.url = (char*)tq_pop(mythr->q, NULL); if (!stratum.url) goto out; applog(LOG_BLUE, "Starting Stratum on %s", stratum.url); while (1) { int failures = 0; while (!stratum.curl) { pthread_mutex_lock(&g_work_lock); g_work_time = 0; pthread_mutex_unlock(&g_work_lock); restart_threads(); if (!stratum_connect(&stratum, stratum.url) || !stratum_subscribe(&stratum) || !stratum_authorize(&stratum, rpc_user, rpc_pass)) { stratum_disconnect(&stratum); if (opt_retries >= 0 && ++failures > opt_retries) { applog(LOG_ERR, "...terminating workio thread"); tq_push(thr_info[work_thr_id].q, NULL); goto out; } if (!opt_benchmark) applog(LOG_ERR, "...retry after %d seconds", opt_fail_pause); sleep(opt_fail_pause); } } if (stratum.job.job_id && (!g_work_time || strncmp(stratum.job.job_id, g_work.job_id + 8, 120))) { pthread_mutex_lock(&g_work_lock); stratum_gen_work(&stratum, &g_work); time(&g_work_time); if (stratum.job.clean) { if (!opt_quiet) applog(LOG_BLUE, "%s send a new %s block %d", short_url, algo_names[opt_algo], stratum.bloc_height); restart_threads(); hashlog_purge_old(); } else if (!opt_quiet) { applog(LOG_BLUE, "%s send job %d for block %d", short_url, strtoul(stratum.job.job_id, NULL, 16), stratum.bloc_height); } pthread_mutex_unlock(&g_work_lock); } if (!stratum_socket_full(&stratum, 120)) { applog(LOG_ERR, "Stratum connection timed out"); s = NULL; } else s = stratum_recv_line(&stratum); if (!s) { stratum_disconnect(&stratum); applog(LOG_ERR, "Stratum connection interrupted"); continue; } if (!stratum_handle_method(&stratum, s)) stratum_handle_response(s); free(s); } out: return NULL; } #define PROGRAM_VERSION "1.4.5" static void show_version_and_exit(void) { printf("%s v%s\n" #ifdef WIN32 "pthreads static %s\n" #endif "%s\n", PACKAGE_STRING, PROGRAM_VERSION, #ifdef WIN32 PTW32_VERSION_STRING, #endif curl_version()); proper_exit(0); } static void show_usage_and_exit(int status) { if (status) fprintf(stderr, "Try `" PROGRAM_NAME " --help' for more information.\n"); else printf(usage); proper_exit(status); } static void parse_arg (int key, char *arg) { char *p; int v, i; double d; switch(key) { case 'a': for (i = 0; i < ARRAY_SIZE(algo_names); i++) { if (algo_names[i] && !strcmp(arg, algo_names[i])) { opt_algo = (sha256_algos)i; break; } } if (i == ARRAY_SIZE(algo_names)) show_usage_and_exit(1); break; case 'B': opt_background = true; break; case 'c': { json_error_t err; if (opt_config) json_decref(opt_config); #if JANSSON_VERSION_HEX >= 0x020000 opt_config = json_load_file(arg, 0, &err); #else opt_config = json_load_file(arg, &err); #endif if (!json_is_object(opt_config)) { applog(LOG_ERR, "JSON decode of %s failed", arg); proper_exit(1); } break; } case 'C': /* color for compat */ use_colors = true; break; case 'K': use_colors = false; break; case 'D': opt_debug = true; break; case 'q': opt_quiet = true; break; case 'p': free(rpc_pass); rpc_pass = strdup(arg); break; case 'P': opt_protocol = true; break; case 'r': v = atoi(arg); if (v < -1 || v > 9999) /* sanity check */ show_usage_and_exit(1); opt_retries = v; break; case 'R': v = atoi(arg); if (v < 1 || v > 9999) /* sanity check */ show_usage_and_exit(1); opt_fail_pause = v; break; case 's': v = atoi(arg); if (v < 1 || v > 9999) /* sanity check */ show_usage_and_exit(1); opt_scantime = v; break; case 'T': v = atoi(arg); if (v < 1 || v > 99999) /* sanity check */ show_usage_and_exit(1); opt_timeout = v; break; case 't': v = atoi(arg); if (v < 1 || v > 9999) /* sanity check */ show_usage_and_exit(1); opt_n_threads = v; break; case 'v': v = atoi(arg); if (v < 0 || v > 1024) /* sanity check */ show_usage_and_exit(1); opt_vote = (uint16_t)v; break; case 'm': opt_trust_pool = true; break; case 'u': free(rpc_user); rpc_user = strdup(arg); break; case 'o': /* --url */ p = strstr(arg, "://"); if (p) { if (strncasecmp(arg, "http://", 7) && strncasecmp(arg, "https://", 8) && strncasecmp(arg, "stratum+tcp://", 14)) show_usage_and_exit(1); free(rpc_url); rpc_url = strdup(arg); short_url = &rpc_url[(p - arg) + 3]; } else { if (!strlen(arg) || *arg == '/') show_usage_and_exit(1); free(rpc_url); rpc_url = (char*)malloc(strlen(arg) + 8); sprintf(rpc_url, "http://%s", arg); short_url = &rpc_url[7]; } p = strrchr(rpc_url, '@'); if (p) { char *sp, *ap; *p = '\0'; ap = strstr(rpc_url, "://") + 3; sp = strchr(ap, ':'); if (sp) { free(rpc_userpass); rpc_userpass = strdup(ap); free(rpc_user); rpc_user = (char*)calloc(sp - ap + 1, 1); strncpy(rpc_user, ap, sp - ap); free(rpc_pass); rpc_pass = strdup(sp + 1); } else { free(rpc_user); rpc_user = strdup(ap); } memmove(ap, p + 1, strlen(p + 1) + 1); short_url = p + 1; } have_stratum = !opt_benchmark && !strncasecmp(rpc_url, "stratum", 7); break; case 'O': /* --userpass */ p = strchr(arg, ':'); if (!p) show_usage_and_exit(1); free(rpc_userpass); rpc_userpass = strdup(arg); free(rpc_user); rpc_user = (char*)calloc(p - arg + 1, 1); strncpy(rpc_user, arg, p - arg); free(rpc_pass); rpc_pass = strdup(p + 1); break; case 'x': /* --proxy */ if (!strncasecmp(arg, "socks4://", 9)) opt_proxy_type = CURLPROXY_SOCKS4; else if (!strncasecmp(arg, "socks5://", 9)) opt_proxy_type = CURLPROXY_SOCKS5; #if LIBCURL_VERSION_NUM >= 0x071200 else if (!strncasecmp(arg, "socks4a://", 10)) opt_proxy_type = CURLPROXY_SOCKS4A; else if (!strncasecmp(arg, "socks5h://", 10)) opt_proxy_type = CURLPROXY_SOCKS5_HOSTNAME; #endif else opt_proxy_type = CURLPROXY_HTTP; free(opt_proxy); opt_proxy = strdup(arg); break; case 1001: free(opt_cert); opt_cert = strdup(arg); break; case 1005: opt_benchmark = true; want_longpoll = false; want_stratum = false; have_stratum = false; break; case 1006: print_hash_tests(); proper_exit(0); break; case 1003: want_longpoll = false; break; case 1007: want_stratum = false; break; case 'S': use_syslog = true; break; case 'd': // CB { char * pch = strtok (arg,","); opt_n_threads = 0; while (pch != NULL) { if (pch[0] >= '0' && pch[0] <= '9' && pch[1] == '\0') { if (atoi(pch) < num_processors) device_map[opt_n_threads++] = atoi(pch); else { applog(LOG_ERR, "Non-existant CUDA device #%d specified in -d option", atoi(pch)); proper_exit(1); } } else { int device = cuda_finddevice(pch); if (device >= 0 && device < num_processors) device_map[opt_n_threads++] = device; else { applog(LOG_ERR, "Non-existant CUDA device '%s' specified in -d option", pch); proper_exit(1); } } pch = strtok (NULL, ","); } } break; case 'f': // CH - Divisor for Difficulty d = atof(arg); if (d == 0) /* sanity check */ show_usage_and_exit(1); opt_difficulty = d; break; case 'V': show_version_and_exit(); case 'h': show_usage_and_exit(0); default: show_usage_and_exit(1); } if (use_syslog) use_colors = false; } static void parse_config(void) { int i; json_t *val; if (!json_is_object(opt_config)) return; for (i = 0; i < ARRAY_SIZE(options); i++) { if (!options[i].name) break; if (!strcmp(options[i].name, "config")) continue; val = json_object_get(opt_config, options[i].name); if (!val) continue; if (options[i].has_arg && json_is_string(val)) { char *s = strdup(json_string_value(val)); if (!s) break; parse_arg(options[i].val, s); free(s); } else if (!options[i].has_arg && json_is_true(val)) parse_arg(options[i].val, ""); else applog(LOG_ERR, "JSON option %s invalid", options[i].name); } if (opt_algo == ALGO_HEAVY && opt_vote == 9999) { fprintf(stderr, "Heavycoin hash requires block reward vote parameter (see --vote)\n"); show_usage_and_exit(1); } } static void parse_cmdline(int argc, char *argv[]) { int key; while (1) { #if HAVE_GETOPT_LONG key = getopt_long(argc, argv, short_options, options, NULL); #else key = getopt(argc, argv, short_options); #endif if (key < 0) break; parse_arg(key, optarg); } if (optind < argc) { fprintf(stderr, "%s: unsupported non-option argument '%s'\n", argv[0], argv[optind]); show_usage_and_exit(1); } if (opt_algo == ALGO_HEAVY && opt_vote == 9999) { fprintf(stderr, "%s: Heavycoin hash requires block reward vote parameter (see --vote)\n", argv[0]); show_usage_and_exit(1); } parse_config(); } #ifndef WIN32 static void signal_handler(int sig) { switch (sig) { case SIGHUP: applog(LOG_INFO, "SIGHUP received"); break; case SIGINT: signal(sig, SIG_IGN); applog(LOG_INFO, "SIGINT received, exiting"); proper_exit(0); break; case SIGTERM: applog(LOG_INFO, "SIGTERM received, exiting"); proper_exit(0); break; } } #else BOOL WINAPI ConsoleHandler(DWORD dwType) { switch (dwType) { case CTRL_C_EVENT: applog(LOG_INFO, "CTRL_C_EVENT received, exiting"); proper_exit(0); break; case CTRL_BREAK_EVENT: applog(LOG_INFO, "CTRL_BREAK_EVENT received, exiting"); proper_exit(0); break; default: return false; } return true; } #endif int main(int argc, char *argv[]) { struct thr_info *thr; long flags; int i; printf("*** ccMiner for nVidia GPUs by Christian Buchner and Christian H. ***\n"); printf("\t This is the forked version "PROGRAM_VERSION" (tpruvot@github)\n"); #ifdef WIN32 printf("\t Built with VC++ 2013 and nVidia CUDA SDK 6.5\n\n"); #else printf("\t Built with the nVidia CUDA SDK 6.5\n\n"); #endif printf("\t based on pooler-cpuminer 2.3.2 (c) 2010 Jeff Garzik, 2012 pooler\n"); printf("\t and HVC extension from http://hvc.1gh.com/" "\n\n"); printf("\tCuda additions Copyright 2014 Christian Buchner, Christian H.\n\n"); printf("\tInclude some of djm34 additions, cleaned by Tanguy Pruvot\n"); printf("\t BTC donation address: 1AJdfCpLWPNoAMDfHF1wD5y8VgKSSTHxPo\n\n"); rpc_user = strdup(""); rpc_pass = strdup(""); pthread_mutex_init(&applog_lock, NULL); num_processors = cuda_num_devices(); /* parse command line */ parse_cmdline(argc, argv); cuda_devicenames(); if (!opt_benchmark && !rpc_url) { fprintf(stderr, "%s: no URL supplied\n", argv[0]); show_usage_and_exit(1); } if (!rpc_userpass) { rpc_userpass = (char*)malloc(strlen(rpc_user) + strlen(rpc_pass) + 2); if (!rpc_userpass) return 1; sprintf(rpc_userpass, "%s:%s", rpc_user, rpc_pass); } /* init stratum data.. */ memset(&stratum.url, 0, sizeof(stratum)); pthread_mutex_init(&stats_lock, NULL); pthread_mutex_init(&g_work_lock, NULL); pthread_mutex_init(&stratum.sock_lock, NULL); pthread_mutex_init(&stratum.work_lock, NULL); flags = !opt_benchmark && strncmp(rpc_url, "https:", 6) ? (CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL) : CURL_GLOBAL_ALL; if (curl_global_init(flags)) { applog(LOG_ERR, "CURL initialization failed"); return 1; } #ifndef WIN32 if (opt_background) { i = fork(); if (i < 0) exit(1); if (i > 0) exit(0); i = setsid(); if (i < 0) applog(LOG_ERR, "setsid() failed (errno = %d)", errno); i = chdir("/"); if (i < 0) applog(LOG_ERR, "chdir() failed (errno = %d)", errno); signal(SIGHUP, signal_handler); signal(SIGTERM, signal_handler); } /* Always catch Ctrl+C */ signal(SIGINT, signal_handler); #else SetConsoleCtrlHandler((PHANDLER_ROUTINE)ConsoleHandler, TRUE); #endif if (num_processors == 0) { applog(LOG_ERR, "No CUDA devices found! terminating."); exit(1); } if (!opt_n_threads) opt_n_threads = num_processors; #ifdef HAVE_SYSLOG_H if (use_syslog) openlog("cpuminer", LOG_PID, LOG_USER); #endif work_restart = (struct work_restart *)calloc(opt_n_threads, sizeof(*work_restart)); if (!work_restart) return 1; thr_info = (struct 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; /* init workio thread info */ work_thr_id = opt_n_threads; thr = &thr_info[work_thr_id]; thr->id = work_thr_id; thr->q = tq_new(); if (!thr->q) return 1; /* start work I/O thread */ if (pthread_create(&thr->pth, NULL, workio_thread, thr)) { applog(LOG_ERR, "workio thread create failed"); return 1; } if (want_longpoll && !have_stratum) { /* init longpoll thread info */ longpoll_thr_id = opt_n_threads + 1; thr = &thr_info[longpoll_thr_id]; thr->id = longpoll_thr_id; thr->q = tq_new(); if (!thr->q) return 1; /* start longpoll thread */ if (unlikely(pthread_create(&thr->pth, NULL, longpoll_thread, thr))) { applog(LOG_ERR, "longpoll thread create failed"); return 1; } } if (want_stratum) { /* init stratum thread info */ stratum_thr_id = opt_n_threads + 2; thr = &thr_info[stratum_thr_id]; thr->id = stratum_thr_id; thr->q = tq_new(); if (!thr->q) return 1; /* start stratum thread */ if (unlikely(pthread_create(&thr->pth, NULL, stratum_thread, thr))) { applog(LOG_ERR, "stratum thread create failed"); return 1; } if (have_stratum) tq_push(thr_info[stratum_thr_id].q, strdup(rpc_url)); } /* start mining threads */ for (i = 0; i < opt_n_threads; i++) { thr = &thr_info[i]; thr->id = i; thr->q = tq_new(); if (!thr->q) return 1; if (unlikely(pthread_create(&thr->pth, NULL, miner_thread, thr))) { applog(LOG_ERR, "thread %d create failed", i); return 1; } } applog(LOG_INFO, "%d miner threads started, " "using '%s' algorithm.", opt_n_threads, algo_names[opt_algo]); #ifdef WIN32 timeBeginPeriod(1); // enable high timer precision (similar to Google Chrome Trick) #endif /* main loop - simply wait for workio thread to exit */ pthread_join(thr_info[work_thr_id].pth, NULL); #ifdef WIN32 timeEndPeriod(1); // be nice and forego high timer precision #endif applog(LOG_INFO, "workio thread dead, exiting."); return 0; }