def detect_stratum_proxy(host): s = None try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) s.sendto( dumps({ "id": 0, "method": "mining.get_upstream", "params": [] }), ('239.3.3.3', 3333)) say_line('Searching stratum proxy for %s', host) s.settimeout(2) try: while True: response, address = s.recvfrom(128) try: response = loads(response) response_host = response['result'][0][0] + ':' + str( response['result'][0][1]) if response_host == host: proxy_address = address[0] + ':' + str( response['result'][1]) say_line('Using stratum proxy at %s', proxy_address) return proxy_address except ValueError: pass except socket.timeout: pass finally: if s != None: s.close()
def set_server_index(self, server_index): self.server_index = server_index user = self.servers[server_index].user name = self.servers[server_index].name # say_line('Setting server %s (%s @ %s)', (name, user, host)) say_line('Setting server (%s @ %s)', (user, name)) log.server = name
def detect_stratum_proxy(host): s = None try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) s.sendto( dumps({"id": 0, "method": "mining.get_upstream", "params": []}), ('239.3.3.3', 3333)) say_line('Searching stratum proxy for %s', host) s.settimeout(2) try: while True: response, address = s.recvfrom(128) try: response = loads(response) response_host = response['result'][0][0] + ':' + str( response['result'][0][1]) if response_host == host: proxy_address = address[0] + ':' + str( response['result'][1]) say_line('Using stratum proxy at %s', proxy_address) return proxy_address except ValueError: pass except socket.timeout: pass finally: if s != None: s.close()
def proposeblock(self, block_data, work_id=None): try: self.connection = \ self.ensure_connected(self.connection, self.server().proto, self.server().host)[0] param = {'mode': 'proposal', 'data': block_data} if work_id: param['workid'] = work_id postdata = { 'method': 'getblocktemplate', 'id': 'json', 'params': (param, ) } with open('last_submission.txt', 'w') as submission_file: submission_file.write(dumps(postdata)) (self.connection, result) = self.request(self.connection, '/', self.headers, dumps(postdata)) self.switch.connection_ok() reject_reason = result['result'] say_line('proposal response: %s', reject_reason) return result['result'] except (IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): say_exception() self.stop() except Exception: say_exception()
def long_poll_thread(self, long_poll_id_available): long_poll_id_available.wait() while True: if self.should_stop or self.authorization_failed: return try: self.long_poll_active = True template = self.getblocktemplate(long_poll_id=self.long_poll_id, timeout=self.long_poll_timeout) self.long_poll_active = False if template: work = self.work_from_template(template) self.queue_work(work) if self.options.verbose: say_line('long poll: new block %s%s', (work['data'][56:64].decode('ascii'), work['data'][48:56].decode('ascii'))) if 'longpollid' in template: self.long_poll_id = template['longpollid'] except (IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): say_exception('long poll IO error') self.close_lp_connection() sleep(.5) except Exception: say_exception()
def get_temperature(self): response = request(self.device, b'ZLX') if len(response) < 23 or response[0] != b'T' or response[-1:] != b'\n': say_line('%s: bad response for temperature: %s', (self.id(), response)) return 0 return float(response[23:-1])
def generation_tx_for_template(self, template): template_tx = template.get('coinbasetxn') # In segwit mode, we need another merkle root that has hashed witness # portions of txes: witness_commitment = unhexlify(template['default_witness_commitment']) # TODO: use the 'hash' attrs if this is missing coinbase_msg = self.options.coinbase_msg.encode('utf-8') if template_tx: if self.options.address: if 'coinbase' in template.get('mutable', ('coinbase',)): return tx_make_generation(coinbase_msg, self.options.address, template['coinbasevalue'], template['height'], witness_commitment=witness_commitment) else: say_line( f'Warning: address {self.options.address} ignored, not allowed by work source.') else: if self.options.address: return tx_make_generation(coinbase_msg, self.options.address, template['coinbasevalue'], template['height'], witness_commitment=witness_commitment) else: raise Exception('Address not supplied by user and no coinbase tx supplied by work source.') return ( unhexlify(template_tx['data']), unhexlify(template_tx['txid']), unhexlify(template_tx['hash']), )
def long_poll_thread(self, long_poll_id_available): long_poll_id_available.wait() while True: if self.should_stop or self.authorization_failed: return try: self.long_poll_active = True template = self.getblocktemplate( long_poll_id=self.long_poll_id, timeout=self.long_poll_timeout) self.long_poll_active = False if template: work = self.work_from_template(template) self.queue_work(work) if self.options.verbose: say_line('long poll: new block %s%s', (work['data'][56:64].decode('ascii'), work['data'][48:56].decode('ascii'))) if 'longpollid' in template: self.long_poll_id = template['longpollid'] except (IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): say_exception('long poll IO error') self.close_lp_connection() sleep(.5) except Exception: say_exception()
def get_temperature(self): response = request(self.device, b'ZLX') if len(response) < 23 or response[0] != b'T' or response[-1:] != b'\n': say_line('%s: bad response for temperature: %s', (self.id(), response)) return 0 return float(response[23:-1])
def check_result(self): response = request(self.device, b'ZFX') if response.startswith(b'B'): return False if response == b'NO-NONCE\n': return response if response[:12] != 'NONCE-FOUND:' or response[-1:] != '\n': say_line('%s: bad response checking result: %s', (self.id(), response)) return None return response[12:-1]
def check_result(self): response = request(self.device, b'ZFX') if response.startswith(b'B'): return False if response == b'NO-NONCE\n': return response if response[:12] != 'NONCE-FOUND:' or response[-1:] != '\n': say_line('%s: bad response checking result: %s', (self.id(), response)) return None return response[12:-1]
def report(self, miner, nonce, accepted): is_block, hash6, hash5 = self.sent[nonce] miner.share_count[1 if accepted else 0] += 1 hash_ = hash6 + hash5 if is_block else hash6 if self.options.verbose or is_block: say_line('%s %s%s, %s', ( miner.id(), 'block ' if is_block else '', hash_, 'accepted' if accepted else '_rejected_')) del self.sent[nonce]
def loop(self): super(StratumSource, self).loop() self.switch.update_time = True while True: if self.should_stop: return if self.current_job: miner = self.switch.updatable_miner() while miner: self.current_job = self.refresh_job(self.current_job) self.queue_work(self.current_job, miner) miner = self.switch.updatable_miner() if self.check_failback(): return True if not self.handler: try: # socket = ssl.wrap_socket(socket) address, port = self.server().host.split(':', 1) if not self.options.proxy: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((address, int(port))) else: self.socket = socks.socksocket() p = self.options.proxy self.socket.setproxy(p.type, p.host, p.port, True, p.user, p.pwd) try: self.socket.connect((address, int(port))) except socks.Socks5AuthError: say_exception('Proxy error:') self.stop() self.handler = Handler(self.socket, self.channel_map, self) thread = Thread(target=self.asyncore_thread) thread.daemon = True thread.start() if not self.subscribe(): say_line('Failed to subscribe') self.stop() elif not self.authorize(): self.stop() except socket.error: say_exception() self.stop() continue with self.send_lock: self.process_result_queue() sleep(1)
def loop(self): super(StratumSource, self).loop() self.switch.update_time = True while True: if self.should_stop: return if self.current_job: miner = self.switch.updatable_miner() while miner: self.current_job = self.refresh_job(self.current_job) self.queue_work(self.current_job, miner) miner = self.switch.updatable_miner() if self.check_failback(): return True if not self.handler: try: # socket = ssl.wrap_socket(socket) address, port = self.server().host.split(':', 1) if not self.options.proxy: self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect((address, int(port))) else: self.socket = socks.socksocket() p = self.options.proxy self.socket.setproxy(p.type, p.host, p.port, True, p.user, p.pwd) try: self.socket.connect((address, int(port))) except socks.Socks5AuthError: say_exception('Proxy error:') self.stop() self.handler = Handler(self.socket, self.channel_map, self) thread = Thread(target=self.asyncore_thread) thread.daemon = True thread.start() if not self.subscribe(): say_line('Failed to subscribe') self.stop() elif not self.authorize(): self.stop() except socket.error: say_exception() self.stop() continue with self.send_lock: self.process_result_queue() sleep(1)
def load_kernel(self): max_worksize = self.device.get_info(cl.device_info.MAX_WORK_GROUP_SIZE) if not self.worksize: self.worksize = max_worksize if self.options.verbose: say_line('Set worksize to %s from device info.', self.worksize) self.defines += f' -D WORK_GROUP_SIZE={self.worksize}' if self.worksize > max_worksize: # Exceeding the max advertised work group size # The overriding size will only be configure # at compile time isntead of at execution. self.execution_local_dims = None else: self.execution_local_dims = (self.worksize,) self.context = cl.Context([self.device], None, None) if self.device.extensions.find('cl_amd_media_ops') != -1: self.defines += ' -D BITALIGN' if self.device_name in ('Cedar', 'Redwood', 'Juniper', 'Cypress', 'Hemlock', 'Caicos', 'Turks', 'Barts', 'Cayman', 'Antilles', 'Wrestler', 'Zacate', 'WinterPark', 'BeaverCreek'): self.defines += ' -D BFI_INT' kernel = pkgutil.get_data('apoclypsebm', f'{self.options.kernel}.cl') m = md5( f'{self.device.platform.name}{self.device.platform.version}' f'{self.device.name}{self.defines}'.encode('utf-8') ) m.update(kernel) cache_name = f'{m.hexdigest()}.elf' try: with open(cache_name, 'rb') as binary: self.program = cl.Program(self.context, [self.device], [binary.read()]).build(self.defines) except (IOError, cl.LogicError): kernel = kernel.decode('ascii') self.program = cl.Program(self.context, kernel).build(self.defines) if self.defines.find('-D BFI_INT') != -1: patched_binary = self.patch(self.program.binaries[0]) self.program = cl.Program(self.context, [self.device], [patched_binary]).build(self.defines) with open(cache_name, 'wb') as binary: binary.write(self.program.binaries[0]) self.kernel = self.program.search if self.options.verbose: compiled_worksize = self.kernel.get_work_group_info( cl.kernel_work_group_info.COMPILE_WORK_GROUP_SIZE, self.device ) say_line('Compiled work size: %s', compiled_worksize)
def add_stratum_source(self): if self.options.stratum_proxies: stratum_proxy = stratum.detect_stratum_proxy( self.server().host) if stratum_proxy: original_server = copy(self.server()) original_server.source = stratum.StratumSource(self) self.servers.insert(self.backup_server_index, original_server) self.server().host = stratum_proxy self.server().name += '(p)' log.server = self.server().name else: say_line('No proxy found') self.server().source = stratum.StratumSource(self)
def check(port, likely=True): result = False try: device = open_device(port) response = init_device(device) device.close() result = is_good_init(response) except SerialException: if likely: say_exception() if not likely and result: say_line('Found BitFORCE on %s', port) elif likely and not result: say_line('No valid response from BitFORCE on %s', port) return result
def check(port, likely=True): result = False try: device = open_device(port) response = init_device(device) device.close() result = is_good_init(response) except SerialException: if likely: say_exception() if not likely and result: say_line('Found BitFORCE on %s', port) elif likely and not result: say_line('No valid response from BitFORCE on %s', port) return result
def long_poll_thread(self): last_host = None while True: if self.should_stop or self.authorization_failed: return url = self.long_poll_url if url != '': proto = self.server().proto host = self.server().host parsedUrl = urlsplit(url) if parsedUrl.scheme != '': proto = parsedUrl.scheme if parsedUrl.netloc != '': host = parsedUrl.netloc url = url[url.find(host) + len(host):] if url == '': url = '/' try: if host != last_host: self.close_lp_connection() self.lp_connection, changed = self.ensure_connected( self.lp_connection, proto, host) if changed: say_line("LP connected to %s", self.server().name) last_host = host self.long_poll_active = True response = self.request(self.lp_connection, url, self.headers, timeout=self.long_poll_timeout) self.long_poll_active = False if response: (self.lp_connection, result) = response self.queue_work(result['result']) if self.options.verbose: say_line('long poll: new block %s%s', (result['result']['data'][56:64], result['result']['data'][48:56])) except (IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): say_exception('long poll IO error') self.close_lp_connection() sleep(.5) except Exception: say_exception()
def long_poll_thread(self): last_host = None while True: if self.should_stop or self.authorization_failed: return url = self.long_poll_url if url != '': proto = self.server().proto host = self.server().host parsedUrl = urlsplit(url) if parsedUrl.scheme != '': proto = parsedUrl.scheme if parsedUrl.netloc != '': host = parsedUrl.netloc url = url[url.find(host) + len(host):] if url == '': url = '/' try: if host != last_host: self.close_lp_connection() self.lp_connection, changed = self.ensure_connected( self.lp_connection, proto, host) if changed: say_line("LP connected to %s", self.server().name) last_host = host self.long_poll_active = True response = self.request(self.lp_connection, url, self.headers, timeout=self.long_poll_timeout) self.long_poll_active = False if response: (self.lp_connection, result) = response self.queue_work(result['result']) if self.options.verbose: say_line('long poll: new block %s%s', ( result['result']['data'][56:64], result['result']['data'][48:56])) except ( IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): say_exception('long poll IO error') self.close_lp_connection() sleep(.5) except Exception: say_exception()
def request(self, connection, url, headers, data=None, timeout=0): result = response = None try: if data: connection.request('POST', url, data, headers) else: connection.request('GET', url, headers=headers) response = self.timeout_response(connection, timeout) if not response: return None if response.status == http.client.UNAUTHORIZED: say_line('Wrong username or password for %s', self.server().name) self.authorization_failed = True raise NotAuthorized() r = self.max_redirects while response.status == http.client.TEMPORARY_REDIRECT: response.read() url = response.getheader('Location', '') if r == 0 or url == '': raise http.client.HTTPException( 'Too much or bad redirects') connection.request('GET', url, headers=headers) response = self.timeout_response(connection, timeout) r -= 1 self.long_poll_url = response.getheader('X-Long-Polling', '') self.switch.update_time = bool( response.getheader('X-Roll-NTime', '')) hostList = response.getheader('X-Host-List', '') self.stratum_header = response.getheader('x-stratum', '') if (not self.options.nsf) and hostList: self.switch.add_servers(loads(hostList)) result = loads(response.read()) if result['error']: say_line('server error: %s', result['error']['message']) raise RPCError(result['error']['message']) return (connection, result) finally: if not result or not response or ( response.version == 10 and response.getheader('connection', '') != 'keep-alive' ) or response.getheader('connection', '') == 'close': connection.close() connection = None
def request(self, connection, url, headers, data=None, timeout=0): result = response = None try: if data: connection.request('POST', url, data, headers) else: connection.request('GET', url, headers=headers) response = self.timeout_response(connection, timeout) if not response: return None if response.status == http.client.UNAUTHORIZED: say_line('Wrong username or password for %s', self.server().name) self.authorization_failed = True raise NotAuthorized() r = self.max_redirects while response.status == http.client.TEMPORARY_REDIRECT: response.read() url = response.getheader('Location', '') if r == 0 or url == '': raise http.client.HTTPException( 'Too much or bad redirects') connection.request('GET', url, headers=headers) response = self.timeout_response(connection, timeout) r -= 1 self.long_poll_url = response.getheader('X-Long-Polling', '') self.switch.update_time = bool( response.getheader('X-Roll-NTime', '')) hostList = response.getheader('X-Host-List', '') self.stratum_header = response.getheader('x-stratum', '') if (not self.options.nsf) and hostList: self.switch.add_servers( loads(hostList)) result = loads(response.read()) if result['error']: say_line('server error: %s', result['error']['message']) raise RPCError(result['error']['message']) return (connection, result) finally: if not result or not response or ( response.version == 10 and response.getheader('connection', '') != 'keep-alive') or response.getheader( 'connection', '') == 'close': connection.close() connection = None
def send(self, result, send_callback): for nonce in result.miner.nonce_generator(result.nonces): h = hash(result.state, result.merkle_end, result.time, result.difficulty, nonce) if h[7] != 0: hash6 = hexlify(pack('<I', int(h[6]))) say_line('Verification failed, check hardware! (%s, %s)', (result.miner.id(), hash6)) return True # consume this particular result else: self.diff1_found(bytereverse(h[6]), result.target[6]) if belowOrEquals(h[:7], result.target[:7]): is_block = belowOrEquals(h[:7], self.true_target[:7]) hash6 = hexlify(pack('<I', int(h[6]))) hash5 = hexlify(pack('<I', int(h[5]))) self.sent[nonce] = (is_block, hash6, hash5) if not send_callback(result, nonce): return False return True
def generation_tx_for_template(self, template): template_tx = template.get('coinbasetxn') # In segwit mode, we need another merkle root that has hashed witness # portions of txes: witness_commitment = unhexlify(template['default_witness_commitment']) # TODO: use the 'hash' attrs if this is missing coinbase_msg = self.options.coinbase_msg.encode('utf-8') if template_tx: if self.options.address: if 'coinbase' in template.get('mutable', ('coinbase', )): return tx_make_generation( coinbase_msg, self.options.address, template['coinbasevalue'], template['height'], witness_commitment=witness_commitment) else: say_line( f'Warning: address {self.options.address} ignored, not allowed by work source.' ) else: if self.options.address: return tx_make_generation( coinbase_msg, self.options.address, template['coinbasevalue'], template['height'], witness_commitment=witness_commitment) else: raise Exception( 'Address not supplied by user and no coinbase tx supplied by work source.' ) return ( unhexlify(template_tx['data']), unhexlify(template_tx['txid']), unhexlify(template_tx['hash']), )
def __init__(self, options, options_encoding): self.lock = RLock() self.miners = [] self.options = options self.options_encoding = options_encoding self.last_work = 0 self.update_time = True self.max_update_time = options.max_update_time self.backup_server_index = 1 self.errors = 0 self.failback_attempt_count = 0 self.server_index = -1 self.last_server = None self.server_map = {} self.user_agent = 'apoclypsebm/' + options.version self.difficulty = 0 self.true_target = None self.last_block = '' self.sent = {} if self.options.proxy: self.options.proxy = self.parse_server(self.options.proxy, False) self.parse_proxy(self.options.proxy) self.servers = [] for server in self.options.servers: try: self.servers.append(self.parse_server(server)) except ValueError: if self.options.verbose: say_exception() say_line("Ignored invalid server entry: %s", server) continue
def proposeblock(self, block_data, work_id=None): try: self.connection = \ self.ensure_connected(self.connection, self.server().proto, self.server().host)[0] param = { 'mode': 'proposal', 'data': block_data } if work_id: param['workid'] = work_id postdata = { 'method': 'getblocktemplate', 'id': 'json', 'params': (param,) } with open('last_submission.txt', 'w') as submission_file: submission_file.write(dumps(postdata)) (self.connection, result) = self.request(self.connection, '/', self.headers, dumps(postdata)) self.switch.connection_ok() reject_reason = result['result'] say_line(f'proposal response: %s', reject_reason) return result['result'] except (IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): say_exception() self.stop() except Exception: say_exception()
def get_adapter_info(self): adapter_info = [] num_adapters = c_int(-1) if ADL_Adapter_NumberOfAdapters_Get(byref(num_adapters)) != ADL_OK: say_line( "ADL_Adapter_NumberOfAdapters_Get failed, cutoff temperature" "disabled for %s", self.id() ) return AdapterInfoArray = (AdapterInfo * num_adapters.value)() if ADL_Adapter_AdapterInfo_Get(cast(AdapterInfoArray, LPAdapterInfo), sizeof(AdapterInfoArray)) != ADL_OK: say_line( "ADL_Adapter_AdapterInfo_Get failed, " "cutoff temperature disabled for %s", self.id() ) return deviceAdapter = namedtuple( 'DeviceAdapter', ('AdapterIndex', 'AdapterID', 'BusNumber', 'UDID') ) devices = [] for adapter in AdapterInfoArray: index = adapter.iAdapterIndex bus_num = adapter.iBusNumber udid = adapter.strUDID adapter_id = c_int(-1) if ADL_Adapter_ID_Get(index, byref(adapter_id)) != ADL_OK: say_line( "ADL_Adapter_ID_Get failed, " "cutoff temperature disabled for %s", self.id() ) return found = False for device in devices: if device.AdapterID.value == adapter_id.value: found = True break if found is False: devices.append(deviceAdapter(index, adapter_id, bus_num, udid)) for device in devices: adapter_info.append(AdapterInfoArray[device.AdapterIndex]) return adapter_info
def server_source(self): if not hasattr(self.server(), 'source'): http_source = None if self.server().proto == 'http': from apoclypsebm.work_sources.getblocktemplate import GetblocktemplateSource http_source = GetblocktemplateSource(self) elif self.server().proto == 'getwork+http': from apoclypsebm.work_sources.getwork import GetworkSource http_source = GetworkSource(self) else: self.add_stratum_source() if http_source: say_line('checking for stratum...') stratum_host = http_source.detect_stratum() if stratum_host: http_source.close_connection() self.server().proto = 'stratum' self.server().host = stratum_host self.add_stratum_source() else: self.server().source = http_source return self.server().source
def get_adapter_info(self): adapter_info = [] num_adapters = c_int(-1) if ADL_Adapter_NumberOfAdapters_Get(byref(num_adapters)) != ADL_OK: say_line( "ADL_Adapter_NumberOfAdapters_Get failed, cutoff temperature" "disabled for %s", self.id() ) return AdapterInfoArray = (AdapterInfo * num_adapters.value)() if ADL_Adapter_AdapterInfo_Get(cast(AdapterInfoArray, LPAdapterInfo), sizeof(AdapterInfoArray)) != ADL_OK: say_line( "ADL_Adapter_AdapterInfo_Get failed, " "cutoff temperature disabled for %s", self.id() ) return deviceAdapter = namedtuple( 'DeviceAdapter', ('AdapterIndex', 'AdapterID', 'BusNumber', 'UDID')) devices = [] for adapter in AdapterInfoArray: index = adapter.iAdapterIndex busNum = adapter.iBusNumber udid = adapter.strUDID adapterID = c_int(-1) if ADL_Adapter_ID_Get(index, byref(adapterID)) != ADL_OK: say_line( "ADL_Adapter_ID_Get failed, " "cutoff temperature disabled for %s", self.id() ) return found = False for device in devices: if device.AdapterID.value == adapterID.value: found = True break if found is False: devices.append(deviceAdapter(index, adapterID, busNum, udid)) for device in devices: adapter_info.append(AdapterInfoArray[device.AdapterIndex]) return adapter_info
def loop(self): self.should_stop = False self.set_server_index(0) while True: if self.should_stop: return failback = self.server_source().loop() sleep(1) if failback: say_line("Attempting to fail back to primary server") self.last_server = self.server_index self.set_server_index(0) continue if self.last_server: self.failback_attempt_count += 1 self.set_server_index(self.last_server) say_line( 'Still unable to reconnect to primary server (attempt %s), failing over', self.failback_attempt_count) self.last_server = None continue self.errors += 1 say_line('IO errors - %s, tolerance %s', (self.errors, self.options.tolerance)) if self.errors > self.options.tolerance: self.errors = 0 if self.backup_server_index >= len(self.servers): say_line( "No more backup servers left. Using primary and starting over.") new_server_index = 0 self.backup_server_index = 1 else: new_server_index = self.backup_server_index self.backup_server_index += 1 self.set_server_index(new_server_index)
def put_job(self): if self.busy: return temperature = self.get_temperature() if temperature < self.cutoff_temp: response = request(self.device, b'ZDX') if self.is_ok(response): if self.switch.update_time: self.job.time = bytereverse( uint32(int(time())) - self.job.time_delta) data = b''.join([ pack('<8I', *self.job.state), pack('<3I', self.job.merkle_end, self.job.time, self.job.difficulty) ]) response = request(self.device, b''.join([b'>>>>>>>>', data, b'>>>>>>>>'])) if self.is_ok(response): self.busy = True self.job_started = time() self.last_job = Object() self.last_job.header = self.job.header self.last_job.merkle_end = self.job.merkle_end self.last_job.time = self.job.time self.last_job.difficulty = self.job.difficulty self.last_job.target = self.job.target self.last_job.state = self.job.state self.last_job.job_id = self.job.job_id self.last_job.extranonce2 = self.job.extranonce2 self.last_job.server = self.job.server self.last_job.miner = self self.check_interval = CHECK_INTERVAL if not self.switch.update_time or bytereverse( self.job.time) - bytereverse( self.job.original_time) > 55: self.update = True self.job = None else: say_line('%s: bad response when sending block data: %s', (self.id(), response)) else: say_line('%s: bad response when submitting job (ZDX): %s', (self.id(), response)) else: say_line('%s: temperature exceeds cutoff, waiting...', self.id())
def put_job(self): if self.busy: return temperature = self.get_temperature() if temperature < self.cutoff_temp: response = request(self.device, b'ZDX') if self.is_ok(response): if self.switch.update_time: self.job.time = bytereverse( uint32(int(time())) - self.job.time_delta) data = b''.join([pack('<8I', *self.job.state), pack('<3I', self.job.merkle_end, self.job.time, self.job.difficulty)]) response = request(self.device, b''.join([b'>>>>>>>>', data, b'>>>>>>>>'])) if self.is_ok(response): self.busy = True self.job_started = time() self.last_job = Object() self.last_job.header = self.job.header self.last_job.merkle_end = self.job.merkle_end self.last_job.time = self.job.time self.last_job.difficulty = self.job.difficulty self.last_job.target = self.job.target self.last_job.state = self.job.state self.last_job.job_id = self.job.job_id self.last_job.extranonce2 = self.job.extranonce2 self.last_job.server = self.job.server self.last_job.miner = self self.check_interval = CHECK_INTERVAL if not self.switch.update_time or bytereverse( self.job.time) - bytereverse( self.job.original_time) > 55: self.update = True self.job = None else: say_line('%s: bad response when sending block data: %s', (self.id(), response)) else: say_line('%s: bad response when submitting job (ZDX): %s', (self.id(), response)) else: say_line('%s: temperature exceeds cutoff, waiting...', self.id())
def detect_stratum(self): work = self.getwork() if self.authorization_failed: return False if work: if self.stratum_header: host = self.stratum_header proto = host.find('://') if proto != -1: host = self.stratum_header[proto + 3:] # this doesn't work in windows/python 2.6 # host = urlparse.urlparse(self.stratum_header).netloc say_line('diverted to stratum on %s', host) return host else: say_line('using JSON-RPC (no stratum header)') self.queue_work(work) return False say_line('no response to getwork, using as stratum') return self.server().host
def detect_stratum(self): work = self.getwork() if self.authorization_failed: return False if work: if self.stratum_header: host = self.stratum_header proto = host.find('://') if proto != -1: host = self.stratum_header[proto + 3:] # this doesn't work in windows/python 2.6 # host = urlparse.urlparse(self.stratum_header).netloc say_line('diverted to stratum on %s', host) return host else: say_line('using JSON-RPC (no stratum header)') self.queue_work(work) return False say_line('no response to getwork, using as stratum') return self.server().host
def handle_message(self, message): # Miner API if 'method' in message: # mining.notify if message['method'] == 'mining.notify': params = message['params'] j = Object() j.job_id = params[0] j.prevhash = params[1] j.coinbase1 = params[2] j.coinbase2 = params[3] j.merkle_branch = params[4] j.version = params[5] j.nbits = params[6] j.ntime = params[7] clear_jobs = params[8] if clear_jobs: self.jobs.clear() j.extranonce2 = self.extranonce2_size * '00' j = self.refresh_job(j) self.jobs[j.job_id] = j self.current_job = j self.queue_work(j) self.switch.connection_ok() # mining.get_version if message['method'] == 'mining.get_version': with self.send_lock: self.send_message({"error": None, "id": message['id'], "result": self.user_agent}) # mining.set_difficulty elif message['method'] == 'mining.set_difficulty': say_line("Setting new difficulty: %s", message['params'][0]) self.server_difficulty = min(MIN_DIFFICULTY, BASE_DIFFICULTY // message['params'][0]) # client.reconnect elif message['method'] == 'client.reconnect': address, port = self.server().host.split(':', 1) (new_address, new_port, timeout) = message['params'][:3] if new_address: address = new_address if new_port != None: port = new_port say_line("%s asked us to reconnect to %s:%d in %d seconds", (self.server().name, address, port, timeout)) self.server().host = address + ':' + str(port) Timer(timeout, self.reconnect).start() # client.add_peers elif message['method'] == 'client.add_peers': hosts = [{'host': host[0], 'port': host[1]} for host in message['params'][0]] self.switch.add_servers(hosts) # responses to server API requests elif 'result' in message: # response to mining.subscribe # store extranonce and extranonce2_size if message['id'] == 's': self.extranonce = message['result'][1] self.extranonce2_size = message['result'][2] self.subscribed = True # check if this is submit confirmation (message id should be in submits dictionary) # cleanup if necessary elif message['id'] in self.submits: miner, nonce = self.submits[message['id']][:2] accepted = message['result'] self.switch.report(miner, nonce, accepted) del self.submits[message['id']] if time() - self.last_submits_cleanup > 3600: now = time() for key, value in self.submits.items(): if now - value[2] > 3600: del self.submits[key] self.last_submits_cleanup = now # response to mining.authorize elif message['id'] == self.server().user: if not message['result']: say_line('authorization failed with %s:%s@%s', ( self.server().user, self.server().pwd, self.server().host)) self.authorized = False else: self.authorized = True
def reconnect(self): say_line("%s reconnecting to %s", (self.server().name, self.server().host)) self.handler.close()
def diff1_found(self, hash_, target): if self.options.verbose and target < 0xFFFF0000: say_line('checking %s <= %s', (hash_, target))
def getblocktemplate(self, long_poll_id=None, timeout=None): param = { 'capabilities': ('longpoll', 'coinbasetxn', 'coinbasevalue', 'workid'), 'rules': ('segwit', ) } try: if long_poll_id: param['longpollid'] = long_poll_id url = self.long_poll_url parsedUrl = urlsplit(url) proto = parsedUrl.scheme or self.server().proto if parsedUrl.netloc != '': host = parsedUrl.netloc url = url[url.find(host) + len(host):] if url == '': url = '/' else: host = self.server().host if host != self.long_poll_last_host: self.close_lp_connection() self.lp_connection, changed = self.ensure_connected( self.lp_connection, proto, host) connection = self.lp_connection if changed: say_line(f'LP connected to {host}') self.long_poll_last_host = host else: url = '/' self.connection, changed = \ self.ensure_connected(self.connection, self.server().proto, self.server().host) connection = self.connection if changed: say_line(f'Connected to {self.server().host}') postdata = { 'method': 'getblocktemplate', 'id': 'json', 'params': (param, ) } connection, result = self.request(connection, url, self.headers, dumps(postdata), timeout=timeout or 0) self.switch.connection_ok() return result['result'] except ConnectionResetError: # Connection resets are normal if the server hasn't heard from us # in a while. if long_poll_id: self.close_lp_connection() else: self.close_connection() except (IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): self.stop() except Exception: say_exception()
def handle_message(self, message): # Miner API if 'method' in message: # mining.notify if message['method'] == 'mining.notify': params = message['params'] j = Object() j.job_id = params[0] j.prevhash = params[1] #john j.prevhash = ''.join([ j.prevhash[i] + j.prevhash[i + 1] for i in range(0, len(j.prevhash), 2) ][::-1]) prev_block_hash_words = bytearray() for word in chunks(unhexlify(j.prevhash), 4): # Prepend because template items are in RPC byte order. prev_block_hash_words[0:0] = word j.prevhash = ''.join( ['%02x' % b for b in prev_block_hash_words]) j.coinbase1 = params[2] j.coinbase2 = params[3] j.merkle_branch = params[4] j.version = params[5] j.nbits = params[6] j.ntime = params[7] clear_jobs = params[8] if clear_jobs: self.jobs.clear() j.extranonce2 = self.extranonce2_size * '00' j = self.refresh_job(j) self.jobs[j.job_id] = j self.current_job = j self.queue_work(j) self.switch.connection_ok() # mining.get_version if message['method'] == 'mining.get_version': with self.send_lock: self.send_message({ "error": None, "id": message['id'], "result": self.user_agent }) # mining.set_difficulty elif message['method'] == 'mining.set_difficulty': say_line("Setting new difficulty: %s", message['params'][0]) self.server_difficulty = min( MIN_DIFFICULTY, int(BASE_DIFFICULTY // message['params'][0])) # client.reconnect elif message['method'] == 'client.reconnect': address, port = self.server().host.split(':', 1) (new_address, new_port, timeout) = message['params'][:3] if new_address: address = new_address if new_port is not None: port = new_port say_line("%s asked us to reconnect to %s:%d in %d seconds", (self.server().name, address, port, timeout)) self.server().host = address + ':' + str(port) Timer(timeout, self.reconnect).start() # client.add_peers elif message['method'] == 'client.add_peers': hosts = [{ 'host': host[0], 'port': host[1] } for host in message['params'][0]] self.switch.add_servers(hosts) # responses to server API requests elif 'result' in message: # response to mining.subscribe # store extranonce and extranonce2_size if message['id'] == 's': self.extranonce = message['result'][1] self.extranonce2_size = message['result'][2] self.subscribed = True # check if this is submit confirmation (message id should be in submits dictionary) # cleanup if necessary elif message['id'] in self.submits: miner, nonce = self.submits[message['id']][:2] accepted = message['result'] self.switch.report(miner, nonce, accepted) del self.submits[message['id']] if monotonic() - self.last_submits_cleanup > 3600: now = monotonic() for key, value in self.submits.items(): if now - value[2] > 3600: del self.submits[key] self.last_submits_cleanup = now # response to mining.authorize elif message['id'] == self.server().user: if not message['result']: say_line('authorization failed with %s:%s@%s', (self.server().user, self.server().pwd, self.server().host)) self.authorized = False else: self.authorized = True
def reconnect(self): say_line("%s reconnecting to %s", (self.server().name, self.server().host)) self.handler.close()
def mining_thread(self): say_line('started BFL miner on %s', (self.id())) while not self.should_stop: try: self.device = open_device(self.port) response = init_device(self.device) if not is_good_init(response): say_line( 'Failed to initialize %s (response: %s), retrying...', (self.id(), response)) self.device.close() self.device = None sleep(1) continue last_rated = time() iterations = 0 self.job = None self.busy = False while not self.should_stop: if (not self.job) or (not self.work_queue.empty()): try: self.job = self.work_queue.get(True, 1) except Empty: if not self.busy: continue else: if not self.job and not self.busy: continue targetQ = self.job.targetQ self.job.original_time = self.job.time self.job.time_delta = uint32(int( time())) - bytereverse(self.job.time) if not self.busy: self.put_job() else: result = self.check_result() if result: now = time() self.busy = False r = self.last_job job_duration = now - self.job_started self.put_job() self.min_interval = min(self.min_interval, job_duration) iterations += 4294967296 t = now - last_rated if t > self.options.rate: self.update_rate(now, iterations, t, targetQ) last_rated = now iterations = 0 if result != b'NO-NONCE\n': r.nonces = result self.switch.put(r) sleep(self.min_interval - (CHECK_INTERVAL * 2)) else: if result is None: self.check_interval = min( self.check_interval * 2, 1) sleep(self.check_interval) except Exception: say_exception() if self.device: self.device.close() self.device = None sleep(1)
def mining_thread(self): say_line('started BFL miner on %s', (self.id())) while not self.should_stop: try: self.device = open_device(self.port) response = init_device(self.device) if not is_good_init(response): say_line( 'Failed to initialize %s (response: %s), retrying...', (self.id(), response)) self.device.close() self.device = None sleep(1) continue last_rated = time() iterations = 0 self.job = None self.busy = False while not self.should_stop: if (not self.job) or (not self.work_queue.empty()): try: self.job = self.work_queue.get(True, 1) except Empty: if not self.busy: continue else: if not self.job and not self.busy: continue targetQ = self.job.targetQ self.job.original_time = self.job.time self.job.time_delta = uint32( int(time())) - bytereverse(self.job.time) if not self.busy: self.put_job() else: result = self.check_result() if result: now = time() self.busy = False r = self.last_job job_duration = now - self.job_started self.put_job() self.min_interval = min(self.min_interval, job_duration) iterations += 4294967296 t = now - last_rated if t > self.options.rate: self.update_rate(now, iterations, t, targetQ) last_rated = now; iterations = 0 if result != b'NO-NONCE\n': r.nonces = result self.switch.put(r) sleep(self.min_interval - (CHECK_INTERVAL * 2)) else: if result is None: self.check_interval = min( self.check_interval * 2, 1) sleep(self.check_interval) except Exception: say_exception() if self.device: self.device.close() self.device = None sleep(1)
def mining_thread(self): say_line('started OpenCL miner on platform %d, device %d (%s)', (self.options.platform, self.device_index, self.device_name)) (self.defines, rate_divisor, hashspace) = ( vectors_definition(), 500, 0x7FFFFFFF) if self.vectors else ( '', 1000, 0xFFFFFFFF) self.defines += (' -DOUTPUT_SIZE=' + str(self.output_size)) self.defines += (' -DOUTPUT_MASK=' + str(self.output_size - 1)) self.load_kernel() frame = 1.0 / max(self.frames, 3) unit = self.worksize * 256 global_threads = unit * 10 queue = cl.CommandQueue(self.context) last_rated_pace = last_rated = last_n_time = last_temperature = time() base = last_hash_rate = threads_run_pace = threads_run = 0 output = bytearray((self.output_size + 1) * 4) output_buffer = cl.Buffer(self.context, cl.mem_flags.WRITE_ONLY | cl.mem_flags.USE_HOST_PTR, hostbuf=output) self.kernel.set_arg(20, output_buffer) work = None temperature = 0 while True: if self.should_stop: return sleep(self.frameSleep) if (not work) or (not self.work_queue.empty()): try: work = self.work_queue.get(True, 1) except Empty: continue else: if not work: continue nonces_left = hashspace state = work.state f = [0] * 8 state2 = partial(state, work.merkle_end, work.time, work.difficulty, f) calculateF(state, work.merkle_end, work.time, work.difficulty, f, state2) set_arg = self.kernel.set_arg set_arg(0, uint32_as_bytes(state[0])) set_arg(1, uint32_as_bytes(state[1])) set_arg(2, uint32_as_bytes(state[2])) set_arg(3, uint32_as_bytes(state[3])) set_arg(4, uint32_as_bytes(state[4])) set_arg(5, uint32_as_bytes(state[5])) set_arg(6, uint32_as_bytes(state[6])) set_arg(7, uint32_as_bytes(state[7])) set_arg(8, uint32_as_bytes(state2[1])) set_arg(9, uint32_as_bytes(state2[2])) set_arg(10, uint32_as_bytes(state2[3])) set_arg(11, uint32_as_bytes(state2[5])) set_arg(12, uint32_as_bytes(state2[6])) set_arg(13, uint32_as_bytes(state2[7])) set_arg(15, uint32_as_bytes(f[0])) set_arg(16, uint32_as_bytes(f[1])) set_arg(17, uint32_as_bytes(f[2])) set_arg(18, uint32_as_bytes(f[3])) set_arg(19, uint32_as_bytes(f[4])) if temperature < self.cutoff_temp: self.kernel.set_arg(14, uint32_as_bytes(base)) cl.enqueue_nd_range_kernel(queue, self.kernel, (global_threads,), (self.worksize,)) nonces_left -= global_threads threads_run_pace += global_threads threads_run += global_threads base = uint32(base + global_threads) else: threads_run_pace = 0 last_rated_pace = time() sleep(self.cutoff_interval) now = time() if self.adapterIndex != None: t = now - last_temperature if temperature >= self.cutoff_temp or t > 1: last_temperature = now with adl_lock: temperature = self.get_temperature() t = now - last_rated_pace if t > 1: rate = (threads_run_pace / t) / rate_divisor last_rated_pace = now threads_run_pace = 0 r = last_hash_rate / rate if r < 0.9 or r > 1.1: global_threads = max( unit * int((rate * frame * rate_divisor) / unit), unit) last_hash_rate = rate t = now - last_rated if t > self.options.rate: self.update_rate(now, threads_run, t, work.targetQ, rate_divisor) last_rated = now threads_run = 0 queue.finish() cl.enqueue_copy(queue, output_buffer, output) queue.finish() if output[-1]: result = Object() result.header = work.header result.merkle_end = work.merkle_end result.time = work.time result.difficulty = work.difficulty result.target = work.target result.state = list(state) result.nonces = output[:] result.job_id = work.job_id result.extranonce2 = work.extranonce2 result.transactions = work.transactions result.server = work.server result.miner = self self.switch.put(result) output[:] = b'\x00' * len(output) cl.enqueue_copy(queue, output, output_buffer) if not self.switch.update_time: if nonces_left < 3 * global_threads * self.frames: self.update = True nonces_left += 0xFFFFFFFFFFFF elif 0xFFFFFFFFFFF < nonces_left < 0xFFFFFFFFFFFF: say_line('warning: job finished, %s is idle', self.id()) work = None elif now - last_n_time > 1: work.time = bytereverse(bytereverse(work.time) + 1) state2 = partial(state, work.merkle_end, work.time, work.difficulty, f) calculateF(state, work.merkle_end, work.time, work.difficulty, f, state2) set_arg = self.kernel.set_arg set_arg(8, uint32_as_bytes(state2[1])) set_arg(9, uint32_as_bytes(state2[2])) set_arg(10, uint32_as_bytes(state2[3])) set_arg(11, uint32_as_bytes(state2[5])) set_arg(12, uint32_as_bytes(state2[6])) set_arg(13, uint32_as_bytes(state2[7])) set_arg(15, uint32_as_bytes(f[0])) set_arg(16, uint32_as_bytes(f[1])) set_arg(17, uint32_as_bytes(f[2])) set_arg(18, uint32_as_bytes(f[3])) set_arg(19, uint32_as_bytes(f[4])) last_n_time = now self.update_time_counter += 1 if self.update_time_counter >= self.switch.max_update_time: self.update = True self.update_time_counter = 1
def getblocktemplate(self, long_poll_id=None, timeout=None): param = { 'capabilities': ('longpoll', 'coinbasetxn', 'coinbasevalue', 'workid'), 'rules': ('segwit',) } try: if long_poll_id: param['longpollid'] = long_poll_id url = self.long_poll_url parsedUrl = urlsplit(url) proto = parsedUrl.scheme or self.server().proto if parsedUrl.netloc != '': host = parsedUrl.netloc url = url[url.find(host) + len(host):] if url == '': url = '/' else: host = self.server().host if host != self.long_poll_last_host: self.close_lp_connection() self.lp_connection, changed = self.ensure_connected( self.lp_connection, proto, host) connection = self.lp_connection if changed: say_line(f"LP connected to {host}") self.long_poll_last_host = host else: url = '/' self.connection, changed = \ self.ensure_connected(self.connection, self.server().proto, self.server().host) connection = self.connection if changed: say_line( f"Connected to {self.server().host}") postdata = { 'method': 'getblocktemplate', 'id': 'json', 'params': (param,) } connection, result = self.request(connection, url, self.headers, dumps(postdata), timeout=timeout or 0) self.switch.connection_ok() return result['result'] except ConnectionResetError: # Connection resets are normal if the server hasn't heard from us # in a while. if long_poll_id: self.close_lp_connection() else: self.close_connection() except (IOError, http.client.HTTPException, ValueError, socks.ProxyError, NotAuthorized, RPCError): self.stop() except Exception: say_exception()
def mining_thread(self): say_line('started OpenCL miner on platform %d, device %d (%s)', (self.options.platform, self.device_idx, self.device_name)) self.defines, rate_divisor, hashspace = ( '-D VECTORS', 500, 0x7FFFFFFF ) if self.vectors else ( '', 1000, 0xFFFFFFFF ) self.defines += ( f' -D OUTPUT_SIZE={self.output_size}' f' -D OUTPUT_MASK={self.output_size - 1}' ) self.load_kernel() frame = 1.0 / max(self.frames, 3) unit = self.worksize * 256 global_threads = unit * 10 queue = cl.CommandQueue(self.context) last_rated_pace = last_rated = last_n_time = last_temperature = monotonic() base = last_hash_rate = threads_run_pace = threads_run = 0 blank_output = b'\x00' * ((self.output_size + 1) * 4) host_output = bytearray(blank_output) cl_output = cl.Buffer( self.context, cl.mem_flags.WRITE_ONLY, size=len(host_output) ) cl.enqueue_copy(queue, cl_output, blank_output) self.kernel.set_arg(20, cl_output) work = None temperature = 0 while True: if self.should_stop: return sleep(self.frame_sleep) if (not work) or (not self.work_queue.empty()): try: work = self.work_queue.get(True, 1) except Empty: continue else: if not work: continue nonces_left = hashspace state = work.state f = [0, 0, 0, 0, 0, 0, 0, 0] state2 = partial(state, work.merkle_end, work.time, work.difficulty, f) calculateF(state, work.merkle_end, work.time, work.difficulty, f, state2) set_arg = self.kernel.set_arg set_arg(0, uint32_as_bytes(state[0])) set_arg(1, uint32_as_bytes(state[1])) set_arg(2, uint32_as_bytes(state[2])) set_arg(3, uint32_as_bytes(state[3])) set_arg(4, uint32_as_bytes(state[4])) set_arg(5, uint32_as_bytes(state[5])) set_arg(6, uint32_as_bytes(state[6])) set_arg(7, uint32_as_bytes(state[7])) set_arg(8, uint32_as_bytes(state2[1])) set_arg(9, uint32_as_bytes(state2[2])) set_arg(10, uint32_as_bytes(state2[3])) set_arg(11, uint32_as_bytes(state2[5])) set_arg(12, uint32_as_bytes(state2[6])) set_arg(13, uint32_as_bytes(state2[7])) set_arg(15, uint32_as_bytes(f[0])) set_arg(16, uint32_as_bytes(f[1])) set_arg(17, uint32_as_bytes(f[2])) set_arg(18, uint32_as_bytes(f[3])) set_arg(19, uint32_as_bytes(f[4])) if temperature < self.cutoff_temp: self.kernel.set_arg(14, uint32_as_bytes(base)) cl.enqueue_nd_range_kernel(queue, self.kernel, (global_threads,), self.execution_local_dims) nonces_left -= global_threads threads_run_pace += global_threads threads_run += global_threads base = uint32(base + global_threads) else: threads_run_pace = 0 last_rated_pace = monotonic() sleep(self.cutoff_interval) now = monotonic() if self.adapter_idx is not None: t = now - last_temperature if temperature >= self.cutoff_temp or t > 1: last_temperature = now with adl_lock: temperature = self.get_temperature() t = now - last_rated_pace if t > 1: rate = (threads_run_pace / t) / rate_divisor last_rated_pace = now threads_run_pace = 0 r = last_hash_rate / rate if r < 0.9 or r > 1.1: global_threads = max( unit * int((rate * frame * rate_divisor) / unit), unit) last_hash_rate = rate t = now - last_rated if t > self.options.rate: self.update_rate(now, threads_run, t, work.targetQ, rate_divisor) last_rated = now threads_run = 0 cl.enqueue_copy(queue, host_output, cl_output) queue.finish() if host_output[-1]: result = Object() result.header = work.header result.merkle_end = work.merkle_end result.time = work.time result.difficulty = work.difficulty result.target = work.target result.state = tuple(state) result.nonces = host_output[:] result.job_id = work.job_id result.extranonce2 = work.extranonce2 result.transactions = work.transactions result.server = work.server result.miner = self self.switch.put(result) cl.enqueue_copy(queue, cl_output, blank_output) if not self.switch.update_time: if nonces_left < 3 * global_threads * self.frames: self.update = True nonces_left += 0xFFFFFFFFFFFF elif 0xFFFFFFFFFFF < nonces_left < 0xFFFFFFFFFFFF: say_line('warning: job finished, %s is idle', self.id()) work = None elif now - last_n_time > 1: work.time = bytereverse(bytereverse(work.time) + 1) state2 = partial(state, work.merkle_end, work.time, work.difficulty, f) calculateF(state, work.merkle_end, work.time, work.difficulty, f, state2) set_arg = self.kernel.set_arg set_arg(8, uint32_as_bytes(state2[1])) set_arg(9, uint32_as_bytes(state2[2])) set_arg(10, uint32_as_bytes(state2[3])) set_arg(11, uint32_as_bytes(state2[5])) set_arg(12, uint32_as_bytes(state2[6])) set_arg(13, uint32_as_bytes(state2[7])) set_arg(15, uint32_as_bytes(f[0])) set_arg(16, uint32_as_bytes(f[1])) set_arg(17, uint32_as_bytes(f[2])) set_arg(18, uint32_as_bytes(f[3])) set_arg(19, uint32_as_bytes(f[4])) last_n_time = now self.update_time_counter += 1 if self.update_time_counter >= self.switch.max_update_time: self.update = True self.update_time_counter = 1