Browse Source

Merge branch 'stratum' into bfgminer

Conflicts:
	NEWS
	driver-opencl.c
	miner.c
	util.c
Luke Dashjr 13 years ago
parent
commit
e5a326bb89
24 changed files with 1804 additions and 481 deletions
  1. 7 0
      API-README
  2. 74 2
      FPGA-README
  3. 1 2
      Makefile.am
  4. 241 0
      NEWS
  5. 46 4
      README
  6. 89 112
      api.c
  7. 8 19
      configure.ac
  8. 1 6
      diablo121016.cl
  9. 1 6
      diakgcn121016.cl
  10. 14 19
      driver-icarus.c
  11. 2 1
      driver-modminer.c
  12. 20 68
      driver-opencl.c
  13. 11 10
      findnonce.c
  14. 1 1
      fpgautils.c
  15. 2 0
      logging.c
  16. 465 186
      miner.c
  17. 94 8
      miner.h
  18. 1 6
      phatk121016.cl
  19. 1 6
      poclbm121016.cl
  20. 12 0
      scrypt.c
  21. 8 0
      scrypt.h
  22. 1 6
      scrypt121016.cl
  23. 651 19
      util.c
  24. 53 0
      util.h

+ 7 - 0
API-README

@@ -384,6 +384,13 @@ 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.20
+
+Modified API commands:
+ 'pools' - add 'Has Stratum', 'Stratum Active', 'Stratum URL'
+
+----------
+
 API V1.19b (BFGMiner v2.8.1)
 API V1.19b (BFGMiner v2.8.1)
 
 
 Added API commands:
 Added API commands:

+ 74 - 2
FPGA-README

@@ -2,7 +2,78 @@
 This README contains extended details about FPGA mining with cgminer
 This README contains extended details about FPGA mining with cgminer
 
 
 
 
-Bitforce
+ModMinerQuad (MMQ)
+------------------
+
+The mining bitstream does not survive a power cycle, so cgminer will upload
+it, if it needs to, before it starts mining
+
+-
+
+You must make sure you have an approriate firmware in your MMQ
+Read here for official details of changing the firmware:
+ http://wiki.btcfpga.com/index.php?title=Firmware
+
+The basics of changing the firmware are:
+ You need two short pieces of conductive wire if your MMQ doesn't have
+ buttons on the "RESET" and "ISP" pads on the backplane board
+ Cutting a small (metal) paper-clip in half works well for this
+
+ Join the 2 left pads of the "RESET" pad with wire and the led will dim
+ Without disconnecting the "RESET", join the 2 left pads of the "ISP" pad
+ with a wire and it will stay dim
+ Release "RESET" then release "ISP" and is should still be dim
+ Unplug the USB and when you plug it back in it will show up as a mass
+ storage device
+  Linux: (as one single line):
+   mcopy -i /dev/disk/by-id/usb-NXP_LPC134X_IFLASH_ISP000000000-0:0
+      modminer091012.bin ::/firmware.bin
+  Windows: delete the MSD device file firmware.bin and copy in the new one
+   rename the new file and put it under the same name 'firmware.bin'
+ Disconnect the USB correctly (so writes are flushed first)
+ Join and then disconnect "RESET" and then plug the USB back in and it's done
+
+Best to update to one of the latest 2 listed below if you don't already
+have one of them in your MMQ
+
+The current latest different firmware are:
+
+ Latest for support of normal or TLM bitstream:
+  http://btcfpga.com/files/firmware/modminer092612-TLM.bin
+
+ Latest with only normal bitstream support (Temps/HW Fix):
+  http://btcfpga.com/files/firmware/modminer091012.bin
+
+The code is currently tested on the modminer091012.bin firmware.
+This comment will be updated when others have been tested
+
+-
+
+On many linux distributions there is an app called modem-manager that
+may cause problems when it is enabled, due to opening the MMQ device
+and writing to it
+
+The problem will typically present itself by the flashing led on the
+backplane going out (no longer flashing) and it takes a power cycle to
+re-enable the MMQ firmware - which then can lead to the problem happening
+again
+
+You can either disable/uninstall modem-manager if you don't need it or:
+a (hack) solution to this is to blacklist the MMQ USB device in
+/lib/udev/rules.d/77-mm-usb-device-blacklist.rules
+
+Adding 2 lines like this (just above APC) should help
+# MMQ
+ATTRS{idVendor}=="ifc9", ATTRS{idProduct}=="0003", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+The change will be lost and need to be re-done, next time you update the
+modem-manager software
+
+TODO: check that all MMQ's have the same product ID
+
+
+Bitforce (BFL)
+--------------
 
 
 --bfl-range         Use nonce range on bitforce devices if supported
 --bfl-range         Use nonce range on bitforce devices if supported
 
 
@@ -37,7 +108,8 @@ the MH/s value reported with the changed firmware - and the MH/s reported
 will be less than the firmware speed since you lose work on every block change.
 will be less than the firmware speed since you lose work on every block change.
 
 
 
 
-Icarus
+Icarus (ICA)
+------------
 
 
 There are two hidden options in cgminer when Icarus support is compiled in:
 There are two hidden options in cgminer when Icarus support is compiled in:
 
 

+ 1 - 2
Makefile.am

@@ -17,7 +17,6 @@ bin_SCRIPTS	= *.cl
 bfgminer_LDFLAGS	= $(PTHREAD_FLAGS)
 bfgminer_LDFLAGS	= $(PTHREAD_FLAGS)
 bfgminer_LDADD	= $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \
 bfgminer_LDADD	= $(DLOPEN_FLAGS) @LIBCURL_LIBS@ @JANSSON_LIBS@ @PTHREAD_LIBS@ \
 		  @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \
 		  @NCURSES_LIBS@ @PDCURSES_LIBS@ @WS2_LIBS@ \
-		  @TIMER_LIBS@ \
 		  @UDEV_LIBS@ @USB_LIBS@ \
 		  @UDEV_LIBS@ @USB_LIBS@ \
 		  @MATH_LIBS@ lib/libgnu.a ccan/libccan.a
 		  @MATH_LIBS@ lib/libgnu.a ccan/libccan.a
 bfgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
 bfgminer_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
@@ -28,7 +27,7 @@ bfgminer_CPPFLAGS += $(NCURSES_CPPFLAGS)
 bfgminer_SOURCES := miner.c
 bfgminer_SOURCES := miner.c
 
 
 bfgminer_SOURCES	+= elist.h miner.h compat.h bench_block.h	\
 bfgminer_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
 bfgminer_DEPENDENCIES =
 bfgminer_DEPENDENCIES =
 
 

+ 241 - 0
NEWS

