Browse Source

Merge branch 'master' of git://github.com/ckolivas/cgminer.git

nfactor-troky
Paul Sheppard 12 years ago
parent
commit
c5696629d7
  1. 2
      API-README
  2. 2
      Makefile.am
  3. 225
      NEWS
  4. 30
      README
  5. 76
      api.c
  6. 940
      cgminer.c
  7. 8
      configure.ac
  8. 7
      driver-bitforce.c
  9. 73
      driver-icarus.c
  10. 10
      driver-modminer.c
  11. 9
      driver-opencl.c
  12. 10
      findnonce.c
  13. 2
      fpgautils.c
  14. 2
      logging.c
  15. 91
      miner.h
  16. 8
      ocl.c
  17. 608
      util.c
  18. 53
      util.h

2
API-README

@ -383,7 +383,7 @@ miner.php - an example web page to access the API @@ -383,7 +383,7 @@ miner.php - an example web page to access the API
Feature Changelog for external applications using the API:
API V1.19
API V1.19 (cgminer v2.7.6)
Added API commands:
'debug'

2
Makefile.am

@ -32,7 +32,7 @@ cgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @OPENCL_FLAGS@ @@ -32,7 +32,7 @@ cgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @OPENCL_FLAGS@
cgminer_SOURCES := cgminer.c
cgminer_SOURCES += elist.h miner.h compat.h bench_block.h \
util.c uthash.h logging.h \
util.c util.h uthash.h logging.h \
sha2.c sha2.h api.c
cgminer_SOURCES += logging.c

225
NEWS

@ -1,5 +1,230 @@ @@ -1,5 +1,230 @@
Version 2.8.3 - October 12, 2012
- Left align values that are suffix_string generated.
- Share_diff should not be converting the work data to hex.
- Off by one error.
- Prevent overflows of the port char array in extract_sockaddr.
- Disable stratum detection with scrypt.
- Use the suffix string function when displaying device hashrates.
- Be consistent with the get_statline function.
- Use the suffix string function for displaying hashrate with 4 significant
digits.
- Display the actual share diff next to the pool required diff, using a suffix
creation function to prevent values of >1000 being shown in their entirety.
- Fix 4 * 0 being 0 that would break dynamic intensity mode.
- Fix wrong byteswap macro being used on mingw32 which was breaking target
generation on stratum.
Version 2.8.2 - October 11, 2012
- Reinstate the history on dynamic intensity mode to damp fluctuations in
intensity but use an upper limit on how much the value can increase at any time
to cope with rare overflows.
- Create a fix-protocol option which prevents cgminer from switching to stratum
if it's detected.
- Simplify target generation code.
- Add support for client.get_version for stratum.
- Use a 64 bit unsigned integer on the diff target to generate the hex target.
- Update reconnect message to show whole address including port.
- Look for null values and parse correct separate array entries for url and port
with client reconnect commands for stratum.
- The command for stratum is client.reconnect, not mining.reconnect.
- Only copy the stratum url to the rpc url if an rpc url does not exist.
- Implement rudimentary mining.reconnect support for stratum.
- Ignore the value of stratum_active on calling initiate_stratum and assume
we're always trying to reinitiate it, and set the active flag to false in that
function.
- stratum auth can be unset if we fail to authorise on subsequent calls to
auth_stratum which undoes the requirement of setting it in one place so set it
in pool_active.
Version 2.8.1 - October 8, 2012
- Use the stratum url as the rpc url advertised if we switch to it.
- Count an invalid nonce count as a hardware error on opencl.
- Count each stratum work item as local work.
- Cope with one stratum pool being the only active pool when it dies by sleeping
for 5 seconds before retrying to get work from it instead of getting work
indefinitely.
- Detect stratum outage based on either select timing out or receiving an empty
buffer and properly re-establish connection by disabling the stratum_active
flag, coping with empty buffers in parse_stratum.
Version 2.8.0 - October 7, 2012
- Major upgrade - support for the stratum mining protocol.
- Fix various modminer warnings on mingw.
- Fix sign warning on windows build for bitforce.
- Cast socketfail to integer since SOCKET is an unsigned int on windows.
- Use strtod not strtol for bitforce temp backup.
- Cope with broken drivers returning nonsense values for bitforce temperatures.
- Minor warning fixes.
- Use the stratum thread to detect when a stratum pool has died based on no
message for 2 minutes.
- Only set the stratum auth flag once and once the stratum thread is started,
use that to set/unset the stratum active flag.
- Only hand off to stratum from getwork if we succeed in initiating the
protocol.
- Target should only be 32 bytes copied.
- Use a static array for work submission data instead of stack memory.
- Clear the buffer data before sprinting to it.
- Clear work stratum strings before setting them and add them to debug output.
- Drop stratum connect failed message to verbose level only since it's a regular
probing message.
- TCP Keepalive in curl is only in very recent versions and not required with
regular messages on stratum anyway.
- Move stratum sockets to curl infrastructure with locking around send+recv to
begin support for proxies and ssl.
- Make detect stratum fail if a proxy has been set up.
- Stratum does not currently have any proxy support so do not try to switch to
stratum if a proxy has been specified.
- Windows doesn't work with MSG_PEEK on recv so move to a continuously updating
buffer for incoming messages.
- Alloca is unreliable on windows so use static arrays in util.c stratum code.
- Begin support for mingw stratum build.
- Add space to reject reason.
- Parse the reject reason where possible from stratum share submission.
- Pass json error value to share result function to be able to parse reject
reason in stratum.
- Don't try to parse unneeded parameters in response to mining.subscribe.
- Remove the sshare hash entry if we failed to send it.
- Change notify message to info level to avoid spamming repeatedly when a pool
is down.
- Check the stratum pool difference has not changed compared to the work diff
when testing whether a share meets the target or not and retarget if necessary.
- Bit error in target calculation for stratum.
- Set work_block in gen_stratum_work for when work is reused to avoid thinking
it's all stale.
- Offset the current block detection to the prev block hash.
- We should be testing for id_val, not id in parse stratum response.
- Make target on stratum scale to any size by clearing sequential bits according
to diff.
- Correct target calculation in gen_stratum_work.
- If a share result has an error code but still has an id, it is likely a
reject, not an error.
- Initiate stratum the first time in pool_active only, allowing us to switch to
it on getting a failed getwork and detecting the presence of stratum on the url
at that time.
- Use 5 second timeout on sock full for now as a temporary workaround.
- If no stratum url is set by the end of the detect stratum routine, copy the
sockaddr url.
- Make all buffers slightly larger to prevent overflow.
- Make the stratum recv buffer larger than the recvsize.
- Userpass needs to be copied to user and pass earlier to allow stratum
authorisation to work with it.
- Store a sockaddr url of the stripped url used in determining sockaddr to not
confuse it with the stratum url and fix build warnings.
- Decrease the queued count with stratum work once it's staged as well.
- Allow the stratum retry to initiate and auth stratum in pool_alive to make
sure the stratum thread is started.
- Avoid duplicating pool->rpc_url and setting pool->stratum_url twice to itself.
- Detect if a getwork based pool has the X-Stratum header on startup, and if so,
switch to the stratum based pool.
- Comment update.
- Minor message change.
- Create a work item from a "clean" request from stratum allowing the new block
to be detected and the appropriate block change message to be given.
- Use statically allocated stratum strings in struct work to cope with the
inability to safely deallocate dynamically allocated ram.
- Use the current pool when deciding whether to reuse work from a stratum source
rather than the work's previous pool.
- Copy the stratum url to the rpc url to avoid none being set.
- Provide locking around stratum send operations to avoid races.
- Submit shares from stratum through the abstracted submit share function
detecting what message they belong to and showing the data from the associated
work, and then deleting it from the hash.
- Use a more robust mechanism to obtain a \n terminated string over a socket.
- Abstract out share submit as a function to be useable by stratum.
- Rename parse_stratum to parse_method as it is only for stratum messages that
contain methods.
- Display stratum as mechanism in status line when current pool is running it.
- Count each stratum notify as a getwork equivalent.
- Correct nonce submitted with share.
- Extranonce2 should be added before coinbase2.
- We should be hashing the binary coinbase, not the hex one.
- Fix endianness of nonce submitted for stratum.
- Check that stratum is already active in initiate_stratum to avoid
de-authorising ourselves by subscribing again.
- Begin implementing a hash database of submissions and attempt sending results.
- Copy parameters from stratum work required for share submission.
- Set lagging flag on first adding a pool to prevent pool slow warning at
startup.
- Fix work->target being a 32 byte binary in gen_stratum_work.
- Store and display stripped url in its own variable.
- Create machinery to divert work requests to stratum.
- Generate the work target in gen_stratum_work, setting default diff to 1 in
case it is not yet set.
- Generate work data, midstate and hash1 in gen_stratum_work.
- Generate header created from stratum structures in gen_stratum_work.
- Generate merkle root hash in gen_stratum_work.
- Generate the coinbase for generation of stratum based work.
- The number of transactions is variable so make merkle a variable length
dynamically allocated array and track how many there are for stratum.
- Rename nonce2 to n2size reflecting that it's a size variable and not the
actual nonce.
- Provide rudimentary support for stratum clean work command in the stratum
thread.
- Cope with pools being removed in the stratum thread.
- Use the pool sock value directly in the stratum thread in case it changes
after reconnecting.
- Create a stratum thread per pool that has stratum that monitors the socket and
serves received data.
- Check return value of stratum_parse.
- Complete authorisation in stratum.
- Implement stratum parsing of notify parameters and storing them in the pool
stratum work structure.
- Create helper functions for duplicating json strings to avoid keeping json
references in use.
- Append \n in the sock_send function instead of adding it when constructing
json in stratum.
- Don't keep any json references around with stratum structures.
- Create parse_stratum function that hands off stratum parameters to other
functions to manage pool stratum work struct variables. Implement mining
difficulty setting.
- Create helper functions for checking when a socket is ready to read on and
receive a single line at a time. Begin stratum authorisation process.
- Provide a helper function for reading a single \n terminated string from a
socket.
- Create a stratum work structure to store current work variables.
- Test specifically for stratum being active in pool_active.
- Detect stratum in common place when adding urls, and use a bool to tell us
when it's active.
- Fix warnings.
- Extract and store various parameters on stratum init confirming successful
mining notify.
- Use existing socket macros and close the socket on failure in init stratum.
- Initiate stratum and grab first json result.
- Get detailed addressinfo from the parsed URL for future raw socket usage when
possible. IPV4 only for now.
- Prepare for getaddrinfo call.
- Add data structures to pool struct for socket communications.
- Put all socket definitions in util.h to allow reusing by added socket
functions to be used in util.c.
Version 2.7.7 - October 7, 2012
- Fix unused warnings on ming build.
- Fix sign warning in ocl.c
- fds need to be zeroed before set in modminer.
- Put scrypt warning on separate line to avoid 0 being shown on windows as
bufsize.
- Display correct pool number when block is found.
- Prevent corrupt values returned from the opencl code from trying to read
beyond the end of the buffer by masking the value to a max of 15.
- Icarus USB write failure is also a comms error
- api.c DEBUG message has no paramter
- Icarus catch more USB errors and close/reopen the port
- API-README update cgminer verison number
- hashmeter fix stats kh/s on 32bit windows
Version 2.7.6 - September 24, 2012
- Reorder libztex header include order to fix missing struct definition.
- 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

30
README

@ -146,6 +146,7 @@ Options for both config file and command line: @@ -146,6 +146,7 @@ Options for both config file and command line:
--debug|-D Enable debug output
--expiry|-E <arg> Upper bound on how many seconds after getting work we consider a share from it stale (default: 120)
--failover-only Don't leak work to backup pools when primary pool is lagging
--fix-protocol Do not redirect to a different getwork protocol (eg. stratum)
--kernel-path|-K <arg> Specify a path to where bitstream and kernel files are (default: "/usr/local/bin")
--load-balance Change multipool strategy from failover to efficiency based balance
--log|-l <arg> Interval in seconds between log output (default: 5)
@ -304,6 +305,10 @@ Single pool with a socks5 proxy, regular desktop: @@ -304,6 +305,10 @@ Single pool with a socks5 proxy, regular desktop:
cgminer -o "socks5:proxy:port|http://pool:port" -u username -p password
Single pool with stratum protocol support:
cgminer -o stratum+tcp://pool:port -u username -p password
The list of proxy types are:
http: standard http 1.1 proxy
http0: http 1.0 proxy
@ -377,6 +382,17 @@ Thread 1: 60.2 Mh/s Enabled ALIVE @@ -377,6 +382,17 @@ Thread 1: 60.2 Mh/s Enabled ALIVE
Or press any other key to continue
The running log shows output like this:
[2012-10-12 18:02:20] Accepted f0c05469 Diff 1/1 GPU 0 pool 1
[2012-10-12 18:02:22] Accepted 218ac982 Diff 7/1 GPU 1 pool 1
[2012-10-12 18:02:23] Accepted d8300795 Diff 1/1 GPU 3 pool 1
[2012-10-12 18:02:24] Accepted 122c1ff1 Diff 14/1 GPU 1 pool 1
The 8 byte hex value are the 2nd 8 bytes of the share being submitted to the
pool. The 2 diff values are the actual difficulty target that share reached
followed by the difficulty target the pool is currently asking for.
---
Also many issues and FAQs are covered in the forum thread
dedicated to this program,
@ -908,6 +924,20 @@ To permanently give your account the 'dialout' group: @@ -908,6 +924,20 @@ To permanently give your account the 'dialout' group:
sudo usermod -G dialout -a `whoami`
Then logout and back in again
Q: What is stratum and how do I use it?
A: Stratum is a protocol designed for pooled mining in such a way as to
minimise the amount of network communications, yet scale to hardware of any
speed. With versions of cgminer 2.8.0+, if a pool has stratum support, cgminer
will automatically detect it and switch to the support as advertised if it can.
Stratum uses direct TCP connections to the pool and thus it will NOT currently
work through a http proxy but will work via a socks proxy if you need to use
one. If you input the stratum port directly into your configuration, or use the
special prefix "stratum+tcp://" instead of "http://", cgminer will ONLY try to
use stratum protocol mining. The advantages of stratum to the miner are no
delays in getting more work for the miner, less rejects across block changes,
and far less network communications for the same amount of mining hashrate. If
you do NOT wish cgminer to automatically switch to stratum protocol even if it
is detected, add the --fix-protocol option.
---

76
api.c

