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_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_fill_open_connection_spots_node_timeout_error(self): self.nodemgr.min_clients = 1 self.nodemgr.max_clients = 2 addr = payloads.NetworkAddress(address='127.0.0.1:1111') node.NeoNode.addresses = [addr] call_back_result = None def client_connect_done(node_instance, failure): nonlocal call_back_result call_back_result = failure # listen to a connection done event msgrouter.on_client_connect_done += client_connect_done with self.assertLogs(network_logger, 'DEBUG') as context: self.loop.create_connection = AsyncMock() self.loop.create_connection.side_effect = asyncio.TimeoutError() await self.nodemgr._fill_open_connection_spots() # advance loop await asyncio.sleep(0.1) self.assertEqual("Timed out", call_back_result[1]) self.assertIn("Found 2 open pool spots, trying to add nodes...", context.output[0])
def _find_address_by_host_port( self, host_port) -> Optional[payloads.NetworkAddress]: addr = payloads.NetworkAddress(address=host_port) try: idx = self.addresses.index(addr) return self.addresses[idx] except ValueError: return None
async def _process(seed): host, port = seed.split(':') if not is_ip_address(host): try: result = await resolver.query(host, 'A') host = result[0].host except aiodns.error.DNSError as e: logger.debug(f"Skipping {host}, address could not be resolved: {e}.") node.NeoNode.addresses.append(payloads.NetworkAddress(address=f"{host}:{port}"))
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)
async def test_sending_addresses(self): n = await node.NeoNode.connect_to('127.0.0.1', 40333) await n.send_address_list([ payloads.NetworkAddress( host='127.0.0.1', timestamp=0, capabilities=[ capabilities.FullNodeCapability(start_height=123) ]) ]) await asyncio.sleep(100)
async def test_fill_open_connection_spots_no_clients(self): # create 1 open spot self.nodemgr.max_clients = 1 # ensure we have an address that can be connected to node.NeoNode.addresses = [payloads.NetworkAddress(address='127.0.0.1:1111')] 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]) self.assertIn("Adding 127.0.0.1:1111 to connection queue", context.output[1])
async def test_fill_open_connection_spots_no_addresses_to_fill_spots(self): # we first test that no errors occur if we have open spots, but we still meet our minimum required clients # this should not cause any error count increases self.nodemgr.min_clients = 1 self.nodemgr.max_clients = 2 # just a place holder to have a count matching min_clients self.nodemgr.nodes = [object()] node.NeoNode.addresses = [] with self.assertLogs(network_logger, 'DEBUG') as context: await self.nodemgr._fill_open_connection_spots() self.assertIn("Found 1 open pool spots, trying to add nodes...", context.output[0]) self.assertIn( "No addresses available to fill spots. However, minimum clients still satisfied", context.output[1]) # next we clear the nodes and start inducing errors for not being able to fill the open spots self.nodemgr.nodes = [] with self.assertLogs(network_logger, 'DEBUG') as context: await self.nodemgr._fill_open_connection_spots() self.assertIn("Found 2 open pool spots, trying to add nodes...", context.output[0]) self.assertIn("Increasing pool spot error count to 1", context.output[1]) with self.assertLogs(network_logger, 'DEBUG') as context: await self.nodemgr._fill_open_connection_spots() self.assertIn("Found 2 open pool spots, trying to add nodes...", context.output[0]) self.assertIn("Increasing pool spot error count to 2", context.output[1]) # 3rd time we reached our threshold # let's also setup an address in POOR state that should be reset to NEW addr = payloads.NetworkAddress(address='127.0.0.1:1111') addr.set_state_poor() node.NeoNode.addresses = [addr] with self.assertLogs(network_logger, 'DEBUG') as context: await self.nodemgr._fill_open_connection_spots() self.assertIn("Found 2 open pool spots, trying to add nodes...", context.output[0]) self.assertIn("Recycling old addresses", context.output[1]) self.assertEqual(0, self.nodemgr.MAX_NODE_POOL_ERROR_COUNT) self.assertTrue(node.NeoNode.addresses[0].is_state_new)
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))
def _connect_done_cb(self, future) -> None: node_instance, failure = future.result() # failures here are hard failures from asyncio's loop.create_connection() if failure: logger.debug(f"Failed to connect to {failure[0]} reason: {failure[1]}.") tmp_addr = payloads.NetworkAddress(address=failure[0]) with suppress(ValueError): idx = node.NeoNode.addresses.index(tmp_addr) addr = node.NeoNode.addresses[idx] addr.set_state_dead() self.queued_addresses.remove(tmp_addr) msgrouter.on_client_connect_done(None, failure) else: msgrouter.on_client_connect_done(node_instance, None) node_instance.start_message_handler() self.tasks.remove(future)
def __init__(self, protocol): self.protocol = protocol #: payloads.NetworkAddress: Address of the remote endpoint. self.address = payloads.NetworkAddress( state=payloads.AddressState.DEAD) self.nodeid: int = id(self) #: int: Unique identifier. self.nodeid_human: str = encode_base62( self.nodeid) #: str: Human readable id. self.version = None self.tasks = [] self.nodeweight = nodeweight.NodeWeight(self.nodeid) self.best_height: int = 0 #: int: Best block height of node. self.best_height_last_update = datetime.utcnow().timestamp() self._read_task = None # type: asyncio.Task #: bool: Whether the node is in the process of disconnecting and shutting down its tasks. self.disconnecting: bool = False #: Dict[message.MessageType, Callable[[message.Message], None]]: A table matching message types to handler #: functions. self.dispatch_table: Dict[message.MessageType, Callable[ [message.Message], None]] = { message.MessageType.ADDR: self.handler_addr, message.MessageType.BLOCK: self.handler_block, message.MessageType.CONSENSUS: self.handler_consensus, message.MessageType.INV: self.handler_inv, message.MessageType.FILTERADD: self.handler_filteradd, message.MessageType.FILTERCLEAR: self.handler_filterclear, message.MessageType.FILTERLOAD: self.handler_filterload, message.MessageType.GETADDR: self.handler_getaddr, message.MessageType.GETBLOCKS: self.handler_getblocks, message.MessageType.GETBLOCKDATA: self.handler_getblockdata, message.MessageType.GETDATA: self.handler_getdata, message.MessageType.GETHEADERS: self.handler_getheaders, message.MessageType.HEADERS: self.handler_headers, message.MessageType.MEMPOOL: self.handler_mempool, message.MessageType.MERKLEBLOCK: self.handler_merkleblock, message.MessageType.PING: self.handler_ping, message.MessageType.PONG: self.handler_pong, message.MessageType.TRANSACTION: self.handler_transaction }