@@ -38,6 +38,247 @@ BFGMiner Version 2.9.0 - Future
 - ft232r: Complete necessary interfaces for X6500
 - ft232r: Complete necessary interfaces for X6500
 - x6500: Bare minimum detection-only X6500 support via libusb
 - x6500: Bare minimum detection-only X6500 support via libusb
 - Minor debian packaging fixes.
 - Minor debian packaging fixes.
+- No longer should hide --no-restart option if OpenCL support is missing
+- Handle crash exceptions by trying to restart cgminer unless the --no-restart
+option is used.
+- Switch queued count when choosing a different pool from a failed stratum pool
+in getwork thread.
+- Put a mandatory 5s wait between reattempting a getwork on failure to avoid
+hammering requests.
+- Make sure to check pool stratum curl exists under lock before attempting any
+recv to not risk dereferencing upon attempting to reinitiate stratum.
+- Avoid redefining macros and align to 4 byte boundaries.
+- API - add Stratum information to pools
+- update FPGA-README for MMQ
+- Time for dynamic is in microseconds, not ms.
+- x86_64 builds of mingw32 are not supported directly and should just configure
+as generic mingw32 builds since they're NOT 64 bit.
+- Use 3 significant digits when suffix string is used and values are >1000.
+- Get rid of unused warning for !scrypt.
+- Use select on stratum send to make sure the socket is writeable.
+- Cope with dval being zero in suffix_string and display a single decimal place
+when significant digits is not specified but the value is greater than 1000.
+- Pad out the suffix string function with zeroes on the right.
+- Failure to calloc in bin2hex is a fatal failure always so just check for that
+failure within the function and abort, simplifying the rest of the code.
+- Provide locking around the change of the stratum curl structures to avoid
+possible races.
+- Bump opencl kernel version numbers.
+- Remove atomic ops from opencl kernels given rarity of more than once nonce on
+the same wavefront and the potential increased ramspeed requirements to use the
+atomics.
+- Clear the pool idle flag in stratum when it comes back to life.
+- Display correct share hash and share difficulty with scrypt mining.
+- Use explicit host to BE functions in scrypt code instead of hard coding
+byteswap everywhere.
+- Show work target diff for scrypt mining.
+- Ease the checking on allocation of padbuffer8 in the hope it works partially
+anyway on an apparently failed call.
+- Watch for buffer overflows on receiving data into the socket buffer.
+- Round target difficulties down to be in keeping with the rounding of detected
+share difficulties.
+- Dramatically simplify the dynamic intensity calculation by oversampling many
+runs through the opencl kernel till we're likely well within the timer
+resolution on windows.
+- String alignment to 4 byte boundaries and optimisations for bin<->hex
+conversions.
+- In opencl_free_work, make sure to still flush results in dynamic mode.
+- Align static arrays to 4 byte boundaries to appease ARM builds for stratum.
+- Update documentation.
+- Left align values that are suffix_string generated.
+- Share_diff should not be converting the work data to hex.
+- Update readme describing difficulty displayed on log lines.
+- Off by one error.
+- Prevent overflows of the port char array in extract_sockaddr.
+- Disable stratum detection with scrypt.
+- 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.
+- Supplement other 64-bit endian swap macros
+- Bugfix: Fix htobe64 on big endian platforms that don't define it
+- Fix lack of htobe64 on mingw32.
+- 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.
+- Update to cgminer's newer dynamic intensity algorithm
+- Support for the stratum mining protocol.
+- 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.
+- Format Stratum submission-start debug the same way as other submissions
+- Bugfix: Set work_restart_id in gen_stratum_work for when work is reused to
+avoid thinking it's all stale.
+- Only auto-switch to Stratum internally, but save HTTP URI in case pool stops
+using Stratum; also always shows original pool URI on RPC
+- SHUT_RDWR is now always defined for us, so no need to check ifdef on LP hang
+- Implement --no-stratum option to disable autodetection
+- Show Stratum pools as "Strtm" protocol in "Pool management" TUI
+- Bugfix: BFGMiner doesn't use rpc_proxytype
+- Remove free that could segfault.
+- 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.
+- 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 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.
+- 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.
+- Remove unused add_pool_details5
+- 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.
 
 
 
 
 BFGMiner Version 2.8.3 - October 18, 2012
 BFGMiner Version 2.8.3 - October 18, 2012

+ 46 - 4
README

@@ -136,6 +136,7 @@ Options for both config file and command line:
 --benchmark         Run BFGMiner in benchmark mode - produces no shares
 --benchmark         Run BFGMiner in benchmark mode - produces no shares
 --coinbase-addr <arg> Set coinbase payout address for solo mining
 --coinbase-addr <arg> Set coinbase payout address for solo mining
 --coinbase-sig <arg> Set coinbase signature when possible
 --coinbase-sig <arg> Set coinbase signature when possible
+--compact           Use compact display without per device statistics
 --debug|-D          Enable debug output
 --debug|-D          Enable debug output
 --debuglog          Enable debug logging
 --debuglog          Enable debug logging
 --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)
@@ -148,6 +149,8 @@ Options for both config file and command line:
 --no-gbt            Disable getblocktemplate support
 --no-gbt            Disable getblocktemplate support
 --no-longpoll       Disable X-Long-Polling support
 --no-longpoll       Disable X-Long-Polling support
 --no-pool-disable   Do not automatically disable pools that continually reject shares
 --no-pool-disable   Do not automatically disable pools that continually reject shares
+--no-restart        Do not attempt to restart devices that hang or BFGMiner if it crashes
+--no-stratum        Disable Stratum detection
 --no-submit-stale   Don't submit shares if they are detected as stale
 --no-submit-stale   Don't submit shares if they are detected as stale
 --pass|-p <arg>     Password for bitcoin JSON-RPC server
 --pass|-p <arg>     Password for bitcoin JSON-RPC server
 --per-device-stats  Force verbose mode and output per-device statistics
 --per-device-stats  Force verbose mode and output per-device statistics
@@ -205,7 +208,6 @@ GPU only options:
 --kernel|-k <arg>   Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated
 --kernel|-k <arg>   Override kernel to use (diablo, poclbm, phatk or diakgcn) - one value or comma separated
 --ndevs|-n          Enumerate number of detected GPUs and exit
 --ndevs|-n          Enumerate number of detected GPUs and exit
 --no-adl            Disable the ATI display library used for monitoring and setting GPU parameters
 --no-adl            Disable the ATI display library used for monitoring and setting GPU parameters
---no-restart        Do not attempt to restart GPUs that hang
 --temp-overheat <arg> Overheat temperature when automatically managing fan and GPU speeds (default: 85)
 --temp-overheat <arg> Overheat temperature when automatically managing fan and GPU speeds (default: 85)
 --vectors|-v <arg>  Override detected optimal vector (1, 2 or 4) - one value or comma separated list
 --vectors|-v <arg>  Override detected optimal vector (1, 2 or 4) - one value or comma separated list
 --worksize|-w <arg> Override detected optimal worksize - one value or comma separated list
 --worksize|-w <arg> Override detected optimal worksize - one value or comma separated list
