Esempio n. 1
0
def worker(host, port, node):
    logger = node.logger

    this_client = f"{host}:{port}"

    if node.IS_STOPPING:
        return

    dict_ip = {'ip': host}
    node.plugin_manager.execute_filter_hook('peer_ip', dict_ip)
    client_instance_worker = client.Client()

    if node.peers.is_banned(host) or dict_ip['ip'] == 'banned':
        node.logger.app_log.warning(f"IP {host} is banned, won't connect")
        return

    timeout_operation = 60  # timeout
    timer_operation = time.time()  # start counting

    try:

        s = socks.socksocket()

        if node.tor:
            s.setproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
        # s.setblocking(0)
        s.connect((host, port))
        node.logger.app_log.info(f"Outbound: Connected to {this_client}")
        client_instance_worker.connected = True

        # communication starter

        send(s, "version")
        send(s, node.version)

        data = receive(s)

        if data == "ok":
            node.logger.app_log.info(
                f"Outbound: Node protocol version of {this_client} matches our client"
            )
        else:
            raise ValueError(
                f"Outbound: Node protocol version of {this_client} mismatch")

        send(s, "getversion")
        peer_version = receive(s)
        if peer_version not in node.version_allow:
            raise ValueError(
                f"Outbound: Incompatible peer version {peer_version} from {this_client}"
            )

        send(s, "hello")

        # communication starter

    except Exception as e:
        node.logger.app_log.info(f"Could not connect to {this_client}: {e}")
        return  # can return here, because no lists are affected yet

    node.peers.store_mainnet(host, peer_version)
    try:
        peer_ip = s.getpeername()[0]
    except:
        # Should not happen, extra safety
        node.logger.app_log.warning(
            "Outbound: Transport endpoint was not connected")
        return

    if this_client not in node.peers.connection_pool:
        node.peers.append_client(this_client)
        node.logger.app_log.info(f"Connected to {this_client}")
        node.logger.app_log.info(
            f"Current active pool: {node.peers.connection_pool}")

    if not node.peers.is_banned(host) and node.peers.version_allowed(
            host, node.version_allow) and not node.IS_STOPPING:
        db_handler_instance = dbhandler.DbHandler(node.index_db,
                                                  node.ledger_path,
                                                  node.hyper_path, node.ram,
                                                  node.ledger_ram_file, logger)

    while not node.peers.is_banned(host) and node.peers.version_allowed(
            host, node.version_allow) and not node.IS_STOPPING:
        try:
            #ensure_good_peer_version(host)

            data = receive(s)  # receive data, one and the only root point
            # print(data)

            if data == "peers":
                subdata = receive(s)  # dict of "ip":"port"
                node.peers.peersync(subdata)

            elif data == "sync":
                if not time.time() <= timer_operation + timeout_operation:
                    timer_operation = time.time()  # reset timer

                try:
                    while len(node.syncing) >= 3:
                        if node.IS_STOPPING:
                            return
                        time.sleep(int(node.pause))

                    node.syncing.append(peer_ip)
                    # sync start

                    # send block height, receive block height
                    send(s, "blockheight")

                    node.logger.app_log.info(
                        f"Outbound: Sending block height to compare: {node.hdd_block}"
                    )
                    # append zeroes to get static length
                    send(s, node.hdd_block)

                    received_block_height = receive(
                        s)  # receive node's block height
                    node.logger.app_log.info(
                        f"Outbound: Node {peer_ip} is at block height: {received_block_height}"
                    )

                    if int(received_block_height) < node.hdd_block:
                        node.logger.app_log.warning(
                            f"Outbound: We have a higher block ({node.hdd_block}) than {peer_ip} ({received_block_height}), sending"
                        )

                        data = receive(s)  # receive client's last block_hash

                        # send all our followup hashes
                        node.logger.app_log.info(
                            f"Outbound: Will seek the following block: {data}")

                        # consensus pool 2 (active connection)
                        consensus_blockheight = int(received_block_height)
                        node.peers.consensus_add(peer_ip,
                                                 consensus_blockheight, s,
                                                 node.hdd_block)
                        # consensus pool 2 (active connection)

                        client_block = db_handler_instance.block_height_from_hash(
                            data)

                        if not client_block:
                            node.logger.app_log.warning(
                                f"Outbound: Block {data[:8]} of {peer_ip} not found"
                            )
                            if node.full_ledger:
                                send(s, "blocknf")
                            else:
                                send(s, "blocknfhb")
                            send(s, data)

                            if node.peers.warning(s, peer_ip, "Forked", 1):
                                raise ValueError(f"{peer_ip} is banned")

                        else:
                            node.logger.app_log.warning(
                                f"Outbound: Node is at block {client_block}"
                            )  # now check if we have any newer

                            if node.hdd_hash == data or not node.egress:
                                if not node.egress:
                                    node.logger.app_log.warning(
                                        f"Outbound: Egress disabled for {peer_ip}"
                                    )
                                    time.sleep(int(
                                        node.pause))  # reduce CPU usage
                                else:
                                    node.logger.app_log.info(
                                        f"Outbound: Node {peer_ip} has the latest block"
                                    )
                                    # TODO: this is unlikely to happen due to conditions above, consider removing
                                send(s, "nonewblk")

                            else:
                                blocks_fetched = db_handler_instance.blocksync(
                                    client_block)

                                node.logger.app_log.info(
                                    f"Outbound: Selected {blocks_fetched}")

                                send(s, "blocksfnd")

                                confirmation = receive(s)

                                if confirmation == "blockscf":
                                    node.logger.app_log.info(
                                        "Outbound: Client confirmed they want to sync from us"
                                    )
                                    send(s, blocks_fetched)

                                elif confirmation == "blocksrj":
                                    node.logger.app_log.info(
                                        "Outbound: Client rejected to sync from us because we're dont have the latest block"
                                    )

                    elif int(received_block_height) >= node.hdd_block:
                        if int(received_block_height) == node.hdd_block:
                            node.logger.app_log.info(
                                f"Outbound: We have the same block as {peer_ip} ({received_block_height}), hash will be verified"
                            )
                        else:
                            node.logger.app_log.warning(
                                f"Outbound: We have a lower block ({node.hdd_block}) than {peer_ip} ({received_block_height}), hash will be verified"
                            )

                        node.logger.app_log.info(
                            f"Outbound: block_hash to send: {node.hdd_hash}")
                        send(s, node.hdd_hash)

                        #ensure_good_peer_version(host)

                        # consensus pool 2 (active connection)
                        consensus_blockheight = int(
                            received_block_height
                        )  # str int to remove leading zeros
                        node.peers.consensus_add(peer_ip,
                                                 consensus_blockheight, s,
                                                 node.hdd_block)
                        # consensus pool 2 (active connection)

                except Exception as e:
                    node.logger.app_log.warning(f"Outbound: Sync failed {e}")
                finally:
                    node.syncing.remove(peer_ip)

            elif data == "blocknfhb":  # one of the possible outcomes
                block_hash_delete = receive(s)
                # print peer_ip
                # if max(consensus_blockheight_list) == int(received_block_height):
                if int(received_block_height) == node.peers.consensus_max:

                    blocknf(node,
                            block_hash_delete,
                            peer_ip,
                            db_handler_instance,
                            hyperblocks=True)

                    if node.peers.warning(s, peer_ip, "Rollback", 2):
                        raise ValueError(f"{peer_ip} is banned")

                sendsync(s, peer_ip, "Block not found", node)

            elif data == "blocknf":  # one of the possible outcomes
                block_hash_delete = receive(s)
                # print peer_ip
                # if max(consensus_blockheight_list) == int(received_block_height):
                if int(received_block_height) == node.peers.consensus_max:

                    blocknf(node, block_hash_delete, peer_ip,
                            db_handler_instance)

                    if node.peers.warning(s, peer_ip, "Rollback", 2):
                        raise ValueError(f"{peer_ip} is banned")

                sendsync(s, peer_ip, "Block not found", node)

            elif data == "blocksfnd":
                node.logger.app_log.info(
                    f"Outbound: Node {peer_ip} has the block(s)"
                )  # node should start sending txs in this step

                # node.logger.app_log.info("Inbound: Combined segments: " + segments)
                # print peer_ip
                if node.db_lock.locked():
                    node.logger.app_log.warning(
                        f"Skipping sync from {peer_ip}, syncing already in progress"
                    )

                else:
                    if int(node.last_block_timestamp) < (time.time() - 600):
                        block_req = node.peers.consensus_most_common
                        node.logger.app_log.warning(
                            "Most common block rule triggered")

                    else:
                        block_req = node.peers.consensus_max
                        node.logger.app_log.warning(
                            "Longest chain rule triggered")

                    #ensure_good_peer_version(host)

                    if int(received_block_height) >= block_req and int(
                            received_block_height) > node.last_block:
                        try:  # they claim to have the longest chain, things must go smooth or ban
                            node.logger.app_log.warning(
                                f"Confirming to sync from {peer_ip}")

                            send(s, "blockscf")
                            segments = receive(s)
                            #ensure_good_peer_version(host)

                        except:
                            if node.peers.warning(
                                    s, peer_ip,
                                    "Failed to deliver the longest chain", 2):
                                raise ValueError(f"{peer_ip} is banned")
                        else:
                            digest_block(node, segments, s, peer_ip,
                                         db_handler_instance)

                            # receive theirs
                    else:
                        send(s, "blocksrj")
                        node.logger.app_log.warning(
                            f"Inbound: Distant peer {peer_ip} is at {received_block_height}, should be at least {max(block_req,node.last_block+1)}"
                        )

                sendsync(s, peer_ip, "Block found", node)

                # block_hash validation end

            elif data == "nonewblk":
                # send and receive mempool
                if mp.MEMPOOL.sendable(peer_ip):
                    mempool_txs = mp.MEMPOOL.tx_to_send(peer_ip)
                    # node.logger.app_log.info("Outbound: Extracted from the mempool: " + str(mempool_txs))  # improve: sync based on signatures only
                    # if len(mempool_txs) > 0: #wont sync mempool until we send something, which is bad
                    # send own
                    send(s, "mempool")
                    send(s, mempool_txs)
                    # send own
                    # receive theirs
                    segments = receive(s)

                    node.logger.app_log.info(
                        mp.MEMPOOL.merge(segments, peer_ip,
                                         db_handler_instance.c, True))

                    # receive theirs
                    # Tell the mempool we just send our pool to a peer
                    mp.MEMPOOL.sent(peer_ip)
                sendsync(s, peer_ip, "No new block", node)

            elif data == "hyperlane":
                pass

            else:
                if data == '*':
                    raise ValueError("Broken pipe")
                raise ValueError(
                    f"Unexpected error, received: {str(data)[:32]}")

        except Exception as e:
            """
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno)
            """

            db_handler_instance.close()

            # remove from active pool
            node.peers.remove_client(this_client)
            node.logger.app_log.warning(
                f"Outbound: Disconnected from {this_client}: {e}")
            # remove from active pool

            # remove from consensus 2
            node.peers.consensus_remove(peer_ip)
            # remove from consensus 2

            node.logger.app_log.info(
                f"Connection to {this_client} terminated due to {e}")
            node.logger.app_log.info(
                f"---thread {threading.currentThread()} ended---")

            # properly end the connection
            s.close()

            # properly end the connection
            if node.debug:
                raise  # major debug client
            else:
                node.logger.app_log.info(f"Ending thread, because {e}")
                return

    if not node.peers.version_allowed(host, node.version_allow):
        node.logger.app_log.warning(
            f"Outbound: Ending thread, because {host} has too old a version: {node.peers.ip_to_mainnet[host]}"
        )
