diff --git a/API-README b/API-README index ff48a3c5..a1c54c55 100644 --- a/API-README +++ b/API-README @@ -124,7 +124,10 @@ The list of requests - a (*) means it requires privileged access - and replies a Log Interval=N, <- log interval (--log N) Device Code=GPU ICA , <- spaced list of compiled devices OS=Linux/Apple/..., <- operating System - Failover-Only=true/false | <- failover-only setting + Failover-Only=true/false, <- failover-only setting + ScanTime=N, <- --scan-time setting + Queue=N, <- --queue setting + Expiry=N| <- --expiry setting summary SUMMARY The status summary of the miner e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...| @@ -259,6 +262,22 @@ The list of requests - a (*) means it requires privileged access - and replies a stating the results of the disable request This is only available if PGA mining is enabled + pgaidentify|N (*) + none There is no reply section just the STATUS section + stating the results of the identify request + This is only available if PGA mining is enabled + and currently only BFL singles support this command + On a BFL single it will flash the led on the front + of the device for appoximately 4s + All other non BFL PGA devices will return a warning + status message stating that they dont support it + This adds a 4s delay to the BFL share being processed + so you may get a message stating that procssing took + longer than 7000ms if the request was sent towards + the end of the timing of any work being worked on + e.g.: BFL0: took 8438ms - longer than 7000ms + You should ignore this + devdetails DEVDETAILS Each device with a list of their static details This lists all devices including those not supported by the 'devs' command @@ -287,6 +306,30 @@ The list of requests - a (*) means it requires privileged access - and replies a Current Block Hash=XXXX..., <- blank if none LP=true/false| <- LP is in use on at least 1 pool + debug|setting (*) + DEBUG Debug settings + The optional commands for 'setting' are the same as + the screen curses debug settings + You can only specify one setting + Only the first character is checked (case insensitive): + Silent, Quiet, Verbose, Debug, RPCProto, PerDevice, + WorkTime, Normal + The output fields are (as above): + Silent=true/false, + Quiet=true/false, + Verbose=true/false, + Debug=true/false, + RPCProto=true/false, + PerDevice=true/false, + WorkTime=true/false| + + setconfig|name,N (*) + none There is no reply section just the STATUS section + stating the results of setting 'name' to N + The valid values for name are currently: + queue, scantime, expiry + N is an integer in the range 0 to 9999 + When you enable, disable or restart a GPU or PGA, you will also get Thread messages in the cgminer status window @@ -306,8 +349,9 @@ windows Obviously, the JSON format is simply just the names as given before the '=' with the values after the '=' -If you enable cgminer debug (-D or --debug) you will also get messages showing -details of the requests received and the replies +If you enable cgminer debug (-D or --debug) or, when cgminer debug is off, +turn on debug with the API command 'debug|debug' you will also get messages +showing some details of the requests received and the replies There are included 4 program examples for accessing the API: @@ -339,13 +383,34 @@ miner.php - an example web page to access the API Feature Changelog for external applications using the API: -API V1.18 +API V1.19 + +Added API commands: + 'debug' + 'pgaidentify|N' (only works for BFL Singles so far) + 'setconfig|name,N' Modified API commands: - 'stats' - add 'Work Had Roll Time', 'Work Can Roll', 'Work Had Expire', - 'Work Roll Time' to the pool stats + 'devs' - add 'Diff1 Work', 'Difficulty Accepted', 'Difficulty Rejected', + 'Last Share Difficulty' to all devices + 'gpu|N' - add 'Diff1 Work', 'Difficulty Accepted', + 'Difficulty Rejected', 'Last Share Difficulty' + 'pga|N' - add 'Diff1 Work', 'Difficulty Accepted', + 'Difficulty Rejected', 'Last Share Difficulty' + 'notify' - add '*Dev Throttle' (for BFL Singles) + 'pools' - add 'Proxy Type', 'Proxy', 'Difficulty Accepted', 'Difficulty Rejected', + 'Difficulty Stale', 'Last Share Difficulty' + 'config' - add 'Queue', 'Expiry' + 'stats' - add 'Work Diff', 'Min Diff', 'Max Diff', 'Min Diff Count', + 'Max Diff Count' to the pool stats + +---------- + +API V1.18 (cgminer v2.7.4) Modified API commands: + 'stats' - add 'Work Had Roll Time', 'Work Can Roll', 'Work Had Expire', + 'Work Roll Time' to the pool stats 'config' - include 'ScanTime' ---------- @@ -866,6 +931,14 @@ e.g. $rigs = array('127.0.0.1:4028','myrig.com:4028:Sugoi'); --------- +Default: + $rigipsecurity = true; + +Set $rigipsecurity to false to show the IP/Port of the rig +in the socket error messages and also show the full socket message + +--------- + Default: $rigtotals = true; $forcerigtotals = false; diff --git a/NEWS b/NEWS index 6b104c30..115bf83c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,60 @@ +Version 2.7.6 - September 24, 2012 + +- Display share difficulty on log with a shortened hash display on submission. +- API stats add some pool getwork difficulty stats +- Ignore any pings pushed to the worker threads if the thread is still paused to +prevent it being enabled and disabled repeatedly. +- README - FAQ - usermod group - shouldn't remove other groups +- Test for sequential getwork failures on a pool that might actually be up but +failing to deliver work as we may end up hammering it repeatedly by mistake. +- reduce windows compile warnings +- util.c - bug - proxy - no data end condition +- As we average gpu time over 5 work intervals for dynamic GPU intensity, there +is no need to maintain a rolling average and it avoids the potential long term +corruption of a single overflow value. +- Test for the now-automatically exported variable AMDAPPSDKROOT when looking +for the presence of the OpenCL headers. +- API don't change 'Diff1 Shares' - backward compatability FTW +- miner.php highlighting correctly handling difficulty +- API - Add last share difficulty for devices and pool +- Store and report Accepted,Rejected,Stale difficulty in the summary and API +- WorkTime - display prevblock for scrypt +- api.c remove compile warnings +- Calculate work difficulty for each getwork and display with WorkTime debug +- remove MMQ unused variable warning +- FPGA - allow long or short device names in detect code + style police +- WorkTime - multiple nonce per work and identify the work source +- Optional WorkTime details with each Accepted/Rejected work item +- Icarus - ignore hardware errors in timing mode +- miner.php oops - mistype +- miner.php by default don't display IP/Port numbers in error messages +- api.c all STATUS messages automatically escaped +- api.c add missing escape for comma in MSG_PGAUNW +- API add display of and setting queue,scantime,expiry +- HW: dont submit bad shares +- save individual pool proxy settings to config +- --default-config - allow command line to define the default configuration file +for loading and saving +- API-README update for pools proxy info +- README URL proxy must use quote so show in the example +- bug: remove proxy: from the front of the proxy used +- CURL support for individual proxy per pool and all proxy types +- README spelling/etc +- README - FPGA device FAQ +- HW: error counter auto for all devices - ztex code not fixed +- API pgaidentify - unsupported message should be a warning +- API/BFL identify a device - currently only BFL to flash the led +- BFL add throttle count to internal stats + API +- BFL: missing device id in log message +- miner.php correct to new Diff1 Work field names +- API add device diff1 work +- API-README update +- api.c Correct diff1 field name +- count device diff1 shares +- API-README more debug parameter information +- API allow full debug settings control + + Version 2.7.5 - August 31, 2012 - Adjust opencl intensity when adjusting thread count to prevent it getting diff --git a/README b/README index 11caccc2..c034a7c5 100644 --- a/README +++ b/README @@ -168,7 +168,7 @@ Options for both config file and command line: --scrypt Use the scrypt algorithm for mining (litecoin only) --sharelog Append share log to file --shares Quit after mining N shares (default: unlimited) ---socks-proxy Set socks4 proxy (host:port) +--socks-proxy Set socks4 proxy (host:port) for all pools without a proxy specified --syslog Use system log for output messages (default: standard error) --temp-cutoff Temperature where a device will be automatically disabled, one value or comma separated list (default: 95) --text-only|-T Disable ncurses formatted screen output @@ -239,6 +239,7 @@ The official supplied binaries are compiled with support for all FPGAs. To force the code to only attempt detection with a specific driver, prepend the argument with the driver name followed by a colon. For example, "icarus:/dev/ttyUSB0" or "bitforce:\\.\COM5" +or using the short name: "ica:/dev/ttyUSB0" or "bfl:\\.\COM5" For other FPGA details see the FPGA-README @@ -295,6 +296,28 @@ Add overclocking settings, GPU and fan control with different engine settings fo cgminer -o http://pool:port -u username -p password -I 9 --auto-fan --auto-gpu --gpu-engine 750-950,945,700-930,960 --gpu-memclock 300 +Single pool with a standard http proxy, regular desktop: + +cgminer -o "http:proxy:port|http://pool:port" -u username -p password + +Single pool with a socks5 proxy, regular desktop: + +cgminer -o "socks5:proxy:port|http://pool:port" -u username -p password + +The list of proxy types are: + http: standard http 1.1 proxy + http0: http 1.0 proxy + socks4: socks4 proxy + socks5: socks5 proxy + socks4a: socks4a proxy + socks5h: socks5 proxy using a hostname + +If you compile cgminer with a version of CURL before 7.19.4 then some of the above will +not be available. All are available since CURL version 7.19.4 + +If you specify the --socks-proxy option to cgminer, it will only be applied to all pools +that don't specify their own proxy setting like above + READ WARNINGS AND DOCUMENTATION BELOW ABOUT OVERCLOCKING On Linux you virtually always need to export your display settings before @@ -451,6 +474,26 @@ it will log to a file called logfile.txt and otherwise work the same. There is also the -m option on linux which will spawn a command of your choice and pipe the output directly to that command. +The WorkTime details 'debug' option adds details on the end of each line +displayed for Accepted or Rejected work done. An example would be: + + <-00000059.ed4834a3 M:X D:1.0 G:17:02:38:0.405 C:1.855 (2.995) W:3.440 (0.000) S:0.461 R:17:02:47 + +The first 2 hex codes are the previous block hash, the rest are reported in +seconds unless stated otherwise: +The previous hash is followed by the getwork mode used M:X where X is one of +P:Pool, T:Test Pool, L:LP or B:Benchmark, +then D:d.ddd is the difficulty required to get a share from the work, +then G:hh:mm:ss:n.nnn, which is when the getwork or LP was sent to the pool and +the n.nnn is how long it took to reply, +followed by 'O' on it's own if it is an original getwork, or 'C:n.nnn' if it was +a clone with n.nnn stating how long after the work was recieved that it was cloned, +(m.mmm) is how long from when the original work was received until work started, +W:n.nnn is how long the work took to process until it was ready to submit, +(m.mmm) is how long from ready to submit to actually doing the submit, this is +usually 0.000 unless there was a problem with submitting the work, +S:n.nnn is how long it took to submit the completed work and await the reply, +R:hh:mm:ss is the actual time the work submit reply was received If you start cgminer with the --sharelog option, you can get detailed information for each share found. The argument to the option may be "-" for @@ -816,7 +859,7 @@ driver version and ATI stream version. Q: cgminer reports no devices or only one device on startup on Linux although I have multiple devices and drivers+SDK installed properly? -A: Try 'export DISPLAY=:0" before running cgminer. +A: Try "export DISPLAY=:0" before running cgminer. Q: My network gets slower and slower and then dies for a minute? A; Try the --net-delay option. @@ -843,10 +886,27 @@ They are Field-Programmable Gate Arrays that have been programmed to do Bitcoin mining. Since the acronym needs to be only 3 characters, the "Field-" part has been skipped. -Q: How do I get my BFL device to auto-recognise? -A: The only thing that needs to be done is to load the driver for them, which -on linux would require: -sudo modprobe ftdi_sio vendor=0x0403 product=0x6014 +Q: How do I get my BFL/Icarus/Lancelot/Cairnsmore device to auto-recognise? +A: On linux, if the /dev/ttyUSB* devices don't automatically appear, the only +thing that needs to be done is to load the driver for them: +BFL: sudo modprobe ftdi_sio vendor=0x0403 product=0x6014 +Icarus: sudo modprobe pl2303 vendor=0x067b product=0x230 +Lancelot: sudo modprobe ftdi_sio vendor=0x0403 product=0x6001 +Cairnsmore: sudo modprobe ftdi_sio product=0x8350 vendor=0x0403 +On windows you must install the pl2303 or ftdi driver required for the device +pl2303: http://prolificusa.com/pl-2303hx-drivers/ +ftdi: http://www.ftdichip.com/Drivers/VCP.htm + +Q: On linux I can see the /dev/ttyUSB* devices for my ICA/BFL/MMQ FPGA, but +cgminer can't mine on them +A: Make sure you have the required priviledges to access the /dev/ttyUSB* devices: + sudo ls -las /dev/ttyUSB* +will give output like: + 0 crw-rw---- 1 root dialout 188, 0 2012-09-11 13:49 /dev/ttyUSB0 +This means your account must have the group 'dialout' or root priviledges +To permanently give your account the 'dialout' group: + sudo usermod -G dialout -a `whoami` +Then logout and back in again --- diff --git a/api.c b/api.c index ca1b9429..8ebf94da 100644 --- a/api.c +++ b/api.c @@ -166,11 +166,13 @@ static const char SEPARATOR = '|'; #define SEPSTR "|" static const char GPUSEP = ','; -static const char *APIVERSION = "1.18"; +static const char *APIVERSION = "1.19"; static const char *DEAD = "Dead"; +#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) static const char *SICK = "Sick"; static const char *NOSTART = "NoStart"; static const char *INIT = "Initialising"; +#endif static const char *DISABLED = "Disabled"; static const char *ALIVE = "Alive"; static const char *REJECTING = "Rejecting"; @@ -258,6 +260,8 @@ static const char *OSINFO = #define _MINESTATS "STATS" #define _CHECK "CHECK" #define _MINECOIN "COIN" +#define _DEBUGSET "DEBUG" +#define _SETCONFIG "SETCONFIG" static const char ISJSON = '{'; #define JSON0 "{" @@ -295,6 +299,8 @@ static const char ISJSON = '{'; #define JSON_MINESTATS JSON1 _MINESTATS JSON2 #define JSON_CHECK JSON1 _CHECK JSON2 #define JSON_MINECOIN JSON1 _MINECOIN JSON2 +#define JSON_DEBUGSET JSON1 _DEBUGSET JSON2 +#define JSON_SETCONFIG JSON1 _SETCONFIG JSON2 #define JSON_END JSON4 JSON5 static const char *JSON_COMMAND = "command"; @@ -390,6 +396,14 @@ static const char *JSON_PARAMETER = "parameter"; #define MSG_INVBOOL 76 #define MSG_FOO 77 #define MSG_MINECOIN 78 +#define MSG_DEBUGSET 79 +#define MSG_PGAIDENT 80 +#define MSG_PGANOID 81 +#define MSG_SETCONFIG 82 +#define MSG_UNKCON 83 +#define MSG_INVNUM 84 +#define MSG_CONPAR 85 +#define MSG_CONVAL 86 enum code_severity { SEVERITY_ERR, @@ -418,6 +432,7 @@ enum code_parameters { PARAM_STR, PARAM_BOTH, PARAM_BOOL, + PARAM_SET, PARAM_NONE }; @@ -543,12 +558,24 @@ struct CODES { { SEVERITY_ERR, MSG_INVBOOL, PARAM_NONE, "Invalid parameter should be true or false" }, { SEVERITY_SUCC, MSG_FOO, PARAM_BOOL, "Failover-Only set to %s" }, { SEVERITY_SUCC, MSG_MINECOIN,PARAM_NONE, "CGMiner coin" }, + { SEVERITY_SUCC, MSG_DEBUGSET,PARAM_STR, "Debug settings" }, +#ifdef HAVE_AN_FPGA + { SEVERITY_SUCC, MSG_PGAIDENT,PARAM_PGA, "Identify command sent to PGA%d" }, + { SEVERITY_WARN, MSG_PGANOID, PARAM_PGA, "PGA%d does not support identify" }, +#endif + { SEVERITY_SUCC, MSG_SETCONFIG,PARAM_SET, "Set config '%s' to %d" }, + { SEVERITY_ERR, MSG_UNKCON, PARAM_STR, "Unknown config '%s'" }, + { SEVERITY_ERR, MSG_INVNUM, PARAM_BOTH, "Invalid number (%d) for '%s' range is 0-9999" }, + { SEVERITY_ERR, MSG_CONPAR, PARAM_NONE, "Missing config parameters 'name,N'" }, + { SEVERITY_ERR, MSG_CONVAL, PARAM_STR, "Missing config value N for '%s,N'" }, { SEVERITY_FAIL, 0, 0, NULL } }; static int my_thr_id = 0; static bool bye; +#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) static bool ping = true; +#endif // Used to control quit restart access to shutdown variables static pthread_mutex_t quit_restart_lock; @@ -749,6 +776,7 @@ static struct api_data *api_add_data_full(struct api_data *root, char *name, enu case API_UTILITY: case API_FREQ: case API_HS: + case API_DIFF: api_data->data = (void *)malloc(sizeof(double)); *((double *)(api_data->data)) = *((double *)data); break; @@ -875,6 +903,11 @@ struct api_data *api_add_hs(struct api_data *root, char *name, double *data, boo return api_add_data_full(root, name, API_HS, (void *)data, copy_data); } +struct api_data *api_add_diff(struct api_data *root, char *name, double *data, bool copy_data) +{ + return api_add_data_full(root, name, API_DIFF, (void *)data, copy_data); +} + static struct api_data *print_data(struct api_data *root, char *buf, bool isjson) { struct api_data *tmp; @@ -946,6 +979,9 @@ static struct api_data *print_data(struct api_data *root, char *buf, bool isjson case API_HS: sprintf(buf, "%.15f", *((double *)(root->data))); break; + case API_DIFF: + sprintf(buf, "%.8f", *((double *)(root->data))); + break; case API_BOOL: sprintf(buf, "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR); break; @@ -1155,6 +1191,9 @@ static char *message(int messageid, int paramid, char *param2, bool isjson) case PARAM_BOOL: sprintf(buf, codes[i].description, paramid ? TRUESTR : FALSESTR); break; + case PARAM_SET: + sprintf(buf, codes[i].description, param2, paramid); + break; case PARAM_NONE: default: strcpy(buf, codes[i].description); @@ -1163,8 +1202,8 @@ static char *message(int messageid, int paramid, char *param2, bool isjson) root = api_add_string(root, _STATUS, severity, false); root = api_add_time(root, "When", &when, false); root = api_add_int(root, "Code", &messageid, false); - root = api_add_string(root, "Msg", buf, false); - root = api_add_string(root, "Description", opt_api_description, false); + root = api_add_escape(root, "Msg", buf, false); + root = api_add_escape(root, "Description", opt_api_description, false); root = print_data(root, ptr, isjson); if (isjson) @@ -1178,8 +1217,8 @@ static char *message(int messageid, int paramid, char *param2, bool isjson) int id = -1; root = api_add_int(root, "Code", &id, false); sprintf(buf, "%d", messageid); - root = api_add_string(root, "Msg", buf, false); - root = api_add_string(root, "Description", opt_api_description, false); + root = api_add_escape(root, "Msg", buf, false); + root = api_add_escape(root, "Description", opt_api_description, false); root = print_data(root, ptr, isjson); if (isjson) @@ -1257,6 +1296,8 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, root = api_add_const(root, "OS", OSINFO, false); root = api_add_bool(root, "Failover-Only", &opt_fail_only, false); root = api_add_int(root, "ScanTime", &opt_scantime, false); + root = api_add_int(root, "Queue", &opt_queue, false); + root = api_add_int(root, "Expiry", &opt_expiry, false); root = print_data(root, buf, isjson); if (isjson) @@ -1264,6 +1305,7 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, strcat(io_buffer, buf); } +#if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) static const char *status2str(enum alive status) { switch (status) { @@ -1281,6 +1323,7 @@ static const char *status2str(enum alive status) return UNKNOWN; } } +#endif #ifdef HAVE_OPENCL static void gpustatus(int gpu, bool isjson) @@ -1341,6 +1384,10 @@ static void gpustatus(int gpu, bool isjson) root = api_add_int(root, "Last Share Pool", &last_share_pool, false); root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); + root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), false); + root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); + root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); + root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); root = print_data(root, buf, isjson); strcat(io_buffer, buf); @@ -1424,6 +1471,10 @@ static void pgastatus(int pga, bool isjson) root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); root = api_add_freq(root, "Frequency", &frequency, false); + root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), false); + root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); + root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); + root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); root = print_data(root, buf, isjson); strcat(io_buffer, buf); @@ -1456,6 +1507,10 @@ static void cpustatus(int cpu, bool isjson) root = api_add_int(root, "Last Share Pool", &last_share_pool, false); root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); + root = api_add_int(root, "Diff1 Work", &(cgpu->diff1), false); + root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); + root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); + root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); root = print_data(root, buf, isjson); strcat(io_buffer, buf); @@ -1693,6 +1748,44 @@ static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __ strcpy(io_buffer, message(MSG_PGADIS, id, NULL, isjson)); } + +static void pgaidentify(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) +{ + int numpga = numpgas(); + int id; + + if (numpga == 0) { + strcpy(io_buffer, message(MSG_PGANON, 0, NULL, isjson)); + return; + } + + if (param == NULL || *param == '\0') { + strcpy(io_buffer, message(MSG_MISID, 0, NULL, isjson)); + return; + } + + id = atoi(param); + if (id < 0 || id >= numpga) { + strcpy(io_buffer, message(MSG_INVPGA, id, NULL, isjson)); + return; + } + + int dev = pgadevice(id); + if (dev < 0) { // Should never happen + strcpy(io_buffer, message(MSG_INVPGA, id, NULL, isjson)); + return; + } + + struct cgpu_info *cgpu = devices[dev]; + struct device_api *api = cgpu->api; + + if (!api->identify_device) + strcpy(io_buffer, message(MSG_PGANOID, id, NULL, isjson)); + else { + api->identify_device(cgpu); + strcpy(io_buffer, message(MSG_PGAIDENT, id, NULL, isjson)); + } +} #endif #ifdef WANT_CPUMINE @@ -1790,6 +1883,17 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, root = api_add_escape(root, "User", pool->rpc_user, false); root = api_add_time(root, "Last Share Time", &(pool->last_share_time), false); root = api_add_int(root, "Diff1 Shares", &(pool->diff1), false); + if (pool->rpc_proxy) { + root = api_add_const(root, "Proxy Type", proxytype(pool->rpc_proxytype), false); + root = api_add_escape(root, "Proxy", pool->rpc_proxy, false); + } else { + root = api_add_const(root, "Proxy Type", BLANK, false); + root = api_add_const(root, "Proxy", BLANK, false); + } + root = api_add_diff(root, "Difficulty Accepted", &(pool->diff_accepted), false); + root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false); + root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false); + root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false); if (isjson && (i > 0)) strcat(io_buffer, COMMA); @@ -1842,6 +1946,9 @@ static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo root = api_add_uint(root, "Network Blocks", &(new_blocks), false); root = api_add_mhtotal(root, "Total MH", &(total_mhashes_done), false); root = api_add_utility(root, "Work Utility", &(work_utility), false); + root = api_add_diff(root, "Difficulty Accepted", &(total_diff_accepted), false); + root = api_add_diff(root, "Difficulty Rejected", &(total_diff_rejected), false); + root = api_add_diff(root, "Difficulty Stale", &(total_diff_stale), false); root = print_data(root, buf, isjson); if (isjson) @@ -2554,6 +2661,7 @@ void notifystatus(int device, struct cgpu_info *cgpu, bool isjson, __maybe_unuse root = api_add_int(root, "*Dev Over Heat", &(cgpu->dev_over_heat_count), false); root = api_add_int(root, "*Dev Thermal Cutoff", &(cgpu->dev_thermal_cutoff_count), false); root = api_add_int(root, "*Dev Comms Error", &(cgpu->dev_comms_error_count), false); + root = api_add_int(root, "*Dev Throttle", &(cgpu->dev_throttle_count), false); if (isjson && (device > 0)) strcat(io_buffer, COMMA); @@ -2681,6 +2789,11 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, struct cgmine root = api_add_bool(root, "Work Can Roll", &(pool_stats->canroll), false); root = api_add_bool(root, "Work Had Expire", &(pool_stats->hadexpire), false); root = api_add_uint32(root, "Work Roll Time", &(pool_stats->rolltime), false); + root = api_add_diff(root, "Work Diff", &(pool_stats->last_diff), false); + root = api_add_diff(root, "Min Diff", &(pool_stats->min_diff), false); + root = api_add_diff(root, "Max Diff", &(pool_stats->max_diff), false); + root = api_add_uint32(root, "Min Diff Count", &(pool_stats->min_diff_count), false); + root = api_add_uint32(root, "Max Diff Count", &(pool_stats->max_diff_count), false); } if (extra) @@ -2772,7 +2885,7 @@ static void minecoin(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo #endif root = api_add_const(root, "Hash Method", SHA256STR, false); - mutex_lock(&ch_lock); + mutex_lock(&ch_lock); if (current_fullhash && *current_fullhash) { root = api_add_timeval(root, "Current Block Time", &block_timeval, true); root = api_add_string(root, "Current Block Hash", current_fullhash, true); @@ -2781,7 +2894,7 @@ static void minecoin(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo root = api_add_timeval(root, "Current Block Time", &t, true); root = api_add_const(root, "Current Block Hash", BLANK, false); } - mutex_unlock(&ch_lock); + mutex_unlock(&ch_lock); root = api_add_bool(root, "LP", &have_longpoll, false); @@ -2791,6 +2904,115 @@ static void minecoin(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo strcat(io_buffer, buf); } +static void debugstate(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) +{ + struct api_data *root = NULL; + char buf[TMPBUFSIZ]; + + if (param == NULL) + param = (char *)BLANK; + else + *param = tolower(*param); + + switch(*param) { + case 's': + opt_realquiet = true; + break; + case 'q': + opt_quiet ^= true; + break; + case 'v': + opt_log_output ^= true; + if (opt_log_output) + opt_quiet = false; + break; + case 'd': + opt_debug ^= true; + opt_log_output = opt_debug; + if (opt_debug) + opt_quiet = false; + break; + case 'r': + opt_protocol ^= true; + if (opt_protocol) + opt_quiet = false; + break; + case 'p': + want_per_device_stats ^= true; + opt_log_output = want_per_device_stats; + break; + case 'n': + opt_log_output = false; + opt_debug = false; + opt_quiet = false; + opt_protocol = false; + want_per_device_stats = false; + opt_worktime = false; + break; + case 'w': + opt_worktime ^= true; + break; + default: + // anything else just reports the settings + break; + } + + sprintf(io_buffer, isjson + ? "%s," JSON_DEBUGSET + : "%s" _DEBUGSET ",", + message(MSG_DEBUGSET, 0, NULL, isjson)); + + root = api_add_bool(root, "Silent", &opt_realquiet, false); + root = api_add_bool(root, "Quiet", &opt_quiet, false); + root = api_add_bool(root, "Verbose", &opt_log_output, false); + root = api_add_bool(root, "Debug", &opt_debug, false); + root = api_add_bool(root, "RPCProto", &opt_protocol, false); + root = api_add_bool(root, "PerDevice", &want_per_device_stats, false); + root = api_add_bool(root, "WorkTime", &opt_worktime, false); + + root = print_data(root, buf, isjson); + if (isjson) + strcat(buf, JSON_CLOSE); + strcat(io_buffer, buf); +} + +static void setconfig(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) +{ + char *comma; + int value; + + if (param == NULL || *param == '\0') { + strcpy(io_buffer, message(MSG_CONPAR, 0, NULL, isjson)); + return; + } + + comma = strchr(param, ','); + if (!comma) { + strcpy(io_buffer, message(MSG_CONVAL, 0, param, isjson)); + return; + } + + *(comma++) = '\0'; + value = atoi(comma); + if (value < 0 || value > 9999) { + strcpy(io_buffer, message(MSG_INVNUM, value, param, isjson)); + return; + } + + if (strcasecmp(param, "queue") == 0) + opt_queue = value; + else if (strcasecmp(param, "scantime") == 0) + opt_scantime = value; + else if (strcasecmp(param, "expiry") == 0) + opt_expiry = value; + else { + strcpy(io_buffer, message(MSG_UNKCON, 0, param, isjson)); + return; + } + + strcpy(io_buffer, message(MSG_SETCONFIG, value, param, isjson)); +} + static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, char group); struct CMDS { @@ -2813,6 +3035,7 @@ struct CMDS { { "pga", pgadev, false }, { "pgaenable", pgaenable, true }, { "pgadisable", pgadisable, true }, + { "pgaidentify", pgaidentify, true }, #endif #ifdef WANT_CPUMINE { "cpu", cpudev, false }, @@ -2843,6 +3066,8 @@ struct CMDS { { "check", checkcommand, false }, { "failover-only", failoveronly, true }, { "coin", minecoin, false }, + { "debug", debugstate, true }, + { "setconfig", setconfig, true }, { NULL, NULL, false } }; diff --git a/cgminer.c b/cgminer.c index c9114b28..10c92207 100644 --- a/cgminer.c +++ b/cgminer.c @@ -86,14 +86,14 @@ static char packagename[255]; bool opt_protocol; static bool opt_benchmark; bool have_longpoll; -static bool want_per_device_stats; +bool want_per_device_stats; bool use_syslog; bool opt_quiet; -static bool opt_realquiet; +bool opt_realquiet; bool opt_loginput; const int opt_cutofftemp = 95; int opt_log_interval = 5; -static int opt_queue = 1; +int opt_queue = 1; int opt_scantime = 60; int opt_expiry = 120; int opt_bench_algo = -1; @@ -142,6 +142,7 @@ bool opt_delaynet; bool opt_disable_pool = true; char *opt_icarus_options = NULL; char *opt_icarus_timing = NULL; +bool opt_worktime; char *opt_kernel_path; char *cgminer_path; @@ -186,6 +187,7 @@ pthread_mutex_t control_lock; int hw_errors; int total_accepted, total_rejected, total_diff1; int total_getworks, total_stale, total_discarded; +double total_diff_accepted, total_diff_rejected, total_diff_stale; static int total_queued, staged_rollable; unsigned int new_blocks; static unsigned int work_block; @@ -226,6 +228,7 @@ static struct block *blocks = NULL; char *opt_socks_proxy = NULL; static const char def_conf[] = "cgminer.conf"; +static char *default_config; static bool config_loaded; static int include_count; #define JSON_INCLUDE_CONF "include" @@ -357,7 +360,7 @@ static void sharelog(const char*disposition, const struct work*work) thr_id = work->thr_id; cgpu = thr_info[thr_id].cgpu; pool = work->pool; - t = (unsigned long int)work->share_found_time; + t = (unsigned long int)(work->tv_work_found.tv_sec); target = bin2hex(work->target, sizeof(work->target)); if (unlikely(!target)) { applog(LOG_ERR, "sharelog target OOM"); @@ -419,6 +422,8 @@ static struct pool *add_pool(void) /* Make sure the pool doesn't think we've been idle since time 0 */ pool->tv_idle.tv_sec = ~0UL; + pool->rpc_proxy = NULL; + return pool; } @@ -547,6 +552,8 @@ static char *set_url(char *arg) add_pool(); pool = pools[total_urls - 1]; + arg = get_proxy(arg, pool); + opt_set_charp(arg, &pool->rpc_url); if (strncmp(arg, "http://", 7) && strncmp(arg, "https://", 8)) { @@ -1058,6 +1065,9 @@ static struct opt_table opt_config_table[] = { OPT_WITH_ARG("--userpass|-O", set_userpass, NULL, NULL, "Username:Password pair for bitcoin JSON-RPC server"), + OPT_WITHOUT_ARG("--worktime", + opt_set_bool, &opt_worktime, + "Display extra work time debug information"), OPT_WITH_ARG("--pools", opt_set_bool, NULL, NULL, opt_hidden), OPT_ENDTABLE @@ -1171,21 +1181,21 @@ static char *load_config(const char *arg, void __maybe_unused *unused) 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 = malloc(PATH_MAX); -#if defined(unix) - if (getenv("HOME") && *getenv("HOME")) { - strcpy(cnfbuf, getenv("HOME")); - strcat(cnfbuf, "/"); - } else - strcpy(cnfbuf, ""); - strcat(cnfbuf, ".cgminer/"); -#else - strcpy(cnfbuf, ""); -#endif - strcat(cnfbuf, def_conf); + default_save_file(cnfbuf); + if (!access(cnfbuf, R_OK)) load_config(cnfbuf, NULL); else { @@ -1233,6 +1243,10 @@ static struct opt_table opt_cmdline_table[] = { load_config, NULL, NULL, "Load a JSON-format configuration file\n" "See example.conf for an example configuration."), + OPT_WITH_ARG("--default-config", + set_default_config, NULL, 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"), @@ -1755,7 +1769,9 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub struct pool *pool = work->pool; int rolltime; uint32_t *hash32; + struct timeval tv_submit, tv_submit_reply; char hashshow[64+1] = ""; + char worktime[200] = ""; #ifdef __BIG_ENDIAN__ int swapcounter = 0; @@ -1780,8 +1796,10 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub applog(LOG_DEBUG, "DBG: sending %s submit RPC call: %s", pool->rpc_url, sd); + gettimeofday(&tv_submit, NULL); /* issue JSON-RPC request */ val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, s, false, false, &rolltime, pool, true); + gettimeofday(&tv_submit_reply, NULL); if (unlikely(!val)) { applog(LOG_INFO, "submit_upstream_work json_rpc_call failed"); if (!pool_tset(pool, &pool->submit_fail)) { @@ -1800,9 +1818,52 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub if (opt_scrypt) sprintf(hashshow, "%08lx.%08lx", (unsigned long)(hash32[7]), (unsigned long)(hash32[6])); else { - sprintf(hashshow, "%08lx.%08lx%s", (unsigned long)(hash32[6]), (unsigned long)(hash32[5]), + int intdiff = round(work->work_difficulty); + + sprintf(hashshow, "%08lx Diff %d%s", (unsigned long)(hash32[6]), intdiff, work->block? " BLOCK!" : ""); } + + 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; + + tm = localtime(&(work->tv_getwork.tv_sec)); + memcpy(&tm_getwork, tm, sizeof(struct tm)); + tm = localtime(&(tv_submit_reply.tv_sec)); + memcpy(&tm_submit_reply, tm, sizeof(struct tm)); + + if (work->clone) { + sprintf(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; + + sprintf(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)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 32 : 28])), + (unsigned long)swab32(*(uint32_t *)&(work->data[opt_scrypt ? 28 : 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); + } } /* Theoretically threads could race when modifying accepted and @@ -1812,18 +1873,23 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub cgpu->accepted++; total_accepted++; pool->accepted++; + cgpu->diff_accepted += work->work_difficulty; + total_diff_accepted += work->work_difficulty; + pool->diff_accepted += work->work_difficulty; 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", - hashshow, cgpu->api->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : ""); + applog(LOG_NOTICE, "Accepted %s %s %d pool %d %s%s", + hashshow, cgpu->api->name, cgpu->device_id, work->pool->pool_no, resubmit ? "(resubmit)" : "", worktime); else - applog(LOG_NOTICE, "Accepted %s %s %d %s", - hashshow, cgpu->api->name, cgpu->device_id, resubmit ? "(resubmit)" : ""); + applog(LOG_NOTICE, "Accepted %s %s %d %s%s", + hashshow, cgpu->api->name, cgpu->device_id, resubmit ? "(resubmit)" : "", worktime); } sharelog("accept", work); if (opt_shares && total_accepted >= opt_shares) { @@ -1845,6 +1911,9 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub 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++; applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)"); if (!QUIET) { @@ -1872,8 +1941,8 @@ static bool submit_upstream_work(const struct work *work, CURL *curl, bool resub } else strcpy(reason, ""); - applog(LOG_NOTICE, "Rejected %s %s %d %s%s %s", - hashshow, cgpu->api->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : ""); + applog(LOG_NOTICE, "Rejected %s %s %d %s%s %s%s", + hashshow, cgpu->api->name, cgpu->device_id, where, reason, resubmit ? "(resubmit)" : "", worktime); sharelog(disposition, work); } @@ -1973,6 +2042,42 @@ static inline struct pool *select_pool(bool lagging) return pool; } +static double DIFFEXACTONE = 26959946667150639794667015087019630673637144422540572481103610249216.0; + +/* + * Calculate the work share difficulty + */ +static void calc_diff(struct work *work) +{ + struct cgminer_pool_stats *pool_stats = &(work->pool->cgminer_pool_stats); + double targ; + int i; + + targ = 0; + for (i = 31; i >= 0; i--) { + targ *= 256; + targ += work->target[i]; + } + + work->work_difficulty = DIFFEXACTONE / (targ ? : DIFFEXACTONE); + + pool_stats->last_diff = work->work_difficulty; + + if (work->work_difficulty == pool_stats->min_diff) + pool_stats->min_diff_count++; + else if (work->work_difficulty < pool_stats->min_diff || pool_stats->min_diff == 0) { + pool_stats->min_diff = work->work_difficulty; + pool_stats->min_diff_count = 1; + } + + if (work->work_difficulty == pool_stats->max_diff) + pool_stats->max_diff_count++; + else if (work->work_difficulty > pool_stats->max_diff) { + pool_stats->max_diff = work->work_difficulty; + pool_stats->max_diff_count = 1; + } +} + static void get_benchmark_work(struct work *work) { // Use a random work block pulled from a pool @@ -1985,13 +2090,17 @@ static void get_benchmark_work(struct work *work) memcpy(work, &bench_block, min_size); work->mandatory = true; work->pool = pools[0]; + gettimeofday(&(work->tv_getwork), NULL); + memcpy(&(work->tv_getwork_reply), &(work->tv_getwork), sizeof(struct timeval)); + work->getwork_mode = GETWORK_MODE_BENCHMARK; + calc_diff(work); } static bool get_upstream_work(struct work *work, CURL *curl) { struct pool *pool = work->pool; struct cgminer_pool_stats *pool_stats = &(pool->cgminer_pool_stats); - struct timeval tv_start, tv_end, tv_elapsed; + struct timeval tv_elapsed; json_t *val = NULL; bool rc = false; char *url; @@ -2000,7 +2109,7 @@ static bool get_upstream_work(struct work *work, CURL *curl) url = pool->rpc_url; - gettimeofday(&tv_start, NULL); + gettimeofday(&(work->tv_getwork), NULL); val = json_rpc_call(curl, url, pool->rpc_userpass, rpc_req, false, false, &work->rolltime, pool, false); @@ -2013,8 +2122,8 @@ static bool get_upstream_work(struct work *work, CURL *curl) } else applog(LOG_DEBUG, "Failed json_rpc_call in get_upstream_work"); - gettimeofday(&tv_end, NULL); - timersub(&tv_end, &tv_start, &tv_elapsed); + gettimeofday(&(work->tv_getwork_reply), NULL); + timersub(&(work->tv_getwork_reply), &(work->tv_getwork), &tv_elapsed); pool_stats->getwork_wait_rolling += ((double)tv_elapsed.tv_sec + ((double)tv_elapsed.tv_usec / 1000000)) * 0.63; pool_stats->getwork_wait_rolling /= 1.63; @@ -2031,6 +2140,8 @@ static bool get_upstream_work(struct work *work, CURL *curl) work->pool = pool; work->longpoll = false; + work->getwork_mode = GETWORK_MODE_POOL; + calc_diff(work); total_getworks++; pool->getwork_requested++; @@ -2339,6 +2450,7 @@ static struct work *make_clone(struct work *work) memcpy(work_clone, work, sizeof(struct work)); work_clone->clone = true; + gettimeofday((struct timeval *)&(work_clone->tv_cloned), NULL); work_clone->longpoll = false; work_clone->mandatory = false; /* Make cloned work appear slightly older to bias towards keeping the @@ -2383,6 +2495,15 @@ out: static bool queue_request(void); +static void pool_died(struct pool *pool) +{ + if (!pool_tset(pool, &pool->idle)) { + applog(LOG_WARNING, "Pool %d %s not responding!", pool->pool_no, pool->rpc_url); + gettimeofday(&pool->tv_idle, NULL); + switch_pools(NULL); + } +} + static void *get_work_thread(void *userdata) { struct workio_cmd *wc = (struct workio_cmd *)userdata; @@ -2415,13 +2536,17 @@ static void *get_work_thread(void *userdata) /* obtain new work from bitcoin via JSON-RPC */ if (!get_upstream_work(ret_work, ce->curl)) { - /* pause, then restart work-request loop */ applog(LOG_DEBUG, "json_rpc_call failed on get work, retrying"); dec_queued(pool); + /* Make sure the pool just hasn't stopped serving + * requests but is up as we'll keep hammering it */ + if (++pool->seq_getfails > mining_threads + opt_queue) + pool_died(pool); queue_request(); free_work(ret_work); goto out; } + pool->seq_getfails = 0; ret_work->queued = true; } @@ -2537,6 +2662,8 @@ static void *submit_work_thread(void *userdata) sharelog("discard", work); total_stale++; pool->stale_shares++; + total_diff_stale += work->work_difficulty; + pool->diff_stale += work->work_difficulty; goto out; } work->stale = true; @@ -2550,6 +2677,8 @@ static void *submit_work_thread(void *userdata) applog(LOG_NOTICE, "Share became stale while retrying submit, discarding"); total_stale++; pool->stale_shares++; + total_diff_stale += work->work_difficulty; + pool->diff_stale += work->work_difficulty; break; } @@ -2996,6 +3125,8 @@ static void display_pool_summary(struct pool *pool) wlog(" Share submissions: %d\n", pool->accepted + pool->rejected); wlog(" Accepted shares: %d\n", pool->accepted); wlog(" Rejected shares: %d\n", pool->rejected); + wlog(" Accepted difficulty shares: %1.f\n", pool->diff_accepted); + wlog(" Rejected difficulty shares: %1.f\n", pool->diff_rejected); if (pool->accepted || pool->rejected) wlog(" Reject ratio: %.1f%%\n", (double)(pool->rejected * 100) / (double)(pool->accepted + pool->rejected)); efficiency = pool->getwork_requested ? pool->accepted * 100.0 / pool->getwork_requested : 0.0; @@ -3089,7 +3220,11 @@ void write_config(FILE *fcfg) /* Write pool values */ fputs("{\n\"pools\" : [", fcfg); for(i = 0; i < total_pools; i++) { - fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s\",", i > 0 ? "," : "", json_escape(pools[i]->rpc_url)); + fprintf(fcfg, "%s\n\t{\n\t\t\"url\" : \"%s%s%s%s\",", i > 0 ? "," : "", + pools[i]->rpc_proxy ? json_escape((char *)proxytype(pools[i]->rpc_proxytype)) : "", + pools[i]->rpc_proxy ? json_escape(pools[i]->rpc_proxy) : "", + pools[i]->rpc_proxy ? "|" : "", + json_escape(pools[i]->rpc_url)); fprintf(fcfg, "\n\t\t\"user\" : \"%s\",", json_escape(pools[i]->rpc_user)); fprintf(fcfg, "\n\t\t\"pass\" : \"%s\"\n\t}", json_escape(pools[i]->rpc_pass)); } @@ -3412,12 +3547,13 @@ static void display_options(void) clear_logwin(); retry: wlogprint("[N]ormal [C]lear [S]ilent mode (disable all output)\n"); - wlogprint("[D]ebug:%s\n[P]er-device:%s\n[Q]uiet:%s\n[V]erbose:%s\n[R]PC debug:%s\n[L]og interval:%d\n", + wlogprint("[D]ebug:%s\n[P]er-device:%s\n[Q]uiet:%s\n[V]erbose:%s\n[R]PC debug:%s\n[W]orkTime details:%s\n[L]og interval:%d\n", opt_debug ? "on" : "off", want_per_device_stats? "on" : "off", opt_quiet ? "on" : "off", opt_log_output ? "on" : "off", opt_protocol ? "on" : "off", + opt_worktime ? "on" : "off", opt_log_interval); wlogprint("Select an option or any other key to return\n"); input = getch(); @@ -3470,6 +3606,10 @@ retry: goto retry; } else if (!strncasecmp(&input, "s", 1)) { opt_realquiet = true; + } else if (!strncasecmp(&input, "w", 1)) { + opt_worktime ^= true; + wlogprint("WorkTime details %s\n", opt_worktime ? "enabled" : "disabled"); + goto retry; } else clear_logwin(); @@ -3480,6 +3620,11 @@ retry: void default_save_file(char *filename) { + if (default_config && *default_config) { + strcpy(filename, default_config); + return; + } + #if defined(unix) if (getenv("HOME") && *getenv("HOME")) { strcpy(filename, getenv("HOME")); @@ -3794,6 +3939,7 @@ static void *longpoll_thread(void *userdata); static bool pool_active(struct pool *pool, bool pinging) { + struct timeval tv_getwork, tv_getwork_reply; bool ret = false; json_t *val; CURL *curl; @@ -3806,8 +3952,10 @@ static bool pool_active(struct pool *pool, bool pinging) } applog(LOG_INFO, "Testing pool %s", pool->rpc_url); + gettimeofday(&tv_getwork, NULL); val = json_rpc_call(curl, pool->rpc_url, pool->rpc_userpass, rpc_req, true, false, &rolltime, pool, false); + gettimeofday(&tv_getwork_reply, NULL); if (val) { struct work *work = make_work(); @@ -3819,6 +3967,10 @@ static bool pool_active(struct pool *pool, bool pinging) pool->pool_no, pool->rpc_url); work->pool = pool; work->rolltime = rolltime; + memcpy(&(work->tv_getwork), &tv_getwork, sizeof(struct timeval)); + memcpy(&(work->tv_getwork_reply), &tv_getwork_reply, sizeof(struct timeval)); + work->getwork_mode = GETWORK_MODE_TESTPOOL; + calc_diff(work); applog(LOG_DEBUG, "Pushing pooltest work to base pool"); tq_push(thr_info[stage_thr_id].q, work); @@ -3878,15 +4030,6 @@ out: return ret; } -static void pool_died(struct pool *pool) -{ - if (!pool_tset(pool, &pool->idle)) { - applog(LOG_WARNING, "Pool %d %s not responding!", pool->pool_no, pool->rpc_url); - gettimeofday(&pool->tv_idle, NULL); - switch_pools(NULL); - } -} - static inline int cp_prio(void) { int prio; @@ -4110,7 +4253,7 @@ out: work->mined = true; } -bool submit_work_sync(struct thr_info *thr, const struct work *work_in) +bool submit_work_sync(struct thr_info *thr, const struct work *work_in, struct timeval *tv_work_found) { struct workio_cmd *wc; @@ -4125,7 +4268,8 @@ bool submit_work_sync(struct thr_info *thr, const struct work *work_in) wc->cmd = WC_SUBMIT_WORK; wc->thr = thr; memcpy(wc->work, work_in, sizeof(*work_in)); - wc->work->share_found_time = time(NULL); + if (tv_work_found) + memcpy(&(wc->work->tv_work_found), tv_work_found, sizeof(struct timeval)); applog(LOG_DEBUG, "Pushing submit work to work thread"); @@ -4141,7 +4285,7 @@ err_out: return false; } -bool hashtest(const struct work *work) +static bool hashtest(struct thr_info *thr, const struct work *work) { uint32_t *data32 = (uint32_t *)(work->data); unsigned char swap[128]; @@ -4162,11 +4306,22 @@ bool hashtest(const struct work *work) memcpy((void*)work->hash, hash2, 32); - return fulltest(work->hash, work->target); + if (hash2_32[7] != 0) { + applog(LOG_WARNING, "%s%d: invalid nonce - HW error", + thr->cgpu->api->name, thr->cgpu->device_id); + hw_errors++; + thr->cgpu->hw_errors++; + return false; + } + + bool test = fulltest(work->hash, work->target); + if (!test) + applog(LOG_INFO, "Share below target"); + return test; } -bool test_nonce(struct work *work, uint32_t nonce) +static bool test_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) { if (opt_scrypt) { uint32_t *work_nonce = (uint32_t *)(work->data + 64 + 12); @@ -4180,21 +4335,24 @@ bool test_nonce(struct work *work, uint32_t nonce) work->data[64 + 12 + 2] = (nonce >> 16) & 0xff; work->data[64 + 12 + 3] = (nonce >> 24) & 0xff; - return hashtest(work); + return hashtest(thr, work); } bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) { + struct timeval tv_work_found; + gettimeofday(&tv_work_found, NULL); + total_diff1++; + thr->cgpu->diff1++; work->pool->diff1++; /* Do one last check before attempting to submit the work */ /* Side effect: sets work->data for us */ - if (!test_nonce(work, nonce)) { - applog(LOG_INFO, "Share below target"); + if (!test_nonce(thr, work, nonce)) return true; - } - return submit_work_sync(thr, work); + + return submit_work_sync(thr, work, &tv_work_found); } static inline bool abandon_work(struct work *work, struct timeval *wdiff, uint64_t hashes) @@ -4214,7 +4372,9 @@ static void mt_disable(struct thr_info *mythr, const int thr_id, mythr->rolling = mythr->cgpu->rolling = 0; applog(LOG_DEBUG, "Popping wakeup ping in miner thread"); thread_reportout(mythr); - tq_pop(mythr->q, NULL); /* Ignore ping that's popped */ + do { + tq_pop(mythr->q, NULL); /* Ignore ping that's popped */ + } while (mythr->pause); thread_reportin(mythr); applog(LOG_WARNING, "Thread %d being re-enabled", thr_id); if (api->thread_enable) @@ -4309,6 +4469,8 @@ void *miner_thread(void *userdata) } pool_stats->getwork_calls++; + gettimeofday(&(work->tv_work_start), NULL); + thread_reportin(mythr); hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce); thread_reportin(mythr); @@ -4405,7 +4567,7 @@ enum { }; /* Stage another work item from the work returned in a longpoll */ -static void convert_to_work(json_t *val, int rolltime, struct pool *pool) +static void convert_to_work(json_t *val, int rolltime, struct pool *pool, struct timeval *tv_lp, struct timeval *tv_lp_reply) { struct work *work; bool rc; @@ -4421,6 +4583,10 @@ static void convert_to_work(json_t *val, int rolltime, struct pool *pool) work->pool = pool; work->rolltime = rolltime; work->longpoll = true; + memcpy(&(work->tv_getwork), tv_lp, sizeof(struct timeval)); + memcpy(&(work->tv_getwork_reply), tv_lp_reply, sizeof(struct timeval)); + work->getwork_mode = GETWORK_MODE_LP; + calc_diff(work); if (pool->enabled == POOL_REJECTING) work->mandatory = true; @@ -4489,7 +4655,7 @@ static void *longpoll_thread(void *userdata) /* This *pool is the source of the actual longpoll, not the pool we've * tied it to */ struct pool *pool = NULL; - struct timeval start, end; + struct timeval start, reply, end; CURL *curl = NULL; int failures = 0; int rolltime; @@ -4534,13 +4700,16 @@ retry_pool: curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1); val = json_rpc_call(curl, pool->lp_url, pool->rpc_userpass, rpc_req, false, true, &rolltime, pool, false); + + gettimeofday(&reply, NULL); + if (likely(val)) { soval = json_object_get(json_object_get(val, "result"), "submitold"); if (soval) pool->submit_old = json_is_true(soval); else pool->submit_old = false; - convert_to_work(val, rolltime, pool); + convert_to_work(val, rolltime, pool, &start, &reply); failures = 0; json_decref(val); } else { @@ -4867,6 +5036,8 @@ static void print_summary(void) applog(LOG_WARNING, "Share submissions: %d", total_accepted + total_rejected); applog(LOG_WARNING, "Accepted shares: %d", total_accepted); applog(LOG_WARNING, "Rejected shares: %d", total_rejected); + applog(LOG_WARNING, "Accepted difficulty shares: %1.f", total_diff_accepted); + applog(LOG_WARNING, "Rejected difficulty shares: %1.f", total_diff_rejected); if (total_accepted || total_rejected) applog(LOG_WARNING, "Reject ratio: %.1f%%", (double)(total_rejected * 100) / (double)(total_accepted + total_rejected)); applog(LOG_WARNING, "Hardware errors: %d", hw_errors); @@ -4892,6 +5063,8 @@ static void print_summary(void) applog(LOG_WARNING, " Share submissions: %d", pool->accepted + pool->rejected); applog(LOG_WARNING, " Accepted shares: %d", pool->accepted); applog(LOG_WARNING, " Rejected shares: %d", pool->rejected); + applog(LOG_WARNING, " Accepted difficulty shares: %1.f", pool->diff_accepted); + applog(LOG_WARNING, " Rejected difficulty shares: %1.f", pool->diff_rejected); if (pool->accepted || pool->rejected) applog(LOG_WARNING, " Reject ratio: %.1f%%", (double)(pool->rejected * 100) / (double)(pool->accepted + pool->rejected)); efficiency = pool->getwork_requested ? pool->accepted * 100.0 / pool->getwork_requested : 0.0; @@ -4988,6 +5161,8 @@ void add_pool_details(bool live, char *url, char *user, char *pass) pool = add_pool(); + url = get_proxy(url, pool); + pool->rpc_url = url; pool->rpc_user = user; pool->rpc_pass = pass; diff --git a/compat.h b/compat.h index c6e38d8a..f7cb4da5 100644 --- a/compat.h +++ b/compat.h @@ -57,7 +57,7 @@ enum { PRIO_PROCESS = 0, }; -static inline int setpriority(int which, int who, int prio) +static inline int setpriority(__maybe_unused int which, __maybe_unused int who, __maybe_unused int prio) { /* FIXME - actually do something */ return 0; diff --git a/configure.ac b/configure.ac index 68ea6ca7..fe618e02 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_maj], [2]) m4_define([v_min], [7]) -m4_define([v_mic], [5]) +m4_define([v_mic], [6]) ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--## m4_define([v_ver], [v_maj.v_min.v_mic]) m4_define([lt_rev], m4_eval(v_maj + v_min)) @@ -104,14 +104,18 @@ case $target in ;; esac +if test "x$have_x86_64" = xtrue; then + ARCH_DIR=x86_64 +else + ARCH_DIR=x86 +fi + if test "x$ATISTREAMSDKROOT" != x; then - if test "x$have_x86_64" = xtrue; then - ATI_STREAM_ARCH_DIR=x86_64 - else - ATI_STREAM_ARCH_DIR=x86 - fi OPENCL_FLAGS="-I$ATISTREAMSDKROOT/include $OPENCL_FLAGS" - OPENCL_LIBS="-L$ATISTREAMSDKROOT/lib/$ATI_STREAM_ARCH_DIR $OPENCL_LIBS" + OPENCL_LIBS="-L$ATISTREAMSDKROOT/lib/$ARCH_DIR $OPENCL_LIBS" +elif test "x$AMDAPPSDKROOT" != x; then + OPENCL_FLAGS="-I$AMDAPPSDKROOT/include $OPENCL_FLAGS" + OPENCL_LIBS="-L$AMDAPPSDKROOT/lib/$ARCH_DIR $OPENCL_LIBS" fi cpumining="no" diff --git a/driver-bitforce.c b/driver-bitforce.c index 93587218..ded9e923 100644 --- a/driver-bitforce.c +++ b/driver-bitforce.c @@ -45,8 +45,8 @@ enum { #endif /* WIN32 */ #include "compat.h" -#include "fpgautils.h" #include "miner.h" +#include "fpgautils.h" #define BITFORCE_SLEEP_MS 500 #define BITFORCE_TIMEOUT_S 7 @@ -99,6 +99,7 @@ static bool bitforce_detect_one(const char *devpath) } BFwrite(fdDev, "ZGX", 3); + pdevbuf[0] = '\0'; BFgets(pdevbuf, sizeof(pdevbuf), fdDev); if (unlikely(!pdevbuf[0])) { applog(LOG_ERR, "BFL: Error reading/timeout (ZGX)"); @@ -229,7 +230,7 @@ static int bitforce_detect_auto(void) static void bitforce_detect(void) { - serial_detect_auto(bitforce_api.dname, bitforce_detect_one, bitforce_detect_auto); + serial_detect_auto(&bitforce_api, bitforce_detect_one, bitforce_detect_auto); } static void get_bitforce_statline_before(char *buf, struct cgpu_info *bitforce) @@ -309,6 +310,7 @@ void bitforce_init(struct cgpu_info *bitforce) do { BFwrite(fdDev, "ZGX", 3); + pdevbuf[0] = '\0'; BFgets(pdevbuf, sizeof(pdevbuf), fdDev); if (unlikely(!pdevbuf[0])) { @@ -338,6 +340,37 @@ void bitforce_init(struct cgpu_info *bitforce) mutex_unlock(&bitforce->device_mutex); } +static void bitforce_flash_led(struct cgpu_info *bitforce) +{ + int fdDev = bitforce->device_fd; + + if (!fdDev) + return; + + /* Do not try to flash the led if we're polling for a result to + * minimise the chance of interleaved results */ + if (bitforce->polling) + return; + + /* It is not critical flashing the led so don't get stuck if we + * can't grab the mutex here */ + if (mutex_trylock(&bitforce->device_mutex)) + return; + + BFwrite(fdDev, "ZMX", 3); + + /* Once we've tried - don't do it until told to again */ + bitforce->flash_led = false; + + /* However, this stops anything else getting a reply + * So best to delay any other access to the BFL */ + sleep(4); + + mutex_unlock(&bitforce->device_mutex); + + return; // nothing is returned by the BFL +} + static bool bitforce_get_temp(struct cgpu_info *bitforce) { int fdDev = bitforce->device_fd; @@ -348,16 +381,23 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) return false; /* Do not try to get the temperature if we're polling for a result to - * minimise the change of interleaved results */ + * minimise the chance of interleaved results */ if (bitforce->polling) return true; - /* It is not critical getting temperature so don't get stuck if we + // Flash instead of Temp - doing both can be too slow + if (bitforce->flash_led) { + bitforce_flash_led(bitforce); + return true; + } + + /* It is not critical getting temperature so don't get stuck if we * can't grab the mutex here */ if (mutex_trylock(&bitforce->device_mutex)) return false; BFwrite(fdDev, "ZLX", 3); + pdevbuf[0] = '\0'; BFgets(pdevbuf, sizeof(pdevbuf), fdDev); mutex_unlock(&bitforce->device_mutex); @@ -385,11 +425,14 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) /* Use the temperature monitor as a kind of watchdog for when * our responses are out of sync and flush the buffer to * hopefully recover */ - applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer"); + applog(LOG_WARNING, "BFL%i: Garbled response probably throttling, clearing buffer", bitforce->device_id); + bitforce->device_last_not_well = time(NULL); + bitforce->device_not_well_reason = REASON_DEV_THROTTLE; + bitforce->dev_throttle_count++; /* Count throttling episodes as hardware errors */ bitforce->hw_errors++; bitforce_clear_buffer(bitforce); - return false;; + return false; } return true; @@ -411,6 +454,7 @@ re_send: BFwrite(fdDev, "ZPX", 3); else BFwrite(fdDev, "ZDX", 3); + pdevbuf[0] = '\0'; BFgets(pdevbuf, sizeof(pdevbuf), fdDev); if (!pdevbuf[0] || !strncasecmp(pdevbuf, "B", 1)) { mutex_unlock(&bitforce->device_mutex); @@ -450,6 +494,7 @@ re_send: BFwrite(fdDev, ob, 68); } + pdevbuf[0] = '\0'; BFgets(pdevbuf, sizeof(pdevbuf), fdDev); mutex_unlock(&bitforce->device_mutex); @@ -493,6 +538,7 @@ static int64_t bitforce_get_result(struct thr_info *thr, struct work *work) mutex_lock(&bitforce->device_mutex); BFwrite(fdDev, "ZFX", 3); + pdevbuf[0] = '\0'; BFgets(pdevbuf, sizeof(pdevbuf), fdDev); mutex_unlock(&bitforce->device_mutex); @@ -637,6 +683,11 @@ static bool bitforce_get_stats(struct cgpu_info *bitforce) return bitforce_get_temp(bitforce); } +static void bitforce_identify(struct cgpu_info *bitforce) +{ + bitforce->flash_led = true; +} + static bool bitforce_thread_init(struct thr_info *thr) { struct cgpu_info *bitforce = thr->cgpu; @@ -673,6 +724,7 @@ struct device_api bitforce_api = { .reinit_device = bitforce_init, .get_statline_before = get_bitforce_statline_before, .get_stats = bitforce_get_stats, + .identify_device = bitforce_identify, .thread_prepare = bitforce_thread_prepare, .thread_init = bitforce_thread_init, .scanhash = bitforce_scanhash, diff --git a/driver-cpu.c b/driver-cpu.c index 8ffc7802..e3a74de6 100644 --- a/driver-cpu.c +++ b/driver-cpu.c @@ -75,7 +75,7 @@ static inline void affine_to_cpu(int id, int cpu) /* TODO: resolve externals */ -extern bool submit_work_sync(struct thr_info *thr, const struct work *work_in); +extern bool submit_work_sync(struct thr_info *thr, const struct work *work_in, struct timeval *tv); extern char *set_int_range(const char *arg, int *i, int min, int max); extern int dev_from_id(int thr_id); @@ -827,7 +827,7 @@ CPUSearch: /* if nonce found, submit work */ if (unlikely(rc)) { applog(LOG_DEBUG, "CPU %d found something?", dev_from_id(thr_id)); - if (unlikely(!submit_work_sync(thr, work))) { + if (unlikely(!submit_work_sync(thr, work, NULL))) { applog(LOG_ERR, "Failed to submit_work_sync in miner_thread %d", thr_id); } work->blk.nonce = last_nonce + 1; diff --git a/driver-icarus.c b/driver-icarus.c index f1cf9d17..4214c31b 100644 --- a/driver-icarus.c +++ b/driver-icarus.c @@ -49,8 +49,8 @@ #endif #include "elist.h" -#include "fpgautils.h" #include "miner.h" +#include "fpgautils.h" // The serial I/O speed - Linux uses a define 'B115200' in bits/termios.h #define ICARUS_IO_SPEED 115200 @@ -598,7 +598,7 @@ static bool icarus_detect_one(const char *devpath) static void icarus_detect() { - serial_detect(icarus_api.dname, icarus_detect_one); + serial_detect(&icarus_api, icarus_detect_one); } static bool icarus_prepare(struct thr_info *thr) @@ -639,7 +639,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, struct timeval tv_start, tv_finish, elapsed; struct timeval tv_history_start, tv_history_finish; double Ti, Xi; - int i; + int curr_hw_errors, i; + bool was_hw_error; struct ICARUS_HISTORY *history0, *history; int count; @@ -712,7 +713,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, nonce = swab32(nonce); #endif + curr_hw_errors = icarus->hw_errors; submit_nonce(thr, work, nonce); + was_hw_error = (curr_hw_errors > icarus->hw_errors); hash_count = (nonce & info->nonce_mask); hash_count++; @@ -726,8 +729,9 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, icarus->device_id, nonce, hash_count, elapsed.tv_sec, elapsed.tv_usec); } - // ignore possible end condition values + // ignore possible end condition values ... and hw errors if (info->do_icarus_timing + && !was_hw_error && ((nonce & info->nonce_mask) > END_CONDITION) && ((nonce & info->nonce_mask) < (info->nonce_mask & ~END_CONDITION))) { gettimeofday(&tv_history_start, NULL); diff --git a/driver-modminer.c b/driver-modminer.c index 040100db..d15f2d88 100644 --- a/driver-modminer.c +++ b/driver-modminer.c @@ -13,9 +13,9 @@ #include #include -#include "fpgautils.h" #include "logging.h" #include "miner.h" +#include "fpgautils.h" #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd" #define BISTREAM_USER_ID "\2\4$B" @@ -103,7 +103,7 @@ modminer_detect_auto() static void modminer_detect() { - serial_detect_auto(modminer_api.dname, modminer_detect_one, modminer_detect_auto); + serial_detect_auto(&modminer_api, modminer_detect_one, modminer_detect_auto); } #define bailout(...) return _bailout(-1, modminer, __VA_ARGS__); @@ -404,7 +404,7 @@ modminer_process_results(struct thr_info*thr) char cmd[2], temperature; uint32_t nonce; long iter; - bool bad; + int curr_hw_errors; cmd[0] = '\x0a'; cmd[1] = fpgaid; @@ -441,12 +441,10 @@ modminer_process_results(struct thr_info*thr) mutex_unlock(&modminer->device_mutex); if (memcmp(&nonce, "\xff\xff\xff\xff", 4)) { state->no_nonce_counter = 0; - bad = !test_nonce(work, nonce); - if (!bad) - submit_nonce(thr, work, nonce); - else { - ++hw_errors; - if (++modminer->hw_errors * 100 > 1000 + state->good_share_counter) + curr_hw_errors = modminer->hw_errors; + submit_nonce(thr, work, nonce); + if (modminer->hw_errors > curr_hw_errors) { + if (modminer->hw_errors * 100 > 1000 + state->good_share_counter) // Only reduce clocks if hardware errors are more than ~1% of results modminer_reduce_clock(thr, true); } diff --git a/driver-opencl.c b/driver-opencl.c index ded20c31..82cbc6bb 100644 --- a/driver-opencl.c +++ b/driver-opencl.c @@ -1585,15 +1585,14 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work, gpu_us = us_tdiff(&tv_gpuend, &gpu->tv_gpumid); if (gpu_us > 0 && ++gpu->hit > 4) { gpu_us = us_tdiff(&tv_gpuend, &gpu->tv_gpustart) / gpu->intervals; - gpu->gpu_us_average = (gpu->gpu_us_average + gpu_us * 0.63) / 1.63; /* Try to not let the GPU be out for longer than * opt_dynamic_interval in ms, but increase * intensity when the system is idle in dynamic mode */ - if (gpu->gpu_us_average > dynamic_us) { + if (gpu_us > dynamic_us) { if (gpu->intensity > MIN_INTENSITY) --gpu->intensity; - } else if (gpu->gpu_us_average < dynamic_us / 2) { + } else if (gpu_us < dynamic_us / 2) { if (gpu->intensity < MAX_INTENSITY) ++gpu->intensity; } diff --git a/findnonce.c b/findnonce.c index d557f17a..788835d9 100644 --- a/findnonce.c +++ b/findnonce.c @@ -131,6 +131,8 @@ void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data) blk->sevenA = blk->ctx_h + SHA256_K[7]; } +#if 0 // not used any more + #define P(t) (W[(t)&0xF] = W[(t-16)&0xF] + (rotate(W[(t-15)&0xF], 25) ^ rotate(W[(t-15)&0xF], 14) ^ (W[(t-15)&0xF] >> 3)) + W[(t-7)&0xF] + (rotate(W[(t-2)&0xF], 15) ^ rotate(W[(t-2)&0xF], 13) ^ (W[(t-2)&0xF] >> 10))) #define IR(u) \ @@ -167,6 +169,8 @@ void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data) R(E, F, G, H, A, B, C, D, P(u+4), SHA256_K[u+4]); \ R(D, E, F, G, H, A, B, C, P(u+5), SHA256_K[u+5]) +#endif + struct pc_data { struct thr_info *thr; struct work *work; @@ -175,6 +179,8 @@ struct pc_data { int found; }; +#if 0 // not used any more + static void send_sha_nonce(struct pc_data *pcd, cl_uint nonce) { dev_blk_ctx *blk = &pcd->work->blk; @@ -222,6 +228,8 @@ static void send_sha_nonce(struct pc_data *pcd, cl_uint nonce) } } +#endif + static void send_scrypt_nonce(struct pc_data *pcd, uint32_t nonce) { struct thr_info *thr = pcd->thr; @@ -238,6 +246,8 @@ static void send_scrypt_nonce(struct pc_data *pcd, uint32_t nonce) static void *postcalc_hash(void *userdata) { struct pc_data *pcd = (struct pc_data *)userdata; + struct thr_info *thr = pcd->thr; + struct work *work = pcd->work; unsigned int entry = 0; pthread_detach(pthread_self()); @@ -248,8 +258,10 @@ static void *postcalc_hash(void *userdata) applog(LOG_DEBUG, "OCL NONCE %u found in slot %d", nonce, entry); if (opt_scrypt) send_scrypt_nonce(pcd, nonce); - else - send_sha_nonce(pcd, nonce); + else { + if (unlikely(submit_nonce(thr, work, nonce) == false)) + applog(LOG_ERR, "Failed to submit work, exiting"); + } } free(pcd); diff --git a/fpgautils.c b/fpgautils.c index 287f277e..de9d93e7 100644 --- a/fpgautils.c +++ b/fpgautils.c @@ -33,13 +33,12 @@ #endif #include "elist.h" -#include "fpgautils.h" #include "logging.h" #include "miner.h" +#include "fpgautils.h" #ifdef HAVE_LIBUDEV -int -serial_autodetect_udev(detectone_func_t detectone, const char*prodname) +int serial_autodetect_udev(detectone_func_t detectone, const char*prodname) { struct udev *udev = udev_new(); struct udev_enumerate *enumerate = udev_enumerate_new(udev); @@ -69,15 +68,13 @@ serial_autodetect_udev(detectone_func_t detectone, const char*prodname) return found; } #else -int -serial_autodetect_udev(__maybe_unused detectone_func_t detectone, __maybe_unused const char*prodname) +int serial_autodetect_udev(__maybe_unused detectone_func_t detectone, __maybe_unused const char*prodname) { return 0; } #endif -int -serial_autodetect_devserial(detectone_func_t detectone, const char*prodname) +int serial_autodetect_devserial(detectone_func_t detectone, const char*prodname) { #ifndef WIN32 DIR *D; @@ -107,30 +104,32 @@ serial_autodetect_devserial(detectone_func_t detectone, const char*prodname) #endif } -int -_serial_detect(const char*dname, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto) +int _serial_detect(struct device_api *api, detectone_func_t detectone, autoscan_func_t autoscan, bool forceauto) { struct string_elist *iter, *tmp; - const char*s, *p; + const char *dev, *colon; bool inhibitauto = false; char found = 0; - size_t dnamel = strlen(dname); + size_t namel = strlen(api->name); + size_t dnamel = strlen(api->dname); list_for_each_entry_safe(iter, tmp, &scan_devices, list) { - s = iter->string; - if ((p = strchr(s, ':')) && p[1] != '\0') { - size_t plen = p - s; - if (plen != dnamel || strncasecmp(s, dname, plen)) + dev = iter->string; + if ((colon = strchr(dev, ':')) && colon[1] != '\0') { + size_t idlen = colon - dev; + + // allow either name:device or dname:device + if ((idlen != namel || strncasecmp(dev, api->name, idlen)) + && (idlen != dnamel || strncasecmp(dev, api->dname, idlen))) continue; - s = p + 1; + + dev = colon + 1; } - if (!strcmp(s, "auto")) + if (!strcmp(dev, "auto")) forceauto = true; - else - if (!strcmp(s, "noauto")) + else if (!strcmp(dev, "noauto")) inhibitauto = true; - else - if (detectone(s)) { + else if (detectone(dev)) { string_elist_del(iter); inhibitauto = true; ++found; @@ -311,8 +310,7 @@ void termios_debug(const char *devpath, struct termios *my_termios, const char * #endif #endif -int -serial_open(const char*devpath, unsigned long baud, signed short timeout, bool purge) +int serial_open(const char *devpath, unsigned long baud, signed short timeout, bool purge) { #ifdef WIN32 HANDLE hSerial = CreateFile(devpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); @@ -429,8 +427,7 @@ serial_open(const char*devpath, unsigned long baud, signed short timeout, bool p #endif } -ssize_t -_serial_read(int fd, char *buf, size_t bufsiz, char *eol) +ssize_t _serial_read(int fd, char *buf, size_t bufsiz, char *eol) { ssize_t len, tlen = 0; while (bufsiz) { @@ -446,8 +443,7 @@ _serial_read(int fd, char *buf, size_t bufsiz, char *eol) return tlen; } -static FILE* -_open_bitstream(const char*path, const char*subdir, const char*filename) +static FILE *_open_bitstream(const char *path, const char *subdir, const char *filename) { char fullpath[PATH_MAX]; strcpy(fullpath, path); @@ -471,8 +467,7 @@ _open_bitstream(const char*path, const char*subdir, const char*filename) _open_bitstream(path, NULL); \ } while(0) -FILE* -open_bitstream(const char*dname, const char*filename) +FILE *open_bitstream(const char *dname, const char *filename) { FILE *f; diff --git a/fpgautils.h b/fpgautils.h index 5b743bc5..5c8b6bfe 100644 --- a/fpgautils.h +++ b/fpgautils.h @@ -16,24 +16,24 @@ typedef bool(*detectone_func_t)(const char*); typedef int(*autoscan_func_t)(); -extern int _serial_detect(const char*dname, detectone_func_t, autoscan_func_t, bool force_autoscan); -#define serial_detect_fauto(dname, detectone, autoscan) \ - _serial_detect(dname, detectone, autoscan, true) -#define serial_detect_auto(dname, detectone, autoscan) \ - _serial_detect(dname, detectone, autoscan, false) -#define serial_detect(dname, detectone) \ - _serial_detect(dname, detectone, NULL, false) -extern int serial_autodetect_devserial(detectone_func_t, const char*prodname); -extern int serial_autodetect_udev (detectone_func_t, const char*prodname); +extern int _serial_detect(struct device_api *api, detectone_func_t, autoscan_func_t, bool force_autoscan); +#define serial_detect_fauto(api, detectone, autoscan) \ + _serial_detect(api, detectone, autoscan, true) +#define serial_detect_auto(api, detectone, autoscan) \ + _serial_detect(api, detectone, autoscan, false) +#define serial_detect(api, detectone) \ + _serial_detect(api, detectone, NULL, false) +extern int serial_autodetect_devserial(detectone_func_t, const char *prodname); +extern int serial_autodetect_udev(detectone_func_t, const char *prodname); -extern int serial_open(const char*devpath, unsigned long baud, signed short timeout, bool purge); -extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char*eol); +extern int serial_open(const char *devpath, unsigned long baud, signed short timeout, bool purge); +extern ssize_t _serial_read(int fd, char *buf, size_t buflen, char *eol); #define serial_read(fd, buf, count) \ _serial_read(fd, (char*)(buf), count, NULL) #define serial_read_line(fd, buf, bufsiz, eol) \ - _serial_read(fd, buf, count, &eol) + _serial_read(fd, buf, bufsiz, &eol) #define serial_close(fd) close(fd) -extern FILE*open_bitstream(const char*dname, const char*filename); +extern FILE *open_bitstream(const char *dname, const char *filename); #endif diff --git a/libztex.c b/libztex.c index 627dfb27..923c224a 100644 --- a/libztex.c +++ b/libztex.c @@ -23,8 +23,8 @@ #include #include -#include "fpgautils.h" #include "miner.h" +#include "fpgautils.h" #include "libztex.h" #define BUFSIZE 256 diff --git a/logging.h b/logging.h index 18f3a744..52f98731 100644 --- a/logging.h +++ b/logging.h @@ -20,6 +20,8 @@ enum { /* original / legacy debug flags */ extern bool opt_debug; extern bool opt_log_output; +extern bool opt_realquiet; +extern bool want_per_device_stats; /* global log_level, messages with lower or equal prio are logged */ extern int opt_log_level; diff --git a/miner.h b/miner.h index a234ecd3..169c9ee1 100644 --- a/miner.h +++ b/miner.h @@ -239,6 +239,7 @@ struct device_api { void (*get_statline)(char*, struct cgpu_info*); struct api_data *(*get_api_stats)(struct cgpu_info*); bool (*get_stats)(struct cgpu_info*); + void (*identify_device)(struct cgpu_info*); // e.g. to flash a led // Thread-specific functions bool (*thread_prepare)(struct thr_info*); @@ -276,6 +277,7 @@ enum dev_reason { REASON_DEV_OVER_HEAT, REASON_DEV_THERMAL_CUTOFF, REASON_DEV_COMMS_ERROR, + REASON_DEV_THROTTLE, }; #define REASON_NONE "None" @@ -288,6 +290,7 @@ enum dev_reason { #define REASON_DEV_OVER_HEAT_STR "Device over heated" #define REASON_DEV_THERMAL_CUTOFF_STR "Device reached thermal cutoff" #define REASON_DEV_COMMS_ERROR_STR "Device comms error" +#define REASON_DEV_THROTTLE_STR "Device throttle" #define REASON_UNKNOWN_STR "Unknown reason - code bug" #define MIN_SEC_UNSET 99999999 @@ -311,6 +314,11 @@ struct cgminer_pool_stats { bool canroll; bool hadexpire; uint32_t rolltime; + double min_diff; + double max_diff; + double last_diff; + uint32_t min_diff_count; + uint32_t max_diff_count; }; struct cgpu_info { @@ -335,6 +343,7 @@ struct cgpu_info { uint32_t nonces; bool nonce_range; bool polling; + bool flash_led; #endif pthread_mutex_t device_mutex; @@ -374,7 +383,6 @@ struct cgpu_info { #endif struct timeval tv_gpustart; struct timeval tv_gpumid; - double gpu_us_average; int intervals, hit; #endif @@ -396,8 +404,12 @@ struct cgpu_info { int gpu_powertune; float gpu_vddc; #endif + int diff1; + double diff_accepted; + double diff_rejected; int last_share_pool; time_t last_share_pool_time; + double last_share_diff; time_t device_last_well; time_t device_last_not_well; @@ -411,6 +423,7 @@ struct cgpu_info { int dev_over_heat_count; // It's a warning but worth knowing int dev_thermal_cutoff_count; int dev_comms_error_count; + int dev_throttle_count; struct cgminer_stats cgminer_stats; }; @@ -450,6 +463,7 @@ extern void thr_info_cancel(struct thr_info *thr); extern void thr_info_freeze(struct thr_info *thr); extern void nmsleep(unsigned int msecs); extern double us_tdiff(struct timeval *end, struct timeval *start); +extern double tdiff(struct timeval *end, struct timeval *start); struct string_elist { char *string; @@ -576,6 +590,7 @@ extern bool opt_delaynet; extern bool opt_restart; extern char *opt_icarus_options; extern char *opt_icarus_timing; +extern bool opt_worktime; #ifdef USE_BITFORCE extern bool opt_bfl_noncerange; #endif @@ -586,6 +601,8 @@ extern const uint32_t sha256_init_state[]; extern json_t *json_rpc_call(CURL *curl, const char *url, const char *userpass, const char *rpc_req, bool, bool, int *, struct pool *pool, bool); +extern const char *proxytype(curl_proxytype proxytype); +extern char *get_proxy(char *url, struct pool *pool); extern char *bin2hex(const unsigned char *p, size_t len); extern bool hex2bin(unsigned char *p, const char *hexstr, size_t len); @@ -599,7 +616,9 @@ typedef bool (*sha256_func)(struct thr_info*, const unsigned char *pmidstate, extern bool fulltest(const unsigned char *hash, const unsigned char *target); +extern int opt_queue; extern int opt_scantime; +extern int opt_expiry; extern pthread_mutex_t console_lock; extern pthread_mutex_t ch_lock; @@ -672,6 +691,7 @@ extern unsigned int new_blocks; extern unsigned int found_blocks; extern int total_accepted, total_rejected, total_diff1;; extern int total_getworks, total_stale, total_discarded; +extern double total_diff_accepted, total_diff_rejected, total_diff_stale; extern unsigned int local_work; extern unsigned int total_go, total_ro; extern const int opt_cutofftemp; @@ -733,9 +753,14 @@ struct pool { int prio; int accepted, rejected; int seq_rejects; + int seq_getfails; int solved; int diff1; + double diff_accepted; + double diff_rejected; + double diff_stale; + int queued; int staged; @@ -764,6 +789,8 @@ struct pool { char *rpc_url; char *rpc_userpass; char *rpc_user, *rpc_pass; + curl_proxytype rpc_proxytype; + char *rpc_proxy; pthread_mutex_t pool_lock; @@ -779,11 +806,17 @@ struct pool { struct list_head curlring; time_t last_share_time; + double last_share_diff; struct cgminer_stats cgminer_stats; struct cgminer_pool_stats cgminer_pool_stats; }; +#define GETWORK_MODE_TESTPOOL 'T' +#define GETWORK_MODE_POOL 'P' +#define GETWORK_MODE_LP 'L' +#define GETWORK_MODE_BENCHMARK 'B' + struct work { unsigned char data[128]; unsigned char hash1[64]; @@ -814,9 +847,16 @@ struct work { unsigned int work_block; int id; - UT_hash_handle hh; + UT_hash_handle hh; + + double work_difficulty; - time_t share_found_time; + struct timeval tv_getwork; + struct timeval tv_getwork_reply; + struct timeval tv_cloned; + struct timeval tv_work_start; + struct timeval tv_work_found; + char getwork_mode; }; #ifdef USE_MODMINER @@ -838,7 +878,6 @@ struct modminer_fpga_state { #endif extern void get_datestamp(char *, struct timeval *); -extern bool test_nonce(struct work *work, uint32_t nonce); bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); extern void tailsprintf(char *f, const char *fmt, ...); extern void wlogprint(const char *f, ...); @@ -881,7 +920,8 @@ enum api_data_type { API_UTILITY, API_FREQ, API_VOLTS, - API_HS + API_HS, + API_DIFF }; struct api_data { @@ -912,5 +952,6 @@ extern struct api_data *api_add_utility(struct api_data *root, char *name, doubl extern struct api_data *api_add_freq(struct api_data *root, char *name, double *data, bool copy_data); extern struct api_data *api_add_volts(struct api_data *root, char *name, float *data, bool copy_data); extern struct api_data *api_add_hs(struct api_data *root, char *name, double *data, bool copy_data); +extern struct api_data *api_add_diff(struct api_data *root, char *name, double *data, bool copy_data); #endif /* __MINER_H__ */ diff --git a/miner.php b/miner.php index 9c6f6949..f6250f0d 100644 --- a/miner.php +++ b/miner.php @@ -2,7 +2,7 @@ session_start(); # global $title, $miner, $port, $readonly, $notify, $rigs; -global $rigtotals, $forcerigtotals; +global $rigipsecurity, $rigtotals, $forcerigtotals; global $socksndtimeoutsec, $sockrcvtimeoutsec; global $checklastshare, $poolinputs, $hidefields; global $ignorerefresh, $changerefresh, $autorefresh; @@ -40,6 +40,10 @@ $poolinputs = false; # format: 'IP:Port' or 'Host:Port' or 'Host:Port:Name' $rigs = array('127.0.0.1:4028'); # +# Set $rigipsecurity to false to show the IP/Port of the rig +# in the socket error messages and also show the full socket message +$rigipsecurity = true; +# # Set $rigtotals to true to display totals on the single rig page # 'false' means no totals (and ignores $forcerigtotals) # You can force it to always show rig totals when there is only @@ -226,7 +230,7 @@ function htmlhead($checkapi, $rig, $pg = null) if ($readonly === false && $checkapi === true) { $error = null; - $access = api('privileged'); + $access = api($rig, 'privileged'); if ($error != null || !isset($access['STATUS']['STATUS']) || $access['STATUS']['STATUS'] != 'S') @@ -275,8 +279,9 @@ global $haderror, $error; $haderror = false; $error = null; # -function getsock($addr, $port) +function getsock($rig, $addr, $port) { + global $rigipsecurity; global $haderror, $error, $socksndtimeoutsec, $sockrcvtimeoutsec; $error = null; @@ -285,9 +290,15 @@ function getsock($addr, $port) if ($socket === false || $socket === null) { $haderror = true; - $error = socket_strerror(socket_last_error()); - $msg = "socket create(TCP) failed"; - $error = "ERR: $msg '$error'\n"; + if ($rigipsecurity === false) + { + $error = socket_strerror(socket_last_error()); + $msg = "socket create(TCP) failed"; + $error = "ERR: $msg '$error'\n"; + } + else + $error = "ERR: socket create(TCP) failed\n"; + return null; } @@ -301,9 +312,15 @@ function getsock($addr, $port) if ($res === false) { $haderror = true; - $error = socket_strerror(socket_last_error()); - $msg = "socket connect($addr,$port) failed"; - $error = "ERR: $msg '$error'\n"; + if ($rigipsecurity === false) + { + $error = socket_strerror(socket_last_error()); + $msg = "socket connect($addr,$port) failed"; + $error = "ERR: $msg '$error'\n"; + } + else + $error = "ERR: socket connect($rig) failed\n"; + socket_close($socket); return null; } @@ -365,12 +382,12 @@ function revert($str) return str_replace(array("\1", "\2", "\3", "\4"), array("|", "\\", "=", ","), $str); } # -function api($cmd) +function api($rig, $cmd) { global $haderror, $error; global $miner, $port, $hidefields; - $socket = getsock($miner, $port); + $socket = getsock($rig, $miner, $port); if ($socket != null) { socket_write($socket, $cmd, strlen($cmd)); @@ -506,7 +523,15 @@ function classlastshare($when, $alldata, $warnclass, $errorclass) if (!isset($alldata['Last Share Time'])) return ''; + if (!isset($alldata['Last Share Difficulty'])) + return ''; + $expected = pow(2, 32) / ($alldata['MHS av'] * pow(10, 6)); + + // If the share difficulty changes while waiting on a share, + // this calculation will of course be incorrect + $expected *= $alldata['Last Share Difficulty']; + $howlong = $when - $alldata['Last Share Time']; if ($howlong < 1) $howlong = 1; @@ -641,11 +666,20 @@ function fmt($section, $name, $value, $when, $alldata) if ($value == 0) $class = $errorclass; else - if (isset($alldata['MHS av'])) + if (isset($alldata['Difficulty Accepted']) + && isset($alldata['Accepted']) + && isset($alldata['MHS av']) + && ($alldata['Difficulty Accepted'] > 0) + && ($alldata['Accepted'] > 0)) { $expected = 60 * $alldata['MHS av'] * (pow(10, 6) / pow(2, 32)); if ($expected == 0) $expected = 0.000001; // 1 H/s + + $da = $alldata['Difficulty Accepted']; + $a = $alldata['Accepted']; + $expected /= ($da / $a); + $ratio = $value / $expected; if ($ratio < 0.9) $class = $loclass; @@ -713,16 +747,26 @@ function fmt($section, $name, $value, $when, $alldata) $dec = ''; else $dec = '.'.$parts[1]; - $ret = number_format($parts[0]).$dec; + $ret = number_format((float)$parts[0]).$dec; if ($value == 0) $class = $errorclass; else - if (isset($alldata['Utility'])) + if (isset($alldata['Difficulty Accepted']) + && isset($alldata['Accepted']) + && isset($alldata['Utility']) + && ($alldata['Difficulty Accepted'] > 0) + && ($alldata['Accepted'] > 0)) { $expected = 60 * $value * (pow(10, 6) / pow(2, 32)); - $utility = $alldata['Utility']; - $ratio = $utility / $expected; + if ($expected == 0) + $expected = 0.000001; // 1 H/s + + $da = $alldata['Difficulty Accepted']; + $a = $alldata['Accepted']; + $expected /= ($da / $a); + + $ratio = $alldata['Utility'] / $expected; if ($ratio < 0.9) $class = $hiclass; else @@ -757,12 +801,15 @@ function fmt($section, $name, $value, $when, $alldata) case 'total.Discarded': case 'POOL.Diff1 Shares': case 'total.Diff1 Shares': + case 'GPU.Diff1 Work': + case 'PGA.Diff1 Work': + case 'total.Diff1 Work': $parts = explode('.', $value, 2); if (count($parts) == 1) $dec = ''; else $dec = '.'.$parts[1]; - $ret = number_format($parts[0]).$dec; + $ret = number_format((float)$parts[0]).$dec; break; case 'GPU.Status': case 'PGA.Status': @@ -1067,7 +1114,7 @@ function processgpus($rig) global $error; global $warnfont, $warnoff; - $gpus = api('gpucount'); + $gpus = api($rig, 'gpucount'); if ($error != null) otherrow("Error getting GPU count: $warnfont$error$warnoff"); @@ -1136,7 +1183,7 @@ function process($cmds, $rig) $count = count($cmds); foreach ($cmds as $cmd => $des) { - $process = api($cmd); + $process = api($rig, $cmd); if ($error != null) { @@ -1251,7 +1298,7 @@ function doforeach($cmd, $des, $sum, $head, $datetime) else $name = $num; - $ans = api($cmd); + $ans = api($name, $cmd); if ($error != null) { @@ -1763,7 +1810,7 @@ function processcustompage($pagename, $sections, $sum, $namemap) foreach ($cmds as $cmd => $one) { - $process = api($cmd); + $process = api($name, $cmd); if ($error != null) { @@ -2052,7 +2099,7 @@ function display() newtable(); doforeach('version', 'rig summary', array(), array(), true); - $sum = array('MHS av', 'Getworks', 'Found Blocks', 'Accepted', 'Rejected', 'Discarded', 'Stale', 'Utility', 'Local Work', 'Total MH', 'Work Utility', 'Diff1 Shares'); + $sum = array('MHS av', 'Getworks', 'Found Blocks', 'Accepted', 'Rejected', 'Discarded', 'Stale', 'Utility', 'Local Work', 'Total MH', 'Work Utility', 'Diff1 Shares', 'Diff1 Work'); doforeach('summary', 'summary information', $sum, array(), false); endtable(); otherrow('