@@ -344,6 +346,7 @@ The following options are available while running with a single keypress:
 P gives you:
 P gives you:
 
 
 Current pool management strategy: Failover
 Current pool management strategy: Failover
+[F]ailover only disabled
 [A]dd pool [R]emove pool [D]isable pool [E]nable pool
 [A]dd pool [R]emove pool [D]isable pool [E]nable pool
 [C]hange management strategy [S]witch pool [I]nformation
 [C]hange management strategy [S]witch pool [I]nformation
 
 
@@ -355,14 +358,21 @@ S gives you:
 [S]cantime: 60
 [S]cantime: 60
 [E]xpiry: 120
 [E]xpiry: 120
 [R]etries: -1
 [R]etries: -1
-[P]ause: 5
 [W]rite config file
 [W]rite config file
+[B]FGMiner restart
 
 
 
 
 D gives you:
 D gives you:
 
 
-Toggle: [D]ebug [N]ormal [S]ilent [V]erbose [R]PC debug
-[L]og interval [C]lear
+[N]ormal [C]lear [S]ilent mode (disable all output)
+[D]ebug:off
+[P]er-device:off
+[Q]uiet:off
+[V]erbose:off
+[R]PC debug:off
+[W]orkTime details:off
+co[M]pact: off
+[L]og interval:5
 
 
 
 
 Q quits the application.
 Q quits the application.
@@ -386,6 +396,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,
@@ -435,6 +456,15 @@ diminish return performance even if the hash rate might appear better. A good
 starting baseline intensity to try on dedicated miners is 9. Higher values are
 starting baseline intensity to try on dedicated miners is 9. Higher values are
 there to cope with future improvements in hardware.
 there to cope with future improvements in hardware.
 
 
+
+The block display shows:
+Block: 0074c5e482e34a506d2a051a...  Started: [17:17:22]  Best share: 2.71K
+
+This shows a short stretch of the current block, when the new block started,
+and the all time best difficulty share you've submitted since starting cgminer
+this time.
+
+
 ---
 ---
 MULTIPOOL
 MULTIPOOL
 
 
@@ -922,6 +952,18 @@ 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 to reduce resources for mining pools at the
+cost of keeping the miner in the dark and blindly transferring his mining
+authority to the pool. It is a return to the problems of the old centralized
+"getwork" protocol, but capable of scaling to hardware of any speed like the
+standard GBT protocol. If a pool uses stratum instead of GBT, BFGMiner
+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://", BFGMiner will ONLY try to
+use stratum protocol mining.
 
 
 ---
 ---
 
 

+ 89 - 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;
@@ -166,7 +131,7 @@ static const char SEPARATOR = '|';
 #define SEPSTR "|"
 #define SEPSTR "|"
 static const char GPUSEP = ',';
 static const char GPUSEP = ',';
 
 
-static const char *APIVERSION = "1.19";
+static const char *APIVERSION = "1.20";
 static const char *DEAD = "Dead";
 static const char *DEAD = "Dead";
 static const char *SICK = "Sick";
 static const char *SICK = "Sick";
 static const char *NOSTART = "NoStart";
 static const char *NOSTART = "NoStart";
@@ -1765,6 +1730,9 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 	for (i = 0; i < total_pools; i++) {
 	for (i = 0; i < total_pools; i++) {
 		struct pool *pool = pools[i];
 		struct pool *pool = pools[i];
 
 
+		if (pool->removed)
+			continue;
+
 		switch (pool->enabled) {
 		switch (pool->enabled) {
 			case POOL_DISABLED:
 			case POOL_DISABLED:
 				status = (char *)DISABLED;
 				status = (char *)DISABLED;
@@ -1812,6 +1780,12 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
 		root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false);
 		root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false);
 		root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false);
 		root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false);
 		root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false);
 		root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false);
+		root = api_add_bool(root, "Has Stratum", &(pool->has_stratum), false);
+		root = api_add_bool(root, "Stratum Active", &(pool->stratum_active), false);
+		if (pool->stratum_active)
+			root = api_add_escape(root, "Stratum URL", pool->stratum_url, false);
+		else
+			root = api_add_const(root, "Stratum URL", BLANK, false);
 
 
 		if (isjson && (i > 0))
 		if (isjson && (i > 0))
 			strcat(io_buffer, COMMA);
 			strcat(io_buffer, COMMA);
@@ -2129,6 +2103,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') {
@@ -2145,7 +2120,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));

+ 8 - 19
configure.ac

@@ -69,7 +69,6 @@ USB_LIBS=""
 USB_FLAGS=""
 USB_FLAGS=""
 DLOPEN_FLAGS="-ldl"
 DLOPEN_FLAGS="-ldl"
 WS2_LIBS=""
 WS2_LIBS=""
-TIMER_LIBS=""
 MATH_LIBS="-lm"
 MATH_LIBS="-lm"
 
 
 case $target in
 case $target in
@@ -82,21 +81,13 @@ case $target in
 esac
 esac
 
 
 case $target in
 case $target in
-	x86_64-w64-mingw32)
-	have_x86_64=true
-	have_win32=true
-	PTHREAD_FLAGS=""
-	DLOPEN_FLAGS=""
-	WS2_LIBS="-lws2_32"
-	TIMER_LIBS="-lwinmm"
-	;;
   *-*-mingw*)
   *-*-mingw*)
     have_x86_64=false
     have_x86_64=false
     have_win32=true
     have_win32=true
     PTHREAD_FLAGS=""
     PTHREAD_FLAGS=""
     DLOPEN_FLAGS=""
     DLOPEN_FLAGS=""
     WS2_LIBS="-lws2_32"
     WS2_LIBS="-lws2_32"
-	TIMER_LIBS="-lwinmm"
+    AC_DEFINE([_WIN32_WINNT], [0x0501], "WinNT version for XP+ support")
     ;;
     ;;
   *-*-cygwin*)
   *-*-cygwin*)
 	have_cygwin=true
 	have_cygwin=true
@@ -379,8 +370,7 @@ AM_CONDITIONAL([HAVE_LIBUDEV], [test x$libudev != xno])
 
 
 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.
@@ -421,11 +411,11 @@ fi
 
 
 AC_DEFINE_UNQUOTED([CGMINER_PREFIX], ["$prefix/bin"], [Path to bfgminer install])
 AC_DEFINE_UNQUOTED([CGMINER_PREFIX], ["$prefix/bin"], [Path to bfgminer install])
 
 
