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
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))
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)
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)
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])
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)
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])
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))
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)
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)
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)
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)
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])
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)
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])
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)
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))
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])
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)