Increases correctness, accuracy and density of information provided to CLI users running initialization commands.

pull/3533/head
KPrasch 2024-07-31 17:45:01 +07:00
parent 0c33c154d9
commit 3e37a106b3
No known key found for this signature in database
5 changed files with 78 additions and 23 deletions

View File

@ -317,8 +317,40 @@ def init(general_config, config_options, force, config_root, key_material):
"""Create a new Ursula node configuration."""
emitter = setup_emitter(general_config, config_options.operator_address)
_pre_launch_warnings(emitter, dev=None, force=force)
if not config_root:
config_root = general_config.config_root
config_path = Path(config_root)
keystore_path = config_path / Keystore._DEFAULT_DIR
if config_path.exists() and keystore_path.exists():
click.clear()
emitter.echo(
f"There are existing secret keys in '{keystore_path}'.\n"
"The 'init' command is a one-time operation, do not run it again.\n\n",
color="red",
)
emitter.echo(
"To review your existing configuration, run:\n\n"
"nucypher ursula config\n\n"
"To run your node with the existing configuration, run:\n\n"
"nucypher ursula run\n",
color="cyan",
)
return click.get_current_context().exit(1)
click.clear()
emitter.echo(
"Hello Operator, welcome on board :-) \n\n"
"NOTE: Initializing a new Ursula node configuration is a one-time operation\n"
"for the lifetime of your node. This is a two-step process:\n\n"
"1. Creating a password to encrypt your operator keys\n"
"2. Securing a taco node seed phase\n\n"
"Please follow the prompts.",
color="cyan",
)
if not config_options.eth_endpoint:
raise click.BadOptionUsage(
"--eth-endpoint",

View File

@ -53,8 +53,8 @@ DEFAULT_TO_LONE_CONFIG_FILE = "Defaulting to {config_class} configuration file:
# Authentication
PASSWORD_COLLECTION_NOTICE = """
Please provide a password to lock Operator keys.
Do not forget this password, and ideally store it using a password manager.
Please provide a password to encrypt your node's private keys.
Do not forget this password. Ideally generate and store this password using a password manager.
"""
COLLECT_ETH_PASSWORD = "Enter ethereum account password ({checksum_address})"

View File

@ -3,6 +3,7 @@ from constant_sorrow.constants import NO_KEYSTORE_ATTACHED
from nucypher.characters.banners import NUCYPHER_BANNER
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, USER_LOG_DIR
from nucypher.crypto.powers import RitualisticPower
def echo_version(ctx, param, value):
@ -30,17 +31,21 @@ def paint_new_installation_help(emitter, new_configuration, filepath):
character_config_class = new_configuration.__class__
character_name = character_config_class.NAME.lower()
if new_configuration.keystore != NO_KEYSTORE_ATTACHED:
maybe_public_key = new_configuration.keystore.id
ritual_power = new_configuration.keystore.derive_crypto_power(RitualisticPower)
ferveo_public_key = bytes(ritual_power.public_key()).hex()
maybe_public_key = f"{ferveo_public_key[:8]}...{ferveo_public_key[-8:]}"
else:
maybe_public_key = "(no keystore attached)"
emitter.message("Generated keystore", color="green")
emitter.message(
f"""
Public Key: {maybe_public_key}
DKG Public Key: {maybe_public_key}
Path to Keystore: {new_configuration.keystore_dir}
Path to Config: {filepath}
Path to Logs: {USER_LOG_DIR}
- You can share your public key with anyone. Others need it to interact with you.
- Never share secret keys with anyone!
- Backup your keystore! Character keys are required to interact with the protocol!
- Remember your password! Without the password, it's impossible to decrypt the key!
@ -48,18 +53,6 @@ Path to Keystore: {new_configuration.keystore_dir}
"""
)
default_config_filepath = True
if new_configuration.default_filepath() != filepath:
default_config_filepath = False
emitter.message(f'Generated configuration file at {"default" if default_config_filepath else "non-default"} '
f'filepath {filepath}', color='green')
# add hint about --config-file
if not default_config_filepath:
emitter.message(f'* NOTE: for a non-default configuration filepath use `--config-file "{filepath}"` '
f'with subsequent `{character_name}` CLI commands', color='yellow')
# Ursula
if character_name == 'ursula':
hint = '''
* Review configuration -> nucypher ursula config

View File

@ -593,6 +593,7 @@ class CharacterConfiguration(BaseConfiguration):
"""Shortcut: Hook-up a new initial installation and configuration."""
node_config = cls(dev_mode=False, *args, **kwargs)
node_config.initialize(key_material=key_material, password=password)
node_config.keystore.unlock(password)
return node_config
def cleanup(self) -> None:

View File

@ -380,20 +380,49 @@ class Keystore:
# notification
emitter = StdoutEmitter()
emitter.message(
"Backup your seed words, you will not be able to view them again.\n"
emitter.echo(
"\nNOTE: Next, you will be assigned a taco node seed phase. This seed phase is used to\n"
"generate your keystore. You will need this seed phase to recover your keystore\n"
"in the future. Please write down the seed phase and keep it in a safe place.\n",
color="cyan",
)
emitter.message(f"{__words}\n", color="cyan")
emitter.message(
"IMPORTANT: Backup your seed phrase, you will not be able to view them again.\n"
"You can use these words to restore your keystore in the future in case of loss of\n"
"your keystore files or password. Do not share these words with anyone.\n",
color="yellow",
)
emitter.message(
"WARNING: If you lose your seed phase and also lose access to your keystore/password "
"your stake will be slashed.\n",
color="red",
)
click.confirm("Reveal seed phase?", default=False, abort=True)
click.clear()
formatted_words = "\n".join(
f"{i} {w}" for i, w in enumerate(__words.split(), start=1)
)
emitter.message(f"{formatted_words}\n", color="green")
if not click.confirm("Have you backed up your seed phrase?"):
emitter.message('Keystore generation aborted.', color='red')
raise click.Abort()
click.clear()
# confirmation
__response = click.prompt("Confirm seed words")
if __response != __words:
raise ValueError('Incorrect seed word confirmation. No keystore has been created, try again.')
while True:
__response = click.prompt("Confirm seed words (space separated)")
if __response != __words:
emitter.message(
"Seed words do not match. Please try again.", color="red"
)
continue
break
click.clear()
emitter.echo("Seed phrase confirmed. Generating keystore...", color="green")
@property
def id(self) -> str: