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=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)) # 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.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 = algolib.getPoWHash(''.join([ header_bin[i*4:i*4+4][::-1] for i in range(0, 20) ])) 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) 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") # 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) 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 bcd_Hash(x): x = to_bytes(x, 'utf8') out = bytes(x13bcd_hash.getPoWHash(x)) return out
import x13bcd_hash from binascii import unhexlify, hexlify block_hash = 'fd21da3c2b0b6e5f438d365ef3f9a2e3a5018b9402fff79bec0a2e47657e802b' block_header_hex = '600000006df1d486138ba00b9ceabc42c71c9d1925ea25896ee5555e78507642be52a4d761f93e1f507975d05a33de16bea7784f5cf492aa8e0eda6bf71029d78e3d939a5a2fa97e1d08379f000b3a0e' block_header_bin = unhexlify(block_header_hex) block_hash_bin = x13bcd_hash.getPoWHash(b''.join( [block_header_bin[i * 4:i * 4 + 4][::-1] for i in range(0, 20)])) block_hash_hex = hexlify(block_hash_bin[::-1]).decode("utf-8") assert block_hash_hex == block_hash
#!/usr/bin/env python # -*- coding: utf-8 -*- from pycoin.block import Block import io, struct import x13bcd_hash def uint256_from_str(s): r = 0L t = struct.unpack("<IIIIIIII", s[:32]) for i in xrange(8): r += t[i] << (i * 32) return r rawblock = '00000060b38cb32058b774ea735c0f6cfaf3fb0153a03496be87208219717f4e4ad744ee5dc71fd1295bfe406e03e23877b2b9a64be33f2a2f1e05a238ef6fdf04bb574ca24ec05b6394001b00cfc14c02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff4c0335350804a24ec05b08fabe6d6d00000000000003600000000000003960000039da4b3161e0000039da4b3161c8010000000000000030d6c52e000000000d2f6e6f64655374726174756d2f00000000020000000000000000266a24aa21a9edd76d419006b113123076afec5868f6b6e3dfde054e82adf4a9693d2a1cc58b256880814a000000001976a914829a4b1a792e9e1514ae902d622aed18a99601b088ac01200000000000000000000000000000000000000000000000000000000000000000000000000c000000d13244a2054b9b33808e72daabd3d9a7c5685b10abd7a4b932f273dd498f131601d4063756d72c0f08af818741b198c97ecf1ae065c464a0ef08ea7c4f0679089d010000006a4730440220243539823fb5fe8fdb8991dda0e5f7e7cf8e3beee36e2dbce6b905e58d142373022058996510c2edf2fbbe77f77016cbfafbe53cc4505e58de05083194af714c50000121021e6a4b6673be9f25e54f4028ebf8459bd2d0726d836dbcee8cecaa856bec458bffffffff0280778e06000000001976a91481b2e127d9c7fec0fbc6b1a1784ba421acf3889d88ac67ccd617000000001976a9145f2bc24285493cafc8ef44b35a5ffe63c5eab5ce88ac00000000' b = Block.parse_as_header(io.BytesIO(rawblock.decode('hex'))) hash_bin = x13bcd_hash.getPoWHash(b.as_bin()) print uint256_from_str(hash_bin)
def submit(self, connection_ref, _id, _params): # TODO: Job ID Check 구현해야함 # TODO: Diff Check 해서 그냥 Share 기록 or Submit 구별 해야함 # TODO: Share Result를 Database에 기록 해야함 - Database 는 Redis가 될듯 session_id = connection_ref.get_session() _worker_name = _params[0] _split_worker_name = _worker_name.strip().split('.') username = _split_worker_name[0] if len(_split_worker_name) == 2: worker = _split_worker_name[1] else: worker = None _job_id = _params[1] _nonce_1 = self.job_manager.get_nonce_from_session_id(session_id) _block_template = self.job_manager.get_block_template(_job_id) if _block_template is None: logger.info('rejected share, worker : %s, reason : job not found' % _worker_name) return {'id': _id, 'result': False, 'error': [21, 'job not found']} if os.getenv("COIN_TYPE") == 'bitcoin': _nonce_2 = _params[2] _time = _params[3] _time_reverse = hash_util.hex_to_reverse_hex(_time) _nonce = _params[4] _nonce_reverse = hash_util.hex_to_reverse_hex(_nonce) if len(_nonce) != 8: logger.info( 'rejected share, worker : %s, reason : incorrect size of nonce' % _worker_name) return { 'id': _id, 'result': False, 'error': [20, 'incorrect size of nonce'] } coinbase = binascii.hexlify(_block_template.coinbase_tx).split( _block_template.extranonce_placeholder) serialized_coinbase = binascii.unhexlify(coinbase[0] + _nonce_1 + _nonce_2.encode() + coinbase[1]) if os.getenv("COIN_ALGORITHM") == 'keccak': coinbase_hash = binascii.hexlify( hash_util.reverse_bytes( hash_util.sha(serialized_coinbase))) else: coinbase_hash = hash_util.bytes_to_reverse_hash( serialized_coinbase) tx_hashes = [coinbase_hash ] + [h['hash'] for h in _block_template.transactions] merkle_root_reverse_hex = hash_util.hex_to_reverse_hex( hash_util.merkle_root(tx_hashes)) # Header POW 종류별 구별 해야댐 header = _block_template.serialize_block_header( _time_reverse, _nonce_reverse, merkle_root_reverse_hex) # 80 bytes block_hex = _block_template.serialize_block( header, None, serialized_coinbase) if os.getenv("COIN_ALGORITHM") == 'lyra2rev2': import lyra2re2_hash header_hash = lyra2re2_hash.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'lyra2rev3': import lyra2re3_hash header_hash = lyra2re3_hash.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'keccak' or os.getenv( "COIN_ALGORITHM") == 'keccakc': import sha3 header_hash = sha3.keccak_256(header).digest() elif os.getenv("COIN_ALGORITHM") == 'x13-bcd': import x13bcd_hash header_hash = x13bcd_hash.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'neoscrypt': import neoscrypt header_hash = neoscrypt.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'yescrypt': import yescrypt_hash header_hash = yescrypt_hash.getHash(header, len(header)) elif os.getenv("COIN_ALGORITHM") == 'xevan': import xevan_hash header_hash = xevan_hash.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'phi2': import phi2_hash header_hash = phi2_hash.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'x16r': import x16r_hash header_hash = x16r_hash.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'x16s': import x16s_hash header_hash = x16s_hash.getPoWHash(header) elif os.getenv("COIN_ALGORITHM") == 'timetravel10': import timetravel10_hash header_hash = timetravel10_hash.getPoWHash(header) else: header_hash = double_sha(header) elif os.getenv("COIN_TYPE") == 'zcash': _time = _params[2] _nonce_2 = _params[3] _soln = _params[4] _nonce = _nonce_1 + _nonce_2.encode() if len(_nonce) != 64: return { 'id': _id, 'result': False, 'error': [20, 'incorrect size of nonce'] } if os.getenv("COIN_ALGORITHM") == 'zhash' and len(_soln) != 202: return { 'id': _id, 'result': False, 'error': [20, 'incorrect size of solution'] } elif os.getenv("COIN_ALGORITHM") != 'zhash' and len(_soln) != 2694: return { 'id': _id, 'result': False, 'error': [20, 'incorrect size of solution'] } n_time_int = int(_time, 16) curtime_int = int(_block_template.curtime, 16) if n_time_int < curtime_int: return { 'id': _id, 'result': False, 'error': [20, 'ntime out of range'] } header = _block_template.serialize_block_header( _time.encode(), _nonce) # 140 bytes header_soln = header + binascii.unhexlify(_soln) header_hash = double_sha(header_soln) block_hex = _block_template.serialize_block( header, binascii.unhexlify(_soln), None) else: raise Exception('invalid coin type') header_bignum = uint256_from_str(header_hash) share_diff = os.getenv("POW_LIMIT") / header_bignum logger.debug('share diff : {0:.8f}'.format(share_diff)) diff = self.user_diffs[connection_ref.username] if share_diff < diff: # logger.debug('low difficulty share of %s' % share_diff) logger.info( 'rejected share, worker : %s, reason : low difficulty share' % _worker_name) self.database_ref.insert_accepted_share( username, worker, False, False, _block_template.block_height, share_diff, _block_template.pool_reward, diff) return { 'id': _id, 'result': None, 'error': [23, 'low difficulty share of %s' % share_diff] } if not self.job_manager.register_submit(_nonce_1, _nonce_2, _nonce, _time): logger.info( 'rejected share, worker : %s, reason : duplicate share' % _worker_name) return { 'id': _id, 'result': None, 'error': [22, 'duplicate share'] } if share_diff >= _block_template.difficulty * 0.99: block_hash = binascii.hexlify( hash_util.reverse_bytes(header_hash)).decode() if os.getenv("COIN") in [ 'monacoin', 'feathercoin', 'phoenixcoin', 'vertcoin', 'shield' ]: temp_hash = double_sha(header) block_hash = binascii.hexlify( hash_util.reverse_bytes(temp_hash)).decode() logger.info('Try new block share, worker : %s, share diff : %s' % (_worker_name, share_diff)) share_result = self.coin_rpc.submit_block( binascii.hexlify(block_hex).decode()) if share_result is None: logger.info('Found Block, result : %s, block hash : %s' % (share_result, block_hash)) result_hash = Interfaces.block_updater.update_block( repeat=False, block_hash=block_hash) if result_hash is not None: block_hash = result_hash self.database_ref.insert_accepted_share( username, worker, True, True, _block_template.block_height, share_diff, _block_template.pool_reward, diff, block_hash) else: logger.error( 'undefined share_result %s, block hash %s, coinbase tx %s' % (share_result, block_hash, binascii.hexlify(_block_template.coinbase_tx))) self.database_ref.insert_accepted_share( username, worker, False, False, _block_template.block_height, share_diff, _block_template.pool_reward, diff) if os.getenv("COIN_TYPE") == 'bitcoin': logger.error('Header : %s' % binascii.hexlify(header).decode()) else: logger.error('Header : %s' % binascii.hexlify(header_soln).decode()) return { 'id': _id, 'result': None, 'error': [20, 'invalid solution'] } else: logger.info('accepted share, worker : %s, share diff : %s' % (_worker_name, '{0:.8f}'.format(share_diff))) self.database_ref.insert_accepted_share( username, worker, True, False, _block_template.block_height, share_diff, _block_template.pool_reward, diff) return {'id': _id, 'result': True, 'error': None}