mirror of https://github.com/nucypher/nucypher.git
Nearly 100% of emitter messages from CLI actions are in literature.py
parent
755a02443b
commit
2b5a39e97e
|
@ -15,18 +15,25 @@ You should have received a copy of the GNU Affero General Public License
|
|||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import click
|
||||
import os
|
||||
from constant_sorrow.constants import NO_PASSWORD
|
||||
from nacl.exceptions import CryptoError
|
||||
|
||||
from nucypher.blockchain.eth.decorators import validate_checksum_address
|
||||
from nucypher.cli.literature import COLLECT_ETH_PASSWORD, COLLECT_NUCYPHER_PASSWORD
|
||||
from nucypher.cli.literature import (
|
||||
COLLECT_ETH_PASSWORD,
|
||||
COLLECT_NUCYPHER_PASSWORD,
|
||||
GENERIC_PASSWORD_PROMPT,
|
||||
DECRYPTING_CHARACTER_KEYRING
|
||||
)
|
||||
from nucypher.config.constants import NUCYPHER_ENVVAR_KEYRING_PASSWORD
|
||||
from nucypher.config.node import CharacterConfiguration
|
||||
|
||||
|
||||
def get_password_from_prompt(prompt: str = "Enter password", envvar: str = '', confirm: bool = False) -> str:
|
||||
def get_password_from_prompt(prompt: str = GENERIC_PASSWORD_PROMPT, envvar: str = '', confirm: bool = False) -> str:
|
||||
password = os.environ.get(envvar, NO_PASSWORD)
|
||||
if password is NO_PASSWORD: # Collect password, prefer env var
|
||||
password = click.prompt(prompt, confirmation_prompt=confirm, hide_input=True)
|
||||
|
@ -50,14 +57,18 @@ def get_nucypher_password(confirm: bool = False, envvar=NUCYPHER_ENVVAR_KEYRING_
|
|||
return keyring_password
|
||||
|
||||
|
||||
def unlock_nucypher_keyring(emitter, password: str, character_configuration: CharacterConfiguration):
|
||||
emitter.message(f'Decrypting {character_configuration._NAME} keyring...', color='yellow')
|
||||
def unlock_nucypher_keyring(emitter, password: str, character_configuration: CharacterConfiguration) -> bool:
|
||||
emitter.message(DECRYPTING_CHARACTER_KEYRING.format(name=character_configuration.NAME), color='yellow')
|
||||
|
||||
# precondition
|
||||
if character_configuration.dev_mode:
|
||||
return True # Dev accounts are always unlocked
|
||||
|
||||
# NuCypher
|
||||
# unlock
|
||||
try:
|
||||
character_configuration.attach_keyring()
|
||||
character_configuration.keyring.unlock(password=password) # Takes ~3 seconds, ~1GB Ram
|
||||
except CryptoError:
|
||||
raise character_configuration.keyring.AuthenticationFailed
|
||||
raise character_configuration.keyring.AuthenticationFailed
|
||||
else:
|
||||
return True
|
||||
|
|
|
@ -17,8 +17,9 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
"""
|
||||
|
||||
|
||||
import click
|
||||
import json
|
||||
|
||||
import click
|
||||
from json.decoder import JSONDecodeError
|
||||
|
||||
from nucypher.blockchain.eth.clients import NuCypherGethGoerliProcess
|
||||
|
@ -26,7 +27,8 @@ from nucypher.cli.literature import (
|
|||
CHARACTER_DESTRUCTION,
|
||||
SUCCESSFUL_DESTRUCTION,
|
||||
CONFIRM_FORGET_NODES,
|
||||
SUCCESSFUL_FORGET_NODES
|
||||
SUCCESSFUL_FORGET_NODES, INVALID_CONFIGURATION_FILE_WARNING, INVALID_JSON_IN_CONFIGURATION_WARNING,
|
||||
SUCCESSFUL_UPDATE_CONFIGURATION_VALUES
|
||||
)
|
||||
|
||||
|
||||
|
@ -36,18 +38,18 @@ def get_or_update_configuration(emitter, config_class, filepath: str, config_opt
|
|||
config = config_class.from_configuration_file(filepath=filepath)
|
||||
except config_class.ConfigurationError:
|
||||
# Issue warning for invalid configuration...
|
||||
emitter.message(f"Invalid Configuration at {filepath}.")
|
||||
emitter.message(INVALID_CONFIGURATION_FILE_WARNING.format(filepath=filepath))
|
||||
try:
|
||||
# ... but try to display it anyways
|
||||
response = config_class._read_configuration_file(filepath=filepath)
|
||||
return emitter.echo(json.dumps(response, indent=4))
|
||||
except JSONDecodeError:
|
||||
# ... sorry
|
||||
return emitter.message(f"Invalid JSON in Configuration File at {filepath}.")
|
||||
return emitter.message(INVALID_JSON_IN_CONFIGURATION_WARNING.format(filepath=filepath))
|
||||
else:
|
||||
updates = config_options.get_updates()
|
||||
if updates:
|
||||
emitter.message(f"Updated configuration values: {', '.join(updates)}", color='yellow')
|
||||
emitter.message(SUCCESSFUL_UPDATE_CONFIGURATION_VALUES.format(fields=', '.join(updates)), color='yellow')
|
||||
config.update(**updates)
|
||||
return emitter.echo(config.serialize())
|
||||
|
||||
|
@ -57,7 +59,7 @@ def destroy_configuration(emitter, character_config, force: bool = False) -> Non
|
|||
try:
|
||||
database = character_config.db_filepath
|
||||
except AttributeError:
|
||||
database = "No database found"
|
||||
database = "No database found" # FIXME: This cannot be right.....
|
||||
|
||||
click.confirm(CHARACTER_DESTRUCTION.format(name=character_config._NAME,
|
||||
root=character_config.config_root,
|
||||
|
|
|
@ -17,8 +17,8 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
"""
|
||||
|
||||
import click
|
||||
|
||||
from constant_sorrow.constants import UNKNOWN_DEVELOPMENT_CHAIN_ID
|
||||
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.cli.literature import (
|
||||
RESTAKING_LOCK_AGREEMENT,
|
||||
|
|
|
@ -26,8 +26,15 @@ from json.decoder import JSONDecodeError
|
|||
from typing import Set, Optional, Dict, List
|
||||
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry
|
||||
from nucypher.cli.literature import CONFIRM_URSULA_IPV4_ADDRESS, COLLECT_URSULA_IPV4_ADDRESS, \
|
||||
FORCE_DETECT_URSULA_IP_WARNING, NO_DOMAIN_PEERS, SEEDNODE_NOT_STAKING_WARNING
|
||||
from nucypher.cli.literature import (
|
||||
CONFIRM_URSULA_IPV4_ADDRESS,
|
||||
COLLECT_URSULA_IPV4_ADDRESS,
|
||||
FORCE_DETECT_URSULA_IP_WARNING,
|
||||
NO_DOMAIN_PEERS,
|
||||
SEEDNODE_NOT_STAKING_WARNING,
|
||||
START_LOADING_SEEDNODES,
|
||||
UNREADABLE_SEEDNODE_ADVISORY
|
||||
)
|
||||
from nucypher.cli.types import IPV4_ADDRESS
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
from nucypher.network.exceptions import NodeSeemsToBeDown
|
||||
|
@ -100,7 +107,7 @@ def load_seednodes(emitter,
|
|||
"""
|
||||
|
||||
# Heads up
|
||||
emitter.message("Connecting to preferred teacher nodes...", color='yellow')
|
||||
emitter.message(START_LOADING_SEEDNODES, color='yellow')
|
||||
from nucypher.characters.lawful import Ursula
|
||||
|
||||
# Aggregate URIs (Ordered by Priority)
|
||||
|
@ -119,7 +126,7 @@ def load_seednodes(emitter,
|
|||
network_middleware=network_middleware,
|
||||
registry=registry)
|
||||
except NodeSeemsToBeDown:
|
||||
emitter.message(f"Failed to connect to teacher: {uri}")
|
||||
emitter.message(UNREADABLE_SEEDNODE_ADVISORY.format(uri=uri))
|
||||
continue
|
||||
except Teacher.NotStaking:
|
||||
emitter.message(SEEDNODE_NOT_STAKING_WARNING.format(uri=uri))
|
||||
|
|
|
@ -32,6 +32,19 @@ from nucypher.blockchain.eth.registry import InMemoryContractRegistry, Individua
|
|||
from nucypher.blockchain.eth.signers import Signer
|
||||
from nucypher.blockchain.eth.token import Stake, NU
|
||||
from nucypher.cli.config import extract_checksum_address_from_filepath
|
||||
from nucypher.cli.literature import (
|
||||
NO_CONFIGURATIONS_ON_DISK,
|
||||
SELECT_NETWORK,
|
||||
IS_THIS_CORRECT,
|
||||
PREALLOCATION_STAKE_ADVISORY,
|
||||
SELECT_STAKING_ACCOUNT_INDEX,
|
||||
GENERIC_SELECT_ACCOUNT,
|
||||
NO_ETH_ACCOUNTS,
|
||||
SELECT_STAKE,
|
||||
NO_DIVISIBLE_STAKES,
|
||||
ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE,
|
||||
NO_STAKES_FOUND
|
||||
)
|
||||
from nucypher.cli.painting import paint_stakes
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT, NUCYPHER_ENVVAR_WORKER_ADDRESS
|
||||
|
||||
|
@ -43,19 +56,19 @@ def select_stake(stakeholder, emitter, divisible: bool = False, staker_address:
|
|||
else:
|
||||
stakes = stakeholder.all_stakes
|
||||
if not stakes:
|
||||
emitter.echo(f"No stakes found.", color='red')
|
||||
emitter.echo(NO_STAKES_FOUND, color='red')
|
||||
raise click.Abort
|
||||
|
||||
stakes = sorted((stake for stake in stakes if stake.is_active), key=lambda s: s.address_index_ordering_key)
|
||||
if divisible:
|
||||
emitter.echo("NOTE: Showing divisible stakes only", color='yellow')
|
||||
emitter.echo(ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE, color='yellow')
|
||||
stakes = list(filter(lambda s: bool(s.value >= stakeholder.economics.minimum_allowed_locked*2), stakes)) # TODO: Move to method on Stake
|
||||
if not stakes:
|
||||
emitter.echo(f"No divisible stakes found.", color='red')
|
||||
emitter.echo(NO_DIVISIBLE_STAKES, color='red')
|
||||
raise click.Abort
|
||||
enumerated_stakes = dict(enumerate(stakes))
|
||||
paint_stakes(stakeholder=stakeholder, emitter=emitter, staker_address=staker_address)
|
||||
choice = click.prompt("Select Stake", type=click.IntRange(min=0, max=len(enumerated_stakes)-1))
|
||||
choice = click.prompt(SELECT_STAKE, type=click.IntRange(min=0, max=len(enumerated_stakes)-1))
|
||||
chosen_stake = enumerated_stakes[choice]
|
||||
return chosen_stake
|
||||
|
||||
|
@ -97,7 +110,7 @@ def select_client_account(emitter,
|
|||
wallet_accounts = wallet.accounts
|
||||
enumerated_accounts = dict(enumerate(wallet_accounts))
|
||||
if len(enumerated_accounts) < 1:
|
||||
emitter.echo("No ETH accounts were found.", color='red', bold=True)
|
||||
emitter.echo(NO_ETH_ACCOUNTS, color='red', bold=True)
|
||||
raise click.Abort()
|
||||
|
||||
# Display account info
|
||||
|
@ -127,7 +140,7 @@ def select_client_account(emitter,
|
|||
emitter.echo(tabulate(rows, headers=headers, showindex='always'))
|
||||
|
||||
# Prompt the user for selection, and return
|
||||
prompt = prompt or "Select index of account"
|
||||
prompt = prompt or GENERIC_SELECT_ACCOUNT
|
||||
account_range = click.IntRange(min=0, max=len(enumerated_accounts)-1)
|
||||
choice = click.prompt(prompt, type=account_range, default=default)
|
||||
chosen_account = enumerated_accounts[choice]
|
||||
|
@ -154,16 +167,15 @@ def handle_client_account_for_staking(emitter,
|
|||
if individual_allocation:
|
||||
client_account = individual_allocation.beneficiary_address
|
||||
staking_address = individual_allocation.contract_address
|
||||
|
||||
message = f"Beneficiary {client_account} will use preallocation contract {staking_address} to stake."
|
||||
message = PREALLOCATION_STAKE_ADVISORY.format(client_account=client_account, staking_address=staking_address)
|
||||
emitter.echo(message, color='yellow', verbosity=1)
|
||||
if not force:
|
||||
click.confirm("Is this correct?", abort=True)
|
||||
click.confirm(IS_THIS_CORRECT, abort=True)
|
||||
else:
|
||||
if staking_address:
|
||||
client_account = staking_address
|
||||
else:
|
||||
client_account = select_client_account(prompt="Select index of staking account",
|
||||
client_account = select_client_account(prompt=SELECT_STAKING_ACCOUNT_INDEX,
|
||||
emitter=emitter,
|
||||
registry=stakeholder.registry,
|
||||
network=stakeholder.network,
|
||||
|
@ -177,7 +189,7 @@ def select_network(emitter) -> str:
|
|||
headers = ["Network"]
|
||||
rows = [[n] for n in NetworksInventory.NETWORKS]
|
||||
emitter.echo(tabulate(rows, headers=headers, showindex='always'))
|
||||
choice = click.prompt("Select Network", default=0, type=click.IntRange(0, len(NetworksInventory.NETWORKS)-1))
|
||||
choice = click.prompt(SELECT_NETWORK, default=0, type=click.IntRange(0, len(NetworksInventory.NETWORKS)-1))
|
||||
network = NetworksInventory.NETWORKS[choice]
|
||||
return network
|
||||
|
||||
|
@ -194,12 +206,12 @@ def select_config_file(emitter,
|
|||
|
||||
config_root = config_root or DEFAULT_CONFIG_ROOT
|
||||
default_config_file = glob.glob(config_class.default_filepath(config_root=config_root))
|
||||
glob_pattern = f'{config_root}/{config_class._NAME}-0x*.{config_class._CONFIG_FILE_EXTENSION}'
|
||||
glob_pattern = f'{config_root}/{config_class.NAME}-0x*.{config_class._CONFIG_FILE_EXTENSION}'
|
||||
secondary_config_files = glob.glob(glob_pattern)
|
||||
config_files = [*default_config_file, *secondary_config_files]
|
||||
if not config_files:
|
||||
emitter.message(f"No {config_class._NAME.capitalize()} configurations found. "
|
||||
f"run 'nucypher {config_class._NAME} init' then try again.", color='red')
|
||||
emitter.message(NO_CONFIGURATIONS_ON_DISK.format(name=config_class.NAME.capitalize(),
|
||||
command=config_class.NAME), color='red')
|
||||
raise click.Abort()
|
||||
|
||||
checksum_address = checksum_address or os.environ.get(NUCYPHER_ENVVAR_WORKER_ADDRESS, None) # TODO: Deprecate worker_address in favor of checksum_address
|
||||
|
@ -213,7 +225,7 @@ def select_config_file(emitter,
|
|||
try:
|
||||
config_file = parsed_addresses[checksum_address]
|
||||
except KeyError:
|
||||
raise ValueError(f"'{checksum_address}' is not a known {config_class._NAME} configuration account.")
|
||||
raise ValueError(f"'{checksum_address}' is not a known {config_class.NAME} configuration account.")
|
||||
|
||||
elif len(config_files) > 1:
|
||||
|
||||
|
@ -228,7 +240,7 @@ def select_config_file(emitter,
|
|||
emitter.echo(tabulate(parsed_addresses, headers=headers, showindex='always'))
|
||||
|
||||
# Prompt the user for selection, and return
|
||||
prompt = f"Select {config_class._NAME} configuration"
|
||||
prompt = f"Select {config_class.NAME} configuration"
|
||||
account_range = click.IntRange(min=0, max=len(config_files) - 1)
|
||||
choice = click.prompt(prompt, type=account_range, default=0)
|
||||
config_file = config_files[choice]
|
||||
|
|
|
@ -20,13 +20,19 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
import click
|
||||
import os
|
||||
import shutil
|
||||
from constant_sorrow.constants import NO_CONTROL_PROTOCOL
|
||||
from nacl.exceptions import CryptoError
|
||||
|
||||
from constant_sorrow.constants import NO_CONTROL_PROTOCOL
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry, InMemoryContractRegistry, LocalContractRegistry
|
||||
from nucypher.cli.actions.auth import unlock_nucypher_keyring, get_nucypher_password
|
||||
from nucypher.cli.actions.network import load_seednodes
|
||||
from nucypher.cli.literature import (
|
||||
FEDERATED_WARNING,
|
||||
PRODUCTION_REGISTRY_ADVISORY,
|
||||
LOCAL_REGISTRY_ADVISORY,
|
||||
CONNECTING_TO_BLOCKCHAIN
|
||||
)
|
||||
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
|
||||
|
||||
|
||||
|
@ -82,7 +88,7 @@ def make_cli_character(character_config,
|
|||
|
||||
# Federated
|
||||
if character_config.federated_only:
|
||||
emitter.message("WARNING: Running in Federated mode", color='yellow')
|
||||
emitter.message(FEDERATED_WARNING, color='yellow')
|
||||
|
||||
return CHARACTER
|
||||
|
||||
|
@ -97,7 +103,7 @@ def establish_deployer_registry(emitter,
|
|||
|
||||
if download_registry:
|
||||
registry = InMemoryContractRegistry.from_latest_publication()
|
||||
emitter.message(f"Using latest published registry from {registry.source}")
|
||||
emitter.message(PRODUCTION_REGISTRY_ADVISORY.format(source=registry.source))
|
||||
return registry
|
||||
|
||||
# Establish a contract registry from disk if specified
|
||||
|
@ -120,7 +126,7 @@ def establish_deployer_registry(emitter,
|
|||
|
||||
# All Done.
|
||||
registry = LocalContractRegistry(filepath=registry_filepath)
|
||||
emitter.message(f"Configured to registry filepath {registry_filepath}")
|
||||
emitter.message(LOCAL_REGISTRY_ADVISORY.format(registry_filepath=registry_filepath))
|
||||
|
||||
return registry
|
||||
|
||||
|
@ -142,7 +148,7 @@ def connect_to_blockchain(provider_uri, emitter, debug: bool = False, light: boo
|
|||
sync=False,
|
||||
emitter=emitter)
|
||||
blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri)
|
||||
emitter.echo(message="Reading Latest Chaindata...")
|
||||
emitter.echo(message=CONNECTING_TO_BLOCKCHAIN)
|
||||
blockchain.connect()
|
||||
return blockchain
|
||||
except Exception as e:
|
||||
|
|
|
@ -1,30 +1,48 @@
|
|||
"""
|
||||
This file is part of nucypher.
|
||||
|
||||
nucypher is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
nucypher is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
|
||||
"""Text blobs that are implemented as part of nucypher CLI emitter messages."""
|
||||
|
||||
|
||||
#
|
||||
# Common
|
||||
#
|
||||
|
||||
IS_THIS_CORRECT = "Is this correct?"
|
||||
|
||||
|
||||
#
|
||||
# Blockchain
|
||||
#
|
||||
|
||||
CONNECTING_TO_BLOCKCHAIN = "Reading Latest Chaindata..."
|
||||
|
||||
PRODUCTION_REGISTRY_ADVISORY = "Using latest published registry from {source}"
|
||||
|
||||
LOCAL_REGISTRY_ADVISORY = "Configured to registry filepath {registry_filepath}"
|
||||
|
||||
FEDERATED_WARNING = "WARNING: Running in Federated mode"
|
||||
|
||||
|
||||
#
|
||||
# Staking
|
||||
#
|
||||
|
||||
RESTAKING_LOCK_AGREEMENT = """
|
||||
By enabling the re-staking lock for {staking_address}, you are committing to automatically
|
||||
re-stake all rewards until a future period. You will not be able to disable re-staking until {release_period}.
|
||||
"""
|
||||
|
||||
RESTAKING_AGREEMENT = "By enabling the re-staking for {staking_address}, all staking rewards will be automatically added to your existing stake."
|
||||
|
||||
WINDING_DOWN_AGREEMENT = """
|
||||
Over time, as the locked stake duration decreases
|
||||
i.e. `winds down`, you will receive decreasing inflationary rewards.
|
||||
|
||||
Instead, by disabling `wind down` (default) the locked stake duration
|
||||
can remain constant until you specify that `wind down` should begin. By
|
||||
keeping the locked stake duration constant, it ensures that you will
|
||||
receive maximum inflation compensation.
|
||||
|
||||
If `wind down` was previously disabled, you can enable it at any point
|
||||
and the locked duration will decrease after each period.
|
||||
|
||||
For more information see https://docs.nucypher.com/en/latest/architecture/sub_stakes.html#winding-down.
|
||||
"""
|
||||
|
||||
CONFIRM_STAGED_STAKE = """
|
||||
* Ursula Node Operator Notice *
|
||||
-------------------------------
|
||||
|
@ -49,6 +67,29 @@ paid out in ethers retro-actively and on-demand.
|
|||
|
||||
Accept ursula node operator obligation?"""
|
||||
|
||||
WINDING_DOWN_AGREEMENT = """
|
||||
Over time, as the locked stake duration decreases
|
||||
i.e. `winds down`, you will receive decreasing inflationary rewards.
|
||||
|
||||
Instead, by disabling `wind down` (default) the locked stake duration
|
||||
can remain constant until you specify that `wind down` should begin. By
|
||||
keeping the locked stake duration constant, it ensures that you will
|
||||
receive maximum inflation compensation.
|
||||
|
||||
If `wind down` was previously disabled, you can enable it at any point
|
||||
and the locked duration will decrease after each period.
|
||||
|
||||
For more information see https://docs.nucypher.com/en/latest/architecture/sub_stakes.html#winding-down.
|
||||
"""
|
||||
|
||||
RESTAKING_LOCK_AGREEMENT = """
|
||||
By enabling the re-staking lock for {staking_address}, you are committing to automatically
|
||||
re-stake all rewards until a future period. You will not be able to disable re-staking until {release_period}.
|
||||
"""
|
||||
|
||||
RESTAKING_AGREEMENT = "By enabling the re-staking for {staking_address}," \
|
||||
" all staking rewards will be automatically added to your existing stake."
|
||||
|
||||
CONFIRM_RESTAKING_LOCK = "Confirm enable re-staking lock for staker {staking_address} until {release_period}?"
|
||||
|
||||
CONFIRM_ENABLE_RESTAKING = "Confirm enable automatic re-staking for staker {staking_address}?"
|
||||
|
@ -59,10 +100,37 @@ CONFIRM_LARGE_STAKE_VALUE = "Wow, {value} - That's a lot of NU - Are you sure th
|
|||
|
||||
CONFIRM_LARGE_STAKE_DURATION = "Woah, {lock_periods} is a long time - Are you sure this is correct?"
|
||||
|
||||
PREALLOCATION_STAKE_ADVISORY = "Beneficiary {client_account} will use preallocation contract {staking_address} to stake."
|
||||
|
||||
SELECT_STAKING_ACCOUNT_INDEX = "Select index of staking account"
|
||||
|
||||
SELECT_STAKE = "Select Stake"
|
||||
|
||||
ONLY_DISPLAYING_DIVISIBLE_STAKES_NOTE = "NOTE: Showing divisible stakes only"
|
||||
|
||||
NO_DIVISIBLE_STAKES = "No divisible stakes found."
|
||||
|
||||
NO_STAKES_FOUND = "No stakes found."
|
||||
|
||||
|
||||
#
|
||||
# Configuration
|
||||
#
|
||||
|
||||
SELECT_NETWORK = "Select Network"
|
||||
|
||||
NO_CONFIGURATIONS_ON_DISK = "No {name} configurations found. run 'nucypher {command} init' then try again."
|
||||
|
||||
SUCCESSFUL_UPDATE_CONFIGURATION_VALUES = "Updated configuration values: {fields}"
|
||||
|
||||
INVALID_JSON_IN_CONFIGURATION_WARNING = "Invalid JSON in Configuration File at {filepath}."
|
||||
|
||||
INVALID_CONFIGURATION_FILE_WARNING = "Invalid Configuration at {filepath}."
|
||||
|
||||
NO_ETH_ACCOUNTS = "No ETH accounts were found."
|
||||
|
||||
GENERIC_SELECT_ACCOUNT = "Select index of account"
|
||||
|
||||
CHARACTER_DESTRUCTION = '''
|
||||
Delete all {name} character files including:
|
||||
- Private and Public Keys ({keystore})
|
||||
|
@ -74,7 +142,6 @@ Are you sure?'''
|
|||
|
||||
SUCCESSFUL_DESTRUCTION = "Successfully destroyed NuCypher configuration"
|
||||
|
||||
|
||||
CONFIRM_FORGET_NODES = "Permanently delete all known node data?"
|
||||
|
||||
SUCCESSFUL_FORGET_NODES = "Removed all stored known nodes metadata and certificates"
|
||||
|
@ -88,18 +155,29 @@ COLLECT_ETH_PASSWORD = "Enter password to unlock account {checksum_address}"
|
|||
|
||||
COLLECT_NUCYPHER_PASSWORD = "Enter NuCypher keyring password"
|
||||
|
||||
GENERIC_PASSWORD_PROMPT = "Enter password"
|
||||
|
||||
DECRYPTING_CHARACTER_KEYRING = 'Decrypting {name} keyring...'
|
||||
|
||||
|
||||
#
|
||||
# Ursula Networking
|
||||
# Networking
|
||||
#
|
||||
|
||||
|
||||
CONFIRM_URSULA_IPV4_ADDRESS = "Is this the public-facing IPv4 address ({rest_host}) you want to use for Ursula?"
|
||||
|
||||
COLLECT_URSULA_IPV4_ADDRESS = "Please enter Ursula's public-facing IPv4 address here:"
|
||||
|
||||
|
||||
#
|
||||
# Seednodes
|
||||
#
|
||||
|
||||
START_LOADING_SEEDNODES = "Connecting to preferred teacher nodes..."
|
||||
|
||||
UNREADABLE_SEEDNODE_ADVISORY = "Failed to connect to teacher: {uri}"
|
||||
|
||||
FORCE_DETECT_URSULA_IP_WARNING = "WARNING: --force is set, using auto-detected IP '{rest_host}'"
|
||||
|
||||
NO_DOMAIN_PEERS = "WARNING - No Peers Available for domains: {domains}"
|
||||
|
|
|
@ -55,7 +55,7 @@ def echo_version(ctx, param, value):
|
|||
|
||||
def paint_new_installation_help(emitter, new_configuration):
|
||||
character_config_class = new_configuration.__class__
|
||||
character_name = character_config_class._NAME.lower()
|
||||
character_name = character_config_class.NAME.lower()
|
||||
|
||||
emitter.message("Generated keyring {}".format(new_configuration.keyring_root), color='green')
|
||||
emitter.message("Saved configuration file {}".format(new_configuration.config_file_location), color='green')
|
||||
|
|
|
@ -65,7 +65,7 @@ class BaseConfiguration(ABC):
|
|||
return filepath
|
||||
"""
|
||||
|
||||
_NAME = NotImplemented
|
||||
NAME = NotImplemented
|
||||
_CONFIG_FILE_EXTENSION = 'json'
|
||||
|
||||
INDENTATION = 2
|
||||
|
@ -90,8 +90,8 @@ class BaseConfiguration(ABC):
|
|||
filepath: str = None,
|
||||
*args, **kwargs):
|
||||
|
||||
if self._NAME is NotImplemented:
|
||||
error = f'_NAME must be implemented on BaseConfiguration subclass {self.__class__.__name__}'
|
||||
if self.NAME is NotImplemented:
|
||||
error = f'NAME must be implemented on BaseConfiguration subclass {self.__class__.__name__}'
|
||||
raise TypeError(error)
|
||||
|
||||
self.config_root = config_root or self.DEFAULT_CONFIG_ROOT
|
||||
|
@ -137,7 +137,7 @@ class BaseConfiguration(ABC):
|
|||
:param modifier: String to modify default filename with.
|
||||
:return: The generated filepath string.
|
||||
"""
|
||||
name = cls._NAME.lower()
|
||||
name = cls.NAME.lower()
|
||||
if modifier:
|
||||
name += f'-{modifier}'
|
||||
filename = f'{name}.{cls._CONFIG_FILE_EXTENSION.lower()}'
|
||||
|
|
|
@ -37,7 +37,7 @@ class UrsulaConfiguration(CharacterConfiguration):
|
|||
|
||||
from nucypher.characters.lawful import Ursula
|
||||
CHARACTER_CLASS = Ursula
|
||||
_NAME = CHARACTER_CLASS.__name__.lower()
|
||||
NAME = CHARACTER_CLASS.__name__.lower()
|
||||
|
||||
DEFAULT_REST_HOST = '127.0.0.1'
|
||||
DEFAULT_REST_PORT = 9151
|
||||
|
@ -144,7 +144,7 @@ class AliceConfiguration(CharacterConfiguration):
|
|||
from nucypher.characters.lawful import Alice
|
||||
|
||||
CHARACTER_CLASS = Alice
|
||||
_NAME = CHARACTER_CLASS.__name__.lower()
|
||||
NAME = CHARACTER_CLASS.__name__.lower()
|
||||
|
||||
DEFAULT_CONTROLLER_PORT = 8151
|
||||
|
||||
|
@ -187,7 +187,7 @@ class BobConfiguration(CharacterConfiguration):
|
|||
from nucypher.characters.lawful import Bob
|
||||
|
||||
CHARACTER_CLASS = Bob
|
||||
_NAME = CHARACTER_CLASS.__name__.lower()
|
||||
NAME = CHARACTER_CLASS.__name__.lower()
|
||||
|
||||
DEFAULT_CONTROLLER_PORT = 7151
|
||||
|
||||
|
@ -203,7 +203,7 @@ class FelixConfiguration(CharacterConfiguration):
|
|||
|
||||
# Character
|
||||
CHARACTER_CLASS = Felix
|
||||
_NAME = CHARACTER_CLASS.__name__.lower()
|
||||
NAME = CHARACTER_CLASS.__name__.lower()
|
||||
|
||||
DEFAULT_DB_NAME = '{}.db'.format(_NAME)
|
||||
DEFAULT_DB_FILEPATH = os.path.join(DEFAULT_CONFIG_ROOT, DEFAULT_DB_NAME)
|
||||
|
@ -248,7 +248,7 @@ class FelixConfiguration(CharacterConfiguration):
|
|||
|
||||
class StakeHolderConfiguration(CharacterConfiguration):
|
||||
|
||||
_NAME = 'stakeholder'
|
||||
NAME = 'stakeholder'
|
||||
CHARACTER_CLASS = StakeHolder
|
||||
|
||||
def __init__(self, checksum_addresses: set = None, *args, **kwargs):
|
||||
|
|
|
@ -39,7 +39,7 @@ def expected_configuration_filepaths():
|
|||
|
||||
class RestorableTestItem(BaseConfiguration):
|
||||
|
||||
_NAME = 'something'
|
||||
NAME = 'something'
|
||||
DEFAULT_CONFIG_ROOT = '/tmp'
|
||||
VERSION = 1
|
||||
|
||||
|
@ -55,7 +55,7 @@ class RestorableTestItem(BaseConfiguration):
|
|||
|
||||
def test_base_configuration_defaults():
|
||||
assert BaseConfiguration.DEFAULT_CONFIG_ROOT == DEFAULT_CONFIG_ROOT
|
||||
assert BaseConfiguration._NAME == NotImplemented
|
||||
assert BaseConfiguration.NAME == NotImplemented
|
||||
assert BaseConfiguration._CONFIG_FILE_EXTENSION == expected_extension
|
||||
|
||||
|
||||
|
@ -87,7 +87,7 @@ def test_configuration_implementation():
|
|||
# Correct minimum viable implementation
|
||||
class BareMinimumConfigurableItem(BaseConfiguration):
|
||||
|
||||
_NAME = 'bare-minimum'
|
||||
NAME = 'bare-minimum'
|
||||
VERSION = 2
|
||||
|
||||
def static_payload(self) -> dict:
|
||||
|
|
Loading…
Reference in New Issue