Browse Source

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

Paul Sheppard 13 years ago
parent
commit
c5696629d7
18 changed files with 1711 additions and 327 deletions
  1. 1 1
      API-README
  2. 1 1
      Makefile.am
  3. 225 0
      NEWS
  4. 30 0
      README
  5. 80 112
      api.c
  6. 537 165
      cgminer.c
  7. 4 4
      configure.ac
  8. 6 1
      driver-bitforce.c
  9. 59 14
      driver-icarus.c
  10. 6 4
      driver-modminer.c
  11. 7 2
      driver-opencl.c
  12. 10 0
      findnonce.c
  13. 1 1
      fpgautils.c
  14. 2 0
      logging.c
  15. 87 4
      miner.h
  16. 4 4
      ocl.c
  17. 598 14
      util.c
  18. 53 0
      util.h

+ 1 - 1
API-README

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

+ 1 - 1
Makefile.am

@@ -32,7 +32,7 @@ cgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib @OPENCL_FLAGS@
 cgminer_SOURCES := cgminer.c
 cgminer_SOURCES := cgminer.c
 
 
 cgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
 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
 		   sha2.c sha2.h api.c
 
 
 cgminer_SOURCES	+= logging.c
 cgminer_SOURCES	+= logging.c

+ 225 - 0
NEWS

@@ -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
 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.
 - Display share difficulty on log with a shortened hash display on submission.
 - API stats add some pool getwork difficulty stats
 - API stats add some pool getwork difficulty stats
 - Ignore any pings pushed to the worker threads if the thread is still paused to
 - Ignore any pings pushed to the worker threads if the thread is still paused to

+ 30 - 0
README

@@ -146,6 +146,7 @@ Options for both config file and command line:
 --debug|-D          Enable debug output
 --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)
 --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
 --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")
 --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
 --load-balance      Change multipool strategy from failover to efficiency based balance
 --log|-l <arg>      Interval in seconds between log output (default: 5)
 --log|-l <arg>      Interval in seconds between log output (default: 5)
@@ -304,6 +305,10 @@ Single pool with a socks5 proxy, regular desktop:
 
 
 cgminer -o "socks5:proxy:port|http://pool:port" -u username -p password
 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:
 The list of proxy types are:
  http:    standard http 1.1 proxy
  http:    standard http 1.1 proxy
  http0:   http 1.0 proxy
  http0:   http 1.0 proxy
@@ -377,6 +382,17 @@ Thread 1: 60.2 Mh/s Enabled ALIVE
 Or press any other key to continue
 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
 Also many issues and FAQs are covered in the forum thread
 dedicated to this program,
 dedicated to this program,
@@ -908,6 +924,20 @@ To permanently give your account the 'dialout' group:
  sudo usermod -G dialout -a `whoami`
  sudo usermod -G dialout -a `whoami`
 Then logout and back in again
 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.
 
 
 ---
 ---
 
 

+ 80 - 112
api.c

@@ -25,122 +25,13 @@
 
 
 #include "compat.h"
 #include "compat.h"
 #include "miner.h"
 #include "miner.h"
+#include "util.h"
 #include "driver-cpu.h" /* for algo_names[], TODO: re-factor dependency */
 #include "driver-cpu.h" /* for algo_names[], TODO: re-factor dependency */
 
 
 #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) || defined(USE_MODMINER)
 #if defined(USE_BITFORCE) || defined(USE_ICARUS) || defined(USE_ZTEX) || defined(USE_MODMINER)
 #define HAVE_AN_FPGA 1
 #define HAVE_AN_FPGA 1
 #endif
 #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
