예제 #1
0
    def setUp(self) -> None:
        self.nodemgr._reset_for_test()
        node.NeoNode._reset_for_test()
        settings.reset_settings_to_default()

        fake_protocol = object()
        self.node1 = node.NeoNode(fake_protocol)
        self.node2 = node.NeoNode(fake_protocol)

        self.addr1 = payloads.NetworkAddress(address="127.0.0.1:10333")
        self.addr2 = payloads.NetworkAddress(address="127.0.0.2:20333")
        self.node1.address = self.addr1
        self.node2.address = self.addr2
예제 #2
0
    def test_on_block_received(self):
        # first test receiving a block we have no outstanding request for
        fake_block = asynctest.MagicMock()
        fake_block.index = 1
        fake_block.__len__.return_value = 50
        self.assertEqual(
            -1,
            self.syncmgr.on_block_received(from_nodeid=123, block=fake_block))

        # next test receiving a block that we DO have an outstanding request for, but not from the node that is now
        # delivering the block
        request_info = convenience.RequestInfo(height=1)
        request_info.add_new_flight(
            convenience.FlightInfo(node_id=456, height=1))
        self.syncmgr.block_requests[1] = request_info
        self.assertEqual(
            -2,
            self.syncmgr.on_block_received(from_nodeid=123, block=fake_block))
        self.assertEqual(request_info,
                         self.syncmgr.block_requests.get(1, None))

        # next test a valid scenario (outstanding request and receiving a block from the right node)
        mocked_node = node.NeoNode(object())
        mocked_node.nodeweight.append_new_speed = asynctest.MagicMock()
        mocked_node.nodeid = 456
        self.nodemgr.nodes = [mocked_node]
        self.syncmgr.on_block_received(from_nodeid=456, block=fake_block)
        mocked_node.nodeweight.append_new_speed.assert_called_once()
        self.assertIn(fake_block, self.syncmgr.block_cache)
        self.assertEqual(1, len(self.syncmgr.block_cache))

        # and finally try again for the same block and ensure it was not added again to the cache
        self.syncmgr.on_block_received(from_nodeid=456, block=fake_block)
        self.assertEqual(1, len(self.syncmgr.block_cache))
예제 #3
0
    def test_version_validation_fail_if_no_full_node_capabilities(self):
        n = node.NeoNode(object())

        version = self._new_version()
        # remove the full node capability
        version.capabilities.pop(0)
        result = n._validate_version(version)
        self.assertFalse(result)
예제 #4
0
    async def test_req_addr_list(self):
        n = node.NeoNode(object())
        n.send_message = asynctest.CoroutineMock()
        await n.request_address_list()

        self.assertIsNotNone(n.send_message.call_args)
        m = n.send_message.call_args[0][0]  # type: message.Message
        self.assertEqual(message.MessageType.GETADDR, m.type)
        self.assertIsInstance(m.payload, payloads.EmptyPayload)
예제 #5
0
    def test_version_validation_client_is_self(self):
        n = node.NeoNode(object())

        # test for client is self
        version = self._new_version()
        version.nonce = n.nodeid
        with self.assertLogs(network_logger, 'DEBUG') as log_context:
            result = n._validate_version(version)
        self.assertFalse(result)
        self.assertIn("Client is self", log_context.output[0])
예제 #6
0
    async def test_send_addr_list(self):
        n = node.NeoNode(object())
        n.send_message = asynctest.CoroutineMock()
        n.addresses = [payloads.NetworkAddress(address='127.0.0.1:1111')]
        await n.send_address_list(n.addresses)

        self.assertIsNotNone(n.send_message.call_args)
        m = n.send_message.call_args[0][0]  # type: message.Message
        self.assertEqual(message.MessageType.ADDR, m.type)
        self.assertIsInstance(m.payload, payloads.AddrPayload)
        self.assertEqual(n.addresses, m.payload.addresses)
