Ejemplo n.º 1
0
    async def test_processing_messages3(self):
        # we got 2 cases for which we need to test without using a backend
        settings.network.magic = 769
        settings.storage.use_default = False
        socket_mock = NeoNodeSocketMock(self.loop, '127.0.0.1', 1111)

        m_inv1 = message.Message(
            msg_type=message.MessageType.INV,
            payload=payloads.InventoryPayload(
                payloads.InventoryType.BLOCK,
                hashes=[
                    types.UInt256.from_string(
                        "65793a030c0dcd4fff4da8a6a6d5daa8b570750da4fdeea1bbc43bdf124aedc9"
                    )
                ]))
        m_ping = message.Message(msg_type=message.MessageType.PING,
                                 payload=payloads.PingPayload(0))

        def _recv_data2(self):
            print("my recv data 2 called ")
            # first do handshake
            yield self.m_send_version.to_array()
            yield self.m_verack.to_array()
            yield m_inv1.to_array()
            yield m_ping.to_array()

        socket_mock.recv_data = _recv_data2(socket_mock)
        with self.assertLogs(network_logger, 'DEBUG') as log_context:
            with mock.patch('neo3.network.node.NeoNode.send_message',
                            new_callable=asynctest.CoroutineMock):
                # with asynctest.patch('neo3.network.node.NeoNode.send_message', return_value=asynctest.CoroutineMock()):
                n, _ = await node.NeoNode.connect_to(socket=socket_mock)
                await asyncio.sleep(0.1)
                await n.disconnect(payloads.DisconnectReason.SHUTTING_DOWN)
Ejemplo n.º 2
0
    def _recv_data(self):
        caps = [capabilities.FullNodeCapability(0)]
        m_send_version = message.Message(msg_type=message.MessageType.VERSION,
                                         payload=payloads.VersionPayload(
                                             nonce=123,
                                             user_agent="NEO3-MOCK-CLIENT",
                                             capabilities=caps))
        m_verack = message.Message(msg_type=message.MessageType.VERACK)

        yield m_send_version.to_array()
        yield m_verack.to_array()
Ejemplo n.º 3
0
 def __init__(self, loop, hostaddr: str, port: int):
     super(NeoNodeSocketMock, self).__init__()
     self.type = socket.SOCK_STREAM
     self.recv_buffer = bytearray()
     self.loop = loop
     self.hostaddr = hostaddr
     self.port = port
     self.recv_data = self._recv_data()
     caps = [capabilities.FullNodeCapability(0),
             capabilities.ServerCapability(n_type=capabilities.NodeCapabilityType.TCPSERVER, port=10333)]
     self.m_send_version = message.Message(msg_type=message.MessageType.VERSION,
                                           payload=payloads.VersionPayload(nonce=123,
                                                                           user_agent="NEO3-MOCK-CLIENT",
                                                                           capabilities=caps))
     self.m_verack = message.Message(msg_type=message.MessageType.VERACK)
Ejemplo n.º 4
0
    async def test_processing_messages(self):
        settings.network.magic = 769
        socket_mock = NeoNodeSocketMock(self.loop, '127.0.0.1', 1111)

        m_addr = message.Message(msg_type=message.MessageType.ADDR, payload=payloads.AddrPayload([]))
        m_block = message.Message(msg_type=message.MessageType.BLOCK, payload=payloads.EmptyPayload())
        m_inv1 = message.Message(msg_type=message.MessageType.INV, payload=payloads.InventoryPayload(
            payloads.InventoryType.BLOCK,
            hashes=[types.UInt256.from_string("65793a030c0dcd4fff4da8a6a6d5daa8b570750da4fdeea1bbc43bdf124aedc9")]
        ))
        m_inv2 = message.Message(msg_type=message.MessageType.INV,
                                 payload=payloads.InventoryPayload(payloads.InventoryType.TX, []))
        m_getaddr = message.Message(msg_type=message.MessageType.GETADDR, payload=payloads.EmptyPayload())
        m_mempool = message.Message(msg_type=message.MessageType.MEMPOOL, payload=payloads.EmptyPayload())

        # taken from the Headers testcase in `test_payloads`
        raw_headers_payload = binascii.unhexlify(b'0000000001FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00A402FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00A400000000000000007B00000000F7B4D00143932F3B6243CFC06CB4A68F22C739E201020102020304')
        m_headers = message.Message(msg_type=message.MessageType.HEADERS,
                                    payload=payloads.HeadersPayload.deserialize_from_bytes(raw_headers_payload))
        m_ping = message.Message(msg_type=message.MessageType.PING, payload=payloads.PingPayload(0))
        m_pong = message.Message(msg_type=message.MessageType.PONG, payload=payloads.PingPayload(0))
        m_reject = message.Message(msg_type=message.MessageType.REJECT, payload=payloads.EmptyPayload())

        def _recv_data2(self):
            # first do handshake
            yield self.m_send_version.to_array()
            yield self.m_verack.to_array()
            # next send all types of messages we handle
            yield m_addr.to_array()
            yield m_block.to_array()
            yield m_inv1.to_array()
            yield m_inv2.to_array()
            yield m_getaddr.to_array()
            yield m_mempool.to_array()
            yield m_headers.to_array()
            yield m_ping.to_array()
            yield m_pong.to_array()
            yield m_reject.to_array()

        socket_mock.recv_data = _recv_data2(socket_mock)
        with self.assertLogs(network_logger, 'DEBUG') as log_context:
            try:
                n, _ = await node.NeoNode.connect_to(socket=socket_mock)
            except Exception as e:
                print(f"GVD {e}")

            await asyncio.sleep(0.5)
            await n.disconnect(payloads.DisconnectReason.SHUTTING_DOWN)
