def build_tx_from_skeleton(tx_skeleton, txos_storage, block_height, historical=False, non_context=False): ''' By given tx_skeleton and txos_storage return transaction. If transaction is invalid or any input/output isn't available exception will be raised. Optionally, if `historical` is True we will check output_indexes both in mempool and spent outputs. ''' tx = Transaction(txos_storage=txos_storage) for _i in tx_skeleton.input_indexes: tx.inputs.append(txos_storage.confirmed[_i]) for _o in tx_skeleton.output_indexes: if historical: try: tx.outputs.append(txos_storage.confirmed.find(_o)) except: tx.outputs.append(txos_storage.mempool[_o]) else: tx.outputs.append(txos_storage.mempool[_o]) tx.additional_excesses = tx_skeleton.additional_excesses.copy() tx.combined_excesses = tx_skeleton.combined_excesses.copy() if historical or non_context: assert tx.non_context_verify(block_height=block_height) else: assert tx.verify(block_height=block_height) return tx
def build_tx_from_skeleton(tx_skeleton, txos_storage, excesses_storage, block_height, block_version, rtx, historical=False, non_context=False): ''' By given tx_skeleton and txos_storage return transaction. If transaction is invalid or any input/output isn't available exception will be raised. Optionally, if `historical` is True we will check output_indexes both in mempool and spent outputs. ''' tx = Transaction(txos_storage=txos_storage, excesses_storage=excesses_storage) for _i in tx_skeleton.input_indexes: if historical or non_context: tx.inputs.append(txos_storage.confirmed.find(_i, rtx=rtx)) else: tx.inputs.append(txos_storage.confirmed.get(_i, rtx=rtx)) for _o in tx_skeleton.output_indexes: if historical or non_context: # About non_context: if we are on one branch and build block from another one # and this block contain output which is already commited on our branch (tx is # confirmed on both branches) we should get txo from confirmed storage try: tx.outputs.append(txos_storage.confirmed.find(_o, rtx=rtx)) except: tx.outputs.append(txos_storage.mempool[_o]) else: tx.outputs.append(txos_storage.mempool[_o]) tx.additional_excesses = tx_skeleton.additional_excesses.copy() tx.updated_excesses = tx_skeleton.updated_excesses.copy() tx.mixer_offset = tx_skeleton.mixer_offset if historical or non_context: assert tx.non_context_verify(block_height=block_height) else: assert tx.verify(block_height=block_height, block_version=block_version, rtx=rtx) return tx
def core_loop(syncer, config): message_queue = syncer.queues['Blockchain'] init_storage_space(config) nodes = {} set_ask_for_blocks_hook(storage_space.blockchain, message_queue) set_ask_for_txouts_hook(storage_space.blocks_storage, message_queue) requests = {} message_queue.put({"action": "give nodes list reminder"}) def send_message(destination, message): if not 'id' in message: message['id'] = uuid4() if not 'sender' in message: message['sender'] = "Blockchain" syncer.queues[destination].put(message) def send_to_nm(message): send_message("NetworkManager", message) logger.debug("Start of core loop") while True: sleep(0.05) put_back_messages = [] while not message_queue.empty(): message = message_queue.get() if 'time' in message and message['time'] > time( ): # delay this message put_back_messages.append(message) continue logger.info("Processing message %s" % message) if not 'action' in message: #it is response if message['id'] in requests: # response is awaited if requests[message['id']] == "give nodes list": requests.pop(message['id']) message_queue.put({ "action": "take nodes list", "nodes": message["result"] }) else: pass #Drop continue try: if message["action"] == "take the headers": process_new_headers(message) if message["action"] == "take the blocks": initial_tip = storage_space.blockchain.current_tip process_new_blocks(message) after_tip = storage_space.blockchain.current_tip if not after_tip == initial_tip: notify_all_nodes_about_new_tip(nodes, send_to_nm) if message["action"] == "take the txos": process_new_txos(message) if message["action"] == "give blocks": process_blocks_request(message, send_message) if message["action"] == "give next headers": process_next_headers_request(message, send_message) if message["action"] == "give txos": process_txos_request(message, send_message) if message["action"] == "find common root": process_find_common_root(message, send_message) if message["action"] == "find common root response": process_find_common_root_reponse(message, nodes[message["node"]], send_message) if message["action"] == "give TBM transaction": process_tbm_tx_request(message, send_message) if message["action"] == "take TBM transaction": process_tbm_tx(message, send_to_nm, nodes) if message["action"] == "give tip height": send_message( message["sender"], { "id": message["id"], "result": storage_space.blockchain.current_height }) if message["action"] == "take tip info": if not message["node"] in nodes: nodes[message["node"]] = {'node': message["node"]} process_tip_info(message, nodes[message["node"]], send=send_to_nm) except DOSException as e: logger.info("DOS Exception %s" % str(e)) #raise e #TODO send to NM except Exception as e: raise e if message["action"] == "give block template": block = storage_space.mempool_tx.give_block_template() ser_head = block.header.serialize() send_message(message["sender"], { "id": message["id"], "result": ser_head }) if message["action"] == "take solved block template": try: initial_tip = storage_space.blockchain.current_tip header = Header() header.deserialize(message["solved template"]) solved_block = storage_space.mempool_tx.get_block_by_header_solution( header) storage_space.headers_manager.add_header( solved_block.header) storage_space.headers_manager.context_validation( solved_block.header.hash) solved_block.non_context_verify() storage_space.blockchain.add_block(solved_block) send_message(message["sender"], { "id": message["id"], "result": "Accepted" }) after_tip = storage_space.blockchain.current_tip if not after_tip == initial_tip: notify_all_nodes_about_new_tip(nodes, send_to_nm) except Exception as e: raise e send_message(message["sender"], { "id": message["id"], "error": str(e) }) if message["action"] == "get confirmed balance stats": if storage_space.mempool_tx.key_manager: stats = storage_space.mempool_tx.key_manager.get_confirmed_balance_stats( storage_space.utxo_index, storage_space.txos_storage, storage_space.blockchain.current_height) send_message(message["sender"], { "id": message["id"], "result": stats }) else: send_message(message["sender"], { "id": message["id"], "error": "No registered key manager" }) if message["action"] == "get confirmed balance list": if storage_space.mempool_tx.key_manager: _list = storage_space.mempool_tx.key_manager.get_confirmed_balance_list( storage_space.utxo_index, storage_space.txos_storage, storage_space.blockchain.current_height) send_message(message["sender"], { "id": message["id"], "result": _list }) else: send_message(message["sender"], { "id": message["id"], "error": "No registered key manager" }) if message["action"] == "give new address": if storage_space.mempool_tx.key_manager: texted_address = storage_space.mempool_tx.key_manager.new_address( ).to_text() send_message(message["sender"], { "id": message["id"], "result": texted_address }) else: send_message(message["sender"], { "id": message["id"], "error": "No registered key manager" }) if message["action"] == "give private key": if storage_space.mempool_tx.key_manager: km = storage_space.mempool_tx.key_manager a = Address() a.from_text(message["address"]) serialized_pk = km.priv_by_address(a).serialize() send_message(message["sender"], { "id": message["id"], "result": serialized_pk }) else: send_message(message["sender"], { "id": message["id"], "error": "No registered key manager" }) if message["action"] == "take private key": if storage_space.mempool_tx.key_manager: km = storage_space.mempool_tx.key_manager pk = PrivateKey() pk.deserialize(message['privkey']) km.add_privkey(pk) send_message(message["sender"], { "id": message["id"], "result": "imported" }) else: send_message(message["sender"], { "id": message["id"], "error": "No registered key manager" }) if message["action"] == "give synchronization status": our_height = storage_space.blockchain.current_height best_known_header = storage_space.headers_manager.best_header_height try: best_advertised_height = max([ nodes[node]["height"] for node in nodes if "height" in nodes[node] ]) except: best_advertised_height = None send_message( message["sender"], { "id": message["id"], "result": { 'height': our_height, 'best_known_header': best_known_header, 'best_advertised_height': best_advertised_height } }) if message["action"] == "send to address": value = int(message["value"]) taddress = message["address"] a = Address() a.from_text(taddress) if storage_space.mempool_tx.key_manager: _list = storage_space.mempool_tx.key_manager.get_confirmed_balance_list( storage_space.utxo_index, storage_space.txos_storage, storage_space.blockchain.current_height) list_to_spend = [] summ = 0 for address in _list: for texted_index in _list[address]: if summ > value: continue if isinstance(_list[address][texted_index], int): _index = base64.b64decode( texted_index.encode()) utxo = storage_space.txos_storage.confirmed[ _index] if not utxo.lock_height <= storage_space.blockchain.current_height: continue list_to_spend.append(utxo) summ += _list[address][texted_index] if summ < value: send_message( message["sender"], { "id": message["id"], "error": "Not enough matured coins" }) tx = Transaction( txos_storage=storage_space.txos_storage, key_manager=storage_space.mempool_tx.key_manager) for utxo in list_to_spend: tx.push_input(utxo) tx.add_destination((a, value)) tx.generate() tx.verify() storage_space.mempool_tx.add_tx(tx) tx_skel = TransactionSkeleton(tx=tx) notify_all_nodes_about_tx(tx_skel.serialize( rich_format=True, max_size=40000), nodes, send_to_nm, _except=[], mode=1) send_message(message["sender"], { "id": message["id"], "result": "generated" }) else: send_message(message["sender"], { "id": message["id"], "error": "No registered key manager" }) #message from core_loop if message["action"] == "check txouts download status": txos = message["txos_hashes"] to_be_downloaded = [] for txo in txos: if not storage_space.txos_storage.known(txo): to_be_downloaded.append(txo) if not to_be_downloaded: continue #We are good, txouts are already downloaded already_asked_nodes = message["already_asked_nodes"] asked = False for node_params in nodes: node = nodes[node_params] if node in already_asked_nodes: continue already_asked_nodes += [node] send_to_nm({ "action": "give txos", "txos_hashes": b"".join(to_be_downloaded), "num": len(to_be_downloaded), "id": str(uuid4()), "node": node_params }) new_message = { "action": "check txouts download status", "txos_hashes": to_be_downloaded, "already_asked_nodes": already_asked_nodes, "id": str(uuid4()), "time": int(time() + 300) } asked = True put_back_messages.append(new_message) break if not asked: #We already asked all applicable nodes message["time"] = int(time()) + 3600 message["already_asked_nodes"] = [] put_back_messages.append( message) # we will try to ask again in an hour #message from core_loop if message["action"] == "check blocks download status": #TODO download many blocks at once block_hashes = message["block_hashes"] to_be_downloaded = [] lowest_height = 1e10 for block_hash in block_hashes: if block_hash in storage_space.blocks_storage: continue #We are good, block already downloaded if not block_hash in storage_space.blockchain.awaited_blocks: continue #For some reason we don't need this block anymore to_be_downloaded.append(block_hash) if storage_space.headers_storage[ block_hash].height < lowest_height: lowest_height = storage_space.headers_storage[ block_hash].height already_asked_nodes = message["already_asked_nodes"] asked = False for node_params in nodes: node = nodes[node_params] if node in already_asked_nodes: continue if node["height"] < lowest_height: continue already_asked_nodes += [node] send_to_nm({ "action": "give blocks", "block_hashes": bytes(b"".join(block_hashes)), 'num': len(block_hashes), "id": str(uuid4()), "node": node_params }) new_message = { "action": "check blocks download status", "block_hashes": to_be_downloaded, "already_asked_nodes": already_asked_nodes, "id": str(uuid4()), "time": int(time() + 300) } asked = True put_back_messages.append(new_message) break if not asked: #We already asked all applicable nodes message["time"] = int(time()) + 3600 message["already_asked_nodes"] = [] put_back_messages.append( message) # we will try to ask again in an hour if message["action"] == "take nodes list": for node in message["nodes"]: if not node in nodes: #Do not overwrite nodes[node] = {"node": node} disconnected_nodes = [] for existing_node in nodes: if not existing_node in message["nodes"]: disconnected_nodes.append(existing_node) for dn in disconnected_nodes: nodes.pop(dn) if message["action"] == "give nodes list reminder": _id = str(uuid4()) send_to_nm({ "action": "give nodes list", "sender": "Blockchain", "id": _id }) requests[_id] = "give nodes list" put_back_messages.append({ "action": "give nodes list reminder", "time": int(time()) + 3 }) for _message in put_back_messages: message_queue.put(_message) try: check_sync_status(nodes, send_to_nm) except Exception as e: logger.error(e)