示例#1
0
def start_mining():
    while True:
        print("Start mining new block")
        initial_time = time()
        block_template = basic_request('getblocktemplate')
        block_template = base64.b64decode(block_template.encode())
        header = Header()
        header.deserialize(block_template)
        print(
            "Got template. Block height %d. Block target %d (* 2**220). Average hashes for block %d. Block timestamp %d"
            % (header.height, header.target / 2**220, 2**256 / header.target,
               header.timestamp))

        partial_template = block_template[:-16]
        nonce = 0
        next_level = 4096
        while not check_solution(partial_template, nonce, header.target):
            nonce += 1
            if not nonce % next_level:
                next_level *= 2
                print("Nonce reached %d" % nonce)
        final_time = time()
        print("Get solution. Nonce = %d. Hashrate %d H/s" %
              (nonce, int(nonce / (final_time - initial_time))))
        solution = partial_template + nonce.to_bytes(16, 'big')
        encoded_solution = base64.b64encode(solution).decode()
        res = basic_request('validatesolution', [encoded_solution])
        print("Submitted block. Result %s" % res)
示例#2
0
def process_find_common_root(message, node_info, rtx, core):
  #node_info is ignored, added to unify process_metadata function sugnature
  try:
    serialized_header = message["serialized_header"]
    header = Header()
    header.deserialize_raw(serialized_header)
  except:
    raise DOSException()
  result = []
  for pointer in [header.hash]+header.popow.pointers:
    if not core.storage_space.headers_storage.has(pointer, rtx=rtx):
      result.append(HEADERSTATE.UNKNOWN)
      continue
    ph = core.storage_space.headers_storage.get(pointer, rtx=rtx)
    if not ph.connected_to_genesis:
      result.append(HEADERSTATE.ISOLATED)
      continue
    if core.storage_space.headers_manager.find_ancestor_with_height(core.storage_space.blockchain.current_tip(rtx=rtx), ph.height, rtx=rtx) == pointer:
      result.append(HEADERSTATE.MAINCHAIN)
      continue
    result.append(HEADERSTATE.INFORK)

  core.send_to_subprocess(message['sender'], \
     {"action":"find common root response", "header_hash":header.hash,
      "flags_num": len(result), 
      "known_headers": b"".join([i.to_bytes(1,"big") for i in result]), 
      "id":message['id'], "node": message["node"] })
示例#3
0
def process_solution(solution_type, message, wtx, core):
    try:
        if solution_type == "block template":
            header = Header()
            header.deserialize(message["solved template"])
            solved_block = core.storage_space.mempool_tx.get_block_by_header_solution(
                header)
        elif solution_type == "work":
            nonce, partial_work = message['nonce'], message['partial_hash']
            solved_block = core.storage_space.mempool_tx.work_block_assoc[
                partial_work]
            solved_block.header.nonce = nonce
        result = add_solved_block(solved_block, wtx, core)
        if result == "stale":
            core.send_to_subprocess(message["sender"], {
                "id": message["id"],
                "result": "Stale"
            })
            core.logger.error("Stale work submitted: height %d" %
                              (header.height))
            return
        elif result == "accepted":
            core.send_to_subprocess(message["sender"], {
                "id": message["id"],
                "result": "Accepted"
            })
    except Exception as e:
        core.logger.error("Wrong block solution %s" % str(e))
        core.send_to_subprocess(message["sender"], {
            "id": message["id"],
            "error": str(e),
            'result': 'error'
        })
