def do_mining(nx_miners, nx_settings, nx_benchmarks, nx_devices): # Initialize miners. logging.info('Querying NiceHash for miner connection information...') mbtc_per_hash = download_time = stratums = None while mbtc_per_hash is None: try: mbtc_per_hash, stratums = nicehash.simplemultialgo_info(nx_settings) except (socket.error, socket.timeout, SSLError, URLError): sleep(5) else: download_time = datetime.now() for miner in nx_miners: miner.stratums = stratums algorithms = sum([miner.algorithms for miner in nx_miners], []) # Initialize profit-switching. profit_switch = NaiveSwitcher(nx_settings) profit_switch.reset() # SIGINT signal for quitting. quit = Event() signal.signal(signal.SIGINT, lambda signum, frame: quit.set()) current_algorithm = {d: None for d in nx_devices} revenues = {d: defaultdict(lambda: 0.0) for d in nx_devices} while not quit.is_set(): # Calculate BTC/day rates. def revenue(device, algorithm): benchmarks = nx_benchmarks[device] if algorithm.name in benchmarks: return sum([mbtc_per_hash[algorithm.algorithms[i]] *benchmarks[algorithm.name][i]] for i in range(len(algorithm.algorithms))]) else: return 0.0 revenues = {device: {algorithm: revenue(device, algorithm) for algorithm in algorithms} for device in nx_devices} # Get device -> algorithm assignments from profit switcher. current_algorithm = profit_switch.decide(revenues, download_time) for this_algorithm in algorithms: this_devices = [device for device, algorithm in current_algorithm.items() if algorithm == this_algorithm] this_algorithm.set_devices(my_devices)
class TestNaiveSwitcher(TestCase): def setUp(self): settings = nuxhash.settings.DEFAULT_SETTINGS settings['switching']['threshold'] = 0.5 self.devices = tests.get_test_devices() self.benchmarks = tests.get_test_benchmarks() self.miner = Excavator(Path('/'), settings) self.equihash = next(a for a in self.miner.algorithms if a.algorithms == ['equihash']) self.neoscrypt = next(a for a in self.miner.algorithms if a.algorithms == ['neoscrypt']) self.switcher = NaiveSwitcher(settings) self.switcher.reset() def test_most_profitable(self): device = self.devices[0] equihash_revenue = 4.0 * self.benchmarks[device]['excavator_equihash'][ 0] neoscrypt_revenue = 2.0 * self.benchmarks[device][ 'excavator_neoscrypt'][0] revenues = { device: { self.equihash: equihash_revenue, self.neoscrypt: neoscrypt_revenue } } decision = self.switcher.decide(revenues, None) self.assertEqual(decision[device], self.neoscrypt) def test_below_threshold(self): device = self.devices[0] self.switcher.decide( {device: { self.equihash: 2.0, self.neoscrypt: 1.0 }}, None) decision = self.switcher.decide( {device: { self.equihash: 2.0, self.neoscrypt: 2.5 }}, None) self.assertEqual(decision[device], self.equihash) def test_above_threshold(self): device = self.devices[0] self.switcher.decide( {device: { self.equihash: 2.0, self.neoscrypt: 1.0 }}, None) decision = self.switcher.decide( {device: { self.equihash: 2.0, self.neoscrypt: 3.5 }}, None) self.assertEqual(decision[device], self.neoscrypt)
class MiningSession(object): PROFIT_PRIORITY = 1 STOP_PRIORITY = 0 def __init__(self, miners, settings, benchmarks, devices): self._miners = miners self._settings = settings self._benchmarks = benchmarks self._devices = devices self._payrates = (None, None) self._quit_signal = Event() self._scheduler = sched.scheduler(time.time, lambda t: self._quit_signal.wait(t)) self._algorithms = [] self._profit_switch = None def run(self): # Initialize miners. logging.info('Querying NiceHash for miner connection information...') payrates = stratums = None while payrates is None: try: payrates = nicehash.simplemultialgo_info(self._settings) stratums = nicehash.stratums(self._settings) except Exception as err: logging.warning( f'NiceHash stats: {err}, retrying in 5 seconds') time.sleep(5) else: self._payrates = (payrates, datetime.now()) for miner in self._miners: miner.stratums = stratums miner.load() self._algorithms = sum([miner.algorithms for miner in self._miners], []) # Initialize profit-switching. self._profit_switch = NaiveSwitcher(self._settings) self._profit_switch.reset() self._scheduler.enter(0, MiningSession.PROFIT_PRIORITY, self._switch_algos) self._scheduler.run() def stop(self): self._scheduler.enter(0, MiningSession.STOP_PRIORITY, self._stop_mining) self._quit_signal.set() def _switch_algos(self): # Get profitability information from NiceHash. try: ret_payrates = nicehash.simplemultialgo_info(self._settings) except Exception as err: logging.warning(f'NiceHash stats: {err}') else: self._payrates = (ret_payrates, datetime.now()) interval = self._settings['switching']['interval'] payrates, payrates_time = self._payrates # Calculate BTC/day rates. def revenue(device, algorithm): benchmarks = self._benchmarks[device] if algorithm.name in benchmarks: return sum([ payrates[sub_algo] * benchmarks[algorithm.name][i] if sub_algo in payrates else 0.0 for i, sub_algo in enumerate(algorithm.algorithms) ]) else: return 0.0 revenues = { device: { algorithm: revenue(device, algorithm) for algorithm in self._algorithms } for device in self._devices } # Get device -> algorithm assignments from profit switcher. self._assignments = self._profit_switch.decide(revenues, payrates_time) for this_algorithm in self._algorithms: this_devices = [ device for device, algorithm in self._assignments.items() if algorithm == this_algorithm ] this_algorithm.set_devices(this_devices) # Donation time. if not self._settings['donate']['optout'] and random() < DONATE_PROB: logging.warning('This interval will be donation time.') donate_settings = deepcopy(self._settings) donate_settings['nicehash']['wallet'] = DONATE_ADDRESS donate_settings['nicehash']['workername'] = 'nuxhash' for miner in self._miners: miner.settings = donate_settings self._scheduler.enter(interval, MiningSession.PROFIT_PRIORITY, self._reset_miners) self._scheduler.enter(interval, MiningSession.PROFIT_PRIORITY, self._switch_algos) def _reset_miners(self): for miner in self._miners: miner.settings = self._settings def _stop_mining(self): logging.info('Quit signal received, terminating miners') # Empty the scheduler. for job in self._scheduler.queue: self._scheduler.cancel(job)
class MiningSession(object): PROFIT_PRIORITY = 1 STOP_PRIORITY = 0 def __init__(self, miners, settings, benchmarks, devices): self._miners = miners self._settings = settings self._benchmarks = benchmarks self._devices = devices self._payrates = (None, None) self._quit_signal = Event() self._scheduler = sched.scheduler(time.time, lambda t: self._quit_signal.wait(t)) self._algorithms = [] self._profit_switch = None def run(self): # Initialize miners. logging.info('Querying NiceHash for miner connection information...') payrates = stratums = None while payrates is None: try: payrates, stratums = nicehash.simplemultialgo_info( self._settings) except NH_EXCEPTIONS as err: logging.warning('NiceHash stats: %s, retrying in 5 seconds' % err) time.sleep(5) else: self._payrates = (payrates, datetime.now()) for miner in self._miners: miner.stratums = stratums miner.load() self._algorithms = sum([miner.algorithms for miner in self._miners], []) # Initialize profit-switching. self._profit_switch = NaiveSwitcher(self._settings) self._profit_switch.reset() # Attach the SIGINT signal for quitting. # NOTE: If running in a shell, Ctrl-C will get sent to our subprocesses too, # because we are the foreground process group. Miners will get killed # before we have a chance to properly shut them down. signal.signal(signal.SIGINT, lambda signum, frame: self.stop()) self._scheduler.enter(0, MiningSession.PROFIT_PRIORITY, self._switch_algos) self._scheduler.run() def stop(self): self._scheduler.enter(0, MiningSession.STOP_PRIORITY, self._stop_mining) self._quit_signal.set() def _switch_algos(self): # Get profitability information from NiceHash. try: ret_payrates, stratums = nicehash.simplemultialgo_info( self._settings) except NH_EXCEPTIONS as err: logging.warning('NiceHash stats: %s' % err) else: self._payrates = (ret_payrates, datetime.now()) interval = self._settings['switching']['interval'] payrates, payrates_time = self._payrates # Calculate BTC/day rates. def revenue(device, algorithm): benchmarks = self._benchmarks[device] if algorithm.name in benchmarks: return sum([ payrates[algorithm.algorithms[i]] * benchmarks[algorithm.name][i] for i in range(len(algorithm.algorithms)) ]) else: return 0.0 revenues = { device: { algorithm: revenue(device, algorithm) for algorithm in self._algorithms } for device in self._devices } # Get device -> algorithm assignments from profit switcher. self._assignments = self._profit_switch.decide(revenues, payrates_time) for this_algorithm in self._algorithms: this_devices = [ device for device, algorithm in self._assignments.items() if algorithm == this_algorithm ] this_algorithm.set_devices(this_devices) # Donation time. if not self._settings['donate']['optout'] and random() < DONATE_PROB: logging.warning('This interval will be donation time.') donate_settings = deepcopy(self._settings) donate_settings['nicehash']['wallet'] = DONATE_ADDRESS donate_settings['nicehash']['workername'] = 'nuxhash' for miner in self._miners: miner.settings = donate_settings self._scheduler.enter(interval, MiningSession.PROFIT_PRIORITY, self._reset_miners) self._scheduler.enter(interval, MiningSession.PROFIT_PRIORITY, self._switch_algos) def _reset_miners(self): for miner in self._miners: miner.settings = self._settings def _stop_mining(self): logging.info('Cleaning up') for algorithm in self._algorithms: algorithm.set_devices([]) for miner in self._miners: miner.unload() # Empty the scheduler. for job in self._scheduler.queue: self._scheduler.cancel(job)
class MiningThread(threading.Thread): PROFIT_PRIORITY = 1 STATUS_PRIORITY = 2 STOP_PRIORITY = 0 def __init__(self, devices=[], window=None, settings=DEFAULT_SETTINGS, benchmarks=EMPTY_BENCHMARKS): threading.Thread.__init__(self) self._window = window self._settings = settings self._benchmarks = benchmarks self._devices = devices self._stop_signal = threading.Event() self._scheduler = sched.scheduler(time.time, lambda t: self._stop_signal.wait(t)) def run(self): # Initialize miners. stratums = None while stratums is None: try: payrates, stratums = nicehash.simplemultialgo_info( self._settings) except (socket.error, socket.timeout, SSLError, URLError): time.sleep(5) self._miners = [miner(main.CONFIG_DIR) for miner in all_miners] for miner in self._miners: miner.settings = self._settings miner.stratums = stratums self._algorithms = sum([miner.algorithms for miner in self._miners], []) # Initialize profit-switching. self._profit_switch = NaiveSwitcher(self._settings) self._profit_switch.reset() self._scheduler.enter(0, MiningThread.PROFIT_PRIORITY, self._switch_algos) self._scheduler.enter(0, MiningThread.STATUS_PRIORITY, self._read_status) self._scheduler.run() def stop(self): self._scheduler.enter(0, MiningThread.STOP_PRIORITY, self._stop_mining) self._stop_signal.set() self.join() def _switch_algos(self): interval = self._settings['switching']['interval'] # Get profitability information from NiceHash. try: payrates, stratums = nicehash.simplemultialgo_info(self._settings) except (socket.error, socket.timeout, SSLError, URLError) as err: logging.warning('NiceHash stats: %s' % err) except nicehash.BadResponseError: logging.warning('NiceHash stats: Bad response') else: download_time = datetime.now() self._current_payrates = payrates # Calculate BTC/day rates. def revenue(device, algorithm): benchmarks = self._benchmarks[device] if algorithm.name in benchmarks: return sum([ payrates[algorithm.algorithms[i]] * benchmarks[algorithm.name][i] for i in range(len(algorithm.algorithms)) ]) else: return 0.0 revenues = { device: { algorithm: revenue(device, algorithm) for algorithm in self._algorithms } for device in self._devices } # Get device -> algorithm assignments from profit switcher. assigned_algorithm = self._profit_switch.decide( revenues, download_time) self._assignments = assigned_algorithm for this_algorithm in self._algorithms: this_devices = [ device for device, algorithm in assigned_algorithm.items() if algorithm == this_algorithm ] this_algorithm.set_devices(this_devices) # Donation time. if not self._settings['donate']['optout'] and random() < DONATE_PROB: logging.warning('This interval will be donation time.') donate_settings = deepcopy(self._settings) donate_settings['nicehash']['wallet'] = DONATE_ADDRESS donate_settings['nicehash']['workername'] = 'nuxhash' for miner in self._miners: miner.settings = donate_settings self._scheduler.enter(interval, MiningThread.PROFIT_PRIORITY, self._reset_miners) self._scheduler.enter(interval, MiningThread.PROFIT_PRIORITY, self._switch_algos) def _reset_miners(self): for miner in self._miners: miner.settings = self._settings def _read_status(self): running_algorithms = self._assignments.values() speeds = { algorithm: algorithm.current_speeds() for algorithm in running_algorithms } revenue = { algorithm: sum([ self._current_payrates[multialgorithm] * speeds[algorithm][i] for i, multialgorithm in enumerate(algorithm.algorithms) ]) for algorithm in running_algorithms } devices = { algorithm: [ device for device, this_algorithm in self._assignments.items() if this_algorithm == algorithm ] for algorithm in running_algorithms } main.sendMessage(self._window, 'mining.status', speeds=speeds, revenue=revenue, devices=devices) self._scheduler.enter(MINING_UPDATE_SECS, MiningThread.STATUS_PRIORITY, self._read_status) def _stop_mining(self): logging.info('Stopping mining') for algorithm in self._algorithms: algorithm.set_devices([]) for miner in self._miners: miner.unload() # Empty the scheduler. for job in self._scheduler.queue: self._scheduler.cancel(job)
class MiningThread(threading.Thread): PROFIT_PRIORITY = 1 STATUS_PRIORITY = 2 STOP_PRIORITY = 0 def __init__(self, window, settings, benchmarks, devices): threading.Thread.__init__(self) self._window = window self._settings = settings self._benchmarks = benchmarks self._devices = devices self._stop_signal = threading.Event() self._scheduler = sched.scheduler(time.time, lambda t: self._stop_signal.wait(t)) def run(self): # Initialize miners. stratums = None while stratums is None: try: payrates, stratums = nicehash.simplemultialgo_info( self._settings) except (socket.error, socket.timeout, SSLError, URLError): time.sleep(5) self._miners = [Excavator(CONFIG_DIR, self._settings)] for miner in self._miners: miner.stratums = stratums self._algorithms = sum([miner.algorithms for miner in self._miners], []) # Initialize profit-switching. self._profit_switch = NaiveSwitcher(self._settings) self._profit_switch.reset() self._scheduler.enter(0, MiningThread.PROFIT_PRIORITY, self._switch_algos) self._scheduler.enter(0, MiningThread.STATUS_PRIORITY, self._read_status) self._scheduler.run() def stop(self): self._scheduler.enter(0, MiningThread.STOP_PRIORITY, self._stop_mining) self._stop_signal.set() def _switch_algos(self): # Get profitability information from NiceHash. try: payrates, stratums = nicehash.simplemultialgo_info(self._settings) except (socket.error, socket.timeout, SSLError, URLError) as err: logging.warning('NiceHash stats: %s' % err) except nicehash.BadResponseError: logging.warning('NiceHash stats: Bad response') else: download_time = datetime.now() self._current_payrates = payrates # Calculate BTC/day rates. def revenue(device, algorithm): benchmarks = self._benchmarks[device] if algorithm.name in benchmarks: return sum([ payrates[algorithm.algorithms[i]] * benchmarks[algorithm.name][i] for i in range(len(algorithm.algorithms)) ]) else: return 0.0 revenues = { device: { algorithm: revenue(device, algorithm) for algorithm in self._algorithms } for device in self._devices } # Get device -> algorithm assignments from profit switcher. assigned_algorithm = self._profit_switch.decide( revenues, download_time) self._assignments = assigned_algorithm for this_algorithm in self._algorithms: this_devices = [ device for device, algorithm in assigned_algorithm.items() if algorithm == this_algorithm ] this_algorithm.set_devices(this_devices) self._scheduler.enter(self._settings['switching']['interval'], MiningThread.PROFIT_PRIORITY, self._switch_algos) def _read_status(self): running_algorithms = self._assignments.values() # Check miner status. for algorithm in running_algorithms: if not algorithm.parent.is_running(): logging.error('Detected %s crash, restarting miner' % algorithm.name) algorithm.parent.reload() speeds = { algorithm: algorithm.current_speeds() for algorithm in running_algorithms } revenue = { algorithm: sum([ self._current_payrates[multialgorithm] * speeds[algorithm][i] for i, multialgorithm in enumerate(algorithm.algorithms) ]) for algorithm in running_algorithms } devices = { algorithm: [ device for device, this_algorithm in self._assignments.items() if this_algorithm == algorithm ] for algorithm in running_algorithms } wx.PostEvent( self._window, MiningStatusEvent(speeds=speeds, revenue=revenue, devices=devices)) self._scheduler.enter(MINING_UPDATE_SECS, MiningThread.STATUS_PRIORITY, self._read_status) def _stop_mining(self): logging.info('Stopping mining') for algorithm in self._algorithms: algorithm.set_devices([]) for miner in self._miners: miner.unload() # Empty the scheduler. for job in self._scheduler.queue: self._scheduler.cancel(job)
class MiningSession(object): PROFIT_PRIORITY = 1 WATCH_PRIORITY = 2 STOP_PRIORITY = 0 WATCH_INTERVAL = 15 def __init__(self, miners, settings, benchmarks, devices): self._miners = miners self._settings = settings self._benchmarks = benchmarks self._devices = devices self._last_payrates = (None, None) self._quit_signal = Event() self._scheduler = sched.scheduler(time.time, lambda t: self._quit_signal.wait(t)) self._algorithms = [] self._profit_switch = None def run(self): # Initialize miners. logging.info('Querying NiceHash for miner connection information...') payrates = stratums = None while payrates is None: try: payrates, stratums = nicehash.simplemultialgo_info( self._settings) except (socket.error, socket.timeout, SSLError, URLError): time.sleep(5) else: self._last_payrates = (payrates, datetime.now()) for miner in self._miners: miner.stratums = stratums miner.load() self._algorithms = sum([miner.algorithms for miner in self._miners], []) # Initialize profit-switching. self._profit_switch = NaiveSwitcher(self._settings) self._profit_switch.reset() # Attach the SIGINT signal for quitting. signal.signal(signal.SIGINT, lambda signum, frame: self.stop()) self._scheduler.enter(0, MiningSession.PROFIT_PRIORITY, self._switch_algos) self._scheduler.enter(0, MiningSession.WATCH_PRIORITY, self._watch_algos) self._scheduler.run() def stop(self): self._scheduler.enter(0, MiningSession.STOP_PRIORITY, self._stop_mining) self._quit_signal.set() def _switch_algos(self): interval = self._settings['switching']['interval'] # Get profitability information from NiceHash. try: payrates, stratums = nicehash.simplemultialgo_info(self._settings) except (socket.error, socket.timeout, SSLError, URLError) as err: logging.warning('NiceHash stats: %s' % err) except nicehash.BadResponseError: logging.warning('NiceHash stats: Bad response') else: download_time = datetime.now() self._last_payrates = (payrates, download_time) # Calculate BTC/day rates. def revenue(device, algorithm): benchmarks = self._benchmarks[device] if algorithm.name in benchmarks: return sum([ payrates[algorithm.algorithms[i]] * benchmarks[algorithm.name][i] for i in range(len(algorithm.algorithms)) ]) else: return 0.0 revenues = { device: { algorithm: revenue(device, algorithm) for algorithm in self._algorithms } for device in self._devices } # Get device -> algorithm assignments from profit switcher. self._assignments = self._profit_switch.decide(revenues, download_time) for this_algorithm in self._algorithms: this_devices = [ device for device, algorithm in self._assignments.items() if algorithm == this_algorithm ] this_algorithm.set_devices(this_devices) # Donation time. if not self._settings['donate']['optout'] and random() < DONATE_PROB: logging.warning('This interval will be donation time.') donate_settings = deepcopy(self._settings) donate_settings['nicehash']['wallet'] = DONATE_ADDRESS donate_settings['nicehash']['workername'] = 'nuxhash' for miner in self._miners: miner.settings = donate_settings self._scheduler.enter(interval, MiningSession.PROFIT_PRIORITY, self._reset_miners) self._scheduler.enter(interval, MiningSession.PROFIT_PRIORITY, self._switch_algos) def _reset_miners(self): for miner in self._miners: miner.settings = self._settings def _watch_algos(self): running_algorithms = self._assignments.values() for algorithm in running_algorithms: if not algorithm.parent.is_running(): logging.error('Detected %s crash, restarting miner' % algorithm.name) algorithm.parent.reload() self._scheduler.enter(MiningSession.WATCH_INTERVAL, MiningSession.WATCH_PRIORITY, self._watch_algos) def _stop_mining(self): logging.info('Cleaning up') for algorithm in self._algorithms: algorithm.set_devices([]) for miner in self._miners: miner.unload() # Empty the scheduler. for job in self._scheduler.queue: self._scheduler.cancel(job)