2017-09-28 00:07:36 +00:00
|
|
|
import asyncio
|
2017-09-30 17:32:39 +00:00
|
|
|
import datetime
|
|
|
|
|
2017-10-28 17:53:56 +00:00
|
|
|
import msgpack
|
2017-10-04 04:13:46 +00:00
|
|
|
import pytest
|
2017-11-05 04:59:35 +00:00
|
|
|
|
2017-11-07 04:42:21 +00:00
|
|
|
from kademlia.utils import digest
|
2017-11-07 20:51:30 +00:00
|
|
|
from nkms.characters import Ursula, Alice, Character, Bob, congregate
|
2017-11-03 04:54:58 +00:00
|
|
|
from nkms.network.blockchain_client import list_all_ursulas
|
2017-10-28 02:26:23 +00:00
|
|
|
from nkms.network.node import NetworkyStuff
|
2017-09-30 17:32:39 +00:00
|
|
|
from nkms.policy.constants import NON_PAYMENT
|
2017-11-07 04:42:21 +00:00
|
|
|
from nkms.policy.models import PolicyManagerForAlice, PolicyOffer, Policy
|
2017-10-03 15:43:12 +00:00
|
|
|
|
2017-10-28 02:26:23 +00:00
|
|
|
EVENT_LOOP = asyncio.get_event_loop()
|
2017-10-27 01:39:13 +00:00
|
|
|
asyncio.set_event_loop(EVENT_LOOP)
|
|
|
|
|
2017-11-03 04:54:58 +00:00
|
|
|
URSULA_PORT = 7468
|
2017-11-07 04:42:21 +00:00
|
|
|
NUMBER_OF_URSULAS_IN_NETWORK = 6
|
|
|
|
|
|
|
|
URSULA_PORTS = range(URSULA_PORT, URSULA_PORT + NUMBER_OF_URSULAS_IN_NETWORK)
|
2017-10-27 01:39:13 +00:00
|
|
|
|
2017-11-03 04:54:58 +00:00
|
|
|
|
|
|
|
def make_fake_ursulas(how_many):
|
|
|
|
URSULAS = []
|
|
|
|
for _u in range(how_many):
|
|
|
|
_URSULA = Ursula()
|
|
|
|
_URSULA.attach_server()
|
|
|
|
_URSULA.listen(URSULA_PORT + _u, "127.0.0.1")
|
|
|
|
|
|
|
|
URSULAS.append(_URSULA)
|
|
|
|
|
|
|
|
for _counter, ursula in enumerate(URSULAS):
|
2017-11-07 04:42:21 +00:00
|
|
|
EVENT_LOOP.run_until_complete(
|
|
|
|
ursula.server.bootstrap([("127.0.0.1", URSULA_PORT + _c) for _c in range(how_many)]))
|
2017-11-03 04:54:58 +00:00
|
|
|
ursula.publish_interface_information()
|
|
|
|
|
|
|
|
return URSULAS
|
|
|
|
|
2017-11-05 04:59:35 +00:00
|
|
|
|
2017-11-07 04:42:21 +00:00
|
|
|
URSULAS = make_fake_ursulas(NUMBER_OF_URSULAS_IN_NETWORK)
|
2017-10-27 01:39:13 +00:00
|
|
|
|
|
|
|
ALICE = Alice()
|
|
|
|
ALICE.attach_server()
|
|
|
|
ALICE.server.listen(8471)
|
2017-10-28 04:32:32 +00:00
|
|
|
EVENT_LOOP.run_until_complete(ALICE.server.bootstrap([("127.0.0.1", URSULA_PORT)]))
|
|
|
|
|
|
|
|
BOB = Bob(alice=ALICE)
|
|
|
|
BOB.attach_server()
|
|
|
|
BOB.server.listen(8475)
|
2017-11-03 04:54:58 +00:00
|
|
|
EVENT_LOOP.run_until_complete(BOB.server.bootstrap([("127.0.0.1", URSULA_PORT)]))
|
2017-10-27 01:39:13 +00:00
|
|
|
|
2017-11-07 20:51:30 +00:00
|
|
|
congregate(ALICE, BOB, URSULAS[0])
|
2017-10-27 01:39:13 +00:00
|
|
|
|
|
|
|
|
2017-11-06 21:35:54 +00:00
|
|
|
def test_all_ursulas_know_about_all_other_ursulas():
|
|
|
|
ignorance = []
|
|
|
|
for acounter, announcing_ursula in enumerate(list_all_ursulas()):
|
|
|
|
for counter, propagating_ursula in enumerate(URSULAS):
|
|
|
|
if not digest(announcing_ursula) in propagating_ursula.server.storage:
|
|
|
|
ignorance.append((counter, acounter))
|
|
|
|
if ignorance:
|
|
|
|
pytest.fail(str(["{} didn't know about {}".format(counter, acounter) for counter, acounter in ignorance]))
|
|
|
|
|
|
|
|
|
2017-10-27 01:39:13 +00:00
|
|
|
def test_alice_finds_ursula():
|
2017-11-03 21:50:04 +00:00
|
|
|
ursula_index = 1
|
2017-11-03 04:54:58 +00:00
|
|
|
all_ursulas = list_all_ursulas()
|
2017-11-03 21:50:04 +00:00
|
|
|
getter = ALICE.server.get(all_ursulas[ursula_index])
|
2017-11-03 04:54:58 +00:00
|
|
|
loop = asyncio.get_event_loop()
|
2017-11-06 03:28:13 +00:00
|
|
|
value = loop.run_until_complete(getter)
|
|
|
|
signature, ursula_pubkey_sig, interface_info = msgpack.loads(value.lstrip(b"uaddr-"))
|
|
|
|
port, interface = msgpack.loads(interface_info)
|
2017-11-03 21:50:04 +00:00
|
|
|
assert port == URSULA_PORT + ursula_index
|
2017-10-27 01:39:13 +00:00
|
|
|
|
|
|
|
|
2017-11-06 21:35:54 +00:00
|
|
|
def test_vladimir_illegal_interface_key_does_not_propagate():
|
2017-11-06 03:28:13 +00:00
|
|
|
vladimir = URSULAS[0]
|
|
|
|
ursula = URSULAS[1]
|
|
|
|
|
|
|
|
# Ursula hasn't seen any illegal keys.
|
|
|
|
assert ursula.server.protocol.illegal_keys_seen == []
|
|
|
|
|
|
|
|
# Vladimir does almost everything right....
|
|
|
|
interface_info = msgpack.dumps((vladimir.port, vladimir.interface))
|
|
|
|
signature = vladimir.seal(interface_info)
|
|
|
|
value = b"uaddr-" + msgpack.dumps([signature, bytes(vladimir.seal), interface_info])
|
|
|
|
|
|
|
|
# Except he sets an illegal key for his interface.
|
|
|
|
illegal_key = "Not allowed to set arbitrary key for this."
|
|
|
|
setter = vladimir.server.set(key=illegal_key, value=value)
|
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
loop.run_until_complete(setter)
|
|
|
|
|
|
|
|
# Now Ursula has seen an illegal key.
|
|
|
|
assert digest(illegal_key) in ursula.server.protocol.illegal_keys_seen
|
|
|
|
|
|
|
|
|
2017-09-19 21:30:35 +00:00
|
|
|
class MockPolicyOfferResponse(object):
|
|
|
|
was_accepted = True
|
|
|
|
|
|
|
|
|
2017-10-28 02:26:23 +00:00
|
|
|
class MockNetworkyStuff(NetworkyStuff):
|
2017-11-07 04:42:21 +00:00
|
|
|
def __init__(self):
|
|
|
|
self.ursulas = iter(URSULAS)
|
|
|
|
|
2017-10-18 08:52:01 +00:00
|
|
|
def go_live_with_policy(self, ursula, policy_offer):
|
|
|
|
return
|
2017-09-19 21:30:35 +00:00
|
|
|
|
2017-10-28 02:26:23 +00:00
|
|
|
def find_ursula(self, id, offer=None):
|
|
|
|
if offer:
|
2017-11-07 04:42:21 +00:00
|
|
|
try:
|
|
|
|
return next(self.ursulas), MockPolicyOfferResponse()
|
|
|
|
except StopIteration:
|
|
|
|
raise self.NotEnoughQualifiedUrsulas
|
2017-10-28 02:26:23 +00:00
|
|
|
else:
|
|
|
|
return super().find_ursula(id)
|
2017-10-03 15:43:12 +00:00
|
|
|
|
2017-10-18 08:52:01 +00:00
|
|
|
def animate_policy(self, ursula, payload):
|
|
|
|
return
|
|
|
|
|
2017-10-28 02:26:23 +00:00
|
|
|
|
2017-10-24 00:28:10 +00:00
|
|
|
def test_treasure_map_from_alice_to_ursula():
|
2017-10-04 04:13:46 +00:00
|
|
|
"""
|
|
|
|
Shows that Alice can share a TreasureMap with Ursula and that Bob can receive and decrypt it.
|
|
|
|
"""
|
2017-11-07 04:42:21 +00:00
|
|
|
# For example, a hashed path.
|
|
|
|
resource_id = b"as098duasdlkj213098asf"
|
|
|
|
policy_group = test_alice_has_ursulas_public_key_and_uses_it_to_encode_policy_payload()
|
|
|
|
|
|
|
|
# treasure_map = TreasureMap([api.secure_random(50) for _ in range(50)]) # TODO: This is still random here.
|
|
|
|
treasure_map = policy_group.treasure_map
|
2017-10-03 15:43:12 +00:00
|
|
|
|
2017-10-27 01:39:13 +00:00
|
|
|
encrypted_treasure_map, signature = ALICE.encrypt_for(BOB, treasure_map.packed_payload())
|
2017-10-28 17:53:56 +00:00
|
|
|
packed_encrypted_treasure_map = msgpack.dumps(encrypted_treasure_map)
|
2017-10-03 15:43:12 +00:00
|
|
|
|
2017-10-28 17:53:56 +00:00
|
|
|
setter = ALICE.server.set(policy_group.id, packed_encrypted_treasure_map)
|
2017-11-07 04:42:21 +00:00
|
|
|
_set_event = EVENT_LOOP.run_until_complete(setter)
|
2017-10-03 15:43:12 +00:00
|
|
|
|
2017-11-03 21:50:04 +00:00
|
|
|
treasure_map_as_set_on_network = URSULAS[0].server.storage[digest(policy_group.id)]
|
2017-10-28 17:53:56 +00:00
|
|
|
assert treasure_map_as_set_on_network == packed_encrypted_treasure_map # IE, Ursula stores it properly.
|
2017-10-28 02:26:23 +00:00
|
|
|
return treasure_map, treasure_map_as_set_on_network, signature, policy_group
|
2017-10-24 00:28:10 +00:00
|
|
|
|
|
|
|
|
2017-10-27 01:39:13 +00:00
|
|
|
def test_treasure_map_stored_by_ursula_is_the_correct_one_for_bob():
|
2017-10-28 02:26:23 +00:00
|
|
|
treasure_map, treasure_map_as_set_on_network, signature, _ = test_treasure_map_from_alice_to_ursula()
|
2017-10-28 17:53:56 +00:00
|
|
|
encrypted_treasure_map = msgpack.loads(treasure_map_as_set_on_network)
|
2017-10-27 01:39:13 +00:00
|
|
|
verified, treasure_map_as_decrypted_by_bob = BOB.verify_from(ALICE, signature,
|
2017-10-28 17:53:56 +00:00
|
|
|
encrypted_treasure_map,
|
2017-10-13 19:56:10 +00:00
|
|
|
decrypt=True,
|
|
|
|
signature_is_on_cleartext=True,
|
2017-10-13 21:06:33 +00:00
|
|
|
)
|
2017-10-04 04:13:46 +00:00
|
|
|
assert treasure_map_as_decrypted_by_bob == treasure_map.packed_payload()
|
2017-10-13 02:11:07 +00:00
|
|
|
assert verified is True
|
2017-10-03 15:43:12 +00:00
|
|
|
|
|
|
|
|
2017-10-24 00:28:10 +00:00
|
|
|
def test_treasure_map_from_ursula_to_bob():
|
|
|
|
"""
|
|
|
|
Bob finds Ursula and upgrades their connection to TLS to receive the TreasureMap.
|
|
|
|
"""
|
2017-10-28 02:26:23 +00:00
|
|
|
treasure_map, treasure_map_as_set_on_network, signature, policy_group = test_treasure_map_from_alice_to_ursula()
|
|
|
|
networky_stuff = MockNetworkyStuff()
|
|
|
|
|
|
|
|
# Of course, in the real world, Bob has sufficient information to reconstitute a PolicyGroup, gleaned, we presume, through a side-channel with Alice.
|
2017-10-28 04:32:32 +00:00
|
|
|
treasure_map_from_wire = BOB.get_treasure_map(policy_group, signature)
|
2017-10-27 02:46:34 +00:00
|
|
|
|
2017-10-28 04:32:32 +00:00
|
|
|
assert treasure_map == treasure_map_from_wire
|
2017-10-24 00:28:10 +00:00
|
|
|
|
|
|
|
|
2017-10-18 04:51:29 +00:00
|
|
|
def test_cannot_offer_policy_without_finding_ursula():
|
|
|
|
networky_stuff = MockNetworkyStuff()
|
2017-10-18 05:31:08 +00:00
|
|
|
policy = Policy(Alice())
|
2017-11-06 01:51:11 +00:00
|
|
|
|
2017-10-18 04:51:29 +00:00
|
|
|
with pytest.raises(Ursula.NotFound):
|
2017-10-18 08:52:01 +00:00
|
|
|
policy_offer = policy.encrypt_payload_for_ursula()
|
2017-10-18 04:51:29 +00:00
|
|
|
|
|
|
|
|
2017-09-16 00:45:32 +00:00
|
|
|
def test_alice_has_ursulas_public_key_and_uses_it_to_encode_policy_payload():
|
2017-09-15 22:03:02 +00:00
|
|
|
# For example, a hashed path.
|
|
|
|
resource_id = b"as098duasdlkj213098asf"
|
2017-09-14 08:42:15 +00:00
|
|
|
|
2017-09-30 17:32:39 +00:00
|
|
|
# Alice has a policy in mind; she crafts an offer.
|
2017-11-07 04:42:21 +00:00
|
|
|
n = NUMBER_OF_URSULAS_IN_NETWORK
|
2017-09-30 17:32:39 +00:00
|
|
|
deposit = NON_PAYMENT
|
|
|
|
contract_end_datetime = datetime.datetime.now() + datetime.timedelta(days=5)
|
|
|
|
offer = PolicyOffer(n, deposit, contract_end_datetime)
|
2017-09-29 23:34:42 +00:00
|
|
|
|
2017-09-30 17:32:39 +00:00
|
|
|
# Now, Alice needs to find N Ursulas to whom to make the offer.
|
|
|
|
networky_stuff = MockNetworkyStuff()
|
2017-10-27 01:39:13 +00:00
|
|
|
policy_manager = PolicyManagerForAlice(ALICE)
|
2017-09-30 17:32:39 +00:00
|
|
|
|
2017-09-22 02:40:57 +00:00
|
|
|
policy_group = policy_manager.create_policy_group(
|
2017-10-27 01:39:13 +00:00
|
|
|
BOB,
|
2017-09-28 00:07:36 +00:00
|
|
|
resource_id,
|
2017-11-07 04:42:21 +00:00
|
|
|
m=3,
|
|
|
|
n=n,
|
2017-09-28 00:07:36 +00:00
|
|
|
)
|
2017-09-19 21:30:35 +00:00
|
|
|
networky_stuff = MockNetworkyStuff()
|
2017-10-18 05:31:08 +00:00
|
|
|
policy_group.find_n_ursulas(networky_stuff, offer)
|
2017-10-18 08:52:01 +00:00
|
|
|
policy_group.transmit_payloads(networky_stuff) # Until we figure out encrypt_for logic
|
2017-09-28 00:07:36 +00:00
|
|
|
|
2017-11-07 04:42:21 +00:00
|
|
|
return policy_group
|
|
|
|
|
2017-09-28 00:07:36 +00:00
|
|
|
|
2017-10-09 21:03:16 +00:00
|
|
|
def test_trying_to_find_unknown_actor_raises_not_found():
|
2017-10-27 01:39:13 +00:00
|
|
|
tony_clifton = Character()
|
2017-10-09 21:03:16 +00:00
|
|
|
|
|
|
|
message = b"some_message"
|
2017-10-27 01:39:13 +00:00
|
|
|
signature = ALICE.seal(message)
|
2017-10-09 21:03:16 +00:00
|
|
|
|
2017-10-27 01:39:13 +00:00
|
|
|
# Tony can't reference Alice...
|
2017-10-09 21:03:16 +00:00
|
|
|
with pytest.raises(Character.NotFound):
|
2017-10-27 01:39:13 +00:00
|
|
|
verification = tony_clifton.verify_from(ALICE, signature, message)
|
2017-10-09 21:03:16 +00:00
|
|
|
|
|
|
|
# ...before learning about Alice.
|
2017-10-27 01:39:13 +00:00
|
|
|
tony_clifton.learn_about_actor(ALICE)
|
|
|
|
verification, NO_DECRYPTION_PERFORMED = tony_clifton.verify_from(ALICE, signature, message)
|
|
|
|
|
2017-10-28 02:26:23 +00:00
|
|
|
assert verification is True
|
2017-11-06 01:51:11 +00:00
|
|
|
|
|
|
|
|
2017-11-07 02:11:03 +00:00
|
|
|
def test_treaure_map_is_legit():
|
|
|
|
treasure_map, treasure_map_as_set_on_network, signature, policy_group = test_treasure_map_from_alice_to_ursula()
|
|
|
|
|
2017-11-07 04:42:21 +00:00
|
|
|
for ursula_interface_id in treasure_map:
|
|
|
|
getter = ALICE.server.get(ursula_interface_id)
|
2017-11-07 02:11:03 +00:00
|
|
|
loop = asyncio.get_event_loop()
|
|
|
|
value = loop.run_until_complete(getter)
|
|
|
|
signature, ursula_pubkey_sig, interface_info = msgpack.loads(value.lstrip(b"uaddr-"))
|
2017-11-07 04:42:21 +00:00
|
|
|
port, _interface = msgpack.loads(interface_info)
|
|
|
|
assert port in URSULA_PORTS
|
2017-11-06 01:51:11 +00:00
|
|
|
|