示例#4
0
def process_new_headers(message, node_info, wtx, core):
    dupplication_header_dos = False  #TODO grammatical typo?
    try:
        serialized_headers = message["headers"]
        num = message["num"]
        header = None
        for i in range(num):
            header = Header()
            serialized_headers = header.deserialize_raw(serialized_headers)
            if not core.storage_space.headers_storage.has(header.hash,
                                                          rtx=wtx):
                core.storage_space.headers_manager.add_header(header, wtx=wtx)
                if not i % 20:
                    core.notify(
                        "best header",
                        core.storage_space.headers_manager.best_header_height)
            else:
                dupplication_header_dos = True
        if ("common_root" in node_info) and ("long_reorganization"
                                             in node_info["common_root"]):
            if core.storage_space.headers_manager.get_best_tip(
            )[0] == header.hash:
                #not reorg anymore
                node_info["common_root"].pop("long_reorganization", None)
                our_tip_hash = core.storage_space.blockchain.current_tip(
                    rtx=wtx)
                core.storage_space.blockchain.update(
                    wtx=wtx, reason="downloaded new headers")
                send_tip_info(node_info=node_info,
                              our_tip_hash=our_tip_hash,
                              rtx=wtx,
                              core=core)
            elif node_info["common_root"][
                    "long_reorganization"] == header.height:
                request_num = min(
                    256, node_info["height"] -
                    core.storage_space.headers_storage.get(
                        node_info["common_root"]["root"], rtx=wtx).height)
                send_next_headers_request(header.hash,
                                          request_num,
                                          message["node"],
                                          send=partial(core.send_to_subprocess,
                                                       "NetworkManager"))
                if node_info["height"] - header.height > request_num:
                    node_info["common_root"][
                        "long_reorganization"] = header.height + request_num
                else:
                    node_info["common_root"].pop("long_reorganization", None)
        core.storage_space.blockchain.update(wtx=wtx,
                                             reason="downloaded new headers")
    except Exception as e:
        raise DOSException()  #TODO add info
示例#5
0
def process_new_headers(message):
    dupplication_header_dos = False
    try:
        serialized_headers = message["headers"]
        num = message["num"]
        for i in range(num):
            header = Header()
            serialized_headers = header.deserialize_raw(serialized_headers)
            if header.hash in storage_space.headers_storage:
                dupplication_header_dos = True
                continue
            storage_space.headers_manager.add_header(header)
        storage_space.blockchain.update(reason="downloaded new headers")
    except Exception as e:
        raise e
示例#6
0
def generate_genesis(tx, storage_space):
    '''
        1. spend inputs and add outputs and excesses from tx to storage
        2. calc new mercles
        3. generate header
        4. rollback outputs
    '''
    storage = storage_space.txos_storage
    excesses = storage_space.excesses_storage

    merkles = storage.apply_tx_get_merkles_and_rollback(tx) + [
        excesses.apply_tx_get_merkles_and_rollback(tx)
    ]
    popow = PoPoW([])
    votedata = VoteData()
    target = initial_target
    header = Header(height=0,
                    supply=tx.coinbase.value,
                    merkles=merkles,
                    popow=popow,
                    votedata=votedata,
                    timestamp=int(time()),
                    target=target,
                    version=int(1),
                    nonce=b"\x00" * 16)

    tx_skeleton = TransactionSkeleton(tx=tx)
    new_block = Block(storage_space, header, tx_skeleton)
    return new_block
示例#7
0
def generate_genesis(tx, storage_space, wtx):
    '''
        1. spend inputs and add outputs and excesses from tx to storage
        2. calc new mercles
        3. generate header
        4. rollback outputs
    '''
    storage = storage_space.txos_storage
    excesses = storage_space.excesses_storage

    exc_merkle = excesses.apply_block_tx_get_merkles_and_rollback(
        tx, wtx=wtx
    )  # it should be calced first, since we nned to calc address_excess_num_index
    merkles = storage.apply_block_tx_get_merkles_and_rollback(
        tx, wtx=wtx) + [exc_merkle]
    popow = PoPoW([])
    votedata = VoteData()
    target = initial_target
    full_offset = tx.mixer_offset
    header = Header(height=0,
                    supply=tx.coinbase.value,
                    full_offset=full_offset,
                    merkles=merkles,
                    popow=popow,
                    votedata=votedata,
                    timestamp=int(time()),
                    target=target,
                    version=int(1),
                    nonce=b"\x00" * 16)

    tx_skeleton = TransactionSkeleton(tx=tx)
    new_block = Block(storage_space, header, tx_skeleton)
    return new_block
示例#8
0
def start_mining():
  while True:
    print("Start mining new block")
    initial_time = time()
    height_check_time = initial_time
    basic_nonce = randint(0, int(256**3))
    block_template = basic_request('getblocktemplate')
    block_template = base64.b64decode(block_template.encode())
    header = Header()
    header.deserialize(block_template)
    print("Got template. Block height %d. Block target %d (* 2**220). Average hashes for block %d. Block timestamp %d"%(header.height,header.target/2**220, 2**256/header.target, header.timestamp)) 
     
    partial_template = block_template[:-16]
    nonce = 0
    step = next_level = 16
    update_block = False
    height, target = header.height, header.target
    partial_hash = header.partial_hash
    solution_found = False
    while not solution_found:
      res = pp_handler.light_search(height, partial_hash, target.to_bytes(32,"big"), start_nonce = basic_nonce+nonce, iterations = next_level, step=step)
      solution_found = res['solution_found']
      if res['solution_found']:
        final_nonce, final_hash = res['nonce'], res['final_hash']
        break
      nonce += next_level
      if time()-height_check_time>5:
        height_check_time = time()
        if height<=get_height():
          print("New block on network")
          update_block = True
          break
      if not nonce%next_level:
        next_level*=2
        print("Nonce reached %d"%nonce)

        
    final_time = time()
    if update_block:
      print("Hashrate %d H/s"%(int(nonce/(final_time-initial_time))))
      continue
    print("Get solution. Nonce = %d (final_nonce %d). Hashrate %d H/s"%(nonce, final_nonce, int(nonce/(final_time-initial_time))))
    solution =partial_template +final_nonce.to_bytes(8,'big')
    encoded_solution = base64.b64encode(solution).decode()
    res = basic_request('validatesolution', [encoded_solution])
    print("Submitted block. Result %s"%res)
示例#9
0
def generate_block_template(tx,
                            storage_space,
                            get_tx_from_mempool=True,
                            timestamp=None):
    '''
        Generate block template: block is correct but nonce (by default) is equal to zero.
        Thus difficulty target (almost always) isn't met.
        arguments:
          tx [mandatory]: transaction which contains coinbase output. It also may contain other inputs and outputs.
          storage_space [mandatory] : -
          get_tx_from_mempool [optional, default True]: if get_tx_from_mempool, transaction from mempool will be merged to block_transaction. If this merge will produce invalid tx (for instance tx from mempool spends the same inputs as tx with coinbase), tx from mempool will be discarded.

        Inner logic:
        1. apply block_tx to txos_storage and excesses_storage
        2. calc new merkles
        3. generate header with new merkles
        4. generate block by appending tx_skeleton and new header
        5. rollback block_tx
    '''

    storage = storage_space.txos_storage
    excesses = storage_space.excesses_storage
    current_block = storage_space.blocks_storage[
        storage_space.blockchain.current_tip]
    if get_tx_from_mempool:
        try:
            tx = tx.merge(storage_space.mempool_tx.give_tx())
        except:
            pass

    merkles = storage.apply_tx_get_merkles_and_rollback(tx) + [
        excesses.apply_tx_get_merkles_and_rollback(tx)
    ]
    popow = current_block.header.next_popow()
    #We subtract relay fee, since coinbase value contain relay fees, but it isn't new money, but redistribution
    supply = current_block.header.supply + tx.coinbase.value - tx.calc_new_outputs_fee(
    ) - tx.relay_fee
    height = current_block.header.height + 1
    votedata = VoteData()
    target = next_target(current_block.hash, storage_space.headers_storage)
    if not timestamp:
        timestamp = max(
            int(time()), storage_space.headers_storage[
                storage_space.blockchain.current_tip].timestamp + 1)
    header = Header(height=height,
                    supply=supply,
                    merkles=merkles,
                    popow=popow,
                    votedata=votedata,
                    timestamp=timestamp,
                    target=target,
                    version=int(1),
                    nonce=b"\x00" * 16)

    tx_skeleton = TransactionSkeleton(tx=tx)
    new_block = Block(storage_space, header, tx_skeleton)
    return new_block