-
-#ifdef WIN32
-	#include <ws2tcpip.h>
-	#include <winsock2.h>
-
-	#define SOCKETTYPE SOCKET
-	#define SOCKETFAIL(a) ((a) == SOCKET_ERROR)
-	#define INVSOCK INVALID_SOCKET
-	#define INVINETADDR INADDR_NONE
-	#define CLOSESOCKET closesocket
-
-	static char WSAbuf[1024];
-
-	struct WSAERRORS {
-		int id;
-		char *code;
-	} WSAErrors[] = {
-		{ 0,			"No error" },
-		{ WSAEINTR,		"Interrupted system call" },
-		{ WSAEBADF,		"Bad file number" },
-		{ WSAEACCES,		"Permission denied" },
-		{ WSAEFAULT,		"Bad address" },
-		{ WSAEINVAL,		"Invalid argument" },
-		{ WSAEMFILE,		"Too many open sockets" },
-		{ WSAEWOULDBLOCK,	"Operation would block" },
-		{ WSAEINPROGRESS,	"Operation now in progress" },
-		{ WSAEALREADY,		"Operation already in progress" },
-		{ WSAENOTSOCK,		"Socket operation on non-socket" },
-		{ WSAEDESTADDRREQ,	"Destination address required" },
-		{ WSAEMSGSIZE,		"Message too long" },
-		{ WSAEPROTOTYPE,	"Protocol wrong type for socket" },
-		{ WSAENOPROTOOPT,	"Bad protocol option" },
-		{ WSAEPROTONOSUPPORT,	"Protocol not supported" },
-		{ WSAESOCKTNOSUPPORT,	"Socket type not supported" },
-		{ WSAEOPNOTSUPP,	"Operation not supported on socket" },
-		{ WSAEPFNOSUPPORT,	"Protocol family not supported" },
-		{ WSAEAFNOSUPPORT,	"Address family not supported" },
-		{ WSAEADDRINUSE,	"Address already in use" },
-		{ WSAEADDRNOTAVAIL,	"Can't assign requested address" },
-		{ WSAENETDOWN,		"Network is down" },
-		{ WSAENETUNREACH,	"Network is unreachable" },
-		{ WSAENETRESET,		"Net connection reset" },
-		{ WSAECONNABORTED,	"Software caused connection abort" },
-		{ WSAECONNRESET,	"Connection reset by peer" },
-		{ WSAENOBUFS,		"No buffer space available" },
-		{ WSAEISCONN,		"Socket is already connected" },
-		{ WSAENOTCONN,		"Socket is not connected" },
-		{ WSAESHUTDOWN,		"Can't send after socket shutdown" },
-		{ WSAETOOMANYREFS,	"Too many references, can't splice" },
-		{ WSAETIMEDOUT,		"Connection timed out" },
-		{ WSAECONNREFUSED,	"Connection refused" },
-		{ WSAELOOP,		"Too many levels of symbolic links" },
-		{ WSAENAMETOOLONG,	"File name too long" },
-		{ WSAEHOSTDOWN,		"Host is down" },
-		{ WSAEHOSTUNREACH,	"No route to host" },
-		{ WSAENOTEMPTY,		"Directory not empty" },
-		{ WSAEPROCLIM,		"Too many processes" },
-		{ WSAEUSERS,		"Too many users" },
-		{ WSAEDQUOT,		"Disc quota exceeded" },
-		{ WSAESTALE,		"Stale NFS file handle" },
-		{ WSAEREMOTE,		"Too many levels of remote in path" },
-		{ WSASYSNOTREADY,	"Network system is unavailable" },
-		{ WSAVERNOTSUPPORTED,	"Winsock version out of range" },
-		{ WSANOTINITIALISED,	"WSAStartup not yet called" },
-		{ WSAEDISCON,		"Graceful shutdown in progress" },
-		{ WSAHOST_NOT_FOUND,	"Host not found" },
-		{ WSANO_DATA,		"No host data of that type was found" },
-		{ -1,			"Unknown error code" }
-	};
-
-	static char *WSAErrorMsg()
-	{
-		int i;
-		int id = WSAGetLastError();
-
-		/* Assume none of them are actually -1 */
-		for (i = 0; WSAErrors[i].id != -1; i++)
-			if (WSAErrors[i].id == id)
-				break;
-
-		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
 // Big enough for largest API request
 //  though a PC with 100s of PGAs/CPUs may exceed the size ...
 //  though a PC with 100s of PGAs/CPUs may exceed the size ...
 // Current code assumes it can socket send this size also
 // Current code assumes it can socket send this size also
@@ -153,6 +44,80 @@
 // However lots of PGA's may mean more
 // However lots of PGA's may mean more
 #define QUEUE	100
 #define QUEUE	100
 
 
+#if defined WIN32
+static char WSAbuf[1024];
+
+struct WSAERRORS {
+	int id;
+	char *code;
+} WSAErrors[] = {
+	{ 0,			"No error" },
+	{ WSAEINTR,		"Interrupted system call" },
+	{ WSAEBADF,		"Bad file number" },
+	{ WSAEACCES,		"Permission denied" },
+	{ WSAEFAULT,		"Bad address" },
+	{ WSAEINVAL,		"Invalid argument" },
+	{ WSAEMFILE,		"Too many open sockets" },
+	{ WSAEWOULDBLOCK,	"Operation would block" },
+	{ WSAEINPROGRESS,	"Operation now in progress" },
+	{ WSAEALREADY,		"Operation already in progress" },
+	{ WSAENOTSOCK,		"Socket operation on non-socket" },
+	{ WSAEDESTADDRREQ,	"Destination address required" },
+	{ WSAEMSGSIZE,		"Message too long" },
+	{ WSAEPROTOTYPE,	"Protocol wrong type for socket" },
+	{ WSAENOPROTOOPT,	"Bad protocol option" },
+	{ WSAEPROTONOSUPPORT,	"Protocol not supported" },
+	{ WSAESOCKTNOSUPPORT,	"Socket type not supported" },
+	{ WSAEOPNOTSUPP,	"Operation not supported on socket" },
+	{ WSAEPFNOSUPPORT,	"Protocol family not supported" },
+	{ WSAEAFNOSUPPORT,	"Address family not supported" },
+	{ WSAEADDRINUSE,	"Address already in use" },
+	{ WSAEADDRNOTAVAIL,	"Can't assign requested address" },
+	{ WSAENETDOWN,		"Network is down" },
+	{ WSAENETUNREACH,	"Network is unreachable" },
+	{ WSAENETRESET,		"Net connection reset" },
+	{ WSAECONNABORTED,	"Software caused connection abort" },
+	{ WSAECONNRESET,	"Connection reset by peer" },
+	{ WSAENOBUFS,		"No buffer space available" },
+	{ WSAEISCONN,		"Socket is already connected" },
+	{ WSAENOTCONN,		"Socket is not connected" },
+	{ WSAESHUTDOWN,		"Can't send after socket shutdown" },
+	{ WSAETOOMANYREFS,	"Too many references, can't splice" },
+	{ WSAETIMEDOUT,		"Connection timed out" },
+	{ WSAECONNREFUSED,	"Connection refused" },
+	{ WSAELOOP,		"Too many levels of symbolic links" },
+	{ WSAENAMETOOLONG,	"File name too long" },
+	{ WSAEHOSTDOWN,		"Host is down" },
+	{ WSAEHOSTUNREACH,	"No route to host" },
+	{ WSAENOTEMPTY,		"Directory not empty" },
+	{ WSAEPROCLIM,		"Too many processes" },
+	{ WSAEUSERS,		"Too many users" },
+	{ WSAEDQUOT,		"Disc quota exceeded" },
+	{ WSAESTALE,		"Stale NFS file handle" },
+	{ WSAEREMOTE,		"Too many levels of remote in path" },
+	{ WSASYSNOTREADY,	"Network system is unavailable" },
+	{ WSAVERNOTSUPPORTED,	"Winsock version out of range" },
+	{ WSANOTINITIALISED,	"WSAStartup not yet called" },
+	{ WSAEDISCON,		"Graceful shutdown in progress" },
+	{ WSAHOST_NOT_FOUND,	"Host not found" },
+	{ WSANO_DATA,		"No host data of that type was found" },
+	{ -1,			"Unknown error code" }
+};
+
+char *WSAErrorMsg(void) {
+	int i;
+	int id = WSAGetLastError();
+
+	/* Assume none of them are actually -1 */
+	for (i = 0; WSAErrors[i].id != -1; i++)
+		if (WSAErrors[i].id == id)
+			break;
+
+	sprintf(WSAbuf, "Socket Error: (%d) %s", id, WSAErrors[i].code);
+
+	return &(WSAbuf[0]);
+}
+#endif
 static char *io_buffer = NULL;
 static char *io_buffer = NULL;
 static char *msg_buffer = NULL;
 static char *msg_buffer = NULL;
 static SOCKETTYPE sock = INVSOCK;
 static SOCKETTYPE sock = INVSOCK;
@@ -558,7 +523,7 @@ struct CODES {
  { SEVERITY_ERR,   MSG_INVBOOL,	PARAM_NONE,	"Invalid parameter should be true or false" },
  { 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_FOO,	PARAM_BOOL,	"Failover-Only set to %s" },
  { SEVERITY_SUCC,  MSG_MINECOIN,PARAM_NONE,	"CGMiner coin" },
  { 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
 #ifdef HAVE_AN_FPGA
  { SEVERITY_SUCC,  MSG_PGAIDENT,PARAM_PGA,	"Identify command sent to PGA%d" },
  { SEVERITY_SUCC,  MSG_PGAIDENT,PARAM_PGA,	"Identify command sent to PGA%d" },
  { SEVERITY_WARN,  MSG_PGANOID,	PARAM_PGA,	"PGA%d does not support identify" },
  { SEVERITY_WARN,  MSG_PGANOID,	PARAM_PGA,	"PGA%d does not support identify" },
@@ -2210,6 +2175,7 @@ exitsama:
 static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
 {
 {
 	char *url, *user, *pass;
 	char *url, *user, *pass;
+	struct pool *pool;
 	char *ptr;
 	char *ptr;
 
 
 	if (param == NULL || *param == '\0') {
 	if (param == NULL || *param == '\0') {
@@ -2226,7 +2192,9 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __may
 		return;
 		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);
 	ptr = escape_string(url, isjson);
 	strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson));
 	strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson));

File diff suppressed because it is too large
+ 537 - 165
cgminer.c


+ 4 - 4
configure.ac

@@ -1,8 +1,8 @@
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 ##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
 m4_define([v_maj], [2])
 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([v_ver], [v_maj.v_min.v_mic])
 m4_define([lt_rev], m4_eval(v_maj + v_min))
 m4_define([lt_rev], m4_eval(v_maj + v_min))
@@ -92,6 +92,7 @@ case $target in
     PTHREAD_FLAGS=""
     PTHREAD_FLAGS=""
     DLOPEN_FLAGS=""
     DLOPEN_FLAGS=""
     WS2_LIBS="-lws2_32"
     WS2_LIBS="-lws2_32"
+    AC_DEFINE([_WIN32_WINNT], [0x0501], "WinNT version for XP+ support")
     ;;
     ;;
   powerpc-*-darwin*)
   powerpc-*-darwin*)
     CFLAGS="$CFLAGS -faltivec"
     CFLAGS="$CFLAGS -faltivec"
