Ejemplo n.º 1
0
    async def request_blocks(self,
                             hash_start: types.UInt256,
                             count: int = None) -> None:
        """
        Send a request for retrieving block hashes from `hash_start` to `hash_start`+`count`.

        Not specifying a `count` results in requesting at most 500 blocks.

        Note:
            The remote node is expected to reply with a Message with the :const:`~neo3.network.message.MessageType.INV`
            type containing the hashes of the requested blocks. Use :meth:`~neo3.network.node.NeoNode.request_data` in
            combination with these hashes to return the actual :class:`~neo3.network.payloads.block.Block` objects.

        See also:
            :meth:`~neo3.network.node.NeoNode.request_block_data()` to immediately retrieve
            :class:`~neo3.network.payloads.block.Block` objects.

        Args:
            hash_start:
            count:
        """
        m = message.Message(msg_type=message.MessageType.GETBLOCKS,
                            payload=payloads.GetBlocksPayload(
                                hash_start, count))
        await self.send_message(m)
Ejemplo n.º 2
0
    async def send_address_list(
            self, network_addresses: List[payloads.NetworkAddress]) -> None:
        """
        Send network addresses.

        Args:
            network_addresses:
        """
        m = message.Message(
            msg_type=message.MessageType.ADDR,
            payload=payloads.AddrPayload(addresses=network_addresses))
        await self.send_message(m)
Ejemplo n.º 3
0
    async def _do_handshake(
            self) -> Tuple[bool, Optional[payloads.DisconnectReason]]:
        caps: List[capabilities.NodeCapability] = [
            capabilities.FullNodeCapability(0)
        ]
        # TODO: fix nonce and port if a service is running
        send_version = message.Message(msg_type=message.MessageType.VERSION,
                                       payload=payloads.VersionPayload.create(
                                           nonce=123,
                                           user_agent="NEO3-PYTHON",
                                           capabilities=caps))
        await self.send_message(send_version)

        m = await self.read_message(timeout=3)
        if not m or m.type != message.MessageType.VERSION:
            await self.disconnect(
                payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR)
            return (False, payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR)

        if not self._validate_version(m.payload):
            await self.disconnect(
                payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR)
            return (False, payloads.DisconnectReason.HANDSHAKE_VERSION_ERROR)

        m_verack = message.Message(msg_type=message.MessageType.VERACK)
        await self.send_message(m_verack)

        m = await self.read_message(timeout=3)
        if not m or m.type != message.MessageType.VERACK:
            await self.disconnect(
                payloads.DisconnectReason.HANDSHAKE_VERACK_ERROR)
            return (False, payloads.DisconnectReason.HANDSHAKE_VERACK_ERROR)

        logger.debug(
            f"Connected to {self.version.user_agent} @ {self.address.address}: {self.best_height}."
        )
        msgrouter.on_node_connected(self)

        return (True, None)
Ejemplo n.º 4
0
    async def send_headers(self, headers: List[payloads.Header]) -> None:
        """
        Send a list of Header objects.

        Args:
            headers:
        """
        if len(headers) > 2000:
            headers = headers[:2000]

        m = message.Message(msg_type=message.MessageType.HEADERS,
                            payload=payloads.HeadersPayload(headers))
        await self.send_message(m)
Ejemplo n.º 5
0
    def handler_ping(self, msg: message.Message) -> None:
        """
        Handler for a message with the PING type.

        Args:
            msg:
        """
        if settings.database:
            height = max(0, blockchain.Blockchain().height)
        else:
            height = 0
        m = message.Message(msg_type=message.MessageType.PONG,
                            payload=payloads.PingPayload(height=height))
        self._create_task_with_cleanup(self.send_message(m))
Ejemplo n.º 6
0
    async def request_data(self, type: payloads.InventoryType,
                           hashes: List[types.UInt256]) -> None:
        """
        Send a request for receiving the specified inventory data.

        Args:
            type:
            hashes:
        """
        if len(hashes) < 1:
            return

        m = message.Message(msg_type=message.MessageType.GETDATA,
                            payload=payloads.InventoryPayload(type, hashes))
        await self.send_message(m)
Ejemplo n.º 7
0
    async def request_headers(self,
                              hash_start: types.UInt256,
                              count: int = None) -> None:
        """
        Send a request for headers from `hash_start` to `hash_start`+`count`.

        Not specifying a `count` results in requesting at most 2000 headers.

        Args:
            hash_start:
            count:
        """
        m = message.Message(msg_type=message.MessageType.GETHEADERS,
                            payload=payloads.GetBlocksPayload(
                                hash_start, count))
        await self.send_message(m)
Ejemplo n.º 8
0
 async def _monitor_node_height(self) -> None:
     now = datetime.utcnow().timestamp()
     for node in self.nodes:
         if now - node.best_height_last_update > self.MAX_HEIGHT_UPDATE_DURATION:
             logger.debug(f"Disconnecting node {node.nodeid} Reason: max height update threshold exceeded.")
             asyncio.create_task(node.disconnect(reason=payloads.DisconnectReason.POOR_PERFORMANCE))
         else:
             logger.debug(f"Asking node {node.nodeid_human} to send us a height update (PING)")
             # Request latest height from node
             if settings.database:
                 height = max(0, blockchain.Blockchain().height)
             else:
                 height = 0
             m = message.Message(msg_type=message.MessageType.PING, payload=payloads.PingPayload(height=height))
             task = asyncio.create_task(node.send_message(m))
             self.tasks.append(task)
             task.add_done_callback(lambda fut: self.tasks.remove(fut))
Ejemplo n.º 9
0
    async def request_block_data(self, index_start, count) -> None:
        """
        Send a request for `count` blocks starting from `index_start`.

        Count cannot exceed :attr:`~neo3.network.payloads.block.GetBlockDataPayload.MAX_BLOCKS_COUNT`.

        See also:
            :meth:`~neo3.network.node.NeoNode.request_blocks()` to only request block hashes.

        Args:
            index_start: block index to start from.
            count: number of blocks to return.
        """
        m = message.Message(msg_type=message.MessageType.GETBLOCKDATA,
                            payload=payloads.GetBlockDataPayload(
                                index_start, count))
        await self.send_message(m)
Ejemplo n.º 10
0
    def handler_inv(self, msg: message.Message) -> None:
        """
        Handler for a message with the INV type.

        Args:
            msg:
        """
        payload = cast(payloads.InventoryPayload, msg.payload)
        if payload.type == payloads.InventoryType.BLOCK:
            # neo-cli broadcasts INV messages on a regular interval. We can use those as trigger to request
            # their latest block height
            if len(payload.hashes) > 0:
                if settings.database:
                    height = max(0, blockchain.Blockchain().height)
                else:
                    height = 0
                m = message.Message(
                    msg_type=message.MessageType.PING,
                    payload=payloads.PingPayload(height=height))
                self._create_task_with_cleanup(self.send_message(m))
        else:
            logger.debug(
                f"Message with type INV received. No processing for payload type "  # type:ignore
                f"{payload.type.name} implemented")
Ejemplo n.º 11
0
 async def send_inventory(self, inv_type: payloads.InventoryType,
                          inv_hash: types.UInt256):
     inv = payloads.InventoryPayload(type=inv_type, hashes=[inv_hash])
     m = message.Message(msg_type=message.MessageType.INV, payload=inv)
     await self.send_message(m)
Ejemplo n.º 12
0
 async def request_address_list(self) -> None:
     """
     Send a request for receiving known network addresses.
     """
     m = message.Message(msg_type=message.MessageType.GETADDR)
     await self.send_message(m)