async def getwork(*args, **kwargs): """ https://en.bitcoin.it/wiki/Getwork Result: 1. "data" (hex string, required) block data 2. "target" (hex string, required) little endian hash target """ if len(args) == 0: now = int(time() - V.BLOCK_GENESIS_TIME) for block in getwork_cashe.values(): if block.previous_hash != chain_builder.best_block.hash: continue if now - block.time < 10: mining_block = block break else: mining_block = await get_mining_block(int(kwargs['password'])) getwork_cashe[mining_block.merkleroot] = mining_block mining_block.bits2target() # Pre-processed SHA-2 input chunks data = mining_block.b # 80 bytes data += a2b_hex('800000000000000000000000000000000000000000000000' '000000000000000000000000000000000000000000000280') # 48+80=128bytes new_data = b'' for i in range(0, 128, 4): new_data += data[i:i + 4][::-1] if extra_target: return {"data": new_data.hex(), "target": extra_target.to_bytes(32, 'big').hex()} else: return {"data": new_data.hex(), "target": mining_block.target_hash.hex()} else: data = a2b_hex(args[0]) new_data = b'' for i in range(0, 128, 4): new_data += data[i:i + 4][::-1] block = Block.from_binary(binary=new_data[:80]) if block.previous_hash != chain_builder.best_block.hash: return 'PreviousHash don\'t match' if block.merkleroot in getwork_cashe: block.txs.extend(getwork_cashe[block.merkleroot].txs) result = await submitblock(block, **kwargs) if result is None: return True elif extra_target and block.pow_check(extra_target=extra_target): return True else: log.debug("GetWorkReject by \"{}\"".format(result)) return result else: log.debug("GetWorkReject by \"Not found merkleroot.\"") return 'Not found merkleroot'
def fill_newblock_info(data): new_block: Block = Block.from_binary(binary=data['binary']) log.debug("fill newblock height={} newblock={}".format( data.get('height'), new_block.hash.hex())) proof: TX = data['proof'] new_block.txs.append(proof) new_block.flag = data['block_flag'] my_block = chain_builder.get_block(new_block.hash) if my_block: raise BlockChainError('Already inserted block {}'.format(my_block)) before_block = chain_builder.get_block(new_block.previous_hash) if before_block is None: log.debug("Cannot find beforeBlock, try to ask outside node") # not found beforeBlock, need to check other node have the the block new_block.inner_score *= 0.70 # unknown previousBlock, score down before_block = make_block_by_node(blockhash=new_block.previous_hash, depth=0) new_height = before_block.height + 1 proof.height = new_height new_block.height = new_height # work check # TODO: correct position? if not new_block.pow_check(): raise BlockChainError('Proof of work is not satisfied') # Append general txs for txhash in data['txs'][1:]: tx = tx_builder.get_tx(txhash) if tx is None: new_block.inner_score *= 0.75 # unknown tx, score down log.debug("Unknown tx, try to download") r = ask_node(cmd=DirectCmd.TX_BY_HASH, data={'txhash': txhash}, f_continue_asking=True) if isinstance(r, str): raise BlockChainError( 'Failed unknown tx download "{}"'.format(r)) tx: TX = r tx.height = None check_tx(tx, include_block=None) tx_builder.put_unconfirmed(tx) log.debug("Success unknown tx download {}".format(tx)) tx.height = new_height new_block.txs.append(tx) return new_block
def load_boot_file(url=None): normal_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'boot.json') extra_path = os.path.join(V.DB_HOME_DIR, 'boot.json') if url: data = requests.get(url=url).json() elif os.path.exists(normal_path): with open(normal_path, mode='r') as fp: data = json.load(fp) elif os.path.exists(extra_path): with open(extra_path, mode='r') as fp: data = json.load(fp) else: raise FileNotFoundError('Cannot find boot.json "{}" or "{}" ?'.format( normal_path, extra_path)) # load from exist boot.json genesis_block = Block.from_binary(binary=a2b_hex(data['genesis_binary'])) assert genesis_block.hash == a2b_hex(data['genesis_hash']) genesis_block.flag = C.BLOCK_GENESIS genesis_block.height = 0 for tx_dct in data['txs']: tx = TX.from_binary(binary=a2b_hex(tx_dct['binary'])) assert tx.hash == a2b_hex(tx_dct['hash']) tx.height = 0 genesis_block.txs.append(tx) connections = data['connections'] network_ver = data['network_ver'] if isinstance(data['params'], dict): # new type boot.json params = data['params'] params['consensus'] = { int(k): v for k, v in params['consensus'].items() } elif isinstance(data['params'], str): # old type boot.json params = original_mpk.unpackb(a2b_hex(data['params']), raw=True, encoding='utf8') else: raise Exception('Unknown type params') return genesis_block, params, network_ver, connections
def object_hook(dct): if isinstance(dct, dict) and '_bc4py_class_' in dct: if dct['_bc4py_class_'] == 'Block': block = Block.from_binary(binary=dct['binary']) block.height = dct['height'] block.flag = dct['flag'] block.txs.extend(object_hook(tx) for tx in dct['txs']) for tx in block.txs: tx.height = block.height return block elif dct['_bc4py_class_'] == 'TX': tx = TX.from_binary(binary=dct['binary']) tx.height = dct['height'] tx.signature.extend(tuple(sig) for sig in dct['signature']) tx.R = dct['R'] return tx else: raise Exception('Not found class name "{}"'.format( dct['_bc4py_class_'])) else: return dct
async def submitblock(*args, **kwargs): """ Attempts to submit new block to network. See https://en.bitcoin.it/wiki/BIP_0022 for full specification. Arguments 1. "hexdata" (string, required) the hex-encoded block data to submit 2. "dummy" (optional) dummy value, for compatibility with BIP22. This value is ignored. Result: null if success string if failed """ if len(args) == 0: raise ValueError('no argument found') block_hex_or_obj = args[0] if isinstance(block_hex_or_obj, str): block_bin = a2b_hex(block_hex_or_obj) # Block mined_block = Block.from_binary(binary=block_bin[:80]) if mined_block.previous_hash != chain_builder.best_block.hash: return 'PreviousHash don\'t match' previous_block = chain_builder.get_block(mined_block.previous_hash) mined_block.height = previous_block.height + 1 mined_block.flag = int(kwargs['password']) # tx length storage_flag = int.from_bytes(block_bin[80:81], 'little') if storage_flag < 0xfd: tx_len = storage_flag pos = 81 elif storage_flag == 0xfd: tx_len = int.from_bytes(block_bin[81:83], 'little') pos = 83 elif storage_flag == 0xfe: tx_len = int.from_bytes(block_bin[81:85], 'little') pos = 85 else: # == 0xff tx_len = int.from_bytes(block_bin[81:89], 'little') pos = 89 log.debug("RpcSubmit block: pos={}, tx_len={}".format(pos, tx_len)) # correct txs while len(block_bin) > pos: tx = TX() tx.b = block_bin tx.deserialize(first_pos=pos, f_raise=False) if tx.version != __chain_version__: return 'tx_ver do not match [{}!={}]'.format(tx.version, __chain_version__) pos += len(tx.b) mined_block.txs.append(tx_builder.get_tx(txhash=tx.hash, default=tx)) # check format if tx_len != len(mined_block.txs): return 'Do not match txlen [{}!={}]'.format(tx_len, len(mined_block.txs)) if pos != len(block_bin): return 'Do not match pos [{}!={}]'.format(pos, len(block_bin)) elif isinstance(block_hex_or_obj, Block): mined_block = block_hex_or_obj previous_block = chain_builder.get_block(mined_block.previous_hash) mined_block.height = previous_block.height + 1 mined_block.flag = int(kwargs['password']) else: return 'Unknown input? -> {}'.format(block_hex_or_obj) mined_block.update_pow() if mined_block.pow_check(): confirmed_generating_block(mined_block) return None # accepted else: return 'not satisfied work'