def test_learner_restores_metadata_from_storage(lonely_ursula_maker, tmpdir):
    # Create a local file-based node storage
    root = tmpdir.mkdir("known_nodes")
    metadata = root.mkdir("metadata")
    certs = root.mkdir("certs")
    old_storage = LocalFileBasedNodeStorage(federated_only=True,
                                            metadata_dir=metadata,
                                            certificates_dir=certs,
                                            storage_root=root)

    # Use the ursula maker with this storage so it's populated with nodes from one domain
    _some_ursulas = lonely_ursula_maker(domain="fistro",
                                        node_storage=old_storage,
                                        know_each_other=True,
                                        quantity=3,
                                        save_metadata=True)

    # Create a pair of new learners in a different domain, using the previous storage, and learn from it
    new_learners = lonely_ursula_maker(domain="duodenal",
                                       node_storage=old_storage,
                                       quantity=2,
                                       know_each_other=True,
                                       save_metadata=False)
    learner, buddy = new_learners
    buddy._Learner__known_nodes = FleetSensor(domain="fistro")

    # The learner shouldn't learn about any node from the first domain, since it's different.
    learner.learn_from_teacher_node()
    for restored_node in learner.known_nodes:
        assert restored_node.mature().domain == learner.domain

    # In fact, since the storage only contains nodes from a different domain,
    # the learner should only know its buddy from the second domain.
    assert set(learner.known_nodes) == {buddy}
Exemple #2
0
def test_learner_uses_both_nodes_from_storage_and_fallback_nodes(
        lonely_ursula_maker, tmpdir, mocker):
    domain = "learner-domain"
    mocker.patch.dict(TEACHER_NODES, {domain: ("teacher-uri", )}, clear=True)

    # Create a local file-based node storage
    root = tmpdir.mkdir("known_nodes")
    metadata = root.mkdir("metadata")
    certs = root.mkdir("certs")
    node_storage = LocalFileBasedNodeStorage(federated_only=True,
                                             metadata_dir=metadata,
                                             certificates_dir=certs,
                                             storage_root=root)

    # Create some nodes and persist them to local storage
    other_nodes = lonely_ursula_maker(domain=domain,
                                      node_storage=node_storage,
                                      know_each_other=True,
                                      quantity=3,
                                      save_metadata=True)

    # Create a teacher and a learner using existing node storage
    learner, teacher = lonely_ursula_maker(domain=domain,
                                           node_storage=node_storage,
                                           quantity=2)
    mocker.patch.object(Ursula, 'from_teacher_uri', return_value=teacher)

    # The learner should learn about all nodes
    learner.learn_from_teacher_node()
    all_nodes = {teacher}
    all_nodes.update(other_nodes)
    assert set(learner.known_nodes) == all_nodes
Exemple #3
0
 def __setup_node_storage(self, node_storage=None) -> None:
     if self.dev_mode:
         node_storage = ForgetfulNodeStorage(registry=self.registry, federated_only=self.federated_only)
     elif not node_storage:
         node_storage = LocalFileBasedNodeStorage(registry=self.registry,
                                                  config_root=self.config_root,
                                                  federated_only=self.federated_only)
     self.node_storage = node_storage
Exemple #4
0
 def __setup_node_storage(self, node_storage=None) -> None:
     if self.dev_mode:
         node_storage = ForgetfulNodeStorage(
             blockchain=self.blockchain, federated_only=self.federated_only)
     elif not node_storage:
         node_storage = LocalFileBasedNodeStorage(
             blockchain=self.blockchain,
             federated_only=self.federated_only,
             config_root=self.config_root)
     self.node_storage = node_storage
Exemple #5
0
def get_external_ip_from_default_teacher(
        network: str,
        federated_only: bool = False,
        registry: Optional[BaseContractRegistry] = None,
        log: Logger = IP_DETECTION_LOGGER) -> Union[str, None]:

    # Prevents circular imports
    from nucypher.characters.lawful import Ursula
    from nucypher.network.nodes import TEACHER_NODES

    if federated_only and registry:
        raise ValueError(
            'Federated mode must not be true if registry is provided.')

    base_error = 'Cannot determine IP using default teacher'

    if network not in TEACHER_NODES:
        log.debug(f'{base_error}: Unknown network "{network}".')
        return

    ####
    # TODO: Clean this mess #1481 (Federated Mode)
    node_storage = LocalFileBasedNodeStorage(federated_only=federated_only)
    Ursula.set_cert_storage_function(node_storage.store_node_certificate)
    Ursula.set_federated_mode(federated_only)
    #####

    external_ip = None
    for teacher_uri in TEACHER_NODES[network]:
        try:
            teacher = Ursula.from_teacher_uri(
                teacher_uri=teacher_uri,
                federated_only=federated_only,
                min_stake=0)  # TODO: Handle customized min stake here.
            # TODO: Pass registry here to verify stake (not essential here since it's a hardcoded node)
            external_ip = _request_from_node(teacher=teacher)
            # Found a reachable teacher, return from loop
            if external_ip:
                break
        except NodeSeemsToBeDown:
            # Teacher is unreachable, try next one
            continue

    if not external_ip:
        log.debug(
            f'{base_error}: No teacher available for network "{network}".')
        return

    return external_ip
