mirror of https://github.com/nucypher/nucypher.git
Past fleet state as a param during learning; only learn if there's a difference.
parent
1635f00626
commit
31f66cf071
|
@ -104,19 +104,26 @@ class RestMiddleware:
|
|||
url,
|
||||
certificate_filepath,
|
||||
announce_nodes=None,
|
||||
nodes_i_need=None):
|
||||
nodes_i_need=None,
|
||||
client=requests,
|
||||
fleet_checksum=None):
|
||||
if nodes_i_need:
|
||||
# TODO: This needs to actually do something.
|
||||
# Include node_ids in the request; if the teacher node doesn't know about the
|
||||
# nodes matching these ids, then it will ask other nodes.
|
||||
pass
|
||||
|
||||
if fleet_checksum:
|
||||
params = {'fleet': fleet_checksum}
|
||||
else:
|
||||
params = {}
|
||||
|
||||
if announce_nodes:
|
||||
payload = bytes().join(bytes(n) for n in announce_nodes)
|
||||
response = requests.post("https://{}/node_metadata".format(url),
|
||||
response = client.post("https://{}/node_metadata".format(url),
|
||||
verify=certificate_filepath,
|
||||
data=payload, timeout=2)
|
||||
data=payload, timeout=2, params=params)
|
||||
else:
|
||||
response = requests.get("https://{}/node_metadata".format(url),
|
||||
verify=certificate_filepath, timeout=2)
|
||||
response = client.get("https://{}/node_metadata".format(url),
|
||||
verify=certificate_filepath, timeout=2, params=params)
|
||||
return response
|
||||
|
|
|
@ -542,7 +542,8 @@ class Learner:
|
|||
response = self.network_middleware.get_nodes_via_rest(url=rest_url,
|
||||
nodes_i_need=self._node_ids_to_learn_about_immediately,
|
||||
announce_nodes=announce_nodes,
|
||||
certificate_filepath=certificate_filepath)
|
||||
certificate_filepath=certificate_filepath,
|
||||
fleet_checksum=self.known_nodes.checksum)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
unresponsive_nodes.add(current_teacher)
|
||||
teacher_rest_info = current_teacher.rest_information()[0]
|
||||
|
@ -550,9 +551,9 @@ class Learner:
|
|||
# TODO: This error isn't necessarily "no repsonse" - let's maybe pass on the text of the exception here.
|
||||
self.log.info("No Response from teacher: {}:{}.".format(teacher_rest_info.host, teacher_rest_info.port))
|
||||
self.cycle_teacher_node()
|
||||
return
|
||||
raise False
|
||||
|
||||
if response.status_code != 200:
|
||||
if response.status_code not in (200, 204):
|
||||
raise RuntimeError("Bad response from teacher: {} - {}".format(response, response.content))
|
||||
|
||||
signature, node_payload = signature_splitter(response.content, return_remainder=True)
|
||||
|
@ -561,12 +562,20 @@ class Learner:
|
|||
self.verify_from(current_teacher, node_payload, signature=signature)
|
||||
except current_teacher.InvalidSignature:
|
||||
# TODO: What to do if the teacher improperly signed the node payload?
|
||||
raise
|
||||
raise False
|
||||
|
||||
fleet_state_checksum_bytes, fleet_state_updated_bytes, nodes = FleetState.snapshot_splitter(node_payload, return_remainder=True)
|
||||
current_teacher.last_seen = maya.now()
|
||||
current_teacher.update_snapshot(checksum=fleet_state_checksum_bytes.hex(),
|
||||
updated=maya.MayaDT(int.from_bytes(fleet_state_updated_bytes, byteorder="big")))
|
||||
|
||||
self.cycle_teacher_node()
|
||||
|
||||
# TODO: This doesn't make sense - a decentralized node can still learn about a federated-only node.
|
||||
from nucypher.characters.lawful import Ursula
|
||||
if response.status_code == 204:
|
||||
return constants.FLEET_STATES_MATCH
|
||||
|
||||
node_list = Ursula.batch_from_bytes(nodes, federated_only=self.federated_only) # TODO: 466
|
||||
|
||||
new_nodes = []
|
||||
|
@ -595,11 +604,6 @@ class Learner:
|
|||
self._adjust_learning(new_nodes)
|
||||
|
||||
learning_round_log_message = "Learning round {}. Teacher: {} knew about {} nodes, {} were new."
|
||||
current_teacher.last_seen = maya.now()
|
||||
current_teacher.update_snapshot(checksum=fleet_state_checksum_bytes.hex(),
|
||||
updated=maya.MayaDT(int.from_bytes(fleet_state_updated_bytes, byteorder="big")))
|
||||
|
||||
self.cycle_teacher_node()
|
||||
self.log.info(learning_round_log_message.format(self._learning_round,
|
||||
current_teacher,
|
||||
len(node_list),
|
||||
|
|
|
@ -169,10 +169,20 @@ class ProxyRESTRoutes:
|
|||
return Response(bytes(signature) + payload, headers=headers)
|
||||
|
||||
def node_metadata_exchange(self, request: Request, query_params: QueryParams):
|
||||
# If these nodes already have the same fleet state, no exchange is necessary.
|
||||
learner_fleet_state = query_params.get('fleet')
|
||||
if learner_fleet_state == self._node_tracker.checksum:
|
||||
self.log.debug("Learner already knew fleet state {}; doing nothing.".format(learner_fleet_state))
|
||||
headers = {'Content-Type': 'application/octet-stream'}
|
||||
payload = self._node_tracker.snapshot()
|
||||
signature = self._stamp(payload)
|
||||
return Response(bytes(signature) + payload, headers=headers, status_code=204)
|
||||
|
||||
nodes = self._node_class.batch_from_bytes(request.body,
|
||||
federated_only=self.federated_only, # TODO: 466
|
||||
)
|
||||
# TODO: This logic is basically repeated in learn_from_teacher_node. Let's find a better way.
|
||||
|
||||
# TODO: This logic is basically repeated in learn_from_teacher_node and remember_node. Let's find a better way. 555
|
||||
for node in nodes:
|
||||
|
||||
if node.checksum_public_address in self._node_tracker:
|
||||
|
|
|
@ -80,24 +80,8 @@ class MockRestMiddleware(RestMiddleware):
|
|||
response = mock_client.get("http://localhost/public_information")
|
||||
return response
|
||||
|
||||
def get_nodes_via_rest(self, url, certificate_filepath, announce_nodes=None, nodes_i_need=None):
|
||||
|
||||
mock_client = self._get_mock_client_by_url(url)
|
||||
|
||||
if nodes_i_need:
|
||||
# TODO: This needs to actually do something.
|
||||
# Include node_ids in the request; if the teacher node doesn't know about the
|
||||
# nodes matching these ids, then it will ask other nodes.
|
||||
pass
|
||||
|
||||
if announce_nodes:
|
||||
response = mock_client.post("https://{}/node_metadata".format(url),
|
||||
verify=certificate_filepath,
|
||||
data=bytes().join(bytes(n) for n in announce_nodes))
|
||||
else:
|
||||
response = mock_client.get("https://{}/node_metadata".format(url),
|
||||
verify=certificate_filepath)
|
||||
return response
|
||||
def get_nodes_via_rest(self, url, *args, **kwargs):
|
||||
return super().get_nodes_via_rest(url, client=self._get_mock_client_by_url(url), *args, **kwargs)
|
||||
|
||||
def put_treasure_map_on_node(self, node, map_id, map_payload):
|
||||
mock_client = self._get_mock_client_by_ursula(node)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
from constant_sorrow.constants import FLEET_STATES_MATCH
|
||||
|
||||
|
||||
def test_all_nodes_have_same_fleet_state(federated_ursulas):
|
||||
checksums = [u.known_nodes.checksum for u in federated_ursulas]
|
||||
assert len(set(checksums)) == 1 # There is only 1 unique value.
|
||||
|
@ -18,3 +21,14 @@ def test_teacher_nodes_cycle(federated_ursulas):
|
|||
second_teacher = ursula._current_teacher_node
|
||||
|
||||
assert first_teacher != second_teacher
|
||||
|
||||
|
||||
def test_nodes_with_equal_fleet_state_do_not_send_anew(federated_ursulas):
|
||||
some_ursula = list(federated_ursulas)[2]
|
||||
another_ursula = list(federated_ursulas)[3]
|
||||
|
||||
# These two have the same fleet state.
|
||||
assert some_ursula.known_nodes.checksum == another_ursula.known_nodes.checksum
|
||||
some_ursula._current_teacher_node = another_ursula
|
||||
result = some_ursula.learn_from_teacher_node()
|
||||
assert result is FLEET_STATES_MATCH
|
Loading…
Reference in New Issue