mirror of https://github.com/nucypher/nucypher.git
Record node snapshots only at the end of a learning round, not every time a node is remembered.
parent
1ef2209262
commit
9be9ac7370
|
@ -178,7 +178,7 @@ class Character(Learner):
|
|||
raise
|
||||
|
||||
if is_me is True:
|
||||
self.known_nodes.start_tracking_state()
|
||||
self.known_nodes.record_fleet_state()
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return bytes(self.stamp) == bytes(other.stamp)
|
||||
|
|
|
@ -586,7 +586,7 @@ class Ursula(VerifiableNode, Character, Miner):
|
|||
# Logging / Updating
|
||||
#
|
||||
if is_me:
|
||||
self.known_nodes.start_tracking_state(additional_nodes_to_track=[self]) # To account for nodes loaded from NodeStorage.
|
||||
self.known_nodes.record_fleet_state(additional_nodes_to_track=[self])
|
||||
message = "Initialized Self {} | {}".format(self.__class__.__name__, self.checksum_public_address)
|
||||
self.log.info(message)
|
||||
else:
|
||||
|
|
|
@ -92,7 +92,7 @@ class FleetStateTracker:
|
|||
def __init__(self):
|
||||
self.additional_nodes_to_track = []
|
||||
self.updated = maya.now()
|
||||
self._nodes = {}
|
||||
self._nodes = OrderedDict()
|
||||
self.states = OrderedDict()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
|
@ -100,7 +100,7 @@ class FleetStateTracker:
|
|||
|
||||
if self._tracking:
|
||||
self.log.info("Updating fleet state after saving node {}".format(value))
|
||||
self.update_fleet_state()
|
||||
self.record_fleet_state()
|
||||
else:
|
||||
self.log.debug("Not updating fleet state.")
|
||||
|
||||
|
@ -122,6 +122,9 @@ class FleetStateTracker:
|
|||
def __eq__(self, other):
|
||||
return self._nodes == other._nodes
|
||||
|
||||
def __repr__(self):
|
||||
return self._nodes.__repr__()
|
||||
|
||||
@property
|
||||
def checksum(self):
|
||||
return self._checksum
|
||||
|
@ -152,8 +155,15 @@ class FleetStateTracker:
|
|||
fleet_state_updated_bytes = self.updated.epoch.to_bytes(4, byteorder="big")
|
||||
return fleet_state_checksum_bytes + fleet_state_updated_bytes
|
||||
|
||||
def update_fleet_state(self):
|
||||
checksum = keccak_digest(b"".join(bytes(n) for n in self.sorted())).hex()
|
||||
def record_fleet_state(self, additional_nodes_to_track=None):
|
||||
if not self._nodes:
|
||||
# No news here.
|
||||
return
|
||||
if additional_nodes_to_track:
|
||||
self.additional_nodes_to_track.extend(additional_nodes_to_track)
|
||||
sorted_nodes = self.sorted()
|
||||
sorted_nodes_joined = b"".join(bytes(n) for n in sorted_nodes)
|
||||
checksum = keccak_digest(sorted_nodes_joined).hex()
|
||||
if checksum != self.checksum:
|
||||
self.checksum = keccak_digest(b"".join(bytes(n) for n in self.sorted())).hex()
|
||||
self.updated = maya.now()
|
||||
|
@ -161,15 +171,10 @@ class FleetStateTracker:
|
|||
# its own class, FleetState, and use it as the basis for partial updates.
|
||||
self.states[checksum] = self.state_template(nickname=self.nickname,
|
||||
icon=self.icon(),
|
||||
nodes=self.sorted(),
|
||||
nodes=sorted_nodes,
|
||||
updated=self.updated,
|
||||
)
|
||||
|
||||
def start_tracking_state(self, additional_nodes_to_track=[]):
|
||||
self.additional_nodes_to_track.extend(additional_nodes_to_track)
|
||||
self._tracking = True
|
||||
self.update_fleet_state()
|
||||
|
||||
def sorted(self):
|
||||
nodes_to_consider = list(self._nodes.values()) + self.additional_nodes_to_track
|
||||
return sorted(nodes_to_consider, key=lambda n: n.checksum_public_address)
|
||||
|
@ -313,7 +318,7 @@ class Learner:
|
|||
for node in stored_nodes:
|
||||
self.remember_node(node)
|
||||
|
||||
def remember_node(self, node, force_verification_check=False):
|
||||
def remember_node(self, node, force_verification_check=False, record_fleet_state=True):
|
||||
|
||||
if node == self: # No need to remember self.
|
||||
return False
|
||||
|
@ -342,8 +347,8 @@ class Learner:
|
|||
listeners = self._learning_listeners.pop(node.checksum_public_address, tuple())
|
||||
address = node.checksum_public_address
|
||||
|
||||
self.__known_nodes[address] = node
|
||||
if self in self.known_nodes._nodes.values():
|
||||
self.known_nodes[address] = node
|
||||
if self in self.known_nodes:
|
||||
raise RuntimeError
|
||||
|
||||
if self.save_metadata:
|
||||
|
@ -354,6 +359,9 @@ class Learner:
|
|||
listener.add(address)
|
||||
self._node_ids_to_learn_about_immediately.discard(address)
|
||||
|
||||
if record_fleet_state:
|
||||
self.known_nodes.record_fleet_state()
|
||||
|
||||
return True
|
||||
|
||||
def start_learning_loop(self, now=False):
|
||||
|
@ -660,7 +668,7 @@ class Learner:
|
|||
message = "Suspicious Activity: Discovered node with bad signature: {}. " \
|
||||
"Propagated by: {}".format(current_teacher.checksum_public_address, rest_url)
|
||||
self.log.warn(message)
|
||||
new = self.remember_node(node)
|
||||
new = self.remember_node(node, record_fleet_state=False)
|
||||
if new:
|
||||
new_nodes.append(node)
|
||||
|
||||
|
@ -671,9 +679,11 @@ class Learner:
|
|||
current_teacher,
|
||||
len(node_list),
|
||||
len(new_nodes)), )
|
||||
if new_nodes and self.known_certificates_dir:
|
||||
for node in new_nodes:
|
||||
node.save_certificate_to_disk(self.known_certificates_dir, force=True)
|
||||
if new_nodes:
|
||||
self.known_nodes.record_fleet_state()
|
||||
if self.known_certificates_dir:
|
||||
for node in new_nodes:
|
||||
node.save_certificate_to_disk(self.known_certificates_dir, force=True)
|
||||
|
||||
return new_nodes
|
||||
|
||||
|
|
|
@ -41,20 +41,50 @@ def test_old_state_is_preserved(federated_ursulas, ursula_federated_test_config)
|
|||
ursula_config=ursula_federated_test_config,
|
||||
quantity=1,
|
||||
know_each_other=False)
|
||||
another_ursula = lonely_ursula_maker().pop()
|
||||
lonely_learner = lonely_ursula_maker().pop()
|
||||
|
||||
# This Ursula doesn't know about any nodes.
|
||||
assert len(another_ursula.known_nodes) == 0
|
||||
assert len(lonely_learner.known_nodes) == 0
|
||||
|
||||
some_ursula_in_the_fleet = list(federated_ursulas)[0]
|
||||
another_ursula.remember_node(some_ursula_in_the_fleet)
|
||||
checksum_after_learning_one = another_ursula.known_nodes.checksum
|
||||
lonely_learner.remember_node(some_ursula_in_the_fleet)
|
||||
checksum_after_learning_one = lonely_learner.known_nodes.checksum
|
||||
|
||||
another_ursula_in_the_fleet = list(federated_ursulas)[1]
|
||||
another_ursula.remember_node(another_ursula_in_the_fleet)
|
||||
checksum_after_learning_two = another_ursula.known_nodes.checksum
|
||||
lonely_learner.remember_node(another_ursula_in_the_fleet)
|
||||
checksum_after_learning_two = lonely_learner.known_nodes.checksum
|
||||
|
||||
assert checksum_after_learning_one != checksum_after_learning_two
|
||||
|
||||
assert another_ursula.known_nodes.states[checksum_after_learning_one].nodes == [some_ursula_in_the_fleet, another_ursula]
|
||||
assert another_ursula.known_nodes.states[checksum_after_learning_two].nodes == [some_ursula_in_the_fleet, another_ursula_in_the_fleet, another_ursula]
|
||||
proper_first_state = sorted([some_ursula_in_the_fleet, lonely_learner], key=lambda n: n.checksum_public_address)
|
||||
assert lonely_learner.known_nodes.states[checksum_after_learning_one].nodes == proper_first_state
|
||||
|
||||
proper_second_state = sorted([some_ursula_in_the_fleet, another_ursula_in_the_fleet, lonely_learner], key=lambda n: n.checksum_public_address)
|
||||
assert lonely_learner.known_nodes.states[checksum_after_learning_two].nodes == proper_second_state
|
||||
|
||||
|
||||
def test_state_is_recorded_after_learning(federated_ursulas, ursula_federated_test_config):
|
||||
"""
|
||||
Similar to above, but this time we show that the Learner records a new state only once after learning
|
||||
about a bunch of nodes.
|
||||
"""
|
||||
lonely_ursula_maker = partial(make_federated_ursulas,
|
||||
ursula_config=ursula_federated_test_config,
|
||||
quantity=1,
|
||||
know_each_other=False)
|
||||
lonely_learner = lonely_ursula_maker().pop()
|
||||
|
||||
# This Ursula doesn't know about any nodes.
|
||||
assert len(lonely_learner.known_nodes) == 0
|
||||
|
||||
some_ursula_in_the_fleet = list(federated_ursulas)[0]
|
||||
lonely_learner.remember_node(some_ursula_in_the_fleet)
|
||||
|
||||
# The rest of the fucking owl.
|
||||
lonely_learner.learn_from_teacher_node()
|
||||
|
||||
states = list(lonely_learner.known_nodes.states.values())
|
||||
assert len(states) == 2
|
||||
|
||||
assert len(states[0].nodes) == 1
|
||||
assert len(states[1].nodes) == len(federated_ursulas)
|
||||
|
|
Loading…
Reference in New Issue