Exemple #6
0
    def _setup_node_storage(self, node_storage=None) -> None:
        # TODO: Disables node metadata persistence..
        # if self.dev_mode:
        #     node_storage = ForgetfulNodeStorage(registry=self.registry, federated_only=self.federated_only)

        # TODO: Forcibly clears the filesystem of any stored node metadata and certificates...
        local_node_storage = LocalFileBasedNodeStorage(
            registry=self.registry,
            config_root=self.config_root,
            federated_only=self.federated_only)
        local_node_storage.clear()
        self.log.info(
            f'Cleared peer metadata from {local_node_storage.root_dir}')

        # TODO: Always sets up nodes for in-memory node metadata storage
        node_storage = ForgetfulNodeStorage(registry=self.registry,
                                            federated_only=self.federated_only)
        self.node_storage = node_storage
Exemple #7
0
    def __init__(
            self,

            # Base
            config_root: str = None,
            config_file_location: str = None,

            # Mode
            dev_mode: bool = False,
            federated_only: bool = False,

            # Identity
            is_me: bool = True,
            checksum_public_address: str = None,
            crypto_power: CryptoPower = None,

            # Keyring
            keyring: NucypherKeyring = None,
            keyring_dir: str = None,

            # Learner
            learn_on_same_thread: bool = False,
            abort_on_learning_error: bool = False,
            start_learning_now: bool = True,

            # REST
            rest_host: str = None,
            rest_port: int = None,

            # TLS
            tls_curve: EllipticCurve = None,
            certificate: Certificate = None,

            # Network
            domains: Set[str] = None,
            interface_signature: Signature = None,
            network_middleware: RestMiddleware = None,

            # Node Storage
            known_nodes: set = None,
            node_storage: NodeStorage = None,
            reload_metadata: bool = True,
            save_metadata: bool = True,

            # Blockchain
            poa: bool = False,
            provider_uri: str = None,

            # Registry
            registry_source: str = None,
            registry_filepath: str = None,
            import_seed_registry: bool = False  # TODO: needs cleanup
    ) -> None:

        # Logs
        self.log = Logger(self.__class__.__name__)

        #
        # REST + TLS (Ursula)
        #
        self.rest_host = rest_host or self.DEFAULT_REST_HOST
        default_port = (self.DEFAULT_DEVELOPMENT_REST_PORT
                        if dev_mode else self.DEFAULT_REST_PORT)
        self.rest_port = rest_port or default_port
        self.tls_curve = tls_curve or self.__DEFAULT_TLS_CURVE
        self.certificate = certificate

        self.interface_signature = interface_signature
        self.crypto_power = crypto_power

        #
        # Keyring
        #
        self.keyring = keyring or NO_KEYRING_ATTACHED
        self.keyring_dir = keyring_dir or UNINITIALIZED_CONFIGURATION

        # Contract Registry
        if import_seed_registry is True:
            registry_source = self.REGISTRY_SOURCE
            if not os.path.isfile(registry_source):
                message = "Seed contract registry does not exist at path {}.".format(
                    registry_filepath)
                self.log.debug(message)
                raise RuntimeError(message)
        self.__registry_source = registry_source or self.REGISTRY_SOURCE
        self.registry_filepath = registry_filepath or UNINITIALIZED_CONFIGURATION

        #
        # Configuration
        #
        self.config_file_location = config_file_location or UNINITIALIZED_CONFIGURATION
        self.config_root = UNINITIALIZED_CONFIGURATION

        #
        # Mode
        #
        self.federated_only = federated_only
        self.__dev_mode = dev_mode

        if self.__dev_mode:
            self.__temp_dir = UNINITIALIZED_CONFIGURATION
            self.node_storage = ForgetfulNodeStorage(
                federated_only=federated_only, character_class=self.__class__)
        else:
            self.__temp_dir = LIVE_CONFIGURATION
            self.config_root = config_root or DEFAULT_CONFIG_ROOT
            self._cache_runtime_filepaths()
            self.node_storage = node_storage or LocalFileBasedNodeStorage(
                federated_only=federated_only, config_root=self.config_root)

        # Domains
        self.domains = domains or {self.DEFAULT_DOMAIN}

        #
        # Identity
        #
        self.is_me = is_me
        self.checksum_public_address = checksum_public_address

        if self.is_me is True or dev_mode is True:
            # Self
            if self.checksum_public_address and dev_mode is False:
                self.attach_keyring()
            self.network_middleware = network_middleware or self.__DEFAULT_NETWORK_MIDDLEWARE_CLASS(
            )

        else:
            # Stranger
            self.node_storage = STRANGER_CONFIGURATION
            self.keyring_dir = STRANGER_CONFIGURATION
            self.keyring = STRANGER_CONFIGURATION
            self.network_middleware = STRANGER_CONFIGURATION
            if network_middleware:
                raise self.ConfigurationError(
                    "Cannot configure a stranger to use network middleware.")

        #
        # Learner
        #
        self.learn_on_same_thread = learn_on_same_thread
        self.abort_on_learning_error = abort_on_learning_error
        self.start_learning_now = start_learning_now
        self.save_metadata = save_metadata
        self.reload_metadata = reload_metadata

        self.__fleet_state = FleetStateTracker()
        known_nodes = known_nodes or set()
        if known_nodes:
            self.known_nodes._nodes.update(
                {node.checksum_public_address: node
                 for node in known_nodes})
            self.known_nodes.record_fleet_state(
            )  # TODO: Does this call need to be here?

        #
        # Blockchain
        #
        self.poa = poa
        self.provider_uri = provider_uri or self.DEFAULT_PROVIDER_URI

        self.blockchain = NO_BLOCKCHAIN_CONNECTION
        self.accounts = NO_BLOCKCHAIN_CONNECTION
        self.token_agent = NO_BLOCKCHAIN_CONNECTION
        self.miner_agent = NO_BLOCKCHAIN_CONNECTION
        self.policy_agent = NO_BLOCKCHAIN_CONNECTION

        #
        # Development Mode
        #
        if dev_mode:

            # Ephemeral dev settings
            self.abort_on_learning_error = True
            self.save_metadata = False
            self.reload_metadata = False

            # Generate one-time alphanumeric development password
            alphabet = string.ascii_letters + string.digits
            password = ''.join(secrets.choice(alphabet) for _ in range(32))

            # Auto-initialize
            self.initialize(password=password,
                            import_registry=import_seed_registry)