@ -25,43 +25,32 @@ @@ -25,43 +25,32 @@
#include "compat.h"
#include "miner.h"
#include "util.h"
#include "driver-cpu.h" /* for algo_names[], TODO: re-factor dependency */
#if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) || defined(USE_MODMINER)
#define HAVE_AN_FPGA 1
#endif
#if defined(unix) || defined(__APPLE__)
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SOCKETTYPE int
#define SOCKETFAIL(a) ((a) < 0)
#define INVSOCK -1
#define INVINETADDR -1
#define CLOSESOCKET close
#define SOCKERRMSG strerror(errno)
#endif
// Big enough for largest API request
// though a PC with 100s of PGAs/CPUs may exceed the size ...
// Current code assumes it can socket send this size also
#define MYBUFSIZ 65432 // TODO: intercept before it's exceeded
#ifdef WIN32
#include <ws2tcpip.h>
#include <winsock2.h>
// BUFSIZ varies on Windows and Linux
#define TMPBUFSIZ 8192
#define SOCKETTYPE SOCKET
#define SOCKETFAIL(a) ((a) == SOCKET_ERROR)
#define INVSOCK INVALID_SOCKET
#define INVINETADDR INADDR_NONE
#define CLOSESOCKET closesocket
// Number of requests to queue - normally would be small
// However lots of PGA's may mean more
#define QUEUE 100
static char WSAbuf[1024];
#if defined WIN32
static char WSAbuf[1024];
struct WSAERRORS {
struct WSAERRORS {
int id;
char *code;
} WSAErrors[] = {
} WSAErrors[] = {
{ 0, "No error" },
{ WSAEINTR, "Interrupted system call" },
{ WSAEBADF, "Bad file number" },
@ -113,10 +102,9 @@ @@ -113,10 +102,9 @@
{ WSAHOST_NOT_FOUND, "Host not found" },
{ WSANO_DATA, "No host data of that type was found" },
{ -1, "Unknown error code" }
};
};
static char *WSAErrorMsg()
{
char *WSAErrorMsg(void) {
int i;
int id = WSAGetLastError();
@ -128,31 +116,8 @@ @@ -128,31 +116,8 @@
sprintf(WSAbuf, "Socket Error: (%d) %s", id, WSAErrors[i].code);
return &(WSAbuf[0]);
}
#define SOCKERRMSG WSAErrorMsg()
#ifndef SHUT_RDWR
#define SHUT_RDWR SD_BOTH
#endif
#ifndef in_addr_t
#define in_addr_t uint32_t
#endif
}
#endif
// Big enough for largest API request
// though a PC with 100s of PGAs/CPUs may exceed the size ...
// Current code assumes it can socket send this size also
#define MYBUFSIZ 65432 // TODO: intercept before it's exceeded
// BUFSIZ varies on Windows and Linux
#define TMPBUFSIZ 8192
// Number of requests to queue - normally would be small
// However lots of PGA's may mean more
#define QUEUE 100
static char *io_buffer = NULL;
static char *msg_buffer = NULL;
static SOCKETTYPE sock = INVSOCK;
@ -558,7 +523,7 @@ struct CODES { @@ -558,7 +523,7 @@ 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" },
{ SEVERITY_SUCC, MSG_DEBUGSET,PARAM_NONE, "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" },
@ -2210,6 +2175,7 @@ exitsama: @@ -2210,6 +2175,7 @@ exitsama:
static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
char *url, *user, *pass;
struct pool *pool;
char *ptr;
if (param == NULL || *param == '\0') {
@ -2226,7 +2192,9 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __may @@ -2226,7 +2192,9 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __may
return;
}
add_pool_details(true, url, user, pass);
pool = add_pool();
detect_stratum(pool, url);
add_pool_details(pool, true, url, user, pass);
ptr = escape_string(url, isjson);
strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson));

940
cgminer.c

File diff suppressed because it is too large Load Diff

8
configure.ac

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
m4_define([v_maj], [2])
m4_define([v_min], [7])
m4_define([v_mic], [6])
m4_define([v_min], [8])
m4_define([v_mic], [3])
##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
m4_define([v_ver], [v_maj.v_min.v_mic])
m4_define([lt_rev], m4_eval(v_maj + v_min))
@ -92,6 +92,7 @@ case $target in @@ -92,6 +92,7 @@ case $target in
PTHREAD_FLAGS=""
DLOPEN_FLAGS=""
WS2_LIBS="-lws2_32"
AC_DEFINE([_WIN32_WINNT], [0x0501], "WinNT version for XP+ support")
;;
powerpc-*-darwin*)
CFLAGS="$CFLAGS -faltivec"
@ -351,8 +352,7 @@ fi @@ -351,8 +352,7 @@ fi
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.15.6], [AC_DEFINE([CURL_HAS_SOCKOPT], [1], [Defined if version of curl supports sockopts.])],
[PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.10.1], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.10.1])])])
PKG_CHECK_MODULES([LIBCURL], [libcurl >= 7.18.2], ,[AC_MSG_ERROR([Missing required libcurl dev >= 7.18.2])])
AC_SUBST(LIBCURL_LIBS)
dnl CCAN wants to know a lot of vars.

7
driver-bitforce.c

