Implementing InterfaceInfo.

pull/330/head
jMyles 2018-06-14 01:49:20 -07:00 committed by Kieran Prasch
parent 0c7646fa94
commit 36b86eb6bb
7 changed files with 72 additions and 30 deletions

View File

@ -16,7 +16,7 @@ from nucypher.network.middleware import RestMiddleware
from umbral.keys import UmbralPublicKey
URSULA = Ursula.from_rest_url(network_middleware=RestMiddleware(),
ip_address="localhost",
host="localhost",
port=3601)
network_middleware = SandboxRestMiddleware([URSULA])

View File

@ -12,7 +12,7 @@ class SandboxRestMiddleware(RestMiddleware):
def consider_arrangement(self, contract=None):
ursula = Ursula.from_rest_url(
self,
ip_address="localhost",
host="localhost",
port=3601,
)
response = requests.post("https://localhost:3601/consider_arrangement", bytes(contract), verify=False)

View File

@ -248,8 +248,8 @@ class Character:
"""
if rest_address is None:
current_teacher = self.current_teacher_node()
rest_address = current_teacher.ip_address
port = current_teacher.rest_port
rest_address = current_teacher.rest_interface.host
port = current_teacher.rest_interface.port
response = self.network_middleware.get_nodes_via_rest(rest_address,
port, node_ids=self._node_ids_to_learn_about_immediately)
@ -262,20 +262,16 @@ class Character:
split_nodes = ursula_interface_splitter.repeat(nodes)
for node_meta in split_nodes:
header, sig, pubkey, ether_address, interface_info = node_meta
header, sig, pubkey, ether_address, rest_info, dht_info = node_meta
message = ether_address + rest_info + dht_info
if not pubkey in self._known_nodes:
if sig.verify(keccak_digest(interface_info), pubkey):
# GARBAGE GARBAGE GARBAGE
rest_address, dht_port, rest_port = msgpack.loads(interface_info)
# ENDGARBAGE
if sig.verify(message, pubkey):
# TOOD: Is this too eager? Does it make sense to only learn later, when we want to make an Arrangement with this node?
ursula = Ursula.from_rest_url(network_middleware=self.network_middleware,
ip_address=rest_address.decode("utf-8"),
port=rest_port)
host=rest_info.host,
port=rest_info.port)
self.remember_node(ursula)
@ -283,7 +279,6 @@ class Character:
self._node_ids_to_learn_about_immediately.discard(pubkey)
else:
message = "Suspicious Activity: Discovered node with bad signature: {}. " \
"Propagated by: {}:{}".format(node_meta, rest_address, port)
self.log.warning(message)
@ -736,18 +731,25 @@ class Ursula(Character, ProxyRESTServer, Miner):
pass
# TODO: 289
def __init__(self, is_me=True,
def __init__(self,
rest_host,
rest_port,
is_me=True,
dht_host=None,
dht_port=None,
federated_only=False,
*args,
**kwargs):
self.dht_port = dht_port
if dht_host:
self.dht_interface = InterfaceInfo(host=dht_host, port=dht_port)
else:
self.dht_interface = constants.NO_INTERFACE.bool_value(False)
self._work_orders = []
Character.__init__(self, is_me=is_me, *args, **kwargs)
if not federated_only:
Miner.__init__(self, is_me=is_me, *args, **kwargs)
ProxyRESTServer.__init__(self, *args, **kwargs)
ProxyRESTServer.__init__(self, host=rest_host, port=rest_port, *args, **kwargs)
if is_me is True:
self.attach_dht_server()

View File

@ -114,3 +114,30 @@ class NucypherSeedOnlyProtocol(NucypherHashProtocol):
"got a store request from %s, but THIS VALUE WILL NOT BE STORED as this is a seed-only node." % str(
sender))
return True
class InterfaceInfo:
expected_bytes_length = lambda: VariableLengthBytestring
def __init__(self, host, port):
self.host = host
self.port = port
@classmethod
def from_bytes(cls, url_string):
host_bytes, port_bytes = url_string.split(b":")
port = int.from_bytes(port_bytes, "big")
host = host_bytes.decode("utf-8")
return cls(host=host, port=port)
def __bytes__(self):
return bytes(self.host, encoding="utf-8") + b":" + self.port.to_bytes(4, "big")
def __add__(self, other):
return bytes(self) + bytes(other)
def __radd__(self, other):
return bytes(other) + bytes(self)
ursula_interface_splitter = dht_value_splitter + BytestringSplitter(InterfaceInfo) * 2

View File

@ -18,7 +18,7 @@ from nucypher.crypto.kits import UmbralMessageKit
from nucypher.crypto.powers import EncryptingPower, SigningPower
from nucypher.keystore.threading import ThreadedSession
from nucypher.network.protocols import NucypherSeedOnlyProtocol, NucypherHashProtocol, \
dht_with_hrac_splitter
dht_with_hrac_splitter, InterfaceInfo
from nucypher.network.storage import SeedOnlyStorage
@ -84,10 +84,9 @@ class NucypherSeedOnlyDHTServer(NucypherDHTServer):
class ProxyRESTServer:
def __init__(self, ip_address=None, rest_port=None, db_name=None, *args, **kwargs):
"""TODO: A server with no uri...? """
self.ip_address = ip_address
self.rest_port = rest_port
def __init__(self, host=None, port=None, db_name=None, *args, **kwargs):
self.rest_interface = InterfaceInfo(host=host, port=port)
self.db_name = db_name
self._rest_app = None
@ -99,7 +98,15 @@ class ProxyRESTServer:
instance = cls()
def public_key(self, power_class: ClassVar):
"""Implemented on Ursula subclass"""
"""Implemented on Ursula"""
raise NotImplementedError
def public_address(self):
"""Implemented on Ursula"""
raise NotImplementedError
def stamp(self, *args, **kwargs):
"""Implemented on Ursula"""
raise NotImplementedError
def attach_rest_server(self, db_name):
@ -112,7 +119,7 @@ class ProxyRESTServer:
'POST',
self.reencrypt_via_rest),
Route('/public_keys', 'GET',
self.get_signing_and_encrypting_public_keys),
self.public_information),
Route('/list_nodes', 'GET',
self.list_all_active_nodes_about_which_we_know),
Route('/consider_arrangement',
@ -150,20 +157,25 @@ class ProxyRESTServer:
# Actual REST Endpoints and utilities
#####################################
def get_signing_and_encrypting_public_keys(self):
def public_information(self):
"""
REST endpoint for getting both signing and encrypting public keys.
REST endpoint for public keys and address..
"""
headers = {'Content-Type': 'application/octet-stream'}
# TODO: Calling public_address() works here because this is mixed in with Character, but it's not really right.
message = bytes(self.public_key(SigningPower)) + bytes(self.public_key(EncryptingPower)) + self.public_address
signature = self.stamp(message)
response = Response(
content=bytes(self.public_key(SigningPower)) + bytes(self.public_key(EncryptingPower)),
content=signature + message,
headers=headers)
return response
def list_all_active_nodes_about_which_we_know(self):
headers = {'Content-Type': 'application/octet-stream'}
# TODO: mm hmmph *slowly exhales* fffff. Some 227 right here.
ursulas_as_bytes = bytes().join(self.dht_server.protocol.ursulas.values())
ursulas_as_bytes += self.interface_info_with_metadata()
signature = self.stamp(ursulas_as_bytes)
@ -279,4 +291,3 @@ class ProxyRESTServer:
else:
# TODO: Make this a proper 500 or whatever.
assert False

View File

@ -61,3 +61,4 @@ def test_alice_can_get_ursulas_keys_via_rest(ursulas):
public_keys = {SigningPower: signing_key, EncryptingPower: encrypting_key}
stranger_ursula_from_public_keys = Ursula.from_public_keys(public_keys)
assert stranger_ursula_from_public_keys == ursulas[0]

View File

@ -36,9 +36,10 @@ def make_ursulas(ether_addresses: list, ursula_starting_port: int, miners=False)
for port, ether_address in enumerate(ether_addresses, start=ursula_starting_port):
ursula = Ursula(is_me=True,
ether_address=ether_address,
dht_host="127.0.0.1",
dht_port=port,
db_name="test-{}".format(port),
ip_address="127.0.0.1",
rest_host="127.0.0.1",
rest_port=port+100,
always_be_learning=False
)
@ -64,7 +65,7 @@ def make_ursulas(ether_addresses: list, ursula_starting_port: int, miners=False)
pass
ursulas.add(ursula)
_ALL_URSULAS[ursula.rest_port] = ursula
_ALL_URSULAS[ursula.rest_interface.port] = ursula
for ursula_to_teach in ursulas:
# Add other Ursulas as known nodes.