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}
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
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
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
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
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
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)
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