예제 #7
0
    def test_version_validation_wrong_network(self):
        n = node.NeoNode(object())

        # test for wrong network
        settings.network.magic = 769
        version = self._new_version()
        version.magic = 111
        with self.assertLogs(network_logger, 'DEBUG') as log_context:
            result = n._validate_version(version)
        self.assertFalse(result)
        self.assertIn("Wrong network id", log_context.output[0])
예제 #8
0
    async def test_send_headers(self):
        n = node.NeoNode(object())
        n.send_message = asynctest.CoroutineMock()
        headers = 2001 * [test_helpers.SerializableObject()]

        await n.send_headers(headers)
        self.assertIsNotNone(n.send_message.call_args)
        m = n.send_message.call_args[0][0]  # type: message.Message
        self.assertEqual(message.MessageType.HEADERS, m.type)
        self.assertIsInstance(m.payload, payloads.HeadersPayload)
        # max sure it clips the size to max 2K headers
        self.assertEqual(2000, len(m.payload.headers))
예제 #9
0
    async def test_req_block_data(self):
        n = node.NeoNode(object())
        n.send_message = asynctest.CoroutineMock()
        index_start = 1
        count = 2

        await n.request_block_data(index_start, count)
        self.assertIsNotNone(n.send_message.call_args)
        m = n.send_message.call_args[0][0]  # type: message.Message
        self.assertEqual(message.MessageType.GETBLOCKBYINDEX, m.type)
        self.assertIsInstance(m.payload, payloads.GetBlockByIndexPayload)
        self.assertEqual(index_start, m.payload.index_start)
        self.assertEqual(count, m.payload.count)
예제 #10
0
    def test_helpers(self):
        addr1 = payloads.NetworkAddress(address='127.0.0.1:1111')
        addr1.set_state_dead()
        addr2 = payloads.NetworkAddress(address='127.0.0.1:2222')
        node.NeoNode.addresses = [addr1, addr2]
        result = node.NeoNode.get_address_new()
        self.assertEqual(addr2, result)

        n = node.NeoNode(object())
        addr = n._find_address_by_host_port('127.0.0.1:1111')
        self.assertEqual(addr1, addr)
        addr = n._find_address_by_host_port('127.0.0.1:3333')
        self.assertEqual(None, addr)
예제 #11
0
    async def test_request_data(self):
        n = node.NeoNode(object())
        n.send_message = asynctest.CoroutineMock()
        hash1 = types.UInt256.from_string("65793a030c0dcd4fff4da8a6a6d5daa8b570750da4fdeea1bbc43bdf124aedc9")
        hash2 = types.UInt256.from_string("65793a030c0dcd4fff4da8a6a6d5daa8b570750da4fdeea1bbc43bdf124aaaaa")
        hashes = [hash1, hash2]

        await n.request_data(payloads.InventoryType.BLOCK, hashes)
        self.assertIsNotNone(n.send_message.call_args)
        m = n.send_message.call_args[0][0]  # type: message.Message
        self.assertEqual(message.MessageType.GETDATA, m.type)
        self.assertIsInstance(m.payload, payloads.InventoryPayload)
        self.assertEqual(hashes, m.payload.hashes)
예제 #12
0
    async def test_req_blocks(self):
        n = node.NeoNode(object())
        n.send_message = asynctest.CoroutineMock()
        hash_start = types.UInt256.from_string("65793a030c0dcd4fff4da8a6a6d5daa8b570750da4fdeea1bbc43bdf124aedc9")
        count = 10

        await n.request_blocks(hash_start, count)
        self.assertIsNotNone(n.send_message.call_args)
        m = n.send_message.call_args[0][0]  # type: message.Message
        self.assertEqual(message.MessageType.GETBLOCKS, m.type)
        self.assertIsInstance(m.payload, payloads.GetBlocksPayload)
        self.assertEqual(hash_start, m.payload.hash_start)
        self.assertEqual(count, m.payload.count)