示例#10
0
def process_find_common_root(message, send_message):
    serialized_header = message["serialized_header"]
    header = Header()
    header.deserialize_raw(serialized_header)
    result = []
    for pointer in [header.hash] + header.popow.pointers:
        if not pointer in storage_space.headers_storage:
            result.append(UNKNOWN)
            continue
        ph = storage_space.headers_storage[pointer]
        if not ph.connected_to_genesis:
            result.append(ISOLATED)
            continue
        if storage_space.headers_manager.find_ancestor_with_height(
                storage_space.blockchain.current_tip, ph.height) == pointer:
            result.append(MAINCHAIN)
            continue
        result.append(INFORK)

    send_message(message['sender'], \
       {"action":"find common root response", "header_hash":header.hash,
        "flags_num": len(result),
        "known_headers": b"".join([i.to_bytes(1,"big") for i in result]),
        "id":message['id'], "node": message["node"] })
示例#11
0
 def header(self):
     try:
         return self._header
     except:
         self._header = Header()
         return self._header
示例#12
0
 def __init__(self, storage_space, header=None, transaction_skeleton=None):
     self._header = header if header else Header()
     self.transaction_skeleton = transaction_skeleton if transaction_skeleton else TransactionSkeleton(
     )
     self.tx = None
     self.storage_space = storage_space
示例#13
0
def generate_block_template(tx,
                            storage_space,
                            wtx,
                            get_tx_from_mempool=True,
                            timestamp=None,
                            dev_reward_vote=b"\x00"):
    '''
        Generate block template: block is correct but nonce (by default) is equal to zero.
        Thus difficulty target (almost always) isn't met.
        arguments:
          tx [mandatory]: transaction which contains coinbase output. It also may contain other inputs and outputs.
          storage_space [mandatory] : -
          get_tx_from_mempool [optional, default True]: if get_tx_from_mempool, transaction from mempool will be merged to block_transaction. If this merge will produce invalid tx (for instance tx from mempool spends the same inputs as tx with coinbase), tx from mempool will be discarded.

        Inner logic:
        1. apply block_tx to txos_storage and excesses_storage
        2. calc new merkles
        3. generate header with new merkles
        4. generate block by appending tx_skeleton and new header
        5. rollback block_tx
    '''

    storage = storage_space.txos_storage
    excesses = storage_space.excesses_storage
    current_block = storage_space.blocks_storage.get(
        storage_space.blockchain.current_tip(rtx=wtx), rtx=wtx)
    if get_tx_from_mempool:
        try:
            tx = tx.merge(storage_space.mempool_tx.give_tx(), rtx=wtx)
        except:
            pass

    exc_merkle = excesses.apply_block_tx_get_merkles_and_rollback(
        tx, wtx=wtx
    )  # it should be calced first, since we nned to calc address_excess_num_index
    merkles = storage.apply_block_tx_get_merkles_and_rollback(
        tx, wtx=wtx) + [exc_merkle]

    popow = current_block.header.next_popow()
    supply = current_block.header.supply + tx.minted_value - tx.calc_new_outputs_fee(
    )
    height = current_block.header.height + 1
    votedata = VoteData()
    target = next_target(current_block.hash,
                         storage_space.headers_storage,
                         rtx=wtx)
    full_offset = sum_offset(current_block.header.full_offset, tx.mixer_offset)
    if not timestamp:
        timestamp = max(
            int(time()),
            storage_space.headers_storage.get(
                storage_space.blockchain.current_tip(rtx=wtx),
                rtx=wtx).timestamp + 1)
    header = Header(height=height,
                    supply=supply,
                    full_offset=full_offset,
                    merkles=merkles,
                    popow=popow,
                    votedata=votedata,
                    timestamp=timestamp,
                    target=target,
                    version=int(1),
                    nonce=b"\x00" * 16)

    tx_skeleton = TransactionSkeleton(tx=tx)
    new_block = Block(storage_space, header, tx_skeleton)
    return new_block
示例#14
0
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)