Esempio n. 2
0
def worker(host, port, node):
    logger = node.logger

    this_client = f"{host}:{port}"

    if node.IS_STOPPING:
        return

    dict_ip = {'ip': host}
    node.plugin_manager.execute_filter_hook('peer_ip', dict_ip)
    client_instance_worker = classes.Client()

    if node.peers.is_banned(host) or dict_ip['ip'] == 'banned':
        client_instance_worker.banned = True
        node.logger.app_log.warning(f"IP {host} is banned, won't connect")
        return

    timeout_operation = 60  # timeout
    timer_operation = time.time()  # start counting

    try:

        s = socks.socksocket()

        if node.tor_conf:
            s.setproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", 9050)
        # s.setblocking(0)
        s.connect((host, port))
        node.logger.app_log.info(f"Outbound: Connected to {this_client}")
        client_instance_worker.connected = True

        # communication starter

        send(s, "version")
        send(s, node.version)

        data = receive(s)

        if data == "ok":
            node.logger.app_log.info(
                f"Outbound: Node protocol version of {this_client} matches our client"
            )
        else:
            raise ValueError(
                f"Outbound: Node protocol version of {this_client} mismatch")

        # If we are post pow fork, then the peer has getversion command
        # if node.last_block >= POW_FORK - FORK_AHEAD:
        # Peers that are not up to date will disconnect since they don't know that command.
        # That is precisely what we need :D
        send(s, "getversion")
        peer_version = receive(s)
        if peer_version not in node.version_allow:
            raise ValueError(
                f"Outbound: Incompatible peer version {peer_version} from {this_client}"
            )

        send(s, "hello")

        # communication starter

    except Exception as e:
        node.logger.app_log.info(f"Could not connect to {this_client}: {e}")
        return  # can return here, because no lists are affected yet

    # if node.last_block >= POW_FORK - FORK_AHEAD:
    node.peers.store_mainnet(host, peer_version)
    try:
        peer_ip = s.getpeername()[0]
    except:
        # Should not happen, extra safety
        node.logger.app_log.warning(
            "Outbound: Transport endpoint was not connected")
        return

    if this_client not in node.peers.connection_pool:
        node.peers.append_client(this_client)
        node.logger.app_log.info(f"Connected to {this_client}")
        node.logger.app_log.info(
            f"Current active pool: {node.peers.connection_pool}")

    if not client_instance_worker.banned and node.peers.version_allowed(
            host, node.version_allow) and not node.IS_STOPPING:
        db_handler_instance = dbhandler.DbHandler(
            node.index_db, node.ledger_path_conf, node.hyper_path_conf,
            node.full_ledger, node.ram_conf, node.ledger_ram_file, logger)

    while not client_instance_worker.banned and node.peers.version_allowed(
            host, node.version_allow) and not node.IS_STOPPING:
        try:
            #ensure_good_peer_version(host)

            data = receive(s)  # receive data, one and the only root point
            # print(data)

            if data == "peers":
                subdata = receive(s)
                node.peers.peersync(subdata)

            elif data == "sync":
                if not time.time() <= timer_operation + timeout_operation:
                    timer_operation = time.time()  # reset timer

                try:
                    while len(node.syncing) >= 3:
                        if node.IS_STOPPING:
                            return
                        time.sleep(int(node.pause_conf))

                    node.syncing.append(peer_ip)
                    # sync start

                    # send block height, receive block height
                    send(s, "blockheight")

                    db_handler_instance.execute(
                        db_handler_instance.c,
                        'SELECT max(block_height) FROM transactions')
                    db_block_height = db_handler_instance.c.fetchone()[0]

                    node.logger.app_log.info(
                        f"Outbound: Sending block height to compare: {db_block_height}"
                    )
                    # append zeroes to get static length
                    send(s, db_block_height)

                    received_block_height = receive(
                        s)  # receive node's block height
                    node.logger.app_log.info(
                        f"Outbound: Node {peer_ip} is at block height: {received_block_height}"
                    )

                    if int(received_block_height) < db_block_height:
                        node.logger.app_log.warning(
                            f"Outbound: We have a higher block ({db_block_height}) than {peer_ip} ({received_block_height}), sending"
                        )

                        data = receive(s)  # receive client's last block_hash

                        # send all our followup hashes
                        node.logger.app_log.info(
                            f"Outbound: Will seek the following block: {data}")

                        # consensus pool 2 (active connection)
                        consensus_blockheight = int(received_block_height)
                        node.peers.consensus_add(peer_ip,
                                                 consensus_blockheight, s,
                                                 node.last_block)
                        # consensus pool 2 (active connection)

                        try:
                            db_handler_instance.execute_param(
                                db_handler_instance.h3,
                                "SELECT block_height FROM transactions WHERE block_hash = ?;",
                                (data, ))
                            client_block = db_handler_instance.h3.fetchone()[0]
                        except Exception:
                            node.logger.app_log.warning(
                                f"Outbound: Block {data[:8]} of {peer_ip} not found"
                            )
                            send(s, "blocknf")
                            send(s, data)

                        else:

                            node.logger.app_log.info(
                                f"Outbound: Node is at block {client_block}"
                            )  # now check if we have any newer

                            db_handler_instance.execute(
                                db_handler_instance.h3,
                                'SELECT block_hash FROM transactions ORDER BY block_height DESC LIMIT 1'
                            )
                            db_block_hash = db_handler_instance.h3.fetchone()[
                                0]  # get latest block_hash

                            if db_block_hash == data or not node.egress:
                                if not node.egress:
                                    node.logger.app_log.warning(
                                        f"Outbound: Egress disabled for {peer_ip}"
                                    )
                                    time.sleep(int(
                                        node.pause_conf))  # reduce CPU usage
                                else:
                                    node.logger.app_log.info(
                                        f"Outbound: Node {peer_ip} has the latest block"
                                    )
                                    # TODO: this is unlikely to happen due to conditions above, consider removing
                                send(s, "nonewblk")

                            else:
                                blocks_fetched = []
                                while sys.getsizeof(
                                        str(blocks_fetched)
                                ) < 500000:  # limited size based on txs in blocks
                                    # db_handler.execute_param(db_handler.h3, ("SELECT block_height, timestamp,address,recipient,amount,signature,public_key,keep,openfield FROM transactions WHERE block_height > ? AND block_height <= ?;"),(str(int(client_block)),) + (str(int(client_block + 1)),))
                                    db_handler_instance.execute_param(
                                        db_handler_instance.h3,
                                        ("SELECT timestamp,address,recipient,amount,signature,public_key,operation,openfield FROM transactions WHERE block_height > ? AND block_height <= ?;"
                                         ), (
                                             str(int(client_block)),
                                             str(int(client_block + 1)),
                                         ))
                                    result = db_handler_instance.h3.fetchall()
                                    if not result:
                                        break
                                    blocks_fetched.extend([result])
                                    client_block = int(client_block) + 1

                                # blocks_send = [[l[1:] for l in group] for _, group in groupby(blocks_fetched, key=itemgetter(0))]  # remove block number

                                node.logger.app_log.info(
                                    f"Outbound: Selected {blocks_fetched}")

                                send(s, "blocksfnd")

                                confirmation = receive(s)

                                if confirmation == "blockscf":
                                    node.logger.app_log.info(
                                        "Outbound: Client confirmed they want to sync from us"
                                    )
                                    send(s, blocks_fetched)

                                elif confirmation == "blocksrj":
                                    node.logger.app_log.info(
                                        "Outbound: Client rejected to sync from us because we're dont have the latest block"
                                    )

                    elif int(received_block_height) >= db_block_height:
                        if int(received_block_height) == db_block_height:
                            node.logger.app_log.info(
                                f"Outbound: We have the same block as {peer_ip} ({received_block_height}), hash will be verified"
                            )
                        else:
                            node.logger.app_log.warning(
                                f"Outbound: We have a lower block ({db_block_height}) than {peer_ip} ({received_block_height}), hash will be verified"
                            )

                        db_handler_instance.execute(
                            db_handler_instance.c,
                            'SELECT block_hash FROM transactions ORDER BY block_height DESC LIMIT 1'
                        )
                        db_block_hash = db_handler_instance.c.fetchone()[
                            0]  # get latest block_hash

                        node.logger.app_log.info(
                            f"Outbound: block_hash to send: {db_block_hash}")
                        send(s, db_block_hash)

                        #ensure_good_peer_version(host)

                        # consensus pool 2 (active connection)
                        consensus_blockheight = int(
                            received_block_height
                        )  # str int to remove leading zeros
                        node.peers.consensus_add(peer_ip,
                                                 consensus_blockheight, s,
                                                 node.last_block)
                        # consensus pool 2 (active connection)

                except Exception as e:
                    node.logger.app_log.info(f"Outbound: Sync failed {e}")
                finally:
                    node.syncing.remove(peer_ip)

            elif data == "blocknf":  # one of the possible outcomes
                block_hash_delete = receive(s)
                # print peer_ip
                # if max(consensus_blockheight_list) == int(received_block_height):
                if int(received_block_height) == node.peers.consensus_max:

                    blocknf(node, block_hash_delete, peer_ip,
                            db_handler_instance)

                    if node.peers.warning(s, peer_ip, "Rollback", 2):
                        raise ValueError(f"{peer_ip} is banned")

                sendsync(s, peer_ip, "Block not found", False, node)

            elif data == "blocksfnd":
                node.logger.app_log.info(
                    f"Outbound: Node {peer_ip} has the block(s)"
                )  # node should start sending txs in this step

                # node.logger.app_log.info("Inbound: Combined segments: " + segments)
                # print peer_ip
                if node.db_lock.locked():
                    node.logger.app_log.warning(
                        f"Skipping sync from {peer_ip}, syncing already in progress"
                    )

                else:
                    db_handler_instance.execute(
                        db_handler_instance.c,
                        "SELECT timestamp FROM transactions WHERE reward != 0 ORDER BY block_height DESC LIMIT 1;"
                    )  # or it takes the first
                    node.last_block_timestamp = quantize_two(
                        db_handler_instance.c.fetchone()[0])

                    if int(node.last_block_timestamp) < (time.time() - 600):
                        block_req = node.peers.consensus_most_common
                        node.logger.app_log.warning(
                            "Most common block rule triggered")

                    else:
                        block_req = node.peers.consensus_max
                        node.logger.app_log.warning(
                            "Longest chain rule triggered")

                    #ensure_good_peer_version(host)

                    if int(received_block_height) >= block_req:
                        try:  # they claim to have the longest chain, things must go smooth or ban
                            node.logger.app_log.warning(
                                f"Confirming to sync from {peer_ip}")

                            send(s, "blockscf")
                            segments = receive(s)
                            #ensure_good_peer_version(host)

                        except:
                            if node.peers.warning(
                                    s, peer_ip,
                                    "Failed to deliver the longest chain", 2):
                                raise ValueError(f"{peer_ip} is banned")

                        else:
                            digest_block(node, segments, s, peer_ip,
                                         db_handler_instance)
                            # receive theirs
                    else:
                        send(s, "blocksrj")
                        node.logger.app_log.warning(
                            f"Inbound: Distant peer {peer_ip} is at {received_block_height}, should be at least {block_req}"
                        )

                sendsync(s, peer_ip, "Block found", True, node)

                # block_hash validation end

            elif data == "nonewblk":
                # send and receive mempool
                if mp.MEMPOOL.sendable(peer_ip):
                    mempool_txs = mp.MEMPOOL.tx_to_send(peer_ip)
                    # node.logger.app_log.info("Outbound: Extracted from the mempool: " + str(mempool_txs))  # improve: sync based on signatures only
                    # if len(mempool_txs) > 0: #wont sync mempool until we send something, which is bad
                    # send own
                    send(s, "mempool")
                    send(s, mempool_txs)
                    # send own
                    # receive theirs
                    segments = receive(s)
                    node.logger.app_log.info(
                        mp.MEMPOOL.merge(segments, peer_ip,
                                         db_handler_instance.c, True))
                    # receive theirs
                    # Tell the mempool we just send our pool to a peer
                    mp.MEMPOOL.sent(peer_ip)
                sendsync(s, peer_ip, "No new block", True, node)

            elif data == "hyperlane":
                pass

            else:
                if data == '*':
                    raise ValueError("Broken pipe")
                raise ValueError(
                    f"Unexpected error, received: {str(data)[:32]}")

        except Exception as e:
            """
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno)
            """

            # remove from active pool
            node.peers.remove_client(this_client)
            node.logger.app_log.warning(
                f"Outbound: Disconnected from {this_client}: {e}")
            # remove from active pool

            # remove from consensus 2
            node.peers.consensus_remove(peer_ip)
            # remove from consensus 2

            node.logger.app_log.info(
                f"Connection to {this_client} terminated due to {e}")
            node.logger.app_log.info(
                f"---thread {threading.currentThread()} ended---")

            # properly end the connection
            s.close()
            # properly end the connection
            if node.debug_conf:
                raise  # major debug client
            else:
                node.logger.app_log.info(f"Ending thread, because {e}")
                return

    if not node.peers.version_allowed(host, node.version_allow):
        node.logger.app_log.warning(
            f"Outbound: Ending thread, because {host} has too old a version: {node.peers.ip_to_mainnet[host]}"
        )