mirror of https://github.com/nucypher/nucypher.git
Tests for new Bob and TreasureMap logic.
parent
4e86386caf
commit
3f55470870
|
@ -5,24 +5,68 @@ from umbral import pre
|
|||
from umbral.fragments import KFrag
|
||||
|
||||
|
||||
def test_bob_can_follow_treasure_map(enacted_policy, ursulas, alice, bob):
|
||||
"""
|
||||
Upon receiving a TreasureMap, Bob populates his list of Ursulas with the correct number.
|
||||
"""
|
||||
def test_bob_cannot_follow_the_treasure_map_in_isolation(enacted_policy, bob):
|
||||
|
||||
# Simulate Bob finding a TreasureMap on the DHT.
|
||||
# A test to show that Bob can do this can be found in test_network_actors.
|
||||
# Assume for the moment that Bob has already received a TreasureMap, perhaps via a side channel.
|
||||
hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map
|
||||
bob.treasure_maps[hrac] = treasure_map
|
||||
|
||||
# Bob knows of no Ursulas.
|
||||
assert len(bob.known_nodes) == 0
|
||||
|
||||
# ...until he follows the TreasureMap.
|
||||
bob.follow_treasure_map(hrac)
|
||||
# He can't successfully follow the TreasureMap until he learns of a node to ask.
|
||||
with pytest.raises(bob.NotEnoughUrsulas):
|
||||
bob.follow_treasure_map(hrac)
|
||||
|
||||
# Now he knows of all the Ursulas.
|
||||
assert len(bob.known_nodes) == len(treasure_map)
|
||||
|
||||
@pytest.mark.usefixtures("treasure_map_is_set_on_dht")
|
||||
def test_bob_can_follow_treasure_map(enacted_policy, ursulas, bob, alice):
|
||||
"""
|
||||
Similar to above, but this time, we'll show that if Bob can connect to a single node, he can
|
||||
learn enough to follow the TreasureMap.
|
||||
|
||||
Also, we'll get the TreasureMap from the hrac alone (ie, not via a side channel).
|
||||
"""
|
||||
hrac = enacted_policy.hrac()
|
||||
|
||||
# Bob knows of no Ursulas.
|
||||
assert len(bob.known_nodes) == 0
|
||||
|
||||
# Now Bob will ask just a single Ursula to tell him of the nodes about which she's aware.
|
||||
bob.network_bootstrap([("127.0.0.1", ursulas[0].rest_port)])
|
||||
|
||||
# Success! This Ursula knew about all the nodes on the network!
|
||||
assert len(bob.known_nodes) == len(ursulas)
|
||||
|
||||
# Now, Bob can get the TreasureMap all by himself, and doesn't need a side channel.
|
||||
bob.get_treasure_map(alice, hrac)
|
||||
newly_discovered, total_known = bob.follow_treasure_map(hrac)
|
||||
|
||||
# He finds that he didn't need to discover any new nodes...
|
||||
assert len(newly_discovered) == 0
|
||||
|
||||
# ...because he already knew of all the Ursulas on the map.
|
||||
assert total_known == len(enacted_policy.treasure_map)
|
||||
|
||||
|
||||
def test_bob_can_follow_treasure_map_even_if_he_only_knows_of_one_node(enacted_policy, ursulas, bob):
|
||||
# Again, let's assume that he received the TreasureMap via a side channel.
|
||||
hrac, treasure_map = enacted_policy.hrac(), enacted_policy.treasure_map
|
||||
bob.treasure_maps[hrac] = treasure_map
|
||||
|
||||
# Now, let's create a scenario in which Bob knows of only one node.
|
||||
k, v = bob.known_nodes.popitem()
|
||||
bob.known_nodes = {k: v}
|
||||
assert len(bob.known_nodes) == 1
|
||||
|
||||
# This time, when he follows the TreasureMap...
|
||||
newly_discovered, total_known = bob.follow_treasure_map(hrac)
|
||||
|
||||
# The newly discovered nodes are all those in the TreasureMap except the one about which he already knew.
|
||||
assert len(newly_discovered) == len(treasure_map) - 1
|
||||
|
||||
# ...and his total known now matches the length of the TreasureMap.
|
||||
assert total_known == len(treasure_map)
|
||||
|
||||
|
||||
def test_bob_can_issue_a_work_order_to_a_specific_ursula(enacted_policy, alice, bob, ursulas,
|
||||
|
@ -145,13 +189,13 @@ def test_bob_gathers_and_combines(enacted_policy, bob, ursulas, capsule_side_cha
|
|||
assert len(bob._saved_work_orders) == 2
|
||||
|
||||
# ...but the policy requires us to collect more cfrags.
|
||||
assert len(bob._saved_work_orders) < enacted_policy.m
|
||||
assert len(bob._saved_work_orders) < enacted_policy.treasure_map.m
|
||||
|
||||
# Bob can't decrypt yet with just two CFrags. He needs to gather at least m.
|
||||
with pytest.raises(pre.GenericUmbralError):
|
||||
bob.decrypt(the_message_kit)
|
||||
|
||||
number_left_to_collect = enacted_policy.m - len(bob._saved_work_orders)
|
||||
number_left_to_collect = enacted_policy.treasure_map.m - len(bob._saved_work_orders)
|
||||
|
||||
new_work_orders = bob.generate_work_orders(enacted_policy.hrac(),
|
||||
the_message_kit.capsule,
|
||||
|
|
|
@ -5,12 +5,7 @@ import maya
|
|||
import pytest
|
||||
from constant_sorrow import constants
|
||||
from sqlalchemy.engine import create_engine
|
||||
from umbral import pre
|
||||
|
||||
from nkms.characters import Alice, Bob
|
||||
|
||||
from nkms.crypto.kits import MessageKit
|
||||
from nkms.crypto.powers import EncryptingPower
|
||||
from nkms.keystore import keystore
|
||||
from nkms.keystore.db import Base
|
||||
|
||||
|
@ -19,7 +14,6 @@ from nkms.data_sources import DataSource
|
|||
from nkms.keystore.keypairs import SigningKeypair
|
||||
|
||||
from nkms.network import blockchain_client
|
||||
from nkms.policy.models import Arrangement
|
||||
from tests.utilities import NUMBER_OF_URSULAS_IN_NETWORK, MockNetworkyStuff, make_ursulas, \
|
||||
URSULA_PORT, EVENT_LOOP
|
||||
|
||||
|
@ -46,7 +40,6 @@ def enacted_policy(idle_policy, ursulas):
|
|||
# Alice has a policy in mind and knows of enough qualifies Ursulas; she crafts an offer for them.
|
||||
deposit = constants.NON_PAYMENT
|
||||
contract_end_datetime = maya.now() + datetime.timedelta(days=5)
|
||||
# contract = Contract(idle_policy.n, deposit, contract_end_datetime)
|
||||
|
||||
networky_stuff = MockNetworkyStuff(ursulas)
|
||||
found_ursulas = idle_policy.find_ursulas(networky_stuff, deposit, expiration=contract_end_datetime)
|
||||
|
@ -58,18 +51,17 @@ def enacted_policy(idle_policy, ursulas):
|
|||
|
||||
@pytest.fixture(scope="module")
|
||||
def alice(ursulas):
|
||||
ALICE = Alice()
|
||||
ALICE = Alice(network_middleware=MockNetworkyStuff(ursulas))
|
||||
ALICE.server.listen(8471)
|
||||
ALICE.__resource_id = b"some_resource_id"
|
||||
EVENT_LOOP.run_until_complete(ALICE.server.bootstrap([("127.0.0.1", u.dht_port) for u in ursulas]))
|
||||
ALICE.network_bootstrap([("127.0.0.1", u.rest_port) for u in ursulas])
|
||||
return ALICE
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def bob():
|
||||
BOB = Bob()
|
||||
BOB.server.listen(8475)
|
||||
EVENT_LOOP.run_until_complete(BOB.server.bootstrap([("127.0.0.1", URSULA_PORT)]))
|
||||
def bob(ursulas):
|
||||
BOB = Bob(network_middleware=MockNetworkyStuff(ursulas))
|
||||
return BOB
|
||||
|
||||
|
||||
|
@ -85,8 +77,9 @@ def ursulas():
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def treasure_map_is_set_on_dht(enacted_policy):
|
||||
enacted_policy.publish_treasure_map()
|
||||
def treasure_map_is_set_on_dht(enacted_policy, ursulas):
|
||||
networky_stuff = MockNetworkyStuff(ursulas)
|
||||
enacted_policy.publish_treasure_map(networky_stuff, use_dht=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
|
|
@ -7,7 +7,7 @@ from kademlia.utils import digest
|
|||
from nkms.crypto.api import keccak_digest
|
||||
from nkms.crypto.kits import UmbralMessageKit
|
||||
from nkms.network import blockchain_client
|
||||
from nkms.network.protocols import dht_value_splitter
|
||||
from nkms.network.protocols import dht_value_splitter, dht_with_hrac_splitter
|
||||
from tests.utilities import MockNetworkyStuff, EVENT_LOOP, URSULA_PORT, NUMBER_OF_URSULAS_IN_NETWORK
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ def test_vladimir_illegal_interface_key_does_not_propagate(ursulas):
|
|||
assert ursula.server.protocol.illegal_keys_seen == []
|
||||
|
||||
# Vladimir does almost everything right....
|
||||
value = vladimir.interface_dht_value()
|
||||
value = vladimir.interface_info_with_metadata()
|
||||
|
||||
# Except he sets an illegal key for his interface.
|
||||
illegal_key = b"Not allowed to set arbitrary key for this."
|
||||
|
@ -55,17 +55,21 @@ def test_alice_finds_ursula_via_dht(alice, ursulas):
|
|||
ursula_index = 1
|
||||
all_ursulas = blockchain_client._ursulas_on_blockchain
|
||||
value = alice.server.get_now(all_ursulas[ursula_index])
|
||||
header, _signature, _ursula_pubkey_sig, _hrac, interface_info = dht_value_splitter(value,
|
||||
header, _signature, _ursula_pubkey_sig, interface_info = dht_value_splitter(value,
|
||||
return_remainder=True)
|
||||
|
||||
assert header == constants.BYTESTRING_IS_URSULA_IFACE_INFO
|
||||
port = msgpack.loads(interface_info)[0]
|
||||
port = msgpack.loads(interface_info)[1]
|
||||
assert port == URSULA_PORT + ursula_index
|
||||
|
||||
|
||||
def test_alice_finds_ursula_via_rest(alice, ursulas):
|
||||
networky_stuff = MockNetworkyStuff(ursulas)
|
||||
new_nodes = alice.learn_about_nodes(networky_stuff, address="https://localhost", port=ursulas[0].rest_port)
|
||||
|
||||
# Imagine alice knows of nobody.
|
||||
alice.known_nodes = {}
|
||||
|
||||
new_nodes = alice.learn_about_nodes(address="https://localhost", port=ursulas[0].rest_port)
|
||||
assert len(new_nodes) == len(ursulas)
|
||||
|
||||
for ursula in ursulas:
|
||||
|
@ -87,7 +91,8 @@ def test_alice_sets_treasure_map_on_network(enacted_policy, ursulas):
|
|||
"""
|
||||
Having enacted all the policies of a PolicyGroup, Alice creates a TreasureMap and sends it to Ursula via the DHT.
|
||||
"""
|
||||
_, packed_encrypted_treasure_map, _, _ = enacted_policy.publish_treasure_map()
|
||||
networky_stuff = MockNetworkyStuff(ursulas)
|
||||
_, packed_encrypted_treasure_map, _, _ = enacted_policy.publish_treasure_map(networky_stuff=networky_stuff, use_dht=True)
|
||||
|
||||
treasure_map_as_set_on_network = ursulas[0].server.storage[
|
||||
digest(enacted_policy.treasure_map_dht_key())]
|
||||
|
@ -120,7 +125,7 @@ def test_treasure_map_stored_by_ursula_is_the_correct_one_for_bob(alice, bob, ur
|
|||
treasure_map_as_set_on_network = ursulas[0].server.storage[
|
||||
digest(enacted_policy.treasure_map_dht_key())]
|
||||
|
||||
header, _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_value_splitter(
|
||||
header, _signature_for_ursula, pubkey_sig_alice, hrac, encrypted_treasure_map = dht_with_hrac_splitter(
|
||||
treasure_map_as_set_on_network, return_remainder=True)
|
||||
|
||||
assert header == constants.BYTESTRING_IS_TREASURE_MAP
|
||||
|
@ -149,13 +154,13 @@ def test_bob_can_retreive_the_treasure_map_and_decrypt_it(enacted_policy, ursula
|
|||
|
||||
# If Bob doesn't know about any Ursulas, he can't find the TreasureMap via the REST swarm:
|
||||
with pytest.raises(bob.NotEnoughUrsulas):
|
||||
treasure_map_from_wire = bob.get_treasure_map(enacted_policy, networky_stuff)
|
||||
treasure_map_from_wire = bob.get_treasure_map(enacted_policy.alice, enacted_policy.hrac())
|
||||
|
||||
# Let's imagine he has learned about some - say, from the blockchain.
|
||||
bob.known_nodes = {u.interface_dht_key(): u for u in ursulas}
|
||||
bob.known_nodes = {u.interface_info_with_metadata(): u for u in ursulas}
|
||||
|
||||
# Now try.
|
||||
treasure_map_from_wire = bob.get_treasure_map(enacted_policy, networky_stuff)
|
||||
treasure_map_from_wire = bob.get_treasure_map(enacted_policy.alice, enacted_policy.hrac())
|
||||
|
||||
assert enacted_policy.treasure_map == treasure_map_from_wire
|
||||
|
||||
|
@ -167,10 +172,10 @@ def test_treaure_map_is_legit(enacted_policy):
|
|||
alice = enacted_policy.alice
|
||||
for ursula_interface_id in enacted_policy.treasure_map:
|
||||
value = alice.server.get_now(ursula_interface_id)
|
||||
header, signature, ursula_pubkey_sig, hrac, interface_info = dht_value_splitter(value,
|
||||
header, signature, ursula_pubkey_sig, interface_info = dht_value_splitter(value,
|
||||
return_remainder=True)
|
||||
assert header == constants.BYTESTRING_IS_URSULA_IFACE_INFO
|
||||
port = msgpack.loads(interface_info)[0]
|
||||
port = msgpack.loads(interface_info)[1]
|
||||
legal_ports = range(NUMBER_OF_URSULAS_IN_NETWORK, NUMBER_OF_URSULAS_IN_NETWORK + URSULA_PORT)
|
||||
assert port in legal_ports
|
||||
|
||||
|
|
|
@ -26,14 +26,14 @@ def make_ursulas(how_many_ursulas: int, ursula_starting_port: int) -> list:
|
|||
URSULAS = []
|
||||
for _u in range(how_many_ursulas):
|
||||
port = ursula_starting_port + _u
|
||||
_URSULA = Ursula(dht_port=port, dht_interface="127.0.0.1", db_name="test-{}".format(port), rest_port=port+100) # TODO: Make ports unstupid and more clear.
|
||||
_URSULA = Ursula(dht_port=port, ip_address="127.0.0.1", db_name="test-{}".format(port), rest_port=port+100) # TODO: Make ports unstupid and more clear.
|
||||
|
||||
class MockDatastoreThreadPool(object):
|
||||
def callInThread(self, f, *args, **kwargs):
|
||||
return f(*args, **kwargs)
|
||||
|
||||
_URSULA.datastore_threadpool = MockDatastoreThreadPool()
|
||||
_URSULA.listen()
|
||||
_URSULA.dht_listen()
|
||||
|
||||
URSULAS.append(_URSULA)
|
||||
|
||||
|
@ -53,8 +53,9 @@ class MockArrangementResponse(ArrangementResponse):
|
|||
|
||||
|
||||
class MockNetworkyStuff(NetworkyStuff):
|
||||
|
||||
def __init__(self, ursulas):
|
||||
self._ursulas = {u.interface_dht_key(): u for u in ursulas}
|
||||
self._ursulas = {bytes(u.stamp): u for u in ursulas}
|
||||
self.ursulas = iter(ursulas)
|
||||
|
||||
def go_live_with_policy(self, ursula, policy_offer):
|
||||
|
@ -72,7 +73,7 @@ class MockNetworkyStuff(NetworkyStuff):
|
|||
def enact_policy(self, ursula, hrac, payload):
|
||||
mock_client = TestClient(ursula.rest_app)
|
||||
response = mock_client.post('http://localhost/kFrag/{}'.format(hrac.hex()), payload)
|
||||
return True, ursula.interface_dht_key()
|
||||
return True, ursula.stamp.as_umbral_pubkey()
|
||||
|
||||
def send_work_order_payload_to_ursula(self, work_order):
|
||||
mock_client = TestClient(work_order.ursula.rest_app)
|
||||
|
@ -81,28 +82,50 @@ class MockNetworkyStuff(NetworkyStuff):
|
|||
return mock_client.post('http://localhost/kFrag/{}/reencrypt'.format(hrac_as_hex), payload)
|
||||
|
||||
def get_treasure_map_from_node(self, node, map_id):
|
||||
mock_client = TestClient(node.rest_app)
|
||||
for ursula in self._ursulas.values():
|
||||
if ursula.rest_port == node.rest_port:
|
||||
rest_app = ursula.rest_app
|
||||
break
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Can't find an Ursula with port {} - did you spin up the right test ursulas?".format(port))
|
||||
mock_client = TestClient(ursula.rest_app)
|
||||
return mock_client.get("http://localhost/treasure_map/{}".format(map_id.hex()))
|
||||
|
||||
def ursula_from_rest_interface(self, address, port):
|
||||
for ursula in self.ursulas:
|
||||
for ursula in self._ursulas.values():
|
||||
if ursula.rest_port == port:
|
||||
rest_app = ursula.rest_app
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("Can't find that one - did you spin up the right test ursulas?")
|
||||
raise RuntimeError(
|
||||
"Can't find an Ursula with port {} - did you spin up the right test ursulas?".format(port))
|
||||
mock_client = TestClient(ursula.rest_app)
|
||||
response = mock_client.get("http://localhost/public_keys")
|
||||
return response
|
||||
|
||||
def get_nodes_via_rest(self, address, port):
|
||||
for ursula in self.ursulas:
|
||||
for ursula in self._ursulas.values():
|
||||
if ursula.rest_port == port:
|
||||
rest_app = ursula.rest_app
|
||||
break
|
||||
else:
|
||||
raise RuntimeError("Can't find that one - did you spin up the right test ursulas?")
|
||||
raise RuntimeError("Can't find an Ursula with port {} - did you spin up the right test ursulas?".format(port))
|
||||
mock_client = TestClient(ursula.rest_app)
|
||||
response = mock_client.get("http://localhost/list_nodes")
|
||||
return response
|
||||
|
||||
def push_treasure_map_to_node(self, node, map_id, map_payload):
|
||||
port = node.rest_port
|
||||
for ursula in self._ursulas.values():
|
||||
if ursula.rest_port == port:
|
||||
rest_app = ursula.rest_app
|
||||
break
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Can't find an Ursula with port {} - did you spin up the right test ursulas?".format(port))
|
||||
mock_client = TestClient(ursula.rest_app)
|
||||
response = mock_client.post("http://localhost/treasure_map/{}".format(map_id.hex()),
|
||||
data=map_payload, verify=False)
|
||||
return response
|
||||
|
||||
|
|
Loading…
Reference in New Issue