-
Notifications
You must be signed in to change notification settings - Fork 277
Description
Describe the Bug
At the moment, test_alice_verifies_ursula_just_in_time does not work on main (reproduced in ~50% of runs on my machine). The error looks like
nucypher/network/nodes.py:1047: in bytestring_of_known_nodes
payload = self.known_nodes.snapshot()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = (Ursula)⇀OldLace Yankee LightGreen Sierra↽ (0x6021D866E5539Bf02d7ED8567af8e0d1fA5bd896)
@property
def known_nodes(self):
> return self.__known_nodes
E AttributeError: 'Ursula' object has no attribute '_Learner__known_nodes'
nucypher/network/nodes.py:305: AttributeError
The issue seems to be that MockRestMiddlewareForLargeFleetTests.get_nodes_via_rest() speeds up getting a node list by accessing remote Ursula's known_nodes directly. Since the tests share fully-functional Ursula objects (when in production those would be created anew from sprouts; see also #2588), this works - most of the time. But when somehow a teacher turns out to be an Ursula created from a NodeSprout, this approach fails. NodeSprout.mature() does not invoke Learner.__init__, where __known_nodes would be created, so the resulting Ursula is in an invalid state.
With #2709 the test fails too, but differently - in that case we may not even have an Ursula at the moment of get_nodes_via_rest() call, because the sprouts are not matured in FleetSensor anymore. Production middleware attempts to mature a sprout before connecting to the node, but not the mock one. So, again, known_nodes is inaccessible.
Additionally, in learn_from_teacher_node(), when processing that error, there is a secondary fail at except current_teacher.InvalidNode as e - since current_teacher is a sprout, it does not have InvalidNode either.
One of the solutions to this is a more clear separation of sprouts, remote Ursulas and local Ursulas, and more localized mature() calls, to avoid situations with passing node_or_sprout to functions, where that object could in fact be one of the three different objects with different functionality. Logically, it seems, the API of a sprout should be a subset of the API of a remote Ursula, and, in turn, its API should be a subset of the API of a local Ursula. Also, is it really necessary to wait up until a connection attempt to mature a sprout? Could it be done in FleetSensor, by an explicit request?
For example, instead of the current teacher selection system where we get the whole contents of known_nodes and then draw randomly from there, could we have a method FleetSensor.get_random_node() that would draw nodes and try to mature them until success?