-AC_DEFINE_UNQUOTED([PHATK_KERNNAME], ["phatk120823"], [Filename for phatk kernel])
-AC_DEFINE_UNQUOTED([POCLBM_KERNNAME], ["poclbm120823"], [Filename for poclbm kernel])
-AC_DEFINE_UNQUOTED([DIAKGCN_KERNNAME], ["diakgcn120823"], [Filename for diakgcn kernel])
-AC_DEFINE_UNQUOTED([DIABLO_KERNNAME], ["diablo120823"], [Filename for diablo kernel])
-AC_DEFINE_UNQUOTED([SCRYPT_KERNNAME], ["scrypt120823"], [Filename for scrypt kernel])
+AC_DEFINE_UNQUOTED([PHATK_KERNNAME], ["phatk121016"], [Filename for phatk kernel])
+AC_DEFINE_UNQUOTED([POCLBM_KERNNAME], ["poclbm121016"], [Filename for poclbm kernel])
+AC_DEFINE_UNQUOTED([DIAKGCN_KERNNAME], ["diakgcn121016"], [Filename for diakgcn kernel])
+AC_DEFINE_UNQUOTED([DIABLO_KERNNAME], ["diablo121016"], [Filename for diablo kernel])
+AC_DEFINE_UNQUOTED([SCRYPT_KERNNAME], ["scrypt121016"], [Filename for scrypt kernel])
 
 
 
 
 AC_SUBST(JANSSON_LIBS)
 AC_SUBST(JANSSON_LIBS)
@@ -436,7 +426,6 @@ AC_SUBST(NCURSES_CPPFLAGS)
 AC_SUBST(NCURSES_LIBS)
 AC_SUBST(NCURSES_LIBS)
 AC_SUBST(PDCURSES_LIBS)
 AC_SUBST(PDCURSES_LIBS)
 AC_SUBST(WS2_LIBS)
 AC_SUBST(WS2_LIBS)
-AC_SUBST(TIMER_LIBS)
 AC_SUBST(MATH_LIBS)
 AC_SUBST(MATH_LIBS)
 AC_SUBST(UDEV_LIBS)
 AC_SUBST(UDEV_LIBS)
 AC_SUBST(USB_LIBS)
 AC_SUBST(USB_LIBS)
@@ -537,7 +526,7 @@ echo "Compilation............: make (or gmake)"
 echo "  CPPFLAGS.............: $CPPFLAGS $NCURSES_CPPFLAGS"
 echo "  CPPFLAGS.............: $CPPFLAGS $NCURSES_CPPFLAGS"
 echo "  CFLAGS...............: $CFLAGS"
 echo "  CFLAGS...............: $CFLAGS"
 echo "  LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS $USB_FLAGS"
 echo "  LDFLAGS..............: $LDFLAGS $PTHREAD_FLAGS $USB_FLAGS"
-echo "  LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $TIMER_LIBS $MATH_LIBS $UDEV_LIBS $USB_LIBS"
+echo "  LDADD................: $DLOPEN_FLAGS $LIBCURL_LIBS $JANSSON_LIBS $PTHREAD_LIBS $NCURSES_LIBS $PDCURSES_LIBS $WS2_LIBS $MATH_LIBS $UDEV_LIBS $USB_LIBS"
 echo
 echo
 echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')"
 echo "Installation...........: make install (as root if needed, with 'su' or 'sudo')"
 echo "  prefix...............: $prefix"
 echo "  prefix...............: $prefix"

+ 1 - 6
diablo120823.cl → diablo121016.cl

