9847 lines (8462 sloc) 260 KB
/*
* Copyright 2011-2015 Con Kolivas
* Copyright 2011-2015 Andrew Smith
* Copyright 2011-2012 Luke Dashjr
* Copyright 2010 Jeff Garzik
*
* 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 3 of the License, or (at your option)
* any later version. See COPYING for more details.
*/
#include "config.h"
#ifdef HAVE_CURSES
#include <curses.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <math.h>
#include <stdarg.h>
#include <assert.h>
#include <signal.h>
#include <limits.h>
#ifdef USE_USBUTILS
#include <semaphore.h>
#endif
#ifdef USE_LIBSYSTEMD
#include <systemd/sd-daemon.h>
#endif
#include <sys/stat.h>
#include <sys/types.h>
#ifndef WIN32
#include <sys/resource.h>
#else
#include <windows.h>
#endif
#include <ccan/opt/opt.h>
#include <jansson.h>
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#else
char *curly = ":D";
#endif
#include <libgen.h>
#include <sha2.h>
#include "compat.h"
#include "miner.h"
#include "bench_block.h"
#ifdef USE_USBUTILS
#include "usbutils.h"
#endif
#if defined(unix) || defined(__APPLE__)
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
#endif
#ifdef USE_AVALON
#include "driver-avalon.h"
#endif
#ifdef USE_AVALON2
#include "driver-avalon2.h"
#endif
#ifdef USE_AVALON4
#include "driver-avalon4.h"
#endif
#ifdef USE_AVALON_MINER
#include "driver-avalon-miner.h"
#endif
#ifdef USE_BFLSC
#include "driver-bflsc.h"
#endif
#ifdef USE_SP10
#include "driver-spondoolies-sp10.h"
#endif
#ifdef USE_SP30
#include "driver-spondoolies-sp30.h"
#endif
#ifdef USE_BLOCK_ERUPTER
#include "driver-blockerupter.h"
#endif
#ifdef USE_BITFURY
#include "driver-bitfury.h"
#endif
#ifdef USE_COINTERRA
#include "driver-cointerra.h"
#endif
#ifdef USE_HASHFAST
#include "driver-hashfast.h"
#endif
#if defined(USE_ANT_S1) || defined(USE_ANT_S2) || defined(USE_ANT_S3)
#include "driver-bitmain.h"
#endif
#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_AVALON) || defined(USE_AVALON2) || defined(USE_MODMINER)
# define USE_FPGA
#endif
struct strategies strategies[] = {
{ "Failover" },
{ "Round Robin" },
{ "Rotate" },
{ "Load Balance" },
{ "Balance" },
};
static char packagename[256];
bool opt_work_update;
bool opt_protocol;
static struct benchfile_layout {
int length;
char *name;
} benchfile_data[] = {
{ 1, "Version" },
{ 64, "MerkleRoot" },
{ 64, "PrevHash" },
{ 8, "DifficultyBits" },
{ 10, "NonceTime" } // 10 digits
};
enum benchwork {
BENCHWORK_VERSION = 0,
BENCHWORK_MERKLEROOT,
BENCHWORK_PREVHASH,
BENCHWORK_DIFFBITS,
BENCHWORK_NONCETIME,
BENCHWORK_COUNT
};
#ifdef HAVE_LIBCURL
static char *opt_btc_address;
static char *opt_btc_sig;
#endif
static char *opt_benchfile;
static bool opt_benchfile_display;
static FILE *benchfile_in;
static int benchfile_line;
static int benchfile_work;
static bool opt_benchmark;
bool have_longpoll;
bool want_per_device_stats;
bool use_syslog;
bool opt_quiet;
bool opt_realquiet;
bool opt_loginput;
bool opt_compact;
const int opt_cutofftemp = 95;
int opt_log_interval = 5;
static const int max_queue = 1;
const int max_scantime = 60;
const int max_expiry = 600;
unsigned long long global_hashrate;
unsigned long global_quota_gcd = 1;
time_t last_getwork;
int opt_pool_fallback = 120;
#if defined(USE_USBUTILS)
int nDevs;
#endif
bool opt_restart = true;
bool opt_nogpu;
struct list_head scan_devices;
static bool opt_display_devs;
int total_devices;
int zombie_devs;
static int most_devices;
struct cgpu_info **devices;
int mining_threads;
int num_processors;
#ifdef HAVE_CURSES
bool use_curses = true;
#else
bool use_curses;
#endif
static bool opt_widescreen;
static bool alt_status;
static bool switch_status;
static bool opt_submit_stale = true;
static int opt_shares;
static bool opt_fix_protocol;
bool opt_lowmem;
bool opt_autofan;
bool opt_autoengine;
bool opt_noadl;
char *opt_api_allow = NULL;
char *opt_api_groups;
char *opt_api_description = PACKAGE_STRING;
int opt_api_port = 4028;
char *opt_api_host = API_LISTEN_ADDR;
bool opt_api_listen;
bool opt_api_mcast;
char *opt_api_mcast_addr = API_MCAST_ADDR;
char *opt_api_mcast_code = API_MCAST_CODE;
char *opt_api_mcast_des = "";
int opt_api_mcast_port = 4028;
bool opt_api_network;
bool opt_delaynet;
bool opt_disable_pool;
static bool no_work;
#ifdef USE_ICARUS
char *opt_icarus_options = NULL;
char *opt_icarus_timing = NULL;
float opt_anu_freq = 250;
float opt_au3_freq = 225;
int opt_au3_volt = 775;
float opt_rock_freq = 270;
#endif
bool opt_worktime;
#ifdef USE_AVALON
char *opt_avalon_options;
char *opt_bitburner_fury_options;
static char *opt_set_avalon_fan;
static char *opt_set_avalon_freq;
#endif
#ifdef USE_AVALON2
static char *opt_set_avalon2_freq;
static char *opt_set_avalon2_fan;
static char *opt_set_avalon2_voltage;
#endif
#ifdef USE_AVALON4
static char *opt_set_avalon4_fan;
static char *opt_set_avalon4_voltage;
static char *opt_set_avalon4_freq;
#endif
#ifdef USE_AVALON_MINER
static char *opt_set_avalonm_voltage;
static char *opt_set_avalonm_freq;
#endif
#ifdef USE_BLOCKERUPTER
int opt_bet_clk = 0;
#endif
#ifdef USE_HASHRATIO
#include "driver-hashratio.h"
#endif
#ifdef USE_KLONDIKE
char *opt_klondike_options = NULL;
#endif
#ifdef USE_DRILLBIT
char *opt_drillbit_options = NULL;
char *opt_drillbit_auto = NULL;
#endif
char *opt_bab_options = NULL;
#ifdef USE_BITMINE_A1
char *opt_bitmine_a1_options = NULL;
#endif
#if defined(USE_ANT_S1) || defined(USE_ANT_S2)
char *opt_bitmain_options;
static char *opt_set_bitmain_fan;
char *opt_bitmain_freq;
// Ignored
static bool opt_bitmain_nobeeper;
static bool opt_bitmain_notempoverctrl;
static bool opt_bitmain_homemode;
#endif
#ifdef USE_ANT_S2
#ifndef USE_ANT_S3
char *opt_bitmain_dev;
#endif
char *opt_bitmain_voltage = BITMAIN_VOLTAGE_DEF;
#endif
#ifdef USE_HASHFAST
static char *opt_set_hfa_fan;
#endif
static char *opt_set_null;
#ifdef USE_MINION
int opt_minion_chipreport;
char *opt_minion_cores;
bool opt_minion_extra;
char *opt_minion_freq;
int opt_minion_freqchange = 1000;
int opt_minion_freqpercent = 70;
bool opt_minion_idlecount;
int opt_minion_ledcount;
int opt_minion_ledlimit = 98;
bool opt_minion_noautofreq;
bool opt_minion_overheat;
int opt_minion_spidelay;
char *opt_minion_spireset;
int opt_minion_spisleep = 200;
int opt_minion_spiusec;
char *opt_minion_temp;
#endif
#ifdef USE_USBUTILS
char *opt_usb_select = NULL;
int opt_usbdump = -1;
bool opt_usb_list_all;
cgsem_t usb_resource_sem;
static pthread_t usb_poll_thread;
static bool usb_polling;
#endif
char *opt_kernel_path;
char *cgminer_path;
#if defined(USE_BITFORCE)
bool opt_bfl_noncerange;
#endif
#define QUIET (opt_quiet || opt_realquiet)
struct thr_info *control_thr;
struct thr_info **mining_thr;
static int gwsched_thr_id;
static int watchpool_thr_id;
static int watchdog_thr_id;
#ifdef HAVE_CURSES
static int input_thr_id;
#endif
int gpur_thr_id;
static int api_thr_id;
#ifdef USE_USBUTILS
static int usbres_thr_id;
static int hotplug_thr_id;
#endif
static int total_control_threads;
bool hotplug_mode;
static int new_devices;
static int new_threads;
int hotplug_time = 5;
#if LOCK_TRACKING
pthread_mutex_t lockstat_lock;
#endif
pthread_mutex_t hash_lock;
static pthread_mutex_t *stgd_lock;
pthread_mutex_t console_lock;
cglock_t ch_lock;
static pthread_rwlock_t blk_lock;
static pthread_mutex_t sshare_lock;
pthread_rwlock_t netacc_lock;
pthread_rwlock_t mining_thr_lock;
pthread_rwlock_t devices_lock;
static pthread_mutex_t lp_lock;
static pthread_cond_t lp_cond;
pthread_mutex_t restart_lock;
pthread_cond_t restart_cond;
pthread_cond_t gws_cond;
double rolling1, rolling5, rolling15;
double total_rolling;
double total_mhashes_done;
static struct timeval total_tv_start, total_tv_end;
static struct timeval restart_tv_start, update_tv_start;
cglock_t control_lock;
pthread_mutex_t stats_lock;
int hw_errors;
int64_t total_accepted, total_rejected, total_diff1;
int64_t total_getworks, total_stale, total_discarded;
double total_diff_accepted, total_diff_rejected, total_diff_stale;
static int staged_rollable;
unsigned int new_blocks;
static unsigned int work_block;
unsigned int found_blocks;
unsigned int local_work;
unsigned int total_go, total_ro;
struct pool **pools;
static struct pool *currentpool = NULL;
int total_pools, enabled_pools;
enum pool_strategy pool_strategy = POOL_FAILOVER;
int opt_rotate_period;
static int total_urls, total_users, total_passes, total_userpasses;
static
#ifndef HAVE_CURSES
const
#endif
bool curses_active;
/* Protected by ch_lock */
char current_hash[68];
static char prev_block[12];
static char current_block[32];
static char datestamp[40];
static char blocktime[32];
struct timeval block_timeval;
static char best_share[8] = "0";
double current_diff = 0xFFFFFFFFFFFFFFFFULL;
static char block_diff[8];
uint64_t best_diff = 0;
struct block {
char hash[68];
UT_hash_handle hh;
int block_no;
};
static struct block *blocks = NULL;
int swork_id;
/* For creating a hash database of stratum shares submitted that have not had
* a response yet */
struct stratum_share {
UT_hash_handle hh;
bool block;
struct work *work;
int id;
time_t sshare_time;
time_t sshare_sent;
};
static struct stratum_share *stratum_shares = NULL;
char *opt_socks_proxy = NULL;
int opt_suggest_diff;
static const char def_conf[] = "cgminer.conf";
static char *default_config;
static bool config_loaded;
static int include_count;
#define JSON_INCLUDE_CONF "include"
#define JSON_LOAD_ERROR "JSON decode of file '%s' failed\n %s"
#define JSON_LOAD_ERROR_LEN strlen(JSON_LOAD_ERROR)
#define JSON_MAX_DEPTH 10
#define JSON_MAX_DEPTH_ERR "Too many levels of JSON includes (limit 10) or a loop"
#define JSON_WEB_ERROR "WEB config err"
#if defined(unix) || defined(__APPLE__)
static char *opt_stderr_cmd = NULL;
static int forkpid;
#endif // defined(unix)
struct sigaction termhandler, inthandler, abrthandler;
struct thread_q *getq;
static uint32_t total_work;
struct work *staged_work = NULL;
struct schedtime {
bool enable;
struct tm tm;
};
struct schedtime schedstart;
struct schedtime schedstop;
bool sched_paused;
static bool time_before(struct tm *tm1, struct tm *tm2)
{
if (tm1->tm_hour < tm2->tm_hour)
return true;
if (tm1->tm_hour == tm2->tm_hour && tm1->tm_min < tm2->tm_min)
return true;
return false;
}
static bool should_run(void)
{
struct timeval tv;
struct tm *tm;
if (!schedstart.enable && !schedstop.enable)
return true;
cgtime(&tv);
const time_t tmp_time = tv.tv_sec;
tm = localtime(&tmp_time);
if (schedstart.enable) {
if (!schedstop.enable) {
if (time_before(tm, &schedstart.tm))
return false;
/* This is a once off event with no stop time set */
schedstart.enable = false;
return true;
}
if (time_before(&schedstart.tm, &schedstop.tm)) {
if (time_before(tm, &schedstop.tm) && !time_before(tm, &schedstart.tm))
return true;
return false;
} /* Times are reversed */
if (time_before(tm, &schedstart.tm)) {
if (time_before(tm, &schedstop.tm))
return true;
return false;
}
return true;
}
/* only schedstop.enable == true */
if (!time_before(tm, &schedstop.tm))
return false;
return true;
}
void get_datestamp(char *f, size_t fsiz, struct timeval *tv)
{
struct tm *tm;
const time_t tmp_time = tv->tv_sec;
int ms = (int)(tv->tv_usec / 1000);
tm = localtime(&tmp_time);
snprintf(f, fsiz, "[%d-%02d-%02d %02d:%02d:%02d.%03d]",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec, ms);
}
static void get_timestamp(char *f, size_t fsiz, struct timeval *tv)
{
struct tm *tm;
const time_t tmp_time = tv->tv_sec;
int ms = (int)(tv->tv_usec / 1000);
tm = localtime(&tmp_time);
snprintf(f, fsiz, "[%02d:%02d:%02d.%03d]",
tm->tm_hour,
tm->tm_min,
tm->tm_sec, ms);
}
static char exit_buf[512];
static void applog_and_exit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(exit_buf, sizeof(exit_buf), fmt, ap);
va_end(ap);
_applog(LOG_ERR, exit_buf, true);
exit(1);
}
static pthread_mutex_t sharelog_lock;
static FILE *sharelog_file = NULL;
static struct thr_info *__get_thread(int thr_id)
{
return mining_thr[thr_id];
}
struct thr_info *get_thread(int thr_id)
{
struct thr_info *thr;
rd_lock(&mining_thr_lock);
thr = __get_thread(thr_id);
rd_unlock(&mining_thr_lock);
return thr;
}
static struct cgpu_info *get_thr_cgpu(int thr_id)
{
struct thr_info *thr = get_thread(thr_id);
return thr->cgpu;
}
struct cgpu_info *get_devices(int id)
{
struct cgpu_info *cgpu;
rd_lock(&devices_lock);
cgpu = devices[id];
rd_unlock(&devices_lock);
return cgpu;
}
static void sharelog(const char*disposition, const struct work*work)
{
char *target, *hash, *data;
struct cgpu_info *cgpu;
unsigned long int t;
struct pool *pool;
int thr_id, rv;
char s[1024];
size_t ret;
if (!sharelog_file)
return;
thr_id = work->thr_id;
cgpu = get_thr_cgpu(thr_id);
pool = work->pool;
t = (unsigned long int)(work->tv_work_found.tv_sec);
target = bin2hex(work->target, sizeof(work->target));
hash = bin2hex(work->hash, sizeof(work->hash));
data = bin2hex(work->data, sizeof(work->data));
// timestamp,disposition,target,pool,dev,thr,sharehash,sharedata
rv = snprintf(s, sizeof(s), "%lu,%s,%s,%s,%s%u,%u,%s,%s\n", t, disposition, target, pool->rpc_url, cgpu->drv->name, cgpu->device_id, thr_id, hash, data);
free(target);
free(hash);
free(data);
if (rv >= (int)(sizeof(s)))
s[sizeof(s) - 1] = '\0';
else if (rv < 0) {
applog(LOG_ERR, "sharelog printf error");
return;
}
mutex_lock(&sharelog_lock);
ret = fwrite(s, rv, 1, sharelog_file);
fflush(sharelog_file);
mutex_unlock(&sharelog_lock);
if (ret != 1)
applog(LOG_ERR, "sharelog fwrite error");
}
static char *gbt_req = "{\"id\": 0, \"method\": \"getblocktemplate\", \"params\": [{\"capabilities\": [\"coinbasetxn\", \"workid\", \"coinbase/append\"]}]}\n";
static char *gbt_solo_req = "{\"id\": 0, \"method\": \"getblocktemplate\"}\n";
/* Adjust all the pools' quota to the greatest common denominator after a pool
* has been added or the quotas changed. */
void adjust_quota_gcd(void)
{
unsigned long gcd, lowest_quota = ~0UL, quota;
struct pool *pool;
int i;
for (i = 0; i < total_pools; i++) {
pool = pools[i];
quota = pool->quota;
if (!quota)
continue;
if (quota < lowest_quota)
lowest_quota = quota;
}
if (likely(lowest_quota < ~0UL)) {
gcd = lowest_quota;
for (i = 0; i < total_pools; i++) {
pool = pools[i];
quota = pool->quota;
if (!quota)
continue;
while (quota % gcd)
gcd--;
}
} else
gcd = 1;
for (i = 0; i < total_pools; i++) {
pool = pools[i];
pool->quota_used *= global_quota_gcd;
pool->quota_used /= gcd;
pool->quota_gcd = pool->quota / gcd;
}
global_quota_gcd = gcd;
applog(LOG_DEBUG, "Global quota greatest common denominator set to %lu", gcd);
}
/* Return value is ignored if not called from input_pool */
struct pool *add_pool(void)
{
struct pool *pool;
pool = cgcalloc(sizeof(struct pool), 1);
pool->pool_no = pool->prio = total_pools;
pools = cgrealloc(pools, sizeof(struct pool *) * (total_pools + 2));
pools[total_pools++] = pool;
mutex_init(&pool->pool_lock);
if (unlikely(pthread_cond_init(&pool->cr_cond, NULL)))
quit(1, "Failed to pthread_cond_init in add_pool");
cglock_init(&pool->data_lock);
mutex_init(&pool->stratum_lock);
cglock_init(&pool->gbt_lock);
INIT_LIST_HEAD(&pool->curlring);
/* Make sure the pool doesn't think we've been idle since time 0 */
pool->tv_idle.tv_sec = ~0UL;
pool->rpc_req = gbt_req;
pool->rpc_proxy = NULL;
pool->quota = 1;
adjust_quota_gcd();
return pool;
}
/* Pool variant of test and set */
static bool pool_tset(struct pool *pool, bool *var)
{
bool ret;
mutex_lock(&pool->pool_lock);
ret = *var;
*var = true;
mutex_unlock(&pool->pool_lock);
return ret;
}
bool pool_tclear(struct pool *pool, bool *var)
{
bool ret;
mutex_lock(&pool->pool_lock);
ret = *var;
*var = false;
mutex_unlock(&pool->pool_lock);
return ret;
}
struct pool *current_pool(void)
{
struct pool *pool;
cg_rlock(&control_lock);
pool = currentpool;
cg_runlock(&control_lock);
return pool;
}
char *set_int_range(const char *arg, int *i, int min, int max)
{
char *err = opt_set_intval(arg, i);
if (err)
return err;
if (*i < min || *i > max)
return "Value out of range";
return NULL;
}
static char *set_int_0_to_9999(const char *arg, int *i)
{
return set_int_range(arg, i, 0, 9999);
}
static char *set_int_1_to_65535(const char *arg, int *i)
{
return set_int_range(arg, i, 1, 65535);
}
static char *set_int_0_to_10(const char *arg, int *i)
{
return set_int_range(arg, i, 0, 10);
}
static char *set_int_0_to_100(const char *arg, int *i)
{
return set_int_range(arg, i, 0, 100);
}
static char *set_int_0_to_255(const char *arg, int *i)
{
return set_int_range(arg, i, 0, 255);
}
static char *set_int_1_to_255(const char *arg, int *i)
{
return set_int_range(arg, i, 1, 255);
}
static char *set_int_0_to_7680(const char *arg, int *i)
{
return set_int_range(arg, i, 0, 7680);
}
static char *set_int_0_to_200(const char *arg, int *i)
{
return set_int_range(arg, i, 0, 200);
}
static char *set_int_32_to_63(const char *arg, int *i)
{
return set_int_range(arg, i, 32, 63);
}
static char *set_int_22_to_75(const char *arg, int *i)
{
return set_int_range(arg, i, 22, 75);
}
static char *set_int_42_to_85(const char *arg, int *i)
{
return set_int_range(arg, i, 42, 85);
}
static char *set_int_1_to_10(const char *arg, int *i)
{
return set_int_range(arg, i, 1, 10);
}
static char __maybe_unused *set_int_0_to_4(const char *arg, int *i)
{
return set_int_range(arg, i, 0, 4);
}
#ifdef USE_FPGA_SERIAL
static char *opt_add_serial;
static char *add_serial(char *arg)
{
string_elist_add(arg, &scan_devices);
return NULL;
}
#endif
void get_intrange(char *arg, int *val1, int *val2)
{
if (sscanf(arg, "%d-%d", val1, val2) == 1)
*val2 = *val1;
}
static char *set_balance(enum pool_strategy *strategy)
{
*strategy = POOL_BALANCE;
return NULL;
}
static char *set_loadbalance(enum pool_strategy *strategy)
{
*strategy = POOL_LOADBALANCE;
return NULL;
}
static char *set_rotate(const char *arg, char __maybe_unused *i)
{
pool_strategy = POOL_ROTATE;
return set_int_range(arg, &opt_rotate_period, 0, 9999);
}
static char *set_rr(enum pool_strategy *strategy)
{
*strategy = POOL_ROUNDROBIN;
return NULL;
}
/* Detect that url is for a stratum protocol either via the presence of
* stratum+tcp or by detecting a stratum server response */
bool detect_stratum(struct pool *pool, char *url)
{
if (!extract_sockaddr(url, &pool->sockaddr_url, &pool->stratum_port))
return false;
if (!strncasecmp(url, "stratum+tcp://", 14)) {
pool->rpc_url = strdup(url);
pool->has_stratum = true;
pool->stratum_url = pool->sockaddr_url;
return true;
}
return false;
}
static struct pool *add_url(void)
{
total_urls++;
if (total_urls > total_pools)
add_pool();
return pools[total_urls - 1];
}
static char *setup_url(struct pool *pool, char *arg)
{
arg = get_proxy(arg, pool);
if (detect_stratum(pool, arg))
goto out;
opt_set_charp(arg, &pool->rpc_url);
if (strncmp(arg, "http://", 7) &&
strncmp(arg, "https://", 8)) {
char *httpinput;
httpinput = cgmalloc(256);
strcpy(httpinput, "stratum+tcp://");
strncat(httpinput, arg, 242);
detect_stratum(pool, httpinput);
}
out:
return pool->rpc_url;
}
static char *set_url(char *arg)
{
struct pool *pool = add_url();
setup_url(pool, arg);
return NULL;
}
static char *set_quota(char *arg)
{
char *semicolon = strchr(arg, ';'), *url;
int len, qlen, quota;
struct pool *pool;
if (!semicolon)
return "No semicolon separated quota;URL pair found";
len = strlen(arg);
*semicolon = '\0';
qlen = strlen(arg);
if (!qlen)
return "No parameter for quota found";
len -= qlen + 1;
if (len < 1)
return "No parameter for URL found";
quota = atoi(arg);
if (quota < 0)
return "Invalid negative parameter for quota set";
url = arg + qlen + 1;
pool = add_url();
setup_url(pool, url);
pool->quota = quota;
applog(LOG_INFO, "Setting pool %d to quota %d", pool->pool_no, pool->quota);
adjust_quota_gcd();
return NULL;
}
static char *set_user(const char *arg)
{
struct pool *pool;
if (total_userpasses)
return "Use only user + pass or userpass, but not both";
total_users++;
if (total_users > total_pools)
add_pool();
pool = pools[total_users - 1];
opt_set_charp(arg, &pool->rpc_user);
return NULL;
}
static char *set_pass(const char *arg)
{
struct pool *pool;
if (total_userpasses)
return "Use only user + pass or userpass, but not both";
total_passes++;
if (total_passes > total_pools)
add_pool();
pool = pools[total_passes - 1];
opt_set_charp(arg, &pool->rpc_pass);
return NULL;
}
static char *set_userpass(const char *arg)
{
struct pool *pool;
char *updup;
if (total_users || total_passes)
return "Use only user + pass or userpass, but not both";
total_userpasses++;
if (total_userpasses > total_pools)
add_pool();
pool = pools[total_userpasses - 1];
updup = strdup(arg);
opt_set_charp(arg, &pool->rpc_userpass);
pool->rpc_user = strtok(updup, ":");
if (!pool->rpc_user)
return "Failed to find : delimited user info";
pool->rpc_pass = strtok(NULL, ":");
if (!pool->rpc_pass)
pool->rpc_pass = strdup("");
return NULL;
}
static char *enable_debug(bool *flag)
{
*flag = true;
/* Turn on verbose output, too. */
opt_log_output = true;
return NULL;
}
static char *opt_set_sched_start;
static char *opt_set_sched_stop;
static char *set_schedtime(const char *arg, struct schedtime *st)
{
if (sscanf(arg, "%d:%d", &st->tm.tm_hour, &st->tm.tm_min) != 2)
return "Invalid time set, should be HH:MM";
if (st->tm.tm_hour > 23 || st->tm.tm_min > 59 || st->tm.tm_hour < 0 || st->tm.tm_min < 0)
return "Invalid time set.";
st->enable = true;
return NULL;
}
static char *set_sched_start(const char *arg)
{
return set_schedtime(arg, &schedstart);
}
static char *set_sched_stop(const char *arg)
{
return set_schedtime(arg, &schedstop);
}
static char *opt_set_sharelog;
static char* set_sharelog(char *arg)
{
char *r = "";
long int i = strtol(arg, &r, 10);
if ((!*r) && i >= 0 && i <= INT_MAX) {
sharelog_file = fdopen((int)i, "a");
if (!sharelog_file)
applog(LOG_ERR, "Failed to open fd %u for share log", (unsigned int)i);
} else if (!strcmp(arg, "-")) {
sharelog_file = stdout;
if (!sharelog_file)
applog(LOG_ERR, "Standard output missing for share log");
} else {
sharelog_file = fopen(arg, "a");
if (!sharelog_file)
applog(LOG_ERR, "Failed to open %s for share log", arg);
}
return NULL;
}
static char *temp_cutoff_str = NULL;
static char __maybe_unused *opt_set_temp_cutoff;
char *set_temp_cutoff(char *arg)
{
int val;
if (!(arg && arg[0]))
return "Invalid parameters for set temp cutoff";
val = atoi(arg);
if (val < 0 || val > 200)
return "Invalid value passed to set temp cutoff";
temp_cutoff_str = arg;
return NULL;
}
static void load_temp_cutoffs()
{
int i, val = 0, device = 0;
char *nextptr;
if (temp_cutoff_str) {
for (device = 0, nextptr = strtok(temp_cutoff_str, ","); nextptr; ++device, nextptr = strtok(NULL, ",")) {
if (device >= total_devices)
quit(1, "Too many values passed to set temp cutoff");
val = atoi(nextptr);
if (val < 0 || val > 200)
quit(1, "Invalid value passed to set temp cutoff");
rd_lock(&devices_lock);
devices[device]->cutofftemp = val;
rd_unlock(&devices_lock);
}
} else {
rd_lock(&devices_lock);
for (i = device; i < total_devices; ++i) {
if (!devices[i]->cutofftemp)
devices[i]->cutofftemp = opt_cutofftemp;
}
rd_unlock(&devices_lock);
return;
}
if (device <= 1) {
rd_lock(&devices_lock);
for (i = device; i < total_devices; ++i)
devices[i]->cutofftemp = val;
rd_unlock(&devices_lock);
}
}
static char *set_float_125_to_500(const char *arg, float *i)
{
char *err = opt_set_floatval(arg, i);
if (err)
return err;
if (*i < 125 || *i > 500)
return "Value out of range";
return NULL;
}
static char *set_float_100_to_250(const char *arg, float *i)
{
char *err = opt_set_floatval(arg, i);
if (err)
return err;
if (*i < 100 || *i > 250)
return "Value out of range";
return NULL;
}
static char *set_null(const char __maybe_unused *arg)
{
return NULL;
}
/* These options are available from config file or commandline */
static struct opt_table opt_config_table[] = {
#ifdef USE_ICARUS
OPT_WITH_ARG("--anu-freq",
set_float_125_to_500, &opt_show_floatval, &opt_anu_freq,
"Set AntminerU1/2 frequency in MHz, range 125-500"),
#endif
OPT_WITH_ARG("--api-allow",
opt_set_charp, NULL, &opt_api_allow,
"Allow API access only to the given list of [G:]IP[/Prefix] addresses[/subnets]"),
OPT_WITH_ARG("--api-description",
opt_set_charp, NULL, &opt_api_description,
"Description placed in the API status header, default: cgminer version"),
OPT_WITH_ARG("--api-groups",
opt_set_charp, NULL, &opt_api_groups,
"API one letter groups G:cmd:cmd[,P:cmd:*...] defining the cmds a groups can use"),
OPT_WITHOUT_ARG("--api-listen",
opt_set_bool, &opt_api_listen,
"Enable API, default: disabled"),
OPT_WITHOUT_ARG("--api-mcast",
opt_set_bool, &opt_api_mcast,
"Enable API Multicast listener, default: disabled"),
OPT_WITH_ARG("--api-mcast-addr",
opt_set_charp, NULL, &opt_api_mcast_addr,
"API Multicast listen address"),
OPT_WITH_ARG("--api-mcast-code",
opt_set_charp, NULL, &opt_api_mcast_code,
"Code expected in the API Multicast message, don't use '-'"),
OPT_WITH_ARG("--api-mcast-des",
opt_set_charp, NULL, &opt_api_mcast_des,
"Description appended to the API Multicast reply, default: ''"),
OPT_WITH_ARG("--api-mcast-port",
set_int_1_to_65535, opt_show_intval, &opt_api_mcast_port,
"API Multicast listen port"),
OPT_WITHOUT_ARG("--api-network",
opt_set_bool, &opt_api_network,
"Allow API (if enabled) to listen on/for any address, default: only 127.0.0.1"),
OPT_WITH_ARG("--api-port",
set_int_1_to_65535, opt_show_intval, &opt_api_port,
"Port number of miner API"),
OPT_WITH_ARG("--api-host",
opt_set_charp, NULL, &opt_api_host,
"Specify API listen address, default: 0.0.0.0"),
#ifdef USE_ICARUS
OPT_WITH_ARG("--au3-freq",
set_float_100_to_250, &opt_show_floatval, &opt_au3_freq,
"Set AntminerU3 frequency in MHz, range 100-250"),
OPT_WITH_ARG("--au3-volt",
set_int_0_to_9999, &opt_show_intval, &opt_au3_volt,
"Set AntminerU3 voltage in mv, range 725-850, 0 to not set"),
#endif
#ifdef USE_AVALON
OPT_WITHOUT_ARG("--avalon-auto",
opt_set_bool, &opt_avalon_auto,
"Adjust avalon overclock frequency dynamically for best hashrate"),
OPT_WITH_ARG("--avalon-cutoff",
set_int_0_to_100, opt_show_intval, &opt_avalon_overheat,
"Set avalon overheat cut off temperature"),
OPT_WITH_CBARG("--avalon-fan",
set_avalon_fan, NULL, &opt_set_avalon_fan,
"Set fanspeed percentage for avalon, single value or range (default: 20-100)"),
OPT_WITH_CBARG("--avalon-freq",
set_avalon_freq, NULL, &opt_set_avalon_freq,
"Set frequency range for avalon-auto, single value or range"),
OPT_WITH_ARG("--avalon-options",
opt_set_charp, NULL, &opt_avalon_options,
"Set avalon options baud:miners:asic:timeout:freq:tech"),
OPT_WITH_ARG("--avalon-temp",
set_int_0_to_100, opt_show_intval, &opt_avalon_temp,
"Set avalon target temperature"),
#endif
#ifdef USE_AVALON2
OPT_WITH_CBARG("--avalon2-freq",
set_avalon2_freq, NULL, &opt_set_avalon2_freq,
"Set frequency range for Avalon2, single value or range, step: 25"),
OPT_WITH_CBARG("--avalon2-voltage",
set_avalon2_voltage, NULL, &opt_set_avalon2_voltage,
"Set Avalon2 core voltage, in millivolts, step: 125"),
OPT_WITH_CBARG("--avalon2-fan",
set_avalon2_fan, NULL, &opt_set_avalon2_fan,
"Set Avalon2 target fan speed"),
OPT_WITH_ARG("--avalon2-cutoff",
set_int_0_to_100, opt_show_intval, &opt_avalon2_overheat,
"Set Avalon2 overheat cut off temperature"),
OPT_WITHOUT_ARG("--avalon2-fixed-speed",
set_avalon2_fixed_speed, &opt_avalon2_fan_fixed,
"Set Avalon2 fan to fixed speed"),
OPT_WITH_ARG("--avalon2-polling-delay",
set_int_1_to_65535, opt_show_intval, &opt_avalon2_polling_delay,
"Set Avalon2 polling delay value (ms)"),
#endif
#ifdef USE_AVALON4
OPT_WITHOUT_ARG("--avalon4-automatic-voltage",
opt_set_bool, &opt_avalon4_autov,
"Automatic adjust voltage base on module DH"),
OPT_WITH_CBARG("--avalon4-voltage",
set_avalon4_voltage, NULL, &opt_set_avalon4_voltage,
"Set Avalon4 core voltage, in millivolts, step: 125"),
OPT_WITH_CBARG("--avalon4-freq",
set_avalon4_freq, NULL, &opt_set_avalon4_freq,
"Set frequency for Avalon4, 1 to 3 values, example: 445:385:370"),
OPT_WITH_CBARG("--avalon4-fan",
set_avalon4_fan, NULL, &opt_set_avalon4_fan,
"Set Avalon4 target fan speed range"),
OPT_WITH_ARG("--avalon4-temp",
set_int_22_to_75, opt_show_intval, &opt_avalon4_temp_target,
"Set Avalon4 target temperature"),
OPT_WITH_ARG("--avalon4-cutoff",
set_int_42_to_85, opt_show_intval, &opt_avalon4_overheat,
"Set Avalon4 overheat cut off temperature"),
OPT_WITH_ARG("--avalon4-polling-delay",
set_int_1_to_65535, opt_show_intval, &opt_avalon4_polling_delay,
"Set Avalon4 polling delay value (ms)"),
OPT_WITH_ARG("--avalon4-ntime-offset",
opt_set_intval, opt_show_intval, &opt_avalon4_ntime_offset,
"Set Avalon4 MM ntime rolling max offset"),
OPT_WITH_ARG("--avalon4-aucspeed",
opt_set_intval, opt_show_intval, &opt_avalon4_aucspeed,
"Set Avalon4 AUC IIC bus speed"),
OPT_WITH_ARG("--avalon4-aucxdelay",
opt_set_intval, opt_show_intval, &opt_avalon4_aucxdelay,
"Set Avalon4 AUC IIC xfer read delay, 4800 ~= 1ms"),
OPT_WITH_ARG("--avalon4-miningmode",
opt_set_intval, opt_show_intval, &opt_avalon4_miningmode,
"Set Avalon4 mining mode(0:custom, 1:eco, 2:normal, 3:turbo"),
OPT_WITHOUT_ARG("--avalon4-freezesafe",
opt_set_bool, &opt_avalon4_freezesafe,
"Make Avalon4 running as a radiator when stratum server failed"),
OPT_WITH_ARG("--avalon4-ntcb",
opt_set_intval, opt_show_intval, &opt_avalon4_ntcb,
"Set Avalon4 MM NTC B value"),
OPT_WITH_ARG("--avalon4-freq-min",
opt_set_intval, opt_show_intval, &opt_avalon4_freq_min,
"Set minimum frequency for Avalon4"),
OPT_WITH_ARG("--avalon4-freq-max",
opt_set_intval, opt_show_intval, &opt_avalon4_freq_max,
"Set maximum frequency for Avalon4"),
OPT_WITHOUT_ARG("--avalon4-noncecheck-off",
opt_set_invbool, &opt_avalon4_noncecheck,
"Disable A3218 inside nonce check function"),
OPT_WITH_ARG("--avalon4-smart-speed",
opt_set_intval, opt_show_intval, &opt_avalon4_smart_speed,
"Set smart speed, range 0-3. 0 means Disable"),
OPT_WITH_ARG("--avalon4-speed-bingo",
set_int_1_to_255, opt_show_intval, &opt_avalon4_speed_bingo,
"Set A3218 speed bingo for smart speed mode 1"),
OPT_WITH_ARG("--avalon4-speed-error",
set_int_1_to_255, opt_show_intval, &opt_avalon4_speed_error,
"Set A3218 speed error for smart speed mode 1"),
OPT_WITH_ARG("--avalon4-least-pll",
set_int_0_to_7680, opt_show_intval, &opt_avalon4_least_pll_check,
"Set least pll check threshold for smart speed mode 2"),
OPT_WITH_ARG("--avalon4-most-pll",
set_int_0_to_7680, opt_show_intval, &opt_avalon4_most_pll_check,
"Set most pll check threshold for smart speed mode 2"),
#endif
#ifdef USE_AVALON_MINER
OPT_WITH_CBARG("--avalonm-voltage",
set_avalonm_voltage, NULL, &opt_set_avalonm_voltage,
"Set Avalon miner core voltage, in millivolts, step: 125"),
OPT_WITH_CBARG("--avalonm-freq",
set_avalonm_freq, NULL, &opt_set_avalonm_freq,
"Set frequency for Avalon miner, 1 to 3 values, example: 275:250:200"),
OPT_WITH_ARG("--avalonm-ntime-offset",
opt_set_intval, opt_show_intval, &opt_avalonm_ntime_offset,
"Set Avalon miner ntime rolling max offset, range 0-4"),
OPT_WITH_ARG("--avalonm-spispeed",
opt_set_intval, opt_show_intval, &opt_avalonm_spispeed,
"Set spi speed for Avalon miner"),
OPT_WITHOUT_ARG("--avalonm-automatic-freq",
opt_set_bool, &opt_avalonm_autof,
"Automatic adjust frequency base on chip HW"),
#endif
#ifdef USE_BAB
OPT_WITH_ARG("--bab-options",
opt_set_charp, NULL, &opt_bab_options,
"Set bab options max:def:min:up:down:hz:delay:trf"),
#endif
OPT_WITHOUT_ARG("--balance",
set_balance, &pool_strategy,
"Change multipool strategy from failover to even share balance"),
OPT_WITH_ARG("--benchfile",
opt_set_charp, NULL, &opt_benchfile,
"Run cgminer in benchmark mode using a work file - produces no shares"),
OPT_WITHOUT_ARG("--benchfile-display",
opt_set_bool, &opt_benchfile_display,
"Display each benchfile nonce found"),
OPT_WITHOUT_ARG("--benchmark",
opt_set_bool, &opt_benchmark,
"Run cgminer in benchmark mode - produces no shares"),
#if defined(USE_BITFORCE)
OPT_WITHOUT_ARG("--bfl-range",
opt_set_bool, &opt_bfl_noncerange,
"Use nonce range on bitforce devices if supported"),
#endif
#ifdef USE_BFLSC
OPT_WITH_ARG("--bflsc-overheat",
set_int_0_to_200, opt_show_intval, &opt_bflsc_overheat,
"Set overheat temperature where BFLSC devices throttle, 0 to disable"),
#endif
#ifdef USE_AVALON
OPT_WITH_ARG("--bitburner-voltage",
opt_set_intval, NULL, &opt_bitburner_core_voltage,
"Set BitBurner (Avalon) core voltage, in millivolts"),
OPT_WITH_ARG("--bitburner-fury-voltage",
opt_set_intval, NULL, &opt_bitburner_fury_core_voltage,
"Set BitBurner Fury core voltage, in millivolts"),
OPT_WITH_ARG("--bitburner-fury-options",
opt_set_charp, NULL, &opt_bitburner_fury_options,
"Override avalon-options for BitBurner Fury boards baud:miners:asic:timeout:freq"),
#endif
#if defined(USE_ANT_S1) || defined(USE_ANT_S2)
OPT_WITHOUT_ARG("--bitmain-auto",
opt_set_bool, &opt_bitmain_auto,
"Adjust bitmain overclock frequency dynamically for best hashrate"),
OPT_WITH_ARG("--bitmain-cutoff",
set_int_0_to_100, opt_show_intval, &opt_bitmain_overheat,
"Set bitmain overheat cut off temperature"),
OPT_WITH_CBARG("--bitmain-fan",
set_bitmain_fan, NULL, &opt_set_bitmain_fan,
"Set fanspeed percentage for bitmain, single value or range (default: 20-100)"),
OPT_WITH_CBARG("--bitmain-freq",
opt_set_charp, NULL, &opt_bitmain_freq,
"Set bitmain freq options timeout:freq:regdata"),
OPT_WITHOUT_ARG("--bitmain-hwerror",
opt_set_bool, &opt_bitmain_hwerror,
"Set bitmain device detect hardware error"),
OPT_WITH_ARG("--bitmain-options",
opt_set_charp, NULL, &opt_bitmain_options,
#ifdef USE_ANT_S1
"Set bitmain options baud:miners:asic:timeout:freq:regdata"
#else
"Set bitmain options baud:miners:asic:ignored..."
#endif
),
OPT_WITH_ARG("--bitmain-temp",
set_int_0_to_100, opt_show_intval, &opt_bitmain_temp,
"Set bitmain target temperature"),
OPT_WITH_ARG("--bitmain-workdelay",
set_int_0_to_100, opt_show_intval, &opt_bitmain_workdelay,
"Set bitmain work delay (ms) 0-100"),
#endif
#ifdef USE_ANT_S2
OPT_WITH_ARG("--bitmain-voltage",
opt_set_charp, NULL, &opt_bitmain_voltage,
"Set bitmain voltage (default: "BITMAIN_VOLTAGE_DEF")"),
#ifndef USE_ANT_S3
OPT_WITH_ARG("--bitmain-dev",
opt_set_charp, NULL, &opt_bitmain_dev,
"Set bitmain device"),
#endif
OPT_WITHOUT_ARG("--bitmainbeeper",
opt_set_bool, &opt_bitmain_beeper,
"Set bitmain beeper ringing"),
OPT_WITHOUT_ARG("--bitmain-checkall",
opt_set_bool, &opt_bitmain_checkall,
opt_hidden),
OPT_WITHOUT_ARG("--bitmain-checkn2diff",
opt_set_bool, &opt_bitmain_checkn2diff,
opt_hidden),
OPT_WITHOUT_ARG("--bitmaintempoverctrl",
opt_set_bool, &opt_bitmain_tempoverctrl,
"Set bitmain stop runing when temprerature is over 80 degree Celsius"),
// Ignored
OPT_WITHOUT_ARG("--bitmain-nobeeper",
opt_set_bool, &opt_bitmain_nobeeper,
opt_hidden),
OPT_WITHOUT_ARG("--bitmain-notempoverctrl",
opt_set_bool, &opt_bitmain_notempoverctrl,
opt_hidden),
OPT_WITHOUT_ARG("--bitmain-homemode",
opt_set_bool, &opt_bitmain_homemode,
opt_hidden),
#endif
#ifdef USE_BITMINE_A1
OPT_WITH_ARG("--bitmine-a1-options",
opt_set_charp, NULL, &opt_bitmine_a1_options,
"Bitmine A1 options ref_clk_khz:sys_clk_khz:spi_clk_khz:override_chip_num"),
#endif
#ifdef USE_BITFURY
OPT_WITH_ARG("--bxf-bits",
set_int_32_to_63, opt_show_intval, &opt_bxf_bits,
"Set max BXF/HXF bits for overclocking"),
OPT_WITH_ARG("--bxf-debug",
set_int_0_to_4, opt_show_intval, &opt_bxf_debug,
"BXF: Debug all USB I/O, > is to the board(s), < is from the board(s)"),
OPT_WITH_ARG("--bxf-temp-target",
set_int_0_to_200, opt_show_intval, &opt_bxf_temp_target,
"Set target temperature for BXF/HXF devices"),
OPT_WITH_ARG("--bxm-bits",
set_int_0_to_100, opt_show_intval, &opt_bxm_bits,
"Set BXM bits for overclocking"),
#endif
#ifdef USE_BLOCKERUPTER
OPT_WITH_ARG("--bet-clk",
opt_set_intval, opt_show_intval, &opt_bet_clk,
"Set Block Erupter clock"),
#endif
#ifdef HAVE_LIBCURL
OPT_WITH_ARG("--btc-address",
opt_set_charp, NULL, &opt_btc_address,
"Set bitcoin target address when solo mining to bitcoind (mandatory)"),
OPT_WITH_ARG("--btc-sig",
opt_set_charp, NULL, &opt_btc_sig,
"Set signature to add to coinbase when solo mining (optional)"),
#endif
#ifdef HAVE_CURSES
OPT_WITHOUT_ARG("--compact",
opt_set_bool, &opt_compact,
"Use compact display without per device statistics"),
#endif
#ifdef USE_COINTERRA
OPT_WITH_ARG("--cta-load",
set_int_0_to_255, opt_show_intval, &opt_cta_load,
"Set load for CTA devices, 0-255 range"),
OPT_WITH_ARG("--ps-load",
set_int_0_to_100, opt_show_intval, &opt_ps_load,
"Set power supply load for CTA devices, 0-100 range"),
#endif
OPT_WITHOUT_ARG("--debug|-D",
enable_debug, &opt_debug,
"Enable debug output"),
OPT_WITHOUT_ARG("--disable-rejecting",
opt_set_bool, &opt_disable_pool,
"Automatically disable pools that continually reject shares"),
#ifdef USE_DRILLBIT
OPT_WITH_ARG("--drillbit-options",
opt_set_charp, NULL, &opt_drillbit_options,
"Set drillbit options <int|ext>:clock[:clock_divider][:voltage]"),
OPT_WITH_ARG("--drillbit-auto",
opt_set_charp, NULL, &opt_drillbit_auto,
"Enable drillbit automatic tuning <every>:[<gooderr>:<baderr>:<maxerr>]"),
#endif
OPT_WITH_ARG("--expiry|-E",
set_null, NULL, &opt_set_null,
opt_hidden),
OPT_WITHOUT_ARG("--failover-only",
set_null, &opt_set_null,
opt_hidden),
OPT_WITH_ARG("--fallback-time",
opt_set_intval, opt_show_intval, &opt_pool_fallback,
"Set time in seconds to fall back to a higher priority pool after period of instability"),
OPT_WITHOUT_ARG("--fix-protocol",
opt_set_bool, &opt_fix_protocol,
"Do not redirect to stratum protocol from GBT"),
#ifdef USE_HASHFAST
OPT_WITHOUT_ARG("--hfa-dfu-boot",
opt_set_bool, &opt_hfa_dfu_boot,
opt_hidden),
OPT_WITH_ARG("--hfa-hash-clock",
set_int_0_to_9999, opt_show_intval, &opt_hfa_hash_clock,
"Set hashfast clock speed"),
OPT_WITH_ARG("--hfa-fail-drop",
set_int_0_to_100, opt_show_intval, &opt_hfa_fail_drop,
"Set how many MHz to drop clockspeed each failure on an overlocked hashfast device"),
OPT_WITH_CBARG("--hfa-fan",
set_hfa_fan, NULL, &opt_set_hfa_fan,
"Set fanspeed percentage for hashfast, single value or range (default: 10-85)"),
OPT_WITH_ARG("--hfa-name",
opt_set_charp, NULL, &opt_hfa_name,
"Set a unique name for a single hashfast device specified with --usb or the first device found"),
OPT_WITHOUT_ARG("--hfa-noshed",
opt_set_bool, &opt_hfa_noshed,
"Disable hashfast dynamic core disabling feature"),
OPT_WITH_ARG("--hfa-ntime-roll",
opt_set_intval, NULL, &opt_hfa_ntime_roll,
opt_hidden),
OPT_WITH_ARG("--hfa-options",
opt_set_charp, NULL, &opt_hfa_options,
"Set hashfast options name:clock (comma separated)"),
OPT_WITHOUT_ARG("--hfa-pll-bypass",
opt_set_bool, &opt_hfa_pll_bypass,
opt_hidden),
OPT_WITH_ARG("--hfa-temp-overheat",
set_int_0_to_200, opt_show_intval, &opt_hfa_overheat,
"Set the hashfast overheat throttling temperature"),
OPT_WITH_ARG("--hfa-temp-target",
set_int_0_to_200, opt_show_intval, &opt_hfa_target,
"Set the hashfast target temperature (0 to disable)"),
#endif
#ifdef USE_HASHRATIO
OPT_WITH_CBARG("--hro-freq",
set_hashratio_freq, NULL, &opt_hashratio_freq,
"Set the hashratio clock frequency"),
#endif
OPT_WITH_ARG("--hotplug",
set_int_0_to_9999, NULL, &hotplug_time,
#ifdef USE_USBUTILS
"Seconds between hotplug checks (0 means never check)"
#else
opt_hidden
#endif
),
#ifdef USE_ICARUS
OPT_WITH_ARG("--icarus-options",
opt_set_charp, NULL, &opt_icarus_options,
opt_hidden),
OPT_WITH_ARG("--icarus-timing",
opt_set_charp, NULL, &opt_icarus_timing,
opt_hidden),
#endif
#if defined(HAVE_MODMINER)
OPT_WITH_ARG("--kernel-path|-K",
opt_set_charp, opt_show_charp, &opt_kernel_path,
"Specify a path to where bitstream files are"),
#endif
#ifdef USE_KLONDIKE
OPT_WITH_ARG("--klondike-options",
opt_set_charp, NULL, &opt_klondike_options,
"Set klondike options clock:temptarget"),
#endif
OPT_WITHOUT_ARG("--load-balance",
set_loadbalance, &pool_strategy,
"Change multipool strategy from failover to quota based balance"),
OPT_WITH_ARG("--log|-l",
set_int_0_to_9999, opt_show_intval, &opt_log_interval,
"Interval in seconds between log output"),
OPT_WITHOUT_ARG("--lowmem",
opt_set_bool, &opt_lowmem,
"Minimise caching of shares for low memory applications"),
#ifdef USE_MINION
OPT_WITH_ARG("--minion-chipreport",
set_int_0_to_100, opt_show_intval, &opt_minion_chipreport,
"Seconds to report chip 5min hashrate, range 0-100 (default: 0=disabled)"),
OPT_WITH_ARG("--minion-cores",
opt_set_charp, NULL, &opt_minion_cores,
opt_hidden),
OPT_WITHOUT_ARG("--minion-extra",
opt_set_bool, &opt_minion_extra,
opt_hidden),
OPT_WITH_ARG("--minion-freq",
opt_set_charp, NULL, &opt_minion_freq,
"Set minion chip frequencies in MHz, single value or comma list, range 100-1400 (default: 1200)"),
OPT_WITH_ARG("--minion-freqchange",
set_int_0_to_9999, opt_show_intval, &opt_minion_freqchange,
"Millisecond total time to do frequency changes (default: 1000)"),
OPT_WITH_ARG("--minion-freqpercent",
set_int_0_to_100, opt_show_intval, &opt_minion_freqpercent,
"Percentage to use when starting up a chip (default: 70%)"),
OPT_WITHOUT_ARG("--minion-idlecount",
opt_set_bool, &opt_minion_idlecount,
"Report when IdleCount is >0 or changes"),
OPT_WITH_ARG("--minion-ledcount",
set_int_0_to_100, opt_show_intval, &opt_minion_ledcount,
"Turn off led when more than this many chips below the ledlimit (default: 0)"),
OPT_WITH_ARG("--minion-ledlimit",
set_int_0_to_200, opt_show_intval, &opt_minion_ledlimit,
"Turn off led when chips GHs are below this (default: 90)"),
OPT_WITHOUT_ARG("--minion-noautofreq",
opt_set_bool, &opt_minion_noautofreq,
"Disable automatic frequency adjustment"),
OPT_WITHOUT_ARG("--minion-overheat",
opt_set_bool, &opt_minion_overheat,
"Enable directly halting any chip when the status exceeds 100C"),
OPT_WITH_ARG("--minion-spidelay",
set_int_0_to_9999, opt_show_intval, &opt_minion_spidelay,
"Add a delay in microseconds after each SPI I/O"),
OPT_WITH_ARG("--minion-spireset",
opt_set_charp, NULL, &opt_minion_spireset,
"SPI regular reset: iNNN for I/O count or sNNN for seconds - 0 means none"),
OPT_WITH_ARG("--minion-spisleep",
set_int_0_to_9999, opt_show_intval, &opt_minion_spisleep,
"Sleep time in milliseconds when doing an SPI reset"),
OPT_WITH_ARG("--minion-spiusec",
set_int_0_to_9999, NULL, &opt_minion_spiusec,
opt_hidden),
OPT_WITH_ARG("--minion-temp",
opt_set_charp, NULL, &opt_minion_temp,
"Set minion chip temperature threshold, single value or comma list, range 120-160 (default: 135C)"),
#endif
#if defined(unix) || defined(__APPLE__)
OPT_WITH_ARG("--monitor|-m",
opt_set_charp, NULL, &opt_stderr_cmd,
"Use custom pipe cmd for output messages"),
#endif // defined(unix)
#ifdef USE_BITFURY
OPT_WITH_ARG("--nfu-bits",
set_int_32_to_63, opt_show_intval, &opt_nfu_bits,
"Set nanofury bits for overclocking, range 32-63"),
#endif
OPT_WITHOUT_ARG("--net-delay",
opt_set_bool, &opt_delaynet,
"Impose small delays in networking to not overload slow routers"),
OPT_WITHOUT_ARG("--no-pool-disable",
opt_set_invbool, &opt_disable_pool,
opt_hidden),
OPT_WITHOUT_ARG("--no-submit-stale",
opt_set_invbool, &opt_submit_stale,
"Don't submit shares if they are detected as stale"),
#ifdef USE_BITFURY
OPT_WITH_ARG("--osm-led-mode",
set_int_0_to_4, opt_show_intval, &opt_osm_led_mode,
"Set LED mode for OneStringMiner devices"),
#endif
OPT_WITH_ARG("--pass|-p",
set_pass, NULL, &opt_set_null,
"Password for bitcoin JSON-RPC server"),
OPT_WITHOUT_ARG("--per-device-stats",
opt_set_bool, &want_per_device_stats,
"Force verbose mode and output per-device statistics"),
OPT_WITH_ARG("--pools",
opt_set_bool, NULL, &opt_set_null, opt_hidden),
OPT_WITHOUT_ARG("--protocol-dump|-P",
opt_set_bool, &opt_protocol,
"Verbose dump of protocol-level activities"),
OPT_WITH_ARG("--queue|-Q",
set_null, NULL, &opt_set_null,
opt_hidden),
OPT_WITHOUT_ARG("--quiet|-q",
opt_set_bool, &opt_quiet,
"Disable logging output, display status and errors"),
OPT_WITH_ARG("--quota|-U",
set_quota, NULL, &opt_set_null,
"quota;URL combination for server with load-balance strategy quotas"),
OPT_WITHOUT_ARG("--real-quiet",
opt_set_bool, &opt_realquiet,
"Disable all output"),
OPT_WITH_ARG("--retries",
set_null, NULL, &opt_set_null,
opt_hidden),
OPT_WITH_ARG("--retry-pause",
set_null, NULL, &opt_set_null,
opt_hidden),
#ifdef USE_ICARUS
OPT_WITH_ARG("--rock-freq",
set_float_125_to_500, &opt_show_floatval, &opt_rock_freq,
"Set RockMiner frequency in MHz, range 125-500"),
#endif
OPT_WITH_ARG("--rotate",
set_rotate, NULL, &opt_set_null,
"Change multipool strategy from failover to regularly rotate at N minutes"),
OPT_WITHOUT_ARG("--round-robin",
set_rr, &pool_strategy,
"Change multipool strategy from failover to round robin on failure"),
#ifdef USE_FPGA_SERIAL
OPT_WITH_CBARG("--scan-serial|-S",
add_serial, NULL, &opt_add_serial,
"Serial port to probe for Serial FPGA Mining device"),
#endif
OPT_WITH_ARG("--scan-time|-s",
set_null, NULL, &opt_set_null,
opt_hidden),
OPT_WITH_CBARG("--sched-start",
set_sched_start, NULL, &opt_set_sched_start,
"Set a time of day in HH:MM to start mining (a once off without a stop time)"),
OPT_WITH_CBARG("--sched-stop",
set_sched_stop, NULL, &opt_set_sched_stop,
"Set a time of day in HH:MM to stop mining (will quit without a start time)"),
OPT_WITH_CBARG("--sharelog",
set_sharelog, NULL, &opt_set_sharelog,
"Append share log to file"),
OPT_WITH_ARG("--shares",
opt_set_intval, NULL, &opt_shares,
"Quit after mining N shares (default: unlimited)"),
OPT_WITH_ARG("--socks-proxy",
opt_set_charp, NULL, &opt_socks_proxy,
"Set socks4 proxy (host:port)"),
OPT_WITH_ARG("--suggest-diff",
opt_set_intval, NULL, &opt_suggest_diff,
"Suggest miner difficulty for pool to user (default: none)"),
#ifdef HAVE_SYSLOG_H
OPT_WITHOUT_ARG("--syslog",
opt_set_bool, &use_syslog,
"Use system log for output messages (default: standard error)"),
#endif
#if defined(USE_BITFORCE) || defined(USE_MODMINER) || defined(USE_BFLSC)
OPT_WITH_CBARG("--temp-cutoff",
set_temp_cutoff, opt_show_intval, &opt_set_temp_cutoff,
"Temperature where a device will be automatically disabled, one value or comma separated list"),
#endif
OPT_WITHOUT_ARG("--text-only|-T",
opt_set_invbool, &use_curses,
#ifdef HAVE_CURSES
"Disable ncurses formatted screen output"
#else
opt_hidden
#endif
),
OPT_WITH_ARG("--url|-o",
set_url, NULL, &opt_set_null,
"URL for bitcoin JSON-RPC server"),
#ifdef USE_USBUTILS
OPT_WITH_ARG("--usb",
opt_set_charp, NULL, &opt_usb_select,
"USB device selection"),
OPT_WITH_ARG("--usb-dump",
set_int_0_to_10, opt_show_intval, &opt_usbdump,
opt_hidden),
OPT_WITHOUT_ARG("--usb-list-all",
opt_set_bool, &opt_usb_list_all,
opt_hidden),
#endif
OPT_WITH_ARG("--user|-u",
set_user, NULL, &opt_set_null,
"Username for bitcoin JSON-RPC server"),
OPT_WITH_ARG("--userpass|-O",
set_userpass, NULL, &opt_set_null,
"Username:Password pair for bitcoin JSON-RPC server"),
OPT_WITHOUT_ARG("--verbose",
opt_set_bool, &opt_log_output,
"Log verbose output to stderr as well as status output"),
OPT_WITHOUT_ARG("--widescreen",
opt_set_bool, &opt_widescreen,
"Use extra wide display without toggling"),
OPT_WITHOUT_ARG("--worktime",
opt_set_bool, &opt_worktime,
"Display extra work time debug information"),
OPT_ENDTABLE
};
static char *load_config(const char *arg, void __maybe_unused *unused);
static int fileconf_load;
static char *parse_config(json_t *config, bool fileconf)
{
static char err_buf[200];
struct opt_table *opt;
const char *str;
json_t *val;
if (fileconf && !fileconf_load)
fileconf_load = 1;
for (opt = opt_config_table; opt->type != OPT_END; opt++) {
char *p, *name;
/* We don't handle subtables. */
assert(!(opt->type & OPT_SUBTABLE));
if (!opt->names)
continue;
/* Pull apart the option name(s). */
name = strdup(opt->names);
for (p = strtok(name, "|"); p; p = strtok(NULL, "|")) {
char *err = NULL;
/* Ignore short options. */
if (p[1] != '-')
continue;
val = json_object_get(config, p+2);
if (!val)
continue;
if ((opt->type & (OPT_HASARG | OPT_PROCESSARG)) && json_is_string(val)) {
str = json_string_value(val);
err = opt->cb_arg(str, opt->u.arg);
if (opt->type == OPT_PROCESSARG)
opt_set_charp(str, opt->u.arg);
} else if ((opt->type & (OPT_HASARG | OPT_PROCESSARG)) && json_is_array(val)) {
json_t *arr_val;
size_t index;
json_array_foreach(val, index, arr_val) {
if (json_is_string(arr_val)) {
str = json_string_value(arr_val);
err = opt->cb_arg(str, opt->u.arg);
if (opt->type == OPT_PROCESSARG)
opt_set_charp(str, opt->u.arg);
} else if (json_is_object(arr_val))
err = parse_config(arr_val, false);
if (err)
break;
}
} else if ((opt->type & OPT_NOARG) && json_is_true(val))
err = opt->cb(opt->u.arg);
else
err = "Invalid value";
if (err) {
/* Allow invalid values to be in configuration
* file, just skipping over them provided the
* JSON is still valid after that. */
if (fileconf) {
applog(LOG_ERR, "Invalid config option %s: %s", p, err);
fileconf_load = -1;
} else {
snprintf(err_buf, sizeof(err_buf), "Parsing JSON option %s: %s",
p, err);
return err_buf;
}
}
}
free(name);
}
val = json_object_get(config, JSON_INCLUDE_CONF);
if (val && json_is_string(val))
return load_config(json_string_value(val), NULL);
return NULL;
}
char *cnfbuf = NULL;
#ifdef HAVE_LIBCURL
char conf_web1[] = "http://";
char conf_web2[] = "https://";
static char *load_web_config(const char *arg)
{
json_t *val = json_web_config(arg);
if (!val || !json_is_object(val))
return JSON_WEB_ERROR;
if (!cnfbuf)
cnfbuf = strdup(arg);
config_loaded = true;
return parse_config(val, true);
}
#endif
static char *load_config(const char *arg, void __maybe_unused *unused)
{
json_error_t err;
json_t *config;
char *json_error;
size_t siz;
#ifdef HAVE_LIBCURL
if (strncasecmp(arg, conf_web1, sizeof(conf_web1)-1) == 0 ||
strncasecmp(arg, conf_web2, sizeof(conf_web2)-1) == 0)
return load_web_config(arg);
#endif
if (!cnfbuf)
cnfbuf = strdup(arg);
if (++include_count > JSON_MAX_DEPTH)
return JSON_MAX_DEPTH_ERR;
config = json_load_file(arg, 0, &err);
if (!json_is_object(config)) {
siz = JSON_LOAD_ERROR_LEN + strlen(arg) + strlen(err.text);
json_error = cgmalloc(siz);
snprintf(json_error, siz, JSON_LOAD_ERROR, arg, err.text);
return json_error;
}
config_loaded = true;
/* Parse the config now, so we can override it. That can keep pointers
* so don't free config object. */
return parse_config(config, true);
}
static char *set_default_config(const char *arg)
{
opt_set_charp(arg, &default_config);
return NULL;
}
void default_save_file(char *filename);
static void load_default_config(void)
{
cnfbuf = cgmalloc(PATH_MAX);
default_save_file(cnfbuf);
if (!access(cnfbuf, R_OK))
load_config(cnfbuf, NULL);
else {
free(cnfbuf);
cnfbuf = NULL;
}
}
extern const char *opt_argv0;
static char *opt_verusage_and_exit(const char *extra)
{
printf("%s\nBuilt with "
#ifdef USE_ANT_S1
"ant.S1 "
#endif
#ifdef USE_ANT_S2
#ifdef USE_ANT_S3
"ant.S3 "
#else
"ant.S2 "
#endif
#endif
#ifdef USE_AVALON
"avalon "
#endif
#ifdef USE_AVALON2
"avalon2 "
#endif
#ifdef USE_AVALON4
"avalon4 "
#endif
#ifdef USE_AVALON_MINER
"avalon miner"
#endif
#ifdef USE_BFLSC
"bflsc "
#endif
#ifdef USE_BITFORCE
"bitforce "
#endif
#ifdef USE_BITFURY
"bitfury "
#endif
#ifdef USE_COINTERRA
"cointerra "
#endif
#ifdef USE_DRILLBIT
"drillbit "
#endif
#ifdef USE_HASHFAST
"hashfast "
#endif
#ifdef USE_ICARUS
"icarus "
#endif
#ifdef USE_KLONDIKE
"klondike "
#endif
#ifdef USE_KNC
"KnC "
#endif
#ifdef USE_BAB
"BaB "
#endif
#ifdef USE_MINION
"minion "
#endif
#ifdef USE_MODMINER
"modminer "
#endif
#ifdef USE_BITMINE_A1
"Bitmine.A1 "
#endif
#ifdef USE_SP10
"spondoolies "
#endif
#ifdef USE_SP30
"sp30 "
#endif
"mining support.\n"
, packagename);
printf("%s", opt_usage(opt_argv0, extra));
fflush(stdout);
exit(0);
}
#if defined(USE_USBUTILS)
char *display_devs(int *ndevs)
{
*ndevs = 0;
usb_all(0);
exit(*ndevs);
}
#endif
/* These options are available from commandline only */
static struct opt_table opt_cmdline_table[] = {
OPT_WITH_ARG("--config|-c",
load_config, NULL, &opt_set_null,
"Load a JSON-format configuration file\n"
"See example.conf for an example configuration."),
OPT_WITH_ARG("--default-config",
set_default_config, NULL, &opt_set_null,
"Specify the filename of the default config file\n"
"Loaded at start and used when saving without a name."),
OPT_WITHOUT_ARG("--help|-h",
opt_verusage_and_exit, NULL,
"Print this message"),
#if defined(USE_USBUTILS)
OPT_WITHOUT_ARG("--ndevs|-n",
display_devs, &nDevs,
"Display all USB devices and exit"),
#endif
OPT_WITHOUT_ARG("--version|-V",
opt_version_and_exit, packagename,
"Display version and exit"),
OPT_ENDTABLE
};
static void calc_midstate(struct work *work)
{
unsigned char data[64];
uint32_t *data32 = (uint32_t *)data;
sha256_ctx ctx;
flip64(data32, work->data);
sha256_init(&ctx);
sha256_update(&ctx, data, 64);
cg_memcpy(work->midstate, ctx.h, 32);
endian_flip32(work->midstate, work->midstate);
}
/* Returns the current value of total_work and increments it */
static int total_work_inc(void)
{
int ret;
cg_wlock(&control_lock);
ret = total_work++;
cg_wunlock(&control_lock);
return ret;
}
static struct work *make_work(void)
{
struct work *work = cgcalloc(1, sizeof(struct work));
work->id = total_work_inc();
return work;
}
/* This is the central place all work that is about to be retired should be
* cleaned to remove any dynamically allocated arrays within the struct */
void clean_work(struct work *work)
{
free(work->job_id);
free(work->ntime);
free(work->coinbase);
free(work->nonce1);
memset(work, 0, sizeof(struct work));
}
/* All dynamically allocated work structs should be freed here to not leak any
* ram from arrays allocated within the work struct. Null the actual pointer
* used to call free_work. */
void _free_work(struct work **workptr, const char *file, const char *func, const int line)
{
struct work *work = *workptr;
if (unlikely(!work)) {
applog(LOG_ERR, "Free work called with null work from %s %s:%d",
file, func, line);
return;
}
clean_work(work);
free(work);
*workptr = NULL;
}
static void gen_hash(unsigned char *data, unsigned char *hash, int len);
static void calc_diff(struct work *work, double known);
char *workpadding = "000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000";
#ifdef HAVE_LIBCURL
/* Process transactions with GBT by storing the binary value of the first
* transaction, and the hashes of the remaining transactions since these
* remain constant with an altered coinbase when generating work. Must be
* entered under gbt_lock */
static void gbt_merkle_bins(struct pool *pool, json_t *transaction_arr);
static void __build_gbt_txns(struct pool *pool, json_t *res_val)
{
json_t *txn_array;
txn_array = json_object_get(res_val, "transactions");
gbt_merkle_bins(pool, txn_array);
}
static void __gbt_merkleroot(struct pool *pool, unsigned char *merkle_root)
{
unsigned char merkle_sha[64];
int i;
gen_hash(pool->coinbase, merkle_root, pool->coinbase_len);
cg_memcpy(merkle_sha, merkle_root, 32);
for (i = 0; i < pool->merkles; i++) {
cg_memcpy(merkle_sha + 32, pool->merklebin + i * 32, 32);
gen_hash(merkle_sha, merkle_root, 64);
cg_memcpy(merkle_sha, merkle_root, 32);
}
}
static bool work_decode(struct pool *pool, struct work *work, json_t *val);
static void update_gbt(struct pool *pool)
{
int rolltime;
json_t *val;
CURL *curl;
curl = curl_easy_init();
if (unlikely(!curl))
quit (1, "CURL initialisation failed in update_gbt");
val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass,
pool->rpc_req, true, false, &rolltime, pool, false);
if (val) {
struct work *work = make_work();
bool rc = work_decode(pool, work, val);
total_getworks++;
pool->getwork_requested++;
if (rc) {
applog(LOG_DEBUG, "Successfully retrieved and updated GBT from pool %u %s",
pool->pool_no, pool->rpc_url);
if (pool == current_pool())
opt_work_update = true;
} else {
applog(LOG_DEBUG, "Successfully retrieved but FAILED to decipher GBT from pool %u %s",
pool->pool_no, pool->rpc_url);
}
json_decref(val);
free_work(work);
} else {
applog(LOG_DEBUG, "FAILED to update GBT from pool %u %s",
pool->pool_no, pool->rpc_url);
}
curl_easy_cleanup(curl);
}
static void gen_gbt_work(struct pool *pool, struct work *work)
{
unsigned char merkleroot[32];
struct timeval now;
uint64_t nonce2le;
cgtime(&now);
if (now.tv_sec - pool->tv_lastwork.tv_sec > 60)
update_gbt(pool);
cg_wlock(&pool->gbt_lock);
nonce2le = htole64(pool->nonce2);
cg_memcpy(pool->coinbase + pool->nonce2_offset, &nonce2le, pool->n2size);
pool->nonce2++;
cg_dwlock(&pool->gbt_lock);
__gbt_merkleroot(pool, merkleroot);
cg_memcpy(work->data, &pool->gbt_version, 4);
cg_memcpy(work->data + 4, pool->previousblockhash, 32);
cg_memcpy(work->data + 4 + 32 + 32, &pool->curtime, 4);
cg_memcpy(work->data + 4 + 32 + 32 + 4, &pool->gbt_bits, 4);
cg_memcpy(work->target, pool->gbt_target, 32);
work->coinbase = bin2hex(pool->coinbase, pool->coinbase_len);
/* For encoding the block data on submission */
work->gbt_txns = pool->gbt_txns + 1;
if (pool->gbt_workid)
work->job_id = strdup(pool->gbt_workid);
cg_runlock(&pool->gbt_lock);
flip32(work->data + 4 + 32, merkleroot);
memset(work->data + 4 + 32 + 32 + 4 + 4, 0, 4); /* nonce */
hex2bin(work->data + 4 + 32 + 32 + 4 + 4 + 4, workpadding, 48);
if (opt_debug) {
char *header = bin2hex(work->data, 128);
applog(LOG_DEBUG, "Generated GBT header %s", header);
applog(LOG_DEBUG, "Work coinbase %s", work->coinbase);
free(header);
}
calc_midstate(work);
local_work++;
work->pool = pool;
work->gbt = true;
work->longpoll = false;
work->getwork_mode = GETWORK_MODE_GBT;
work->work_block = work_block;
/* Nominally allow a driver to ntime roll 60 seconds */
work->drv_rolllimit = 60;
calc_diff(work, 0);
cgtime(&work->tv_staged);
}
static bool gbt_decode(struct pool *pool, json_t *res_val)
{
const char *previousblockhash;
const char *target;
const char *coinbasetxn;
const char *longpollid;
unsigned char hash_swap[32];
int expires;
int version;
int curtime;
bool submitold;
const char *bits;
const char *workid;
int cbt_len, orig_len;
uint8_t *extra_len;
size_t cal_len;
previousblockhash = json_string_value(json_object_get(res_val, "previousblockhash"));
target = json_string_value(json_object_get(res_val, "target"));
coinbasetxn = json_string_value(json_object_get(json_object_get(res_val, "coinbasetxn"), "data"));
longpollid = json_string_value(json_object_get(res_val, "longpollid"));
expires = json_integer_value(json_object_get(res_val, "expires"));
version = json_integer_value(json_object_get(res_val, "version"));
curtime = json_integer_value(json_object_get(res_val, "curtime"));
submitold = json_is_true(json_object_get(res_val, "submitold"));
bits = json_string_value(json_object_get(res_val, "bits"));
workid = json_string_value(json_object_get(res_val, "workid"));
if (!previousblockhash || !target || !coinbasetxn || !longpollid ||
!expires || !version || !curtime || !bits) {
applog(LOG_ERR, "JSON failed to decode GBT");
return false;
}
applog(LOG_DEBUG, "previousblockhash: %s", previousblockhash);
applog(LOG_DEBUG, "target: %s", target);
applog(LOG_DEBUG, "coinbasetxn: %s", coinbasetxn);
applog(LOG_DEBUG, "longpollid: %s", longpollid);
applog(LOG_DEBUG, "expires: %d", expires);
applog(LOG_DEBUG, "version: %d", version);
applog(LOG_DEBUG, "curtime: %d", curtime);
applog(LOG_DEBUG, "submitold: %s", submitold ? "true" : "false");
applog(LOG_DEBUG, "bits: %s", bits);
if (workid)
applog(LOG_DEBUG, "workid: %s", workid);
cg_wlock(&pool->gbt_lock);
free(pool->coinbasetxn);
pool->coinbasetxn = strdup(coinbasetxn);
cbt_len = strlen(pool->coinbasetxn) / 2;
/* We add 8 bytes of extra data corresponding to nonce2 */
pool->n2size = 8;
pool->coinbase_len = cbt_len + pool->n2size;
cal_len = pool->coinbase_len + 1;
free(pool->coinbase);
pool->coinbase = cgcalloc(cal_len, 1);
hex2bin(pool->coinbase, pool->coinbasetxn, 42);
extra_len = (uint8_t *)(pool->coinbase + 41);
orig_len = *extra_len;
hex2bin(pool->coinbase + 42, pool->coinbasetxn + 84, orig_len);
*extra_len += pool->n2size;
hex2bin(pool->coinbase + 42 + *extra_len, pool->coinbasetxn + 84 + (orig_len * 2),
cbt_len - orig_len - 42);
pool->nonce2_offset = orig_len + 42;
free(pool->longpollid);
pool->longpollid = strdup(longpollid);
free(pool->gbt_workid);
if (workid)
pool->gbt_workid = strdup(workid);
else
pool->gbt_workid = NULL;
hex2bin(hash_swap, previousblockhash, 32);
swap256(pool->previousblockhash, hash_swap);
hex2bin(hash_swap, target, 32);
swab256(pool->gbt_target, hash_swap);
pool->gbt_expires = expires;
pool->gbt_version = htobe32(version);
pool->curtime = htobe32(curtime);
pool->submit_old = submitold;
hex2bin((unsigned char *)&pool->gbt_bits, bits, 4);
__build_gbt_txns(pool, res_val);
if (pool->transactions < 3)
pool->bad_work++;
cg_wunlock(&pool->gbt_lock);
return true;
}
static void gbt_merkle_bins(struct pool *pool, json_t *transaction_arr)
{
unsigned char *hashbin;
json_t *arr_val;
int i, j, binleft, binlen;
free(pool->txn_data);
pool->txn_data = NULL;
pool->transactions = 0;
pool->merkles = 0;
pool->transactions = json_array_size(transaction_arr);
binlen = pool->transactions * 32 + 32;
hashbin = alloca(binlen + 32);
memset(hashbin, 0, 32);
binleft = binlen / 32;
if (pool->transactions) {
int len = 0, ofs = 0;
const char *txn;
for (i = 0; i < pool->transactions; i++) {
arr_val = json_array_get(transaction_arr, i);
txn = json_string_value(json_object_get(arr_val, "data"));
if (!txn) {
applog(LOG_ERR, "Pool %d json_string_value fail - cannot find transaction data",
pool->pool_no);
return;
}
len += strlen(txn);
}
pool->txn_data = cgmalloc(len + 1);
pool->txn_data[len] = '\0';
for (i = 0; i < pool->transactions; i++) {
unsigned char binswap[32];
const char *hash;
arr_val = json_array_get(transaction_arr, i);
hash = json_string_value(json_object_get(arr_val, "hash"));
txn = json_string_value(json_object_get(arr_val, "data"));
len = strlen(txn);
cg_memcpy(pool->txn_data + ofs, txn, len);
ofs += len;
if (!hash) {
unsigned char *txn_bin;
int txn_len;
txn_len = len / 2;
txn_bin = cgmalloc(txn_len);
hex2bin(txn_bin, txn, txn_len);
/* This is needed for pooled mining since only
* transaction data and not hashes are sent */
gen_hash(txn_bin, hashbin + 32 + 32 * i, txn_len);
continue;
}
if (!hex2bin(binswap, hash, 32)) {
applog(LOG_ERR, "Failed to hex2bin hash in gbt_merkle_bins");
return;
}
swab256(hashbin + 32 + 32 * i, binswap);
}
}
if (binleft > 1) {
while (42) {
if (binleft == 1)
break;
cg_memcpy(pool->merklebin + (pool->merkles * 32), hashbin + 32, 32);
pool->merkles++;
if (binleft % 2) {
cg_memcpy(hashbin + binlen, hashbin + binlen - 32, 32);
binlen += 32;
binleft++;
}
for (i = 32, j = 64; j < binlen; i += 32, j += 64) {
gen_hash(hashbin + j, hashbin + i, 64);
}
binleft /= 2;
binlen = binleft * 32;
}
}
if (opt_debug) {
char hashhex[68];
for (i = 0; i < pool->merkles; i++) {
__bin2hex(hashhex, pool->merklebin + i * 32, 32);
applog(LOG_DEBUG, "MH%d %s",i, hashhex);
}
}
applog(LOG_INFO, "Stored %d transactions from pool %d", pool->transactions,
pool->pool_no);
}
static double diff_from_target(void *target);
static const char scriptsig_header[] = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff";
static unsigned char scriptsig_header_bin[41];
static bool gbt_solo_decode(struct pool *pool, json_t *res_val)
{
json_t *transaction_arr, *coinbase_aux;
const char *previousblockhash;
unsigned char hash_swap[32];
struct timeval now;
const char *target;
uint64_t coinbasevalue;
const char *flags;
const char *bits;
char header[228];
int ofs = 0, len;
uint64_t *u64;
uint32_t *u32;
int version;
int curtime;
int height;
previousblockhash = json_string_value(json_object_get(res_val, "previousblockhash"));
target = json_string_value(json_object_get(res_val, "target"));
transaction_arr = json_object_get(res_val, "transactions");
version = json_integer_value(json_object_get(res_val, "version"));
curtime = json_integer_value(json_object_get(res_val, "curtime"));
bits = json_string_value(json_object_get(res_val, "bits"));
height = json_integer_value(json_object_get(res_val, "height"));
coinbasevalue = json_integer_value(json_object_get(res_val, "coinbasevalue"));
coinbase_aux = json_object_get(res_val, "coinbaseaux");
flags = json_string_value(json_object_get(coinbase_aux, "flags"));
if (!previousblockhash || !target || !version || !curtime || !bits || !coinbase_aux || !flags) {
applog(LOG_ERR, "Pool %d JSON failed to decode GBT", pool->pool_no);
return false;
}
applog(LOG_DEBUG, "previousblockhash: %s", previousblockhash);
applog(LOG_DEBUG, "target: %s", target);
applog(LOG_DEBUG, "version: %d", version);
applog(LOG_DEBUG, "curtime: %d", curtime);
applog(LOG_DEBUG, "bits: %s", bits);
applog(LOG_DEBUG, "height: %d", height);
applog(LOG_DEBUG, "flags: %s", flags);
cg_wlock(&pool->gbt_lock);
hex2bin(hash_swap, previousblockhash, 32);
swap256(pool->previousblockhash, hash_swap);
__bin2hex(pool->prev_hash, pool->previousblockhash, 32);
hex2bin(hash_swap, target, 32);
swab256(pool->gbt_target, hash_swap);
pool->sdiff = diff_from_target(pool->gbt_target);
pool->gbt_version = htobe32(version);
pool->curtime = htobe32(curtime);
snprintf(pool->ntime, 9, "%08x", curtime);
snprintf(pool->bbversion, 9, "%08x", version);
snprintf(pool->nbit, 9, "%s", bits);
pool->nValue = coinbasevalue;
hex2bin((unsigned char *)&pool->gbt_bits, bits, 4);
gbt_merkle_bins(pool, transaction_arr);
if (pool->transactions < 3)
pool->bad_work++;
pool->height = height;
memset(pool->scriptsig_base, 0, 42);
ofs++; // Leave room for template length
/* Put block height at start of template. */
ofs += ser_number(pool->scriptsig_base + ofs, height); // max 5
/* Followed by flags */
len = strlen(flags) / 2;
pool->scriptsig_base[ofs++] = len;
hex2bin(pool->scriptsig_base + ofs, flags, len);
ofs += len;
/* Followed by timestamp */
cgtime(&now);
pool->scriptsig_base[ofs++] = 0xfe; // Encode seconds as u32
u32 = (uint32_t *)&pool->scriptsig_base[ofs];
*u32 = htole32(now.tv_sec);
ofs += 4; // sizeof uint32_t
pool->scriptsig_base[ofs++] = 0xfe; // Encode usecs as u32
u32 = (uint32_t *)&pool->scriptsig_base[ofs];
*u32 = htole32(now.tv_usec);
ofs += 4; // sizeof uint32_t
cg_memcpy(pool->scriptsig_base + ofs, "\x09\x63\x67\x6d\x69\x6e\x65\x72\x34\x32", 10);
ofs += 10;
/* Followed by extranonce size, fixed at 8 */
pool->scriptsig_base[ofs++] = 8;
pool->nonce2_offset = 41 + ofs;
ofs += 8;
if (opt_btc_sig) {
len = strlen(opt_btc_sig);
if (len > 32)
len = 32;
pool->scriptsig_base[ofs++] = len;
cg_memcpy(pool->scriptsig_base + ofs, opt_btc_sig, len);
ofs += len;
}
pool->scriptsig_base[0] = ofs++; // Template length
pool->n1_len = ofs;
len = 41 // prefix
+ ofs // Template length
+ 4 // txin sequence no
+ 1 // transactions
+ 8 // value
+ 1 + 25 // txout
+ 4; // lock
free(pool->coinbase);
pool->coinbase = cgcalloc(len, 1);
cg_memcpy(pool->coinbase + 41, pool->scriptsig_base, ofs);
cg_memcpy(pool->coinbase + 41 + ofs, "\xff\xff\xff\xff", 4);
pool->coinbase[41 + ofs + 4] = 1;
u64 = (uint64_t *)&(pool->coinbase[41 + ofs + 4 + 1]);
*u64 = htole64(coinbasevalue);
pool->nonce2 = 0;
pool->n2size = 4;
pool->coinbase_len = 41 + ofs + 4 + 1 + 8 + 1 + 25 + 4;
cg_wunlock(&pool->gbt_lock);
snprintf(header, 225, "%s%s%s%s%s%s%s",
pool->bbversion,
pool->prev_hash,
"0000000000000000000000000000000000000000000000000000000000000000",
pool->ntime,
pool->nbit,
"00000000", /* nonce */
workpadding);
if (unlikely(!hex2bin(pool->header_bin, header, 112)))
quit(1, "Failed to hex2bin header in gbt_solo_decode");
return true;
}
static bool work_decode(struct pool *pool, struct work *work, json_t *val)
{
json_t *res_val = json_object_get(val, "result");
bool ret = false;
cgtime(&pool->tv_lastwork);
if (!res_val || json_is_null(res_val)) {
applog(LOG_ERR, "JSON Failed to decode result");
goto out;
}
if (pool->gbt_solo) {
if (unlikely(!gbt_solo_decode(pool, res_val)))
goto out;
goto out_true;
}
if (unlikely(!gbt_decode(pool, res_val)))
goto out;
work->gbt = true;
memset(work->hash, 0, sizeof(work->hash));
cgtime(&work->tv_staged);
out_true:
ret = true;
out:
return ret;
}
#else /* HAVE_LIBCURL */
#define json_rpc_call(curl, url, userpass, rpc_req, probe, longpoll, rolltime, pool, share) (NULL)
#define work_decode(pool, work, val) (false)
#define gen_gbt_work(pool, work) {}
#endif /* HAVE_LIBCURL */
int dev_from_id(int thr_id)
{
struct cgpu_info *cgpu = get_thr_cgpu(thr_id);
return cgpu->device_id;
}
/* Create an exponentially decaying average over the opt_log_interval */
void decay_time(double *f, double fadd, double fsecs, double interval)
{
double ftotal, fprop;
if (fsecs <= 0)
return;
fprop = 1.0 - 1 / (exp(fsecs / interval));
ftotal = 1.0 + fprop;
*f += (fadd / fsecs * fprop);
*f /= ftotal;
}
static int __total_staged(void)
{
return HASH_COUNT(staged_work);
}
static int total_staged(void)
{
int ret;
mutex_lock(stgd_lock);
ret = __total_staged();
mutex_unlock(stgd_lock);
return ret;
}
#ifdef HAVE_CURSES
WINDOW *mainwin, *statuswin, *logwin;
#endif
double total_secs = 1.0;
static char statusline[256];
/* logstart is where the log window should start */
static int devcursor, logstart, logcursor;
#ifdef HAVE_CURSES
/* statusy is where the status window goes up to in cases where it won't fit at startup */
static int statusy;
#endif
#ifdef HAVE_CURSES
static inline void unlock_curses(void)
{
mutex_unlock(&console_lock);
}
static inline void lock_curses(void)
{
mutex_lock(&console_lock);
}
static bool curses_active_locked(void)
{
bool ret;
lock_curses();
ret = curses_active;
if (!ret)
unlock_curses();
return ret;
}
#endif
/* Convert a uint64_t value into a truncated string for displaying with its
* associated suitable for Mega, Giga etc. Buf array needs to be long enough */
static void suffix_string(uint64_t val, char *buf, size_t bufsiz, int sigdigits)
{
const double dkilo = 1000.0;
const uint64_t kilo = 1000ull;
const uint64_t mega = 1000000ull;
const uint64_t giga = 1000000000ull;
const uint64_t tera = 1000000000000ull;
const uint64_t peta = 1000000000000000ull;
const uint64_t exa = 1000000000000000000ull;
char suffix[2] = "";
bool decimal = true;
double dval;
if (val >= exa) {
val /= peta;
dval = (double)val / dkilo;
strcpy(suffix, "E");
} else if (val >= peta) {
val /= tera;
dval = (double)val / dkilo;
strcpy(suffix, "P");
} else if (val >= tera) {
val /= giga;
dval = (double)val / dkilo;
strcpy(suffix, "T");
} else if (val >= giga) {
val /= mega;
dval = (double)val / dkilo;
strcpy(suffix, "G");
} else if (val >= mega) {
val /= kilo;
dval = (double)val / dkilo;
strcpy(suffix, "M");
} else if (val >= kilo) {
dval = (double)val / dkilo;
strcpy(suffix, "K");
} else {
dval = val;
decimal = false;
}
if (!sigdigits) {
if (decimal)
snprintf(buf, bufsiz, "%.3g%s", dval, suffix);
else
snprintf(buf, bufsiz, "%d%s", (unsigned int)dval, suffix);
} else {
/* Always show sigdigits + 1, padded on right with zeroes
* followed by suffix */
int ndigits = sigdigits - 1 - (dval > 0.0 ? floor(log10(dval)) : 0);
snprintf(buf, bufsiz, "%*.*f%s", sigdigits + 1, ndigits, dval, suffix);
}
}
double cgpu_runtime(struct cgpu_info *cgpu)
{
struct timeval now;
double dev_runtime;
if (cgpu->dev_start_tv.tv_sec == 0)
dev_runtime = total_secs;
else {
cgtime(&now);
dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
}
if (dev_runtime < 1.0)
dev_runtime = 1.0;
return dev_runtime;
}
double tsince_restart(void)
{
struct timeval now;
cgtime(&now);
return tdiff(&now, &restart_tv_start);
}
double tsince_update(void)
{
struct timeval now;
cgtime(&now);
return tdiff(&now, &update_tv_start);
}
static void get_statline(char *buf, size_t bufsiz, struct cgpu_info *cgpu)
{
char displayed_hashes[16], displayed_rolling[16];
double dev_runtime, wu;
uint64_t dh64, dr64;
dev_runtime = cgpu_runtime(cgpu);
wu = cgpu->diff1 / dev_runtime * 60.0;
dh64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull;
dr64 = (double)cgpu->rolling * 1000000ull;
suffix_string(dh64, displayed_hashes, sizeof(displayed_hashes), 4);
suffix_string(dr64, displayed_rolling, sizeof(displayed_rolling), 4);
snprintf(buf, bufsiz, "%s %d ", cgpu->drv->name, cgpu->device_id);
cgpu->drv->get_statline_before(buf, bufsiz, cgpu);
tailsprintf(buf, bufsiz, "(%ds):%s (avg):%sh/s | A:%.0f R:%.0f HW:%d WU:%.1f/m",
opt_log_interval,
displayed_rolling,
displayed_hashes,
cgpu->diff_accepted,
cgpu->diff_rejected,
cgpu->hw_errors,
wu);
cgpu->drv->get_statline(buf, bufsiz, cgpu);
}
static bool shared_strategy(void)
{
return (pool_strategy == POOL_LOADBALANCE || pool_strategy == POOL_BALANCE);
}
#ifdef HAVE_CURSES
#define CURBUFSIZ 256
#define cg_mvwprintw(win, y, x, fmt, ...) do { \
char tmp42[CURBUFSIZ]; \
snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \
mvwprintw(win, y, x, "%s", tmp42); \
} while (0)
#define cg_wprintw(win, fmt, ...) do { \
char tmp42[CURBUFSIZ]; \
snprintf(tmp42, sizeof(tmp42), fmt, ##__VA_ARGS__); \
wprintw(win, "%s", tmp42); \
} while (0)
/* Must be called with curses mutex lock held and curses_active */
static void curses_print_status(void)
{
struct pool *pool = current_pool();
int linewidth = opt_widescreen ? 100 : 80;
wattron(statuswin, A_BOLD);
cg_mvwprintw(statuswin, 0, 0, " " PACKAGE " version " VERSION " - Started: %s", datestamp);
wattroff(statuswin, A_BOLD);
mvwhline(statuswin, 1, 0, '-', linewidth);
cg_mvwprintw(statuswin, 2, 0, " %s", statusline);
wclrtoeol(statuswin);
if (opt_widescreen) {
cg_mvwprintw(statuswin, 3, 0, " A:%.0f R:%.0f HW:%d WU:%.1f/m |"
" ST: %d SS: %"PRId64" NB: %d LW: %d GF: %d RF: %d",
total_diff_accepted, total_diff_rejected, hw_errors,
total_diff1 / total_secs * 60,
total_staged(), total_stale, new_blocks, local_work, total_go, total_ro);
} else if (alt_status) {
cg_mvwprintw(statuswin, 3, 0, " ST: %d SS: %"PRId64" NB: %d LW: %d GF: %d RF: %d",
total_staged(), total_stale, new_blocks, local_work, total_go, total_ro);
} else {
cg_mvwprintw(statuswin, 3, 0, " A:%.0f R:%.0f HW:%d WU:%.1f/m",
total_diff_accepted, total_diff_rejected, hw_errors,
total_diff1 / total_secs * 60);
}
wclrtoeol(statuswin);
if (shared_strategy() && total_pools > 1) {
cg_mvwprintw(statuswin, 4, 0, " Connected to multiple pools with%s block change notify",
have_longpoll ? "": "out");
} else if (pool->has_stratum) {
cg_mvwprintw(statuswin, 4, 0, " Connected to %s diff %s with stratum as user %s",
pool->sockaddr_url, pool->diff, pool->rpc_user);
} else {
cg_mvwprintw(statuswin, 4, 0, " Connected to %s diff %s with%s %s as user %s",
pool->sockaddr_url, pool->diff, have_longpoll ? "": "out",
pool->has_gbt ? "GBT" : "LP", pool->rpc_user);
}
wclrtoeol(statuswin);
cg_mvwprintw(statuswin, 5, 0, " Block: %s... Diff:%s Started: %s Best share: %s ",
prev_block, block_diff, blocktime, best_share);
mvwhline(statuswin, 6, 0, '-', linewidth);
mvwhline(statuswin, statusy - 1, 0, '-', linewidth);
#ifdef USE_USBUTILS
cg_mvwprintw(statuswin, devcursor - 1, 1, "[U]SB management [P]ool management [S]ettings [D]isplay options [Q]uit");
#else
cg_mvwprintw(statuswin, devcursor - 1, 1, "[P]ool management [S]ettings [D]isplay options [Q]uit");
#endif
}
static void adj_width(int var, int *length)
{
if ((int)(log10(var) + 1) > *length)
(*length)++;
}
static void adj_fwidth(float var, int *length)
{
if ((int)(log10(var) + 1) > *length)
(*length)++;
}
#define STATBEFORELEN 23
const char blanks[] = " ";
static void curses_print_devstatus(struct cgpu_info *cgpu, int devno, int count)
{
static int devno_width = 1, dawidth = 1, drwidth = 1, hwwidth = 1, wuwidth = 1;
char logline[256], unique_id[12];
struct timeval now;
double dev_runtime, wu;
unsigned int devstatlen;
if (opt_compact)
return;
if (devcursor + count > LINES - 2)
return;
if (count >= most_devices)
return;
if (cgpu->dev_start_tv.tv_sec == 0)
dev_runtime = total_secs;
else {
cgtime(&now);
dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
}
if (dev_runtime < 1.0)
dev_runtime = 1.0;
cgpu->utility = cgpu->accepted / dev_runtime * 60;
wu = cgpu->diff1 / dev_runtime * 60;
wmove(statuswin,devcursor + count, 0);
adj_width(devno, &devno_width);
if (cgpu->unique_id) {
unique_id[8] = '\0';
cg_memcpy(unique_id, blanks, 8);
strncpy(unique_id, cgpu->unique_id, 8);
} else
sprintf(unique_id, "%-8d", cgpu->device_id);
cg_wprintw(statuswin, " %*d: %s %-8s: ", devno_width, devno, cgpu->drv->name,
unique_id);
logline[0] = '\0';
cgpu->drv->get_statline_before(logline, sizeof(logline), cgpu);
devstatlen = strlen(logline);
if (devstatlen < STATBEFORELEN)
strncat(logline, blanks, STATBEFORELEN - devstatlen);
cg_wprintw(statuswin, "%s | ", logline);
#ifdef USE_USBUTILS
if (cgpu->usbinfo.nodev)
cg_wprintw(statuswin, "ZOMBIE");
else
#endif
if (cgpu->status == LIFE_DEAD)
cg_wprintw(statuswin, "DEAD ");
else if (cgpu->status == LIFE_SICK)
cg_wprintw(statuswin, "SICK ");
else if (cgpu->deven == DEV_DISABLED)
cg_wprintw(statuswin, "OFF ");
else if (cgpu->deven == DEV_RECOVER)
cg_wprintw(statuswin, "REST ");
else if (opt_widescreen) {
char displayed_hashes[16], displayed_rolling[16];
uint64_t d64;
d64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull;
suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4);
d64 = (double)cgpu->rolling * 1000000ull;
suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4);
adj_width(wu, &wuwidth);
adj_fwidth(cgpu->diff_accepted, &dawidth);
adj_fwidth(cgpu->diff_rejected, &drwidth);
adj_width(cgpu->hw_errors, &hwwidth);
cg_wprintw(statuswin, "%6s / %6sh/s WU:%*.1f/m "
"A:%*.0f R:%*.0f HW:%*d",
displayed_rolling,
displayed_hashes, wuwidth + 2, wu,
dawidth, cgpu->diff_accepted,
drwidth, cgpu->diff_rejected,
hwwidth, cgpu->hw_errors);
} else if (!alt_status) {
char displayed_hashes[16], displayed_rolling[16];
uint64_t d64;
d64 = (double)cgpu->total_mhashes / dev_runtime * 1000000ull;
suffix_string(d64, displayed_hashes, sizeof(displayed_hashes), 4);
d64 = (double)cgpu->rolling * 1000000ull;
suffix_string(d64, displayed_rolling, sizeof(displayed_rolling), 4);
adj_width(wu, &wuwidth);
cg_wprintw(statuswin, "%6s / %6sh/s WU:%*.1f/m", displayed_rolling,
displayed_hashes, wuwidth + 2, wu);
} else {
adj_fwidth(cgpu->diff_accepted, &dawidth);
adj_fwidth(cgpu->diff_rejected, &drwidth);
adj_width(cgpu->hw_errors, &hwwidth);
cg_wprintw(statuswin, "A:%*.0f R:%*.0f HW:%*d",
dawidth, cgpu->diff_accepted,
drwidth, cgpu->diff_rejected,
hwwidth, cgpu->hw_errors);
}
logline[0] = '\0';
cgpu->drv->get_statline(logline, sizeof(logline), cgpu);
cg_wprintw(statuswin, "%s", logline);
wclrtoeol(statuswin);
}
#endif
#ifdef HAVE_CURSES
/* Check for window resize. Called with curses mutex locked */
static inline void change_logwinsize(void)
{
int x, y, logx, logy;
getmaxyx(mainwin, y, x);
if (x < 80 || y < 25)
return;
if (y > statusy + 2 && statusy < logstart) {
if (y - 2 < logstart)
statusy = y - 2;
else
statusy = logstart;
logcursor = statusy + 1;
mvwin(logwin, logcursor, 0);
wresize(statuswin, statusy, x);
}
y -= logcursor;
getmaxyx(logwin, logy, logx);
/* Detect screen size change */
if (x != logx || y != logy)
wresize(logwin, y, x);
}
static void check_winsizes(void)
{
if (!use_curses)
return;
if (curses_active_locked()) {
int y, x;
erase();
x = getmaxx(statuswin);
if (logstart > LINES - 2)
statusy = LINES - 2;
else
statusy = logstart;
logcursor = statusy;
wresize(statuswin, statusy, x);
getmaxyx(mainwin, y, x);
y -= logcursor;
wresize(logwin, y, x);
mvwin(logwin, logcursor, 0);
unlock_curses();
}
}
static void disable_curses_windows(void);
static void enable_curses_windows(void);
static void switch_logsize(bool __maybe_unused newdevs)
{
if (curses_active_locked()) {
#ifdef WIN32
if (newdevs)
disable_curses_windows();
#endif
if (opt_compact) {
logstart = devcursor + 1;
logcursor = logstart + 1;
} else {
logstart = devcursor + most_devices + 1;
logcursor = logstart + 1;
}
#ifdef WIN32
if (newdevs)
enable_curses_windows();
#endif
unlock_curses();
check_winsizes();
}
}
/* For mandatory printing when mutex is already locked */
void _wlog(const char *str)
{
wprintw(logwin, "%s", str);
}
/* Mandatory printing */
void _wlogprint(const char *str)
{
if (curses_active_locked()) {
wprintw(logwin, "%s", str);
unlock_curses();
}
}
#endif
#ifdef HAVE_CURSES
bool log_curses_only(int prio, const char *datetime, const char *str)
{
bool high_prio;
high_prio = (prio == LOG_WARNING || prio == LOG_ERR);
if (curses_active_locked()) {
if (!opt_loginput || high_prio) {
wprintw(logwin, "%s%s\n", datetime, str);
if (high_prio) {
touchwin(logwin);
wrefresh(logwin);
}
}
unlock_curses();
return true;
}
return false;
}
void clear_logwin(void)
{
if (curses_active_locked()) {
erase();
wclear(logwin);
unlock_curses();
}
}
void logwin_update(void)
{
if (curses_active_locked()) {
touchwin(logwin);
wrefresh(logwin);
unlock_curses();
}
}
#endif
static void enable_pool(struct pool *pool)
{
if (pool->enabled != POOL_ENABLED) {
enabled_pools++;
pool->enabled = POOL_ENABLED;
}
}
#ifdef HAVE_CURSES
static void disable_pool(struct pool *pool)
{
if (pool->enabled == POOL_ENABLED)
enabled_pools--;
pool->enabled = POOL_DISABLED;
}
#endif
static void reject_pool(struct pool *pool)
{
if (pool->enabled == POOL_ENABLED)
enabled_pools--;
pool->enabled = POOL_REJECTING;
}
static void restart_threads(void);
/* Theoretically threads could race when modifying accepted and
* rejected values but the chance of two submits completing at the
* same time is zero so there is no point adding extra locking */
static void
share_result(json_t *val, json_t *res, json_t *err, const struct work *work,
char *hashshow, bool resubmit, char *worktime)
{
struct pool *pool = work->pool;
struct cgpu_info *cgpu;
cgpu = get_thr_cgpu(work->thr_id);
if (json_is_true(res) || (work->gbt && json_is_null(res))) {
mutex_lock(&stats_lock);
cgpu->accepted++;
total_accepted++;
pool->accepted++;
cgpu->diff_accepted += work->work_difficulty;
total_diff_accepted += work->work_difficulty;
pool->diff_accepted += work->work_difficulty;
mutex_unlock(&stats_lock);
pool->seq_rejects = 0;
cgpu->last_share_pool = pool->pool_no;
cgpu->last_share_pool_time = time(NULL);
cgpu->last_share_diff = work->work_difficulty;
pool->last_share_time = cgpu->last_share_pool_time;
pool->last_share_diff = work->work_difficulty;
applog(LOG_DEBUG, "PROOF OF WORK RESULT: true (yay!!!)");
if (!QUIET) {
if (total_pools > 1)
applog(LOG_NOTICE, "Accepted %s %s %d pool %d %s%s",
hashshow, cgpu->drv->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : "", worktime);
else
applog(LOG_NOTICE, "Accepted %s %s %d %s%s",
hashshow, cgpu->drv->name, cgpu->device_id, resubmit ? "(resubmit)" : "", worktime);
}
sharelog("accept", work);
if (opt_shares && total_diff_accepted >= opt_shares) {
applog(LOG_WARNING, "Successfully mined %d accepted shares as requested and exiting.", opt_shares);
kill_work();
return;
}
/* Detect if a pool that has been temporarily disabled for
* continually rejecting shares has started accepting shares.
* This will only happen with the work returned from a
* longpoll */
if (unlikely(pool->enabled == POOL_REJECTING)) {
applog(LOG_WARNING, "Rejecting pool %d now accepting shares, re-enabling!", pool->pool_no);
enable_pool(pool);
switch_pools(NULL);
}
/* If we know we found the block we know better than anyone
* that new work is needed. */
if (unlikely(work->block))
restart_threads();
} else {
mutex_lock(&stats_lock);
cgpu->rejected++;
total_rejected++;
pool->rejected++;
cgpu->diff_rejected += work->work_difficulty;
total_diff_rejected += work->work_difficulty;
pool->diff_rejected += work->work_difficulty;
pool->seq_rejects++;
mutex_unlock(&stats_lock);
applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)");
if (!QUIET) {
char where[20];
char disposition[36] = "reject";
char reason[32];
strcpy(reason, "");
if (total_pools > 1)
snprintf(where, sizeof(where), "pool %d", work->pool->pool_no);
else
strcpy(where, "");
if (!work->gbt)
res = json_object_get(val, "reject-reason");
if (res) {
const char *reasontmp = json_string_value(res);
size_t reasonLen = strlen(reasontmp);
if (reasonLen > 28)
reasonLen = 28;
reason[0] = ' '; reason[1] = '(';
cg_memcpy(2 + reason, reasontmp, reasonLen);
reason[reasonLen + 2] = ')'; reason[reasonLen + 3] = '\0';
cg_memcpy(disposition + 7, reasontmp, reasonLen);
disposition[6] = ':'; disposition[reasonLen + 7] = '\0';
} else if (work->stratum && err) {
if (json_is_array(err)) {
json_t *reason_val = json_array_get(err, 1);
char *reason_str;
if (reason_val && json_is_string(reason_val)) {
reason_str = (char *)json_string_value(reason_val);
snprintf(reason, 31, " (%s)", reason_str);
}
} else if (json_is_string(err)) {
const char *s = json_string_value(err);
snprintf(reason, 31, " (%s)", s);
}
}
applog(LOG_NOTICE, "Rejected %s %s %d %s%s %s%s",
hashshow, cgpu->drv->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : "", worktime);
sharelog(disposition, work);
}
/* Once we have more than a nominal amount of sequential rejects,
* at least 10 and more than 3 mins at the current utility,
* disable the pool because some pool error is likely to have
* ensued. Do not do this if we know the share just happened to
* be stale due to networking delays.
*/
if (pool->seq_rejects > 10 && !work->stale && opt_disable_pool && enabled_pools > 1) {
double utility = total_accepted / total_secs * 60;
if (pool->seq_rejects > utility * 3 && enabled_pools > 1) {
applog(LOG_WARNING, "Pool %d rejected %d sequential shares, disabling!",
pool->pool_no, pool->seq_rejects);
reject_pool(pool);
if (pool == current_pool())
switch_pools(NULL);
pool->seq_rejects = 0;
}
}
}
}
static void show_hash(struct work *work, char *hashshow)
{
unsigned char rhash[32];
char diffdisp[16];
unsigned long h32;
uint32_t *hash32;
uint64_t uintdiff;
int ofs;
swab256(rhash, work->hash);
for (ofs = 0; ofs <= 28; ofs ++) {
if (rhash[ofs])
break;
}
hash32 = (uint32_t *)(rhash + ofs);
h32 = be32toh(*hash32);
uintdiff = round(work->work_difficulty);
suffix_string(work->share_diff, diffdisp, sizeof (diffdisp), 0);
snprintf(hashshow, 64, "%08lx Diff %s/%"PRIu64"%s", h32, diffdisp, uintdiff,
work->block? " BLOCK!" : "");
}
#ifdef HAVE_LIBCURL
static void text_print_status(int thr_id)
{
struct cgpu_info *cgpu;
char logline[256];
cgpu = get_thr_cgpu(thr_id);
if (cgpu) {
get_statline(logline, sizeof(logline), cgpu);
printf("%s\n", logline);
}
}
static void print_status(int thr_id)
{
if (!curses_active)
text_print_status(thr_id);
}
static bool submit_upstream_work(struct work *work, CURL *curl, bool resubmit)
{
json_t *val, *res, *err;
char *s;
bool rc = false;
int thr_id = work->thr_id;
struct cgpu_info *cgpu = get_thr_cgpu(thr_id);
struct pool *pool = work->pool;
int rolltime;
struct timeval tv_submit, tv_submit_reply;
char hashshow[64 + 4] = "";
char worktime[200] = "";
struct timeval now;
double dev_runtime;
char gbt_block[1024], varint[12];
unsigned char data[80];
/* build JSON-RPC request */
flip80(data, work->data);
__bin2hex(gbt_block, data, 80); // 160 length
if (work->gbt_txns < 0xfd) {
uint8_t val8 = work->gbt_txns;
__bin2hex(varint, (const unsigned char *)&val8, 1);
} else if (work->gbt_txns <= 0xffff) {
uint16_t val16 = htole16(work->gbt_txns);
strcat(gbt_block, "fd"); // +2
__bin2hex(varint, (const unsigned char *)&val16, 2);
} else {
uint32_t val32 = htole32(work->gbt_txns);
strcat(gbt_block, "fe"); // +2
__bin2hex(varint, (const unsigned char *)&val32, 4);
}
strcat(gbt_block, varint); // +8 max
strcat(gbt_block, work->coinbase);
s = cgmalloc(1024);
sprintf(s, "{\"id\": 0, \"method\": \"submitblock\", \"params\": [\"%s", gbt_block);
/* Has submit/coinbase support */
if (!pool->has_gbt) {
cg_rlock(&pool->gbt_lock);
if (pool->txn_data)
s = realloc_strcat(s, pool->txn_data);
cg_runlock(&pool->gbt_lock);
}
if (work->job_id) {
s = realloc_strcat(s, "\", {\"workid\": \"");
s = realloc_strcat(s, work->job_id);
s = realloc_strcat(s, "\"}]}");
} else
s = realloc_strcat(s, "\"]}");
applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, s);
s = realloc_strcat(s, "\n");
cgtime(&tv_submit);
/* issue JSON-RPC request */
val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, false, false, &rolltime, pool, true);
cgtime(&tv_submit_reply);
free(s);
if (unlikely(!val)) {
applog(LOG_INFO, "submit_upstream_work json_rpc_call failed");
if (!pool_tset(pool, &pool->submit_fail)) {
total_ro++;
pool->remotefail_occasions++;
if (opt_lowmem) {
applog(LOG_WARNING, "Pool %d communication failure, discarding shares", pool->pool_no);
goto out;
}
applog(LOG_WARNING, "Pool %d communication failure, caching submissions", pool->pool_no);
}
cgsleep_ms(5000);
goto out;
} else if (pool_tclear(pool, &pool->submit_fail))
applog(LOG_WARNING, "Pool %d communication resumed, submitting work", pool->pool_no);
res = json_object_get(val, "result");
err = json_object_get(val, "error");
if (!QUIET) {
show_hash(work, hashshow);
if (opt_worktime) {
char workclone[20];
struct tm *tm, tm_getwork, tm_submit_reply;
double getwork_time = tdiff((struct timeval *)&(work->tv_getwork_reply),
(struct timeval *)&(work->tv_getwork));
double getwork_to_work = tdiff((struct timeval *)&(work->tv_work_start),
(struct timeval *)&(work->tv_getwork_reply));
double work_time = tdiff((struct timeval *)&(work->tv_work_found),
(struct timeval *)&(work->tv_work_start));
double work_to_submit = tdiff(&tv_submit,
(struct timeval *)&(work->tv_work_found));
double submit_time = tdiff(&tv_submit_reply, &tv_submit);
int diffplaces = 3;
time_t tmp_time = work->tv_getwork.tv_sec;
tm = localtime(&tmp_time);
cg_memcpy(&tm_getwork, tm, sizeof(struct tm));
tmp_time = tv_submit_reply.tv_sec;
tm = localtime(&tmp_time);
cg_memcpy(&tm_submit_reply, tm, sizeof(struct tm));
if (work->clone) {
snprintf(workclone, sizeof(workclone), "C:%1.3f",
tdiff((struct timeval *)&(work->tv_cloned),
(struct timeval *)&(work->tv_getwork_reply)));
}
else
strcpy(workclone, "O");
if (work->work_difficulty < 1)
diffplaces = 6;
snprintf(worktime, sizeof(worktime),
" <-%08lx.%08lx M:%c D:%1.*f G:%02d:%02d:%02d:%1.3f %s (%1.3f) W:%1.3f (%1.3f) S:%1.3f R:%02d:%02d:%02d",
(unsigned long)be32toh(*(uint32_t *)&(work->data[28])),
(unsigned long)be32toh(*(uint32_t *)&(work->data[24])),
work->getwork_mode, diffplaces, work->work_difficulty,
tm_getwork.tm_hour, tm_getwork.tm_min,
tm_getwork.tm_sec, getwork_time, workclone,
getwork_to_work, work_time, work_to_submit, submit_time,
tm_submit_reply.tm_hour, tm_submit_reply.tm_min,
tm_submit_reply.tm_sec);
}
}
share_result(val, res, err, work, hashshow, resubmit, worktime);
if (cgpu->dev_start_tv.tv_sec == 0)
dev_runtime = total_secs;
else {
cgtime(&now);
dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
}
if (dev_runtime < 1.0)
dev_runtime = 1.0;
cgpu->utility = cgpu->accepted / dev_runtime * 60;
if (!opt_realquiet)
print_status(thr_id);
if (!want_per_device_stats) {
char logline[256];
get_statline(logline, sizeof(logline), cgpu);
applog(LOG_INFO, "%s", logline);
}
json_decref(val);
rc = true;
out:
return rc;
}
#endif /* HAVE_LIBCURL */
/* Specifies whether we can use this pool for work or not. */
static bool pool_unusable(struct pool *pool)
{
if (pool->idle)
return true;
if (pool->enabled != POOL_ENABLED)
return true;
if (pool->has_stratum && (!pool->stratum_active || !pool->stratum_notify))
return true;
return false;
}
/* In balanced mode, the amount of diff1 solutions per pool is monitored as a
* rolling average per 10 minutes and if pools start getting more, it biases
* away from them to distribute work evenly. The share count is reset to the
* rolling average every 10 minutes to not send all work to one pool after it
* has been disabled/out for an extended period. */
static struct pool *select_balanced(struct pool *cp)
{
int i, lowest = cp->shares;
struct pool *ret = cp;
for (i = 0; i < total_pools; i++) {
struct pool *pool = pools[i];
if (pool_unusable(pool))
continue;
if (pool->shares < lowest) {
lowest = pool->shares;
ret = pool;
}
}
ret->shares++;
return ret;
}
static struct pool *priority_pool(int choice);
/* Select any active pool in a rotating fashion when loadbalance is chosen if
* it has any quota left. */
static inline struct pool *select_pool(void)
{
static int rotating_pool = 0;
struct pool *pool, *cp;
bool avail = false;
int tested, i;
cp = current_pool();
if (pool_strategy == POOL_BALANCE) {
pool = select_balanced(cp);
goto out;
}
if (pool_strategy != POOL_LOADBALANCE) {
pool = cp;
goto out;
} else
pool = NULL;
for (i = 0; i < total_pools; i++) {
struct pool *tp = pools[i];
if (tp->quota_used < tp->quota_gcd) {
avail = true;
break;
}
}
/* There are no pools with quota, so reset them. */
if (!avail) {
for (i = 0; i < total_pools; i++)
pools[i]->quota_used = 0;
if (++rotating_pool >= total_pools)
rotating_pool = 0;
}
/* Try to find the first pool in the rotation that is usable */
tested = 0;
while (!pool && tested++ < total_pools) {
pool = pools[rotating_pool];
if (pool->quota_used++ < pool->quota_gcd) {
if (!pool_unusable(pool))
break;
}
pool = NULL;
if (++rotating_pool >= total_pools)
rotating_pool = 0;
}
/* If there are no alive pools with quota, choose according to
* priority. */
if (!pool) {
for (i = 0; i < total_pools; i++) {
struct pool *tp = priority_pool(i);
if (!pool_unusable(tp)) {
pool = tp;
break;
}
}
}
/* If still nothing is usable, use the current pool */
if (!pool)
pool = cp;
out:
applog(LOG_DEBUG, "Selecting pool %d for work", pool->pool_no);
return pool;
}
/* truediffone == 0x00000000FFFF0000000000000000000000000000000000000000000000000000
* Generate a 256 bit binary LE target by cutting up diff into 64 bit sized
* portions or vice versa. */
static const double truediffone = 26959535291011309493156476344723991336010898738574164086137773096960.0;
static const double bits192 = 6277101735386680763835789423207666416102355444464034512896.0;
static const double bits128 = 340282366920938463463374607431768211456.0;
static const double bits64 = 18446744073709551616.0;
/* Converts a little endian 256 bit value to a double */
static double le256todouble(const void *target)
{
uint64_t *data64;
double dcut64;
data64 = (uint64_t *)(target + 24);
dcut64 = le64toh(*data64) * bits192;
data64 = (uint64_t *)(target + 16);
dcut64 += le64toh(*data64) * bits128;
data64 = (uint64_t *)(target + 8);
dcut64 += le64toh(*data64) * bits64;
data64 = (uint64_t *)(target);
dcut64 += le64toh(*data64);
return dcut64;
}