def request_felix_landing_page(_result): # Init an equal Felix to the already running one. felix_config = FelixConfiguration.from_configuration_file( filepath=configuration_file_location, registry_filepath=MOCK_REGISTRY_FILEPATH) felix_config.attach_keyring() felix_config.keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) felix = felix_config.produce() # Make a flask app web_app = felix.make_web_app() test_client = web_app.test_client() # Load the landing page response = test_client.get('/') assert response.status_code == 200 # Register a new recipient response = test_client.post( '/register', data={'address': testerchain.client.accounts[-1]}) assert response.status_code == 200 return
def _get_config(emitter, network, config_file, registry_filepath, eth_node, provider_uri, host, port, db_filepath, poa): # Domains -> bytes | or default domains = [network] if network else None # Load Felix from Configuration File with overrides try: felix_config = FelixConfiguration.from_configuration_file( filepath=config_file, domains=domains, registry_filepath=registry_filepath, provider_process=eth_node, provider_uri=provider_uri, rest_host=host, rest_port=port, db_filepath=db_filepath, poa=poa) return felix_config except FileNotFoundError: emitter.echo( f"No Felix configuration file found at {config_file}. " f"Check the filepath or run 'nucypher felix init' to create a new system configuration." ) raise click.Abort
def init( click_config, # Admin Options checksum_address, geth, dev, network, registry_filepath, provider_uri, host, db_filepath, poa, # Other config_root, discovery_port): """ Create a brand-new Felix. """ emitter = _setup_emitter(click_config, checksum_address) ETH_NODE = NO_BLOCKCHAIN_CONNECTION if geth: ETH_NODE = actions.get_provider_process(dev) provider_uri = ETH_NODE.provider_uri if not config_root: # Flag config_root = DEFAULT_CONFIG_ROOT # Envvar or init-only default try: new_felix_config = FelixConfiguration.generate( password=get_nucypher_password(confirm=True), config_root=config_root, rest_host=host, rest_port=discovery_port, db_filepath=db_filepath, domains={network} if network else None, checksum_address=checksum_address, registry_filepath=registry_filepath, provider_uri=provider_uri, provider_process=ETH_NODE, poa=poa) except Exception as e: if click_config.debug: raise else: emitter.echo(str(e), color='red', bold=True) raise click.Abort # Paint Help painting.paint_new_installation_help(emitter, new_configuration=new_felix_config)
def generate_config(self, config_root, discovery_port): return FelixConfiguration.generate( password=get_nucypher_password(confirm=True), config_root=config_root, rest_host=self.host, rest_port=discovery_port, db_filepath=self.db_filepath, domains=self.domains, checksum_address=self.checksum_address, registry_filepath=self.registry_filepath, provider_uri=self.provider_uri, provider_process=self.eth_node, poa=self.poa)
def generate_config(self, config_root, discovery_port): return FelixConfiguration.generate( password=get_nucypher_password(emitter=StdoutEmitter(), confirm=True), config_root=config_root, rest_host=self.host, rest_port=discovery_port, db_filepath=self.db_filepath, domain=self.domain, checksum_address=self.checksum_address, registry_filepath=self.registry_filepath, provider_uri=self.provider_uri, signer_uri=self.signer_uri, poa=self.poa)
def create_config(self, emitter, config_file): # Load Felix from Configuration File with overrides try: return FelixConfiguration.from_configuration_file( emitter=emitter, filepath=config_file, domains=self.domains, registry_filepath=self.registry_filepath, provider_process=self.eth_node, provider_uri=self.provider_uri, rest_host=self.host, rest_port=self.port, db_filepath=self.db_filepath, poa=self.poa) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=FelixConfiguration, config_file=config_file)
def create_config(self, emitter, config_file): # Load Felix from Configuration File with overrides if not config_file: config_file = select_config_file(emitter=emitter, checksum_address=self.checksum_address, config_class=FelixConfiguration) try: return FelixConfiguration.from_configuration_file( emitter=emitter, filepath=config_file, domain=self.domain, registry_filepath=self.registry_filepath, provider_uri=self.provider_uri, signer=self.signer_uri, rest_host=self.host, rest_port=self.port, db_filepath=self.db_filepath, poa=self.poa) except FileNotFoundError: return handle_missing_configuration_file( character_config_class=FelixConfiguration, config_file=config_file )
def test_run_felix(click_runner, testerchain, test_registry, agency, deploy_user_input): clock = Clock() Felix._CLOCK = clock Felix.DISTRIBUTION_INTERVAL = 5 # seconds Felix.DISBURSEMENT_INTERVAL = 0.01 # hours Felix.STAGING_DELAY = 2 # seconds # Main thread (Flask) os.environ['NUCYPHER_FELIX_DB_SECRET'] = INSECURE_DEVELOPMENT_PASSWORD # Test subproc (Click) envvars = { NUCYPHER_ENVVAR_KEYRING_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_FELIX_DB_SECRET': INSECURE_DEVELOPMENT_PASSWORD, 'FLASK_DEBUG': '1' } # Felix creates a system configuration init_args = ('felix', 'init', '--debug', '--registry-filepath', MOCK_REGISTRY_FILEPATH, '--checksum-address', testerchain.client.accounts[0], '--config-root', MOCK_CUSTOM_INSTALLATION_PATH_2, '--network', TEMPORARY_DOMAIN, '--provider', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 configuration_file_location = os.path.join( MOCK_CUSTOM_INSTALLATION_PATH_2, FelixConfiguration.generate_filename()) # Felix Creates a Database db_args = ('felix', 'createdb', '--debug', '--config-file', configuration_file_location, '--provider', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, db_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # Felix Runs Web Services def run_felix(): args = ('felix', 'run', '--debug', '--config-file', configuration_file_location, '--provider', TEST_PROVIDER_URI, '--dry-run') run_result = click_runner.invoke(nucypher_cli, args, catch_exceptions=False, env=envvars) assert run_result.exit_code == 0 return run_result # A (mocked) client requests Felix's services def request_felix_landing_page(_result): # Init an equal Felix to the already running one. felix_config = FelixConfiguration.from_configuration_file( filepath=configuration_file_location, registry_filepath=MOCK_REGISTRY_FILEPATH) felix_config.attach_keyring() felix_config.keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) felix = felix_config.produce() # Make a flask app web_app = felix.make_web_app() test_client = web_app.test_client() # Register a new recipient response = test_client.post( '/register', data={'address': testerchain.client.accounts[-1]}) assert response.status_code == 200 return def time_travel(_result): clock.advance(amount=60) # Record starting ether balance recipient = testerchain.client.accounts[-1] staker = Staker(checksum_address=recipient, registry=test_registry, is_me=True) original_eth_balance = staker.eth_balance # Run the callbacks d = threads.deferToThread(run_felix) d.addCallback(request_felix_landing_page) d.addCallback(time_travel) yield d def confirm_airdrop(_results): recipient = testerchain.client.accounts[-1] staker = Staker(checksum_address=recipient, registry=test_registry, is_me=True) assert staker.token_balance == NU(45000, 'NU') # TODO: Airdrop Testnet Ethers? new_eth_balance = original_eth_balance + testerchain.w3.fromWei( Felix.ETHER_AIRDROP_AMOUNT, 'ether') assert staker.eth_balance == new_eth_balance staged_airdrops = Felix._AIRDROP_QUEUE next_airdrop = staged_airdrops[0] next_airdrop.addCallback(confirm_airdrop) yield next_airdrop # Felix view view_args = ('felix', 'view', '--config-file', configuration_file_location, '--provider', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, view_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert "Address" in result.output assert "NU" in result.output assert "ETH" in result.output # Felix accounts accounts_args = ('felix', 'accounts', '--config-file', configuration_file_location, '--provider', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, accounts_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert testerchain.client.accounts[-1] in result.output # Felix destroy destroy_args = ('felix', 'destroy', '--config-file', configuration_file_location, '--provider', TEST_PROVIDER_URI, '--force') result = click_runner.invoke(nucypher_cli, destroy_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert SUCCESSFUL_DESTRUCTION in result.output assert not os.path.exists( configuration_file_location), "Felix configuration file was deleted"
def test_coexisting_configurations(click_runner, custom_filepath, testerchain, agency_local_registry): # # Setup # if custom_filepath.exists(): shutil.rmtree(str(custom_filepath), ignore_errors=True) assert not custom_filepath.exists() # Parse node addresses # TODO: Is testerchain & Full contract deployment needed here (causes massive slowdown)? alice, ursula, another_ursula, felix, staker, *all_yall = testerchain.unassigned_accounts envvars = {NUCYPHER_ENVVAR_KEYRING_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_FELIX_DB_SECRET': INSECURE_DEVELOPMENT_PASSWORD} # Future configuration filepaths for assertions... public_keys_dir = custom_filepath / 'keyring' / 'public' known_nodes_dir = custom_filepath / 'known_nodes' # ... Ensure they do not exist to begin with. # No keys have been generated... assert not public_keys_dir.exists() # No known nodes exist... assert not known_nodes_dir.exists() # Not the configuration root... assert not os.path.isdir(custom_filepath) # ... nothing None # # Create # # Expected config files felix_file_location = custom_filepath / FelixConfiguration.generate_filename() alice_file_location = custom_filepath / AliceConfiguration.generate_filename() ursula_file_location = custom_filepath / UrsulaConfiguration.generate_filename() another_ursula_configuration_file_location = custom_filepath / UrsulaConfiguration.generate_filename(modifier=another_ursula) # Felix creates a system configuration felix_init_args = ('felix', 'init', '--config-root', custom_filepath, '--network', TEMPORARY_DOMAIN, '--provider', TEST_PROVIDER_URI, '--checksum-address', felix, '--registry-filepath', agency_local_registry.filepath, '--debug') result = click_runner.invoke(nucypher_cli, felix_init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert os.path.isdir(custom_filepath) assert os.path.isfile(felix_file_location) assert os.path.isdir(public_keys_dir) assert len(os.listdir(public_keys_dir)) == 3 # Use a custom local filepath to init a persistent Alice alice_init_args = ('alice', 'init', '--network', TEMPORARY_DOMAIN, '--provider', TEST_PROVIDER_URI, '--pay-with', alice, '--registry-filepath', agency_local_registry.filepath, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, alice_init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert len(os.listdir(public_keys_dir)) == 5 # Use the same local filepath to init a persistent Ursula init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--provider', TEST_PROVIDER_URI, '--worker-address', ursula, '--rest-host', MOCK_IP_ADDRESS, '--registry-filepath', agency_local_registry.filepath, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert len(os.listdir(public_keys_dir)) == 8 assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert os.path.isfile(ursula_file_location) # Use the same local filepath to init another persistent Ursula init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--worker-address', another_ursula, '--rest-host', MOCK_IP_ADDRESS_2, '--registry-filepath', agency_local_registry.filepath, '--provider', TEST_PROVIDER_URI, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert os.path.isfile(another_ursula_configuration_file_location) assert os.path.isfile(ursula_file_location) assert len(os.listdir(public_keys_dir)) == 11 # # Run # # Run an Ursula amidst the other configuration files run_args = ('ursula', 'run', '--dry-run', '--config-file', another_ursula_configuration_file_location) user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' * 2 Worker.BONDING_POLL_RATE = 1 Worker.BONDING_TIMEOUT = 1 with pytest.raises(Teacher.UnbondedWorker): # TODO: Why is this being checked here? # Worker init success, but not bonded. result = click_runner.invoke(nucypher_cli, run_args, input=user_input, catch_exceptions=False) assert result.exit_code == 0 Worker.BONDING_TIMEOUT = None # All configuration files still exist. assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert os.path.isfile(another_ursula_configuration_file_location) assert os.path.isfile(ursula_file_location) assert len(os.listdir(public_keys_dir)) == 11 # Check that the proper Ursula console is attached assert another_ursula in result.output # # Destroy # another_ursula_destruction_args = ('ursula', 'destroy', '--force', '--config-file', another_ursula_configuration_file_location) result = click_runner.invoke(nucypher_cli, another_ursula_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 8 assert not os.path.isfile(another_ursula_configuration_file_location) ursula_destruction_args = ('ursula', 'destroy', '--config-file', ursula_file_location) result = click_runner.invoke(nucypher_cli, ursula_destruction_args, input='Y', catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert 'y/N' in result.output assert len(os.listdir(public_keys_dir)) == 5 assert not os.path.isfile(ursula_file_location) alice_destruction_args = ('alice', 'destroy', '--force', '--config-file', alice_file_location) result = click_runner.invoke(nucypher_cli, alice_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 3 assert not os.path.isfile(alice_file_location) felix_destruction_args = ('felix', 'destroy', '--force', '--config-file', felix_file_location) result = click_runner.invoke(nucypher_cli, felix_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 0 assert not os.path.isfile(felix_file_location)
def felix(click_config, action, teacher_uri, enode, min_stake, network, host, dry_run, port, discovery_port, provider_uri, geth, config_root, checksum_address, poa, config_file, db_filepath, no_registry, registry_filepath, dev, force): # Intro click.clear() if not click_config.quiet: click.secho(FELIX_BANNER.format(checksum_address or '')) ETH_NODE = NO_BLOCKCHAIN_CONNECTION if geth: ETH_NODE = actions.get_provider_process(dev=dev) provider_uri = ETH_NODE.provider_uri if action == "init": """Create a brand-new Felix""" if not config_root: # Flag config_root = DEFAULT_CONFIG_ROOT # Envvar or init-only default # Acquire Keyring Password new_password = click_config.get_password(confirm=True) try: new_felix_config = FelixConfiguration.generate( password=new_password, config_root=config_root, rest_host=host, rest_port=discovery_port, db_filepath=db_filepath, domains={network} if network else None, checksum_address=checksum_address, download_registry=not no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri, provider_process=ETH_NODE, poa=poa) except Exception as e: if click_config.debug: raise else: click.secho(str(e), fg='red', bold=True) raise click.Abort # Paint Help painting.paint_new_installation_help( new_configuration=new_felix_config, config_root=config_root, config_file=config_file) return # <-- do not remove (conditional flow control) # Domains -> bytes | or default domains = [network] if network else None # Load Felix from Configuration File with overrides try: felix_config = FelixConfiguration.from_configuration_file( filepath=config_file, domains=domains, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, rest_host=host, rest_port=port, db_filepath=db_filepath, poa=poa) except FileNotFoundError: click.secho( f"No Felix configuration file found at {config_file}. " f"Check the filepath or run 'nucypher felix init' to create a new system configuration." ) raise click.Abort try: # Connect to Blockchain felix_config.connect_to_blockchain() # Authenticate password = click_config.get_password(confirm=False) click_config.unlock_keyring(character_configuration=felix_config, password=password) # Produce Teacher Ursulas teacher_nodes = actions.load_seednodes( teacher_uris=[teacher_uri] if teacher_uri else None, min_stake=min_stake, federated_only=felix_config.federated_only, network_domains=felix_config.domains, network_middleware=click_config.middleware) # Add ETH Bootnode or Peer if enode: if geth: felix_config.blockchain.interface.w3.geth.admin.addPeer(enode) click.secho(f"Added ethereum peer {enode}") else: raise NotImplemented # TODO: other backends # Produce Felix FELIX = felix_config.produce(domains=network, known_nodes=teacher_nodes) FELIX.make_web_app() # attach web application, but dont start service except Exception as e: if click_config.debug: raise else: click.secho(str(e), fg='red', bold=True) raise click.Abort if action == "createdb": # Initialize Database if os.path.isfile(FELIX.db_filepath): if not force: click.confirm("Overwrite existing database?", abort=True) os.remove(FELIX.db_filepath) click.secho(f"Destroyed existing database {FELIX.db_filepath}") FELIX.create_tables() click.secho(f"\nCreated new database at {FELIX.db_filepath}", fg='green') elif action == 'view': token_balance = FELIX.token_balance eth_balance = FELIX.eth_balance click.secho(f""" Address .... {FELIX.checksum_address} NU ......... {str(token_balance)} ETH ........ {str(eth_balance)} """) elif action == "accounts": accounts = FELIX.blockchain.interface.w3.eth.accounts for account in accounts: click.secho(account) elif action == "destroy": """Delete all configuration files from the disk""" actions.destroy_configuration(character_config=felix_config, force=force) elif action == 'run': # Start web services try: click.secho("Waiting for blockchain sync...", fg='yellow') FELIX.blockchain.sync() FELIX.start(host=host, port=port, web_services=not dry_run, distribution=True, crash_on_error=click_config.debug) finally: FELIX.blockchain.disconnect() else: raise click.BadArgumentUsage("No such argument {}".format(action))
def felix(click_config, action, teacher_uri, enode, min_stake, network, host, dry_run, port, discovery_port, provider_uri, geth, config_root, checksum_address, poa, config_file, db_filepath, registry_filepath, dev, force): """ "Felix the Faucet" management commands. """ emitter = click_config.emitter # Intro emitter.clear() emitter.banner(FELIX_BANNER.format(checksum_address or '')) ETH_NODE = NO_BLOCKCHAIN_CONNECTION if geth: ETH_NODE = actions.get_provider_process(dev=dev) provider_uri = ETH_NODE.provider_uri if action == "init": """Create a brand-new Felix""" if not config_root: # Flag config_root = DEFAULT_CONFIG_ROOT # Envvar or init-only default try: new_felix_config = FelixConfiguration.generate( password=get_nucypher_password(confirm=True), config_root=config_root, rest_host=host, rest_port=discovery_port, db_filepath=db_filepath, domains={network} if network else None, checksum_address=checksum_address, registry_filepath=registry_filepath, provider_uri=provider_uri, provider_process=ETH_NODE, poa=poa) except Exception as e: if click_config.debug: raise else: emitter.echo(str(e), color='red', bold=True) raise click.Abort # Paint Help painting.paint_new_installation_help( emitter, new_configuration=new_felix_config) return # <-- do not remove (conditional flow control) # Domains -> bytes | or default domains = [network] if network else None # Load Felix from Configuration File with overrides try: felix_config = FelixConfiguration.from_configuration_file( filepath=config_file, domains=domains, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, rest_host=host, rest_port=port, db_filepath=db_filepath, poa=poa) except FileNotFoundError: emitter.echo( f"No Felix configuration file found at {config_file}. " f"Check the filepath or run 'nucypher felix init' to create a new system configuration." ) raise click.Abort if action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher felix destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) actions.destroy_configuration(emitter, character_config=felix_config, force=force) return try: # Authenticate unlock_nucypher_keyring(emitter, character_configuration=felix_config, password=get_nucypher_password(confirm=False)) # Produce Teacher Ursulas teacher_nodes = actions.load_seednodes( emitter, teacher_uris=[teacher_uri] if teacher_uri else None, min_stake=min_stake, federated_only=felix_config.federated_only, network_domains=felix_config.domains, network_middleware=click_config.middleware) # Produce Felix FELIX = felix_config.produce(domains=network, known_nodes=teacher_nodes) FELIX.make_web_app() # attach web application, but dont start service except Exception as e: if click_config.debug: raise else: emitter.echo(str(e), color='red', bold=True) raise click.Abort if action == "createdb": # Initialize Database if os.path.isfile(FELIX.db_filepath): if not force: click.confirm("Overwrite existing database?", abort=True) os.remove(FELIX.db_filepath) emitter.echo(f"Destroyed existing database {FELIX.db_filepath}") FELIX.create_tables() emitter.echo(f"\nCreated new database at {FELIX.db_filepath}", color='green') elif action == 'view': token_balance = FELIX.token_balance eth_balance = FELIX.eth_balance emitter.echo(f""" Address .... {FELIX.checksum_address} NU ......... {str(token_balance)} ETH ........ {str(eth_balance)} """) elif action == "accounts": accounts = FELIX.blockchain.client.accounts for account in accounts: emitter.echo(account) elif action == 'run': # Start web services emitter.echo("Waiting for blockchain sync...", color='yellow') emitter.message(f"Running Felix on {host}:{port}") FELIX.start(host=host, port=port, web_services=not dry_run, distribution=True, crash_on_error=click_config.debug) else: raise click.BadArgumentUsage("No such argument {}".format(action))
def test_run_felix(click_runner, testerchain, agency, deploy_user_input, mock_primary_registry_filepath): clock = Clock() Felix._CLOCK = clock Felix.DISTRIBUTION_INTERVAL = 5 # seconds Felix.DISBURSEMENT_INTERVAL = 0.01 # hours Felix.STAGING_DELAY = 2 # seconds # Main thread (Flask) os.environ['NUCYPHER_FELIX_DB_SECRET'] = INSECURE_DEVELOPMENT_PASSWORD # Simulate "Reconnection" real_attach_provider = BlockchainDeployerInterface._attach_provider cached_blockchain = BlockchainDeployerInterface.reconnect() cached_blockchain.registry.commit(filepath=mock_primary_registry_filepath) def attach_cached_provider(interface, *args, **kwargs): cached_provider = cached_blockchain.provider real_attach_provider(interface, provider=cached_provider) BlockchainDeployerInterface._attach_provider = attach_cached_provider # Mock live contract registry reads EthereumContractRegistry.read = lambda *a, **kw: cached_blockchain.registry.read( ) # Test subproc (Click) envvars = { 'NUCYPHER_KEYRING_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_FELIX_DB_SECRET': INSECURE_DEVELOPMENT_PASSWORD, 'FLASK_DEBUG': '1' } # Felix creates a system configuration init_args = ('--debug', 'felix', 'init', '--registry-filepath', mock_primary_registry_filepath, '--checksum-address', testerchain.client.accounts[0], '--config-root', MOCK_CUSTOM_INSTALLATION_PATH_2, '--network', TEMPORARY_DOMAIN, '--no-registry', '--provider-uri', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 configuration_file_location = os.path.join( MOCK_CUSTOM_INSTALLATION_PATH_2, FelixConfiguration.generate_filename()) # Felix Creates a Database db_args = ('--debug', 'felix', 'createdb', '--registry-filepath', mock_primary_registry_filepath, '--config-file', configuration_file_location, '--provider-uri', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, db_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # Felix Runs Web Services def run_felix(): args = ('--debug', 'felix', 'run', '--registry-filepath', mock_primary_registry_filepath, '--config-file', configuration_file_location, '--provider-uri', TEST_PROVIDER_URI, '--dry-run', '--no-registry') run_result = click_runner.invoke(nucypher_cli, args, catch_exceptions=False, env=envvars) assert run_result.exit_code == 0 return run_result # A (mocked) client requests Felix's services def request_felix_landing_page(_result): # Init an equal Felix to the already running one. felix_config = FelixConfiguration.from_configuration_file( filepath=configuration_file_location) felix_config.attach_keyring() felix_config.keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) felix = felix_config.produce() # Make a flask app web_app = felix.make_web_app() test_client = web_app.test_client() # Load the landing page response = test_client.get('/') assert response.status_code == 200 # Register a new recipient response = test_client.post( '/register', data={'address': felix.blockchain.client.accounts[-1]}) assert response.status_code == 200 return def time_travel(_result): clock.advance(amount=60) # Record starting ether balance recipient = testerchain.client.accounts[-1] staker = Staker(checksum_address=recipient, blockchain=testerchain, is_me=True) original_eth_balance = staker.eth_balance # Run the callbacks d = threads.deferToThread(run_felix) d.addCallback(request_felix_landing_page) d.addCallback(time_travel) yield d def confirm_airdrop(_results): recipient = testerchain.client.accounts[-1] staker = Staker(checksum_address=recipient, blockchain=testerchain, is_me=True) assert staker.token_balance == NU(15000, 'NU') # TODO: Airdrop Testnet Ethers? # new_eth_balance = original_eth_balance + testerchain.w3.fromWei(Felix.ETHER_AIRDROP_AMOUNT, 'ether') assert staker.eth_balance == original_eth_balance staged_airdrops = Felix._AIRDROP_QUEUE next_airdrop = staged_airdrops[0] next_airdrop.addCallback(confirm_airdrop) yield next_airdrop
def felix(click_config, action, teacher_uri, min_stake, network, host, dry_run, port, discovery_port, provider_uri, config_root, checksum_address, poa, config_file, db_filepath, no_registry, registry_filepath, force): if not click_config.quiet: click.secho(FELIX_BANNER.format(checksum_address or '')) if action == "init": """Create a brand-new Felix""" # Validate "Init" Input if not network: raise click.BadArgumentUsage( '--network is required to initialize a new configuration.') # Validate "Init" Input if not checksum_address: raise click.BadArgumentUsage( '--checksum-address is required to initialize a new Felix configuration.' ) # Acquire Keyring Password if not config_root: # Flag config_root = click_config.config_file # Envvar new_password = click_config.get_password(confirm=True) new_felix_config = FelixConfiguration.generate( password=new_password, config_root=config_root, rest_host=host, rest_port=discovery_port, db_filepath=db_filepath, domains={network} if network else None, checksum_public_address=checksum_address, no_registry=no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri, poa=poa) # Paint Help painting.paint_new_installation_help( new_configuration=new_felix_config, config_root=config_root, config_file=config_file) return # <-- do not remove (conditional flow control) # # Authenticated Configurations # # Domains -> bytes | or default domains = [bytes(network, encoding='utf-8')] if network else None # Load Felix from Configuration File with overrides try: felix_config = FelixConfiguration.from_configuration_file( filepath=config_file, domains=domains, registry_filepath=registry_filepath, provider_uri=provider_uri, rest_host=host, rest_port=port, db_filepath=db_filepath, poa=poa) except FileNotFoundError: click.secho( f"No Felix configuration file found at {config_file}. " f"Check the filepath or run 'nucypher felix init' to create a new system configuration." ) raise click.Abort else: # Produce Teacher Ursulas teacher_uris = [teacher_uri] if teacher_uri else list() teacher_nodes = actions.load_seednodes( teacher_uris=teacher_uris, min_stake=min_stake, federated_only=False, network_middleware=click_config.middleware) # Produce Felix click_config.unlock_keyring(character_configuration=felix_config) FELIX = felix_config.produce(domains=network, known_nodes=teacher_nodes) FELIX.make_web_app() # attach web application, but dont start service if action == "createdb": # Initialize Database if os.path.isfile(FELIX.db_filepath): if not force: click.confirm("Overwrite existing database?", abort=True) os.remove(FELIX.db_filepath) click.secho(f"Destroyed existing database {FELIX.db_filepath}") FELIX.create_tables() click.secho(f"Created new database at {FELIX.db_filepath}") elif action == 'view': token_balance = FELIX.token_balance eth_balance = FELIX.eth_balance click.secho(f""" Address .... {FELIX.checksum_public_address} NU ......... {str(token_balance)} ETH ........ {str(eth_balance)} """) return elif action == "accounts": accounts = FELIX.blockchain.interface.w3.eth.accounts for account in accounts: click.secho(account) return elif action == 'run': # Start web services FELIX.start(host=host, port=port, web_services=not dry_run, distribution=True, crash_on_error=click_config.debug) else: # Error raise click.BadArgumentUsage("No such argument {}".format(action))
def test_coexisting_configurations(click_runner, custom_filepath, mock_primary_registry_filepath, testerchain, deploy_user_input): # Parse node addresses deployer, alice, ursula, another_ursula, *all_yall = testerchain.client.accounts envvars = { 'NUCYPHER_KEYRING_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD, # Upgradeable Contracts 'NUCYPHER_STAKING_ESCROW_SECRET': INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_POLICY_MANAGER_SECRET': INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_ADJUDICATOR_SECRET': INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_USER_ESCROW_PROXY_SECRET': INSECURE_DEVELOPMENT_PASSWORD, # Auxiliary 'NUCYPHER_FELIX_DB_SECRET': INSECURE_DEVELOPMENT_PASSWORD } # Future configuration filepaths for assertions... public_keys_dir = os.path.join(custom_filepath, 'keyring', 'public') known_nodes_dir = os.path.join(custom_filepath, 'known_nodes') # ... Ensure they do not exist to begin with. assert not os.path.isdir(public_keys_dir) assert not os.path.isfile(known_nodes_dir) # Deploy contracts deploy_args = ('contracts', '--registry-outfile', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--config-root', custom_filepath, '--poa') result = click_runner.invoke(deploy.deploy, deploy_args, input=f'0\nY\nDEPLOY', catch_exceptions=False, env=envvars) assert result.exit_code == 0 # No keys have been generated... with pytest.raises(FileNotFoundError): assert len(os.listdir(public_keys_dir)) == 0 # No known nodes exist... with pytest.raises(FileNotFoundError): assert len(os.listdir(known_nodes_dir)) == 0 # Just the configuration root... assert os.path.isdir(custom_filepath) # and the fresh registry. assert os.path.isfile(mock_primary_registry_filepath) # # Create # # Expected config files felix_file_location = os.path.join(custom_filepath, FelixConfiguration.generate_filename()) alice_file_location = os.path.join(custom_filepath, AliceConfiguration.generate_filename()) ursula_file_location = os.path.join( custom_filepath, UrsulaConfiguration.generate_filename()) another_ursula_configuration_file_location = os.path.join( custom_filepath, UrsulaConfiguration.generate_filename(modifier=another_ursula)) # Felix creates a system configuration felix_init_args = ('felix', 'init', '--config-root', custom_filepath, '--network', TEMPORARY_DOMAIN, '--provider-uri', TEST_PROVIDER_URI, '--checksum-address', deployer, '--registry-filepath', mock_primary_registry_filepath) result = click_runner.invoke(nucypher_cli, felix_init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert os.path.isfile(felix_file_location) assert len(os.listdir(public_keys_dir)) == 3 # Use a custom local filepath to init a persistent Alice alice_init_args = ('alice', 'init', '--network', TEMPORARY_DOMAIN, '--provider-uri', TEST_PROVIDER_URI, '--pay-with', alice, '--registry-filepath', mock_primary_registry_filepath, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, alice_init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert os.path.isfile(alice_file_location) assert len(os.listdir(public_keys_dir)) == 5 # Use the same local filepath to init a persistent Ursula init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--provider-uri', TEST_PROVIDER_URI, '--checksum-address', ursula, '--rest-host', MOCK_IP_ADDRESS, '--registry-filepath', mock_primary_registry_filepath, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 8 assert os.path.isfile(ursula_file_location) # Use the same local filepath to init another persistent Ursula init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--checksum-address', another_ursula, '--rest-host', MOCK_IP_ADDRESS_2, '--registry-filepath', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert os.path.isfile(another_ursula_configuration_file_location) assert os.path.isfile(ursula_file_location) assert len(os.listdir(public_keys_dir)) == 11 # # Run # # Run an Ursula amidst the other configuration files run_args = ('ursula', 'run', '--dry-run', '--interactive', '--config-file', another_ursula_configuration_file_location) user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}' result = click_runner.invoke(nucypher_cli, run_args, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Check that the proper Ursula console is attached assert another_ursula in result.output # # Destroy # another_ursula_destruction_args = ( 'ursula', 'destroy', '--force', '--config-file', another_ursula_configuration_file_location) result = click_runner.invoke(nucypher_cli, another_ursula_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 8 assert not os.path.isfile(another_ursula_configuration_file_location) ursula_destruction_args = ('ursula', 'destroy', '--config-file', ursula_file_location) result = click_runner.invoke(nucypher_cli, ursula_destruction_args, input='Y', catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert 'y/N' in result.output assert len(os.listdir(public_keys_dir)) == 5 assert not os.path.isfile(ursula_file_location) felix_destruction_args = ('alice', 'destroy', '--force', '--config-file', alice_file_location) result = click_runner.invoke(nucypher_cli, felix_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 3 assert not os.path.isfile(alice_file_location) felix_destruction_args = ('felix', 'destroy', '--force', '--config-file', felix_file_location) result = click_runner.invoke(nucypher_cli, felix_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 0 assert not os.path.isfile(felix_file_location)
def test_coexisting_configurations(click_runner, custom_filepath, mock_primary_registry_filepath, testerchain, test_registry, agency): # # Setup # # Parse node addresses alice, ursula, another_ursula, felix, staker, *all_yall = testerchain.unassigned_accounts envvars = { 'NUCYPHER_KEYRING_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_FELIX_DB_SECRET': INSECURE_DEVELOPMENT_PASSWORD } # Future configuration filepaths for assertions... public_keys_dir = os.path.join(custom_filepath, 'keyring', 'public') known_nodes_dir = os.path.join(custom_filepath, 'known_nodes') # ... Ensure they do not exist to begin with. assert not os.path.isdir(public_keys_dir) assert not os.path.isfile(known_nodes_dir) # No keys have been generated... with pytest.raises(FileNotFoundError): assert len(os.listdir(public_keys_dir)) == 0 # No known nodes exist... with pytest.raises(FileNotFoundError): assert len(os.listdir(known_nodes_dir)) == 0 # Not the configuration root... assert not os.path.isdir(custom_filepath) # # Create # # Expected config files felix_file_location = os.path.join(custom_filepath, FelixConfiguration.generate_filename()) alice_file_location = os.path.join(custom_filepath, AliceConfiguration.generate_filename()) ursula_file_location = os.path.join( custom_filepath, UrsulaConfiguration.generate_filename()) another_ursula_configuration_file_location = os.path.join( custom_filepath, UrsulaConfiguration.generate_filename(modifier=another_ursula)) # Felix creates a system configuration felix_init_args = ('felix', 'init', '--config-root', custom_filepath, '--network', TEMPORARY_DOMAIN, '--provider', TEST_PROVIDER_URI, '--checksum-address', felix, '--registry-filepath', mock_primary_registry_filepath, '--debug') result = click_runner.invoke(nucypher_cli, felix_init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert os.path.isdir(custom_filepath) assert os.path.isfile(felix_file_location) assert os.path.isdir(public_keys_dir) assert len(os.listdir(public_keys_dir)) == 3 # Use a custom local filepath to init a persistent Alice alice_init_args = ('alice', 'init', '--network', TEMPORARY_DOMAIN, '--provider', TEST_PROVIDER_URI, '--pay-with', alice, '--registry-filepath', mock_primary_registry_filepath, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, alice_init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert len(os.listdir(public_keys_dir)) == 5 # Use the same local filepath to init a persistent Ursula init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--provider', TEST_PROVIDER_URI, '--worker-address', ursula, '--staker-address', staker, '--rest-host', MOCK_IP_ADDRESS, '--registry-filepath', mock_primary_registry_filepath, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert len(os.listdir(public_keys_dir)) == 8 assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert os.path.isfile(ursula_file_location) # Use the same local filepath to init another persistent Ursula init_args = ('ursula', 'init', '--network', TEMPORARY_DOMAIN, '--worker-address', another_ursula, '--staker-address', staker, '--rest-host', MOCK_IP_ADDRESS_2, '--registry-filepath', mock_primary_registry_filepath, '--provider', TEST_PROVIDER_URI, '--config-root', custom_filepath) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # All configuration files still exist. assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert os.path.isfile(another_ursula_configuration_file_location) assert os.path.isfile(ursula_file_location) assert len(os.listdir(public_keys_dir)) == 11 # # Run # # Run an Ursula amidst the other configuration files run_args = ('ursula', 'run', '--dry-run', '--interactive', '--config-file', another_ursula_configuration_file_location) user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n' * 2 with pytest.raises(Teacher.DetachedWorker): # Worker init success, but unassigned. result = click_runner.invoke(nucypher_cli, run_args, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # All configuration files still exist. assert os.path.isfile(felix_file_location) assert os.path.isfile(alice_file_location) assert os.path.isfile(another_ursula_configuration_file_location) assert os.path.isfile(ursula_file_location) assert len(os.listdir(public_keys_dir)) == 11 # Check that the proper Ursula console is attached assert another_ursula in result.output # # Destroy # another_ursula_destruction_args = ( 'ursula', 'destroy', '--force', '--config-file', another_ursula_configuration_file_location) result = click_runner.invoke(nucypher_cli, another_ursula_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 8 assert not os.path.isfile(another_ursula_configuration_file_location) ursula_destruction_args = ('ursula', 'destroy', '--config-file', ursula_file_location) result = click_runner.invoke(nucypher_cli, ursula_destruction_args, input='Y', catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert 'y/N' in result.output assert len(os.listdir(public_keys_dir)) == 5 assert not os.path.isfile(ursula_file_location) alice_destruction_args = ('alice', 'destroy', '--force', '--config-file', alice_file_location) result = click_runner.invoke(nucypher_cli, alice_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 3 assert not os.path.isfile(alice_file_location) felix_destruction_args = ('felix', 'destroy', '--force', '--config-file', felix_file_location) result = click_runner.invoke(nucypher_cli, felix_destruction_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 assert len(os.listdir(public_keys_dir)) == 0 assert not os.path.isfile(felix_file_location)