@ -154,7 +154,7 @@ static int bitforce_autodetect_ftdi(void) @@ -154,7 +154,7 @@ static int bitforce_autodetect_ftdi(void)
char **bufptrs;
char *buf;
int found = 0;
int i;
DWORD i;
FT_STATUS ftStatus;
DWORD numDevs;
@ -410,6 +410,11 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce) @@ -410,6 +410,11 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) {
float temp = strtof(s + 1, NULL);
/* Cope with older software that breaks and reads nonsense
* values */
if (temp > 100)
temp = strtod(s + 1, NULL);
if (temp > 0) {
bitforce->temp = temp;
if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) {

73
driver-icarus.c

@ -29,6 +29,8 @@ @@ -29,6 +29,8 @@
* nonce range is completely calculated.
*/
#include "config.h"
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
@ -223,6 +225,11 @@ static void rev(unsigned char *s, size_t l) @@ -223,6 +225,11 @@ static void rev(unsigned char *s, size_t l)
#define icarus_open2(devpath, baud, purge) serial_open(devpath, baud, ICARUS_READ_FAULT_DECISECONDS, purge)
#define icarus_open(devpath, baud) icarus_open2(devpath, baud, false)
#define ICA_GETS_ERROR -1
#define ICA_GETS_OK 0
#define ICA_GETS_RESTART 1
#define ICA_GETS_TIMEOUT 2
static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, struct thr_info *thr, int read_count)
{
ssize_t ret = 0;
@ -233,12 +240,14 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, st @@ -233,12 +240,14 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, st
// Read reply 1 byte at a time to get earliest tv_finish
while (true) {
ret = read(fd, buf, 1);
if (ret < 0)
return ICA_GETS_ERROR;
if (first)
gettimeofday(tv_finish, NULL);
if (ret >= read_amount)
return 0;
return ICA_GETS_OK;
if (ret > 0) {
buf += ret;
@ -254,16 +263,16 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, st @@ -254,16 +263,16 @@ static int icarus_gets(unsigned char *buf, int fd, struct timeval *tv_finish, st
"Icarus Read: No data in %.2f seconds",
(float)rc/(float)TIME_FACTOR);
}
return 1;
return ICA_GETS_TIMEOUT;
}
if (thr->work_restart) {
if (thr && thr->work_restart) {
if (opt_debug) {
applog(LOG_DEBUG,
"Icarus Read: Work restart at %.2f seconds",
(float)(rc)/(float)TIME_FACTOR);
}
return 1;
return ICA_GETS_RESTART;
}
}
}
@ -281,6 +290,13 @@ static int icarus_write(int fd, const void *buf, size_t bufLen) @@ -281,6 +290,13 @@ static int icarus_write(int fd, const void *buf, size_t bufLen)
#define icarus_close(fd) close(fd)
static void do_icarus_close(struct thr_info *thr)
{
struct cgpu_info *icarus = thr->cgpu;
icarus_close(icarus->device_fd);
icarus->device_fd = -1;
}
static const char *timing_mode_str(enum timing_mode timing_mode)
{
switch(timing_mode) {
@ -533,10 +549,7 @@ static bool icarus_detect_one(const char *devpath) @@ -533,10 +549,7 @@ static bool icarus_detect_one(const char *devpath)
gettimeofday(&tv_start, NULL);
memset(nonce_bin, 0, sizeof(nonce_bin));
struct thr_info dummy = {
.work_restart = false,
};
icarus_gets(nonce_bin, fd, &tv_finish, &dummy, 1);
icarus_gets(nonce_bin, fd, &tv_finish, NULL, 1);
icarus_close(fd);
@ -563,6 +576,7 @@ static bool icarus_detect_one(const char *devpath) @@ -563,6 +576,7 @@ static bool icarus_detect_one(const char *devpath)
icarus = calloc(1, sizeof(struct cgpu_info));
icarus->api = &icarus_api;
icarus->device_path = strdup(devpath);
icarus->device_fd = -1;
icarus->threads = 1;
add_cgpu(icarus);
icarus_info = realloc(icarus_info, sizeof(struct ICARUS_INFO *) * (total_devices + 1));
@ -607,6 +621,8 @@ static bool icarus_prepare(struct thr_info *thr) @@ -607,6 +621,8 @@ static bool icarus_prepare(struct thr_info *thr)
struct timeval now;
icarus->device_fd = -1;
int fd = icarus_open(icarus->device_path, icarus_info[icarus->device_id]->baud);
if (unlikely(-1 == fd)) {
applog(LOG_ERR, "Failed to open Icarus on %s",
@ -653,6 +669,17 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, @@ -653,6 +669,17 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
elapsed.tv_sec = elapsed.tv_usec = 0;
icarus = thr->cgpu;
if (icarus->device_fd == -1)
if (!icarus_prepare(thr)) {
applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
icarus->device_last_not_well = time(NULL);
icarus->device_not_well_reason = REASON_DEV_COMMS_ERROR;
icarus->dev_comms_error_count++;
// fail the device if the reopen attempt fails
return -1;
}
fd = icarus->device_fd;
memset(ob_bin, 0, sizeof(ob_bin));
@ -664,8 +691,14 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, @@ -664,8 +691,14 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
tcflush(fd, TCOFLUSH);
#endif
ret = icarus_write(fd, ob_bin, sizeof(ob_bin));
if (ret)
return -1; /* This should never happen */
if (ret) {
do_icarus_close(thr);
applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
icarus->device_last_not_well = time(NULL);
icarus->device_not_well_reason = REASON_DEV_COMMS_ERROR;
icarus->dev_comms_error_count++;
return 0; /* This should never happen */
}
gettimeofday(&tv_start, NULL);
@ -682,12 +715,19 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, @@ -682,12 +715,19 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
memset(nonce_bin, 0, sizeof(nonce_bin));
info = icarus_info[icarus->device_id];
ret = icarus_gets(nonce_bin, fd, &tv_finish, thr, info->read_count);
if (ret == ICA_GETS_ERROR) {
do_icarus_close(thr);
applog(LOG_ERR, "ICA%i: Comms error", icarus->device_id);
icarus->device_last_not_well = time(NULL);
icarus->device_not_well_reason = REASON_DEV_COMMS_ERROR;
icarus->dev_comms_error_count++;
return 0;
}
work->blk.nonce = 0xffffffff;
memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin));
// aborted before becoming idle, get new work
if (nonce == 0 && ret) {
if (ret == ICA_GETS_TIMEOUT || ret == ICA_GETS_RESTART) {
timersub(&tv_finish, &tv_start, &elapsed);
// ONLY up to just when it aborted
@ -709,6 +749,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, @@ -709,6 +749,8 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
return estimate_hashes;
}
memcpy((char *)&nonce, nonce_bin, sizeof(nonce_bin));
#if !defined (__BIG_ENDIAN__) && !defined(MIPSEB)
nonce = swab32(nonce);
#endif
@ -717,6 +759,10 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work, @@ -717,6 +759,10 @@ static int64_t icarus_scanhash(struct thr_info *thr, struct work *work,
submit_nonce(thr, work, nonce);
was_hw_error = (curr_hw_errors > icarus->hw_errors);
// Force a USB close/reopen on any hw error
if (was_hw_error)
do_icarus_close(thr);
hash_count = (nonce & info->nonce_mask);
hash_count++;
hash_count *= info->fpga_count;
@ -862,8 +908,7 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu) @@ -862,8 +908,7 @@ static struct api_data *icarus_api_stats(struct cgpu_info *cgpu)
static void icarus_shutdown(struct thr_info *thr)
{
struct cgpu_info *icarus = thr->cgpu;
icarus_close(icarus->device_fd);
do_icarus_close(thr);
}
struct device_api icarus_api = {

10
driver-modminer.c

@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
#include "logging.h"
#include "miner.h"
#include "fpgautils.h"
#include "util.h"
#define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.ncd"
#define BISTREAM_USER_ID "\2\4$B"
@ -48,7 +49,7 @@ modminer_detect_one(const char *devpath) @@ -48,7 +49,7 @@ modminer_detect_one(const char *devpath)
bailout(LOG_DEBUG, "ModMiner detect: failed to open %s", devpath);
char buf[0x100];
size_t len;
ssize_t len;
// Sending 45 noops, just in case the device was left in "start job" reading
(void)(write(fd, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", 45) ?:0);
@ -128,6 +129,7 @@ modminer_detect() @@ -128,6 +129,7 @@ modminer_detect()
} while(0)
#define status_read(eng) do { \
FD_ZERO(&fds); \
FD_SET(fd, &fds); \
select(fd+1, &fds, NULL, NULL, NULL); \
if (1 != read(fd, buf, 1)) \
@ -139,7 +141,7 @@ select(fd+1, &fds, NULL, NULL, NULL); \ @@ -139,7 +141,7 @@ select(fd+1, &fds, NULL, NULL, NULL); \
static bool
modminer_fpga_upload_bitstream(struct cgpu_info*modminer)
{
fd_set fds;
fd_set fds;
char buf[0x100];
unsigned char *ubuf = (unsigned char*)buf;
unsigned long len;
@ -185,7 +187,7 @@ fd_set fds; @@ -185,7 +187,7 @@ fd_set fds;
len = ((unsigned long)ubuf[0] << 24) | ((unsigned long)ubuf[1] << 16) | (ubuf[2] << 8) | ubuf[3];
applog(LOG_DEBUG, " Bitstream size: %lu", len);
int fd = modminer->device_fd;
SOCKETTYPE fd = modminer->device_fd;
applog(LOG_WARNING, "%s %u: Programming %s... DO NOT EXIT CGMINER UNTIL COMPLETE", modminer->api->name, modminer->device_id, modminer->device_path);
buf[0] = '\x05'; // Program Bitstream
@ -375,7 +377,7 @@ fd_set fds; @@ -375,7 +377,7 @@ fd_set fds;
struct cgpu_info*modminer = thr->cgpu;
struct modminer_fpga_state *state = thr->cgpu_data;
char fpgaid = thr->device_thread;
int fd = modminer->device_fd;
SOCKETTYPE fd = modminer->device_fd;
char buf[1];

9
driver-opencl.c

@ -1585,14 +1585,19 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work, @@ -1585,14 +1585,19 @@ 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;
/* Very rarely we may get an overflow so put an upper
* limit on the detected time */
if (unlikely(gpu->gpu_us_average > 0 && gpu_us > gpu->gpu_us_average * 4))
gpu_us = gpu->gpu_us_average * 4;
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_us > dynamic_us) {
if (gpu->gpu_us_average > dynamic_us) {
if (gpu->intensity > MIN_INTENSITY)
--gpu->intensity;
} else if (gpu_us < dynamic_us / 2) {
} else if (gpu->gpu_us_average < dynamic_us / 2) {
if (gpu->intensity < MAX_INTENSITY)
++gpu->intensity;
}

10
findnonce.c

@ -252,6 +252,16 @@ static void *postcalc_hash(void *userdata) @@ -252,6 +252,16 @@ static void *postcalc_hash(void *userdata)
pthread_detach(pthread_self());
/* To prevent corrupt values in FOUND from trying to read beyond the
* end of the res[] array */
if (unlikely(pcd->res[FOUND] & ~FOUND)) {
applog(LOG_WARNING, "%s%d: invalid nonce count - HW error",
thr->cgpu->api->name, thr->cgpu->device_id);
hw_errors++;
thr->cgpu->hw_errors++;
pcd->res[FOUND] &= FOUND;
}
for (entry = 0; entry < pcd->res[FOUND]; entry++) {
uint32_t nonce = pcd->res[entry];

2
fpgautils.c

@ -74,7 +74,7 @@ int serial_autodetect_udev(__maybe_unused detectone_func_t detectone, __maybe_un @@ -74,7 +74,7 @@ int serial_autodetect_udev(__maybe_unused detectone_func_t detectone, __maybe_un
}
#endif
int serial_autodetect_devserial(detectone_func_t detectone, const char*prodname)
int serial_autodetect_devserial(__maybe_unused detectone_func_t detectone, __maybe_unused const char*prodname)
{
#ifndef WIN32
DIR *D;

2
logging.c

@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
* any later version. See COPYING for more details.
*/
#include "config.h"
#include <unistd.h>
#include "logging.h"

91
miner.h

@ -12,6 +12,7 @@ @@ -12,6 +12,7 @@
#include "elist.h"
#include "uthash.h"
#include "logging.h"
#include "util.h"
#ifdef HAVE_OPENCL
#ifdef __APPLE_CC__
@ -51,6 +52,19 @@ void *alloca (size_t); @@ -51,6 +52,19 @@ void *alloca (size_t);
# endif
#endif
#ifdef __MINGW32__
#include <windows.h>
#include <io.h>
static inline int fsync (int fd)
{
return (FlushFileBuffers ((HANDLE) _get_osfhandle (fd))) ? 0 : -1;
}
#ifndef MSG_DONTWAIT
# define MSG_DONTWAIT 0x1000000
#endif
#endif /* __MINGW32__ */
#if defined (__linux)
#ifndef LINUX
#define LINUX
@ -125,14 +139,19 @@ void *alloca (size_t); @@ -125,14 +139,19 @@ void *alloca (size_t);
#endif
#endif /* !defined(__GLXBYTEORDER_H__) */
/* This assumes htobe32 is a macro in endian.h */
/* This assumes htobe32 is a macro in endian.h, and if it doesn't exist, then
* htobe64 also won't exist */
#ifndef htobe32
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define be32toh(x) bswap_32(x)
# define be64toh(x) bswap_64(x)
# define htobe32(x) bswap_32(x)
# define htobe64(x) bswap_64(x)
# elif __BYTE_ORDER == __BIG_ENDIAN
# define be32toh(x) (x)
# define be64toh(x) (x)
# define htobe32(x) (x)
# define htobe64(x) (x)
#else
#error UNKNOWN BYTE ORDER
#endif
@ -378,11 +397,12 @@ struct cgpu_info { @@ -378,11 +397,12 @@ struct cgpu_info {
#ifdef USE_SCRYPT
int opt_lg, lookup_gap;
int opt_tc, thread_concurrency;
int shaders;
size_t opt_tc, thread_concurrency;
size_t shaders;
#endif
struct timeval tv_gpustart;
struct timeval tv_gpumid;
double gpu_us_average;
int intervals, hit;
#endif
@ -510,6 +530,21 @@ static inline void swap256(void *dest_p, const void *src_p) @@ -510,6 +530,21 @@ static inline void swap256(void *dest_p, const void *src_p)
dest[7] = src[0];
}
static inline void swab256(void *dest_p, const void *src_p)
{
uint32_t *dest = dest_p;
const uint32_t *src = src_p;
dest[0] = swab32(src[7]);
dest[1] = swab32(src[6]);
dest[2] = swab32(src[5]);
dest[3] = swab32(src[4]);
dest[4] = swab32(src[3]);
dest[5] = swab32(src[2]);
dest[6] = swab32(src[1]);
dest[7] = swab32(src[0]);
}
extern void quit(int status, const char *format, ...);
static inline void mutex_lock(pthread_mutex_t *lock)
@ -594,6 +629,7 @@ extern bool opt_worktime; @@ -594,6 +629,7 @@ extern bool opt_worktime;
#ifdef USE_BITFORCE
extern bool opt_bfl_noncerange;
#endif
extern int swork_id;
extern pthread_rwlock_t netacc_lock;
@ -645,7 +681,9 @@ extern void api(int thr_id); @@ -645,7 +681,9 @@ extern void api(int thr_id);
extern struct pool *current_pool(void);
extern int enabled_pools;
extern void add_pool_details(bool live, char *url, char *user, char *pass);
extern bool detect_stratum(struct pool *pool, char *url);
extern struct pool *add_pool(void);
extern void add_pool_details(struct pool *pool, bool live, char *url, char *user, char *pass);
#define MAX_GPUDEVICES 16
@ -748,6 +786,24 @@ enum pool_enable { @@ -748,6 +786,24 @@ enum pool_enable {
POOL_REJECTING,
};
struct stratum_work {
char *job_id;
char *prev_hash;
char *coinbase1;
char *coinbase2;
char **merkle;
char *bbversion;
char *nbit;
char *ntime;
bool clean;
int merkles;
int diff;
};
#define RECVSIZE 8191
#define RBUFSIZE (RECVSIZE + 1)
struct pool {
int pool_no;
int prio;
@ -810,12 +866,31 @@ struct pool { @@ -810,12 +866,31 @@ struct pool {
struct cgminer_stats cgminer_stats;
struct cgminer_pool_stats cgminer_pool_stats;
/* Stratum variables */
char *stratum_url;
char *stratum_port;
CURL *stratum_curl;
SOCKETTYPE sock;
char sockbuf[RBUFSIZE];
struct sockaddr_in *server, client;
char *sockaddr_url; /* stripped url used for sockaddr */
char *nonce1;
uint32_t nonce2;
int n2size;
bool has_stratum;
bool stratum_active;
bool stratum_auth;
struct stratum_work swork;
pthread_t stratum_thread;
pthread_mutex_t stratum_lock;
};
#define GETWORK_MODE_TESTPOOL 'T'
#define GETWORK_MODE_POOL 'P'
#define GETWORK_MODE_LP 'L'
#define GETWORK_MODE_BENCHMARK 'B'
#define GETWORK_MODE_STRATUM 'S'
struct work {
unsigned char data[128];
@ -845,6 +920,14 @@ struct work { @@ -845,6 +920,14 @@ struct work {
bool block;
bool queued;
bool stratum;
/* These are arbitrary lengths as it is too hard to keep track of
* dynamically allocated ram in work structs */
char job_id[64];
char nonce2[64];
char ntime[16];
int sdiff;
unsigned int work_block;
int id;
UT_hash_handle hh;

8
ocl.c

@ -541,7 +541,7 @@ _clState *initCl(unsigned int gpu, char *name, size_t nameSize) @@ -541,7 +541,7 @@ _clState *initCl(unsigned int gpu, char *name, size_t nameSize)
strcat(binaryfilename, "g");
if (opt_scrypt) {
#ifdef USE_SCRYPT
sprintf(numbuf, "lg%dtc%d", cgpu->lookup_gap, cgpu->thread_concurrency);
sprintf(numbuf, "lg%utc%u", cgpu->lookup_gap, (unsigned int)cgpu->thread_concurrency);
strcat(binaryfilename, numbuf);
#endif
} else {
@ -614,7 +614,7 @@ build: @@ -614,7 +614,7 @@ build:
#ifdef USE_SCRYPT
if (opt_scrypt)
sprintf(CompilerOptions, "-D LOOKUP_GAP=%d -D CONCURRENT_THREADS=%d -D WORKSIZE=%d",
cgpu->lookup_gap, cgpu->thread_concurrency, (int)clState->wsize);
cgpu->lookup_gap, (unsigned int)cgpu->thread_concurrency, (int)clState->wsize);
else
#endif
{
@ -810,8 +810,8 @@ built: @@ -810,8 +810,8 @@ built:
/* Use the max alloc value which has been rounded to a power of
* 2 greater >= required amount earlier */
if (bufsize > cgpu->max_alloc) {
applog(LOG_WARNING, "Maximum buffer memory device %d supports says %u, your scrypt settings come to %u",
gpu, cgpu->max_alloc, bufsize);
applog(LOG_WARNING, "Maximum buffer memory device %d supports says %u", gpu, cgpu->max_alloc);
applog(LOG_WARNING, "Your scrypt settings come to %u", bufsize);
} else
bufsize = cgpu->max_alloc;
applog(LOG_DEBUG, "Creating scrypt buffer sized %d", bufsize);

608
util.c

@ -26,20 +26,17 @@ @@ -26,20 +26,17 @@
# include <sys/socket.h>
# include <netinet/in.h>
# include <netinet/tcp.h>
# include <netdb.h>
#else
# include <winsock2.h>
# include <mstcpip.h>
# include <ws2tcpip.h>
#endif
#include "miner.h"
#include "elist.h"
#include "compat.h"
#if JANSSON_MAJOR_VERSION >= 2
#define JSON_LOADS(str, err_ptr) json_loads((str), 0, (err_ptr))
#else
#define JSON_LOADS(str, err_ptr) json_loads((str), (err_ptr))
#endif
#include "util.h"
bool successful_connect = false;
struct timeval nettime;
@ -58,6 +55,7 @@ struct header_info { @@ -58,6 +55,7 @@ struct header_info {
char *lp_path;
int rolltime;
char *reason;
char *stratum_url;
bool hadrolltime;
bool canroll;
bool hadexpire;
@ -187,13 +185,17 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data) @@ -187,13 +185,17 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
val = NULL;
}
if (!strcasecmp("X-Stratum", key)) {
hi->stratum_url = val;
val = NULL;
}
out:
free(key);
free(val);
return ptrlen;
}
#ifdef CURL_HAS_SOCKOPT
int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
curlsocktype __maybe_unused purpose)
{
@ -241,7 +243,6 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd, @@ -241,7 +243,6 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
return 0;
}
#endif
static void last_nettime(struct timeval *last)
{
@ -265,7 +266,7 @@ json_t *json_rpc_call(CURL *curl, const char *url, @@ -265,7 +266,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
{
long timeout = longpoll ? (60 * 60) : 60;
struct data_buffer all_data = {NULL, 0};
struct header_info hi = {NULL, 0, NULL, false, false, false};
struct header_info hi = {NULL, 0, NULL, NULL, false, false, false};
char len_hdr[64], user_agent_hdr[128];
char curl_err_str[CURL_ERROR_SIZE];
struct curl_slist *headers = NULL;
@ -316,10 +317,8 @@ json_t *json_rpc_call(CURL *curl, const char *url, @@ -316,10 +317,8 @@ json_t *json_rpc_call(CURL *curl, const char *url,
curl_easy_setopt(curl, CURLOPT_USERPWD, userpass);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
}
#ifdef CURL_HAS_SOCKOPT
if (longpoll)
curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_sockopt_cb);
#endif
curl_easy_setopt(curl, CURLOPT_POST, 1);
if (opt_protocol)
@ -392,10 +391,20 @@ json_t *json_rpc_call(CURL *curl, const char *url, @@ -392,10 +391,20 @@ json_t *json_rpc_call(CURL *curl, const char *url,
pool->hdr_path = hi.lp_path;
} else
pool->hdr_path = NULL;
} else if (hi.lp_path) {
if (hi.stratum_url) {
pool->stratum_url = hi.stratum_url;
hi.stratum_url = NULL;
}
} else {
if (hi.lp_path) {
free(hi.lp_path);
hi.lp_path = NULL;
}
if (hi.stratum_url) {
free(hi.stratum_url);
hi.stratum_url = NULL;
}
}
*rolltime = hi.rolltime;
pool->cgminer_pool_stats.rolltime = hi.rolltime;
@ -794,3 +803,578 @@ double tdiff(struct timeval *end, struct timeval *start) @@ -794,3 +803,578 @@ double tdiff(struct timeval *end, struct timeval *start)
{
return end->tv_sec - start->tv_sec + (end->tv_usec - start->tv_usec) / 1000000.0;
}
bool extract_sockaddr(struct pool *pool, char *url)
{
char *url_begin, *url_end, *port_start = NULL;
char url_address[256], port[6];
struct addrinfo hints, *res;
int url_len, port_len = 0;
url_begin = strstr(url, "//");
if (!url_begin)
url_begin = url;
else
url_begin += 2;
url_end = strstr(url_begin, ":");
if (url_end) {
url_len = url_end - url_begin;
port_len = strlen(url_begin) - url_len - 1;
if (port_len < 1)
return false;
port_start = url_end + 1;
} else
url_len = strlen(url_begin);
if (url_len < 1)
return false;
sprintf(url_address, "%.*s", url_len, url_begin);
if (port_len)
snprintf(port, 6, "%.*s", port_len, port_start);
else
strcpy(port, "80");
pool->stratum_port = strdup(port);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo(url_address, port, &hints, &res)) {
applog(LOG_DEBUG, "Failed to extract sock addr");
return false;
}
pool->server = (struct sockaddr_in *)res->ai_addr;
pool->sockaddr_url = strdup(url_address);
return true;
}
/* Send a single command across a socket, appending \n to it */
bool stratum_send(struct pool *pool, char *s, ssize_t len)
{
ssize_t ssent = 0;
bool ret = false;
if (opt_protocol)
applog(LOG_DEBUG, "SEND: %s", s);
strcat(s, "\n");
len++;
mutex_lock(&pool->stratum_lock);
while (len > 0 ) {
size_t sent = 0;
if (curl_easy_send(pool->stratum_curl, s + ssent, len, &sent) != CURLE_OK) {
applog(LOG_DEBUG, "Failed to curl_easy_send in stratum_send");
ret = false;
goto out_unlock;
}
ssent += sent;
len -= ssent;
}
ret = true;
out_unlock:
mutex_unlock(&pool->stratum_lock);
return ret;;
}
#define RECVSIZE 8191
#define RBUFSIZE (RECVSIZE + 1)
static void clear_sock(struct pool *pool)
{
SOCKETTYPE sock = pool->sock;
recv(sock, pool->sockbuf, RECVSIZE, MSG_DONTWAIT);
strcpy(pool->sockbuf, "");
}
/* Check to see if Santa's been good to you */
static bool sock_full(struct pool *pool, bool wait)
{
SOCKETTYPE sock = pool->sock;
struct timeval timeout;
fd_set rd;
if (strlen(pool->sockbuf))
return true;
FD_ZERO(&rd);
FD_SET(sock, &rd);
timeout.tv_usec = 0;
if (wait)
timeout.tv_sec = 60;
else
timeout.tv_sec = 0;
if (select(sock + 1, &rd, NULL, NULL, &timeout) > 0)
return true;
return false;
}
/* Peeks at a socket to find the first end of line and then reads just that
* from the socket and returns that as a malloced char */
char *recv_line(struct pool *pool)
{
ssize_t len, buflen;
char *tok, *sret = NULL;
size_t n;
if (!strstr(pool->sockbuf, "\n")) {
char s[RBUFSIZE];
CURLcode rc;
if (!sock_full(pool, true)) {
applog(LOG_DEBUG, "Timed out waiting for data on sock_full");
goto out;
}
memset(s, 0, RBUFSIZE);
mutex_lock(&pool->stratum_lock);
rc = curl_easy_recv(pool->stratum_curl, s, RECVSIZE, &n);
mutex_unlock(&pool->stratum_lock);
if (rc != CURLE_OK) {
applog(LOG_DEBUG, "Failed to recv sock in recv_line");
goto out;
}
strcat(pool->sockbuf, s);
}
buflen = strlen(pool->sockbuf);
tok = strtok(pool->sockbuf, "\n");
if (!tok) {
applog(LOG_DEBUG, "Failed to parse a \\n terminated string in recv_line");
goto out;
}
sret = strdup(tok);
len = strlen(sret);
/* Copy what's left in the buffer after the \n, including the
* terminating \0 */
if (buflen > len + 1)
memmove(pool->sockbuf, pool->sockbuf + len + 1, buflen - len + 1);
else
strcpy(pool->sockbuf, "");
out:
if (!sret)
clear_sock(pool);
else if (opt_protocol)
applog(LOG_DEBUG, "RECVD: %s", sret);
return sret;
}
/* Extracts a string value from a json array with error checking. To be used
* when the value of the string returned is only examined and not to be stored.
* See json_array_string below */
static char *__json_array_string(json_t *val, unsigned int entry)
{
json_t *arr_entry;
if (json_is_null(val))
return NULL;
if (!json_is_array(val))
return NULL;
if (entry > json_array_size(val))
return NULL;
arr_entry = json_array_get(val, entry);
if (!json_is_string(arr_entry))
return NULL;
return (char *)json_string_value(arr_entry);
}
/* Creates a freshly malloced dup of __json_array_string */
static char *json_array_string(json_t *val, unsigned int entry)
{
char *buf = __json_array_string(val, entry);
if (buf)
return strdup(buf);
return NULL;
}
static bool parse_notify(struct pool *pool, json_t *val)
{
char *job_id, *prev_hash, *coinbase1, *coinbase2, *bbversion, *nbit, *ntime;
int merkles, i;
json_t *arr;
bool clean;
arr = json_array_get(val, 4);
if (!arr || !json_is_array(arr))
return false;
merkles = json_array_size(arr);
job_id = json_array_string(val, 0);
prev_hash = json_array_string(val, 1);
coinbase1 = json_array_string(val, 2);
coinbase2 = json_array_string(val, 3);
bbversion = json_array_string(val, 5);
nbit = json_array_string(val, 6);
ntime = json_array_string(val, 7);
clean = json_is_true(json_array_get(val, 8));
if (!job_id || !prev_hash || !coinbase1 || !coinbase2 || !bbversion || !nbit || !ntime) {
/* Annoying but we must not leak memory */
if (job_id)
free(job_id);
if (prev_hash)
free(prev_hash);
if (coinbase1)
free(coinbase1);
if (coinbase2)
free(coinbase2);
if (bbversion)
free(bbversion);
if (nbit)
free(nbit);
if (ntime)
free(ntime);
return false;
}
mutex_lock(&pool->pool_lock);
pool->swork.job_id = job_id;
pool->swork.prev_hash = prev_hash;
pool->swork.coinbase1 = coinbase1;
pool->swork.coinbase2 = coinbase2;
pool->swork.bbversion = bbversion;
pool->swork.nbit = nbit;
pool->swork.ntime = ntime;
pool->swork.clean = clean;
for (i = 0; i < pool->swork.merkles; i++)
free(pool->swork.merkle[i]);
if (merkles) {
pool->swork.merkle = realloc(pool->swork.merkle, sizeof(char *) * merkles + 1);
for (i = 0; i < merkles; i++)
pool->swork.merkle[i] = json_array_string(arr, i);
}
pool->swork.merkles = merkles;
if (clean)
pool->nonce2 = 0;
mutex_unlock(&pool->pool_lock);
if (opt_protocol) {
applog(LOG_DEBUG, "job_id: %s", job_id);
applog(LOG_DEBUG, "prev_hash: %s", prev_hash);
applog(LOG_DEBUG, "coinbase1: %s", coinbase1);
applog(LOG_DEBUG, "coinbase2: %s", coinbase2);
for (i = 0; i < merkles; i++)
applog(LOG_DEBUG, "merkle%d: %s", i, pool->swork.merkle[i]);
applog(LOG_DEBUG, "bbversion: %s", bbversion);
applog(LOG_DEBUG, "nbit: %s", nbit);
applog(LOG_DEBUG, "ntime: %s", ntime);
applog(LOG_DEBUG, "clean: %s", clean ? "yes" : "no");
}
/* A notify message is the closest stratum gets to a getwork */
pool->getwork_requested++;
total_getworks++;
return true;
}
static bool parse_diff(struct pool *pool, json_t *val)
{
int diff;
diff = json_integer_value(json_array_get(val, 0));
if (diff < 1)
return false;
mutex_lock(&pool->pool_lock);
pool->swork.diff = diff;
mutex_unlock(&pool->pool_lock);
applog(LOG_DEBUG, "Pool %d difficulty set to %d", pool->pool_no, diff);
return true;
}
static bool parse_reconnect(struct pool *pool, json_t *val)
{
char *url, *port, address[256];
memset(address, 0, 255);
url = (char *)json_string_value(json_array_get(val, 0));
if (!url)
url = pool->sockaddr_url;
port = (char *)json_string_value(json_array_get(val, 1));
if (!port)
port = pool->stratum_port;
sprintf(address, "%s:%s", url, port);
if (!extract_sockaddr(pool, address))
return false;
pool->stratum_url = pool->sockaddr_url;
applog(LOG_NOTICE, "Reconnect requested from pool %d to %s", pool->pool_no, address);
if (!initiate_stratum(pool) || !auth_stratum(pool))
return false;
return true;
}
static bool send_version(struct pool *pool, json_t *val)
{
char s[RBUFSIZE];
int id = json_integer_value(json_object_get(val, "id"));
if (!id)
return false;
sprintf(s, "{\"id\": %d, \"result\": \""PACKAGE"/"VERSION"\", \"error\": null}", id);
if (!stratum_send(pool, s, strlen(s)))
return false;
return true;
}
bool parse_method(struct pool *pool, char *s)
{
json_t *val = NULL, *method, *err_val, *params;
json_error_t err;
bool ret = false;
char *buf;
if (!s)
goto out;
val = JSON_LOADS(s, &err);
if (!val) {
applog(LOG_INFO, "JSON decode failed(%d): %s", err.line, err.text);
goto out;
}
method = json_object_get(val, "method");
if (!method)
goto out;
err_val = json_object_get(val, "error");
params = json_object_get(val, "params");
if (err_val && !json_is_null(err_val)) {
char *ss;
if (err_val)
ss = json_dumps(err_val, JSON_INDENT(3));
else
ss = strdup("(unknown reason)");
applog(LOG_INFO, "JSON-RPC method decode failed: %s", ss);
free(ss);
goto out;
}
buf = (char *)json_string_value(method);
if (!buf)
goto out;
if (!strncasecmp(buf, "mining.notify", 13) && parse_notify(pool, params)) {
ret = true;
goto out;
}
if (!strncasecmp(buf, "mining.set_difficulty", 21) && parse_diff(pool, params)) {
ret = true;
goto out;
}
if (!strncasecmp(buf, "client.reconnect", 16) && parse_reconnect(pool, params)) {
ret = true;
goto out;
}
if (!strncasecmp(buf, "client.get_version", 18) && send_version(pool, val)) {
ret = true;
goto out;
}
out:
if (val)
json_decref(val);
return ret;
}
bool auth_stratum(struct pool *pool)
{
json_t *val = NULL, *res_val, *err_val;
char s[RBUFSIZE], *sret = NULL;
json_error_t err;
bool ret = false;
sprintf(s, "{\"id\": %d, \"method\": \"mining.authorize\", \"params\": [\"%s\", \"%s\"]}",
swork_id++, pool->rpc_user, pool->rpc_pass);
/* Parse all data prior sending auth request */
while (sock_full(pool, false)) {
sret = recv_line(pool);
if (!parse_method(pool, sret)) {
clear_sock(pool);
applog(LOG_INFO, "Failed to parse stratum buffer");
free(sret);
return ret;
}
free(sret);
}
if (!stratum_send(pool, s, strlen(s)))
goto out;
sret = recv_line(pool);
if (!sret)
goto out;
val = JSON_LOADS(sret, &err);
free(sret);
res_val = json_object_get(val, "result");
err_val = json_object_get(val, "error");
if (!res_val || json_is_false(res_val) || (err_val && !json_is_null(err_val))) {
char *ss;
if (err_val)
ss = json_dumps(err_val, JSON_INDENT(3));
else
ss = strdup("(unknown reason)");
applog(LOG_WARNING, "JSON stratum auth failed: %s", ss);
free(ss);
goto out;
}
ret = true;
applog(LOG_INFO, "Stratum authorisation success for pool %d", pool->pool_no);
out:
if (val)
json_decref(val);
return ret;
}
bool initiate_stratum(struct pool *pool)
{
json_t *val = NULL, *res_val, *err_val;
char curl_err_str[CURL_ERROR_SIZE];
char s[RBUFSIZE], *sret = NULL;
CURL *curl = NULL;
json_error_t err;
bool ret = false;
if (!pool->stratum_curl) {
pool->stratum_curl = curl_easy_init();
if (unlikely(!pool->stratum_curl))
quit(1, "Failed to curl_easy_init in initiate_stratum");
}
curl = pool->stratum_curl;
/* Create a http url for use with curl */
memset(s, 0, RBUFSIZE);
sprintf(s, "http://%s:%s", pool->sockaddr_url, pool->stratum_port);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_URL, s);
curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY);
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);
}
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1);
if (curl_easy_perform(curl)) {
applog(LOG_INFO, "Stratum connect failed to pool %d: %s", pool->pool_no, curl_err_str);
goto out;
}
curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, (long *)&pool->sock);
sprintf(s, "{\"id\": %d, \"method\": \"mining.subscribe\", \"params\": []}", swork_id++);
if (!stratum_send(pool, s, strlen(s))) {
applog(LOG_DEBUG, "Failed to send s in initiate_stratum");
goto out;
}
if (!sock_full(pool, true)) {
applog(LOG_DEBUG, "Timed out waiting for response in initiate_stratum");
goto out;
}
sret = recv_line(pool);
if (!sret)
goto out;
val = JSON_LOADS(sret, &err);
free(sret);
if (!val) {
applog(LOG_INFO, "JSON decode failed(%d): %s", err.line, err.text);
goto out;
}
res_val = json_object_get(val, "result");
err_val = json_object_get(val, "error");
if (!res_val || json_is_null(res_val) ||
(err_val && !json_is_null(err_val))) {
char *ss;
if (err_val)
ss = json_dumps(err_val, JSON_INDENT(3));
else
ss = strdup("(unknown reason)");
applog(LOG_INFO, "JSON-RPC decode failed: %s", ss);
free(ss);
goto out;
}
pool->nonce1 = json_array_string(res_val, 1);
if (!pool->nonce1) {
applog(LOG_INFO, "Failed to get nonce1 in initiate_stratum");
goto out;
}
pool->n2size = json_integer_value(json_array_get(res_val, 2));
if (!pool->n2size) {
applog(LOG_INFO, "Failed to get n2size in initiate_stratum");
goto out;
}
ret = true;
out:
if (val)
json_decref(val);
if (ret) {
if (!pool->stratum_url)
pool->stratum_url = pool->sockaddr_url;
pool->stratum_active = true;
pool->swork.diff = 1;
if (opt_protocol) {
applog(LOG_DEBUG, "Pool %d confirmed mining.subscribe with extranonce1 %s extran2size %d",
pool->pool_no, pool->nonce1, pool->n2size);
}
} else {
pool->stratum_active = false;
if (curl) {
curl_easy_cleanup(curl);
pool->stratum_curl = NULL;
}
}
return ret;
}

53
util.h

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
#ifndef __UTIL_H__
#define __UTIL_H__
#if defined(unix) || defined(__APPLE__)
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SOCKETTYPE long
#define SOCKETFAIL(a) ((a) < 0)
#define INVSOCK -1
#define INVINETADDR -1
#define CLOSESOCKET close
#define SOCKERRMSG strerror(errno)
#elif defined WIN32
#include <ws2tcpip.h>
#include <winsock2.h>
#define SOCKETTYPE SOCKET
#define SOCKETFAIL(a) ((int)(a) == SOCKET_ERROR)
#define INVSOCK INVALID_SOCKET
#define INVINETADDR INADDR_NONE
#define CLOSESOCKET closesocket
extern char *WSAErrorMsg(void);
#define SOCKERRMSG WSAErrorMsg()
#ifndef SHUT_RDWR
#define SHUT_RDWR SD_BOTH
#endif
#ifndef in_addr_t
#define in_addr_t uint32_t
#endif
#endif
#if JANSSON_MAJOR_VERSION >= 2
#define JSON_LOADS(str, err_ptr) json_loads((str), 0, (err_ptr))
#else
#define JSON_LOADS(str, err_ptr) json_loads((str), (err_ptr))
#endif
struct pool;
bool stratum_send(struct pool *pool, char *s, ssize_t len);
char *recv_line(struct pool *pool);
bool parse_method(struct pool *pool, char *s);
bool extract_sockaddr(struct pool *pool, char *url);
bool auth_stratum(struct pool *pool);
bool initiate_stratum(struct pool *pool);
#endif /* __UTIL_H__ */
Loading…
Cancel
Save