@@ -1243,12 +1243,7 @@ void search(
     ZA[924] = (ZCh(ZA[922], ZA[920], ZA[918]) + ZA[923]) + ZR26(ZA[922]);
     ZA[924] = (ZCh(ZA[922], ZA[920], ZA[918]) + ZA[923]) + ZR26(ZA[922]);
     
     
 #define FOUND (0x0F)
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 
 #if defined(VECTORS4)
 #if defined(VECTORS4)
 	bool result = any(ZA[924] == 0x136032EDU);
 	bool result = any(ZA[924] == 0x136032EDU);

+ 1 - 6
diakgcn120823.cl → diakgcn121016.cl

@@ -572,12 +572,7 @@ __kernel
 	V[7] += V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]);
 	V[7] += V[3] + W[12] + ch(V[0], V[1], V[2]) + rotr26(V[0]);
 
 
 #define FOUND (0x0F)
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 
 #ifdef VECTORS4
 #ifdef VECTORS4
 	if ((V[7].x == 0x136032edU) ^ (V[7].y == 0x136032edU) ^ (V[7].z == 0x136032edU) ^ (V[7].w == 0x136032edU)) {
 	if ((V[7].x == 0x136032edU) ^ (V[7].y == 0x136032edU) ^ (V[7].z == 0x136032edU) ^ (V[7].w == 0x136032edU)) {

+ 14 - 19
driver-icarus.c

@@ -586,22 +586,19 @@ bool icarus_detect_custom(const char *devpath, struct device_api *api, struct IC
 	icarus_close(fd);
 	icarus_close(fd);
 
 
 	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
 	nonce_hex = bin2hex(nonce_bin, sizeof(nonce_bin));
-	if (nonce_hex) {
-		if (strncmp(nonce_hex, golden_nonce, 8)) {
-			applog(LOG_DEBUG,
-				"Icarus Detect: "
-				"Test failed at %s: get %s, should: %s",
-				devpath, nonce_hex, golden_nonce);
-			free(nonce_hex);
-			return false;
-		}
-		applog(LOG_DEBUG, 
+	if (strncmp(nonce_hex, golden_nonce, 8)) {
+		applog(LOG_DEBUG,
 			"Icarus Detect: "
 			"Icarus Detect: "
-			"Test succeeded at %s: got %s",
-				devpath, nonce_hex);
+			"Test failed at %s: get %s, should: %s",
+			devpath, nonce_hex, golden_nonce);
 		free(nonce_hex);
 		free(nonce_hex);
-	} else
 		return false;
 		return false;
+	}
+	applog(LOG_DEBUG,
+		"Icarus Detect: "
+		"Test succeeded at %s: got %s",
+			devpath, nonce_hex);
+	free(nonce_hex);
 
 
 	if (serial_claim(devpath, api)) {
 	if (serial_claim(devpath, api)) {
 		const char *claimedby = serial_claim(devpath, api)->dname;
 		const char *claimedby = serial_claim(devpath, api)->dname;
@@ -741,12 +738,10 @@ static bool icarus_start_work(struct thr_info *thr, const unsigned char *ob_bin)
 
 
 	if (opt_debug) {
 	if (opt_debug) {
 		ob_hex = bin2hex(ob_bin, 64);
 		ob_hex = bin2hex(ob_bin, 64);
-		if (ob_hex) {
-			applog(LOG_DEBUG, "%s %u sent: %s",
-				icarus->api->name,
-				icarus->device_id, ob_hex);
-			free(ob_hex);
-		}
+		applog(LOG_DEBUG, "%s %u sent: %s",
+			icarus->api->name,
+			icarus->device_id, ob_hex);
+		free(ob_hex);
 	}
 	}
 
 
 	return true;
 	return true;

+ 2 - 1
driver-modminer.c

@@ -17,6 +17,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.bit"
 #define BITSTREAM_FILENAME "fpgaminer_top_fixed7_197MHz.bit"
 #define BISTREAM_USER_ID "\2\4$B"
 #define BISTREAM_USER_ID "\2\4$B"
@@ -75,7 +76,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 a "ping" first, to workaround bug in new firmware betas (see issue #62)
 	// Sending a "ping" first, to workaround bug in new firmware betas (see issue #62)
 	// 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

+ 20 - 68
driver-opencl.c

@@ -800,21 +800,6 @@ struct cgpu_info *cpus;
 
 
 #ifdef HAVE_OPENCL
 #ifdef HAVE_OPENCL
 
 
-#ifdef WIN32
-static UINT timeperiod_set;
-#endif
-
-void opencl_dynamic_cleanup() {
-#ifdef WIN32
-	if (timeperiod_set) {
-		timeEndPeriod(timeperiod_set);
-		timeperiod_set = 0;
-	}
-#endif
-}
-
-extern int opt_dynamic_interval;
-
 /* In dynamic mode, only the first thread of each device will be in use.
 /* In dynamic mode, only the first thread of each device will be in use.
  * This potentially could start a thread that was stopped with the start-stop
  * This potentially could start a thread that was stopped with the start-stop
  * options if one were to disable dynamic from the menu on a paused GPU */
  * options if one were to disable dynamic from the menu on a paused GPU */
@@ -822,18 +807,10 @@ void pause_dynamic_threads(int gpu)
 {
 {
 	struct cgpu_info *cgpu = &gpus[gpu];
 	struct cgpu_info *cgpu = &gpus[gpu];
 	int i;
 	int i;
-#ifdef WIN32
-	bool any_dynamic = false;
-#endif
 
 
 	for (i = 1; i < cgpu->threads; i++) {
 	for (i = 1; i < cgpu->threads; i++) {
 		struct thr_info *thr = &thr_info[i];
 		struct thr_info *thr = &thr_info[i];
 
 
-#ifdef WIN32
-		if (cgpu->dynamic)
-			any_dynamic = true;
-#endif
-
 		if (!thr->pause && cgpu->dynamic) {
 		if (!thr->pause && cgpu->dynamic) {
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			applog(LOG_WARNING, "Disabling extra threads due to dynamic mode.");
 			applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval");
 			applog(LOG_WARNING, "Tune dynamic intensity with --gpu-dyninterval");
@@ -843,20 +820,6 @@ void pause_dynamic_threads(int gpu)
 		if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED)
 		if (!cgpu->dynamic && cgpu->deven != DEV_DISABLED)
 			tq_push(thr->q, &ping);
 			tq_push(thr->q, &ping);
 	}
 	}
-
-#ifdef WIN32
-	if (any_dynamic) {
-		if (!timeperiod_set) {
-			timeperiod_set = opt_dynamic_interval > 3 ? (opt_dynamic_interval / 2) : 1;
-			if (TIMERR_NOERROR != timeBeginPeriod(timeperiod_set))
-				timeperiod_set = 0;
-		}
-	} else {
-		if (timeperiod_set) {
-			opencl_dynamic_cleanup();
-		}
-	}
-#endif
 }
 }
 
 
 
 
@@ -1719,10 +1682,8 @@ static void opencl_free_work(struct thr_info *thr, struct work *work)
 	const int thr_id = thr->id;
 	const int thr_id = thr->id;
 	struct opencl_thread_data *thrdata = thr->cgpu_data;
 	struct opencl_thread_data *thrdata = thr->cgpu_data;
 	_clState *clState = clStates[thr_id];
 	_clState *clState = clStates[thr_id];
-	struct cgpu_info *gpu = thr->cgpu;
 
 
-	if (!gpu->dynamic)
-		clFinish(clState->commandQueue);
+	clFinish(clState->commandQueue);
 
 
 	if (thrdata->res[FOUND]) {
 	if (thrdata->res[FOUND]) {
 		thrdata->last_work = &thrdata->_last_work;
 		thrdata->last_work = &thrdata->_last_work;
@@ -1759,8 +1720,25 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 	int64_t hashes;
 	int64_t hashes;
 
 
 	/* This finish flushes the readbuffer set with CL_FALSE later */
 	/* This finish flushes the readbuffer set with CL_FALSE later */
-	if (!gpu->dynamic)
-		clFinish(clState->commandQueue);
+	clFinish(clState->commandQueue);
+
+	/* Windows' timer resolution is only 15ms so oversample 5x */
+	if (gpu->dynamic && (++gpu->intervals * dynamic_us) > 70000) {
+		struct timeval tv_gpuend;
+		double gpu_us;
+
+		gettimeofday(&tv_gpuend, NULL);
+		gpu_us = us_tdiff(&tv_gpuend, &gpu->tv_gpustart) / gpu->intervals;
+		if (gpu_us > dynamic_us) {
+			if (gpu->intensity > MIN_INTENSITY)
+				--gpu->intensity;
+		} else if (gpu_us < dynamic_us / 2) {
+			if (gpu->intensity < MAX_INTENSITY)
+				++gpu->intensity;
+		}
+		memcpy(&(gpu->tv_gpustart), &tv_gpuend, sizeof(struct timeval));
+		gpu->intervals = 0;
+	}
 
 
 	set_threads_hashes(clState->vwidth, &hashes, globalThreads, localThreads[0], &gpu->intensity);
 	set_threads_hashes(clState->vwidth, &hashes, globalThreads, localThreads[0], &gpu->intensity);
 	if (hashes > gpu->max_hashes)
 	if (hashes > gpu->max_hashes)
@@ -1787,8 +1765,6 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 		clFinish(clState->commandQueue);
 		clFinish(clState->commandQueue);
 	}
 	}
 
 
-	gettimeofday(&gpu->tv_gpustart, NULL);
-
 	status = thrdata->queue_kernel_parameters(clState, &work->blk, globalThreads[0]);
 	status = thrdata->queue_kernel_parameters(clState, &work->blk, globalThreads[0]);
 	if (unlikely(status != CL_SUCCESS)) {
 	if (unlikely(status != CL_SUCCESS)) {
 		applog(LOG_ERR, "Error: clSetKernelArg of all params failed.");
 		applog(LOG_ERR, "Error: clSetKernelArg of all params failed.");
@@ -1816,30 +1792,6 @@ static int64_t opencl_scanhash(struct thr_info *thr, struct work *work,
 		return -1;
 		return -1;
 	}
 	}
 
 
-	if (gpu->dynamic) {
-		struct timeval diff;
-		suseconds_t gpu_us;
-
-		clFinish(clState->commandQueue);
-		gettimeofday(&gpu->tv_gpuend, NULL);
-		timersub(&gpu->tv_gpuend, &gpu->tv_gpustart, &diff);
-		gpu_us = diff.tv_sec * 1000000 + diff.tv_usec;
-		if (likely(gpu_us >= 0)) {
-			gpu->gpu_us_average = (gpu->gpu_us_average + gpu_us * 0.63) / 1.63;
-
-			/* Try to not let the GPU be out for longer than
-			 * opt_dynamic_interval in ms, but increase
-			 * intensity when the system is idle in dynamic mode */
-			if (gpu->gpu_us_average > dynamic_us) {
-				if (gpu->intensity > MIN_INTENSITY)
-					--gpu->intensity;
-			} else if (gpu->gpu_us_average < dynamic_us / 2) {
-				if (gpu->intensity < MAX_INTENSITY)
-					++gpu->intensity;
-			}
-		}
-	}
-
 	/* The amount of work scanned can fluctuate when intensity changes
 	/* The amount of work scanned can fluctuate when intensity changes
 	 * and since we do this one cycle behind, we increment the work more
 	 * and since we do this one cycle behind, we increment the work more
 	 * than enough to prevent repeating work */
 	 * than enough to prevent repeating work */

+ 11 - 10
findnonce.c

@@ -134,7 +134,7 @@ void precalc_hash(dev_blk_ctx *blk, uint32_t *state, uint32_t *data)
 
 
 struct pc_data {
 struct pc_data {
 	struct thr_info *thr;
 	struct thr_info *thr;
-	struct work *work;
+	struct work work;
 	uint32_t res[MAXBUFFERS];
 	uint32_t res[MAXBUFFERS];
 	pthread_t pth;
 	pthread_t pth;
 	int found;
 	int found;
@@ -144,7 +144,6 @@ static void *postcalc_hash(void *userdata)
 {
 {
 	struct pc_data *pcd = (struct pc_data *)userdata;
 	struct pc_data *pcd = (struct pc_data *)userdata;
 	struct thr_info *thr = pcd->thr;
 	struct thr_info *thr = pcd->thr;
-	struct work *work = pcd->work;
 	unsigned int entry = 0;
 	unsigned int entry = 0;
 
 
 	pthread_detach(pthread_self());
 	pthread_detach(pthread_self());
@@ -152,12 +151,19 @@ static void *postcalc_hash(void *userdata)
 
 
 	/* To prevent corrupt values in FOUND from trying to read beyond the
 	/* To prevent corrupt values in FOUND from trying to read beyond the
 	 * end of the res[] array */
 	 * end of the res[] array */
-	pcd->res[FOUND] &= FOUND;
+	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];
 
 
 		applog(LOG_DEBUG, "OCL NONCE %u found in slot %d", nonce, entry);
 		applog(LOG_DEBUG, "OCL NONCE %u found in slot %d", nonce, entry);
-		submit_nonce(thr, work, nonce);
+		submit_nonce(thr, &pcd->work, nonce);
 	}
 	}
 
 
 	free(pcd);
 	free(pcd);
@@ -172,14 +178,9 @@ void postcalc_hash_async(struct thr_info *thr, struct work *work, uint32_t *res)
 		applog(LOG_ERR, "Failed to malloc pc_data in postcalc_hash_async");
 		applog(LOG_ERR, "Failed to malloc pc_data in postcalc_hash_async");
 		return;
 		return;
 	}
 	}
-	pcd->work = calloc(1, sizeof(struct work));
-	if (unlikely(!pcd->work)) {
-		applog(LOG_ERR, "Failed to malloc work in postcalc_hash_async");
-		return;
-	}
 
 
 	pcd->thr = thr;
 	pcd->thr = thr;
-	memcpy(pcd->work, work, sizeof(struct work));
+	memcpy(&pcd->work, work, sizeof(struct work));
 	memcpy(&pcd->res, res, BUFFERSIZE);
 	memcpy(&pcd->res, res, BUFFERSIZE);
 
 
 	if (pthread_create(&pcd->pth, NULL, postcalc_hash, (void *)pcd)) {
 	if (pthread_create(&pcd->pth, NULL, postcalc_hash, (void *)pcd)) {

+ 1 - 1
fpgautils.c

@@ -137,7 +137,7 @@ int serial_autodetect_ftdi(detectone_func_t detectone, const char *needle, const
 	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;

+ 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"

File diff suppressed because it is too large
+ 465 - 186
miner.c


+ 94 - 8
miner.h

@@ -16,6 +16,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
 #include "CL/cl.h"
 #include "CL/cl.h"
@@ -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
@@ -137,18 +151,27 @@ void *alloca (size_t);
   #include <endian.h>
   #include <endian.h>
 #endif
 #endif
 
 
-/* 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
+ * the others 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)
 #  define le32toh(x) (x)
 #  define le32toh(x) (x)
+#  define le64toh(x) (x)
 #  define htole32(x) (x)
 #  define htole32(x) (x)
+#  define htole64(x) (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)
 #  define le32toh(x) bswap_32(x)
 #  define le32toh(x) bswap_32(x)
+#  define le64toh(x) bswap_64(x)
 #  define htole32(x) bswap_32(x)
 #  define htole32(x) bswap_32(x)
+#  define htole64(x) bswap_64(x)
 #else
 #else
 #error UNKNOWN BYTE ORDER
 #error UNKNOWN BYTE ORDER
 #endif
 #endif
@@ -290,6 +313,7 @@ struct device_api {
 	void (*free_work)(struct thr_info*, struct work*);
 	void (*free_work)(struct thr_info*, struct work*);
 	bool (*prepare_work)(struct thr_info*, struct work*);
 	bool (*prepare_work)(struct thr_info*, struct work*);
 	int64_t (*scanhash)(struct thr_info*, struct work*, int64_t);
 	int64_t (*scanhash)(struct thr_info*, struct work*, int64_t);
+	void (*hw_error)(struct thr_info*);
 	void (*thread_shutdown)(struct thr_info*);
 	void (*thread_shutdown)(struct thr_info*);
 	void (*thread_enable)(struct thr_info*);
 	void (*thread_enable)(struct thr_info*);
 };
 };
@@ -433,9 +457,8 @@ struct cgpu_info {
 	size_t opt_tc, thread_concurrency;
 	size_t opt_tc, thread_concurrency;
 	size_t shaders;
 	size_t shaders;
 #endif
 #endif
-	struct timeval tv_gpustart;;
-	struct timeval tv_gpuend;
-	double gpu_us_average;
+	struct timeval tv_gpustart;
+	int intervals;
 #endif
 #endif
 
 
 	bool new_work;
 	bool new_work;
@@ -519,6 +542,7 @@ extern int thr_info_create(struct thr_info *thr, pthread_attr_t *attr, void *(*s
 extern void thr_info_cancel(struct thr_info *thr);
 extern void thr_info_cancel(struct thr_info *thr);
 extern void thr_info_freeze(struct thr_info *thr);
 extern void thr_info_freeze(struct thr_info *thr);
 extern void nmsleep(unsigned int msecs);
 extern void nmsleep(unsigned int msecs);
+extern double us_tdiff(struct timeval *end, struct timeval *start);
 extern void rename_thr(const char* name);
 extern void rename_thr(const char* name);
 extern double tdiff(struct timeval *end, struct timeval *start);
 extern double tdiff(struct timeval *end, struct timeval *start);
 
 
@@ -582,6 +606,21 @@ static inline void swap32yes(void*out, const void*in, size_t sz) {
 #  define swap32tole(out, in, sz)  (void)0
 #  define swap32tole(out, in, sz)  (void)0
 #endif
 #endif
 
 
+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)
@@ -667,6 +706,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;
 
 
@@ -719,8 +759,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_details5(bool live, char *url, char *user, char *pass, char *proxy);
-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
 
 
@@ -831,6 +872,24 @@ enum pool_protocol {
 	PLP_GETBLOCKTEMPLATE,
 	PLP_GETBLOCKTEMPLATE,
 };
 };
 
 
+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 8192
+#define RBUFSIZE (RECVSIZE + 4)
+
 struct pool {
 struct pool {
 	int pool_no;
 	int pool_no;
 	int prio;
 	int prio;
@@ -899,12 +958,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];
@@ -913,10 +991,10 @@ struct work {
 	unsigned char	target[32];
 	unsigned char	target[32];
 	unsigned char	hash[32];
 	unsigned char	hash[32];
 
 
+	uint32_t	outputhash;
+
 	int		rolls;
 	int		rolls;
 
 
-	uint32_t	output[1];
-	uint32_t	valid;
 	dev_blk_ctx	blk;
 	dev_blk_ctx	blk;
 
 
 	struct thr_info	*thr;
 	struct thr_info	*thr;
@@ -934,6 +1012,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 char	work_restart_id;
 	unsigned char	work_restart_id;
 	int		id;
 	int		id;
 	UT_hash_handle hh;
 	UT_hash_handle hh;

+ 1 - 6
phatk120823.cl → phatk121016.cl

@@ -388,12 +388,7 @@ void search(	const uint state0, const uint state1, const uint state2, const uint
 		(-(K[60] + H[7]) - S1((Vals[0] + Vals[4]) + (K[59] + W(59+64))  + s1(64+59)+ ch(59+64)));
 		(-(K[60] + H[7]) - S1((Vals[0] + Vals[4]) + (K[59] + W(59+64))  + s1(64+59)+ ch(59+64)));
 
 
 #define FOUND (0x0F)
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 
 #ifdef VECTORS4
 #ifdef VECTORS4
 	bool result = W[117].x & W[117].y & W[117].z & W[117].w;
 	bool result = W[117].x & W[117].y & W[117].z & W[117].w;

+ 1 - 6
poclbm120823.cl → poclbm121016.cl

@@ -1322,12 +1322,7 @@ Vals[2]+=(rotr(Vals[1],6)^rotr(Vals[1],11)^rotr(Vals[1],25));
 Vals[2]+=ch(Vals[1],Vals[4],Vals[3]);
 Vals[2]+=ch(Vals[1],Vals[4],Vals[3]);
 
 
 #define FOUND (0x0F)
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 
 #if defined(VECTORS2) || defined(VECTORS4)
 #if defined(VECTORS2) || defined(VECTORS4)
 	if (any(Vals[2] == 0x136032edU)) {
 	if (any(Vals[2] == 0x136032edU)) {

+ 12 - 0
scrypt.c

@@ -405,6 +405,18 @@ static uint32_t scrypt_1024_1_1_256_sp(const uint32_t* input, char* scratchpad)
 	return PBKDF2_SHA256_80_128_32(input, X);
 	return PBKDF2_SHA256_80_128_32(input, X);
 }
 }
 
 
+void scrypt_outputhash(struct work *work)
+{
+	uint32_t data[20];
+	char *scratchbuf;
+	uint32_t *nonce = (uint32_t *)(work->data + 76);
+
+	be32enc_vect(data, (const uint32_t *)work->data, 19);
+	data[19] = htobe32(*nonce);
+	scratchbuf = alloca(131584);
+	work->outputhash = scrypt_1024_1_1_256_sp(data, scratchbuf);
+}
+
 /* Used externally as confirmation of correct OCL code */
 /* Used externally as confirmation of correct OCL code */
 bool scrypt_test(unsigned char *pdata, const unsigned char *ptarget, uint32_t nonce)
 bool scrypt_test(unsigned char *pdata, const unsigned char *ptarget, uint32_t nonce)
 {
 {

+ 8 - 0
scrypt.h

@@ -1,9 +1,13 @@
 #ifndef SCRYPT_H
 #ifndef SCRYPT_H
 #define SCRYPT_H
 #define SCRYPT_H
 
 
+#include "miner.h"
+
 #ifdef USE_SCRYPT
 #ifdef USE_SCRYPT
 extern bool scrypt_test(unsigned char *pdata, const unsigned char *ptarget,
 extern bool scrypt_test(unsigned char *pdata, const unsigned char *ptarget,
 			uint32_t nonce);
 			uint32_t nonce);
+extern void scrypt_outputhash(struct work *work);
+
 #else /* USE_SCRYPT */
 #else /* USE_SCRYPT */
 static inline bool scrypt_test(__maybe_unused unsigned char *pdata,
 static inline bool scrypt_test(__maybe_unused unsigned char *pdata,
 			       __maybe_unused const unsigned char *ptarget,
 			       __maybe_unused const unsigned char *ptarget,
@@ -11,6 +15,10 @@ static inline bool scrypt_test(__maybe_unused unsigned char *pdata,
 {
 {
 	return false;
 	return false;
 }
 }
+
+static inline void scrypt_outputhash(__maybe_unused struct work *work)
+{
+}
 #endif /* USE_SCRYPT */
 #endif /* USE_SCRYPT */
 
 
 #endif /* SCRYPT_H */
 #endif /* SCRYPT_H */

+ 1 - 6
scrypt120823.cl → scrypt121016.cl

@@ -683,12 +683,7 @@ void scrypt_core(uint4 X[8], __global uint4*restrict lookup)
 }
 }
 
 
 #define FOUND (0x0F)
 #define FOUND (0x0F)
-
-#if defined(OCL1)
-	#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
-#else
-	#define SETFOUND(Xnonce) output[atomic_add(&output[FOUND], 1)] = Xnonce
-#endif
+#define SETFOUND(Xnonce) output[output[FOUND]++] = Xnonce
 
 
 __attribute__((reqd_work_group_size(WORKSIZE, 1, 1)))
 __attribute__((reqd_work_group_size(WORKSIZE, 1, 1)))
 __kernel void search(__global const uint4 * restrict input,
 __kernel void search(__global const uint4 * restrict input,

+ 651 - 19
util.c

@@ -35,20 +35,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;
@@ -68,6 +65,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;
@@ -207,18 +205,21 @@ 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,
-			     curlsocktype __maybe_unused purpose)
+static int keep_sockalive(SOCKETTYPE fd)
 {
 {
-	int tcp_keepidle = 120;
-	int tcp_keepintvl = 120;
+	int tcp_keepidle = 60;
+	int tcp_keepintvl = 60;
 
 
 #ifndef WIN32
 #ifndef WIN32
 	int keepalive = 1;
 	int keepalive = 1;
@@ -261,7 +262,12 @@ int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
 
 
 	return 0;
 	return 0;
 }
 }
-#endif
+
+int json_rpc_call_sockopt_cb(void __maybe_unused *userdata, curl_socket_t fd,
+			     curlsocktype __maybe_unused purpose)
+{
+	return keep_sockalive(fd);
+}
 
 
 static void last_nettime(struct timeval *last)
 static void last_nettime(struct timeval *last)
 {
 {
@@ -285,7 +291,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, NULL};
 	struct data_buffer all_data = {NULL, 0, NULL};
-	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;
@@ -338,10 +344,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)
@@ -418,9 +422,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;
@@ -509,7 +523,7 @@ char *bin2hex(const unsigned char *p, size_t len)
 		slen += 4 - (slen % 4);
 		slen += 4 - (slen % 4);
 	s = calloc(slen, 1);
 	s = calloc(slen, 1);
 	if (unlikely(!s))
 	if (unlikely(!s))
-		return NULL;
+		quit(1, "Failed to calloc in bin2hex");
 
 
 	for (i = 0; i < len; i++)
 	for (i = 0; i < len; i++)
 		sprintf(s + (i * 2), "%02x", (unsigned int) p[i]);
 		sprintf(s + (i * 2), "%02x", (unsigned int) p[i]);
@@ -765,6 +779,12 @@ void nmsleep(unsigned int msecs)
 	} while (ret == -1 && errno == EINTR);
 	} while (ret == -1 && errno == EINTR);
 }
 }
 
 
+/* Returns the microseconds difference between end and start times as a double */
+double us_tdiff(struct timeval *end, struct timeval *start)
+{
+	return end->tv_sec * 1000000 + end->tv_usec - start->tv_sec * 1000000 - start->tv_usec;
+}
+
 void rename_thr(const char* name) {
 void rename_thr(const char* name) {
 #if defined(PR_SET_NAME)
 #if defined(PR_SET_NAME)
 	// Only the first 15 characters are used (16 - NUL terminator)
 	// Only the first 15 characters are used (16 - NUL terminator)
@@ -784,3 +804,615 @@ 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;
+
+	pool->sockaddr_url = url;
+	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. This should all
+ * be done under stratum lock except when first establishing the socket */
+static bool __stratum_send(struct pool *pool, char *s, ssize_t len)
+{
+	SOCKETTYPE sock = pool->sock;
+	ssize_t ssent = 0;
+
+	if (opt_protocol)
+		applog(LOG_DEBUG, "SEND: %s", s);
+
+	strcat(s, "\n");
+	len++;
+
+	while (len > 0 ) {
+		struct timeval timeout = {0, 0};
+		CURLcode rc = CURLE_SEND_ERROR;
+		size_t sent = 0;
+		fd_set wd;
+
+		FD_ZERO(&wd);
+		FD_SET(sock, &wd);
+		if (select(sock + 1, NULL, &wd, NULL, &timeout) < 1) {
+			applog(LOG_DEBUG, "Write select failed on pool %d sock", pool->pool_no);
+			return false;
+		}
+		if (likely(pool->stratum_curl))
+			rc = curl_easy_send(pool->stratum_curl, s + ssent, len, &sent);
+		if (rc != CURLE_OK) {
+			applog(LOG_DEBUG, "Failed to curl_easy_send in stratum_send");
+			return false;
+		}
+		ssent += sent;
+		len -= ssent;
+	}
+
+	return true;
+}
+
+bool stratum_send(struct pool *pool, char *s, ssize_t len)
+{
+	bool ret = false;
+
+	mutex_lock(&pool->stratum_lock);
+	if (pool->stratum_active)
+		ret = __stratum_send(pool, s, len);
+	else
+		applog(LOG_DEBUG, "Stratum send failed due to no pool stratum_active");
+	mutex_unlock(&pool->stratum_lock);
+
+	return ret;
+}
+
+static void clear_sock(struct pool *pool)
+{
+	size_t n = 0;
+
+	mutex_lock(&pool->stratum_lock);
+	/* Ignore return code of curl_easy_recv since we're just clearing
+	 * anything in the socket if it's still alive */
+	if (likely(pool->stratum_curl))
+		curl_easy_recv(pool->stratum_curl, pool->sockbuf, RECVSIZE, &n);
+	mutex_unlock(&pool->stratum_lock);
+	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 = 0;
+
+	if (!strstr(pool->sockbuf, "\n")) {
+		CURLcode rc = CURLE_RECV_ERROR;
+		char s[RBUFSIZE];
+		size_t sspace;
+
+		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);
+		if (likely(pool->stratum_curl))
+			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;
+		}
+		/* Prevent buffer overflows, but if 8k is still not enough,
+		 * likely we have had some comms issues and the data is all
+		 * useless anyway */
+		sspace = RECVSIZE - strlen(pool->sockbuf);
+		strncat(pool->sockbuf, s, sspace);
+	}
+
+	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;
+
+	mutex_lock(&pool->stratum_lock);
+	pool->stratum_active = 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");
+	}
+	mutex_unlock(&pool->stratum_lock);
+	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);
+	} 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);
+	keep_sockalive(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 {
+		applog(LOG_DEBUG, "Initiate stratum failed, disabling stratum_active");
+		mutex_lock(&pool->stratum_lock);
+		if (curl) {
+			curl_easy_cleanup(curl);
+			pool->stratum_curl = NULL;
+		}
+		mutex_unlock(&pool->stratum_lock);
+	}
+
+	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