Ejemplo n.º 5
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.º 6
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.º 7
0
 def test_deserialization_without_payload(self):
     # some message types like PING/PONG have no payload
     m = message.Message(message.MessageType.PING)
     data = m.to_array()
     m2 = message.Message.deserialize_from_bytes(data)
     self.assertEqual(message.MessageType.PING, m2.type)
     self.assertEqual(0, len(m2.payload))
Ejemplo n.º 8
0
    def test_create_inv_message(self):
        hashes = [UInt256.zero()]
        inv_payload = payloads.InventoryPayload(payloads.InventoryType.BLOCK,
                                                hashes)
        m = message.Message(message.MessageType.INV, inv_payload)
        data = m.to_array()

        self.assertEqual(message.MessageType.INV, m.type)
        self.assertEqual(message.MessageConfig.NONE, m.config)
        self.assertIsInstance(m.payload, payloads.InventoryPayload)
        """
            Taken from constructing the same object in C#
            
            UInt256[] hashes = { UInt256.Zero };
            var inv_payload = InvPayload.Create(InventoryType.Block, hashes);
            ISerializable message = Message.Create(MessageCommand.Inv, inv_payload);

            using (MemoryStream ms = new MemoryStream())
            using (BinaryWriter writer = new BinaryWriter(ms))
            {
                message.Serialize(writer);
                writer.Flush();
                byte[] data = ms.ToArray();
                Console.WriteLine($"b\'{BitConverter.ToString(data).Replace("-","")}\'");
            }          

        """
        expected_data = binascii.unhexlify(
            b'0027222C010000000000000000000000000000000000000000000000000000000000000000'
        )
        self.assertEqual(expected_data, data)
Ejemplo n.º 9
0
    def test_deserialization_with_unsupported_payload_type(self):
        hashes = [UInt256.zero()]
        inv_payload = payloads.InventoryPayload(payloads.InventoryType.BLOCK, hashes)
        m = message.Message(message.MessageType.ALERT, inv_payload)

        m2 = message.Message.deserialize_from_bytes(m.to_array())
        self.assertIsInstance(m2.payload, payloads.EmptyPayload)
Ejemplo n.º 10
0
 def test_deserialization_from_stream(self):
     # see test_create_compressed_inv_message() how it was obtained
     raw_data = binascii.unhexlify(b'01270D3F2C0400010067500000000000')
     with serialization.BinaryReader(raw_data) as br:
         m = message.Message()
         m.deserialize(br)
         self.assertEqual(m.type, message.MessageType.INV)
         self.assertEqual(m.payload.type, payloads.inventory.InventoryType.BLOCK)
Ejemplo n.º 11
0
    async def send_ping(self):
        if settings.database:
            height = max(0, blockchain.Blockchain().height)
        else:
            height = 0

        ping = payloads.PingPayload(height)
        m = message.Message(msg_type=message.MessageType.PING, payload=ping)
        await self.send_message(m)
Ejemplo n.º 12
0
    async def test_basic_setup(self):
        n = await node.NeoNode.connect_to('127.0.0.1', 40333)
        m = message.Message(
            message.MessageType.GETFULLBLOCKS,
            payloads.GetFullBlocksPayload(index_start=1, count=10))
        if n:
            await n.send_message(m)

            # await n.request_address_list()
            await asyncio.sleep(500)
            self.assertIsInstance(n, node.NeoNode)
Ejemplo n.º 13
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.º 14
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.º 15
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.º 16
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.º 17
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.º 18
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.º 19
0
    def test_create_compressed_inv_message(self):
        hashes = [UInt256.zero(), UInt256.zero(), UInt256.zero(), UInt256.zero()]
        inv_payload = payloads.InventoryPayload(payloads.InventoryType.BLOCK, hashes)
        m = message.Message(message.MessageType.INV, inv_payload)
        data = m.to_array() # triggers payload compression

        self.assertEqual(message.MessageType.INV, m.type)
        self.assertEqual(message.MessageConfig.COMPRESSED, m.config)
        self.assertIsInstance(m.payload, payloads.InventoryPayload)

        """
        Data created in the same fashion as how it's done in test_create_inv_message()
        The deviation is `hashes` now contains 4 x UInt256.zero()
        """
        expected_data = binascii.unhexlify(b'01270D3F2C0400010067500000000000')
        self.assertEqual(expected_data, data)
Ejemplo n.º 20
0
    def handler_getdata(self, msg: message.Message) -> None:
        """
        Handler for a message with the GETDATA type.

        Args:
            msg:
        """
        payload = cast(payloads.InventoryPayload, msg.payload)
        for h in payload.hashes:
            item = relaycache.RelayCache().try_get(h)
            if item is None:
                # for the time being we only support data retrieval for our own relays
                continue
            if payload.type == payloads.InventoryType.TX:
                m = message.Message(msg_type=message.MessageType.TRANSACTION,
                                    payload=item)
                self._create_task_with_cleanup(self.send_message(m))
Ejemplo n.º 21
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.º 22
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.º 23
0
 def test_create_no_payload(self):
     m = message.Message(message.MessageType.PING, payload=None)
     self.assertEqual(message.MessageType.PING, m.type)
     self.assertEqual(message.MessageConfig.NONE, m.config)
Ejemplo n.º 24
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.º 25
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)