def submit(self, worker_name, password, data): session = self.connection_ref().get_session() session.setdefault('authorized', {}) # Check if worker is authorized to submit shares ip = self.connection_ref()._get_ip() if not Interfaces.worker_manager.authorize( worker_name, session['authorized'].get(worker_name)): log.info("Worker is not authorized: IP %s" % str(ip)) raise SubmitException("Worker is not authorized") # Check if extranonce1 is in connection session extranonce1_bin = session.get('extranonce1', None) if not extranonce1_bin: log.info("Connection is not subscribed for mining: IP %s" % str(ip)) raise SubmitException("Connection is not subscribed for mining") # Get current block job_id difficulty = session['difficulty'] basediff = session['basediff'] merkle_root = data[72:136] if extranonce1_bin in Interfaces.worker_manager.job_log and merkle_root in Interfaces.worker_manager.job_log[ extranonce1_bin]: ( job_id, difficulty, basediff, job_ts ) = Interfaces.worker_manager.job_log[extranonce1_bin][merkle_root] else: raise SubmitException("Job not registered!") #log.debug("worker_job_log: %s" % repr(Interfaces.worker_manager.job_log)) submit_time = Interfaces.timestamper.time() pool_share = settings.POOL_SHARE if ((basediff - settings.POOL_TARGET)) != 0: pool_share = round( pow(2, ((basediff - settings.POOL_TARGET) / 0.693147181)) * settings.POOL_SHARE) is_ext_diff = False if settings.ENABLE_WORKER_STATS: (valid, invalid, is_banned, is_ext_diff, last_ts) = Interfaces.worker_manager.worker_log['authorized'][ extranonce1_bin] percent = float( float(invalid) / (float(valid) if valid else 1) * 100) if is_banned and submit_time - last_ts > settings.WORKER_BAN_TIME: if percent > settings.INVALID_SHARES_PERCENT: log.info("Worker invalid percent: %0.2f %s STILL BANNED!" % (percent, worker_name)) else: is_banned = False log.info("Clearing ban for worker: %s UNBANNED" % worker_name) (valid, invalid, is_banned, last_ts) = (0, 0, is_banned, Interfaces.timestamper.time()) if submit_time - last_ts > settings.WORKER_CACHE_TIME and not is_banned: if percent > settings.INVALID_SHARES_PERCENT and settings.ENABLE_WORKER_BANNING: is_banned = True log.info("Worker invalid percent: %0.2f %s BANNED!" % (percent, worker_name)) else: log.debug("Clearing worker stats for: %s" % worker_name) (valid, invalid, is_banned, last_ts) = (0, 0, is_banned, Interfaces.timestamper.time()) if settings.ENABLE_WORKER_STATS: log.debug( "%s (%d, %d, %s, %d) %0.2f%% job_id(%s) diff(%0.9f) share(%i)" % (worker_name, valid, invalid, is_banned, last_ts, percent, job_id, basediff, pool_share)) if not is_ext_diff: Interfaces.share_limiter.submit(self.connection_ref, job_id, basediff, submit_time, worker_name, extranonce1_bin) try: (block_hash, share_diff, on_submit) = Interfaces.template_registry.submit_share( job_id, worker_name, session, extranonce1_bin, data, difficulty) except SubmitException as e: # block_header and block_hash are None when submitted data are corrupted if settings.ENABLE_WORKER_STATS: invalid += 1 if invalid > settings.INVALID_SHARES_SPAM: is_banned = True log.info("Worker SPAM %s BANNED! IP: %s" % (worker_name, ip)) Interfaces.worker_manager.worker_log['authorized'][ extranonce1_bin] = (valid, invalid, is_banned, is_ext_diff, last_ts) if is_banned: raise SubmitException("Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, basediff, False, pool_share, submit_time, False, ip, e[0], 0, job_id) raise if settings.ENABLE_WORKER_STATS: valid += 1 Interfaces.worker_manager.worker_log['authorized'][ extranonce1_bin] = (valid, invalid, is_banned, is_ext_diff, last_ts) if is_banned: raise SubmitException("Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, basediff, block_hash, pool_share, submit_time, True, ip, '', share_diff, job_id) if on_submit != None: on_submit.addCallback(Interfaces.share_manager.on_submit_block, worker_name, basediff, block_hash, submit_time, ip, share_diff) return True
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2)) # Check for job job = self.get_job(job_id) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if settings.COINDAEMON_ALGO == 'riecoin': if len(ntime) != 16: raise SubmitException("Incorrect size of ntime. Expected 16 chars") else: if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if settings.COINDAEMON_ALGO == 'riecoin': if len(nonce) != 64: raise SubmitException("Incorrect size of nonce. Expected 64 chars") else: if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 0. Some sugar extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) if settings.COINDAEMON_ALGO == 'riecoin': ntime_bin = (''.join([ ntime_bin[(1-i)*4:(1-i)*4+4] for i in range(0, 2) ])) nonce_bin = (''.join([ nonce_bin[(7-i)*4:(7-i)*4+4] for i in range(0, 8) ])) # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user if settings.COINDAEMON_ALGO == 'scrypt': hash_bin = ltc_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) elif settings.COINDAEMON_ALGO == 'scrypt-jane': hash_bin = yac_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]), int(ntime, 16)) elif settings.COINDAEMON_ALGO == 'quark': hash_bin = quark_hash.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) elif settings.COINDAEMON_ALGO == 'skeinhash': hash_bin = skeinhash.skeinhash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) elif settings.COINDAEMON_ALGO == 'magi': hash_bin = magi_hash.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) else: hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) hash_int = util.uint256_from_str(hash_bin) scrypt_hash_hex = "%064x" % hash_int if settings.COINDAEMON_ALGO == 'riecoin': # this is kind of an ugly hack: we use hash_int to store the number of primes hash_int = util.riecoinPoW( hash_int, job.target, int(nonce, 16) ) header_hex = binascii.hexlify(header_bin) if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'scrypt-jane': header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" elif settings.COINDAEMON_ALGO == 'quark': header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" elif settings.COINDAEMON_ALGO == 'magi': header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" elif settings.COINDAEMON_ALGO == 'riecoin': header_hex = header_hex+"00000080000000000000000080030000" else: pass target_user = self.diff_to_target(difficulty) if settings.COINDAEMON_ALGO == 'riecoin': if hash_int < target_user: raise SubmitException("Share does not meet target") else: if hash_int > target_user: raise SubmitException("Share is above target") # Mostly for debugging purposes target_info = self.diff_to_target(100000) if hash_int <= target_info: log.info("Yay, share with diff above 100000") # Algebra tells us the diff_to_target is the same as hash_to_diff share_diff = int(self.diff_to_target(hash_int)) # 5. Compare hash with target of the network isBlockCandidate = False if settings.COINDAEMON_ALGO == 'riecoin': if hash_int == 6: isBlockCandidate = True else: if hash_int <= job.target: isBlockCandidate = True if isBlockCandidate == True: # Yay! It is block candidate! log.info("We found a block candidate! %s" % scrypt_hash_hex) # Reverse the header and get the potential block hash (for scrypt only) if settings.COINDAEMON_ALGO == 'riecoin': block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 28) ])) else: block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') # 6. Finalize and serialize block object job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): # Should not happen log.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)") # 7. Submit block to the network serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex, scrypt_hash_hex) if on_submit: self.update_block() if settings.SOLUTION_BLOCK_HASH: return (header_hex, block_hash_hex, share_diff, on_submit) else: return (header_hex, scrypt_hash_hex, share_diff, on_submit) if settings.SOLUTION_BLOCK_HASH: # Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table if settings.COINDAEMON_ALGO == 'riecoin': block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 28) ])) else: block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') return (header_hex, block_hash_hex, share_diff, None) else: return (header_hex, scrypt_hash_hex, share_diff, None)
def submit(self, worker_name, work_id, extranonce2, ntime, nonce): '''Try to solve block candidate using given parameters.''' session = self.connection_ref().get_session() session.setdefault('authorized', {}) # Check if worker is authorized to submit shares ip = self.connection_ref()._get_ip() if not Interfaces.worker_manager.authorize( worker_name, session['authorized'].get(worker_name)): log.info("Worker is not authorized: IP %s", str(ip)) raise SubmitException("Worker is not authorized") # Check if extranonce1 is in connection session extranonce1_bin = session.get('extranonce1', None) if not extranonce1_bin: log.info("Connection is not subscribed for mining: IP %s", str(ip)) raise SubmitException("Connection is not subscribed for mining") # Get current block job_id difficulty = session['difficulty'] if worker_name in Interfaces.worker_manager.job_log and work_id in Interfaces.worker_manager.job_log[ worker_name]: (job_id, difficulty, job_ts) = Interfaces.worker_manager.job_log[worker_name][work_id] else: job_ts = Interfaces.timestamper.time() Interfaces.worker_manager.job_log.setdefault( worker_name, {})[work_id] = (work_id, difficulty, job_ts) job_id = work_id #log.debug("worker_job_log: %s" % repr(Interfaces.worker_manager.job_log)) submit_time = Interfaces.timestamper.time() (valid, invalid, is_banned, diff, is_ext_diff, last_ts ) = Interfaces.worker_manager.worker_log['authorized'][worker_name] percent = float(float(invalid) / (float(valid) if valid else 1) * 100) if is_banned and submit_time - last_ts > settings.WORKER_BAN_TIME: if percent > settings.INVALID_SHARES_PERCENT: log.debug("Worker invalid percent: %0.2f %s STILL BANNED!" % (percent, worker_name)) else: is_banned = False log.debug("Clearing ban for worker: %s UNBANNED" % worker_name) (valid, invalid, is_banned, last_ts) = (0, 0, is_banned, Interfaces.timestamper.time()) if submit_time - last_ts > settings.WORKER_CACHE_TIME and not is_banned: if percent > settings.INVALID_SHARES_PERCENT and settings.ENABLE_WORKER_BANNING: is_banned = True log.debug("Worker invalid percent: %0.2f %s BANNED!" % (percent, worker_name)) else: log.debug("Clearing worker stats for: %s" % worker_name) (valid, invalid, is_banned, last_ts) = (0, 0, is_banned, Interfaces.timestamper.time()) log.debug( "%s (%d, %d, %s, %s, %d) %0.2f%% work_id(%s) job_id(%s) diff(%f)" % (worker_name, valid, invalid, is_banned, is_ext_diff, last_ts, percent, work_id, job_id, difficulty)) if not is_ext_diff: Interfaces.share_limiter.submit(self.connection_ref, job_id, difficulty, submit_time, worker_name) # This checks if submitted share meet all requirements # and it is valid proof of work. try: (block_header, block_hash, share_diff, on_submit) = Interfaces.template_registry.submit_share( job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty) except SubmitException as e: # block_header and block_hash are None when submitted data are corrupted invalid += 1 Interfaces.worker_manager.worker_log['authorized'][worker_name] = ( valid, invalid, is_banned, difficulty, is_ext_diff, last_ts) if is_banned: raise SubmitException("Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, False, False, difficulty, submit_time, False, ip, e[0], 0) raise valid += 1 Interfaces.worker_manager.worker_log['authorized'][worker_name] = ( valid, invalid, is_banned, difficulty, is_ext_diff, last_ts) if is_banned: raise SubmitException("Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, block_header, block_hash, difficulty, submit_time, True, ip, '', share_diff) if on_submit != None: # Pool performs submitblock() to vdinard. Let's hook # to result and report it to share manager on_submit.addCallback(Interfaces.share_manager.on_submit_block, worker_name, block_header, block_hash, submit_time, ip, share_diff) return True
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty, ip=False): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session - submitblock_callback - reference to method which receive result of submitblock() - difficulty is checked to see if its lower than the vardiff minimum target or pool target from conf/config.py and if it is the share is rejected due to it not meeting the requirements for a share ''' if settings.VARIABLE_DIFF == True: # Share Diff Should never be 0 if difficulty < settings.VDIFF_MIN_TARGET : log.exception("Worker %s @ IP: %s seems to be submitting Fake Shares"%(worker_name,ip)) raise SubmitException("Diff is %s Share Rejected Reporting to Admin"%(difficulty)) else: if difficulty < settings.POOL_TARGET: log.exception("Worker %s @ IP: %s seems to be submitting Fake Shares"%(worker_name,ip)) raise SubmitException("Diff is %s Share Rejected Reporting to Admin"%(difficulty)) # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2)) # Check for job job = self.get_job(job_id, worker_name, ip) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 0. Some sugar extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user if settings.COINDAEMON_ALGO == 'scrypt': hash_bin = ltc_scrypt.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) elif settings.COINDAEMON_ALGO == 'scrypt-jane': if settings.SCRYPTJANE_NAME == 'vtc_scrypt': hash_bin = scryptjane.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) else: hash_bin = scryptjane.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ]), int(ntime, 16)) elif settings.COINDAEMON_ALGO == 'quark': hash_bin = quark_hash.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) elif settings.COINDAEMON_ALGO == 'skeinhash': hash_bin = skeinhash.skeinhash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) else: hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) hash_int = util.uint256_from_str(hash_bin) scrypt_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'scrypt-jane': header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" elif settings.COINDAEMON_ALGO == 'quark': header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" else: pass target_user = self.diff_to_target(difficulty) if hash_int > target_user: raise SubmitException("Share is above target") # Mostly for debugging purposes target_info = self.diff_to_target(100000) if hash_int <= target_info: log.info("Yay, share with diff above 100000") # Algebra tells us the diff_to_target is the same as hash_to_diff share_diff = int(self.diff_to_target(hash_int)) # 5. Compare hash with target of the network if hash_int <= job.target: # Yay! It is block candidate! log.info("We found a block candidate! %s" % scrypt_hash_hex) # Reverse the header and get the potential block hash (for scrypt only) #if settings.COINDAEMON_ALGO == 'scrypt' or settings.COINDAEMON_ALGO == 'sha256d': # if settings.COINDAEMON_Reward == 'POW': block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') #else: block_hash_hex = hash_bin[::-1].encode('hex_codec') #else: block_hash_hex = hash_bin[::-1].encode('hex_codec') # 6. Finalize and serialize block object job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): # Should not happen log.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)") # 7. Submit block to the network serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex, scrypt_hash_hex) if on_submit: self.update_block() if settings.SOLUTION_BLOCK_HASH: return (header_hex, block_hash_hex, share_diff, on_submit) else: return (header_hex, scrypt_hash_hex, share_diff, on_submit) if settings.SOLUTION_BLOCK_HASH: # Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') return (header_hex, block_hash_hex, share_diff, None) else: return (header_hex, scrypt_hash_hex, share_diff, None)
def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce, difficulty): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException( "Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size * 2)) # Check for job job = self.get_job(job_id) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Convert from hex to binary extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2_bin, ntime_bin, nonce_bin): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user hash_bin = x13bcd_hash.getPoWHash(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) hash_int = util.uint256_from_str(hash_bin) block_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) target_user = self.diff_to_target(difficulty) if hash_int > target_user: raise SubmitException("Share is above target") # Mostly for debugging purposes target_info = self.diff_to_target(100000) if hash_int <= target_info: log.info("Yay, share with diff above 100000") # 5. Compare hash with target of the network if hash_int <= job.target: # Yay! It is block candidate! log.info("We found a block candidate! %s" % block_hash_hex) # 6. Finalize and serialize block object job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): # Should not happen log.error("Final job validation failed!") # 7. Submit block to the network serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(serialized) return (header_hex, block_hash_hex, on_submit) return (header_hex, block_hash_hex, None)
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty, ip, submit_time): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException( "Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size * 2)) # Check for job job = self.get_job(job_id) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 0. Some sugar extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user if settings.DAEMON_ALGO == 'scrypt': hash_bin = ltc_scrypt.getPoWHash(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) elif settings.DAEMON_ALGO == 'x11': hash_bin = x11_hash.getPoWHash(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) else: hash_bin = util.doublesha(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) hash_int = util.uint256_from_str(hash_bin) pow_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) target_user = self.diff_to_target(difficulty) if hash_int > target_user: raise SubmitException("Share is above target") # Mostly for debugging purposes target_info = self.diff_to_target(50) if hash_int <= target_info: log.info("YAY, share with diff above 50") # Algebra tells us the diff_to_target is the same as hash_to_diff share_diff = float(self.diff_to_target(hash_int)) block_hash_bin = util.doublesha(header_bin) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') on_submit = None if hash_int <= job.target: log.info( "BLOCK CANDIDATE! %s diff(%f/%f)" % (block_hash_hex, share_diff, self.diff_to_target(job.target))) job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): log.exception("FINAL JOB VALIDATION FAILED!") serialized = binascii.hexlify(job.serialize()) if settings.SOLUTION_BLOCK_HASH: on_submit = self.bitcoin_rpc.submitblock( serialized, block_hash_hex) else: on_submit = self.bitcoin_rpc.submitblock( serialized, pow_hash_hex) if on_submit: self.update_block() if settings.SOLUTION_BLOCK_HASH: return (header_hex, block_hash_hex, share_diff, on_submit) else: return (header_hex, pow_hash_hex, share_diff, on_submit)
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty, ip, version_rolling): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session - submitblock_callback - reference to method which receive result of submitblock() - difficulty is checked to see if its lower than the vardiff minimum target or pool target from conf/config.py and if it is the share is rejected due to it not meeting the requirements for a share ''' if difficulty < settings.VDIFF_MIN_TARGET or difficulty < settings.POOL_TARGET: # Share Diff Should never be 0 raise SubmitException( "Diff is %s Share Rejected Reporting to Admin" % (difficulty)) # normalize the case to prevent duplication of valid shares by the client ntime = ntime.lower() nonce = nonce.lower() extranonce2 = extranonce2.lower() # Check for job job = self.get_job(job_id, worker_name, ip) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # 0. Some sugar extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2_bin, ntime_bin, nonce_bin): log.debug("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin, version_rolling) # 4. Reverse header and compare it with target of the user hash_bin = util.doublesha(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) hash_int = util.uint256_from_str(hash_bin) scrypt_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) if settings.CUSTOM_HEADER != None: header_hex = header_hex + settings.CUSTOM_HEADER target_user = self.diff_to_target(difficulty) if hash_int > target_user: raise SubmitException("Share is above target %064x > %064x" % (hash_int, target_user)) # Algebra tells us the diff_to_target is the same as hash_to_diff share_diff = int(self.diff_to_target(hash_int)) # 5. Compare hash with target of the network if hash_int <= job.target: # Yay! It is block candidate! log.info("We found a block candidate! %s" % scrypt_hash_hex) block_hash_bin = util.doublesha(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') # 6. Finalize and serialize block object job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): # Should not happen log.exception( "FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)" ) # 7. Submit block to the network serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex, scrypt_hash_hex) if on_submit: self.update_block() if settings.SOLUTION_BLOCK_HASH: return (header_hex, block_hash_hex, share_diff, on_submit) else: return (header_hex, scrypt_hash_hex, share_diff, on_submit) if settings.SOLUTION_BLOCK_HASH: block_hash_bin = util.doublesha(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') return (header_hex, block_hash_hex, share_diff, None) else: return (header_hex, scrypt_hash_hex, share_diff, None)
def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce, difficulty): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' global rsk_last_received_share_time global rsk_submitted_shares start = Interfaces.timestamper.time() logid = util.id_generator() log.info( json.dumps({ "rsk": "[RSKLOG]", "tag": "[SHARE_RECEIVED_START]", "uuid": logid, "start": start, "elapsed": 0 })) # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException( "Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size * 2)) # Check for job job = self.get_job(job_id) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Convert from hex to binary extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2_bin, ntime_bin, nonce_bin): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user # header 80-bytes (19*4 + 4) header_le = ''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)]) hash_bin = util.doublesha(header_le) hash_int = util.uint256_from_str(hash_bin) block_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) log.info( json.dumps({ "rsk": "[RSKLOG]", "tag": "[SHARE_RECEIVED_HEX]", "uuid": logid, "start": Interfaces.timestamper.time(), "elapsed": 0, "data": block_hash_hex })) if not settings.RSK_DEV_MODE: target_user = self.diff_to_target(difficulty) if hash_int > target_user: raise SubmitException("Share is above target") # Mostly for debugging purposes target_info = self.diff_to_target(100000) if hash_int <= target_info: log.info("Yay, share with diff above 100000") # 5. Compare hash with target of the network log.info("Hash_Int: %s, Job.Target %s" % (hash_int, job.target)) btc_solution = hash_int <= job.target rsk_solution = False if self.rootstock_rpc is not None: rsk_solution = hash_int <= self.rootstock_rpc.rsk_target and self._is_rsk_tag_in_coinbase( coinbase_bin) on_submit_rsk = None on_submit = None if btc_solution or rsk_solution: log.info("We found a block candidate! %s" % block_hash_hex) job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if btc_solution: serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(serialized) log.info( json.dumps({ "rsk": "[RSKLOG]", "tag": "[BTC_SUBMITBLOCK]", "uuid": util.id_generator(), "start": start, "elapsed": Interfaces.timestamper.time(), "data": block_hash_hex })) if rsk_solution: if rsk_last_received_share_time is None: rsk_last_received_share_time = int(round(time() * 1000)) rsk_submitted_shares = 0 last_received_share_time_now = int(round(time() * 1000)) if last_received_share_time_now - rsk_last_received_share_time >= 1000: rsk_submitted_shares = 0 rsk_last_received_share_time = last_received_share_time_now if last_received_share_time_now - rsk_last_received_share_time < 1000 and rsk_submitted_shares < 3: rsk_submitted_shares += 1 else: return (header_hex, block_hash_hex, on_submit, on_submit_rsk) serialized = binascii.hexlify(job.serialize()) block_header_hex = binascii.hexlify(header_le) coinbase_hex = binascii.hexlify(coinbase_bin) coinbase_hash_hex = binascii.hexlify(coinbase_hash) merkle_hashes_array = [ binascii.hexlify(x) for x in job.merkletree._steps ] merkle_hashes_array.insert(0, coinbase_hash_hex) merkle_hashes = ' '.join(merkle_hashes_array) txn_count = hex(len(merkle_hashes_array))[2:] on_submit_rsk = self.rootstock_rpc.submitBitcoinBlockPartialMerkle( block_hash_hex, block_header_hex, coinbase_hex, merkle_hashes, txn_count) log.info( json.dumps({ "rsk": "[RSKLOG]", "tag": "[RSK_SUBMITBLOCK]", "uuid": util.id_generator(), "start": start, "elapsed": Interfaces.timestamper.time(), "data": block_hash_hex })) return (header_hex, block_hash_hex, on_submit, on_submit_rsk)
def submit(self, worker_name, work_id, extranonce2, ntime, nonce): '''Try to solve block candidate using given parameters.''' session = self.connection_ref().get_session() session.setdefault('authorized', {}) # Check if worker is authorized to submit shares ip = self.connection_ref()._get_ip() if not Interfaces.worker_manager.authorize( worker_name, session['authorized'].get(worker_name)): log.info("Worker is not authorized: %s IP %s" % (worker_name, str(ip))) raise SubmitException("Worker is not authorized") # Check if extranonce1 is in connection session extranonce1_bin = session.get('extranonce1', None) if not extranonce1_bin: log.info("Connection is not subscribed for mining: IP %s" % str(ip)) raise SubmitException("Connection is not subscribed for mining") difficulty = session['difficulty'] submit_time = Interfaces.timestamper.time() if extranonce1_bin in Interfaces.worker_manager.job_log and work_id in Interfaces.worker_manager.job_log[ extranonce1_bin]: (job_id, difficulty, job_ts ) = Interfaces.worker_manager.job_log[extranonce1_bin][work_id] else: job_ts = Interfaces.timestamper.time() Interfaces.worker_manager.job_log.setdefault( extranonce1_bin, {})[work_id] = (work_id, difficulty, job_ts) job_id = work_id #log.debug("worker_job_log: %s" % repr(Interfaces.worker_manager.job_log)) pool_share = float( float(difficulty) * float(settings.SHARE_MULTIPLIER)) if settings.ENABLE_WORKER_STATS: (valid, invalid, is_banned, last_ts) = Interfaces.worker_manager.worker_log['authorized'][ extranonce1_bin] percent = float( float(invalid) / (float(valid) if valid else 1) * 100) if is_banned and submit_time - last_ts > settings.WORKER_BAN_TIME: if percent > settings.INVALID_SHARES_PERCENT: log.info("Worker invalid percent: %0.2f %s STILL BANNED!" % (percent, worker_name)) else: is_banned = False log.info("Clearing ban for worker: %s UNBANNED" % worker_name) (valid, invalid, is_banned, last_ts) = (0, 0, is_banned, Interfaces.timestamper.time()) if submit_time - last_ts > settings.WORKER_CACHE_TIME and not is_banned: if percent > settings.INVALID_SHARES_PERCENT and settings.ENABLE_WORKER_BANNING: is_banned = True log.info("Worker invalid percent: %0.2f %s BANNED!" % (percent, worker_name)) else: log.debug("Clearing worker stats for: %s" % worker_name) (valid, invalid, is_banned, last_ts) = (0, 0, is_banned, Interfaces.timestamper.time()) if settings.ENABLE_WORKER_STATS: log.debug( "%s (%d, %d, %s, %d) %0.2f%% job_id(%s) diff(%i) share(%i)" % (worker_name, valid, invalid, is_banned, last_ts, percent, job_id, difficulty, pool_share)) Interfaces.share_limiter.submit(self.connection_ref, job_id, difficulty, submit_time, worker_name, extranonce1_bin) try: (block_header, block_hash, share_diff, on_submit) = Interfaces.template_registry.submit_share( job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty, ip, submit_time) except SubmitException as e: # block_header and block_hash are None when submitted data are corrupted if settings.ENABLE_WORKER_STATS: invalid += 1 if invalid > settings.INVALID_SHARES_SPAM: is_banned = True log.info("Worker SPAM %s BANNED! IP: %s" % (worker_name, ip)) Interfaces.worker_manager.worker_log['authorized'][ extranonce1_bin] = (valid, invalid, is_banned, last_ts) if is_banned: raise SubmitException("Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, False, False, difficulty, pool_share, submit_time, False, ip, e[0], 0, job_id) raise if settings.ENABLE_WORKER_STATS: valid += 1 Interfaces.worker_manager.worker_log['authorized'][ extranonce1_bin] = (valid, invalid, is_banned, last_ts) if is_banned: raise SubmitException("Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, block_header, block_hash, difficulty, pool_share, submit_time, True, ip, '', share_diff, job_id) if on_submit != None: on_submit.addCallback(Interfaces.share_manager.on_submit_block, worker_name, block_header, block_hash, submit_time, ip, share_diff) return True
def submit_share(self, job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce, difficulty, payment_type): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException( "Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size * 2)) # Check for job job = self.get_job(job_id) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): logger.log('bad_client', worker_name, "Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 0. Some sugar extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user hash_bin = util.doublesha(''.join( [header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) hash_int = util.uint256_from_str(hash_bin) block_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) target_user = self.diff_to_target(difficulty) #logger.log('share', worker_name, 'HASH %064x' % hash_int, 'TARGET %064x' % job.target, 'DIFF %d' % difficulty) if hash_int > target_user: logger.log('bad_client', 'Share is above target') raise SubmitException("Share is above target") if payment_type == 'PPS': logger.log('share', worker_name, 'HASH %064x' % hash_int, 'TARGET %064x' % job.target, 'DIFF %d' % difficulty) logger.log('pplns', worker_name, 'HASH %064x' % hash_int, 'DIFF %d' % difficulty, 'PAY 0') if payment_type == 'PPLNS': logger.log('pplns', worker_name, 'HASH %064x' % hash_int, 'DIFF %d' % difficulty, 'PAY 1') # 5. Compare hash with target of the network if hash_int <= job.target: # Yay! It is block candidate! logger.log('block', worker_name, block_hash_hex) # 6. Finalize and serialize block object job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): # Should not happen logger.log('error', "Final job validation failed!") # 7. Submit block to the network serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(serialized) logger.log('submitblock', serialized) return (True, worker_name, block_hash_hex) return (False, worker_name, block_hash_hex)
def submit(self, worker_name, job_id, extranonce2, ntime, nonce): '''Try to solve block candidate using given parameters.''' session = self.connection_ref().get_session() session.setdefault('authorized', {}) # Check if worker is authorized to submit shares if not Interfaces.worker_manager.authorize( worker_name, session['authorized'].get(worker_name)): raise SubmitException("Worker is not authorized") start = Interfaces.timestamper.time() # Check if extranonce1 is in connection session extranonce1_bin = session.get('extranonce1', None) if not extranonce1_bin: raise SubmitException("Connection is not subscribed for mining") if settings.RSK_DEV_MODE and hasattr(settings, 'RSK_STRATUM_DIFFICULTY'): difficulty = settings.RSK_STRATUM_DIFFICULTY else: difficulty = session['difficulty'] submit_time = Interfaces.timestamper.time() Interfaces.share_limiter.submit(self.connection_ref, difficulty, submit_time) # This checks if submitted share meet all requirements # and it is valid proof of work. try: (block_header, block_hash, on_submit, on_submit_rsk) = Interfaces.template_registry.submit_share( job_id, worker_name, extranonce1_bin, extranonce2, ntime, nonce, difficulty) except SubmitException as e: log.error("SUBMIT EXCEPTION: %s", e) # block_header and block_hash are None when submitted data are corrupted Interfaces.share_manager.on_submit_share(worker_name, None, None, difficulty, submit_time, False) raise Interfaces.share_manager.on_submit_share(worker_name, block_header, block_hash, difficulty, submit_time, True) if on_submit != None: # Pool performs submitblock() to bitcoind. Let's hook # to result and report it to share manager on_submit.addCallback(Interfaces.share_manager.on_submit_block, worker_name, block_header, block_hash, submit_time) if on_submit_rsk != None: # Pool performs submitBitcoinBlockPartialMerkle() to rskd. Let's hook # to result and report it to share manager on_submit_rsk.addCallback( Interfaces.share_manager.on_submit_block_rsk, worker_name, block_header, block_hash, submit_time) if on_submit or on_submit_rsk: log.info( json.dumps({ "uuid": util.id_generator(), "rsk": "[RSKLOG]", "tag": "[SUBMITBLOCK_END]", "start": submit_time, "elapsed": Interfaces.timestamper.time() - submit_time, "data": (block_hash, job_id) })) return True
is_ext_diff, last_ts) if is_banned: raise SubmitException(u"Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, False, False, difficulty, submit_time, False, ip, e[0], 0) raise valid += 1 Interfaces.worker_manager.worker_log[u'authorized'][worker_name] = ( valid, invalid, is_banned, difficulty, is_ext_diff, last_ts) if is_banned: raise SubmitException(u"Worker is temporarily banned") Interfaces.share_manager.on_submit_share(worker_name, block_header, block_hash, difficulty, submit_time, True, ip, u'', share_diff) if on_submit is not None: # Pool performs submitblock() to litecoind. Let's hook # to result and report it to share manager on_submit.addCallback(Interfaces.share_manager.on_submit_block, worker_name, block_header, block_hash, submit_time, ip, share_diff) return True
class TemplateRegistry(object): '''Implements the main logic of the pool. Keep track on valid block templates, provide internal interface for stratum service and implements block validation and submits.''' def __init__(self, block_template_class, coinbaser, bitcoin_rpc, instance_id, on_template_callback, on_block_callback): self.prevhashes = {} self.jobs = weakref.WeakValueDictionary() self.extranonce_counter = ExtranonceCounter(instance_id) self.extranonce2_size = block_template_class.coinbase_transaction_class.extranonce_size \ - self.extranonce_counter.get_size() log.debug("Got to Template Registry") self.coinbaser = coinbaser self.block_template_class = block_template_class self.bitcoin_rpc = bitcoin_rpc self.on_block_callback = on_block_callback self.on_template_callback = on_template_callback self.last_block = None self.update_in_progress = False self.last_update = None # Create first block template on startup self.update_block() def get_new_extranonce1(self): '''Generates unique extranonce1 (e.g. for newly subscribed connection.''' log.debug("Getting Unique Extronance") return self.extranonce_counter.get_new_bin() def get_last_broadcast_args(self): '''Returns arguments for mining.notify from last known template.''' log.debug("Getting Laat Template") return self.last_block.broadcast_args def add_template(self, block,block_height): '''Adds new template to the registry. It also clean up templates which should not be used anymore.''' prevhash = block.prevhash_hex if prevhash in self.prevhashes.keys(): new_block = False else: new_block = True self.prevhashes[prevhash] = [] # Blocks sorted by prevhash, so it's easy to drop # them on blockchain update self.prevhashes[prevhash].append(block) # Weak reference for fast lookup using job_id self.jobs[block.job_id] = block # Use this template for every new request self.last_block = block # Drop templates of obsolete blocks for ph in self.prevhashes.keys(): if ph != prevhash: del self.prevhashes[ph] log.info("New template for %s" % prevhash) if new_block: # Tell the system about new block # It is mostly important for share manager self.on_block_callback(prevhash, block_height) # Everything is ready, let's broadcast jobs! self.on_template_callback(new_block) #from twisted.internet import reactor #reactor.callLater(10, self.on_block_callback, new_block) def update_block(self): '''Registry calls the getblocktemplate() RPC and build new block template.''' if self.update_in_progress: # Block has been already detected return self.update_in_progress = True self.last_update = Interfaces.timestamper.time() d = self.bitcoin_rpc.getblocktemplate() d.addCallback(self._update_block) d.addErrback(self._update_block_failed) def _update_block_failed(self, failure): log.error(str(failure)) self.update_in_progress = False def _update_block(self, data): start = Interfaces.timestamper.time() template = self.block_template_class(Interfaces.timestamper, self.coinbaser, JobIdGenerator.get_new_id()) log.info(template.fill_from_rpc(data)) self.add_template(template,data['height']) log.info("Update finished, %.03f sec, %d txes" % \ (Interfaces.timestamper.time() - start, len(template.vtx))) self.update_in_progress = False return data def diff_to_target(self, difficulty): '''Converts difficulty to target''' diff1 = coindef.diff1() return diff1 / difficulty def get_job(self, job_id, worker_name, ip=False): '''For given job_id returns BlockTemplate instance or None''' try: j = self.jobs[job_id] except: log.info("Job id '%s' not found, worker_name: '%s'" % (job_id, worker_name)) if ip: log.info("Worker submited invalid Job id: IP %s", str(ip)) return None # Now we have to check if job is still valid. # Unfortunately weak references are not bulletproof and # old reference can be found until next run of garbage collector. if j.prevhash_hex not in self.prevhashes: log.info("Prevhash of job '%s' is unknown" % job_id) return None if j not in self.prevhashes[j.prevhash_hex]: log.info("Job %s is unknown" % job_id) return None return j def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty, ip=False): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2)) # Check for job job = self.get_job(job_id, worker_name, ip) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 0. Some sugar extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user hash_bin = algo.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) hash_int = util.uint256_from_str(hash_bin) scrypt_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) if coindef.header() = True: header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000" target_user = self.diff_to_target(difficulty) if hash_int > target_user: raise SubmitException("Share is above target") # Mostly for debugging purposes target_info = self.diff_to_target(100000) if hash_int <= target_info: log.info("Yay, share with diff above 100000") # Algebra tells us the diff_to_target is the same as hash_to_diff share_diff = int(self.diff_to_target(hash_int)) # 5. Compare hash with target of the network if hash_int <= job.target: # Yay! It is block candidate! log.info("We found a block candidate! %s" % scrypt_hash_hex) # Reverse the header and get the potential block hash (for scrypt only) block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') # 6. Finalize and serialize block object job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): # Should not happen log.exception("FINAL JOB VALIDATION FAILED!(Try enabling/disabling tx messages)") # 7. Submit block to the network serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(serialized, block_hash_hex, scrypt_hash_hex) if on_submit: self.update_block() if settings.SOLUTION_BLOCK_HASH: return (header_hex, block_hash_hex, share_diff, on_submit) else: return (header_hex, scrypt_hash_hex, share_diff, on_submit) if settings.SOLUTION_BLOCK_HASH: # Reverse the header and get the potential block hash (for scrypt only) only do this if we want to send in the block hash to the shares table block_hash_bin = util.doublesha(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) block_hash_hex = block_hash_bin[::-1].encode('hex_codec') return (header_hex, block_hash_hex, share_diff, None) else: return (header_hex, scrypt_hash_hex, share_diff, None)
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty, ip=False): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the bitcoin network. - extranonce1_bin is binary. No checks performed, it should be from session data - job_id, extranonce2, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException("Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size*2)) # Check for job job = self.get_job(job_id, worker_name, ip) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 0. Some sugar extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = util.doublesha(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint256_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_int, ntime_bin, nonce_bin) # 4. Reverse header and compare it with target of the user hash_bin = algo.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) hash_int = util.uint256_from_str(hash_bin) scrypt_hash_hex = "%064x" % hash_int header_hex = binascii.hexlify(header_bin) if coindef.header() = True: header_hex = header_hex+"000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000"
def submit_share(self, job_id, worker_name, extranonce, ntime, nonce, difficulty): '''Check parameters and finalize block template. If it leads to valid block candidate, asynchronously submits the block back to the network. - job_id, extranonce, ntime, nonce - in hex form sent by the client - difficulty - decimal number from session, again no checks performed - submitblock_callback - reference to method which receive result of submitblock() ''' # Check if extranonce looks correctly. extranonce is in hex form... if len(extranonce) != self.extranonce_size * 2: raise SubmitException( "Incorrect size of extranonce. Expected 16 chars") # Check for job job = self.get_job(job_id) if job == None: raise SubmitException("Job '%s' not found" % job_id) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") ntime_int = util.hex_to_int32(ntime) if not job.check_ntime(ntime_int): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Convert from hex to binary extranonce_int = util.hex_to_int64(extranonce) nonce_int = util.hex_to_int32(nonce) # Check for duplicated submit if not job.register_submit(extranonce_int, ntime_int, nonce_int): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, extranonce_int, ntime_int, nonce_int)) raise SubmitException("Duplicate share") # Now let's do the hard work! # --------------------------- # 1. Calculate merkle root mt = MerkleTree(job.intermediate_merkles) merkle_root_bin = mt.calculate_merkle_root(extranonce_int) # 2. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_bin, ntime_int, nonce_int) # 3. Compare header with target of the user hash_bin = vblake.getPoWHash(header_bin) hash_int = util.uint192_from_str_be(hash_bin) block_hash_hex = "%048x" % hash_int header_hex = binascii.hexlify(header_bin) target_user = self.diff_to_target(difficulty) if hash_int > target_user: raise SubmitException("Share is above target") # 4. Compare hash with target of the network if hash_int < job.target: # Yay! It is block candidate! log.info("We found a block candidate! %s" % block_hash_hex) # 5. Finalize and serialize block object job.finalize(merkle_root_bin, extranonce_int, ntime_int, nonce_int) if not job.is_valid(): # Should not happen log.error("Final job validation failed!") # 6. Submit block to the network on_submit = self.protocol.submit_share(job.nc_job_id, extranonce_int, ntime_int, nonce_int) return (header_hex, block_hash_hex, on_submit) return (header_hex, block_hash_hex, None)
def submit_share(self, job_id, worker_name, session, extranonce1_bin, extranonce2, ntime, nonce, difficulty): # Check for job job = self.get_job(job_id) if job == None: raise SubmitException("Job '%s' not found" % job_id) nonce = util.rev(nonce) ntime = util.rev(ntime) extranonce2_bin = binascii.unhexlify(extranonce2) ntime_bin = binascii.unhexlify(ntime) nonce_bin = binascii.unhexlify(nonce) # Check if extranonce2 looks correctly. extranonce2 is in hex form... if len(extranonce2) != self.extranonce2_size * 2: raise SubmitException( "Incorrect size of extranonce2. Expected %d chars" % (self.extranonce2_size * 2)) # Check if ntime looks correct if len(ntime) != 8: raise SubmitException("Incorrect size of ntime. Expected 8 chars") if not job.check_ntime(int(ntime, 16)): raise SubmitException("Ntime out of range") # Check nonce if len(nonce) != 8: raise SubmitException("Incorrect size of nonce. Expected 8 chars") # Check for duplicated submit if not job.register_submit(extranonce1_bin, extranonce2, ntime, nonce): log.info("Duplicate from %s, (%s %s %s %s)" % \ (worker_name, binascii.hexlify(extranonce1_bin), extranonce2, ntime, nonce)) raise SubmitException("Duplicate share") # 1. Build coinbase coinbase_bin = job.serialize_coinbase(extranonce1_bin, extranonce2_bin) coinbase_hash = kshake320_hash.getHash320(coinbase_bin) # 2. Calculate merkle root merkle_root_bin = job.merkletree.withFirst(coinbase_hash) merkle_root_int = util.uint320_from_str(merkle_root_bin) # 3. Serialize header with given merkle, ntime and nonce header_bin = job.serialize_header(merkle_root_bin, int(ntime, 16), int(nonce, 16)) header_hex = binascii.hexlify(header_bin) header_hex = header_hex + "0000000000000000" # 4. Reverse header and compare it with target of the user hash_bin = kshake320_hash.getPoWHash(header_bin) hash_int = util.uint320_from_str(hash_bin) hash_hex = "%080x" % hash_int block_hash_hex = hash_bin[::-1].encode('hex_codec') target_user = float(self.diff_to_target(difficulty)) if hash_int > target_user: log.info("ABOVE TARGET!") raise SubmitException("Share above target") target_info = self.diff_to_target(50) if hash_int <= target_info: log.info("YAY, share with diff above 50") # Algebra tells us the diff_to_target is the same as hash_to_diff share_diff = float(self.diff_to_target(hash_int)) if hash_int <= job.target: # Yay! It is block candidate! log.info("BLOCK CANDIDATE! %s" % block_hash_hex) # Finalize and serialize block object job.finalize(merkle_root_int, extranonce1_bin, extranonce2_bin, int(ntime, 16), int(nonce, 16)) if not job.is_valid(): # Should not happen log.info("FINAL JOB VALIDATION FAILED!") # Submit block to the network '''serialized = binascii.hexlify(job.serialize()) on_submit = self.bitcoin_rpc.submitblock(str(serialized), block_hash_hex)''' job.vtx[0].set_extranonce(extranonce1_bin + extranonce2_bin) txs = binascii.hexlify(util.ser_vector(job.vtx)) on_submit = self.bitcoin_rpc.submitblock_wtxs( str(header_hex), str(txs), block_hash_hex) '''if on_submit: self.update_block()''' return (block_hash_hex, share_diff, on_submit) return (block_hash_hex, share_diff, None)