예제 #13
0
    def test_version_validation_should_add_new_address(self):
        # when using the `connect_to` method or when a server is hosted accepting incoming clients
        # we should add the address to our know addresses list
        n = node.NeoNode(object())
        # normally this is updated by `connection_made()`, since we skip that in this test we set it manually
        n.address.address = '127.0.0.1:1111'
        self.assertEqual(0, len(node.NeoNode.addresses))

        version = self._new_version()
        with self.assertLogs(network_logger, 'DEBUG') as log_context:
            n._validate_version(version)
        self.assertEqual(1, len(node.NeoNode.addresses))
        self.assertIn("Adding address from outside 127.0.0.1:1111", log_context.output[0])
예제 #14
0
    def test_version_validation_should_updating_address_to_connected_state(self):
        n = node.NeoNode(object())

        # test updating address state to CONNECTED
        # this is relevant for addresses that have been added through the nodemanager based on the seedlist
        # set the addresses ourselves to mimick the nodemanager startup behaviour
        node.NeoNode.addresses = [n.address]
        version = self._new_version()

        result = n._validate_version(version)
        # validate our address is now set to CONNECTED
        self.assertTrue(result)
        self.assertTrue(n.address.is_state_connected)
        self.assertEqual(self.start_height, n.best_height)
예제 #15
0
    async def test_send_inventory_and_relay(self):
        # test 2 in 1
        # taken from the Transaction testcase in `test_payloads.py`
        raw_tx = binascii.unhexlify(b'007B000000C8010000000000001503000000000000010000000154A64CAC1B1073E662933EF3E30B007CD98D67D7000002010201000155')
        tx = payloads.Transaction.deserialize_from_bytes(raw_tx)

        n = node.NeoNode(object())
        n.send_message = asynctest.CoroutineMock()

        await n.relay(tx)
        m = n.send_message.call_args[0][0]  # type: message.Message
        self.assertEqual(message.MessageType.INV, m.type)
        self.assertIsInstance(m.payload, payloads.InventoryPayload)
        self.assertEqual(tx.hash(), m.payload.hashes[0])
예제 #16
0
    def __init__(self, *args, **kwargs):
        """

        Args:
            *args:
            **kwargs:
        """
        sr = StreamReader()
        self._stream_reader_orig = sr
        self._stream_reader_wr = weakref.ref(sr)
        self._stream_writer = None
        self.client = node.NeoNode(self)
        self._loop = events.get_event_loop()
        super().__init__(sr)
예제 #17
0
    async def test_fill_open_connection_spots_dont_queue_addr_that_is_already_queued(self):
        fake_protocol = object()
        self.node1 = node.NeoNode(fake_protocol)

        # ensure we have an address that can be connected to
        addr = payloads.NetworkAddress(address='127.0.0.1:1111')
        # create 1 open spot
        self.nodemgr.max_clients = 2
        node.NeoNode.addresses = [addr]
        # and ensure we pretend we have already queued this address
        self.nodemgr.queued_addresses = [addr]

        with self.assertLogs(network_logger, 'DEBUG') as context:
            with mock.patch('asyncio.create_task'):
                await self.nodemgr._fill_open_connection_spots()
        self.assertIn("Found 1 open pool spots", context.output[0])
        # context.output length should only contain the above debug message, nothing more
        self.assertEqual(1, len(context.output))
