diff --git a/nucypher/utilities/blockchain.py b/nucypher/utilities/blockchain.py index 91f4d564d..1c698b1e8 100644 --- a/nucypher/utilities/blockchain.py +++ b/nucypher/utilities/blockchain.py @@ -4,16 +4,22 @@ from typing import List from typing import Set from constant_sorrow import constants +from eth_utils import to_checksum_address from umbral.keys import UmbralPrivateKey from web3 import Web3 from nucypher.characters import Ursula +from nucypher.crypto.api import secure_random _TEST_KNOWN_URSULAS_CACHE = {} -def make_ursulas(ether_addresses: list, ursula_starting_port: int, - miner_agent=None, miners=False, bare=False) -> Set[Ursula]: +def make_ursulas(ether_addresses: list, + miner_agent=None, + miners=False, + bare=False, + know_each_other=True, + **ursula_kwargs) -> Set[Ursula]: """ :param ether_addresses: Ethereum addresses to create ursulas with. :param ursula_starting_port: The port of the first created Ursula; subsequent Ursulas will increment the port number by 1. @@ -27,10 +33,18 @@ def make_ursulas(ether_addresses: list, ursula_starting_port: int, :return: A list of created Ursulas """ + if isinstance(ether_addresses, int): + ether_addresses = [to_checksum_address(secure_random(20)) for _ in range(ether_addresses)] + + event_loop = asyncio.get_event_loop() + if not _TEST_KNOWN_URSULAS_CACHE: + starting_port = constants.URSULA_PORT_SEED + else: + starting_port = max(_TEST_KNOWN_URSULAS_CACHE.keys()) + 1 ursulas = set() - for port, ether_address in enumerate(ether_addresses, start=ursula_starting_port): + for port, ether_address in enumerate(ether_addresses, start=starting_port): if bare: ursula = Ursula(is_me=False, # do not attach dht server @@ -39,7 +53,8 @@ def make_ursulas(ether_addresses: list, ursula_starting_port: int, checksum_address=ether_address, always_be_learning=False, miner_agent=miner_agent, - abort_on_learning_error=True) + abort_on_learning_error=True, + **ursula_kwargs) ursula.is_me = True # Patch to allow execution of transacting methods in tests @@ -56,7 +71,8 @@ def make_ursulas(ether_addresses: list, ursula_starting_port: int, rest_port=port+100, always_be_learning=False, miner_agent=miner_agent, - federated_only=federated_only) + federated_only=federated_only, + **ursula_kwargs) ursula.attach_rest_server() @@ -67,32 +83,35 @@ def make_ursulas(ether_addresses: list, ursula_starting_port: int, ursula.datastore_threadpool = MockDatastoreThreadPool() ursula.dht_listen() - for ursula_to_teach in ursulas: - # Add other Ursulas as known nodes. - for ursula_to_learn_about in ursulas: - ursula_to_teach.remember_node(ursula_to_learn_about) - - event_loop.run_until_complete( - ursula.dht_server.bootstrap( - [("127.0.0.1", ursula_starting_port + _c) for _c in range(len(ursulas))])) - ursula.publish_dht_information() - if miners is True: + # TODO: 309 # stake a random amount - min_stake, balance = int(constants.MIN_ALLOWED_LOCKED), ursula.token_balance + min_stake, balance = constants.MIN_ALLOWED_LOCKED, ursula.token_balance amount = random.randint(min_stake, balance) # for a random lock duration - min_locktime, max_locktime = int(constants.MIN_LOCKED_PERIODS), int(constants.MAX_MINTING_PERIODS) + min_locktime, max_locktime = constants.MIN_LOCKED_PERIODS, constants.MAX_MINTING_PERIODS periods = random.randint(min_locktime, max_locktime) - ursula.initialize_stake(amount=amount, lock_periods=periods) + ursula.stake(amount=amount, lock_periods=periods) else: ursula.federated_only = True ursulas.add(ursula) _TEST_KNOWN_URSULAS_CACHE[ursula.rest_interface.port] = ursula + if know_each_other and not bare: + + for ursula_to_teach in ursulas: + # Add other Ursulas as known nodes. + for ursula_to_learn_about in ursulas: + ursula_to_teach.remember_node(ursula_to_learn_about) + + event_loop.run_until_complete( + ursula.dht_server.bootstrap( + [("127.0.0.1", starting_port + _c) for _c in range(len(ursulas))])) + ursula.publish_dht_information() + return ursulas diff --git a/nucypher/utilities/network.py b/nucypher/utilities/network.py index dee189db1..ae635b575 100644 --- a/nucypher/utilities/network.py +++ b/nucypher/utilities/network.py @@ -46,17 +46,17 @@ class MockRestMiddleware(RestMiddleware): assert response.status_code == 200 return response - def enact_policy(self, ursula, hrac, payload): + def enact_policy(self, ursula, id, payload): mock_client = self.__get_mock_client_by_port(ursula.rest_interface.port) - response = mock_client.post('http://localhost/kFrag/{}'.format(hrac.hex()), payload) + response = mock_client.post('http://localhost/kFrag/{}'.format(id.hex()), payload) assert response.status_code == 200 return True, ursula.stamp.as_umbral_pubkey() def send_work_order_payload_to_ursula(self, work_order): mock_client = self.__get_mock_client_by_port(work_order.ursula.rest_interface.port) payload = work_order.payload() - hrac_as_hex = work_order.kfrag_hrac.hex() - return mock_client.post('http://localhost/kFrag/{}/reencrypt'.format(hrac_as_hex), payload) + id_as_hex = work_order.arrangement_id.hex() + return mock_client.post('http://localhost/kFrag/{}/reencrypt'.format(id_as_hex), payload) def get_treasure_map_from_node(self, node, map_id): mock_client = self.__get_mock_client_by_port(node.rest_interface.port) @@ -67,15 +67,22 @@ class MockRestMiddleware(RestMiddleware): response = mock_client.get("http://localhost/public_information") return response - def get_nodes_via_rest(self, address, port, node_ids): + def get_nodes_via_rest(self, address, port, announce_nodes=None, nodes_i_need=None): mock_client = self.__get_mock_client_by_port(port) - # TODO: Better passage of node IDs here. - # if node_ids: - # node_address_bytestring = bytes().join(bytes(id) for id in node_ids) - # params = {'nodes': node_address_bytestring} - # else: - # params = None - response = mock_client.get("http://localhost/list_nodes") + + 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 via the DHT or whatever. + pass + + if announce_nodes: + response = mock_client.post("https://{}:{}/node_metadata".format(address, port), + verify=False, + data=bytes().join(bytes(n) for n in announce_nodes)) # TODO: TLS-only. + else: + response = mock_client.get("https://{}:{}/node_metadata".format(address, port), + verify=False) # TODO: TLS-only. return response def put_treasure_map_on_node(self, node, map_id, map_payload): diff --git a/tests/characters/test_ursula_prepares_to_act_as_mining_node.py b/tests/characters/test_ursula_prepares_to_act_as_mining_node.py index 186e72b86..54d83b34a 100644 --- a/tests/characters/test_ursula_prepares_to_act_as_mining_node.py +++ b/tests/characters/test_ursula_prepares_to_act_as_mining_node.py @@ -1,10 +1,10 @@ import pytest -from constant_sorrow import constants from eth_keys.datatypes import Signature as EthSignature from nucypher.characters import Ursula from nucypher.crypto.powers import SigningPower, CryptoPower from nucypher.utilities.blockchain import make_ursulas +from nucypher.utilities.network import MockRestMiddleware @pytest.mark.skip diff --git a/tests/fixtures.py b/tests/fixtures.py index 1800ee58a..88d4ef9f4 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -169,7 +169,7 @@ def ursulas(three_agents): ether_addresses = [to_checksum_address(os.urandom(20)) for _ in range(constants.NUMBER_OF_URSULAS_IN_NETWORK)] _ursulas = make_ursulas(ether_addresses=ether_addresses, miner_agent=miner_agent, - ursula_starting_port=int(constants.URSULA_PORT_SEED)) + ) try: yield _ursulas finally: @@ -188,8 +188,7 @@ def mining_ursulas(three_agents): amount=1000000 * constants.M) ursula_addresses = all_yall[:int(constants.NUMBER_OF_URSULAS_IN_NETWORK)] - _ursulas = make_ursulas(ursula_starting_port=starting_point, - ether_addresses=ursula_addresses, + _ursulas = make_ursulas(ether_addresses=ursula_addresses, miner_agent=miner_agent, miners=True) try: