def getwork(self): conn = http_client.HTTPConnection(self.host, self.port, True, self.getworktimeout) req = json.dumps({ "method": "getwork", "params": [], "id": 0 }).encode("utf_8") headers = { "User-Agent": self.useragent, "Content-type": "application/json", "Content-Length": len(req) } if self.auth != None: headers["Authorization"] = self.auth conn.request("POST", self.path, req, headers) response = conn.getresponse() with self.statlock: if not self.longpolling: self.longpolling = False headers = response.getheaders() for h in headers: if h[0].lower() == "x-long-polling": url = h[1] try: if url[0] == "/": url = "http://" + self.host + ":" + str( self.port) + url if url[:7] != "http://": raise Exception("Long poll URL isn't HTTP!") parts = url[7:].split("/", 1) if len(parts) == 2: path = "/" + parts[1] else: path = "/" parts = parts[0].split(":") if len(parts) != 2: raise Exception( "Long poll URL contains host but no port!") host = parts[0] port = parts[1] self.miner.log( "Found long polling URL for %s: %s\n" % (self.name, url), "g") self.longpolling = True self.longpollingthread = threading.Thread( None, self.longpollingworker, self.name + "_longpolling", (host, port, path)) self.longpollingthread.daemon = True self.longpollingthread.start() except: self.miner.log( "Invalid long polling URL for %s: %s\n" % (self.name, url), "y") break response = json.loads(response.read().decode("utf_8")) state = binascii.unhexlify( response["result"]["midstate"].encode("ascii")) data = binascii.unhexlify(response["result"]["data"].encode("ascii")) target = binascii.unhexlify( response["result"]["target"].encode("ascii")) return common.Job(self.miner, self, self.longpollepoch, state, data, target)
def longpollingworker(self, host, port, path): while True: try: conn = http_client.HTTPConnection(host, port, True, self.longpolltimeout) headers = {"User-Agent": self.useragent} if self.auth != None: headers["Authorization"] = self.auth conn.request("GET", path, None, headers) data = conn.getresponse().read().decode("utf_8") response = json.loads(data) state = binascii.unhexlify( response["result"]["midstate"].encode("ascii")) data = binascii.unhexlify( response["result"]["data"].encode("ascii")) target = binascii.unhexlify( response["result"]["target"].encode("ascii")) job = common.Job(self.miner, self, self.longpollepoch, state, data, target) self.miner.newblock(job) except Exception as e: self.miner.log("%s long poll failed: %s\n" % (self.name, e), "y") time.sleep(3) pass
def main(self): # Loop forever. If anything fails, restart threads. while True: try: # Exception container: If an exception occurs in the listener thread, the listener thread # will store it here and terminate, and the main thread will rethrow it and then restart. self.error = None # Initialize megahashes per second to zero, will be measured later. self.mhps = 0 # Job that the device is currently working on (found nonces are coming from this one). self.job = None # Job that is currently being uploaded to the device but not yet being processed. self.nextjob = None # Get handle for the serial port self.handle = serial.Serial(self.port, self.baudrate, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, 1, False, False, None, False, None) # We keep control of the wakeup lock at all times unless we're sleeping self.wakeup.acquire() # Set validation success flag to false self.checksuccess = False # Initialize job cancellation (long poll) flag to false self.canceled = False # Start device response listener thread self.listenerthread = threading.Thread(None, self.listener, self.name + "_listener") self.listenerthread.daemon = True self.listenerthread.start() # Send validation job to device job = common.Job( self.miner, None, None, binascii.unhexlify( b"1625cbf1a5bc6ba648d1218441389e00a9dc79768a2fc6f2b79c70cf576febd0" ), b"\0" * 64 + binascii.unhexlify(b"4c0afa494de837d81a269421"), None, binascii.unhexlify(b"7bc2b302")) self.sendjob(job) # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # Wait for the validation job to complete. The wakeup flag will be set by the listener # thread when the validation job completes. 60 seconds should be sufficient for devices # down to about 1520KH/s, for slower devices this timeout will need to be increased. self.wakeup.wait(60) # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # We woke up, but the validation job hasn't succeeded in the mean time. # This usually means that the wakeup timeout has expired. if not self.checksuccess: raise Exception( "Timeout waiting for validation job to finish") # self.mhps has now been populated by the listener thread self.miner.log( self.name + ": Running at %f MH/s\n" % self.mhps, "B") # Calculate the time that the device will need to process 2**32 nonces. # This is limited at 30 seconds so that new transactions can be included into the block # by the work source. (Requirement of the bitcoin protocol and enforced by most pools.) interval = min(30, 2**32 / 1000000. / self.mhps) # Add some safety margin and take user's interval setting (if present) into account. self.jobinterval = min(self.jobinterval, max(0.5, interval * 0.8 - 1)) self.miner.log( self.name + ": Job interval: %f seconds\n" % self.jobinterval, "B") # Tell the MPBM core that our hash rate has changed, so that it can adjust its work buffer. self.jobspersecond = 1. / self.jobinterval self.miner.updatehashrate(self) # Main loop, continues until something goes wrong. while True: # Fetch a job. Blocks until one is available. Because of this we need to release the # wake lock temporarily in order to avoid possible deadlocks. self.canceled = False self.wakeup.release() job = self.miner.getjob(self) # Doesn't need acquisition of the statlock because we're the only one who modifies this. self.jobsaccepted = self.jobsaccepted + 1 self.wakeup.acquire() # If a new block was found while we were fetching that job, # check the long poll epoch to verify that the work that we got isn't stale. # If it is, just discard it and get a new one. if self.canceled == True: if job.longpollepoch != job.pool.blockchain.longpollepoch: continue self.canceled = False # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # Upload the piece of work to the device self.sendjob(job) # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # If the job that was send above has not been moved from nextjob to job by the listener # thread yet, something went wrong. Throw an exception to make everything restart. if self.canceled: continue # Wait while the device is processing the job. If nonces are sent by the device, they # will be processed by the listener thread. If a long poll comes in, we will be woken up. self.wakeup.wait(self.jobinterval) # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # If something went wrong... except Exception as e: # ...complain about it! self.miner.log(self.name + ": %s\n" % e, "rB") # Make sure that the listener thread realizes that something went wrong self.error = e # We're not doing productive work any more, update stats self.mhps = 0 # Release the wake lock to allow the listener thread to move. Ignore it if that goes wrong. try: self.wakeup.release() except: pass # Wait for the listener thread to terminate. # If it doens't within 10 seconds, continue anyway. We can't do much about that. try: self.listenerthread.join(10) except: pass # Set MH/s to zero again, the listener thread might have overwritten that. self.mhps = 0 # Make sure that the RS232 interface handle is closed, # otherwise we can't reopen it after restarting. try: self.handle.close() except: pass # Wait for a second to avoid 100% CPU load if something fails reproducibly time.sleep(1)
def main(self): # Make sure the FPGA is put to sleep when MPBM exits #atexit.register(self.fpga.sleep) # Loop forever. If anything fails, restart. while True: try: # Exception container: If an exception occurs in the listener thread, the listener thread # will store it here and terminate, and the main thread will rethrow it and then restart. self.error = None # Initialize megahashes per second to zero, will be measured later. self.mhps = 0 # Job that the device is currently working on (found nonces are coming from this one). self.job = None # Job that is currently being uploaded to the device but not yet being processed. self.nextjob = None # We keep control of the wakeup lock at all times unless we're sleeping self.wakeup.acquire() # Set validation success flag to false self.checksuccess = False # Set validation job second iteration flag to false self.seconditeration = False # Initialize job cancellation (long poll) flag to false self.canceled = False # Initialize hash rate tracking data self.lasttime = None self.lastnonce = None if self.firmware_rev > 0: self.miner.log( self.name + ": Setting clock speed to %d MHz...\n" % self.clockspeed, "B") self.fpga.setClockSpeed(self.clockspeed) if self.fpga.readClockSpeed() != self.clockspeed: self.miner.log( self.name + ": ERROR: Setting clock speed failed!\n", "rB") # TODO: Raise an exception and shutdown the FPGA self.mhps = self.clockspeed * 1 # this coefficient is the hashes per clock, change this when that increases :) # Clear FPGA's nonce queue self.fpga.clearQueue() # Start device response listener thread self.listenerthread = threading.Thread(None, self.listener, self.name + "_listener") self.listenerthread.daemon = True self.listenerthread.start() # Send validation job to device self.miner.log( self.name + ": Verifying correct operation...\n", "B") job = common.Job( \ miner = self.miner, \ pool = None, \ longpollepoch = None, \ state = binascii.unhexlify(b"5517a3f06a2469f73025b60444e018e61ff2c557ea403ccf3b9c5445dd353710"), \ data = b"\0" * 64 + binascii.unhexlify(b"42490d634f2c87761a0cd43f"), \ target = None, \ check = binascii.unhexlify(b"8fa95303") \ ) self.sendjob(job) # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # Wait for the validation job to complete. The wakeup flag will be set by the listener # thread when the validation job completes. 180 seconds should be sufficient for devices # down to about 50MH/s, for slower devices this timeout will need to be increased. if self.firmware_rev > 0: interval = (2**32 / 1000000. / self.mhps) * 1.1 self.wakeup.wait(interval) else: self.wakeup.wait(180) # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # We woke up, but the validation job hasn't succeeded in the mean time. # This usually means that the wakeup timeout has expired. if not self.checksuccess: raise Exception( "Timeout waiting for validation job to finish") # self.mhps has now been populated by the listener thread self.miner.log( self.name + ": Running at %f MH/s\n" % self.mhps, "B") # Calculate the time that the device will need to process 2**32 nonces. # This is limited at 30 seconds so that new transactions can be included into the block # by the work source. (Requirement of the bitcoin protocol and enforced by most pools.) interval = min(30, 2**32 / 1000000. / self.mhps) # Add some safety margin and take user's interval setting (if present) into account. self.jobinterval = min(self.jobinterval, max(0.5, interval * 0.8 - 1)) self.miner.log( self.name + ": Job interval: %f seconds\n" % self.jobinterval, "B") # Tell the MPBM core that our hash rate has changed, so that it can adjust its work buffer. self.jobspersecond = 1. / self.jobinterval self.miner.updatehashrate(self) # Main loop, continues until something goes wrong. while True: # Fetch a job. Blocks until one is available. Because of this we need to release the # wake lock temporarily in order to avoid possible deadlocks. self.canceled = False self.wakeup.release() job = self.miner.getjob(self) # Doesn't need acquisition of the statlock because we're the only one who modifies this. self.jobsaccepted = self.jobsaccepted + 1 self.wakeup.acquire() # If a new block was found while we were fetching that job, # check the long poll epoch to verify that the work that we got isn't stale. # If it is, just discard it and get a new one. if self.canceled == True: if job.longpollepoch != job.pool.blockchain.longpollepoch: continue self.canceled = False # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # Upload the piece of work to the device self.sendjob(job) # Go through the safety checks and reduce the clock if necessary self.safetycheck() # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # If the job was already caught by a long poll while we were uploading it, # jump back to the beginning of the main loop in order to immediately fetch new work. # Don't check for the canceled flag before the job was accepted by the device, # otherwise we might get out of sync. if self.canceled: continue # Wait while the device is processing the job. If nonces are sent by the device, they # will be processed by the listener thread. If a long poll comes in, we will be woken up. self.wakeup.wait(self.jobinterval) # If an exception occurred in the listener thread, rethrow it if self.error != None: raise self.error # If something went wrong... except Exception as e: # ...complain about it! self.miner.log(self.name + ": %s\n" % e, "rB") # Make sure that the listener thread realizes that something went wrong self.error = e # We're not doing productive work any more, update stats self.mhps = 0 # Release the wake lock to allow the listener thread to move. Ignore it if that goes wrong. try: self.wakeup.release() except: pass if self.parent.hotplug: for child in self.parent.children: child.error = Exception( "Sibling FPGA worker died, restarting board") try: self.parent.device.close() except: pass # Wait for the listener thread to terminate. # If it doens't within 10 seconds, continue anyway. We can't do much about that. try: self.listenerthread.join(10) except: pass # Set MH/s to zero again, the listener thread might have overwritten that. self.mhps = 0 # Notify the hotplug manager about our death, so that it can respawn as neccessary if self.parent.hotplug: self.parent.dead = True return # Wait for a second to avoid 100% CPU load if something fails reproducibly time.sleep(1)