'); diff --git a/util.c b/util.c index 724ffa9d..3a0dbc18 100644 --- a/util.c +++ b/util.c @@ -305,7 +305,10 @@ json_t *json_rpc_call(CURL *curl, const char *url, curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, resp_hdr_cb); curl_easy_setopt(curl, CURLOPT_HEADERDATA, &hi); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); - if (opt_socks_proxy) { + if (pool->rpc_proxy) { + curl_easy_setopt(curl, CURLOPT_PROXY, pool->rpc_proxy); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE, pool->rpc_proxytype); + } else if (opt_socks_proxy) { curl_easy_setopt(curl, CURLOPT_PROXY, opt_socks_proxy); curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); } @@ -460,6 +463,69 @@ err_out: return NULL; } +#if (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 10) || (LIBCURL_VERSION_MAJOR > 7) +static struct { + const char *name; + curl_proxytype proxytype; +} proxynames[] = { + { "http:", CURLPROXY_HTTP }, +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR > 19) || (LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH >= 4) + { "http0:", CURLPROXY_HTTP_1_0 }, +#endif +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR > 15) || (LIBCURL_VERSION_MINOR == 15 && LIBCURL_VERSION_PATCH >= 2) + { "socks4:", CURLPROXY_SOCKS4 }, +#endif + { "socks5:", CURLPROXY_SOCKS5 }, +#if (LIBCURL_VERSION_MAJOR > 7) || (LIBCURL_VERSION_MINOR >= 18) + { "socks4a:", CURLPROXY_SOCKS4A }, + { "socks5h:", CURLPROXY_SOCKS5_HOSTNAME }, +#endif + { NULL, 0 } +}; +#endif + +const char *proxytype(curl_proxytype proxytype) +{ + int i; + + for (i = 0; proxynames[i].name; i++) + if (proxynames[i].proxytype == proxytype) + return proxynames[i].name; + + return "invalid"; +} + +char *get_proxy(char *url, struct pool *pool) +{ + pool->rpc_proxy = NULL; + +#if (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 10) || (LIBCURL_VERSION_MAJOR > 7) + char *split; + int plen, len, i; + + for (i = 0; proxynames[i].name; i++) { + plen = strlen(proxynames[i].name); + if (strncmp(url, proxynames[i].name, plen) == 0) { + if (!(split = strchr(url, '|'))) + return url; + + *split = '\0'; + len = split - url; + pool->rpc_proxy = malloc(1 + len - plen); + if (!(pool->rpc_proxy)) + quit(1, "Failed to malloc rpc_proxy"); + + strcpy(pool->rpc_proxy, url + plen); + pool->rpc_proxytype = proxynames[i].proxytype; + url = split + 1; + break; + } + } +#endif + return url; +} + + char *bin2hex(const unsigned char *p, size_t len) { char *s = malloc((len * 2) + 1); @@ -722,3 +788,9 @@ double us_tdiff(struct timeval *end, struct timeval *start) { return end->tv_sec * 1000000 + end->tv_usec - start->tv_sec * 1000000 - start->tv_usec; } + +/* Returns the seconds difference between end and start times as a double */ +double tdiff(struct timeval *end, struct timeval *start) +{ + return end->tv_sec - start->tv_sec + (end->tv_usec - start->tv_usec) / 1000000.0; +}