def main( ctx: click.Context, line_length: int, check: bool, diff: bool, compact_diff: bool, include: str, exclude: str, src: List[PathLike], config: Optional[PathLike], verbose: bool, ): """The uncompromising Snakemake code formatter. SRC specifies directories and files to format. Directories will be searched for file names that conform to the include/exclude patterns provided. Files are modified in-place by default; use diff, check, or `snakefmt - < Snakefile` to avoid this. """ log_level = logging.DEBUG if verbose else logging.INFO logging.basicConfig(format="[%(levelname)s] %(message)s", level=log_level) if not src: click.echo( "No path provided. Nothing to do 😴. Call with -h for help.", err=True) ctx.exit(0) if "-" in src and len(src) > 1: raise click.BadArgumentUsage("Cannot mix stdin (-) with other files") if diff and compact_diff: logging.warning( "Both --diff and --compact-diff given. Returning compact diff...") try: include_regex = construct_regex(include) except re.error: raise InvalidRegularExpression( f"Invalid regular expression for --include given: {include!r}") try: exclude_regex = construct_regex(exclude) except re.error: raise InvalidRegularExpression( f"Invalid regular expression for --exclude given: {exclude!r}") files_to_format: Set[PathLike] = set() gitignore = get_gitignore(Path()) for path in src: path = Path(path) if path.name == "-" or path.is_file(): # if a file was explicitly given, we don't care about its extension files_to_format.add(path) elif path.is_dir(): files_to_format.update( get_snakefiles_in_dir(path, include_regex, exclude_regex, gitignore)) else: logging.warning(f"ignoring invalid path: {path}") differ = Diff(compact=compact_diff) files_changed, files_unchanged = 0, 0 files_with_errors = 0 for path in files_to_format: path_is_stdin = path.name == "-" if path_is_stdin: logging.debug("Formatting from stdin") path = sys.stdin else: logging.debug(f"Formatting {path}") try: original_content = path.read_text() except AttributeError: original_content = path.read() try: snakefile = Snakefile(StringIO(original_content)) formatter = Formatter(snakefile, line_length=line_length, black_config=config) formatted_content = formatter.get_formatted() except Exception as error: if check: logging.error( f"'{error.__class__.__name__}: {error}' in file {path}") files_with_errors += 1 continue else: raise error if check: is_changed = differ.is_changed(original_content, formatted_content) if is_changed: logging.debug("Formatted content is different from original") files_changed += 1 else: files_unchanged += 1 if diff or compact_diff: filename = "stdin" if path_is_stdin else str(path) click.echo(f"{'=' * 5}> Diff for {filename} <{'=' * 5}\n") difference = differ.compare(original_content, formatted_content) click.echo(difference) elif not any([check, diff, compact_diff]): if path_is_stdin: sys.stdout.write(formatted_content) else: write_file_back = differ.is_changed(original_content, formatted_content) if write_file_back: logging.info(f"Writing formatted content to {path}") with path.open("w") as out_handle: out_handle.write(formatted_content) if check: if files_unchanged == len(files_to_format): logging.info( f"All {len(files_to_format)} file(s) would be left unchanged 🎉" ) ctx.exit(ExitCode.NO_CHANGE.value) elif files_with_errors > 0: exit_value = ExitCode.ERROR.value elif files_changed > 0: exit_value = ExitCode.WOULD_CHANGE.value if files_with_errors > 0: logging.info( f"{files_with_errors} file(s) raised parsing errors 🤕") if files_changed > 0: logging.info(f"{files_changed} file(s) would be changed 😬") if files_unchanged > 0: logging.info( f"{files_unchanged} file(s) would be left unchanged 🎉") ctx.exit(exit_value) logging.info("All done 🎉")
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 upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, multisig): """ Upgrade NuCypher existing proxy contract deployments. """ # Init emitter = general_config.emitter ADMINISTRATOR, _, _, registry = actor_options.create_actor(emitter) contract_name = actor_options.contract_name if not contract_name: raise click.BadArgumentUsage(message="--contract-name is required when using --upgrade") existing_secret = click.prompt('Enter existing contract upgrade secret', hide_input=True) new_secret = click.prompt('Enter new contract upgrade secret', hide_input=True, confirmation_prompt=True) if multisig: if not target_address: raise click.BadArgumentUsage(message="--multisig requires using --target-address.") if not actor_options.force: click.confirm(f"Confirm building a re-target transaction for {contract_name}'s proxy to {target_address}?", abort=True) transaction = ADMINISTRATOR.retarget_proxy(contract_name=contract_name, target_address=target_address, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret, just_build_transaction=True) trustee = Trustee(registry=registry, checksum_address=ADMINISTRATOR.deployer_address) data_for_multisig_executives = trustee.produce_data_to_sign(transaction) emitter.message(f"Transaction to retarget {contract_name} proxy to {target_address} was built:", color='green') paint_multisig_proposed_transaction(emitter, data_for_multisig_executives) # TODO: Move this logic to a better place nonce = data_for_multisig_executives['parameters']['nonce'] filepath = f'proposal-{nonce}.json' with open(filepath, 'w') as outfile: json.dump(data_for_multisig_executives, outfile) emitter.echo(f"Saved proposal to {filepath}", color='blue', bold=True) elif retarget: if not target_address: raise click.BadArgumentUsage(message="--target-address is required when using --retarget") if not actor_options.force: click.confirm(f"Confirm re-target {contract_name}'s proxy to {target_address}?", abort=True) receipt = ADMINISTRATOR.retarget_proxy(contract_name=contract_name, target_address=target_address, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret) emitter.message(f"Successfully re-targeted {contract_name} proxy to {target_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt) else: if not actor_options.force: click.confirm(f"Confirm deploy new version of {contract_name} and retarget proxy?", abort=True) receipts = ADMINISTRATOR.upgrade_contract(contract_name=contract_name, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret, ignore_deployed=ignore_deployed) emitter.message(f"Successfully deployed and upgraded {contract_name}", color='green') for name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt)
def gpu_use_view_command(node, user, lab, dense, only_errors, display_time, display_load): r"""Display real-time information about the GPUs on skynet There are two blocks per GPU, the left block indicates whether or not the GPU is alloced and the right block indicates whether or not something is running on it. Red means there is some error (idle reservation or process running out of SLURM) and pink/magenta means idle reservation but in the debug partition. Notes: - When using gpu-use with `watch`, add `--color` for the output to be displayed correctly, i.e. use `watch --color gpu-use -d`. """ if dense and only_errors: dense = False node_re = re.compile(node) if node is not None else None user_re = re.compile(user) if user is not None else None session = SessionMaker() nodes = session.query(Node) users = None if node_re is not None: node_names = [ name[0] for name in session.query(Node.name).all() if node_re.match(name[0]) is not None ] if len(node_names) == 0: raise click.BadArgumentUsage("No nodes matched {}".format(node)) node_filter = Node.name.in_(node_names) nodes = nodes.filter(Node.name.in_(node_names)) if lab is not None: labs = filter_labs(session, lab) user_names = list(set(user.name for lab in labs for user in lab.users)) users = session.query(User).filter(User.name.in_(user_names)) nodes = nodes.filter(Node.users.any(User.name.in_(user_names))) if user_re is not None: user_names = [ name[0] for name in session.query(User.name).all() if user_re.match(name[0]) is not None ] if len(user_names) == 0: raise click.BadArgumentUsage("No users matched {}".format(user)) users = (session.query(User) if users is None else users).filter( User.name.in_(user_names)) nodes = nodes.filter(Node.users.any(User.name.in_(user_names))) nodes = (nodes.order_by(Node.name).options( sa.orm.joinedload(Node.gpus), sa.orm.joinedload(Node.slurm_jobs), sa.orm.joinedload(Node.gpus).joinedload("processes"), sa.orm.joinedload(Node.slurm_jobs).joinedload("processes"), ).all()) if users is not None: users = users.all() if not supports_unicode(): click.echo( "Terminal does not support unicode, do `export LANG=en_US.UTF-8` for a better experience (may also need to start tmux with `-u`)" ) nodes = sorted(nodes, key=lambda n: len(n.gpus)) if not only_errors and dense is None: if len(nodes) > 4: dense = True if only_errors: show_errors(nodes, users) elif dense: show_dense(nodes, users, display_time, display_load) else: show_regular(nodes, users, display_time, display_load)
def register_key(key_name: str, experimental_login: bool) -> None: """Register a key with the store to sign assertions.""" if experimental_login: raise click.BadArgumentUsage( f"Set {storeapi.constants.ENVIRONMENT_STORE_AUTH}=candid instead") snapcraft_legacy.register_key(key_name)
def cmd( ctx, name: Optional[str], version: Optional[str], idn: Optional[str], *, overrides: Optional[str], remove: bool, yes: bool, no_iam: bool, no_noop: bool, ): """Update the infrastructure.""" s3_bucket, s3_prefix = ctx.obj["s3_bucket"], ctx.obj["s3_prefix"] if remove: if name and version and idn or name and version: extra = [a for a in [version, idn] if a] raise click.BadArgumentUsage( f"Got unexpected extra arguments ({', '.join(extra)})") idn, name = name, None if not idn: raise click.MissingParameter(param_hint='"name"', param_type="argument") stk = stack.Stack(idn) if not stk.exists: raise cli_util.UserError(f"brick does not exist: {idn}") tree = _merge(stk.tree) state = {"root": stack.State(["DELETE"])} for node in tree.all_nodes_itr(): if "type" in node.data: state[node.identifier] = stack.State(["DELETE"]) def states_itr() -> Iterator[Dict[str, stack.State]]: stk.delete(False) yield from stk.events(state, False) _display(tree, states_itr(), state, yes) return node, _idn = load.execute(idn, name, version, overrides, s3_bucket, s3_prefix) tpl = encode.Template(node) body = tpl.dumps() tplb = body.encode("utf-8") artifacts: List[Tuple[Union[pathlib.Path, IO[bytes]], aws_util.AssetInfo]] = [] if len(tplb) > 51_000: # pragma: no cover info = aws_util.asset_info(io.BytesIO(tplb)) body = info.url artifacts.append((io.BytesIO(tplb), info)) # pylint: disable=protected-access artifacts += [(a._path, cast(aws_util.AssetInfo, a)) for a in config.ASSETS.get()] util.upload(artifacts) stk = stack.Stack(_idn) tree = _merge(stk.tree if stk.exists else {}, tpl.tree) with stk.plan(body, tpl.params, not no_iam) as plan: states = iter(plan) try: state = next(states) except StopIteration: msg = f"{_idn} is already up to date" if no_noop: raise cli_util.UserError(msg) LOGGER.info(msg) return _display(tree, states, state, yes) click.secho("\nInstance value\n", fg="magenta") value = json.dumps(stack.Stack(_idn).value, indent=2, separators=(", ", ": ")) if sys.stdout.isatty(): # pragma: no cover # pylint: disable=no-member # PyCQA/pylint#491 value = highlight(value, lexers.JsonLexer(), formatters.TerminalFormatter()) click.echo(value, nl=not sys.stdout.isatty())
def rpg(pass_length: int, number: int, output: click.File, exclude_charsets: str, no_safe: bool, verbose: bool) -> None: """ Generate random, entropic, complex and safe password. \f :param int pass_length: desire password length :param int number: number of password to generate. Default is 1 :param click.File output: output file :param str exclude_charsets: comma-separated charsets to exclude. Default is None :param bool no_safe: do not check password in Have I Been Pwned db. Default is False :param bool verbose: print verbose output. Default is False :return: None """ # Check pass_length validity msg.Prints.verbose(f"Checking <pass-length> ({pass_length}) validity", verbose) if pass_length > _max_pass_length or pass_length < _min_pass_length: raise click.BadArgumentUsage( msg.Echoes.error(( f"Invalid value for \"<pass-length>\": {pass_length} " f"is not in the valid range of {_min_pass_length} to {_max_pass_length}." ))) # Check number validity msg.Prints.verbose(f"Checking <pass-number> ({number}) validity", verbose) if number > _max_pass_number: raise click.BadOptionUsage( "number", msg.Echoes.error( "Invalid value for \"<pass-number>\": the maximum value accepted is 50." )) # Load charsets and check the validity msg.Prints.verbose("Loading charsets", verbose) chars = _get_char_list(exclude_charsets) msg.Prints.verbose("Charsets loaded\nChecking charsets validity", verbose) # Check at least one charsets type has been selected if not chars: raise click.BadOptionUsage( "--exclude-charsets", msg.Echoes.error( "RPG needs at least one charsets type to generate password.")) else: if not len(chars) == len(string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation): # User chose to not use any charsets, print warning message msg.Prints.warning( "You are going to generate passwords without one or more of default charsets!" ) msg.Prints.warning( "RPG cares a lot for your security, it's recommended to avoid this practice if possible!\n" ) # Check if --no-safe option is in use, if so, print a warning message if no_safe: msg.Prints.warning( "You are going to generate passwords without checking if they have been already leaked!" ) msg.Prints.warning( "RPG cares a lot for your security, it's recommended to avoid this practice if possible!\n" ) msg.Prints.verbose("Start to generate passwords", verbose) # Print loading pw_list = [] with click.progressbar(length=number, label="Generating passwords", show_pos=True) as pw_bar: for _ in pw_bar: pw = _generate_random_password(pass_length, chars, no_safe) if pw is not None: pw_list.append(pw) else: raise click.ClickException( msg.Echoes.error( "An error occurred while querying Have I Been Pwned API. Please retry or use --no-safe option" )) # Print generated passwords if output: output.write("Passwords:\n") else: msg.Prints.emphasis("Passwords:") for pw in pw_list: if output: output.write(f"{pw}\n") else: msg.Prints.info(pw) # Calculate entropy and print it entropy = _get_entropy(pass_length, chars) if output: output.write(f"\nEntropy: {entropy}") else: msg.Prints.emphasis( f"\nThe entropy of generated password is: {entropy}") # Print summary table, only if --verbose or --output if output: output.write(f"\n{_password_entropy_table}") else: msg.Prints.verbose(_password_entropy_table, verbose)
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))
def upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, confirmations): """Upgrade NuCypher existing proxy contract deployments.""" # # Setup # emitter = general_config.emitter ADMINISTRATOR, deployer_address, blockchain, local_registry = actor_options.create_actor( emitter) # # Pre-flight # contract_name = actor_options.contract_name if not contract_name: raise click.BadArgumentUsage( message="--contract-name is required when using --upgrade") try: # Check contract name exists Deployer = ADMINISTRATOR.deployers[contract_name] except KeyError: message = UNKNOWN_CONTRACT_NAME.format( contract_name=contract_name, constants=ADMINISTRATOR.deployers.keys()) emitter.echo(message, color='red', bold=True) raise click.Abort() deployer = Deployer(registry=local_registry) # Check deployer address is owner if Deployer._ownable and deployer_address != deployer.owner: # blockchain read emitter.echo( DEPLOYER_IS_NOT_OWNER.format(deployer_address=deployer_address, contract_name=contract_name, agent=deployer.make_agent())) raise click.Abort() else: emitter.echo('✓ Verified deployer address as contract owner', color='green') # # Business # if retarget: if not target_address: raise click.BadArgumentUsage( message="--target-address is required when using --retarget") if not actor_options.force: click.confirm(CONFIRM_RETARGET.format( contract_name=contract_name, target_address=target_address), abort=True) receipt = ADMINISTRATOR.retarget_proxy(contract_name=contract_name, target_address=target_address, confirmations=confirmations) message = SUCCESSFUL_RETARGET.format(contract_name=contract_name, target_address=target_address) emitter.message(message, color='green') paint_receipt_summary(emitter=emitter, receipt=receipt) return # Exit else: github_registry = establish_deployer_registry( emitter=emitter, download_registry=True, network=actor_options.network) if not actor_options.force: # Check for human verification of versioned upgrade details click.confirm( CONFIRM_BEGIN_UPGRADE.format(contract_name=contract_name), abort=True) if deployer._ownable: # Only ownable + upgradeable contracts apply verify_upgrade_details(blockchain=blockchain, registry=github_registry, deployer=deployer) # Success receipts = ADMINISTRATOR.upgrade_contract( contract_name=contract_name, ignore_deployed=ignore_deployed, confirmations=confirmations) emitter.message(SUCCESSFUL_UPGRADE.format(contract_name=contract_name), color='green') for name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt) emitter.echo(REGISTRY_PUBLICATION_HINT.format( contract_name=contract_name, local_registry=local_registry, network=actor_options.network), color='blue') emitter.echo(ETHERSCAN_VERIFY_HINT.format( solc_version=SOLIDITY_COMPILER_VERSION), color='blue') return # Exit
def stake(click_config, action, checksum_address, index, value, duration): """ Manage token staking. TODO \b Actions ------------------------------------------------- \b list List all stakes for this node. init Stage a new stake. confirm-activity Manually confirm-activity for the current period. divide Divide an existing stake. collect-reward Withdraw staking reward. """ ursula_config = click_config.ursula_config # # Initialize # if not ursula_config.federated_only: ursula_config.connect_to_blockchain(click_config) ursula_config.connect_to_contracts(click_config) if not checksum_address: if click_config.accounts == NO_BLOCKCHAIN_CONNECTION: click.echo('No account found.') raise click.Abort() for index, address in enumerate(click_config.accounts): if index == 0: row = 'etherbase (0) | {}'.format(address) else: row = '{} .......... | {}'.format(index, address) click.echo(row) click.echo("Select ethereum address") account_selection = click.prompt("Enter 0-{}".format(len(ur.accounts)), type=click.INT) address = click_config.accounts[account_selection] if action == 'list': live_stakes = ursula_config.miner_agent.get_all_stakes(miner_address=checksum_address) for index, stake_info in enumerate(live_stakes): row = '{} | {}'.format(index, stake_info) click.echo(row) elif action == 'init': click.confirm("Stage a new stake?", abort=True) live_stakes = ursula_config.miner_agent.get_all_stakes(miner_address=checksum_address) if len(live_stakes) > 0: raise RuntimeError("There is an existing stake for {}".format(checksum_address)) # Value balance = ursula_config.miner_agent.token_agent.get_balance(address=checksum_address) click.echo("Current balance: {}".format(balance)) value = click.prompt("Enter stake value", type=click.INT) # Duration message = "Minimum duration: {} | Maximum Duration: {}".format(MIN_LOCKED_PERIODS, MAX_MINTING_PERIODS) click.echo(message) duration = click.prompt("Enter stake duration in periods (1 Period = 24 Hours)", type=click.INT) start_period = ursula_config.miner_agent.get_current_period() end_period = start_period + duration # Review click.echo(""" | Staged Stake | Node: {address} Value: {value} Duration: {duration} Start Period: {start_period} End Period: {end_period} """.format(address=checksum_address, value=value, duration=duration, start_period=start_period, end_period=end_period)) raise NotImplementedError elif action == 'confirm-activity': """Manually confirm activity for the active period""" stakes = ursula_config.miner_agent.get_all_stakes(miner_address=checksum_address) if len(stakes) == 0: raise RuntimeError("There are no active stakes for {}".format(checksum_address)) ursula_config.miner_agent.confirm_activity(node_address=checksum_address) elif action == 'divide': """Divide an existing stake by specifying the new target value and end period""" stakes = ursula_config.miner_agent.get_all_stakes(miner_address=checksum_address) if len(stakes) == 0: raise RuntimeError("There are no active stakes for {}".format(checksum_address)) if not index: for selection_index, stake_info in enumerate(stakes): click.echo("{} ....... {}".format(selection_index, stake_info)) index = click.prompt("Select a stake to divide", type=click.INT) target_value = click.prompt("Enter new target value", type=click.INT) extension = click.prompt("Enter number of periods to extend", type=click.INT) click.echo(""" Current Stake: {} New target value {} New end period: {} """.format(stakes[index], target_value, target_value + extension)) click.confirm("Is this correct?", abort=True) ursula_config.miner_agent.divide_stake(miner_address=checksum_address, stake_index=index, value=value, periods=extension) elif action == 'collect-reward': # TODO: Implement """Withdraw staking reward to the specified wallet address""" # click.confirm("Send {} to {}?".format) # ursula_config.miner_agent.collect_staking_reward(collector_address=address) raise NotImplementedError else: raise click.BadArgumentUsage("No such argument {}".format(action))
def ursula(click_config, action, debug, dev, quiet, dry_run, force, lonely, network, teacher_uri, min_stake, rest_host, rest_port, db_filepath, checksum_address, federated_only, poa, config_root, config_file, metadata_dir, # TODO: Start nodes from an additional existing metadata dir provider_uri, no_registry, registry_filepath ) -> None: """ Manage and run an Ursula node. \b Actions ------------------------------------------------- \b run Run an "Ursula" node. init Create a new Ursula node configuration. view View the Ursula node's configuration. forget Forget all known nodes. save-metadata Manually write node metadata to disk without running destroy Delete Ursula node configuration. """ # # Boring Setup Stuff # if not quiet: log = Logger('ursula.cli') if debug and quiet: raise click.BadOptionUsage(option_name="quiet", message="--debug and --quiet cannot be used at the same time.") if debug: click_config.log_to_sentry = False click_config.log_to_file = True globalLogPublisher.removeObserver(logToSentry) # Sentry globalLogPublisher.addObserver(SimpleObserver(log_level_name='debug')) # Print elif quiet: globalLogPublisher.removeObserver(logToSentry) globalLogPublisher.removeObserver(SimpleObserver) globalLogPublisher.removeObserver(getJsonFileObserver()) # # Pre-Launch Warnings # if not quiet: if dev: click.secho("WARNING: Running in development mode", fg='yellow') if force: click.secho("WARNING: Force is enabled", fg='yellow') # # Unauthenticated Configurations # if action == "init": """Create a brand-new persistent Ursula""" if dev and not quiet: click.secho("WARNING: Using temporary storage area", fg='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") 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) if not quiet: click.secho("Generated keyring {}".format(ursula_config.keyring_dir), fg='green') click.secho("Saved configuration file {}".format(ursula_config.config_file_location), fg='green') # Give the use a suggestion as to what to do next... how_to_run_message = "\nTo run an Ursula node from the default configuration filepath run: \n\n'{}'\n" suggested_command = 'nucypher ursula run' if config_root is not None: config_file_location = os.path.join(config_root, config_file or UrsulaConfiguration.CONFIG_FILENAME) suggested_command += ' --config-file {}'.format(config_file_location) click.secho(how_to_run_message.format(suggested_command), fg='green') return # FIN else: click.secho("OK") 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) try: ursula_config = UrsulaConfiguration.from_configuration_file(filepath=config_file, domains={network}) except FileNotFoundError: config_root = config_root or DEFAULT_CONFIG_ROOT config_file_location = config_file or UrsulaConfiguration.DEFAULT_CONFIG_FILE_LOCATION if not force: message = "No configuration file found at {}; \n" \ "Destroy top-level configuration directory: {}?".format(config_file_location, config_root) click.confirm(message, abort=True) # ABORT shutil.rmtree(config_root, ignore_errors=False) else: if not force: click.confirm(''' *Permanently and irreversibly delete all* nucypher files including - Private and Public Keys - Known Nodes - TLS certificates - Node Configurations - Log Files Delete {}?'''.format(ursula_config.config_root), abort=True) try: ursula_config.destroy(force=force) except FileNotFoundError: message = 'Failed: No nucypher files found at {}'.format(ursula_config.config_root) click.secho(message, fg='red') log.debug(message) raise click.Abort() else: message = "Deleted configuration files at {}".format(ursula_config.config_root) click.secho(message, fg='green') log.debug(message) if not quiet: click.secho("Destroyed {}".format(config_root)) return # 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: # Deserialize network domain name if override passed if network: domain_constant = getattr(constants, network.upper()) domains = {domain_constant} else: domains = None 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, # TODO: Handle Boolean overrides # poa=poa, # federated_only=federated_only, ) try: # Unlock Keyring if not quiet: click.secho('Decrypting keyring...', fg='blue') ursula_config.keyring.unlock(password=click_config.get_password()) # Takes ~3 seconds, ~1GB Ram except CryptoError: raise ursula_config.keyring.AuthenticationFailed if not ursula_config.federated_only: try: ursula_config.connect_to_blockchain(recompile_contracts=False) ursula_config.connect_to_contracts() except EthereumContractRegistry.NoRegistry: message = "Cannot configure blockchain character: No contract registry found; " \ "Did you mean to pass --federated-only?" raise EthereumContractRegistry.NoRegistry(message) click_config.ursula_config = ursula_config # Pass Ursula's config onto staking sub-command # # Launch Warnings # if not quiet: if ursula_config.federated_only: click.secho("WARNING: Running in Federated mode", fg='yellow') # # Action Switch # if action == 'run': """Seed, Produce, Run!""" # # Seed - Step 1 # teacher_nodes = list() if teacher_uri: node = Ursula.from_teacher_uri(teacher_uri=teacher_uri, min_stake=min_stake, federated_only=ursula_config.federated_only) teacher_nodes.append(node) # # Produce - Step 2 # ursula = ursula_config(known_nodes=teacher_nodes, lonely=lonely) # GO! try: # # Run - Step 3 # click.secho("Connecting to {}".format(','.join(str(d) for d in ursula_config.domains)), fg='blue', bold=True) click.secho("Running Ursula {} on {}".format(ursula, ursula.rest_interface), fg='green', bold=True) if not debug: stdio.StandardIO(UrsulaCommandProtocol(ursula=ursula)) if dry_run: # That's all folks! return ursula.get_deployer().run() # <--- Blocking Call (Reactor) except Exception as e: ursula_config.log.critical(str(e)) click.secho("{} {}".format(e.__class__.__name__, str(e)), fg='red') raise # Crash :-( finally: if not quiet: click.secho("Stopping Ursula") ursula_config.cleanup() if not quiet: click.secho("Ursula Stopped", fg='red') return elif action == "save-metadata": """Manually save a node self-metadata file""" ursula = ursula_config.produce(ursula_config=ursula_config) metadata_path = ursula.write_node_metadata(node=ursula) if not quiet: click.secho("Successfully saved node metadata to {}.".format(metadata_path), fg='green') return elif action == "view": """Paint an existing configuration to the console""" paint_configuration(config_filepath=config_file or ursula_config.config_file_location) return elif action == "forget": """Forget all known nodes via storages""" click.confirm("Permanently delete all known node data?", abort=True) ursula_config.forget_nodes() message = "Removed all stored node node metadata and certificates" click.secho(message=message, fg='red') return else: raise click.BadArgumentUsage("No such argument {}".format(action))
def _get_indicator(indname): try: return xc.core.indicator.registry[indname.upper()].get_instance() except KeyError: raise click.BadArgumentUsage( f"Indicator '{indname}' not found in xclim.")
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, device, # 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=get_password(confirm=True), config_root=config_root, checksum_address=pay_with, 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) 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) 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( 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) 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) # # Admin Actions # if action == "run": """Start Alice Controller""" # RPC if click_config.json_ipc: rpc_controller = ALICE.make_rpc_controller() _transport = rpc_controller.make_control_transport() rpc_controller.start() return # HTTP else: 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=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 _bkt_info( repo: Repo, task: Task, geometry: Polygon, bucket_name: str, sort: tuple, limit: int, column: tuple, where: str, check=False, ) -> (GeoDataFrame, list): if geometry.area == 0: raise click.BadArgumentUsage('ROI has zero area') cache_file_name = _cache_pairs_file_name(repo) # TODO check ROI exists _df = pairs.load_from_cache(cache_file_name=cache_file_name, geometry=geometry) _bk = bucket.create_list(_df, buckets_dir='') if _bk is None or _bk.empty: raise AssertionError(f'No products could be found for ROI ') if bucket_name.isnumeric(): _t = _bk.groupby('bucket', as_index=False).ngroup() _df = _bk[_t == int(bucket_name)] else: _df = _bk[_bk['bucket'] == bucket_name] _f = list(column) _ds = _list_products(_df, where=where, sort=list(sort), limit=limit) _m, _s = _task_ms(task) # log.error(f"{_m} {_s}") if task.loaded: if _m or _s: def _ms(b): _x = 'm' if _m == b else ' ' _x += 's' if _s == b else ' ' return _x _ds['task'] = _ds['title'].apply(_ms) _f += ['task'] _e, eodata = task.get_valid_key('eodata') # TODO other formats parsers: Sentinel-1.ZIP ,TOPSAR def _ch_fs(b): _p = _local_eodata_relative_path(eodata, b) if os.path.isfile(os.path.join(_p, 'manifest.safe')): _m = os.path.join(_p, 'measurement') if os.path.isdir(_m): return '+' if any(os.scandir(_m)) else '~' return '' if check and not _e: _ds['exists'] = _ds['productIdentifier'].apply(_ch_fs) _f += ['exists'] pass # _ds = _ds.reindex(['task', *_f], axis=1, copy=False) # output.comment(f"Task '{_tname}' applied\n\n") headers = ['#', 'task', *_f] if _df.empty: raise OCLIException(f"bucket {bucket_name} not found") bname = _df.iloc[0]['bucket'] _ds = _ds[_f] # remove injected title if not in columns return bname, _ds, _ds.columns.to_list()
def do_update( project: Project, dev: bool = False, sections: Sequence[str] = (), default: bool = True, strategy: str = "reuse", save: str = "compatible", unconstrained: bool = False, packages: Sequence[str] = (), ) -> None: """Update specified packages or all packages :param project: The project instance :param dev: whether to update dev dependencies :param sections: update speicified sections :param default: update default :param strategy: update strategy (reuse/eager) :param save: save strategy (compatible/exact/wildcard) :param unconstrained: ignore version constraint :param packages: specified packages to update :return: None """ check_project_file(project) if len(packages) > 0 and (len(sections) > 1 or not default): raise click.BadParameter( "packages argument can't be used together with multple -s or --no-default." ) if not packages: if unconstrained: raise click.BadArgumentUsage( "--unconstrained must be used with package names given.") # pdm update with no packages given, same as 'lock' + 'sync' do_lock(project) do_sync(project, sections, dev, default, clean=False) return section = sections[0] if sections else ("dev" if dev else "default") all_dependencies = project.all_dependencies dependencies = all_dependencies[section] updated_deps = {} tracked_names = set() for name in packages: matched_name = next( filter( lambda k: safe_name(strip_extras(k)[0]).lower() == safe_name( name).lower(), dependencies.keys(), ), None, ) if not matched_name: raise ProjectError("{} does not exist in {} dependencies.".format( context.io.green(name, bold=True), section)) if unconstrained: dependencies[matched_name].specifier = get_specifier("") tracked_names.add(matched_name) updated_deps[matched_name] = dependencies[matched_name] context.io.echo("Updating packages: {}.".format(", ".join( context.io.green(v, bold=True) for v in tracked_names))) resolved = do_lock(project, strategy, tracked_names, all_dependencies) do_sync(project, sections=(section, ), default=False, clean=False) if unconstrained: # Need to update version constraints save_version_specifiers(updated_deps, resolved, save) project.add_dependencies(updated_deps) lockfile = project.lockfile lockfile["root"]["content_hash"] = "md5:" + project.get_content_hash( "md5") project.write_lockfile(lockfile, False)
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.emitter( 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 = [bytes(network, encoding='utf-8')] if network else None # Load Ursula from Configuration File 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) click_config.unlock_keyring(character_configuration=ursula_config) # # 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.emitter(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=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.emitter(message="Starting Ursula on {}".format( URSULA.rest_interface), color='green', bold=True) # Ursula Deploy Warnings click_config.emitter(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.emitter( message= f"Staking {str(URSULA.total_staked)} ~ 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.emitter(message="{} {}".format( e.__class__.__name__, str(e)), color='red', bold=True) raise # Crash :-( # Graceful Exit / Crash finally: click_config.emitter(message="Stopping Ursula", color='green') ursula_config.cleanup() click_config.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) return click_config.emitter( 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.emitter(response=response) elif action == "forget": actions.forget(configuration=ursula_config) return 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) destroyed_filepath = destroy_system_configuration( config_class=UrsulaConfiguration, config_file=config_file, network=network, config_root=ursula_config.config_file_location, force=force) return click_config.emitter(message=f"Destroyed {destroyed_filepath}", color='green') 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) txhash_bytes = 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 ........... {txhash_bytes.hex()}') # 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: value = click.prompt(f"Enter stake value", type=STAKE_VALUE, default=NU(MIN_ALLOWED_LOCKED, 'NuNit')) else: value = NU(int(value), 'NU') # Duration if not quiet: message = "Minimum duration: {} | Maximum Duration: {}".format( MIN_LOCKED_PERIODS, MAX_MINTING_PERIODS) 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) staking_transactions = URSULA.initialize_stake(amount=int(value), lock_periods=duration) painting.paint_staking_confirmation(ursula=URSULA, transactions=staking_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))