def destroy(click_config, # Admin Options geth, provider_uri, network, registry_filepath, staker_address, worker_address, federated_only, rest_host, rest_port, db_filepath, poa, light, # Other config_file, force, dev): """ Delete Ursula node configuration. """ ### Setup ### _validate_args(geth, federated_only, staker_address, registry_filepath) emitter = _setup_emitter(click_config, worker_address) _pre_launch_warnings(emitter, dev=dev, force=force) ursula_config, provider_uri = _get_ursula_config(emitter, geth, provider_uri, network, registry_filepath, dev, config_file, staker_address, worker_address, federated_only, rest_host, rest_port, db_filepath, poa, light) ############# actions.destroy_configuration(emitter, character_config=ursula_config, force=force)
def destroy( click_config, # Admin Options checksum_address, geth, dev, network, registry_filepath, provider_uri, host, db_filepath, poa, # Other config_file, port, force): """ Destroy Felix Configuration. """ 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 felix_config = _get_config(emitter, network, config_file, registry_filepath, ETH_NODE, provider_uri, host, port, db_filepath, poa) actions.destroy_configuration(emitter, character_config=felix_config, force=force)
def destroy(general_config, config_options, config_file, force): """ Delete Ursula node configuration. """ emitter = _setup_emitter(general_config, config_options.worker_address) _pre_launch_warnings(emitter, dev=config_options.dev, force=force) ursula_config = config_options.create_config(emitter, config_file) actions.destroy_configuration(emitter, character_config=ursula_config, force=force)
def destroy(general_config, config_options, config_file, force): """ Destroy Felix Configuration. """ emitter = _setup_emitter(general_config, config_options.checksum_address) felix_config = config_options.create_config(emitter, config_file) actions.destroy_configuration(emitter, character_config=felix_config, force=force)
def destroy( click_config, # Admin Options geth, provider_uri, federated_only, dev, pay_with, network, registry_filepath, # Other config_file, discovery_port, force): """ Delete existing Alice's configuration. """ ### Setup ### emitter = _setup_emitter(click_config) alice_config, provider_uri = _get_alice_config( click_config, config_file, dev, discovery_port, federated_only, geth, network, pay_with, provider_uri, registry_filepath) ############# if dev: message = "'nucypher alice destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) return actions.destroy_configuration(emitter, character_config=alice_config, force=force)
def destroy( click_config, # Admin Options provider_uri, network, registry_filepath, checksum_address, # Other dev, config_file, discovery_port, force): """ Delete existing Bob's configuration. """ ### Setup ### emitter = _setup_emitter(click_config) bob_config = _get_bob_config(click_config, dev, provider_uri, network, registry_filepath, checksum_address, config_file, discovery_port) ############# # Validate if dev: message = "'nucypher bob destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) # Request return actions.destroy_configuration(emitter, character_config=bob_config, force=force)
def destroy(general_config, config_options, config_file, force): """ Delete existing Alice's configuration. """ emitter = _setup_emitter(general_config) alice_config = config_options.create_config(emitter, config_file) return actions.destroy_configuration(emitter, character_config=alice_config, force=force)
def destroy(general_config, config_options, config_file, force): """ Delete existing Bob's configuration. """ emitter = _setup_emitter(general_config) # Validate if config_options.dev: message = "'nucypher bob destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) bob_config = config_options.create_config(emitter, config_file) # Request return actions.destroy_configuration(emitter, character_config=bob_config, force=force)
def ursula( click_config, action, dev, dry_run, force, lonely, network, teacher_uri, min_stake, rest_host, rest_port, db_filepath, staker_address, worker_address, federated_only, poa, config_root, config_file, provider_uri, geth, registry_filepath, interactive, sync, ) -> None: """ "Ursula the Untrusted" PRE Re-encryption node management commands. \b Actions ------------------------------------------------- \b init Create a new Ursula node configuration. view View the Ursula node's configuration. run Run an "Ursula" node. save-metadata Manually write node metadata to disk without running forget Forget all known nodes. destroy Delete Ursula node configuration. confirm-activity Manually confirm-activity for the current period. """ emitter = click_config.emitter # # Validate # if federated_only: if geth: raise click.BadOptionUsage( option_name="--geth", message="Federated only cannot be used with the --geth flag") if staker_address: raise click.BadOptionUsage( option_name='--federated-only', message="Staking address cannot be used in federated mode.") # Banner emitter.banner(URSULA_BANNER.format(worker_address or '')) # # Pre-Launch Warnings # if dev: emitter.echo("WARNING: Running in Development mode", color='yellow', verbosity=1) if force: emitter.echo("WARNING: Force is enabled", color='yellow', verbosity=1) # # Internal Ethereum Client # ETH_NODE = NO_BLOCKCHAIN_CONNECTION if geth: ETH_NODE = actions.get_provider_process() provider_uri = ETH_NODE.provider_uri(scheme='file') # # Eager Actions # if action == "init": """Create a brand-new persistent Ursula""" if dev: raise click.BadArgumentUsage( "Cannot create a persistent development character") if (not staker_address or not worker_address) and not federated_only: # Connect to Blockchain fetch_registry = registry_filepath is None and not click_config.no_registry registry = None if registry_filepath: registry = EthereumContractRegistry( registry_filepath=registry_filepath) blockchain = BlockchainInterface(provider_uri=provider_uri, registry=registry, poa=poa) blockchain.connect(fetch_registry=fetch_registry, sync_now=sync, emitter=emitter) if not staker_address: prompt = "Select staker account" staker_address = select_client_account(emitter=emitter, blockchain=blockchain, prompt=prompt) if not worker_address: prompt = "Select worker account" worker_address = select_client_account(emitter=emitter, blockchain=blockchain, prompt=prompt) if not config_root: # Flag config_root = click_config.config_file # Envvar if not rest_host: rest_host = actions.determine_external_ip_address(emitter, force=force) download_registry = not federated_only and not click_config.no_registry ursula_config = UrsulaConfiguration.generate( password=get_nucypher_password(confirm=True), config_root=config_root, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, domains={network} if network else None, federated_only=federated_only, checksum_address=staker_address, worker_address=worker_address, download_registry=download_registry, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, poa=poa) painting.paint_new_installation_help(emitter, new_configuration=ursula_config) return # # Make Ursula # if dev: ursula_config = UrsulaConfiguration( dev_mode=True, domains={TEMPORARY_DOMAIN}, poa=poa, download_registry=False, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, checksum_address=staker_address, worker_address=worker_address, federated_only=federated_only, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath) else: try: ursula_config = UrsulaConfiguration.from_configuration_file( filepath=config_file, domains={network} if network else None, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, poa=poa, federated_only=federated_only) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=UrsulaConfiguration, config_file=config_file) except NucypherKeyring.AuthenticationFailed as e: emitter.echo(str(e), color='red', bold=True) click.get_current_context().exit(1) # TODO: Exit codes (not only for this, but for other exceptions) # # Configured Pre-Authentication Actions # # Handle destruction and forget *before* network bootstrap and character initialization below if action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher ursula destroy' cannot be used in --dev mode - There is nothing to destroy." raise click.BadOptionUsage(option_name='--dev', message=message) actions.destroy_configuration(emitter, character_config=ursula_config, force=force) return elif action == "forget": actions.forget(emitter, configuration=ursula_config) return # # Make Ursula # client_password = None if not ursula_config.federated_only: if not dev and not click_config.json_ipc: client_password = get_client_password( checksum_address=ursula_config.worker_address, envvar="NUCYPHER_WORKER_ETH_PASSWORD") try: URSULA = actions.make_cli_character(character_config=ursula_config, click_config=click_config, min_stake=min_stake, teacher_uri=teacher_uri, dev=dev, lonely=lonely, client_password=client_password) except NucypherKeyring.AuthenticationFailed as e: emitter.echo(str(e), color='red', bold=True) click.get_current_context().exit(1) # TODO: Exit codes (not only for this, but for other exceptions) # # Authenticated Action Switch # if action == 'run': """Seed, Produce, Run!""" # GO! try: # Ursula Deploy Warnings emitter.message(f"Starting Ursula on {URSULA.rest_interface}", color='green', bold=True) emitter.message(f"Connecting to {','.join(ursula_config.domains)}", color='green', bold=True) emitter.message("Working ~ Keep Ursula Online!", color='blue', bold=True) if interactive: stdio.StandardIO( UrsulaCommandProtocol(ursula=URSULA, emitter=emitter)) if dry_run: return # <-- ABORT - (Last Chance) # Run - Step 3 node_deployer = URSULA.get_deployer() node_deployer.addServices() node_deployer.catalogServers(node_deployer.hendrix) node_deployer.run() # <--- Blocking Call (Reactor) # Handle Crash except Exception as e: ursula_config.log.critical(str(e)) emitter.message(f"{e.__class__.__name__} {e}", color='red', bold=True) raise # Crash :-( # Graceful Exit finally: emitter.message("Stopping Ursula", color='green') ursula_config.cleanup() emitter.message("Ursula Stopped", color='red') return elif action == "save-metadata": """Manually save a node self-metadata file""" metadata_path = ursula.write_node_metadata(node=URSULA) emitter.message( f"Successfully saved node metadata to {metadata_path}.", color='green') return elif action == "view": """Paint an existing configuration to the console""" if not URSULA.federated_only: emitter.echo("BLOCKCHAIN ----------\n") painting.paint_contract_status(emitter=emitter, blockchain=URSULA.blockchain) current_block = URSULA.blockchain.w3.eth.blockNumber emitter.echo(f'Block # {current_block}') # TODO: 1231 emitter.echo(f'NU Balance (staker): {URSULA.token_balance}') emitter.echo( f'ETH Balance (worker): {URSULA.blockchain.client.get_balance(URSULA.worker_address)}' ) emitter.echo( f'Current Gas Price {URSULA.blockchain.client.gas_price}') emitter.echo("CONFIGURATION --------") response = UrsulaConfiguration._read_configuration_file( filepath=config_file or ursula_config.config_file_location) return emitter.ipc( response=response, request_id=0, duration=0 ) # FIXME: #1216 - what are request_id and duration here? elif action == 'confirm-activity': receipt = URSULA.confirm_activity() confirmed_period = URSULA.staking_agent.get_current_period() + 1 date = datetime_at_period(period=confirmed_period) # TODO: Double-check dates here emitter.echo( f'\nActivity confirmed for period #{confirmed_period} ' f'(starting at {date})', bold=True, color='blue') painting.paint_receipt_summary( emitter=emitter, receipt=receipt, chain_name=URSULA.blockchain.client.chain_name) # TODO: Check ActivityConfirmation event (see #1193) return else: raise click.BadArgumentUsage("No such argument {}".format(action))
def bob(click_config, action, teacher_uri, min_stake, controller_port, discovery_port, federated_only, network, config_root, config_file, checksum_address, provider_uri, registry_filepath, dev, force, poa, dry_run, label, policy_encrypting_key, alice_verifying_key, message_kit, sync): """ "Bob" management commands. \b Actions ------------------------------------------------- \b init Create a brand new persistent Bob view View existing Bob's configuration. run Start Bob's controller. destroy Delete existing Bob's configuration. public-keys Obtain Bob's public verification and encryption keys. retrieve Obtain plaintext from encrypted data, if access was granted. """ # # Validate # # Banner emitter = click_config.emitter emitter.clear() emitter.banner(BOB_BANNER) # # Eager Actions # if action == 'init': """Create a brand-new persistent Bob""" if dev: raise click.BadArgumentUsage( "Cannot create a persistent development character") if not config_root: # Flag config_root = click_config.config_file # Envvar if not checksum_address and not federated_only: registry = None if registry_filepath: registry = EthereumContractRegistry( registry_filepath=registry_filepath) blockchain = BlockchainInterface(provider_uri=provider_uri, registry=registry, poa=poa) blockchain.connect(sync_now=sync, emitter=emitter) checksum_address = select_client_account(emitter=emitter, blockchain=blockchain) download_registry = not federated_only and not click_config.no_registry new_bob_config = BobConfiguration.generate( password=get_nucypher_password(confirm=True), config_root=config_root or DEFAULT_CONFIG_ROOT, checksum_address=checksum_address, domains={network} if network else None, federated_only=federated_only, download_registry=download_registry, registry_filepath=registry_filepath, provider_uri=provider_uri) return painting.paint_new_installation_help( emitter, new_configuration=new_bob_config) # # Make Bob # if dev: bob_config = BobConfiguration( dev_mode=True, domains={network}, provider_uri=provider_uri, federated_only=True, checksum_address=checksum_address, network_middleware=click_config.middleware) else: try: bob_config = BobConfiguration.from_configuration_file( filepath=config_file, domains={network} if network else None, checksum_address=checksum_address, rest_port=discovery_port, provider_uri=provider_uri, network_middleware=click_config.middleware) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=BobConfiguration, config_file=config_file) BOB = actions.make_cli_character(character_config=bob_config, click_config=click_config, dev=dev, teacher_uri=teacher_uri, min_stake=min_stake) # # Admin Action # if action == "run": # RPC if click_config.json_ipc: rpc_controller = BOB.make_rpc_controller() _transport = rpc_controller.make_control_transport() rpc_controller.start() return # Echo Public Keys emitter.message(f"Bob Verifying Key {bytes(BOB.stamp).hex()}", color='green', bold=True) bob_encrypting_key = bytes(BOB.public_keys(DecryptingPower)).hex() emitter.message(f"Bob Encrypting Key {bob_encrypting_key}", color="blue", bold=True) # Start Controller controller = BOB.make_web_controller(crash_on_error=click_config.debug) BOB.log.info('Starting HTTP Character Web Controller') return controller.start(http_port=controller_port, dry_run=dry_run) elif action == "view": """Paint an existing configuration to the console""" response = BobConfiguration._read_configuration_file( filepath=config_file or bob_config.config_file_location) return BOB.controller.emitter.ipc( response, request_id=0, duration=0 ) # FIXME: #1216 - what are request_id and duration here? elif action == "destroy": """Delete Bob's character configuration files from the disk""" # Validate if dev: message = "'nucypher bob destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) # Request return actions.destroy_configuration(emitter, character_config=bob_config) # # Bob API Actions # elif action == "public-keys": response = BOB.controller.public_keys() return response elif action == "retrieve": # Validate if not all( (label, policy_encrypting_key, alice_verifying_key, message_kit)): input_specification, output_specification = BOB.control.get_specifications( interface_name='retrieve') required_fields = ', '.join(input_specification) raise click.BadArgumentUsage( f'{required_fields} are required flags to retrieve') # Request bob_request_data = { 'label': label, 'policy_encrypting_key': policy_encrypting_key, 'alice_verifying_key': alice_verifying_key, 'message_kit': message_kit, } response = BOB.controller.retrieve(request=bob_request_data) return response else: raise click.BadArgumentUsage(f"No such argument {action}")
def alice( click_config, action, # Mode dev, force, dry_run, # Network teacher_uri, min_stake, federated_only, network, discovery_port, controller_port, # Filesystem config_root, config_file, # Blockchain pay_with, provider_uri, geth, sync, poa, no_registry, registry_filepath, # Alice bob_encrypting_key, bob_verifying_key, label, m, n, value, rate, duration, expiration, message_kit): # # Validate # if federated_only and geth: raise click.BadOptionUsage( option_name="--geth", message="Federated only cannot be used with the --geth flag") # Banner click.clear() if not click_config.json_ipc and not click_config.quiet: click.secho(ALICE_BANNER) # # Managed Ethereum Client # ETH_NODE = NO_BLOCKCHAIN_CONNECTION if geth: ETH_NODE = actions.get_provider_process() provider_uri = ETH_NODE.provider_uri(scheme='file') # # Eager Actions (No Authentication Required) # if action == 'init': """Create a brand-new persistent Alice""" if dev: raise click.BadArgumentUsage( "Cannot create a persistent development character") if not config_root: # Flag config_root = click_config.config_file # Envvar new_alice_config = AliceConfiguration.generate( password=click_config.get_password(confirm=True), config_root=config_root, checksum_address=pay_with, rest_host="localhost", domains={network} if network else None, federated_only=federated_only, download_registry=no_registry, registry_filepath=registry_filepath, provider_process=ETH_NODE, poa=poa, provider_uri=provider_uri, m=m, n=n, duration=duration, rate=rate) painting.paint_new_installation_help( new_configuration=new_alice_config, config_root=config_root, config_file=config_file) return # Exit elif action == "view": """Paint an existing configuration to the console""" configuration_file_location = config_file or AliceConfiguration.DEFAULT_CONFIG_FILE_LOCATION response = AliceConfiguration._read_configuration_file( filepath=configuration_file_location) click_config.emit(response) return # Exit # # Make Alice # if dev: alice_config = AliceConfiguration( dev_mode=True, network_middleware=click_config.middleware, domains={network}, provider_process=ETH_NODE, provider_uri=provider_uri, federated_only=True) else: try: alice_config = AliceConfiguration.from_configuration_file( filepath=config_file, domains={network} if network else None, network_middleware=click_config.middleware, rest_port=discovery_port, checksum_address=pay_with, provider_process=ETH_NODE, provider_uri=provider_uri) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=AliceConfiguration, config_file=config_file) ALICE = actions.make_cli_character(character_config=alice_config, click_config=click_config, dev=dev, teacher_uri=teacher_uri, min_stake=min_stake, sync=sync) # # Admin Actions # if action == "run": """Start Alice Web Controller""" ALICE.controller.emitter( message=f"Alice Verifying Key {bytes(ALICE.stamp).hex()}", color="green", bold=True) controller = ALICE.make_web_controller( crash_on_error=click_config.debug) ALICE.log.info('Starting Alice Web Controller') return controller.start(http_port=controller_port or alice_config.controller_port, dry_run=dry_run) elif action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher alice destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) return actions.destroy_configuration(character_config=alice_config, force=force) # # Alice API # elif action == "public-keys": response = ALICE.controller.public_keys() return response elif action == "derive-policy-pubkey": # Validate if not label: raise click.BadOptionUsage( option_name='label', message= "--label is required for deriving a policy encrypting key.") # Request return ALICE.controller.derive_policy_encrypting_key(label=label) elif action == "grant": # Validate if not all((bob_verifying_key, bob_encrypting_key, label)): raise click.BadArgumentUsage( message= "--bob-verifying-key, --bob-encrypting-key, and --label are " "required options to grant (optionally --m, --n, and --expiration)." ) # Request grant_request = { 'bob_encrypting_key': bob_encrypting_key, 'bob_verifying_key': bob_verifying_key, 'label': label, 'm': m, 'n': n, 'expiration': expiration, } if not ALICE.federated_only: grant_request.update({'value': value}) return ALICE.controller.grant(request=grant_request) elif action == "revoke": # Validate if not label and bob_verifying_key: raise click.BadArgumentUsage( message= f"--label and --bob-verifying-key are required options for revoke." ) # Request revoke_request = { 'label': label, 'bob_verifying_key': bob_verifying_key } return ALICE.controller.revoke(request=revoke_request) elif action == "decrypt": # Validate if not all((label, message_kit)): input_specification, output_specification = ALICE.controller.get_specifications( interface_name=action) required_fields = ', '.join(input_specification) raise click.BadArgumentUsage( f'{required_fields} are required flags to decrypt') # Request request_data = {'label': label, 'message_kit': message_kit} response = ALICE.controller.decrypt(request=request_data) return response else: raise click.BadArgumentUsage(f"No such argument {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, 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 bob(click_config, action, quiet, teacher_uri, min_stake, http_port, discovery_port, federated_only, network, config_root, config_file, provider_uri, registry_filepath, dev, force, dry_run, label, policy_encrypting_key, alice_verifying_key, message_kit): """ Start and manage a "Bob" character. """ if not click_config.json_ipc and not click_config.quiet: click.secho(BOB_BANNER) if action == 'init': """Create a brand-new persistent Bob""" if dev: click_config.emit(message="WARNING: Using temporary storage area", color='yellow') if not config_root: # Flag config_root = click_config.config_file # Envvar new_bob_config = BobConfiguration.generate( password=click_config.get_password(confirm=True), config_root=config_root or click_config, rest_host="localhost", domains={network} if network else None, federated_only=federated_only, no_registry=click_config.no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri) return painting.paint_new_installation_help( new_configuration=new_bob_config, config_file=config_file) # # Get Bob Configuration # if dev: bob_config = BobConfiguration( dev_mode=True, domains={network}, provider_uri=provider_uri, federated_only=True, network_middleware=click_config.middleware) else: try: bob_config = BobConfiguration.from_configuration_file( filepath=config_file, domains={network or GLOBAL_DOMAIN}, rest_port=discovery_port, provider_uri=provider_uri, network_middleware=click_config.middleware) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=BobConfiguration, config_file=config_file) # Teacher Ursula teacher_uris = [teacher_uri] if teacher_uri else list() teacher_nodes = actions.load_seednodes( teacher_uris=teacher_uris, min_stake=min_stake, federated_only=federated_only, network_middleware=click_config.middleware) if not dev: click_config.unlock_keyring(character_configuration=bob_config) # Produce BOB = bob_config(known_nodes=teacher_nodes, network_middleware=click_config.middleware) # Switch to character control emitter if click_config.json_ipc: BOB.controller.emitter = IPCStdoutEmitter(quiet=click_config.quiet) if action == "run": click_config.emitter( message=f"Bob Verifying Key {bytes(BOB.stamp).hex()}", color='green', bold=True) bob_encrypting_key = bytes(BOB.public_keys(DecryptingPower)).hex() click_config.emitter( message=f"Bob Encrypting Key {bob_encrypting_key}", color="blue", bold=True) controller = BOB.make_web_controller() BOB.log.info('Starting HTTP Character Web Controller') return controller.start(http_port=http_port, dry_run=dry_run) elif action == "view": """Paint an existing configuration to the console""" response = BobConfiguration._read_configuration_file( filepath=config_file or bob_config.config_file_location) return BOB.controller.emitter(response=response) elif action == "public-keys": response = BOB.controller.public_keys() return response elif action == "retrieve": if not all( (label, policy_encrypting_key, alice_verifying_key, message_kit)): input_specification, output_specification = BOB.control.get_specifications( interface_name='retrieve') required_fields = ', '.join(input_specification) raise click.BadArgumentUsage( f'{required_fields} are required flags to retrieve') bob_request_data = { 'label': label, 'policy_encrypting_key': policy_encrypting_key, 'alice_verifying_key': alice_verifying_key, 'message_kit': message_kit, } response = BOB.controller.retrieve(request=bob_request_data) return response elif action == "destroy": """Delete Bob's character configuration files from the disk""" if dev: message = "'nucypher ursula destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) return actions.destroy_configuration(character_config=bob_config) else: raise click.BadArgumentUsage(f"No such argument {action}")
def bob(click_config, action, quiet, teacher_uri, min_stake, http_port, discovery_port, federated_only, network, config_root, config_file, pay_with, provider_uri, registry_filepath, dev, force, dry_run, label, policy_encrypting_key, alice_verifying_key, message_kit): """ Start and manage a "Bob" character. """ # # Validate # # Banner click.clear() if not click_config.json_ipc and not click_config.quiet: click.secho(BOB_BANNER) # # Eager Actions # if action == 'init': """Create a brand-new persistent Bob""" if dev: raise click.BadArgumentUsage("Cannot create a persistent development character") if not config_root: # Flag config_root = click_config.config_file # Envvar new_bob_config = BobConfiguration.generate(password=get_password(confirm=True), config_root=config_root or DEFAULT_CONFIG_ROOT, checksum_address=pay_with, domains={network} if network else None, federated_only=federated_only, download_registry=click_config.no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri) return painting.paint_new_installation_help(new_configuration=new_bob_config) # TODO # elif action == "view": # """Paint an existing configuration to the console""" # response = BobConfiguration._read_configuration_file(filepath=config_file or bob_config.config_file_location) # return BOB.controller.emitter(response=response) # # Make Bob # if dev: bob_config = BobConfiguration(dev_mode=True, domains={network}, provider_uri=provider_uri, federated_only=True, checksum_address=pay_with, network_middleware=click_config.middleware) else: try: bob_config = BobConfiguration.from_configuration_file( filepath=config_file, domains={network} if network else None, checksum_address=pay_with, rest_port=discovery_port, provider_uri=provider_uri, network_middleware=click_config.middleware) except FileNotFoundError: return actions.handle_missing_configuration_file(character_config_class=BobConfiguration, config_file=config_file) BOB = actions.make_cli_character(character_config=bob_config, click_config=click_config, dev=dev, teacher_uri=teacher_uri, min_stake=min_stake) # # Admin Action # if action == "run": # Echo Public Keys click_config.emit(message=f"Bob Verifying Key {bytes(BOB.stamp).hex()}", color='green', bold=True) # RPC if click_config.json_ipc: rpc_controller = BOB.make_rpc_controller() _transport = rpc_controller.make_control_transport() rpc_controller.start() return click_config.emit(message=f"Bob Verifying Key {bytes(BOB.stamp).hex()}", color='green', bold=True) bob_encrypting_key = bytes(BOB.public_keys(DecryptingPower)).hex() click_config.emit(message=f"Bob Encrypting Key {bob_encrypting_key}", color="blue", bold=True) # Start Controller controller = BOB.make_control_transport() BOB.log.info('Starting HTTP Character Web Controller') return controller.start(http_port=http_port, dry_run=dry_run) elif action == "destroy": """Delete Bob's character configuration files from the disk""" # Validate if dev: message = "'nucypher bob destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) # Request return actions.destroy_configuration(character_config=bob_config) # # Bob API Actions # elif action == "public-keys": response = BOB.controller.public_keys() return response elif action == "retrieve": # Validate if not all((label, policy_encrypting_key, alice_verifying_key, message_kit)): input_specification, output_specification = BOB.control.get_specifications(interface_name='retrieve') required_fields = ', '.join(input_specification) raise click.BadArgumentUsage(f'{required_fields} are required flags to retrieve') # Request bob_request_data = { 'label': label, 'policy_encrypting_key': policy_encrypting_key, 'alice_verifying_key': alice_verifying_key, 'message_kit': message_kit, } response = BOB.controller.retrieve(request=bob_request_data) return response else: raise click.BadArgumentUsage(f"No such argument {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 alice(click_config, action, checksum_address, teacher_uri, min_stake, http_port, discovery_port, federated_only, network, config_root, config_file, provider_uri, no_registry, registry_filepath, dev, force, dry_run, bob_encrypting_key, bob_verifying_key, label, m, n): """ Start and manage an "Alice" character. """ if not click_config.json_ipc and not click_config.quiet: click.secho(ALICE_BANNER) if action == 'init': """Create a brand-new persistent Alice""" if not network: raise click.BadArgumentUsage( '--network is required to initialize a new configuration.') if dev: click_config.emitter( message="WARNING: Using temporary storage area", color='yellow') if not config_root: # Flag config_root = click_config.config_file # Envvar new_alice_config = AliceConfiguration.generate( password=click_config.get_password(confirm=True), config_root=config_root, checksum_public_address=checksum_address, rest_host="localhost", domains={network} if network else None, federated_only=federated_only, no_registry=no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri) return painting.paint_new_installation_help( new_configuration=new_alice_config, config_root=config_root, config_file=config_file) # # Get Alice Configuration # if dev: alice_config = AliceConfiguration( dev_mode=True, network_middleware=click_config.middleware, domains={network}, provider_uri=provider_uri, federated_only=True) else: try: alice_config = AliceConfiguration.from_configuration_file( filepath=config_file, domains={network or GLOBAL_DOMAIN}, network_middleware=click_config.middleware, rest_port=discovery_port, checksum_public_address=checksum_address, provider_uri=provider_uri) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=AliceConfiguration, config_file=config_file) if not dev: click_config.unlock_keyring(character_configuration=alice_config) # Teacher Ursula teacher_uris = [teacher_uri] if teacher_uri else list() teacher_nodes = actions.load_seednodes( teacher_uris=teacher_uris, min_stake=min_stake, federated_only=federated_only, network_middleware=click_config.middleware) # Produce ALICE = alice_config(known_nodes=teacher_nodes, network_middleware=click_config.middleware) # Switch to character control emitter if click_config.json_ipc: ALICE.controller.emitter = IPCStdoutEmitter(quiet=click_config.quiet) if action == "run": """Start Alice Web Controller""" ALICE.controller.emitter( message=f"Alice Verifying Key {bytes(ALICE.stamp).hex()}", color="green", bold=True) controller = ALICE.make_web_controller( crash_on_error=click_config.debug) ALICE.log.info('Starting HTTP Character Web Controller') return controller.start(http_port=http_port, dry_run=dry_run) elif action == "view": """Paint an existing configuration to the console""" configuration_file_location = config_file or alice_config.config_file_location response = AliceConfiguration._read_configuration_file( filepath=configuration_file_location) return ALICE.controller.emitter( response=response) # TODO: Uses character control instead elif action == "public-keys": response = ALICE.controller.public_keys() return response elif action == "create-policy": if not all((bob_verifying_key, bob_encrypting_key, label)): raise click.BadArgumentUsage( message= "--bob-verifying-key, --bob-encrypting-key, and --label are " "required options to create a new policy.") create_policy_request = { 'bob_encrypting_key': bob_encrypting_key, 'bob_verifying_key': bob_verifying_key, 'label': label, 'm': m, 'n': n, } return ALICE.controller.create_policy(request=create_policy_request) elif action == "derive-policy-pubkey": return ALICE.controller.derive_policy_encrypting_key(label=label) elif action == "grant": grant_request = { 'bob_encrypting_key': bob_encrypting_key, 'bob_verifying_key': bob_verifying_key, 'label': label, 'm': m, 'n': n, 'expiration': (maya.now() + datetime.timedelta(days=3)).iso8601(), # TODO } return ALICE.controller.grant(request=grant_request) elif action == "revoke": revoke_request = { 'label': label, 'bob_verifying_key': bob_verifying_key } return ALICE.controller.revoke(request=revoke_request) elif action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher ursula destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) return actions.destroy_configuration(character_config=alice_config, force=force) else: raise click.BadArgumentUsage(f"No such argument {action}")
def ursula( click_config, action, dev, quiet, dry_run, force, lonely, network, teacher_uri, min_stake, rest_host, rest_port, db_filepath, checksum_address, withdraw_address, federated_only, poa, config_root, config_file, provider_uri, geth, no_registry, registry_filepath, value, duration, index, list_, divide, sync, device, interactive, ) -> None: """ Manage and run an "Ursula" PRE node. \b Actions ------------------------------------------------- \b init Create a new Ursula node configuration. view View the Ursula node's configuration. run Run an "Ursula" node. save-metadata Manually write node metadata to disk without running forget Forget all known nodes. destroy Delete Ursula node configuration. stake Manage stakes for this node. confirm-activity Manually confirm-activity for the current period. collect-reward Withdraw staking reward. """ # # Validate # if federated_only and geth: raise click.BadOptionUsage( option_name="--geth", message="Federated only cannot be used with the --geth flag") if click_config.debug and quiet: raise click.BadOptionUsage( option_name="quiet", message="--debug and --quiet cannot be used at the same time.") # Banner if not click_config.json_ipc and not click_config.quiet: click.secho(URSULA_BANNER.format(checksum_address or '')) # # Pre-Launch Warnings # if not click_config.quiet: if dev: click.secho("WARNING: Running in Development mode", fg='yellow') if force: click.secho("WARNING: Force is enabled", fg='yellow') # # Internal Ethereum Client # ETH_NODE = NO_BLOCKCHAIN_CONNECTION if geth: ETH_NODE = actions.get_provider_process() provider_uri = ETH_NODE.provider_uri(scheme='file') # # Eager Actions # if action == "init": """Create a brand-new persistent Ursula""" if dev: raise click.BadArgumentUsage( "Cannot create a persistent development character") if not config_root: # Flag config_root = click_config.config_file # Envvar if not rest_host: rest_host = actions.determine_external_ip_address(force=force) ursula_config = UrsulaConfiguration.generate( password=get_password(confirm=True), config_root=config_root, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, domains={network} if network else None, federated_only=federated_only, checksum_address=checksum_address, download_registry=federated_only or no_registry, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, poa=poa) painting.paint_new_installation_help(new_configuration=ursula_config) return # # Make Ursula # if dev: ursula_config = UrsulaConfiguration( dev_mode=True, domains={TEMPORARY_DOMAIN}, poa=poa, download_registry=False, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, checksum_address=checksum_address, federated_only=federated_only, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath) else: try: ursula_config = UrsulaConfiguration.from_configuration_file( filepath=config_file, domains={network} if network else None, registry_filepath=registry_filepath, provider_process=ETH_NODE, provider_uri=provider_uri, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, poa=poa, federated_only=federated_only) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=UrsulaConfiguration, config_file=config_file) except Exception as e: if click_config.debug: raise else: click.secho(str(e), fg='red', bold=True) raise click.Abort # # Configured Pre-Authentication Actions # # Handle destruction *before* network bootstrap and character initialization below if action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher ursula destroy' cannot be used in --dev mode - There is nothing to destroy." raise click.BadOptionUsage(option_name='--dev', message=message) return actions.destroy_configuration(character_config=ursula_config, force=force) # # Make Ursula # URSULA = actions.make_cli_character(character_config=ursula_config, click_config=click_config, min_stake=min_stake, teacher_uri=teacher_uri, dev=dev, lonely=lonely) # # Authenticated Action Switch # if action == 'run': """Seed, Produce, Run!""" # GO! try: # Ursula Deploy Warnings click_config.emit(message="Starting Ursula on {}".format( URSULA.rest_interface), color='green', bold=True) click_config.emit(message="Connecting to {}".format(','.join( ursula_config.domains)), color='green', bold=True) if not URSULA.federated_only and URSULA.stakes: click_config.emit( message= f"Staking {str(URSULA.current_stake)} ~ Keep Ursula Online!", color='blue', bold=True) if interactive: stdio.StandardIO(UrsulaCommandProtocol(ursula=URSULA)) if dry_run: return # <-- ABORT - (Last Chance) # Run - Step 3 node_deployer = URSULA.get_deployer() node_deployer.addServices() node_deployer.catalogServers(node_deployer.hendrix) node_deployer.run() # <--- Blocking Call (Reactor) # Handle Crash except Exception as e: ursula_config.log.critical(str(e)) click_config.emit(message="{} {}".format(e.__class__.__name__, str(e)), color='red', bold=True) raise # Crash :-( # Graceful Exit finally: click_config.emit(message="Stopping Ursula", color='green') ursula_config.cleanup() click_config.emit(message="Ursula Stopped", color='red') return elif action == "save-metadata": """Manually save a node self-metadata file""" metadata_path = ursula.write_node_metadata(node=URSULA) return click_config.emit( message="Successfully saved node metadata to {}.".format( metadata_path), color='green') elif action == "view": """Paint an existing configuration to the console""" if not URSULA.federated_only: click.secho("BLOCKCHAIN ----------\n") painting.paint_contract_status(click_config=click_config, ursula_config=ursula_config) current_block = URSULA.blockchain.w3.eth.blockNumber click.secho(f'Block # {current_block}') click.secho(f'NU Balance: {URSULA.token_balance}') click.secho(f'ETH Balance: {URSULA.eth_balance}') click.secho( f'Current Gas Price {URSULA.blockchain.client.gasPrice}') click.secho("CONFIGURATION --------") response = UrsulaConfiguration._read_configuration_file( filepath=config_file or ursula_config.config_file_location) return click_config.emit(response=response) elif action == "forget": actions.forget(configuration=ursula_config) return elif action == 'stake': # List Only if list_: if not URSULA.stakes: click.echo( f"There are no active stakes for {URSULA.checksum_address}" ) else: painting.paint_stakes(stakes=URSULA.stakes) return # Divide Only if divide: """Divide an existing stake by specifying the new target value and end period""" # Validate if not URSULA.stakes: click.echo( f"There are no active stakes for {URSULA.checksum_address}" ) return # Selection if index is None: painting.paint_stakes(stakes=URSULA.stakes) index = click.prompt("Select a stake to divide", type=click.IntRange( min=0, max=len(URSULA.stakes) - 1)) # Lookup the stake current_stake = URSULA.stakes[index] # Value if not value: value = click.prompt( f"Enter target value (must be less than {str(current_stake.value)})", type=STAKE_VALUE) value = NU(value, 'NU') # Duration if not duration: extension = click.prompt("Enter number of periods to extend", type=STAKE_EXTENSION) else: extension = duration if not force: painting.paint_staged_stake_division( ursula=URSULA, original_index=index, original_stake=current_stake, target_value=value, extension=extension) click.confirm("Is this correct?", abort=True) modified_stake, new_stake = URSULA.divide_stake( stake_index=index, target_value=value, additional_periods=extension) if not quiet: click.secho('Successfully divided stake', fg='green') click.secho( f'Transaction Hash ........... {new_stake.receipt}') # Show the resulting stake list painting.paint_stakes(stakes=URSULA.stakes) return # Confirm new stake init if not force: click.confirm("Stage a new stake?", abort=True) # Validate balance balance = URSULA.token_balance if balance == 0: click.secho(f"{URSULA.checksum_address} has 0 NU.") raise click.Abort if not quiet: click.echo(f"Current balance: {balance}") # Gather stake value if not value: min_locked = NU(URSULA.economics.minimum_allowed_locked, 'NuNit') value = click.prompt(f"Enter stake value", type=STAKE_VALUE, default=min_locked) else: value = NU(int(value), 'NU') # Duration if not quiet: message = f"Minimum duration: {URSULA.economics.minimum_allowed_locked} | " \ f"Maximum Duration: {URSULA.economics.maximum_allowed_locked}" click.echo(message) if not duration: duration = click.prompt( "Enter stake duration in periods (1 Period = 24 Hours)", type=STAKE_DURATION) start_period = URSULA.staking_agent.get_current_period() end_period = start_period + duration # Review if not force: painting.paint_staged_stake(ursula=URSULA, stake_value=value, duration=duration, start_period=start_period, end_period=end_period) if not dev: actions.confirm_staged_stake(ursula=URSULA, value=value, duration=duration) # Last chance to bail if not force: click.confirm("Publish staged stake to the blockchain?", abort=True) stake = URSULA.initialize_stake(amount=int(value), lock_periods=duration) # TODO temporary fix to not break backward compatibility URSULA.set_worker(worker_address=URSULA.checksum_address) painting.paint_staking_confirmation(ursula=URSULA, transactions=stake.transactions) return elif action == 'confirm-activity': if not URSULA.stakes: click.secho("There are no active stakes for {}".format( URSULA.checksum_address)) return URSULA.staking_agent.confirm_activity( node_address=URSULA.checksum_address) return elif action == 'collect-reward': """Withdraw staking reward to the specified wallet address""" if not force: click.confirm( f"Send {URSULA.calculate_reward()} to {URSULA.checksum_address}?" ) inflation_reward = URSULA.calculate_reward() if inflation_reward: URSULA.collect_staking_reward() URSULA.collect_policy_reward( collector_address=withdraw_address or checksum_address) else: raise click.BadArgumentUsage("No such argument {}".format(action))
def ursula(click_config, action, dev, quiet, dry_run, force, lonely, network, teacher_uri, min_stake, rest_host, rest_port, db_filepath, checksum_address, withdraw_address, federated_only, poa, config_root, config_file, provider_uri, recompile_solidity, no_registry, registry_filepath, value, duration, index, list_, divide) -> None: """ Manage and run an "Ursula" PRE node. \b Actions ------------------------------------------------- \b init Create a new Ursula node configuration. view View the Ursula node's configuration. run Run an "Ursula" node. save-metadata Manually write node metadata to disk without running forget Forget all known nodes. destroy Delete Ursula node configuration. stake Manage stakes for this node. confirm-activity Manually confirm-activity for the current period. collect-reward Withdraw staking reward. """ # # Boring Setup Stuff # if not quiet: log = Logger('ursula.cli') if click_config.debug and quiet: raise click.BadOptionUsage( option_name="quiet", message="--debug and --quiet cannot be used at the same time.") if not click_config.json_ipc and not click_config.quiet: click.secho(URSULA_BANNER.format(checksum_address or '')) # # Pre-Launch Warnings # if not click_config.quiet: if dev: click.secho("WARNING: Running in Development mode", fg='yellow') if force: click.secho("WARNING: Force is enabled", fg='yellow') # # Unauthenticated Configurations & Un-configured Ursula Control # if action == "init": """Create a brand-new persistent Ursula""" if not network: raise click.BadArgumentUsage( '--network is required to initialize a new configuration.') if dev: click_config.emit(message="WARNING: Using temporary storage area", color='yellow') if not config_root: # Flag config_root = click_config.config_file # Envvar if not rest_host: rest_host = click.prompt( "Enter Ursula's public-facing IPv4 address" ) # TODO: Remove this step ursula_config = UrsulaConfiguration.generate( password=click_config.get_password(confirm=True), config_root=config_root, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, domains={network} if network else None, federated_only=federated_only, checksum_public_address=checksum_address, no_registry=federated_only or no_registry, registry_filepath=registry_filepath, provider_uri=provider_uri, poa=poa) painting.paint_new_installation_help(new_configuration=ursula_config, config_root=config_root, config_file=config_file, federated_only=federated_only) return # # Configured Ursulas # # Development Configuration if dev: ursula_config = UrsulaConfiguration( dev_mode=True, domains={TEMPORARY_DOMAIN}, poa=poa, registry_filepath=registry_filepath, provider_uri=provider_uri, checksum_public_address=checksum_address, federated_only=federated_only, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath) # Authenticated Configurations else: # Domains -> bytes | or default domains = set(bytes(network, encoding='utf-8')) if network else None # Load Ursula from Configuration File try: ursula_config = UrsulaConfiguration.from_configuration_file( filepath=config_file, domains=domains, registry_filepath=registry_filepath, provider_uri=provider_uri, rest_host=rest_host, rest_port=rest_port, db_filepath=db_filepath, poa=poa, federated_only=federated_only) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=UrsulaConfiguration, config_file=config_file) click_config.unlock_keyring(character_configuration=ursula_config) # Handle destruction *before* network bootstrap and character initialization below if action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher ursula destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) return actions.destroy_configuration(character_config=ursula_config, force=force) # # Connect to Blockchain (Non-Federated) # if not ursula_config.federated_only: click_config.connect_to_blockchain( character_configuration=ursula_config, recompile_contracts=recompile_solidity) click_config.ursula_config = ursula_config # Pass Ursula's config onto staking sub-command # # Launch Warnings # if ursula_config.federated_only: click_config.emit(message="WARNING: Running in Federated mode", color='yellow') # Seed - Step 1 teacher_uris = [teacher_uri] if teacher_uri else list() teacher_nodes = actions.load_seednodes( teacher_uris=teacher_uris, min_stake=min_stake, federated_only=ursula_config.federated_only, network_middleware=click_config.middleware) # Produce - Step 2 URSULA = ursula_config(known_nodes=teacher_nodes, lonely=lonely) # # Action Switch # if action == 'run': """Seed, Produce, Run!""" # GO! try: click_config.emit(message="Starting Ursula on {}".format( URSULA.rest_interface), color='green', bold=True) # Ursula Deploy Warnings click_config.emit(message="Connecting to {}".format(','.join( str(d, encoding='utf-8') for d in ursula_config.domains)), color='green', bold=True) if not URSULA.federated_only and URSULA.stakes: click_config.emit( message= f"Staking {str(URSULA.current_stake)} ~ Keep Ursula Online!", color='blue', bold=True) if not click_config.debug: stdio.StandardIO(UrsulaCommandProtocol(ursula=URSULA)) if dry_run: return # <-- ABORT -X (Last Chance) # Run - Step 3 node_deployer = URSULA.get_deployer() node_deployer.addServices() node_deployer.catalogServers(node_deployer.hendrix) node_deployer.run() # <--- Blocking Call (Reactor) # Handle Crash except Exception as e: ursula_config.log.critical(str(e)) click_config.emit(message="{} {}".format(e.__class__.__name__, str(e)), color='red', bold=True) raise # Crash :-( # Graceful Exit / Crash finally: click_config.emit(message="Stopping Ursula", color='green') ursula_config.cleanup() click_config.emit(message="Ursula Stopped", color='red') return elif action == "save-metadata": """Manually save a node self-metadata file""" metadata_path = ursula.write_node_metadata(node=URSULA) return click_config.emit( message="Successfully saved node metadata to {}.".format( metadata_path), color='green') elif action == "view": """Paint an existing configuration to the console""" response = UrsulaConfiguration._read_configuration_file( filepath=config_file or ursula_config.config_file_location) return click_config.emit(response=response) elif action == "forget": actions.forget(configuration=ursula_config) return elif action == 'stake': # List Only if list_: if not URSULA.stakes: click.echo( f"There are no existing stakes for {URSULA.checksum_public_address}" ) painting.paint_stakes(stakes=URSULA.stakes) return # Divide Only if divide: """Divide an existing stake by specifying the new target value and end period""" # Validate if len(URSULA.stakes) == 0: click.secho("There are no active stakes for {}".format( URSULA.checksum_public_address)) return # Selection if index is None: painting.paint_stakes(stakes=URSULA.stakes) index = click.prompt("Select a stake to divide", type=click.IntRange( min=0, max=len(URSULA.stakes) - 1)) # Lookup the stake current_stake = URSULA.stakes[index] # Value if not value: value = click.prompt( f"Enter target value (must be less than {str(current_stake.value)})", type=STAKE_VALUE) value = NU(value, 'NU') # Duration if not duration: extension = click.prompt("Enter number of periods to extend", type=STAKE_EXTENSION) else: extension = duration if not force: painting.paint_staged_stake_division( ursula=URSULA, original_index=index, original_stake=current_stake, target_value=value, extension=extension) click.confirm("Is this correct?", abort=True) modified_stake, new_stake = URSULA.divide_stake( stake_index=index, target_value=value, additional_periods=extension) if not quiet: click.secho('Successfully divided stake', fg='green') click.secho( f'Transaction Hash ........... {new_stake.receipt}') # Show the resulting stake list painting.paint_stakes(stakes=URSULA.stakes) return # Confirm new stake init if not force: click.confirm("Stage a new stake?", abort=True) # Validate balance balance = URSULA.token_balance if balance == 0: click.secho(f"{ursula.checksum_public_address} has 0 NU.") raise click.Abort if not quiet: click.echo(f"Current balance: {balance}") # Gather stake value if not value: min_locked = NU( URSULA.miner_agent.economics.minimum_allowed_locked, 'NuNit') value = click.prompt(f"Enter stake value", type=STAKE_VALUE, default=min_locked) else: value = NU(int(value), 'NU') # Duration if not quiet: message = f"Minimum duration: {URSULA.economics.minimum_allowed_locked} | " \ f"Maximum Duration: {URSULA.economics.maximum_allowed_locked}" click.echo(message) if not duration: duration = click.prompt( "Enter stake duration in periods (1 Period = 24 Hours)", type=STAKE_DURATION) start_period = URSULA.miner_agent.get_current_period() end_period = start_period + duration # Review if not force: painting.paint_staged_stake(ursula=URSULA, stake_value=value, duration=duration, start_period=start_period, end_period=end_period) if not dev: actions.confirm_staged_stake(ursula=URSULA, value=value, duration=duration) # Last chance to bail if not force: click.confirm("Publish staged stake to the blockchain?", abort=True) stake = URSULA.initialize_stake(amount=int(value), lock_periods=duration) painting.paint_staking_confirmation(ursula=URSULA, transactions=stake.transactions) return elif action == 'confirm-activity': if not URSULA.stakes: click.secho("There are no active stakes for {}".format( URSULA.checksum_public_address)) return URSULA.miner_agent.confirm_activity( node_address=URSULA.checksum_public_address) return elif action == 'collect-reward': """Withdraw staking reward to the specified wallet address""" if not force: click.confirm( f"Send {URSULA.calculate_reward()} to {URSULA.checksum_public_address}?" ) URSULA.collect_policy_reward( collector_address=withdraw_address or checksum_address) URSULA.collect_staking_reward() else: raise click.BadArgumentUsage("No such argument {}".format(action))
def alice( click_config, action, # Mode dev, force, dry_run, # Network teacher_uri, min_stake, federated_only, network, discovery_port, controller_port, # Filesystem config_root, config_file, # Blockchain pay_with, provider_uri, geth, sync, poa, registry_filepath, hw_wallet, # Alice bob_encrypting_key, bob_verifying_key, label, m, n, value, rate, duration_periods, expiration, message_kit, ): """ "Alice the Policy Authority" management commands. \b Actions ------------------------------------------------- \b init Create a brand new persistent Alice view View existing Alice's configuration. run Start Alice's controller. destroy Delete existing Alice's configuration. public-keys Obtain Alice's public verification and encryption keys. derive-policy-pubkey Get a policy public key from a policy label. grant Create and enact an access policy for some Bob. revoke Revoke a policy. decrypt Decrypt data encrypted under an Alice's policy public key. """ # # Validate # if federated_only and geth: raise click.BadOptionUsage( option_name="--geth", message="Federated only cannot be used with the --geth flag") # Banner emitter = click_config.emitter emitter.clear() emitter.banner(ALICE_BANNER) # # Managed Ethereum Client # ETH_NODE = NO_BLOCKCHAIN_CONNECTION if geth: ETH_NODE = actions.get_provider_process() provider_uri = ETH_NODE.provider_uri(scheme='file') # # Eager Actions (No Authentication Required) # if action == 'init': """Create a brand-new persistent Alice""" if dev: raise click.BadArgumentUsage( "Cannot create a persistent development character") if not provider_uri and not federated_only: raise click.BadOptionUsage( option_name='--provider', message= "--provider is required to create a new decentralized alice.") if not config_root: # Flag config_root = click_config.config_file # Envvar if not pay_with and not federated_only: pay_with = select_client_account(emitter=emitter, provider_uri=provider_uri) new_alice_config = AliceConfiguration.generate( password=get_nucypher_password(confirm=True), config_root=config_root, checksum_address=pay_with, domains={network} if network else None, federated_only=federated_only, registry_filepath=registry_filepath, provider_process=ETH_NODE, poa=poa, provider_uri=provider_uri, m=m, n=n, duration_periods=duration_periods, rate=rate) painting.paint_new_installation_help( emitter, new_configuration=new_alice_config) return # Exit elif action == "view": """Paint an existing configuration to the console""" configuration_file_location = config_file or AliceConfiguration.default_filepath( ) response = AliceConfiguration._read_configuration_file( filepath=configuration_file_location) return emitter.ipc( response=response, request_id=0, duration=0) # FIXME: what are request_id and duration here? # # Get Alice Configuration # if dev: alice_config = AliceConfiguration( dev_mode=True, network_middleware=click_config.middleware, domains={network}, provider_process=ETH_NODE, provider_uri=provider_uri, federated_only=True) else: try: alice_config = AliceConfiguration.from_configuration_file( dev_mode=False, filepath=config_file, domains={network} if network else None, network_middleware=click_config.middleware, rest_port=discovery_port, checksum_address=pay_with, provider_process=ETH_NODE, provider_uri=provider_uri, registry_filepath=registry_filepath) except FileNotFoundError: return actions.handle_missing_configuration_file( character_config_class=AliceConfiguration, config_file=config_file) if action == "destroy": """Delete all configuration files from the disk""" if dev: message = "'nucypher alice destroy' cannot be used in --dev mode" raise click.BadOptionUsage(option_name='--dev', message=message) return actions.destroy_configuration(emitter, character_config=alice_config, force=force) # # Produce Alice # # TODO: OH MY. client_password = None if not alice_config.federated_only: if (not hw_wallet or not dev) and not click_config.json_ipc: client_password = get_client_password( checksum_address=alice_config.checksum_address) try: ALICE = actions.make_cli_character(character_config=alice_config, click_config=click_config, dev=dev, teacher_uri=teacher_uri, min_stake=min_stake, client_password=client_password) except NucypherKeyring.AuthenticationFailed as e: emitter.echo(str(e), color='red', bold=True) click.get_current_context().exit(1) # TODO: Exit codes (not only for this, but for other exceptions) # # Admin Actions # if action == "run": """Start Alice Controller""" try: # RPC if click_config.json_ipc: rpc_controller = ALICE.make_rpc_controller() _transport = rpc_controller.make_control_transport() rpc_controller.start() return # HTTP else: emitter.message( f"Alice Verifying Key {bytes(ALICE.stamp).hex()}", color="green", bold=True) controller = ALICE.make_web_controller( crash_on_error=click_config.debug) ALICE.log.info('Starting HTTP Character Web Controller') emitter.message( f'Running HTTP Alice Controller at http://localhost:{controller_port}' ) return controller.start(http_port=controller_port, dry_run=dry_run) # Handle Crash except Exception as e: alice_config.log.critical(str(e)) emitter.message(f"{e.__class__.__name__} {e}", color='red', bold=True) if click_config.debug: raise # Crash :-( return # # Alice API # elif action == "public-keys": response = ALICE.controller.public_keys() return response elif action == "derive-policy-pubkey": # Validate if not label: raise click.BadOptionUsage( option_name='label', message= "--label is required for deriving a policy encrypting key.") # Request return ALICE.controller.derive_policy_encrypting_key(label=label) elif action == "grant": # Validate if not all((bob_verifying_key, bob_encrypting_key, label)): raise click.BadArgumentUsage( message= "--bob-verifying-key, --bob-encrypting-key, and --label are " "required options to grant (optionally --m, --n, and --expiration)." ) # Request grant_request = { 'bob_encrypting_key': bob_encrypting_key, 'bob_verifying_key': bob_verifying_key, 'label': label, 'm': m, 'n': n, 'expiration': expiration, } if not ALICE.federated_only: grant_request.update({'value': value}) return ALICE.controller.grant(request=grant_request) elif action == "revoke": # Validate if not label and bob_verifying_key: raise click.BadArgumentUsage( message= f"--label and --bob-verifying-key are required options for revoke." ) # Request revoke_request = { 'label': label, 'bob_verifying_key': bob_verifying_key } return ALICE.controller.revoke(request=revoke_request) elif action == "decrypt": # Validate if not all((label, message_kit)): input_specification, output_specification = ALICE.controller.get_specifications( interface_name=action) required_fields = ', '.join(input_specification) raise click.BadArgumentUsage( f'{required_fields} are required flags to decrypt') # Request request_data = {'label': label, 'message_kit': message_kit} response = ALICE.controller.decrypt(request=request_data) return response else: raise click.BadArgumentUsage(f"No such argument {action}")
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 == "destroy": """Delete all configuration files from the disk""" return actions.destroy_configuration(character_config=felix_config, force=force) 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))