@@ -351,8 +352,7 @@ fi
 
 
 PKG_PROG_PKG_CONFIG()
 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)
 AC_SUBST(LIBCURL_LIBS)
 
 
 dnl CCAN wants to know a lot of vars.
 dnl CCAN wants to know a lot of vars.

+ 6 - 1
driver-bitforce.c

@@ -154,7 +154,7 @@ static int bitforce_autodetect_ftdi(void)
 	char **bufptrs;
 	char **bufptrs;
 	char *buf;
 	char *buf;
 	int found = 0;
 	int found = 0;
-	int i;
+	DWORD i;
 
 
 	FT_STATUS ftStatus;
 	FT_STATUS ftStatus;
 	DWORD numDevs;
 	DWORD numDevs;
@@ -410,6 +410,11 @@ static bool bitforce_get_temp(struct cgpu_info *bitforce)
 	if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) {
 	if ((!strncasecmp(pdevbuf, "TEMP", 4)) && (s = strchr(pdevbuf + 4, ':'))) {
 		float temp = strtof(s + 1, NULL);
 		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) {
 		if (temp > 0) {
 			bitforce->temp = temp;
 			bitforce->temp = temp;
 			if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) {
 			if (unlikely(bitforce->cutofftemp > 0 && temp > bitforce->cutofftemp)) {

+ 59 - 14
driver-icarus.c

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

+ 6 - 4
driver-modminer.c

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

+ 7 - 2
driver-opencl.c

@@ -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);
 		gpu_us = us_tdiff(&tv_gpuend, &gpu->tv_gpumid);
 		if (gpu_us > 0 && ++gpu->hit > 4) {
 		if (gpu_us > 0 && ++gpu->hit > 4) {
 			gpu_us = us_tdiff(&tv_gpuend, &gpu->tv_gpustart) / gpu->intervals;
 			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
 			/* Try to not let the GPU be out for longer than
 			 * opt_dynamic_interval in ms, but increase
 			 * opt_dynamic_interval in ms, but increase
 			 * intensity when the system is idle in dynamic mode */
 			 * 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)
 				if (gpu->intensity > MIN_INTENSITY)
 					--gpu->intensity;
 					--gpu->intensity;
-			} else if (gpu_us < dynamic_us / 2) {
+			} else if (gpu->gpu_us_average < dynamic_us / 2) {	
 				if (gpu->intensity < MAX_INTENSITY)
 				if (gpu->intensity < MAX_INTENSITY)
 					++gpu->intensity;
 					++gpu->intensity;
 			}
 			}

+ 10 - 0
findnonce.c

@@ -252,6 +252,16 @@ static void *postcalc_hash(void *userdata)
 
 
 	pthread_detach(pthread_self());
 	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++) {
 	for (entry = 0; entry < pcd->res[FOUND]; entry++) {
 		uint32_t nonce = pcd->res[entry];
 		uint32_t nonce = pcd->res[entry];
 
 

+ 1 - 1
fpgautils.c

@@ -74,7 +74,7 @@ int serial_autodetect_udev(__maybe_unused detectone_func_t detectone, __maybe_un
 }
 }
 #endif
 #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
 #ifndef WIN32
 	DIR *D;
 	DIR *D;

+ 2 - 0
logging.c

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

+ 87 - 4
miner.h

@@ -12,6 +12,7 @@
 #include "elist.h"
 #include "elist.h"
 #include "uthash.h"
 #include "uthash.h"
 #include "logging.h"
 #include "logging.h"
+#include "util.h"
 
 
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
 #ifdef __APPLE_CC__
 #ifdef __APPLE_CC__
@@ -51,6 +52,19 @@ void *alloca (size_t);
 # endif
 # endif
 #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)
 #if defined (__linux)
  #ifndef LINUX
  #ifndef LINUX
   #define LINUX
   #define LINUX
@@ -125,14 +139,19 @@ void *alloca (size_t);
 #endif
 #endif
 #endif /* !defined(__GLXBYTEORDER_H__) */
 #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
 #ifndef htobe32
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 # if __BYTE_ORDER == __LITTLE_ENDIAN
 #  define be32toh(x) bswap_32(x)
 #  define be32toh(x) bswap_32(x)
+#  define be64toh(x) bswap_64(x)
 #  define htobe32(x) bswap_32(x)
 #  define htobe32(x) bswap_32(x)
+#  define htobe64(x) bswap_64(x)
 # elif __BYTE_ORDER == __BIG_ENDIAN
 # elif __BYTE_ORDER == __BIG_ENDIAN
 #  define be32toh(x) (x)
 #  define be32toh(x) (x)
+#  define be64toh(x) (x)
 #  define htobe32(x) (x)
 #  define htobe32(x) (x)
+#  define htobe64(x) (x)
 #else
 #else
 #error UNKNOWN BYTE ORDER
 #error UNKNOWN BYTE ORDER
 #endif
 #endif
@@ -378,11 +397,12 @@ struct cgpu_info {
 
 
 #ifdef USE_SCRYPT
 #ifdef USE_SCRYPT
 	int opt_lg, lookup_gap;
 	int opt_lg, lookup_gap;
-	int opt_tc, thread_concurrency;
-	int shaders;
+	size_t opt_tc, thread_concurrency;
+	size_t shaders;
 #endif
 #endif
 	struct timeval tv_gpustart;
 	struct timeval tv_gpustart;
 	struct timeval tv_gpumid;
 	struct timeval tv_gpumid;
+	double gpu_us_average;
 	int intervals, hit;
 	int intervals, hit;
 #endif
 #endif
 
 
@@ -510,6 +530,21 @@ static inline void swap256(void *dest_p, const void *src_p)
 	dest[7] = src[0];
 	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, ...);
 extern void quit(int status, const char *format, ...);
 
 
 static inline void mutex_lock(pthread_mutex_t *lock)
 static inline void mutex_lock(pthread_mutex_t *lock)
@@ -594,6 +629,7 @@ extern bool opt_worktime;
 #ifdef USE_BITFORCE
 #ifdef USE_BITFORCE
 extern bool opt_bfl_noncerange;
 extern bool opt_bfl_noncerange;
 #endif
 #endif
+extern int swork_id;
 
 
 extern pthread_rwlock_t netacc_lock;
 extern pthread_rwlock_t netacc_lock;
 
 
@@ -645,7 +681,9 @@ extern void api(int thr_id);
 
 
 extern struct pool *current_pool(void);
 extern struct pool *current_pool(void);
 extern int enabled_pools;
 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
 #define MAX_GPUDEVICES 16
 
 
@@ -748,6 +786,24 @@ enum pool_enable {
 	POOL_REJECTING,
 	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 {
 struct pool {
 	int pool_no;
 	int pool_no;
 	int prio;
 	int prio;
@@ -810,12 +866,31 @@ struct pool {
 
 
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_stats cgminer_stats;
 	struct cgminer_pool_stats cgminer_pool_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_TESTPOOL 'T'
 #define GETWORK_MODE_POOL 'P'
 #define GETWORK_MODE_POOL 'P'
 #define GETWORK_MODE_LP 'L'
 #define GETWORK_MODE_LP 'L'
 #define GETWORK_MODE_BENCHMARK 'B'
 #define GETWORK_MODE_BENCHMARK 'B'
+#define GETWORK_MODE_STRATUM 'S'
 
 
 struct work {
 struct work {
 	unsigned char	data[128];
 	unsigned char	data[128];
@@ -845,6 +920,14 @@ struct work {
 	bool		block;
 	bool		block;
 	bool		queued;
 	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;
 	unsigned int	work_block;
 	int		id;
 	int		id;
 	UT_hash_handle	hh;
 	UT_hash_handle	hh;

+ 4 - 4
ocl.c

@@ -541,7 +541,7 @@ _clState *initCl(unsigned int gpu, char *name, size_t nameSize)
 		strcat(binaryfilename, "g");
 		strcat(binaryfilename, "g");
 	if (opt_scrypt) {
 	if (opt_scrypt) {
 #ifdef USE_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);
 		strcat(binaryfilename, numbuf);
 #endif
 #endif
 	} else {
 	} else {
@@ -614,7 +614,7 @@ build:
 #ifdef USE_SCRYPT
 #ifdef USE_SCRYPT
 	if (opt_scrypt)
 	if (opt_scrypt)
 		sprintf(CompilerOptions, "-D LOOKUP_GAP=%d -D CONCURRENT_THREADS=%d -D WORKSIZE=%d",
 		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
 	else
 #endif
 #endif
 	{
 	{
@@ -810,8 +810,8 @@ built:
 		/* Use the max alloc value which has been rounded to a power of
 		/* Use the max alloc value which has been rounded to a power of
 		 * 2 greater >= required amount earlier */
 		 * 2 greater >= required amount earlier */
 		if (bufsize > cgpu->max_alloc) {
 		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
 		} else
 			bufsize = cgpu->max_alloc;
 			bufsize = cgpu->max_alloc;
 		applog(LOG_DEBUG, "Creating scrypt buffer sized %d", bufsize);
 		applog(LOG_DEBUG, "Creating scrypt buffer sized %d", bufsize);

+ 598 - 14
util.c

@@ -26,20 +26,17 @@
 # include <sys/socket.h>
 # include <sys/socket.h>
 # include <netinet/in.h>
 # include <netinet/in.h>
 # include <netinet/tcp.h>
 # include <netinet/tcp.h>
+# include <netdb.h>
 #else
 #else
 # include <winsock2.h>
 # include <winsock2.h>
 # include <mstcpip.h>
 # include <mstcpip.h>
+# include <ws2tcpip.h>
 #endif
 #endif
 
 
 #include "miner.h"
 #include "miner.h"
 #include "elist.h"
 #include "elist.h"
 #include "compat.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;
 bool successful_connect = false;
 struct timeval nettime;
 struct timeval nettime;
@@ -58,6 +55,7 @@ struct header_info {
 	char		*lp_path;
 	char		*lp_path;
 	int		rolltime;
 	int		rolltime;
 	char		*reason;
 	char		*reason;
+	char		*stratum_url;
 	bool		hadrolltime;
 	bool		hadrolltime;
 	bool		canroll;
 	bool		canroll;
 	bool		hadexpire;
 	bool		hadexpire;
@@ -187,13 +185,17 @@ static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
 		val = NULL;
 		val = NULL;
 	}
 	}
 
 
+	if (!strcasecmp("X-Stratum", key)) {
+		hi->stratum_url = val;
+		val = NULL;
+	}
+
 out:
 out:
 	free(key);
 	free(key);
 	free(val);
 	free(val);
 	return ptrlen;
 	return ptrlen;
 }
 }
 
 
-#ifdef CURL_HAS_SOCKOPT
 int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
 int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
 			     curlsocktype __maybe_unused purpose)
 			     curlsocktype __maybe_unused purpose)
 {
 {
@@ -241,7 +243,6 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
 
 
 	return 0;
 	return 0;
 }
 }
-#endif
 
 
 static void last_nettime(struct timeval *last)
 static void last_nettime(struct timeval *last)
 {
 {
@@ -265,7 +266,7 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 {
 {
 	long timeout = longpoll ? (60 * 60) : 60;
 	long timeout = longpoll ? (60 * 60) : 60;
 	struct data_buffer all_data = {NULL, 0};
 	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 len_hdr[64], user_agent_hdr[128];
 	char curl_err_str[CURL_ERROR_SIZE];
 	char curl_err_str[CURL_ERROR_SIZE];
 	struct curl_slist *headers = NULL;
 	struct curl_slist *headers = NULL;
@@ -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_USERPWD, userpass);
 		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 		curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
 	}
 	}
-#ifdef CURL_HAS_SOCKOPT
 	if (longpoll)
 	if (longpoll)
 		curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_sockopt_cb);
 		curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_sockopt_cb);
-#endif
 	curl_easy_setopt(curl, CURLOPT_POST, 1);
 	curl_easy_setopt(curl, CURLOPT_POST, 1);
 
 
 	if (opt_protocol)
 	if (opt_protocol)
@@ -392,9 +391,19 @@ json_t *json_rpc_call(CURL *curl, const char *url,
 			pool->hdr_path = hi.lp_path;
 			pool->hdr_path = hi.lp_path;
 		} else
 		} else
 			pool->hdr_path = NULL;
 			pool->hdr_path = NULL;
-	} else if (hi.lp_path) {
-		free(hi.lp_path);
-		hi.lp_path = NULL;
+		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;
 	*rolltime = hi.rolltime;
@@ -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;
 	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 - 0
util.h

@@ -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__ */

Some files were not shown because too many files changed in this diff