Exemple #8
0
def test_alices_powers_are_persistent(federated_ursulas, tmpdir):

    passphrase = TEST_URSULA_INSECURE_DEVELOPMENT_PASSWORD

    # Let's create an Alice from a Configuration.
    # This requires creating a local storage for her first.
    node_storage = LocalFileBasedNodeStorage(
        federated_only=True,
        character_class=Ursula, # Alice needs to store some info about Ursula
        known_metadata_dir=os.path.join(tmpdir, "known_metadata"),
    )

    alice_config = AliceConfiguration(
        config_root=os.path.join(tmpdir, "config_root"),
        node_storage=node_storage,
        auto_initialize=True,
        auto_generate_keys=True,
        passphrase=passphrase,
        is_me=True,
        network_middleware=MockRestMiddleware(),
        known_nodes=federated_ursulas,
        start_learning_now=False,
        federated_only=True,
        save_metadata=False,
        load_metadata=False
    )
    alice = alice_config(passphrase=passphrase)

    # We will save Alice's config to a file for later use
    alice_config_file = alice_config.to_configuration_file()

    # Let's save Alice's public keys too to check they are correctly restored later
    alices_verifying_key = alice.public_keys(SigningPower)
    alices_receiving_key = alice.public_keys(EncryptingPower)

    # Next, let's fix a label for all the policies we will create later.
    label = b"this_is_the_path_to_which_access_is_being_granted"

    # Even before creating the policies, we can know what will be its public key.
    # This can be used by DataSources to encrypt messages before Alice grants access to Bobs
    policy_pubkey = alice.get_policy_pubkey_from_label(label)

    # Now, let's create a policy for some Bob.
    m, n = 3, 4
    policy_end_datetime = maya.now() + datetime.timedelta(days=5)

    bob = Bob(federated_only=True,
              start_learning_now=False,
              network_middleware=MockRestMiddleware(),
              )

    bob_policy = alice.grant(bob, label, m=m, n=n, expiration=policy_end_datetime)

    assert policy_pubkey == bob_policy.public_key

    # ... and Alice and her configuration disappear.
    del alice
    del alice_config

    ###################################
    #        Some time passes.        #
    #               ...               #
    # (jmyles plays the Song of Time) #
    #               ...               #
    #       Alice appears again.      #
    ###################################

    # A new Alice is restored from the configuration file
    new_alice_config = AliceConfiguration.from_configuration_file(
        filepath=alice_config_file,
        network_middleware=MockRestMiddleware(),
        known_nodes=federated_ursulas,
        start_learning_now=False,
    )

    new_alice = new_alice_config(passphrase=passphrase)

    # First, we check that her public keys are correctly restored
    assert alices_verifying_key == new_alice.public_keys(SigningPower)
    assert alices_receiving_key == new_alice.public_keys(EncryptingPower)

    # Bob's eldest brother, Roberto, appears too
    roberto = Bob(federated_only=True,
                  start_learning_now=False,
                  network_middleware=MockRestMiddleware(),
                  )

    # Alice creates a new policy for Roberto. Note how all the parameters
    # except for the label (i.e., recipient, m, n, policy_end) are different
    # from previous policy
    m, n = 2, 5
    policy_end_datetime = maya.now() + datetime.timedelta(days=3)
    roberto_policy = new_alice.grant(roberto, label, m=m, n=n, expiration=policy_end_datetime)

    # Both policies must share the same public key (i.e., the policy public key)
    assert policy_pubkey == roberto_policy.public_key