def update_pool(conn): try: # request local memory pool and load it in bt = conn.getblocktemplate({'capabilities': [ 'coinbasevalue', 'coinbase/append', 'coinbase', 'generation', 'time', 'transactions/remove', 'prevblock', ]}) except Exception: logger.warn("Failed to fetch new job, RPC must be down..") down_connection(conn, net_state) return False dirty = 0 # track a change in the transaction pool for trans in bt['transactions']: if trans['hash'] not in net_state['transactions']: dirty += 1 new_trans = Transaction(unhexlify(trans['data']), fees=trans['fee']) assert trans['hash'] == new_trans.lehexhash net_state['transactions'][trans['hash']] = new_trans if dirty or len(net_state['jobs']) == 0: # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (config['extranonce_size'] + config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(bt['height'], b'\0' * extranonce_length)) # simple output to the proper address and value fees = 0 for t in net_state['transactions'].itervalues(): fees += t.fees coinbase.outputs.append( Output.to_address(bt['coinbasevalue'] - fees, config['pool_address'])) job_id = hexlify(pack(str("I"), net_state['job_counter'])) logger.info("Generating new block template with {} trans" .format(len(net_state['transactions']))) bt_obj = BlockTemplate.from_gbt(bt, coinbase, extranonce_length, []) bt_obj.job_id = job_id bt_obj.block_height = bt['height'] bt_obj.acc_shares = set() net_state['job_counter'] += 1 net_state['jobs'][job_id] = bt_obj net_state['latest_job'] = job_id logger.debug("Adding {} new transactions to transaction pool, " "created job {}".format(dirty, job_id)) return bt_obj
def test_coinbase(self): coinbase = Transaction() coinbase.version = 2 coinbase.inputs.append(Input.coinbase(12000, b'\0' * 6)) coinbase.outputs.append( Output.to_address(50000, 'D7QJyeBNuwEqxsyVCLJi3pHs64uPdMDuBa')) one, two = coinbase.assemble(split=True) one = one[:-6] test = Transaction(one + unhexlify('0abcdef01012') + two) test.disassemble()
def test_block_template(): gbt = {u'coinbaseaux': {u'flags': u''}, u'vbavailable': {}, u'previousblockhash': u'00000061786317587bcfe97516cd00a541962334779d60efdb7c4efeb670ac77', u'target': u'7fffff0000000000000000000000000000000000000000000000000000000000', u'noncerange': u'00000000ffffffff', u'transactions': [], u'rules': [], u'vbrequired': 0, u'curtime': 1500584586, u'capabilities': [u'proposal'], u'height': 16, 'update_time': 1500584586.899153, u'mintime': 1500584329, u'version': 536870912, u'bits': u'207fffff', u'coinbasevalue': 5000000000, u'sigoplimit': 20000, u'sizelimit': 1000000, u'mutable': [u'time', u'transactions', u'prevblock'], u'longpollid': u'00000061786317587bcfe97516cd00a541962334779d60efdb7c4efeb670ac7718'} extranonce_length = 4 coinbase = Transaction() coinbase.version = 2 coinbase.inputs.append( Input.coinbase(gbt['height'], [], extra_script_sig=b'\0' * extranonce_length)) coinbase_value = 10000000 coinbase.outputs.append(Output.to_address(coinbase_value, "1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX")) bt_obj = BlockTemplate.from_gbt(gbt, coinbase, extranonce_length, [])
def test_stratum_confirm(self): """ Test some raw data from cgminer submitting a share, confirm hashes come out the same as cgminer. Raw stratum params: """ gbt = {u'bits': u'1e00e92b', u'coinbaseaux': {u'flags': u'062f503253482f'}, u'coinbasevalue': 5000000000, u'curtime': 1392509565, u'height': 203588, u'mintime': 1392508633, u'mutable': [u'time', u'transactions', u'prevblock'], u'noncerange': u'00000000ffffffff', u'previousblockhash': u'b0f5ecb62774f2f07fdc0f72fa0585ae3e8ca78ad8692209a355d12bc690fb73', u'sigoplimit': 20000, u'sizelimit': 1000000, u'target': u'000000e92b000000000000000000000000000000000000000000000000000000', u'transactions': [], u'version': 2} extra1 = '0000000000000000' submit = {'extra2': '00000000', 'nonce': 'd5160000', 'result': '000050ccfe8a3efe93b2ee33d2aecf4a60c809995c7dd19368a7d00c86880f30'} # build a block template object from the raw data coinbase = Transaction() coinbase.version = 2 coinbase.inputs.append(Input.coinbase(gbt['height'], b'\0' * 12)) coinbase.outputs.append(Output.to_address(gbt['coinbasevalue'], 'D7QJyeBNuwEqxsyVCLJi3pHs64uPdMDuBa')) transactions = [] for trans in gbt['transactions']: new_trans = Transaction(unhexlify(trans['data']), fees=trans['fee']) assert trans['hash'] == new_trans.lehexhash transactions.append(new_trans) bt = BlockTemplate.from_gbt(gbt, coinbase, 12, transactions) send_params = bt.stratum_params() print("job_id: {0}\nprevhash: {1}\ncoinbase1: {2}\ncoinbase2: {3}" "\nmerkle_branch: {4}\nversion: {5}\nnbits: {6}\nntime: {7}" .format(*send_params)) header = bt.block_header(submit['nonce'], extra1, submit['extra2']) hash_bin = scrypt(header) target = target_from_diff(1, 0x0000FFFF00000000000000000000000000000000000000000000000000000000) hash_int = uint256_from_str(hash_bin) hash_hex = "%064x" % hash_int self.assertEquals(hash_hex, submit['result']) assert hash_int < target
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.merged_work: tree, size = bitcoin_data.make_auxpow_tree(self.merged_work) mm_hashes = [ self.merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size) ] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack( dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) merged_data = {} for aux_work in self.merged_work.itervalues(): data = dict(target=aux_work['target'], hash=aux_work['hash'], height=aux_work['height'], index=mm_hashes.index(aux_work['hash']), type=aux_work['type'], hashes=mm_hashes) merged_data[aux_work['type']] = data else: merged_data = {} mm_data = None self.logger.info("Generating new block template with {} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. " "Merged chains: {}".format( len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(merged_data.keys()))) # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # Darkcoin payee amount if self._last_gbt.get('payee', '') != '': payout = self._last_gbt['coinbasevalue'] / 5 self._last_gbt['coinbasevalue'] -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.info( "Paying out masternode at addr {}. Payout {}. Blockval reduced to {}" .format(self._last_gbt['payee'], payout, self._last_gbt['coinbasevalue'])) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self._last_gbt['coinbasevalue'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt( self._last_gbt, coinbase, extranonce_length, [ Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions'] ]) # add in our merged mining data if mm_data: hashes = [ bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions ] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = merged_data bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: self.logger.info("New work announced! Wiping previous jobs...") self.jobs.clear() self.latest_job = None else: self.logger.info("New work announced!") self._job_counter += 1 self.jobs[job_id] = bt_obj self.latest_job = job_id if push: t = time.time() bt_obj.stratum_string() for idx, client in viewitems(self.stratum_manager.clients): try: if client.authenticated: client._push(bt_obj, flush=flush) except AttributeError: pass self.logger.info( "New job enqueued for transmission to {} users in {}".format( len(self.stratum_manager.clients), time_format(time.time() - t))) if flush: self.server['work_restarts'].incr() self.server['work_pushes'].incr() self.server['new_jobs'].incr() if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions)
def generate_job(self, push=False, flush=False, new_block=False, network='main'): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.auxmons: merged_work = {} auxdata = {} for auxmon in self.auxmons: # If this network hasn't pushed a job yet, skip it if auxmon.last_work['hash'] is None: continue merged_work[auxmon.last_work['chainid']] = dict( hash=auxmon.last_work['hash'], target=auxmon.last_work['type']) tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [ merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size) ] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack( dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) for auxmon in self.auxmons: if auxmon.last_work['hash'] is None: continue data = dict(target=auxmon.last_work['target'], hash=auxmon.last_work['hash'], height=auxmon.last_work['height'], found_block=auxmon.found_block, index=mm_hashes.index(auxmon.last_work['hash']), type=auxmon.last_work['type'], hashes=mm_hashes) auxdata[auxmon.config['currency']] = data else: auxdata = {} mm_data = None # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.manager.config['extranonce_size'] + self.manager.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) coinbase_value = self._last_gbt['coinbasevalue'] # Payout Darkcoin masternodes mn_enforcement = self._last_gbt.get('enforce_masternode_payments', True) if (self.config['payout_drk_mn'] is True or mn_enforcement is True) \ and self._last_gbt.get('payee', '') != '': # Grab the darkcoin payout amount, default to 20% payout = self._last_gbt.get('payee_amount', coinbase_value / 5) coinbase_value -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.debug( "Created TX output for masternode at ({}:{}). Coinbase value " "reduced to {}".format(self._last_gbt['payee'], payout, coinbase_value)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(coinbase_value, self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt( self._last_gbt, coinbase, extranonce_length, [ Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions'] ]) # add in our merged mining data if mm_data: hashes = [ bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions ] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = auxdata bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.currency = self.config['currency'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if flush: bt_obj.type = 0 elif push: bt_obj.type = 1 else: bt_obj.type = 2 bt_obj.found_block = self.found_block # Push the fresh job to users after updating details self._job_counter += 1 if flush: self.jobs.clear() self.jobs[job_id] = bt_obj self.latest_job = bt_obj self.new_job.job = bt_obj self.new_job.set() self.new_job.clear() event = ("{name}.jobmanager.new_job:1|c\n".format( name=self.manager.config['procname'])) if push or flush: self.logger.info( "{}: New block template with {:,} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. Merged: {}". format("FLUSH" if flush else "PUSH", len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(auxdata.keys()))) event += ("{name}.jobmanager.work_push:1|c\n".format( name=self.manager.config['procname'])) # Stats and notifications now that it's pushed if flush: event += ("{name}.jobmanager.work_restart:1|c\n".format( name=self.manager.config['procname'])) self.logger.info("New {} network block announced! Wiping previous" " jobs and pushing".format(network)) elif push: self.logger.info( "New {} network block announced, pushing new job!".format( network)) if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['last_block'] = time.time() self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions) event += ("{name}.{curr}.difficulty:{diff}|g\n" "{name}.{curr}.subsidy:{subsidy}|g\n" "{name}.{curr}.job_generate:{t}|g\n" "{name}.{curr}.height:{height}|g".format( name=self.manager.config['procname'], curr=self.config['currency'], diff=self.current_net['difficulty'], subsidy=bt_obj.total_value, height=bt_obj.block_height - 1, t=(time.time() - self._last_gbt['update_time']) * 1000)) self.manager.log_event(event)
def generate_job(self, push=False, flush=False, new_block=False, network='main'): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.auxmons: merged_work = {} auxdata = {} for auxmon in self.auxmons: if auxmon.last_work['hash'] is None: continue merged_work[auxmon.last_work['chainid']] = dict( hash=auxmon.last_work['hash'], target=auxmon.last_work['type'] ) tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size)] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack(dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) for auxmon in self.auxmons: if auxmon.last_work['hash'] is None: continue data = dict(target=auxmon.last_work['target'], hash=auxmon.last_work['hash'], height=auxmon.last_work['height'], found_block=auxmon.found_block, index=mm_hashes.index(auxmon.last_work['hash']), type=auxmon.last_work['type'], hashes=mm_hashes) auxdata[auxmon.config['currency']] = data else: auxdata = {} mm_data = None # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.manager.config['extranonce_size'] + self.manager.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) coinbase_value = self._last_gbt['coinbasevalue'] # Payout Darkcoin masternodes mn_enforcement = self._last_gbt.get('enforce_masternode_payments', True) if (self.config['payout_drk_mn'] is True or mn_enforcement is True) \ and self._last_gbt.get('payee', '') != '': # Grab the darkcoin payout amount, default to 20% payout = self._last_gbt.get('payee_amount', coinbase_value / 5) coinbase_value -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.debug( "Created TX output for masternode at ({}:{}). Coinbase value " "reduced to {}".format(self._last_gbt['payee'], payout, coinbase_value)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(coinbase_value, self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt(self._last_gbt, coinbase, extranonce_length, [Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions']]) # add in our merged mining data if mm_data: hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = auxdata bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.currency = self.config['currency'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if flush: bt_obj.type = 0 elif push: bt_obj.type = 1 else: bt_obj.type = 2 bt_obj.found_block = self.found_block # Push the fresh job to users after updating details self._job_counter += 1 if flush: self.jobs.clear() self.jobs[job_id] = bt_obj self.latest_job = bt_obj self.new_job.job = bt_obj self.new_job.set() self.new_job.clear() event = ("{name}.jobmanager.new_job:1|c\n" .format(name=self.manager.config['procname'])) if push or flush: self.logger.info( "{}: New block template with {:,} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. Merged: {}" .format("FLUSH" if flush else "PUSH", len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(auxdata.keys()))) event += ("{name}.jobmanager.work_push:1|c\n" .format(name=self.manager.config['procname'])) # Stats and notifications now that it's pushed if flush: event += ("{name}.jobmanager.work_restart:1|c\n" .format(name=self.manager.config['procname'])) self.logger.info("New {} network block announced! Wiping previous" " jobs and pushing".format(network)) elif push: self.logger.info("New {} network block announced, pushing new job!" .format(network)) if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['last_block'] = time.time() self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions) event += ( "{name}.{curr}.difficulty:{diff}|g\n" "{name}.{curr}.subsidy:{subsidy}|g\n" "{name}.{curr}.job_generate:{t}|g\n" "{name}.{curr}.height:{height}|g" .format(name=self.manager.config['procname'], curr=self.config['currency'], diff=self.current_net['difficulty'], subsidy=bt_obj.total_value, height=bt_obj.block_height - 1, t=(time.time() - self._last_gbt['update_time']) * 1000)) self.manager.log_event(event)
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if not self._last_gbt: self.logger.warn("Cannot generate new job, missing last GBT info") return if self.merged_work: tree, size = bitcoin_data.make_auxpow_tree(self.merged_work) mm_hashes = [self.merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size)] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack(dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) merged_data = {} for aux_work in self.merged_work.itervalues(): data = dict(target=aux_work['target'], hash=aux_work['hash'], height=aux_work['height'], index=mm_hashes.index(aux_work['hash']), type=aux_work['type'], hashes=mm_hashes) merged_data[aux_work['type']] = data else: merged_data = {} mm_data = None self.logger.info("Generating new block template with {} trans. " "Diff {:,.4f}. Subsidy {:,.2f}. Height {:,}. " "Merged chains: {}" .format(len(self._last_gbt['transactions']), bits_to_difficulty(self._last_gbt['bits']), self._last_gbt['coinbasevalue'] / 100000000.0, self._last_gbt['height'], ', '.join(merged_data.keys()))) # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self._last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # Darkcoin payee amount if self._last_gbt.get('payee', '') != '': payout = self._last_gbt['coinbasevalue'] / 5 self._last_gbt['coinbasevalue'] -= payout coinbase.outputs.append( Output.to_address(payout, self._last_gbt['payee'])) self.logger.info("Paying out masternode at addr {}. Payout {}. Blockval reduced to {}" .format(self._last_gbt['payee'], payout, self._last_gbt['coinbasevalue'])) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self._last_gbt['coinbasevalue'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self._job_counter)) bt_obj = BlockTemplate.from_gbt(self._last_gbt, coinbase, extranonce_length, [Transaction(unhexlify(t['data']), fees=t['fee']) for t in self._last_gbt['transactions']]) # add in our merged mining data if mm_data: hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.merged_data = merged_data bt_obj.job_id = job_id bt_obj.diff1 = self.config['diff1'] bt_obj.algo = self.config['algo'] bt_obj.pow_block_hash = self.config['pow_block_hash'] bt_obj.block_height = self._last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: self.logger.info("New work announced! Wiping previous jobs...") self.jobs.clear() self.latest_job = None else: self.logger.info("New work announced!") self._job_counter += 1 self.jobs[job_id] = bt_obj self.latest_job = job_id if push: t = time.time() bt_obj.stratum_string() for idx, client in viewitems(self.stratum_manager.clients): try: if client.authenticated: client._push(bt_obj, flush=flush) except AttributeError: pass self.logger.info("New job enqueued for transmission to {} users in {}" .format(len(self.stratum_manager.clients), time_format(time.time() - t))) if flush: self.server['work_restarts'].incr() self.server['work_pushes'].incr() self.server['new_jobs'].incr() if new_block: hex_bits = hexlify(bt_obj.bits) self.current_net['difficulty'] = bits_to_difficulty(hex_bits) self.current_net['subsidy'] = bt_obj.total_value self.current_net['height'] = bt_obj.block_height - 1 self.current_net['prev_hash'] = bt_obj.hashprev_be_hex self.current_net['transactions'] = len(bt_obj.transactions)
def test_stratum_confirm(self): """ Test some raw data from cgminer submitting a share, confirm hashes come out the same as cgminer. Raw stratum params: """ gbt = { u'bits': u'1e00e92b', u'coinbaseaux': { u'flags': u'062f503253482f' }, u'coinbasevalue': 5000000000, u'curtime': 1392509565, u'height': 203588, u'mintime': 1392508633, u'mutable': [u'time', u'transactions', u'prevblock'], u'noncerange': u'00000000ffffffff', u'previousblockhash': u'b0f5ecb62774f2f07fdc0f72fa0585ae3e8ca78ad8692209a355d12bc690fb73', u'sigoplimit': 20000, u'sizelimit': 1000000, u'target': u'000000e92b000000000000000000000000000000000000000000000000000000', u'transactions': [], u'version': 2 } extra1 = '0000000000000000' submit = { 'extra2': '00000000', 'nonce': 'd5160000', 'result': '000050ccfe8a3efe93b2ee33d2aecf4a60c809995c7dd19368a7d00c86880f30' } # build a block template object from the raw data coinbase = Transaction() coinbase.version = 2 coinbase.inputs.append(Input.coinbase(gbt['height'], [b'\0' * 12])) coinbase.outputs.append( Output.to_address(gbt['coinbasevalue'], 'D7QJyeBNuwEqxsyVCLJi3pHs64uPdMDuBa')) transactions = [] for trans in gbt['transactions']: new_trans = Transaction(unhexlify(trans['data']), fees=trans['fee']) assert trans['hash'] == new_trans.lehexhash transactions.append(new_trans) bt = BlockTemplate.from_gbt(gbt, coinbase, 12, transactions) send_params = bt.stratum_params() print( "job_id: {0}\nprevhash: {1}\ncoinbase1: {2}\ncoinbase2: {3}" "\nmerkle_branch: {4}\nversion: {5}\nnbits: {6}\nntime: {7}". format(*send_params)) header = bt.block_header(submit['nonce'], extra1, submit['extra2']) target = target_from_diff( 1, 0x0000FFFF00000000000000000000000000000000000000000000000000000000) self.assertEquals( hexlify(sha256d(header)[::-1]).decode('ascii'), submit['result']) assert hash_int < target
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if self.last_gbt is None: return merged_work = self.net_state['merged_work'] if self.net_state['merged_work']: tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size)] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack(dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) mm_later = [(aux_work, mm_hashes.index(aux_work['hash']), mm_hashes) for chain_id, aux_work in merged_work.iteritems()] else: mm_later = [] mm_data = None with self.job_lock: # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self.last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self.last_gbt['coinbasevalue'] - self.last_gbt['fees'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self.net_state['job_counter'])) logger.info("Generating new block template with {} trans. Diff {}. Subsidy {}. Fees {}." .format(len(self.net_state['transactions']), bits_to_difficulty(self.last_gbt['bits']), self.last_gbt['coinbasevalue'], self.last_gbt['fees'])) bt_obj = BlockTemplate.from_gbt(self.last_gbt, coinbase, extranonce_length, []) bt_obj.mm_later = copy(mm_later) # hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None], 0) bt_obj.job_id = job_id bt_obj.block_height = self.last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: logger.info("New work announced! Wiping previous jobs...") self.net_state['jobs'].clear() self.net_state['latest_job'] = None else: logger.info("New work announced!") self.net_state['job_counter'] += 1 self.net_state['jobs'][job_id] = bt_obj self.net_state['latest_job'] = job_id if push: for idx, client in viewitems(self.stratum_clients): try: if flush: client.new_block_event.set() else: client.new_work_event.set() except AttributeError: pass if new_block: if self.config['send_new_block']: hex_bits = hexlify(bt_obj.bits) self.celery.send_task_pp('new_block', bt_obj.block_height, hex_bits, bt_obj.total_value) self.net_state['difficulty'] = bits_to_difficulty(hex_bits)
def generate_job(self, push=False, flush=False, new_block=False): """ Creates a new job for miners to work on. Push will trigger an event that sends new work but doesn't force a restart. If flush is true a job restart will be triggered. """ # aux monitors will often call this early when not needed at startup if self.last_gbt is None: return merged_work = self.net_state['merged_work'] if self.net_state['merged_work']: tree, size = bitcoin_data.make_auxpow_tree(merged_work) mm_hashes = [ merged_work.get(tree.get(i), dict(hash=0))['hash'] for i in xrange(size) ] mm_data = '\xfa\xbemm' mm_data += bitcoin_data.aux_pow_coinbase_type.pack( dict( merkle_root=bitcoin_data.merkle_hash(mm_hashes), size=size, nonce=0, )) mm_later = [(aux_work, mm_hashes.index(aux_work['hash']), mm_hashes) for chain_id, aux_work in merged_work.iteritems()] else: mm_later = [] mm_data = None # here we recalculate the current merkle branch and partial # coinbases for passing to the mining clients coinbase = Transaction() coinbase.version = 2 # create a coinbase input with encoded height and padding for the # extranonces so script length is accurate extranonce_length = (self.config['extranonce_size'] + self.config['extranonce_serv_size']) coinbase.inputs.append( Input.coinbase(self.last_gbt['height'], addtl_push=[mm_data] if mm_data else [], extra_script_sig=b'\0' * extranonce_length)) # simple output to the proper address and value coinbase.outputs.append( Output.to_address(self.last_gbt['coinbasevalue'], self.config['pool_address'])) job_id = hexlify(struct.pack(str("I"), self.net_state['job_counter'])) logger.info( "Generating new block template with {} trans. Diff {}. Subsidy {}." .format(len(self.last_gbt['transactions']), bits_to_difficulty(self.last_gbt['bits']), self.last_gbt['coinbasevalue'])) bt_obj = BlockTemplate.from_gbt( self.last_gbt, coinbase, extranonce_length, [ Transaction(unhexlify(t['data']), fees=t['fee']) for t in self.last_gbt['transactions'] ]) bt_obj.mm_later = copy(mm_later) hashes = [bitcoin_data.hash256(tx.raw) for tx in bt_obj.transactions] bt_obj.merkle_link = bitcoin_data.calculate_merkle_link([None] + hashes, 0) bt_obj.job_id = job_id bt_obj.block_height = self.last_gbt['height'] bt_obj.acc_shares = set() if push: if flush: logger.info("New work announced! Wiping previous jobs...") self.net_state['jobs'].clear() self.net_state['latest_job'] = None else: logger.info("New work announced!") self.net_state['job_counter'] += 1 self.net_state['jobs'][job_id] = bt_obj self.net_state['latest_job'] = job_id if push: for idx, client in viewitems(self.stratum_clients): try: if flush: client.new_block_event.set() else: client.new_work_event.set() except AttributeError: pass if new_block: hex_bits = hexlify(bt_obj.bits) self.net_state['work']['difficulty'] = bits_to_difficulty(hex_bits) if self.config['send_new_block']: self.celery.send_task_pp('new_block', bt_obj.block_height, hex_bits, bt_obj.total_value)