예제 #18
0
    async def test_sync_blocks_get_1_block(self):
        # scenario
        # - cache has 2 spot available
        # - the connected nodes best_height can only fill 1 of the spots
        # expected result: 1 flight added

        self.syncmgr.block_cache = (self.syncmgr.BLOCK_MAX_CACHE_SIZE -
                                    2) * [None]
        mock_node = node.NeoNode(protocol=object())
        mock_node.best_height = 2
        mock_node.request_block_data = asynctest.CoroutineMock()
        self.nodemgr.nodes = [mock_node]

        with self.assertLogs(network_logger, 'DEBUG') as log_context:
            with asynctest.patch.object(self.syncmgr,
                                        '_get_best_stored_block_height',
                                        return_value=1):
                with asynctest.patch.object(
                        self.syncmgr,
                        '_add_block_flight_info') as mocked_add_flight_info:
                    await self.syncmgr._sync_blocks()
        mocked_add_flight_info.assert_called_once()
        self.assertIn("Asking for blocks 2 - 2", log_context.output[0])
예제 #19
0
    async def test_flights_timed_out(self):
        # scenario: have 2 outstanding flights that timed out
        # - 1 flight for a request that is still is not completed
        # - 1 flight has been made obsolete by a secondary for the same request_info and is still in cache waiting to be processed

        # construct flight 1 - request not yet satisfied
        target_height = 2
        node1_id = 123
        request_info = convenience.RequestInfo(target_height)
        request_info.mark_failed_node = asynctest.MagicMock()
        flight_info = convenience.FlightInfo(node_id=node1_id,
                                             height=target_height)
        # reduce start time to enforce exceeding timeout treshold
        flight_info.start_time -= self.syncmgr.BLOCK_REQUEST_TIMEOUT + 1
        request_info.add_new_flight(flight_info)
        self.syncmgr.block_requests[target_height] = request_info

        # construct flight 2 - request already satisfied
        target_height2 = 1
        node2_id = 456
        request_info2 = convenience.RequestInfo(target_height2)
        flight_info2 = convenience.FlightInfo(node_id=node2_id,
                                              height=target_height2)
        flight_info2.start_time -= self.syncmgr.BLOCK_REQUEST_TIMEOUT + 1
        request_info2.add_new_flight(flight_info2)
        self.syncmgr.block_requests[target_height2] = request_info2

        # we patch '_get_best_stored_block_height' to return `target_height2` as a way of saying;
        # either the chain or cache already has the data for this height
        with asynctest.patch.object(self.syncmgr,
                                    '_get_best_stored_block_height',
                                    return_value=target_height2):
            with asynctest.patch.object(self.nodemgr,
                                        'increase_node_timeout_count'
                                        ) as nodemgr_increase_timeout_count:
                result = await self.syncmgr._check_timeout()

        request_info.mark_failed_node.assert_called_with(node1_id)
        # both nodes had a flight that timed out
        nodemgr_increase_timeout_count.assert_has_calls(
            [asynctest.mock.call(node1_id),
             asynctest.mock.call(node2_id)],
            any_order=True)
        # the first time we call it we no longer have any connected nodes, so we can't request from anyone anymore
        self.assertEqual(-3, result)

        # now we "connect" a new node
        mock_node = node.NeoNode(protocol=object())
        mock_node.best_height = 10
        mock_node_id = 789
        mock_node.nodeid = mock_node_id
        mock_node.request_block_data = asynctest.CoroutineMock()
        self.nodemgr.nodes = [mock_node]
        # and try again
        with self.assertLogs(network_logger, 'DEBUG') as log_context:
            with asynctest.patch.object(self.syncmgr,
                                        '_get_best_stored_block_height',
                                        return_value=target_height2):
                with asynctest.patch.object(self.nodemgr,
                                            'increase_node_timeout_count'):
                    await self.syncmgr._check_timeout()

        # and validate that a new data request is sent
        self.assertIn("Block timeout for blocks 2 - 2", log_context.output[0])
        mock_node.request_block_data.assert_awaited_once_with(count=1,
                                                              index_start=2)
        # and also a new flight was added for the new node
        flight = request_info.most_recent_flight()
        self.assertEqual(mock_node_id, flight.node_id)

        # just for coverage
        flight.reset_start_time()
        self.assertTrue(
            datetime.utcnow().timestamp() - flight.start_time < 0.1)